Clean up CPDF_AnnotList.
[pdfium.git] / fpdfsdk / src / fpdf_transformpage.cpp
index a666666..8a835fb 100644 (file)
 // Copyright 2014 PDFium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include "../../public/fpdf_transformpage.h"
 #include "../include/fsdk_define.h"
-#include "../include/fpdf_transformpage.h"
-
-DLLEXPORT void STDCALL FPDFPage_SetMediaBox(FPDF_PAGE page, float left, float bottom, float right, float top)
-{
-       if(!page)
-               return;
-       CPDF_Page* pPage = (CPDF_Page*)page;
-       CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
-       CPDF_Array* pMediaBoxArray = FX_NEW CPDF_Array;
-       pMediaBoxArray->Add(FX_NEW CPDF_Number(left));
-       pMediaBoxArray->Add(FX_NEW CPDF_Number(bottom));
-       pMediaBoxArray->Add(FX_NEW CPDF_Number(FX_FLOAT(right)));
-       pMediaBoxArray->Add(FX_NEW CPDF_Number(FX_FLOAT(top)));
-       
-       pPageDict->SetAt("MediaBox", pMediaBoxArray);
+
+namespace {
+
+void SetBoundingBox(CPDF_Page* page,
+                    const CFX_ByteStringC& key,
+                    float left,
+                    float bottom,
+                    float right,
+                    float top) {
+  CPDF_Dictionary* pPageDict = page->m_pFormDict;
+  CPDF_Array* pBoundingBoxArray = new CPDF_Array;
+  pBoundingBoxArray->Add(new CPDF_Number(left));
+  pBoundingBoxArray->Add(new CPDF_Number(bottom));
+  pBoundingBoxArray->Add(new CPDF_Number(right));
+  pBoundingBoxArray->Add(new CPDF_Number(top));
+  pPageDict->SetAt(key, pBoundingBoxArray);
+}
+
+FPDF_BOOL GetBoundingBox(CPDF_Page* page,
+                         const CFX_ByteStringC& key,
+                         float* left,
+                         float* bottom,
+                         float* right,
+                         float* top) {
+  CPDF_Dictionary* pPageDict = page->m_pFormDict;
+  CPDF_Array* pArray = pPageDict->GetArray(key);
+  if (!pArray)
+    return FALSE;
+
+  *left = pArray->GetFloat(0);
+  *bottom = pArray->GetFloat(1);
+  *right = pArray->GetFloat(2);
+  *top = pArray->GetFloat(3);
+  return TRUE;
 }
 
+}  // namespace
 
-DLLEXPORT void STDCALL FPDFPage_SetCropBox(FPDF_PAGE page, float left, float bottom, float right, float top)
-{
-       if(!page)
-               return;
-       CPDF_Page* pPage = (CPDF_Page*)page;
-       CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
-       CPDF_Array* pCropBoxArray = FX_NEW CPDF_Array;
-       pCropBoxArray->Add(FX_NEW CPDF_Number(left));
-       pCropBoxArray->Add(FX_NEW CPDF_Number(bottom));
-       pCropBoxArray->Add(FX_NEW CPDF_Number(FX_FLOAT(right)));
-       pCropBoxArray->Add(FX_NEW CPDF_Number(FX_FLOAT(top)));
-       
-       
-       pPageDict->SetAt("CropBox", pCropBoxArray);
+DLLEXPORT void STDCALL FPDFPage_SetMediaBox(FPDF_PAGE page,
+                                            float left,
+                                            float bottom,
+                                            float right,
+                                            float top) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  SetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
 }
 
+DLLEXPORT void STDCALL FPDFPage_SetCropBox(FPDF_PAGE page,
+                                           float left,
+                                           float bottom,
+                                           float right,
+                                           float top) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
 
-DLLEXPORT FX_BOOL STDCALL FPDFPage_GetMediaBox(FPDF_PAGE page, float* left, float* bottom, float* right, float* top)
-{
-       if(!page)
-               return FALSE;
-       CPDF_Page* pPage = (CPDF_Page*)page;
-       CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
-       CPDF_Array* pArray = pPageDict->GetArray("MediaBox");
-       if(pArray)
-       {
-               *left = pArray->GetFloat(0);
-               *bottom = pArray->GetFloat(1);
-               *right = pArray->GetFloat(2);
-               *top = pArray->GetFloat(3);
-               return TRUE;
-       }
-       return FALSE;
+  SetBoundingBox(pPage, "CropBox", left, bottom, right, top);
 }
 
-DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetCropBox(FPDF_PAGE page, float* left, float* bottom, float* right, float* top)
-{
-       if(!page)
-               return FALSE;
-       CPDF_Page* pPage = (CPDF_Page*)page;
-       CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
-       CPDF_Array* pArray = pPageDict->GetArray("CropBox");
-       if(pArray)
-       {
-               *left = pArray->GetFloat(0);
-               *bottom = pArray->GetFloat(1);
-               *right = pArray->GetFloat(2);
-               *top = pArray->GetFloat(3);
-               return TRUE;
-       }
-       return FALSE;
+DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetMediaBox(FPDF_PAGE page,
+                                                 float* left,
+                                                 float* bottom,
+                                                 float* right,
+                                                 float* top) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  return pPage && GetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
 }
 
-DLLEXPORT FPDF_BOOL STDCALL FPDFPage_TransFormWithClip(FPDF_PAGE page, FS_MATRIX* matrix, FS_RECTF* clipRect)
-{
-       if(!page)
-               return FALSE;
-
-       CFX_ByteTextBuf textBuf;
-       textBuf<<"q ";
-       CFX_FloatRect rect(clipRect->left, clipRect->bottom, clipRect->right, clipRect->top);
-       rect.Normalize();
-       CFX_ByteString bsClipping;
-       bsClipping.Format("%f %f %f %f re W* n ", rect.left, rect.bottom, rect.Width(), rect.Height());
-       textBuf<<bsClipping;
-
-       CFX_ByteString bsMatix;
-       bsMatix.Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b,matrix->c,matrix->d,matrix->e,matrix->f);
-       textBuf<<bsMatix;
-       
-
-       CPDF_Page* pPage = (CPDF_Page*)page;
-       CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
-       CPDF_Object* pContentObj = pPageDic ? pPageDic->GetElement("Contents") : NULL;
-       if(!pContentObj)
-               pContentObj = pPageDic ? pPageDic->GetArray("Contents") : NULL;
-       if(!pContentObj)
-               return FALSE;
-       
-       CPDF_Dictionary* pDic = FX_NEW CPDF_Dictionary;
-       CPDF_Stream* pStream = FX_NEW CPDF_Stream(NULL,0, pDic);
-       pStream->SetData(textBuf.GetBuffer(), textBuf.GetSize(), FALSE, FALSE);
-       CPDF_Document* pDoc = pPage->m_pDocument;
-       if(!pDoc)
-               return FALSE;
-       pDoc->AddIndirectObject(pStream);
-
-       pDic = FX_NEW CPDF_Dictionary;
-       CPDF_Stream* pEndStream = FX_NEW CPDF_Stream(NULL,0, pDic);
-       pEndStream->SetData((FX_LPCBYTE)" Q", 2, FALSE, FALSE);
-       pDoc->AddIndirectObject(pEndStream);
-       
-       CPDF_Array* pContentArray = NULL;
-       if (pContentObj && pContentObj->GetType() == PDFOBJ_ARRAY)
-       {
-               pContentArray = (CPDF_Array*)pContentObj;
-               CPDF_Reference* pRef = FX_NEW CPDF_Reference(pDoc, pStream->GetObjNum());
-               pContentArray->InsertAt(0, pRef);
-               pContentArray->AddReference(pDoc,pEndStream);
-               
-       }
-       else if(pContentObj && pContentObj->GetType() == PDFOBJ_REFERENCE)
-       {
-               CPDF_Reference* pReference = (CPDF_Reference*)pContentObj;
-               CPDF_Object* pDirectObj = pReference->GetDirect();
-               if(pDirectObj != NULL)
-               {
-                       if(pDirectObj->GetType() == PDFOBJ_ARRAY)
-                       {
-                               pContentArray = (CPDF_Array*)pDirectObj;
-                               CPDF_Reference* pRef = FX_NEW CPDF_Reference(pDoc, pStream->GetObjNum());
-                               pContentArray->InsertAt(0, pRef);
-                               pContentArray->AddReference(pDoc,pEndStream);
-                               
-                       }
-                       else if(pDirectObj->GetType() == PDFOBJ_STREAM)
-                       {
-                               pContentArray = FX_NEW CPDF_Array();
-                               pContentArray->AddReference(pDoc,pStream->GetObjNum());
-                               pContentArray->AddReference(pDoc,pDirectObj->GetObjNum());
-                               pContentArray->AddReference(pDoc, pEndStream);
-                               pPageDic->SetAtReference("Contents", pDoc, pDoc->AddIndirectObject(pContentArray));
-                       }
-               }
-       }       
-
-       //Need to transform the patterns as well.
-       CPDF_Dictionary* pRes = pPageDic->GetDict(FX_BSTRC("Resources"));
-       if(pRes)
-       {
-               CPDF_Dictionary* pPattenDict = pRes->GetDict(FX_BSTRC("Pattern"));
-               if(pPattenDict)
-               {
-                       FX_POSITION pos = pPattenDict->GetStartPos();
-                       while(pos)
-                       {
-                               CPDF_Dictionary* pDict = NULL;
-                               CFX_ByteString key;
-                               CPDF_Object* pObj = pPattenDict->GetNextElement(pos, key);
-                               if(pObj->GetType() == PDFOBJ_REFERENCE)
-                                       pObj = pObj->GetDirect();
-                               if(pObj->GetType() == PDFOBJ_DICTIONARY)
-                               {
-                                       pDict = (CPDF_Dictionary*)pObj;
-                               }
-                               else if(pObj->GetType() == PDFOBJ_STREAM)
-                               {
-                                       pDict = ((CPDF_Stream*)pObj)->GetDict();
-                               }
-                               else
-                                       continue;
-                               
-                               CFX_AffineMatrix m = pDict->GetMatrix(FX_BSTRC("Matrix"));
-                               CFX_AffineMatrix t = *(CFX_AffineMatrix*)matrix;
-                               m.Concat(t);
-                               pDict->SetAtMatrix(FX_BSTRC("Matrix"), m);
-                       }
-               }
-       }
-
-       return TRUE;
+DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetCropBox(FPDF_PAGE page,
+                                                float* left,
+                                                float* bottom,
+                                                float* right,
+                                                float* top) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  return pPage && GetBoundingBox(pPage, "CropBox", left, bottom, right, top);
 }
 
