1 // Copyright 2014 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "../include/fsdk_define.h" 8 #include "../include/fpdf_flatten.h" 9 10 typedef CFX_ArrayTemplate<CPDF_Dictionary*> CPDF_ObjectArray; 11 typedef CFX_ArrayTemplate<CPDF_Rect> CPDF_RectArray; 12 13 enum FPDF_TYPE { MAX, MIN }; 14 enum FPDF_VALUE { TOP, LEFT, RIGHT, BOTTOM }; 15 16 FX_BOOL IsValiableRect(CPDF_Rect rect, CPDF_Rect rcPage) 17 { 18 if ( rect.left - rect.right > 0.000001f || 19 rect.bottom - rect.top > 0.000001f) 20 return FALSE; 21 22 if (rect.left == 0.0f && 23 rect.top == 0.0f && 24 rect.right == 0.0f && 25 rect.bottom == 0.0f) 26 return FALSE; 27 28 if (!rcPage.IsEmpty()) 29 { 30 if (rect.left - rcPage.left < -10.000001f || 31 rect.right - rcPage.right > 10.000001f || 32 rect.top - rcPage.top > 10.000001f || 33 rect.bottom - rcPage.bottom < -10.000001f) 34 return FALSE; 35 } 36 37 return TRUE; 38 } 39 40 41 FX_BOOL GetContentsRect( CPDF_Document * pDoc, CPDF_Dictionary* pDict, CPDF_RectArray * pRectArray ) 42 { 43 CPDF_Page* pPDFPage = FX_NEW CPDF_Page; 44 pPDFPage->Load( pDoc, pDict, FALSE ); 45 pPDFPage->ParseContent(); 46 47 FX_POSITION pos = pPDFPage->GetFirstObjectPosition(); 48 49 while (pos) 50 { 51 CPDF_PageObject* pPageObject = pPDFPage->GetNextObject(pos); 52 if (!pPageObject)continue; 53 54 CPDF_Rect rc; 55 rc.left = pPageObject->m_Left; 56 rc.right = pPageObject->m_Right; 57 rc.bottom = pPageObject->m_Bottom; 58 rc.top = pPageObject->m_Top; 59 60 if (IsValiableRect(rc, pDict->GetRect("MediaBox"))) 61 { 62 pRectArray->Add(rc); 63 } 64 } 65 66 delete pPDFPage; 67 return TRUE; 68 } 69 70 71 void ParserStream( CPDF_Dictionary * pPageDic, CPDF_Dictionary* pStream, CPDF_RectArray * pRectArray, CPDF_ObjectArray * pObjectArray ) 72 { 73 if (!pStream)return; 74 CPDF_Rect rect; 75 if (pStream->KeyExist("Rect")) 76 rect = pStream->GetRect("Rect"); 77 else if (pStream->KeyExist("BBox")) 78 rect = pStream->GetRect("BBox"); 79 80 if (IsValiableRect(rect, pPageDic->GetRect("MediaBox"))) 81 pRectArray->Add(rect); 82 83 pObjectArray->Add(pStream); 84 } 85 86 87 int ParserAnnots( CPDF_Document* pSourceDoc, CPDF_Dictionary * pPageDic, CPDF_RectArray * pRectArray, CPDF_ObjectArray * pObjectArray, int nUsage) 88 { 89 if (!pSourceDoc || !pPageDic) return FLATTEN_FAIL; 90 91 GetContentsRect( pSourceDoc, pPageDic, pRectArray ); 92 CPDF_Array* pAnnots = pPageDic->GetArray("Annots"); 93 if (pAnnots) 94 { 95 FX_DWORD dwSize = pAnnots->GetCount(); 96 97 for (int i = 0; i < (int)dwSize; i++) 98 { 99 CPDF_Object* pObj = pAnnots->GetElementValue(i); 100 101 if (!pObj)continue; 102 103 if (pObj->GetType() == PDFOBJ_DICTIONARY) 104 { 105 CPDF_Dictionary* pAnnotDic = (CPDF_Dictionary*)pObj; 106 CFX_ByteString sSubtype = pAnnotDic->GetString("Subtype"); 107 if (sSubtype == "Popup")continue; 108 109 int nAnnotFlag = pAnnotDic->GetInteger("F"); 110 111 if(nAnnotFlag & ANNOTFLAG_HIDDEN) 112 continue; 113 if(nUsage == FLAT_NORMALDISPLAY) 114 { 115 if(nAnnotFlag & ANNOTFLAG_INVISIBLE) 116 continue; 117 ParserStream( pPageDic, pAnnotDic, pRectArray, pObjectArray ); 118 } 119 else 120 { 121 if(nAnnotFlag & ANNOTFLAG_PRINT) 122 ParserStream( pPageDic, pAnnotDic, pRectArray, pObjectArray ); 123 } 124 } 125 } 126 return FLATTEN_SUCCESS; 127 }else{ 128 return FLATTEN_NOTINGTODO; 129 } 130 } 131 132 133 FX_FLOAT GetMinMaxValue( CPDF_RectArray& array, FPDF_TYPE type, FPDF_VALUE value) 134 { 135 int nRects = array.GetSize(); 136 FX_FLOAT fRet = 0.0f; 137 138 if (nRects <= 0)return 0.0f; 139 140 FX_FLOAT* pArray = new FX_FLOAT[nRects]; 141 switch(value) 142 { 143 case LEFT: 144 { 145 for (int i = 0; i < nRects; i++) 146 pArray[i] = CPDF_Rect(array.GetAt(i)).left; 147 148 break; 149 } 150 case TOP: 151 { 152 for (int i = 0; i < nRects; i++) 153 pArray[i] = CPDF_Rect(array.GetAt(i)).top; 154 155 break; 156 } 157 case RIGHT: 158 { 159 for (int i = 0; i < nRects; i++) 160 pArray[i] = CPDF_Rect(array.GetAt(i)).right; 161 162 break; 163 } 164 case BOTTOM: 165 { 166 for (int i = 0; i < nRects; i++) 167 pArray[i] = CPDF_Rect(array.GetAt(i)).bottom; 168 169 break; 170 } 171 default: 172 break; 173 } 174 fRet = pArray[0]; 175 if (type == MAX) 176 { 177 for (int i = 1; i < nRects; i++) 178 if (fRet <= pArray[i]) 179 fRet = pArray[i]; 180 } 181 else 182 { 183 for (int i = 1; i < nRects; i++) 184 if (fRet >= pArray[i]) 185 fRet = pArray[i]; 186 } 187 delete[] pArray; 188 return fRet; 189 } 190 191 CPDF_Rect CalculateRect( CPDF_RectArray * pRectArray ) 192 { 193 194 CPDF_Rect rcRet; 195 196 rcRet.left = GetMinMaxValue(*pRectArray, MIN, LEFT); 197 rcRet.top = GetMinMaxValue(*pRectArray, MAX, TOP); 198 rcRet.right = GetMinMaxValue(*pRectArray, MAX, RIGHT); 199 rcRet.bottom = GetMinMaxValue(*pRectArray, MIN, BOTTOM); 200 201 return rcRet; 202 } 203 204 205 void SetPageContents(CFX_ByteString key, CPDF_Dictionary* pPage, CPDF_Document* pDocument) 206 { 207 CPDF_Object* pContentsObj = pPage->GetStream("Contents"); 208 if (!pContentsObj) 209 { 210 pContentsObj = pPage->GetArray("Contents"); 211 } 212 213 if (!pContentsObj) 214 { 215 //Create a new contents dictionary 216 if (!key.IsEmpty()) 217 { 218 CPDF_Stream* pNewContents = FX_NEW CPDF_Stream(NULL, 0, FX_NEW CPDF_Dictionary); 219 if (!pNewContents)return; 220 pPage->SetAtReference("Contents", pDocument, pDocument->AddIndirectObject(pNewContents)); 221 222 CFX_ByteString sStream; 223 sStream.Format("q 1 0 0 1 0 0 cm /%s Do Q", (FX_LPCSTR)key); 224 pNewContents->SetData((FX_LPCBYTE)sStream, sStream.GetLength(), FALSE, FALSE); 225 } 226 return; 227 } 228 229 int iType = pContentsObj->GetType(); 230 CPDF_Array* pContentsArray = NULL; 231 232 switch(iType) 233 { 234 case PDFOBJ_STREAM: 235 { 236 pContentsArray = FX_NEW CPDF_Array; 237 CPDF_Stream* pContents = (CPDF_Stream*)pContentsObj; 238 FX_DWORD dwObjNum = pDocument->AddIndirectObject(pContents); 239 CPDF_StreamAcc acc; 240 acc.LoadAllData(pContents); 241 CFX_ByteString sStream = "q\n"; 242 CFX_ByteString sBody = CFX_ByteString((FX_LPCSTR)acc.GetData(), acc.GetSize()); 243 sStream = sStream + sBody + "\nQ"; 244 pContents->SetData((FX_LPCBYTE)sStream, sStream.GetLength(), FALSE, FALSE); 245 pContentsArray->AddReference(pDocument, dwObjNum); 246 break; 247 } 248 249 case PDFOBJ_ARRAY: 250 { 251 pContentsArray = (CPDF_Array*)pContentsObj; 252 break; 253 } 254 default: 255 break; 256 } 257 258 if (!pContentsArray)return; 259 260 FX_DWORD dwObjNum = pDocument->AddIndirectObject(pContentsArray); 261 pPage->SetAtReference("Contents", pDocument, dwObjNum); 262 263 if (!key.IsEmpty()) 264 { 265 CPDF_Stream* pNewContents = FX_NEW CPDF_Stream(NULL, 0, FX_NEW CPDF_Dictionary); 266 dwObjNum = pDocument->AddIndirectObject(pNewContents); 267 pContentsArray->AddReference(pDocument, dwObjNum); 268 269 CFX_ByteString sStream; 270 sStream.Format("q 1 0 0 1 0 0 cm /%s Do Q", (FX_LPCSTR)key); 271 pNewContents->SetData((FX_LPCBYTE)sStream, sStream.GetLength(), FALSE, FALSE); 272 } 273 } 274 275 CFX_AffineMatrix GetMatrix(CPDF_Rect rcAnnot, CPDF_Rect rcStream, CFX_AffineMatrix matrix) 276 { 277 if(rcStream.IsEmpty()) 278 return CFX_AffineMatrix(); 279 280 matrix.TransformRect(rcStream); 281 rcStream.Normalize(); 282 283 FX_FLOAT a = rcAnnot.Width()/rcStream.Width(); 284 FX_FLOAT d = rcAnnot.Height()/rcStream.Height(); 285 286 FX_FLOAT e = rcAnnot.left - rcStream.left * a; 287 FX_FLOAT f = rcAnnot.bottom - rcStream.bottom * d; 288 return CFX_AffineMatrix(a, 0, 0, d, e, f); 289 } 290 291 void GetOffset(FX_FLOAT& fa, FX_FLOAT& fd, FX_FLOAT& fe, FX_FLOAT& ff, CPDF_Rect rcAnnot, CPDF_Rect rcStream, CFX_AffineMatrix matrix) 292 { 293 FX_FLOAT fStreamWidth = 0.0f; 294 FX_FLOAT fStreamHeight = 0.0f; 295 296 297 298 if (matrix.a != 0 && matrix.d != 0) 299 { 300 fStreamWidth = rcStream.right - rcStream.left; 301 fStreamHeight = rcStream.top - rcStream.bottom; 302 } 303 else 304 { 305 fStreamWidth = rcStream.top - rcStream.bottom; 306 fStreamHeight = rcStream.right - rcStream.left; 307 } 308 309 FX_FLOAT x1 = matrix.a * rcStream.left + matrix.c * rcStream.bottom + matrix.e; 310 FX_FLOAT y1 = matrix.b * rcStream.left + matrix.d * rcStream.bottom + matrix.f; 311 FX_FLOAT x2 = matrix.a * rcStream.left + matrix.c * rcStream.top + matrix.e; 312 FX_FLOAT y2 = matrix.b * rcStream.left + matrix.d * rcStream.top + matrix.f; 313 FX_FLOAT x3 = matrix.a * rcStream.right + matrix.c * rcStream.bottom + matrix.e; 314 FX_FLOAT y3 = matrix.b * rcStream.right + matrix.d * rcStream.bottom + matrix.f; 315 FX_FLOAT x4 = matrix.a * rcStream.right + matrix.c * rcStream.top + matrix.e; 316 FX_FLOAT y4 = matrix.b * rcStream.right + matrix.d * rcStream.top + matrix.f; 317 318 FX_FLOAT left = FX_MIN(FX_MIN(x1, x2), FX_MIN(x3, x4)); 319 FX_FLOAT bottom = FX_MIN(FX_MIN(y1, y2), FX_MIN(y3, y4)); 320 321 fa = (rcAnnot.right - rcAnnot.left)/fStreamWidth; 322 fd = (rcAnnot.top - rcAnnot.bottom)/fStreamHeight; 323 fe = rcAnnot.left - left * fa; 324 ff = rcAnnot.bottom - bottom * fd; 325 } 326 327 328 DLLEXPORT int STDCALL FPDFPage_Flatten( FPDF_PAGE page, int nFlag) 329 { 330 if (!page) 331 { 332 return FLATTEN_FAIL; 333 } 334 335 CPDF_Page * pPage = (CPDF_Page*)( page ); 336 CPDF_Document * pDocument = pPage->m_pDocument; 337 CPDF_Dictionary * pPageDict = pPage->m_pFormDict; 338 339 if ( !pDocument || !pPageDict ) 340 { 341 return FLATTEN_FAIL; 342 } 343 344 CPDF_ObjectArray ObjectArray; 345 CPDF_RectArray RectArray; 346 347 int iRet = FLATTEN_FAIL; 348 iRet = ParserAnnots( pDocument, pPageDict, &RectArray, &ObjectArray, nFlag); 349 if (iRet == FLATTEN_NOTINGTODO) 350 { 351 return FLATTEN_NOTINGTODO; 352 }else if (iRet == FLATTEN_FAIL) 353 { 354 return FLATTEN_FAIL; 355 } 356 357 CPDF_Rect rcOriginalCB; 358 CPDF_Rect rcMerger = CalculateRect( &RectArray ); 359 CPDF_Rect rcOriginalMB = pPageDict->GetRect("MediaBox"); 360 361 if (pPageDict->KeyExist("CropBox")) 362 rcOriginalMB = pPageDict->GetRect("CropBox"); 363 364 if (rcOriginalMB.IsEmpty()) 365 { 366 rcOriginalMB = CPDF_Rect(0.0f, 0.0f, 612.0f, 792.0f); 367 } 368 369 rcMerger.left = rcMerger.left < rcOriginalMB.left? rcOriginalMB.left : rcMerger.left; 370 rcMerger.right = rcMerger.right > rcOriginalMB.right? rcOriginalMB.right : rcMerger.right; 371 rcMerger.top = rcMerger.top > rcOriginalMB.top? rcOriginalMB.top : rcMerger.top; 372 rcMerger.bottom = rcMerger.bottom < rcOriginalMB.bottom? rcOriginalMB.bottom : rcMerger.bottom; 373 374 if (pPageDict->KeyExist("ArtBox")) 375 rcOriginalCB = pPageDict->GetRect("ArtBox"); 376 else 377 rcOriginalCB = rcOriginalMB; 378 379 if (!rcOriginalMB.IsEmpty()) 380 { 381 CPDF_Array* pMediaBox = FX_NEW CPDF_Array(); 382 383 pMediaBox->Add(FX_NEW CPDF_Number(rcOriginalMB.left)); 384 pMediaBox->Add(FX_NEW CPDF_Number(rcOriginalMB.bottom)); 385 pMediaBox->Add(FX_NEW CPDF_Number(rcOriginalMB.right)); 386 pMediaBox->Add(FX_NEW CPDF_Number(rcOriginalMB.top)); 387 388 pPageDict->SetAt("MediaBox",pMediaBox); 389 } 390 391 if (!rcOriginalCB.IsEmpty()) 392 { 393 CPDF_Array* pCropBox = FX_NEW CPDF_Array(); 394 pCropBox->Add(FX_NEW CPDF_Number(rcOriginalCB.left)); 395 pCropBox->Add(FX_NEW CPDF_Number(rcOriginalCB.bottom)); 396 pCropBox->Add(FX_NEW CPDF_Number(rcOriginalCB.right)); 397 pCropBox->Add(FX_NEW CPDF_Number(rcOriginalCB.top)); 398 pPageDict->SetAt("ArtBox", pCropBox); 399 } 400 401 CPDF_Dictionary* pRes = NULL; 402 pRes = pPageDict->GetDict("Resources"); 403 if (!pRes) 404 { 405 pRes = FX_NEW CPDF_Dictionary; 406 pPageDict->SetAt( "Resources", pRes ); 407 } 408 409 CPDF_Stream* pNewXObject = FX_NEW CPDF_Stream(NULL, 0, FX_NEW CPDF_Dictionary); 410 FX_DWORD dwObjNum = pDocument->AddIndirectObject(pNewXObject); 411 CPDF_Dictionary* pPageXObject = pRes->GetDict("XObject"); 412 if (!pPageXObject) 413 { 414 pPageXObject = FX_NEW CPDF_Dictionary; 415 pRes->SetAt("XObject", pPageXObject); 416 } 417 418 CFX_ByteString key = ""; 419 int nStreams = ObjectArray.GetSize(); 420 421 if (nStreams > 0) 422 { 423 for (int iKey = 0; /*iKey < 100*/; iKey++) 424 { 425 char sExtend[5] = {0}; 426 FXSYS_itoa(iKey, sExtend, 10); 427 key = CFX_ByteString("FFT") + CFX_ByteString(sExtend); 428 429 if (!pPageXObject->KeyExist(key)) 430 break; 431 } 432 } 433 434 SetPageContents(key, pPageDict, pDocument); 435 436 CPDF_Dictionary* pNewXORes = NULL; 437 438 if (!key.IsEmpty()) 439 { 440 pPageXObject->SetAtReference(key, pDocument, dwObjNum); 441 CPDF_Dictionary* pNewOXbjectDic = pNewXObject->GetDict(); 442 pNewXORes = FX_NEW CPDF_Dictionary; 443 pNewOXbjectDic->SetAt("Resources", pNewXORes); 444 pNewOXbjectDic->SetAtName("Type", "XObject"); 445 pNewOXbjectDic->SetAtName("Subtype", "Form"); 446 pNewOXbjectDic->SetAtInteger("FormType", 1); 447 pNewOXbjectDic->SetAtName("Name", "FRM"); 448 CPDF_Rect rcBBox = pPageDict->GetRect("ArtBox"); 449 pNewOXbjectDic->SetAtRect("BBox", rcBBox); 450 } 451 452 for (int i = 0; i < nStreams; i++) 453 { 454 CPDF_Dictionary* pAnnotDic = ObjectArray.GetAt(i); 455 if (!pAnnotDic)continue; 456 457 CPDF_Rect rcAnnot = pAnnotDic->GetRect("Rect"); 458 rcAnnot.Normalize(); 459 460 CFX_ByteString sAnnotState = pAnnotDic->GetString("AS"); 461 CPDF_Dictionary* pAnnotAP = pAnnotDic->GetDict("AP"); 462 if (!pAnnotAP)continue; 463 464 CPDF_Stream* pAPStream = pAnnotAP->GetStream("N"); 465 if (!pAPStream) 466 { 467 CPDF_Dictionary* pAPDic = pAnnotAP->GetDict("N"); 468 if (!pAPDic)continue; 469 470 if (!sAnnotState.IsEmpty()) 471 { 472 pAPStream = pAPDic->GetStream(sAnnotState); 473 } 474 else 475 { 476 FX_POSITION pos = pAPDic->GetStartPos(); 477 if (pos) 478 { 479 CFX_ByteString sKey; 480 CPDF_Object* pFirstObj = pAPDic->GetNextElement(pos, sKey); 481 if (pFirstObj) 482 { 483 if (pFirstObj->GetType() == PDFOBJ_REFERENCE) 484 pFirstObj = pFirstObj->GetDirect(); 485 486 if (pFirstObj->GetType() != PDFOBJ_STREAM) 487 continue; 488 489 pAPStream = (CPDF_Stream*)pFirstObj; 490 } 491 } 492 } 493 } 494 495 if (!pAPStream)continue; 496 497 CPDF_Dictionary* pAPDic = pAPStream->GetDict(); 498 CFX_AffineMatrix matrix = pAPDic->GetMatrix("Matrix"); 499 500 CPDF_Rect rcStream; 501 if (pAPDic->KeyExist("Rect")) 502 rcStream = pAPDic->GetRect("Rect"); 503 else if (pAPDic->KeyExist("BBox")) 504 rcStream = pAPDic->GetRect("BBox"); 505 506 if (rcStream.IsEmpty())continue; 507 508 CPDF_Object* pObj = pAPStream; 509 510 if (pObj) 511 { 512 CPDF_Dictionary* pObjDic = pObj->GetDict(); 513 if (pObjDic) 514 { 515 pObjDic->SetAtName("Type", "XObject"); 516 pObjDic->SetAtName("Subtype", "Form"); 517 } 518 } 519 520 CPDF_Dictionary* pXObject = pNewXORes->GetDict("XObject"); 521 if (!pXObject) 522 { 523 pXObject = FX_NEW CPDF_Dictionary; 524 pNewXORes->SetAt("XObject", pXObject); 525 } 526 527 CFX_ByteString sFormName; 528 sFormName.Format("F%d", i); 529 FX_DWORD dwObjNum = pDocument->AddIndirectObject(pObj); 530 pXObject->SetAtReference(sFormName, pDocument, dwObjNum); 531 532 CPDF_StreamAcc acc; 533 acc.LoadAllData(pNewXObject); 534 535 FX_LPCBYTE pData = acc.GetData(); 536 CFX_ByteString sStream(pData, acc.GetSize()); 537 CFX_ByteString sTemp; 538 539 if (matrix.IsIdentity()) 540 { 541 matrix.a = 1.0f; 542 matrix.b = 0.0f; 543 matrix.c = 0.0f; 544 matrix.d = 1.0f; 545 matrix.e = 0.0f; 546 matrix.f = 0.0f; 547 } 548 549 CFX_AffineMatrix m = GetMatrix(rcAnnot, rcStream, matrix); 550 sTemp.Format("q %f 0 0 %f %f %f cm /%s Do Q\n", m.a, m.d, m.e, m.f, (FX_LPCSTR)sFormName); 551 sStream += sTemp; 552 553 pNewXObject->SetData((FX_LPCBYTE)sStream, sStream.GetLength(), FALSE, FALSE); 554 } 555 pPageDict->RemoveAt( "Annots" ); 556 557 ObjectArray.RemoveAll(); 558 RectArray.RemoveAll(); 559 560 return FLATTEN_SUCCESS; 561 } 562