Home | History | Annotate | Download | only in src
      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 "public/fpdf_transformpage.h"
      8 
      9 #include "fpdfsdk/include/fsdk_define.h"
     10 
     11 namespace {
     12 
     13 void SetBoundingBox(CPDF_Page* page,
     14                     const CFX_ByteStringC& key,
     15                     float left,
     16                     float bottom,
     17                     float right,
     18                     float top) {
     19   CPDF_Dictionary* pPageDict = page->m_pFormDict;
     20   CPDF_Array* pBoundingBoxArray = new CPDF_Array;
     21   pBoundingBoxArray->Add(new CPDF_Number(left));
     22   pBoundingBoxArray->Add(new CPDF_Number(bottom));
     23   pBoundingBoxArray->Add(new CPDF_Number(right));
     24   pBoundingBoxArray->Add(new CPDF_Number(top));
     25   pPageDict->SetAt(key, pBoundingBoxArray);
     26 }
     27 
     28 FPDF_BOOL GetBoundingBox(CPDF_Page* page,
     29                          const CFX_ByteStringC& key,
     30                          float* left,
     31                          float* bottom,
     32                          float* right,
     33                          float* top) {
     34   CPDF_Dictionary* pPageDict = page->m_pFormDict;
     35   CPDF_Array* pArray = pPageDict->GetArray(key);
     36   if (!pArray)
     37     return FALSE;
     38 
     39   *left = pArray->GetFloat(0);
     40   *bottom = pArray->GetFloat(1);
     41   *right = pArray->GetFloat(2);
     42   *top = pArray->GetFloat(3);
     43   return TRUE;
     44 }
     45 
     46 }  // namespace
     47 
     48 DLLEXPORT void STDCALL FPDFPage_SetMediaBox(FPDF_PAGE page,
     49                                             float left,
     50                                             float bottom,
     51                                             float right,
     52                                             float top) {
     53   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
     54   if (!pPage)
     55     return;
     56 
     57   SetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
     58 }
     59 
     60 DLLEXPORT void STDCALL FPDFPage_SetCropBox(FPDF_PAGE page,
     61                                            float left,
     62                                            float bottom,
     63                                            float right,
     64                                            float top) {
     65   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
     66   if (!pPage)
     67     return;
     68 
     69   SetBoundingBox(pPage, "CropBox", left, bottom, right, top);
     70 }
     71 
     72 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetMediaBox(FPDF_PAGE page,
     73                                                  float* left,
     74                                                  float* bottom,
     75                                                  float* right,
     76                                                  float* top) {
     77   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
     78   return pPage && GetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
     79 }
     80 
     81 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetCropBox(FPDF_PAGE page,
     82                                                 float* left,
     83                                                 float* bottom,
     84                                                 float* right,
     85                                                 float* top) {
     86   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
     87   return pPage && GetBoundingBox(pPage, "CropBox", left, bottom, right, top);
     88 }
     89 
     90 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_TransFormWithClip(FPDF_PAGE page,
     91                                                        FS_MATRIX* matrix,
     92                                                        FS_RECTF* clipRect) {
     93   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
     94   if (!pPage)
     95     return FALSE;
     96 
     97   CFX_ByteTextBuf textBuf;
     98   textBuf << "q ";
     99   CFX_FloatRect rect(clipRect->left, clipRect->bottom, clipRect->right,
    100                      clipRect->top);
    101   rect.Normalize();
    102   CFX_ByteString bsClipping;
    103   bsClipping.Format("%f %f %f %f re W* n ", rect.left, rect.bottom,
    104                     rect.Width(), rect.Height());
    105   textBuf << bsClipping;
    106 
    107   CFX_ByteString bsMatix;
    108   bsMatix.Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b, matrix->c,
    109                  matrix->d, matrix->e, matrix->f);
    110   textBuf << bsMatix;
    111 
    112   CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
    113   CPDF_Object* pContentObj =
    114       pPageDic ? pPageDic->GetElement("Contents") : nullptr;
    115   if (!pContentObj)
    116     pContentObj = pPageDic ? pPageDic->GetArray("Contents") : nullptr;
    117   if (!pContentObj)
    118     return FALSE;
    119 
    120   CPDF_Dictionary* pDic = new CPDF_Dictionary;
    121   CPDF_Stream* pStream = new CPDF_Stream(nullptr, 0, pDic);
    122   pStream->SetData(textBuf.GetBuffer(), textBuf.GetSize(), FALSE, FALSE);
    123   CPDF_Document* pDoc = pPage->m_pDocument;
    124   if (!pDoc)
    125     return FALSE;
    126   pDoc->AddIndirectObject(pStream);
    127 
    128   pDic = new CPDF_Dictionary;
    129   CPDF_Stream* pEndStream = new CPDF_Stream(nullptr, 0, pDic);
    130   pEndStream->SetData((const uint8_t*)" Q", 2, FALSE, FALSE);
    131   pDoc->AddIndirectObject(pEndStream);
    132 
    133   CPDF_Array* pContentArray = nullptr;
    134   if (CPDF_Array* pArray = ToArray(pContentObj)) {
    135     pContentArray = pArray;
    136     CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
    137     pContentArray->InsertAt(0, pRef);
    138     pContentArray->AddReference(pDoc, pEndStream);
    139   } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
    140     CPDF_Object* pDirectObj = pReference->GetDirect();
    141     if (pDirectObj) {
    142       if (CPDF_Array* pArray = pDirectObj->AsArray()) {
    143         pContentArray = pArray;
    144         CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
    145         pContentArray->InsertAt(0, pRef);
    146         pContentArray->AddReference(pDoc, pEndStream);
    147       } else if (pDirectObj->IsStream()) {
    148         pContentArray = new CPDF_Array();
    149         pContentArray->AddReference(pDoc, pStream->GetObjNum());
    150         pContentArray->AddReference(pDoc, pDirectObj->GetObjNum());
    151         pContentArray->AddReference(pDoc, pEndStream);
    152         pPageDic->SetAtReference("Contents", pDoc,
    153                                  pDoc->AddIndirectObject(pContentArray));
    154       }
    155     }
    156   }
    157 
    158   // Need to transform the patterns as well.
    159   CPDF_Dictionary* pRes = pPageDic->GetDict("Resources");
    160   if (pRes) {
    161     CPDF_Dictionary* pPattenDict = pRes->GetDict("Pattern");
    162     if (pPattenDict) {
    163       for (const auto& it : *pPattenDict) {
    164         CPDF_Object* pObj = it.second;
    165         if (pObj->IsReference())
    166           pObj = pObj->GetDirect();
    167 
    168         CPDF_Dictionary* pDict = nullptr;
    169         if (pObj->IsDictionary())
    170           pDict = pObj->AsDictionary();
    171         else if (CPDF_Stream* pStream = pObj->AsStream())
    172           pDict = pStream->GetDict();
    173         else
    174           continue;
    175 
    176         CFX_Matrix m = pDict->GetMatrix("Matrix");
    177         CFX_Matrix t = *(CFX_Matrix*)matrix;
    178         m.Concat(t);
    179         pDict->SetAtMatrix("Matrix", m);
    180       }
    181     }
    182   }
    183 
    184   return TRUE;
    185 }
    186 
    187 DLLEXPORT void STDCALL
    188 FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
    189                               double a,
    190                               double b,
    191                               double c,
    192                               double d,
    193                               double e,
    194                               double f) {
    195   CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
    196   if (!pPageObj)
    197     return;
    198   CFX_Matrix matrix((FX_FLOAT)a, (FX_FLOAT)b, (FX_FLOAT)c, (FX_FLOAT)d,
    199                     (FX_FLOAT)e, (FX_FLOAT)f);
    200 
    201   // Special treatment to shading object, because the ClipPath for shading
    202   // object is already transformed.
    203   if (pPageObj->m_Type != PDFPAGE_SHADING)
    204     pPageObj->TransformClipPath(matrix);
    205   pPageObj->TransformGeneralState(matrix);
    206 }
    207 
    208 DLLEXPORT FPDF_CLIPPATH STDCALL FPDF_CreateClipPath(float left,
    209                                                     float bottom,
    210                                                     float right,
    211                                                     float top) {
    212   CPDF_ClipPath* pNewClipPath = new CPDF_ClipPath();
    213   pNewClipPath->GetModify();
    214   CPDF_Path Path;
    215   Path.GetModify();
    216   Path.AppendRect(left, bottom, right, top);
    217   pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, FALSE);
    218   return pNewClipPath;
    219 }
    220 
    221 DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
    222   delete (CPDF_ClipPath*)clipPath;
    223 }
    224 
    225 void OutputPath(CFX_ByteTextBuf& buf, CPDF_Path path) {
    226   const CFX_PathData* pPathData = path;
    227   if (!pPathData)
    228     return;
    229 
    230   FX_PATHPOINT* pPoints = pPathData->GetPoints();
    231 
    232   if (path.IsRect()) {
    233     buf << (pPoints[0].m_PointX) << " " << (pPoints[0].m_PointY) << " "
    234         << (pPoints[2].m_PointX - pPoints[0].m_PointX) << " "
    235         << (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re\n";
    236     return;
    237   }
    238 
    239   CFX_ByteString temp;
    240   for (int i = 0; i < pPathData->GetPointCount(); i++) {
    241     buf << (pPoints[i].m_PointX) << " " << (pPoints[i].m_PointY);
    242     int point_type = pPoints[i].m_Flag & FXPT_TYPE;
    243     if (point_type == FXPT_MOVETO)
    244       buf << " m\n";
    245     else if (point_type == FXPT_BEZIERTO) {
    246       buf << " " << (pPoints[i + 1].m_PointX) << " "
    247           << (pPoints[i + 1].m_PointY) << " " << (pPoints[i + 2].m_PointX)
    248           << " " << (pPoints[i + 2].m_PointY);
    249       if (pPoints[i + 2].m_Flag & FXPT_CLOSEFIGURE)
    250         buf << " c h\n";
    251       else
    252         buf << " c\n";
    253       i += 2;
    254     } else if (point_type == FXPT_LINETO) {
    255       if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE)
    256         buf << " l h\n";
    257       else
    258         buf << " l\n";
    259     }
    260   }
    261 }
    262 
    263 DLLEXPORT void STDCALL FPDFPage_InsertClipPath(FPDF_PAGE page,
    264                                                FPDF_CLIPPATH clipPath) {
    265   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    266   if (!pPage)
    267     return;
    268 
    269   CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
    270   CPDF_Object* pContentObj =
    271       pPageDic ? pPageDic->GetElement("Contents") : nullptr;
    272   if (!pContentObj)
    273     pContentObj = pPageDic ? pPageDic->GetArray("Contents") : nullptr;
    274   if (!pContentObj)
    275     return;
    276 
    277   CFX_ByteTextBuf strClip;
    278   CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
    279   FX_DWORD i;
    280   for (i = 0; i < pClipPath->GetPathCount(); i++) {
    281     CPDF_Path path = pClipPath->GetPath(i);
    282     int iClipType = pClipPath->GetClipType(i);
    283     if (path.GetPointCount() == 0) {
    284       // Empty clipping (totally clipped out)
    285       strClip << "0 0 m W n ";
    286     } else {
    287       OutputPath(strClip, path);
    288       if (iClipType == FXFILL_WINDING)
    289         strClip << "W n\n";
    290       else
    291         strClip << "W* n\n";
    292     }
    293   }
    294   CPDF_Dictionary* pDic = new CPDF_Dictionary;
    295   CPDF_Stream* pStream = new CPDF_Stream(nullptr, 0, pDic);
    296   pStream->SetData(strClip.GetBuffer(), strClip.GetSize(), FALSE, FALSE);
    297   CPDF_Document* pDoc = pPage->m_pDocument;
    298   if (!pDoc)
    299     return;
    300   pDoc->AddIndirectObject(pStream);
    301 
    302   CPDF_Array* pContentArray = nullptr;
    303   if (CPDF_Array* pArray = ToArray(pContentObj)) {
    304     pContentArray = pArray;
    305     CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
    306     pContentArray->InsertAt(0, pRef);
    307   } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
    308     CPDF_Object* pDirectObj = pReference->GetDirect();
    309     if (pDirectObj) {
    310       if (CPDF_Array* pArray = pDirectObj->AsArray()) {
    311         pContentArray = pArray;
    312         CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
    313         pContentArray->InsertAt(0, pRef);
    314       } else if (pDirectObj->IsStream()) {
    315         pContentArray = new CPDF_Array();
    316         pContentArray->AddReference(pDoc, pStream->GetObjNum());
    317         pContentArray->AddReference(pDoc, pDirectObj->GetObjNum());
    318         pPageDic->SetAtReference("Contents", pDoc,
    319                                  pDoc->AddIndirectObject(pContentArray));
    320       }
    321     }
    322   }
    323 }
    324