-DLLEXPORT void STDCALL FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,double a, double b, double c, double d, double e, double f)
-{
-       CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
-       if(pPageObj == NULL)
-               return;
-       CFX_AffineMatrix matrix((FX_FLOAT)a,(FX_FLOAT)b,(FX_FLOAT)c,(FX_FLOAT)d,(FX_FLOAT)e,(FX_FLOAT)f);
-       
-       //Special treatment to shading object, because the ClipPath for shading object is already transformed.
-       if(pPageObj->m_Type != PDFPAGE_SHADING)
-               pPageObj->TransformClipPath(matrix);
-       pPageObj->TransformGeneralState(matrix);
+DLLEXPORT FPDF_BOOL STDCALL FPDFPage_TransFormWithClip(FPDF_PAGE page,
+                                                       FS_MATRIX* matrix,
+                                                       FS_RECTF* clipRect) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return FALSE;
+
+  CFX_ByteTextBuf textBuf;
+  textBuf << "q ";
+  CFX_FloatRect rect(clipRect->left, clipRect->bottom, clipRect->right,
+                     clipRect->top);
+  rect.Normalize();
+  CFX_ByteString bsClipping;
+  bsClipping.Format("%f %f %f %f re W* n ", rect.left, rect.bottom,
+                    rect.Width(), rect.Height());
+  textBuf << bsClipping;
+
+  CFX_ByteString bsMatix;
+  bsMatix.Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b, matrix->c,
+                 matrix->d, matrix->e, matrix->f);
+  textBuf << bsMatix;
+
+  CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
+  CPDF_Object* pContentObj =
+      pPageDic ? pPageDic->GetElement("Contents") : nullptr;
+  if (!pContentObj)
+    pContentObj = pPageDic ? pPageDic->GetArray("Contents") : nullptr;
+  if (!pContentObj)
+    return FALSE;
+
+  CPDF_Dictionary* pDic = new CPDF_Dictionary;
+  CPDF_Stream* pStream = new CPDF_Stream(nullptr, 0, pDic);
+  pStream->SetData(textBuf.GetBuffer(), textBuf.GetSize(), FALSE, FALSE);
+  CPDF_Document* pDoc = pPage->m_pDocument;
+  if (!pDoc)
+    return FALSE;
+  pDoc->AddIndirectObject(pStream);
+
+  pDic = new CPDF_Dictionary;
+  CPDF_Stream* pEndStream = new CPDF_Stream(nullptr, 0, pDic);
+  pEndStream->SetData((const uint8_t*)" Q", 2, FALSE, FALSE);
+  pDoc->AddIndirectObject(pEndStream);
+
+  CPDF_Array* pContentArray = nullptr;
+  if (CPDF_Array* pArray = ToArray(pContentObj)) {
+    pContentArray = pArray;
+    CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
+    pContentArray->InsertAt(0, pRef);
+    pContentArray->AddReference(pDoc, pEndStream);
+  } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
+    CPDF_Object* pDirectObj = pReference->GetDirect();
+    if (pDirectObj) {
+      if (CPDF_Array* pArray = pDirectObj->AsArray()) {
+        pContentArray = pArray;
+        CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
+        pContentArray->InsertAt(0, pRef);
+        pContentArray->AddReference(pDoc, pEndStream);
+      } else if (pDirectObj->IsStream()) {
+        pContentArray = new CPDF_Array();
+        pContentArray->AddReference(pDoc, pStream->GetObjNum());
+        pContentArray->AddReference(pDoc, pDirectObj->GetObjNum());
+        pContentArray->AddReference(pDoc, pEndStream);
+        pPageDic->SetAtReference("Contents", pDoc,
+                                 pDoc->AddIndirectObject(pContentArray));
+      }
+    }
+  }
+
+  // Need to transform the patterns as well.
+  CPDF_Dictionary* pRes = pPageDic->GetDict(FX_BSTRC("Resources"));
+  if (pRes) {
+    CPDF_Dictionary* pPattenDict = pRes->GetDict(FX_BSTRC("Pattern"));
+    if (pPattenDict) {
+      FX_POSITION pos = pPattenDict->GetStartPos();
+      while (pos) {
+        CPDF_Dictionary* pDict = nullptr;
+        CFX_ByteString key;
+        CPDF_Object* pObj = pPattenDict->GetNextElement(pos, key);
+        if (pObj->IsReference())
+          pObj = pObj->GetDirect();
+
+        if (pObj->IsDictionary())
+          pDict = pObj->AsDictionary();
+        else if (CPDF_Stream* pStream = pObj->AsStream())
+          pDict = pStream->GetDict();
+        else
+          continue;
+
+        CFX_AffineMatrix m = pDict->GetMatrix(FX_BSTRC("Matrix"));
+        CFX_AffineMatrix t = *(CFX_AffineMatrix*)matrix;
+        m.Concat(t);
+        pDict->SetAtMatrix(FX_BSTRC("Matrix"), m);
+      }
+    }
+  }
+
+  return TRUE;
 }
 
