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