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