+DLLEXPORT void STDCALL
+FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
+                              double a,
+                              double b,
+                              double c,
+                              double d,
+                              double e,
+                              double f) {
+  CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
+  if (pPageObj == NULL)
+    return;
+  CFX_AffineMatrix matrix((FX_FLOAT)a, (FX_FLOAT)b, (FX_FLOAT)c, (FX_FLOAT)d,
+                          (FX_FLOAT)e, (FX_FLOAT)f);
 
-DLLEXPORT FPDF_CLIPPATH STDCALL FPDF_CreateClipPath(float left, float bottom, float right, float top)
-{
-       CPDF_ClipPath* pNewClipPath =  FX_NEW CPDF_ClipPath();
-       pNewClipPath->GetModify();
-       CPDF_Path Path;
-       Path.GetModify();
-       Path.AppendRect(left, bottom, right, top);
-       pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, FALSE);
-       return pNewClipPath;
+  // Special treatment to shading object, because the ClipPath for shading
+  // object is already transformed.
+  if (pPageObj->m_Type != PDFPAGE_SHADING)
+    pPageObj->TransformClipPath(matrix);
+  pPageObj->TransformGeneralState(matrix);
 }
 
-DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath)
-{
-       if(clipPath)
-               delete (CPDF_ClipPath*)clipPath;
+DLLEXPORT FPDF_CLIPPATH STDCALL FPDF_CreateClipPath(float left,
+                                                    float bottom,
+                                                    float right,
+                                                    float top) {
+  CPDF_ClipPath* pNewClipPath = new CPDF_ClipPath();
+  pNewClipPath->GetModify();
+  CPDF_Path Path;
+  Path.GetModify();
+  Path.AppendRect(left, bottom, right, top);
+  pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, FALSE);
+  return pNewClipPath;
 }
 
