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