-void OutputPath(CFX_ByteTextBuf& buf, CPDF_Path path)
-{
-       const CFX_PathData* pPathData = path;
-       if (pPathData == NULL) return;
-       
-       FX_PATHPOINT* pPoints = pPathData->GetPoints();
-       
-       if (path.IsRect()) {
-               buf << (pPoints[0].m_PointX) << " " << (pPoints[0].m_PointY) << " " 
-                       << (pPoints[2].m_PointX - pPoints[0].m_PointX) << " " 
-                       << (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re\n";
-               return;
-       }
-       
-       CFX_ByteString temp;
-       for (int i = 0; i < pPathData->GetPointCount(); i ++) {
-               buf << (pPoints[i].m_PointX) << " " << (pPoints[i].m_PointY);
-               int point_type = pPoints[i].m_Flag & FXPT_TYPE;
-               if (point_type == FXPT_MOVETO)
-                       buf << " m\n";
-               else if (point_type == FXPT_BEZIERTO) {
-                       buf << " " << (pPoints[i+1].m_PointX) << " " << (pPoints[i+1].m_PointY) << " " << 
-                               (pPoints[i+2].m_PointX) << " " << (pPoints[i+2].m_PointY);
-                       if (pPoints[i+2].m_Flag & FXPT_CLOSEFIGURE)
-                               buf << " c h\n";
-                       else
-                               buf << " c\n";
-                       i += 2;
-               } else if (point_type == FXPT_LINETO) {
-                       if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE)
-                               buf << " l h\n";
-                       else
-                               buf << " l\n";
-               }
-       }
+DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
+  delete (CPDF_ClipPath*)clipPath;
 }
 
-DLLEXPORT void STDCALL FPDFPage_InsertClipPath(FPDF_PAGE page,FPDF_CLIPPATH clipPath)
-{
-       if(!page)
-               return;
-       CPDF_Page* pPage = (CPDF_Page*)page;
-       CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
-       CPDF_Object* pContentObj = pPageDic ? pPageDic->GetElement("Contents") : NULL;
-       if(!pContentObj)
-               pContentObj = pPageDic ? pPageDic->GetArray("Contents") : NULL;
-       if(!pContentObj)
-               return;
-
-       CFX_ByteTextBuf strClip;
-       CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
-       FX_DWORD i;
-       for (i = 0; i < pClipPath->GetPathCount(); i ++) {
-               CPDF_Path path = pClipPath->GetPath(i);
-               int iClipType = pClipPath->GetClipType(i);
-               if (path.GetPointCount() == 0) {
-                       // Empty clipping (totally clipped out)
-                       strClip << "0 0 m W n ";
-               } else {
-                       OutputPath(strClip, path);
-                       if (iClipType == FXFILL_WINDING)
-                               strClip << "W n\n";
-                       else
-                               strClip << "W* n\n";
-               }
-       }
-       CPDF_Dictionary* pDic = FX_NEW CPDF_Dictionary;
-       CPDF_Stream* pStream = FX_NEW CPDF_Stream(NULL,0, pDic);
-       pStream->SetData(strClip.GetBuffer(), strClip.GetSize(), FALSE, FALSE);
-       CPDF_Document* pDoc = pPage->m_pDocument;
-       if(!pDoc)
-               return;
-       pDoc->AddIndirectObject(pStream);
-       
-       CPDF_Array* pContentArray = NULL;
-       if (pContentObj && pContentObj->GetType() == PDFOBJ_ARRAY)
-       {
-               pContentArray = (CPDF_Array*)pContentObj;
-               CPDF_Reference* pRef = FX_NEW CPDF_Reference(pDoc, pStream->GetObjNum());
-               pContentArray->InsertAt(0, pRef);
-               
-       }
-       else if(pContentObj && pContentObj->GetType() == PDFOBJ_REFERENCE)
-       {
-               CPDF_Reference* pReference = (CPDF_Reference*)pContentObj;
-               CPDF_Object* pDirectObj = pReference->GetDirect();
-               if(pDirectObj != NULL)
-               {
-                       if(pDirectObj->GetType() == PDFOBJ_ARRAY)
-                       {
-                               pContentArray = (CPDF_Array*)pDirectObj;
-                               CPDF_Reference* pRef = FX_NEW CPDF_Reference(pDoc, pStream->GetObjNum());
-                               pContentArray->InsertAt(0, pRef);
-                               
-                       }
-                       else if(pDirectObj->GetType() == PDFOBJ_STREAM)
-                       {
-                               pContentArray = FX_NEW CPDF_Array();
-                               pContentArray->AddReference(pDoc,pStream->GetObjNum());
-                               pContentArray->AddReference(pDoc,pDirectObj->GetObjNum());
-                               pPageDic->SetAtReference("Contents", pDoc, pDoc->AddIndirectObject(pContentArray));
-                       }
-               }
-       }       
+void OutputPath(CFX_ByteTextBuf& buf, CPDF_Path path) {
+  const CFX_PathData* pPathData = path;
+  if (pPathData == NULL)
+    return;
+
+  FX_PATHPOINT* pPoints = pPathData->GetPoints();
+
+  if (path.IsRect()) {
+    buf << (pPoints[0].m_PointX) << " " << (pPoints[0].m_PointY) << " "
+        << (pPoints[2].m_PointX - pPoints[0].m_PointX) << " "
+        << (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re\n";
+    return;
+  }
+
+  CFX_ByteString temp;
+  for (int i = 0; i < pPathData->GetPointCount(); i++) {
+    buf << (pPoints[i].m_PointX) << " " << (pPoints[i].m_PointY);
+    int point_type = pPoints[i].m_Flag & FXPT_TYPE;
+    if (point_type == FXPT_MOVETO)
+      buf << " m\n";
+    else if (point_type == FXPT_BEZIERTO) {
+      buf << " " << (pPoints[i + 1].m_PointX) << " "
+          << (pPoints[i + 1].m_PointY) << " " << (pPoints[i + 2].m_PointX)
+          << " " << (pPoints[i + 2].m_PointY);
+      if (pPoints[i + 2].m_Flag & FXPT_CLOSEFIGURE)
+        buf << " c h\n";
+      else
+        buf << " c\n";
+      i += 2;
+    } else if (point_type == FXPT_LINETO) {
+      if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE)
+        buf << " l h\n";
+      else
+        buf << " l\n";
+    }
+  }
 }
 
+DLLEXPORT void STDCALL FPDFPage_InsertClipPath(FPDF_PAGE page,
+                                               FPDF_CLIPPATH clipPath) {
+  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
+  if (!pPage)
+    return;
+
+  CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
+  CPDF_Object* pContentObj =
+      pPageDic ? pPageDic->GetElement("Contents") : nullptr;
+  if (!pContentObj)
+    pContentObj = pPageDic ? pPageDic->GetArray("Contents") : nullptr;
+  if (!pContentObj)
+    return;
+
+  CFX_ByteTextBuf strClip;
+  CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
+  FX_DWORD i;
+  for (i = 0; i < pClipPath->GetPathCount(); i++) {
+    CPDF_Path path = pClipPath->GetPath(i);
+    int iClipType = pClipPath->GetClipType(i);
+    if (path.GetPointCount() == 0) {
+      // Empty clipping (totally clipped out)
+      strClip << "0 0 m W n ";
+    } else {
+      OutputPath(strClip, path);
+      if (iClipType == FXFILL_WINDING)
+        strClip << "W n\n";
+      else
+        strClip << "W* n\n";
+    }
+  }
+  CPDF_Dictionary* pDic = new CPDF_Dictionary;
+  CPDF_Stream* pStream = new CPDF_Stream(nullptr, 0, pDic);
+  pStream->SetData(strClip.GetBuffer(), strClip.GetSize(), FALSE, FALSE);
+  CPDF_Document* pDoc = pPage->m_pDocument;
+  if (!pDoc)
+    return;
+  pDoc->AddIndirectObject(pStream);
+
+  CPDF_Array* pContentArray = nullptr;
+  if (CPDF_Array* pArray = ToArray(pContentObj)) {
+    pContentArray = pArray;
+    CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
+    pContentArray->InsertAt(0, pRef);
+  } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
+    CPDF_Object* pDirectObj = pReference->GetDirect();
+    if (pDirectObj) {
+      if (CPDF_Array* pArray = pDirectObj->AsArray()) {
+        pContentArray = pArray;
+        CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
+        pContentArray->InsertAt(0, pRef);
+      } else if (pDirectObj->IsStream()) {
+        pContentArray = new CPDF_Array();
+        pContentArray->AddReference(pDoc, pStream->GetObjNum());
+        pContentArray->AddReference(pDoc, pDirectObj->GetObjNum());
+        pPageDic->SetAtReference("Contents", pDoc,
+                                 pDoc->AddIndirectObject(pContentArray));
+      }
+    }
+  }
+}