summaryrefslogtreecommitdiff
path: root/core/fpdfdoc
diff options
context:
space:
mode:
authorDan Sinclair <dsinclair@chromium.org>2016-03-14 13:35:12 -0400
committerDan Sinclair <dsinclair@chromium.org>2016-03-14 13:35:12 -0400
commit764ec513eecbebd12781bcc96ce81ed5e736ee92 (patch)
tree12763fde4be1f10ea1183d92185917b2b587e00b /core/fpdfdoc
parent97da97662417085774f75c26e535c6fbe70266ae (diff)
downloadpdfium-764ec513eecbebd12781bcc96ce81ed5e736ee92.tar.xz
Move core/src/ up to core/.
This CL moves the core/src/ files up to core/ and fixes up the include guards, includes and build files. R=tsepez@chromium.org Review URL: https://codereview.chromium.org/1800523005 .
Diffstat (limited to 'core/fpdfdoc')
-rw-r--r--core/fpdfdoc/doc_action.cpp295
-rw-r--r--core/fpdfdoc/doc_annot.cpp350
-rw-r--r--core/fpdfdoc/doc_ap.cpp936
-rw-r--r--core/fpdfdoc/doc_basic.cpp510
-rw-r--r--core/fpdfdoc/doc_basic_unittest.cpp169
-rw-r--r--core/fpdfdoc/doc_bookmark.cpp95
-rw-r--r--core/fpdfdoc/doc_form.cpp1207
-rw-r--r--core/fpdfdoc/doc_formcontrol.cpp432
-rw-r--r--core/fpdfdoc/doc_formfield.cpp1097
-rw-r--r--core/fpdfdoc/doc_link.cpp92
-rw-r--r--core/fpdfdoc/doc_metadata.cpp29
-rw-r--r--core/fpdfdoc/doc_ocg.cpp284
-rw-r--r--core/fpdfdoc/doc_tagged.cpp440
-rw-r--r--core/fpdfdoc/doc_utils.cpp753
-rw-r--r--core/fpdfdoc/doc_utils.h80
-rw-r--r--core/fpdfdoc/doc_viewerPreferences.cpp54
-rw-r--r--core/fpdfdoc/doc_vt.cpp1825
-rw-r--r--core/fpdfdoc/doc_vtmodule.cpp16
-rw-r--r--core/fpdfdoc/pdf_vt.h554
-rw-r--r--core/fpdfdoc/tagged_int.h106
20 files changed, 9324 insertions, 0 deletions
diff --git a/core/fpdfdoc/doc_action.cpp b/core/fpdfdoc/doc_action.cpp
new file mode 100644
index 0000000000..0af219e47e
--- /dev/null
+++ b/core/fpdfdoc/doc_action.cpp
@@ -0,0 +1,295 @@
+// 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 <vector>
+
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+CPDF_Dest CPDF_Action::GetDest(CPDF_Document* pDoc) const {
+ if (!m_pDict) {
+ return CPDF_Dest();
+ }
+ CFX_ByteString type = m_pDict->GetStringBy("S");
+ if (type != "GoTo" && type != "GoToR") {
+ return CPDF_Dest();
+ }
+ CPDF_Object* pDest = m_pDict->GetElementValue("D");
+ if (!pDest) {
+ return CPDF_Dest();
+ }
+ if (pDest->IsString() || pDest->IsName()) {
+ CPDF_NameTree name_tree(pDoc, "Dests");
+ CFX_ByteStringC name = pDest->GetString();
+ return CPDF_Dest(name_tree.LookupNamedDest(pDoc, name));
+ }
+ if (CPDF_Array* pArray = pDest->AsArray())
+ return CPDF_Dest(pArray);
+ return CPDF_Dest();
+}
+const FX_CHAR* g_sATypes[] = {
+ "Unknown", "GoTo", "GoToR", "GoToE", "Launch",
+ "Thread", "URI", "Sound", "Movie", "Hide",
+ "Named", "SubmitForm", "ResetForm", "ImportData", "JavaScript",
+ "SetOCGState", "Rendition", "Trans", "GoTo3DView", ""};
+CPDF_Action::ActionType CPDF_Action::GetType() const {
+ ActionType eType = Unknown;
+ if (m_pDict) {
+ CFX_ByteString csType = m_pDict->GetStringBy("S");
+ if (!csType.IsEmpty()) {
+ int i = 0;
+ while (g_sATypes[i][0] != '\0') {
+ if (csType == g_sATypes[i]) {
+ return (ActionType)i;
+ }
+ i++;
+ }
+ }
+ }
+ return eType;
+}
+CFX_WideString CPDF_Action::GetFilePath() const {
+ CFX_ByteString type = m_pDict->GetStringBy("S");
+ if (type != "GoToR" && type != "Launch" && type != "SubmitForm" &&
+ type != "ImportData") {
+ return CFX_WideString();
+ }
+ CPDF_Object* pFile = m_pDict->GetElementValue("F");
+ CFX_WideString path;
+ if (!pFile) {
+ if (type == "Launch") {
+ CPDF_Dictionary* pWinDict = m_pDict->GetDictBy("Win");
+ if (pWinDict) {
+ return CFX_WideString::FromLocal(pWinDict->GetStringBy("F"));
+ }
+ }
+ return path;
+ }
+ CPDF_FileSpec filespec(pFile);
+ filespec.GetFileName(&path);
+ return path;
+}
+CFX_ByteString CPDF_Action::GetURI(CPDF_Document* pDoc) const {
+ CFX_ByteString csURI;
+ if (!m_pDict) {
+ return csURI;
+ }
+ if (m_pDict->GetStringBy("S") != "URI") {
+ return csURI;
+ }
+ csURI = m_pDict->GetStringBy("URI");
+ CPDF_Dictionary* pRoot = pDoc->GetRoot();
+ CPDF_Dictionary* pURI = pRoot->GetDictBy("URI");
+ if (pURI) {
+ if (csURI.Find(":", 0) < 1) {
+ csURI = pURI->GetStringBy("Base") + csURI;
+ }
+ }
+ return csURI;
+}
+FX_DWORD CPDF_ActionFields::GetFieldsCount() const {
+ if (!m_pAction) {
+ return 0;
+ }
+ CPDF_Dictionary* pDict = m_pAction->GetDict();
+ if (!pDict) {
+ return 0;
+ }
+ CFX_ByteString csType = pDict->GetStringBy("S");
+ CPDF_Object* pFields = NULL;
+ if (csType == "Hide") {
+ pFields = pDict->GetElementValue("T");
+ } else {
+ pFields = pDict->GetArrayBy("Fields");
+ }
+ if (!pFields)
+ return 0;
+ if (pFields->IsDictionary())
+ return 1;
+ if (pFields->IsString())
+ return 1;
+ if (CPDF_Array* pArray = pFields->AsArray())
+ return pArray->GetCount();
+ return 0;
+}
+
+std::vector<CPDF_Object*> CPDF_ActionFields::GetAllFields() const {
+ std::vector<CPDF_Object*> fields;
+ if (!m_pAction)
+ return fields;
+
+ CPDF_Dictionary* pDict = m_pAction->GetDict();
+ if (!pDict)
+ return fields;
+
+ CFX_ByteString csType = pDict->GetStringBy("S");
+ CPDF_Object* pFields;
+ if (csType == "Hide")
+ pFields = pDict->GetElementValue("T");
+ else
+ pFields = pDict->GetArrayBy("Fields");
+ if (!pFields)
+ return fields;
+
+ if (pFields->IsDictionary() || pFields->IsString()) {
+ fields.push_back(pFields);
+ } else if (CPDF_Array* pArray = pFields->AsArray()) {
+ FX_DWORD iCount = pArray->GetCount();
+ for (FX_DWORD i = 0; i < iCount; ++i) {
+ CPDF_Object* pObj = pArray->GetElementValue(i);
+ if (pObj) {
+ fields.push_back(pObj);
+ }
+ }
+ }
+ return fields;
+}
+
+CPDF_Object* CPDF_ActionFields::GetField(FX_DWORD iIndex) const {
+ if (!m_pAction) {
+ return NULL;
+ }
+ CPDF_Dictionary* pDict = m_pAction->GetDict();
+ if (!pDict) {
+ return NULL;
+ }
+ CFX_ByteString csType = pDict->GetStringBy("S");
+ CPDF_Object* pFields = NULL;
+ if (csType == "Hide") {
+ pFields = pDict->GetElementValue("T");
+ } else {
+ pFields = pDict->GetArrayBy("Fields");
+ }
+ if (!pFields) {
+ return NULL;
+ }
+ CPDF_Object* pFindObj = NULL;
+ if (pFields->IsDictionary() || pFields->IsString()) {
+ if (iIndex == 0)
+ pFindObj = pFields;
+ } else if (CPDF_Array* pArray = pFields->AsArray()) {
+ pFindObj = pArray->GetElementValue(iIndex);
+ }
+ return pFindObj;
+}
+
+CFX_WideString CPDF_Action::GetJavaScript() const {
+ CFX_WideString csJS;
+ if (!m_pDict) {
+ return csJS;
+ }
+ CPDF_Object* pJS = m_pDict->GetElementValue("JS");
+ return pJS ? pJS->GetUnicodeText() : csJS;
+}
+CPDF_Dictionary* CPDF_Action::GetAnnot() const {
+ if (!m_pDict) {
+ return nullptr;
+ }
+ CFX_ByteString csType = m_pDict->GetStringBy("S");
+ if (csType == "Rendition") {
+ return m_pDict->GetDictBy("AN");
+ }
+ if (csType == "Movie") {
+ return m_pDict->GetDictBy("Annotation");
+ }
+ return nullptr;
+}
+int32_t CPDF_Action::GetOperationType() const {
+ if (!m_pDict) {
+ return 0;
+ }
+ CFX_ByteString csType = m_pDict->GetStringBy("S");
+ if (csType == "Rendition") {
+ return m_pDict->GetIntegerBy("OP");
+ }
+ if (csType == "Movie") {
+ CFX_ByteString csOP = m_pDict->GetStringBy("Operation");
+ if (csOP == "Play") {
+ return 0;
+ }
+ if (csOP == "Stop") {
+ return 1;
+ }
+ if (csOP == "Pause") {
+ return 2;
+ }
+ if (csOP == "Resume") {
+ return 3;
+ }
+ }
+ return 0;
+}
+FX_DWORD CPDF_Action::GetSubActionsCount() const {
+ if (!m_pDict || !m_pDict->KeyExist("Next"))
+ return 0;
+
+ CPDF_Object* pNext = m_pDict->GetElementValue("Next");
+ if (!pNext)
+ return 0;
+ if (pNext->IsDictionary())
+ return 1;
+ if (CPDF_Array* pArray = pNext->AsArray())
+ return pArray->GetCount();
+ return 0;
+}
+CPDF_Action CPDF_Action::GetSubAction(FX_DWORD iIndex) const {
+ if (!m_pDict || !m_pDict->KeyExist("Next")) {
+ return CPDF_Action();
+ }
+ CPDF_Object* pNext = m_pDict->GetElementValue("Next");
+ if (CPDF_Dictionary* pDict = ToDictionary(pNext)) {
+ if (iIndex == 0)
+ return CPDF_Action(pDict);
+ } else if (CPDF_Array* pArray = ToArray(pNext)) {
+ return CPDF_Action(pArray->GetDictAt(iIndex));
+ }
+ return CPDF_Action();
+}
+const FX_CHAR* g_sAATypes[] = {"E", "X", "D", "U", "Fo", "Bl", "PO", "PC",
+ "PV", "PI", "O", "C", "K", "F", "V", "C",
+ "WC", "WS", "DS", "WP", "DP", ""};
+FX_BOOL CPDF_AAction::ActionExist(AActionType eType) const {
+ return m_pDict && m_pDict->KeyExist(g_sAATypes[(int)eType]);
+}
+CPDF_Action CPDF_AAction::GetAction(AActionType eType) const {
+ if (!m_pDict) {
+ return CPDF_Action();
+ }
+ return CPDF_Action(m_pDict->GetDictBy(g_sAATypes[(int)eType]));
+}
+
+CPDF_DocJSActions::CPDF_DocJSActions(CPDF_Document* pDoc) : m_pDocument(pDoc) {}
+
+int CPDF_DocJSActions::CountJSActions() const {
+ ASSERT(m_pDocument);
+ CPDF_NameTree name_tree(m_pDocument, "JavaScript");
+ return name_tree.GetCount();
+}
+CPDF_Action CPDF_DocJSActions::GetJSAction(int index,
+ CFX_ByteString& csName) const {
+ ASSERT(m_pDocument);
+ CPDF_NameTree name_tree(m_pDocument, "JavaScript");
+ CPDF_Object* pAction = name_tree.LookupValue(index, csName);
+ if (!ToDictionary(pAction)) {
+ return CPDF_Action();
+ }
+ return CPDF_Action(pAction->GetDict());
+}
+CPDF_Action CPDF_DocJSActions::GetJSAction(const CFX_ByteString& csName) const {
+ ASSERT(m_pDocument);
+ CPDF_NameTree name_tree(m_pDocument, "JavaScript");
+ CPDF_Object* pAction = name_tree.LookupValue(csName);
+ if (!ToDictionary(pAction)) {
+ return CPDF_Action();
+ }
+ return CPDF_Action(pAction->GetDict());
+}
+int CPDF_DocJSActions::FindJSAction(const CFX_ByteString& csName) const {
+ ASSERT(m_pDocument);
+ CPDF_NameTree name_tree(m_pDocument, "JavaScript");
+ return name_tree.GetIndex(csName);
+}
diff --git a/core/fpdfdoc/doc_annot.cpp b/core/fpdfdoc/doc_annot.cpp
new file mode 100644
index 0000000000..b17347cce3
--- /dev/null
+++ b/core/fpdfdoc/doc_annot.cpp
@@ -0,0 +1,350 @@
+// 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 "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_reference.h"
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage)
+ : m_pDocument(pPage->m_pDocument) {
+ if (!pPage->m_pFormDict)
+ return;
+
+ CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayBy("Annots");
+ if (!pAnnots)
+ return;
+
+ CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
+ CPDF_Dictionary* pAcroForm = pRoot->GetDictBy("AcroForm");
+ FX_BOOL bRegenerateAP =
+ pAcroForm && pAcroForm->GetBooleanBy("NeedAppearances");
+ for (FX_DWORD i = 0; i < pAnnots->GetCount(); ++i) {
+ CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetElementValue(i));
+ if (!pDict)
+ continue;
+
+ FX_DWORD dwObjNum = pDict->GetObjNum();
+ if (dwObjNum == 0) {
+ dwObjNum = m_pDocument->AddIndirectObject(pDict);
+ CPDF_Reference* pAction = new CPDF_Reference(m_pDocument, dwObjNum);
+ pAnnots->InsertAt(i, pAction);
+ pAnnots->RemoveAt(i + 1);
+ pDict = pAnnots->GetDictAt(i);
+ }
+ m_AnnotList.push_back(new CPDF_Annot(pDict, this));
+ if (bRegenerateAP && pDict->GetConstStringBy("Subtype") == "Widget" &&
+ CPDF_InterForm::UpdatingAPEnabled()) {
+ FPDF_GenerateAP(m_pDocument, pDict);
+ }
+ }
+}
+
+CPDF_AnnotList::~CPDF_AnnotList() {
+ for (CPDF_Annot* annot : m_AnnotList)
+ delete annot;
+}
+
+void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage,
+ CFX_RenderDevice* pDevice,
+ CPDF_RenderContext* pContext,
+ FX_BOOL bPrinting,
+ CFX_Matrix* pMatrix,
+ FX_BOOL bWidgetPass,
+ CPDF_RenderOptions* pOptions,
+ FX_RECT* clip_rect) {
+ for (CPDF_Annot* pAnnot : m_AnnotList) {
+ bool bWidget = pAnnot->GetSubType() == "Widget";
+ if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget))
+ continue;
+
+ FX_DWORD annot_flags = pAnnot->GetFlags();
+ if (annot_flags & ANNOTFLAG_HIDDEN)
+ continue;
+
+ if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0)
+ continue;
+
+ if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW))
+ continue;
+
+ if (pOptions) {
+ IPDF_OCContext* pOCContext = pOptions->m_pOCContext;
+ CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
+ if (pOCContext && pAnnotDict &&
+ !pOCContext->CheckOCGVisible(pAnnotDict->GetDictBy("OC"))) {
+ continue;
+ }
+ }
+ CFX_FloatRect annot_rect_f;
+ pAnnot->GetRect(annot_rect_f);
+ CFX_Matrix matrix = *pMatrix;
+ if (clip_rect) {
+ annot_rect_f.Transform(&matrix);
+ FX_RECT annot_rect = annot_rect_f.GetOutterRect();
+ annot_rect.Intersect(*clip_rect);
+ if (annot_rect.IsEmpty()) {
+ continue;
+ }
+ }
+ if (pContext) {
+ pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal);
+ } else if (!pAnnot->DrawAppearance(pPage, pDevice, &matrix,
+ CPDF_Annot::Normal, pOptions)) {
+ pAnnot->DrawBorder(pDevice, &matrix, pOptions);
+ }
+ }
+}
+
+void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage,
+ CFX_RenderDevice* pDevice,
+ CPDF_RenderContext* pContext,
+ FX_BOOL bPrinting,
+ CFX_Matrix* pUser2Device,
+ FX_DWORD dwAnnotFlags,
+ CPDF_RenderOptions* pOptions,
+ FX_RECT* pClipRect) {
+ if (dwAnnotFlags & 0x01) {
+ DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, FALSE,
+ pOptions, pClipRect);
+ }
+ if (dwAnnotFlags & 0x02) {
+ DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, TRUE,
+ pOptions, pClipRect);
+ }
+}
+
+CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_AnnotList* pList)
+ : m_pAnnotDict(pDict),
+ m_pList(pList),
+ m_sSubtype(m_pAnnotDict->GetConstStringBy("Subtype")) {}
+CPDF_Annot::~CPDF_Annot() {
+ ClearCachedAP();
+}
+void CPDF_Annot::ClearCachedAP() {
+ for (const auto& pair : m_APMap) {
+ delete pair.second;
+ }
+ m_APMap.clear();
+}
+CFX_ByteString CPDF_Annot::GetSubType() const {
+ return m_sSubtype;
+}
+
+void CPDF_Annot::GetRect(CFX_FloatRect& rect) const {
+ if (!m_pAnnotDict) {
+ return;
+ }
+ rect = m_pAnnotDict->GetRectBy("Rect");
+ rect.Normalize();
+}
+
+FX_DWORD CPDF_Annot::GetFlags() const {
+ return m_pAnnotDict->GetIntegerBy("F");
+}
+
+CPDF_Stream* FPDFDOC_GetAnnotAP(CPDF_Dictionary* pAnnotDict,
+ CPDF_Annot::AppearanceMode mode) {
+ CPDF_Dictionary* pAP = pAnnotDict->GetDictBy("AP");
+ if (!pAP) {
+ return NULL;
+ }
+ const FX_CHAR* ap_entry = "N";
+ if (mode == CPDF_Annot::Down)
+ ap_entry = "D";
+ else if (mode == CPDF_Annot::Rollover)
+ ap_entry = "R";
+ if (!pAP->KeyExist(ap_entry))
+ ap_entry = "N";
+
+ CPDF_Object* psub = pAP->GetElementValue(ap_entry);
+ if (!psub)
+ return nullptr;
+ if (CPDF_Stream* pStream = psub->AsStream())
+ return pStream;
+
+ if (CPDF_Dictionary* pDict = psub->AsDictionary()) {
+ CFX_ByteString as = pAnnotDict->GetStringBy("AS");
+ if (as.IsEmpty()) {
+ CFX_ByteString value = pAnnotDict->GetStringBy("V");
+ if (value.IsEmpty()) {
+ CPDF_Dictionary* pDict = pAnnotDict->GetDictBy("Parent");
+ value = pDict ? pDict->GetStringBy("V") : CFX_ByteString();
+ }
+ if (value.IsEmpty() || !pDict->KeyExist(value))
+ as = "Off";
+ else
+ as = value;
+ }
+ return pDict->GetStreamBy(as);
+ }
+ return nullptr;
+}
+
+CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
+ CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pAnnotDict, mode);
+ if (!pStream)
+ return nullptr;
+
+ auto it = m_APMap.find(pStream);
+ if (it != m_APMap.end())
+ return it->second;
+
+ CPDF_Form* pNewForm =
+ new CPDF_Form(m_pList->GetDocument(), pPage->m_pResources, pStream);
+ pNewForm->ParseContent(nullptr, nullptr, nullptr, nullptr);
+ m_APMap[pStream] = pNewForm;
+ return pNewForm;
+}
+
+static CPDF_Form* FPDFDOC_Annot_GetMatrix(const CPDF_Page* pPage,
+ CPDF_Annot* pAnnot,
+ CPDF_Annot::AppearanceMode mode,
+ const CFX_Matrix* pUser2Device,
+ CFX_Matrix& matrix) {
+ CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
+ if (!pForm) {
+ return NULL;
+ }
+ CFX_FloatRect form_bbox = pForm->m_pFormDict->GetRectBy("BBox");
+ CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrixBy("Matrix");
+ form_matrix.TransformRect(form_bbox);
+ CFX_FloatRect arect;
+ pAnnot->GetRect(arect);
+ matrix.MatchRect(arect, form_bbox);
+ matrix.Concat(*pUser2Device);
+ return pForm;
+}
+FX_BOOL CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
+ CFX_RenderDevice* pDevice,
+ const CFX_Matrix* pUser2Device,
+ AppearanceMode mode,
+ const CPDF_RenderOptions* pOptions) {
+ CFX_Matrix matrix;
+ CPDF_Form* pForm =
+ FPDFDOC_Annot_GetMatrix(pPage, this, mode, pUser2Device, matrix);
+ if (!pForm) {
+ return FALSE;
+ }
+ CPDF_RenderContext context(pPage);
+ context.AppendLayer(pForm, &matrix);
+ context.Render(pDevice, pOptions, nullptr);
+ return TRUE;
+}
+FX_BOOL CPDF_Annot::DrawInContext(const CPDF_Page* pPage,
+ CPDF_RenderContext* pContext,
+ const CFX_Matrix* pUser2Device,
+ AppearanceMode mode) {
+ CFX_Matrix matrix;
+ CPDF_Form* pForm =
+ FPDFDOC_Annot_GetMatrix(pPage, this, mode, pUser2Device, matrix);
+ if (!pForm) {
+ return FALSE;
+ }
+ pContext->AppendLayer(pForm, &matrix);
+ return TRUE;
+}
+void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice,
+ const CFX_Matrix* pUser2Device,
+ const CPDF_RenderOptions* pOptions) {
+ if (GetSubType() == "Popup") {
+ return;
+ }
+ FX_DWORD annot_flags = GetFlags();
+ if (annot_flags & ANNOTFLAG_HIDDEN) {
+ return;
+ }
+ bool bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER ||
+ (pOptions && (pOptions->m_Flags & RENDER_PRINTPREVIEW));
+ if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
+ return;
+ }
+ if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
+ return;
+ }
+ CPDF_Dictionary* pBS = m_pAnnotDict->GetDictBy("BS");
+ char style_char;
+ FX_FLOAT width;
+ CPDF_Array* pDashArray = NULL;
+ if (!pBS) {
+ CPDF_Array* pBorderArray = m_pAnnotDict->GetArrayBy("Border");
+ style_char = 'S';
+ if (pBorderArray) {
+ width = pBorderArray->GetNumberAt(2);
+ if (pBorderArray->GetCount() == 4) {
+ pDashArray = pBorderArray->GetArrayAt(3);
+ if (!pDashArray) {
+ return;
+ }
+ int nLen = pDashArray->GetCount();
+ int i = 0;
+ for (; i < nLen; ++i) {
+ CPDF_Object* pObj = pDashArray->GetElementValue(i);
+ if (pObj && pObj->GetInteger()) {
+ break;
+ }
+ }
+ if (i == nLen) {
+ return;
+ }
+ style_char = 'D';
+ }
+ } else {
+ width = 1;
+ }
+ } else {
+ CFX_ByteString style = pBS->GetStringBy("S");
+ pDashArray = pBS->GetArrayBy("D");
+ style_char = style[1];
+ width = pBS->GetNumberBy("W");
+ }
+ if (width <= 0) {
+ return;
+ }
+ CPDF_Array* pColor = m_pAnnotDict->GetArrayBy("C");
+ FX_DWORD argb = 0xff000000;
+ if (pColor) {
+ int R = (int32_t)(pColor->GetNumberAt(0) * 255);
+ int G = (int32_t)(pColor->GetNumberAt(1) * 255);
+ int B = (int32_t)(pColor->GetNumberAt(2) * 255);
+ argb = ArgbEncode(0xff, R, G, B);
+ }
+ CFX_GraphStateData graph_state;
+ graph_state.m_LineWidth = width;
+ if (style_char == 'D') {
+ if (pDashArray) {
+ FX_DWORD dash_count = pDashArray->GetCount();
+ if (dash_count % 2) {
+ dash_count++;
+ }
+ graph_state.m_DashArray = FX_Alloc(FX_FLOAT, dash_count);
+ graph_state.m_DashCount = dash_count;
+ FX_DWORD i;
+ for (i = 0; i < pDashArray->GetCount(); ++i) {
+ graph_state.m_DashArray[i] = pDashArray->GetNumberAt(i);
+ }
+ if (i < dash_count) {
+ graph_state.m_DashArray[i] = graph_state.m_DashArray[i - 1];
+ }
+ } else {
+ graph_state.m_DashArray = FX_Alloc(FX_FLOAT, 2);
+ graph_state.m_DashCount = 2;
+ graph_state.m_DashArray[0] = graph_state.m_DashArray[1] = 3 * 1.0f;
+ }
+ }
+ CFX_FloatRect rect;
+ GetRect(rect);
+ CFX_PathData path;
+ width /= 2;
+ path.AppendRect(rect.left + width, rect.bottom + width, rect.right - width,
+ rect.top - width);
+ int fill_type = 0;
+ if (pOptions && (pOptions->m_Flags & RENDER_NOPATHSMOOTH)) {
+ fill_type |= FXFILL_NOPATHSMOOTH;
+ }
+ pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
+}
diff --git a/core/fpdfdoc/doc_ap.cpp b/core/fpdfdoc/doc_ap.cpp
new file mode 100644
index 0000000000..d2400123d2
--- /dev/null
+++ b/core/fpdfdoc/doc_ap.cpp
@@ -0,0 +1,936 @@
+// 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 "core/fpdfdoc/doc_utils.h"
+#include "core/fpdfdoc/pdf_vt.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_simple_parser.h"
+#include "core/include/fpdfdoc/fpdf_ap.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+#include "core/include/fpdfdoc/fpdf_vt.h"
+
+#define PBS_SOLID 0
+#define PBS_DASH 1
+#define PBS_BEVELED 2
+#define PBS_INSET 3
+#define PBS_UNDERLINED 4
+
+FX_BOOL FPDF_GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
+ if (!pAnnotDict || pAnnotDict->GetConstStringBy("Subtype") != "Widget") {
+ return FALSE;
+ }
+ CFX_ByteString field_type = FPDF_GetFieldAttr(pAnnotDict, "FT")->GetString();
+ FX_DWORD flags = FPDF_GetFieldAttr(pAnnotDict, "Ff")
+ ? FPDF_GetFieldAttr(pAnnotDict, "Ff")->GetInteger()
+ : 0;
+ if (field_type == "Tx") {
+ return CPVT_GenerateAP::GenerateTextFieldAP(pDoc, pAnnotDict);
+ }
+ if (field_type == "Ch") {
+ return (flags & (1 << 17))
+ ? CPVT_GenerateAP::GenerateComboBoxAP(pDoc, pAnnotDict)
+ : CPVT_GenerateAP::GenerateListBoxAP(pDoc, pAnnotDict);
+ }
+ if (field_type == "Btn") {
+ if (!(flags & (1 << 16))) {
+ if (!pAnnotDict->KeyExist("AS")) {
+ if (CPDF_Dictionary* pParentDict = pAnnotDict->GetDictBy("Parent")) {
+ if (pParentDict->KeyExist("AS")) {
+ pAnnotDict->SetAtString("AS", pParentDict->GetStringBy("AS"));
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+class CPVT_FontMap : public IPVT_FontMap {
+ public:
+ CPVT_FontMap(CPDF_Document* pDoc,
+ CPDF_Dictionary* pResDict,
+ CPDF_Font* pDefFont,
+ const CFX_ByteString& sDefFontAlias);
+ ~CPVT_FontMap() override;
+
+ // IPVT_FontMap
+ CPDF_Font* GetPDFFont(int32_t nFontIndex) override;
+ CFX_ByteString GetPDFFontAlias(int32_t nFontIndex) override;
+
+ static void GetAnnotSysPDFFont(CPDF_Document* pDoc,
+ CPDF_Dictionary* pResDict,
+ CPDF_Font*& pSysFont,
+ CFX_ByteString& sSysFontAlias);
+
+ private:
+ CPDF_Document* m_pDocument;
+ CPDF_Dictionary* m_pResDict;
+ CPDF_Font* m_pDefFont;
+ CFX_ByteString m_sDefFontAlias;
+ CPDF_Font* m_pSysFont;
+ CFX_ByteString m_sSysFontAlias;
+};
+
+CPVT_FontMap::CPVT_FontMap(CPDF_Document* pDoc,
+ CPDF_Dictionary* pResDict,
+ CPDF_Font* pDefFont,
+ const CFX_ByteString& sDefFontAlias)
+ : m_pDocument(pDoc),
+ m_pResDict(pResDict),
+ m_pDefFont(pDefFont),
+ m_sDefFontAlias(sDefFontAlias),
+ m_pSysFont(NULL),
+ m_sSysFontAlias() {}
+CPVT_FontMap::~CPVT_FontMap() {}
+void CPVT_FontMap::GetAnnotSysPDFFont(CPDF_Document* pDoc,
+ CPDF_Dictionary* pResDict,
+ CPDF_Font*& pSysFont,
+ CFX_ByteString& sSysFontAlias) {
+ if (pDoc && pResDict) {
+ CFX_ByteString sFontAlias;
+ CPDF_Dictionary* pFormDict = pDoc->GetRoot()->GetDictBy("AcroForm");
+ if (CPDF_Font* pPDFFont =
+ AddNativeInterFormFont(pFormDict, pDoc, sSysFontAlias)) {
+ if (CPDF_Dictionary* pFontList = pResDict->GetDictBy("Font")) {
+ if (!pFontList->KeyExist(sSysFontAlias)) {
+ pFontList->SetAtReference(sSysFontAlias, pDoc,
+ pPDFFont->GetFontDict());
+ }
+ }
+ pSysFont = pPDFFont;
+ }
+ }
+}
+CPDF_Font* CPVT_FontMap::GetPDFFont(int32_t nFontIndex) {
+ switch (nFontIndex) {
+ case 0:
+ return m_pDefFont;
+ case 1:
+ if (!m_pSysFont) {
+ GetAnnotSysPDFFont(m_pDocument, m_pResDict, m_pSysFont,
+ m_sSysFontAlias);
+ }
+ return m_pSysFont;
+ }
+ return NULL;
+}
+CFX_ByteString CPVT_FontMap::GetPDFFontAlias(int32_t nFontIndex) {
+ switch (nFontIndex) {
+ case 0:
+ return m_sDefFontAlias;
+ case 1:
+ if (!m_pSysFont) {
+ GetAnnotSysPDFFont(m_pDocument, m_pResDict, m_pSysFont,
+ m_sSysFontAlias);
+ }
+ return m_sSysFontAlias;
+ }
+ return "";
+}
+CPVT_Provider::CPVT_Provider(IPVT_FontMap* pFontMap) : m_pFontMap(pFontMap) {
+ ASSERT(m_pFontMap);
+}
+CPVT_Provider::~CPVT_Provider() {}
+int32_t CPVT_Provider::GetCharWidth(int32_t nFontIndex,
+ FX_WORD word,
+ int32_t nWordStyle) {
+ if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) {
+ FX_DWORD charcode = pPDFFont->CharCodeFromUnicode(word);
+ if (charcode != CPDF_Font::kInvalidCharCode) {
+ return pPDFFont->GetCharWidthF(charcode);
+ }
+ }
+ return 0;
+}
+int32_t CPVT_Provider::GetTypeAscent(int32_t nFontIndex) {
+ if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) {
+ return pPDFFont->GetTypeAscent();
+ }
+ return 0;
+}
+int32_t CPVT_Provider::GetTypeDescent(int32_t nFontIndex) {
+ if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) {
+ return pPDFFont->GetTypeDescent();
+ }
+ return 0;
+}
+int32_t CPVT_Provider::GetWordFontIndex(FX_WORD word,
+ int32_t charset,
+ int32_t nFontIndex) {
+ if (CPDF_Font* pDefFont = m_pFontMap->GetPDFFont(0)) {
+ if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode) {
+ return 0;
+ }
+ }
+ if (CPDF_Font* pSysFont = m_pFontMap->GetPDFFont(1)) {
+ if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode) {
+ return 1;
+ }
+ }
+ return -1;
+}
+FX_BOOL CPVT_Provider::IsLatinWord(FX_WORD word) {
+ if ((word >= 0x61 && word <= 0x7A) || (word >= 0x41 && word <= 0x5A) ||
+ word == 0x2D || word == 0x27) {
+ return TRUE;
+ }
+ return FALSE;
+}
+int32_t CPVT_Provider::GetDefaultFontIndex() {
+ return 0;
+}
+
+static CFX_ByteString GetPDFWordString(IPVT_FontMap* pFontMap,
+ int32_t nFontIndex,
+ FX_WORD Word,
+ FX_WORD SubWord) {
+ CFX_ByteString sWord;
+ if (SubWord > 0) {
+ sWord.Format("%c", SubWord);
+ return sWord;
+ }
+
+ if (!pFontMap)
+ return sWord;
+
+ if (CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex)) {
+ if (pPDFFont->GetBaseFont().Compare("Symbol") == 0 ||
+ pPDFFont->GetBaseFont().Compare("ZapfDingbats") == 0) {
+ sWord.Format("%c", Word);
+ } else {
+ FX_DWORD dwCharCode = pPDFFont->CharCodeFromUnicode(Word);
+ if (dwCharCode != CPDF_Font::kInvalidCharCode) {
+ pPDFFont->AppendChar(sWord, dwCharCode);
+ }
+ }
+ }
+ return sWord;
+}
+
+static CFX_ByteString GetWordRenderString(const CFX_ByteString& strWords) {
+ if (strWords.GetLength() > 0) {
+ return PDF_EncodeString(strWords) + " Tj\n";
+ }
+ return "";
+}
+static CFX_ByteString GetFontSetString(IPVT_FontMap* pFontMap,
+ int32_t nFontIndex,
+ FX_FLOAT fFontSize) {
+ CFX_ByteTextBuf sRet;
+ if (pFontMap) {
+ CFX_ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
+ if (sFontAlias.GetLength() > 0 && fFontSize > 0) {
+ sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
+ }
+ }
+ return sRet.GetByteString();
+}
+static CPVT_Color ParseColor(const CFX_ByteString& str) {
+ CPDF_SimpleParser syntax(str);
+ if (syntax.FindTagParamFromStart("g", 1)) {
+ return CPVT_Color(CPVT_Color::kGray, FX_atof(syntax.GetWord()));
+ }
+ if (syntax.FindTagParamFromStart("rg", 3)) {
+ FX_FLOAT f1 = FX_atof(syntax.GetWord());
+ FX_FLOAT f2 = FX_atof(syntax.GetWord());
+ FX_FLOAT f3 = FX_atof(syntax.GetWord());
+ return CPVT_Color(CPVT_Color::kRGB, f1, f2, f3);
+ }
+ if (syntax.FindTagParamFromStart("k", 4)) {
+ FX_FLOAT f1 = FX_atof(syntax.GetWord());
+ FX_FLOAT f2 = FX_atof(syntax.GetWord());
+ FX_FLOAT f3 = FX_atof(syntax.GetWord());
+ FX_FLOAT f4 = FX_atof(syntax.GetWord());
+ return CPVT_Color(CPVT_Color::kCMYK, f1, f2, f3, f4);
+ }
+ return CPVT_Color(CPVT_Color::kTransparent);
+}
+static CPVT_Color ParseColor(const CPDF_Array& array) {
+ CPVT_Color rt;
+ switch (array.GetCount()) {
+ case 1:
+ rt = CPVT_Color(CPVT_Color::kGray, array.GetFloatAt(0));
+ break;
+ case 3:
+ rt = CPVT_Color(CPVT_Color::kRGB, array.GetFloatAt(0),
+ array.GetFloatAt(1), array.GetFloatAt(2));
+ break;
+ case 4:
+ rt = CPVT_Color(CPVT_Color::kCMYK, array.GetFloatAt(0),
+ array.GetFloatAt(1), array.GetFloatAt(2),
+ array.GetFloatAt(3));
+ break;
+ }
+ return rt;
+}
+static FX_BOOL GenerateWidgetAP(CPDF_Document* pDoc,
+ CPDF_Dictionary* pAnnotDict,
+ const int32_t& nWidgetType) {
+ CPDF_Dictionary* pFormDict = NULL;
+ if (CPDF_Dictionary* pRootDict = pDoc->GetRoot()) {
+ pFormDict = pRootDict->GetDictBy("AcroForm");
+ }
+ if (!pFormDict) {
+ return FALSE;
+ }
+ CFX_ByteString DA;
+ if (CPDF_Object* pDAObj = FPDF_GetFieldAttr(pAnnotDict, "DA")) {
+ DA = pDAObj->GetString();
+ }
+ if (DA.IsEmpty()) {
+ DA = pFormDict->GetStringBy("DA");
+ }
+ if (DA.IsEmpty()) {
+ return FALSE;
+ }
+ CPDF_SimpleParser syntax(DA);
+ syntax.FindTagParamFromStart("Tf", 2);
+ CFX_ByteString sFontName = syntax.GetWord();
+ sFontName = PDF_NameDecode(sFontName);
+ if (sFontName.IsEmpty()) {
+ return FALSE;
+ }
+ FX_FLOAT fFontSize = FX_atof(syntax.GetWord());
+ CPVT_Color crText = ParseColor(DA);
+ FX_BOOL bUseFormRes = FALSE;
+ CPDF_Dictionary* pFontDict = NULL;
+ CPDF_Dictionary* pDRDict = pAnnotDict->GetDictBy("DR");
+ if (!pDRDict) {
+ pDRDict = pFormDict->GetDictBy("DR");
+ bUseFormRes = TRUE;
+ }
+ CPDF_Dictionary* pDRFontDict = NULL;
+ if (pDRDict && (pDRFontDict = pDRDict->GetDictBy("Font"))) {
+ pFontDict = pDRFontDict->GetDictBy(sFontName.Mid(1));
+ if (!pFontDict && !bUseFormRes) {
+ pDRDict = pFormDict->GetDictBy("DR");
+ pDRFontDict = pDRDict->GetDictBy("Font");
+ if (pDRFontDict) {
+ pFontDict = pDRFontDict->GetDictBy(sFontName.Mid(1));
+ }
+ }
+ }
+ if (!pDRFontDict) {
+ return FALSE;
+ }
+ if (!pFontDict) {
+ pFontDict = new CPDF_Dictionary;
+ pFontDict->SetAtName("Type", "Font");
+ pFontDict->SetAtName("Subtype", "Type1");
+ pFontDict->SetAtName("BaseFont", "Helvetica");
+ pFontDict->SetAtName("Encoding", "WinAnsiEncoding");
+ pDoc->AddIndirectObject(pFontDict);
+ pDRFontDict->SetAtReference(sFontName.Mid(1), pDoc, pFontDict);
+ }
+ CPDF_Font* pDefFont = pDoc->LoadFont(pFontDict);
+ if (!pDefFont) {
+ return FALSE;
+ }
+ CFX_FloatRect rcAnnot = pAnnotDict->GetRectBy("Rect");
+ int32_t nRotate = 0;
+ if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictBy("MK")) {
+ nRotate = pMKDict->GetIntegerBy("R");
+ }
+ CFX_FloatRect rcBBox;
+ CFX_Matrix matrix;
+ switch (nRotate % 360) {
+ case 0:
+ rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
+ rcAnnot.top - rcAnnot.bottom);
+ break;
+ case 90:
+ matrix = CFX_Matrix(0, 1, -1, 0, rcAnnot.right - rcAnnot.left, 0);
+ rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
+ rcAnnot.right - rcAnnot.left);
+ break;
+ case 180:
+ matrix = CFX_Matrix(-1, 0, 0, -1, rcAnnot.right - rcAnnot.left,
+ rcAnnot.top - rcAnnot.bottom);
+ rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
+ rcAnnot.top - rcAnnot.bottom);
+ break;
+ case 270:
+ matrix = CFX_Matrix(0, -1, 1, 0, 0, rcAnnot.top - rcAnnot.bottom);
+ rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
+ rcAnnot.right - rcAnnot.left);
+ break;
+ }
+ int32_t nBorderStyle = PBS_SOLID;
+ FX_FLOAT fBorderWidth = 1;
+ CPVT_Dash dsBorder(3, 0, 0);
+ CPVT_Color crLeftTop, crRightBottom;
+ if (CPDF_Dictionary* pBSDict = pAnnotDict->GetDictBy("BS")) {
+ if (pBSDict->KeyExist("W")) {
+ fBorderWidth = pBSDict->GetNumberBy("W");
+ }
+ if (CPDF_Array* pArray = pBSDict->GetArrayBy("D")) {
+ dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1),
+ pArray->GetIntegerAt(2));
+ }
+ switch (pBSDict->GetStringBy("S").GetAt(0)) {
+ case 'S':
+ nBorderStyle = PBS_SOLID;
+ break;
+ case 'D':
+ nBorderStyle = PBS_DASH;
+ break;
+ case 'B':
+ nBorderStyle = PBS_BEVELED;
+ fBorderWidth *= 2;
+ crLeftTop = CPVT_Color(CPVT_Color::kGray, 1);
+ crRightBottom = CPVT_Color(CPVT_Color::kGray, 0.5);
+ break;
+ case 'I':
+ nBorderStyle = PBS_INSET;
+ fBorderWidth *= 2;
+ crLeftTop = CPVT_Color(CPVT_Color::kGray, 0.5);
+ crRightBottom = CPVT_Color(CPVT_Color::kGray, 0.75);
+ break;
+ case 'U':
+ nBorderStyle = PBS_UNDERLINED;
+ break;
+ }
+ }
+ CPVT_Color crBorder, crBG;
+ if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictBy("MK")) {
+ if (CPDF_Array* pArray = pMKDict->GetArrayBy("BC")) {
+ crBorder = ParseColor(*pArray);
+ }
+ if (CPDF_Array* pArray = pMKDict->GetArrayBy("BG")) {
+ crBG = ParseColor(*pArray);
+ }
+ }
+ CFX_ByteTextBuf sAppStream;
+ CFX_ByteString sBG = CPVT_GenerateAP::GenerateColorAP(crBG, TRUE);
+ if (sBG.GetLength() > 0) {
+ sAppStream << "q\n" << sBG << rcBBox.left << " " << rcBBox.bottom << " "
+ << rcBBox.Width() << " " << rcBBox.Height() << " re f\n"
+ << "Q\n";
+ }
+ CFX_ByteString sBorderStream = CPVT_GenerateAP::GenerateBorderAP(
+ rcBBox, fBorderWidth, crBorder, crLeftTop, crRightBottom, nBorderStyle,
+ dsBorder);
+ if (sBorderStream.GetLength() > 0) {
+ sAppStream << "q\n" << sBorderStream << "Q\n";
+ }
+ CFX_FloatRect rcBody =
+ CFX_FloatRect(rcBBox.left + fBorderWidth, rcBBox.bottom + fBorderWidth,
+ rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth);
+ rcBody.Normalize();
+ CPDF_Dictionary* pAPDict = pAnnotDict->GetDictBy("AP");
+ if (!pAPDict) {
+ pAPDict = new CPDF_Dictionary;
+ pAnnotDict->SetAt("AP", pAPDict);
+ }
+ CPDF_Stream* pNormalStream = pAPDict->GetStreamBy("N");
+ if (!pNormalStream) {
+ pNormalStream = new CPDF_Stream(nullptr, 0, nullptr);
+ int32_t objnum = pDoc->AddIndirectObject(pNormalStream);
+ pAnnotDict->GetDictBy("AP")->SetAtReference("N", pDoc, objnum);
+ }
+ CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
+ if (pStreamDict) {
+ pStreamDict->SetAtMatrix("Matrix", matrix);
+ pStreamDict->SetAtRect("BBox", rcBBox);
+ CPDF_Dictionary* pStreamResList = pStreamDict->GetDictBy("Resources");
+ if (pStreamResList) {
+ CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictBy("Font");
+ if (!pStreamResFontList) {
+ pStreamResFontList = new CPDF_Dictionary;
+ pStreamResList->SetAt("Font", pStreamResFontList);
+ }
+ if (!pStreamResFontList->KeyExist(sFontName)) {
+ pStreamResFontList->SetAtReference(sFontName, pDoc, pFontDict);
+ }
+ } else {
+ pStreamDict->SetAt("Resources", pFormDict->GetDictBy("DR")->Clone());
+ pStreamResList = pStreamDict->GetDictBy("Resources");
+ }
+ }
+ switch (nWidgetType) {
+ case 0: {
+ CFX_WideString swValue =
+ FPDF_GetFieldAttr(pAnnotDict, "V")
+ ? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
+ : CFX_WideString();
+ int32_t nAlign = FPDF_GetFieldAttr(pAnnotDict, "Q")
+ ? FPDF_GetFieldAttr(pAnnotDict, "Q")->GetInteger()
+ : 0;
+ FX_DWORD dwFlags = FPDF_GetFieldAttr(pAnnotDict, "Ff")
+ ? FPDF_GetFieldAttr(pAnnotDict, "Ff")->GetInteger()
+ : 0;
+ FX_DWORD dwMaxLen =
+ FPDF_GetFieldAttr(pAnnotDict, "MaxLen")
+ ? FPDF_GetFieldAttr(pAnnotDict, "MaxLen")->GetInteger()
+ : 0;
+ CPVT_FontMap map(pDoc,
+ pStreamDict ? pStreamDict->GetDictBy("Resources") : NULL,
+ pDefFont, sFontName.Right(sFontName.GetLength() - 1));
+ CPVT_Provider prd(&map);
+ CPDF_VariableText vt;
+ vt.SetProvider(&prd);
+ vt.SetPlateRect(rcBody);
+ vt.SetAlignment(nAlign);
+ if (IsFloatZero(fFontSize)) {
+ vt.SetAutoFontSize(TRUE);
+ } else {
+ vt.SetFontSize(fFontSize);
+ }
+ FX_BOOL bMultiLine = (dwFlags >> 12) & 1;
+ if (bMultiLine) {
+ vt.SetMultiLine(TRUE);
+ vt.SetAutoReturn(TRUE);
+ }
+ FX_WORD subWord = 0;
+ if ((dwFlags >> 13) & 1) {
+ subWord = '*';
+ vt.SetPasswordChar(subWord);
+ }
+ FX_BOOL bCharArray = (dwFlags >> 24) & 1;
+ if (bCharArray) {
+ vt.SetCharArray(dwMaxLen);
+ } else {
+ vt.SetLimitChar(dwMaxLen);
+ }
+ vt.Initialize();
+ vt.SetText(swValue.c_str());
+ vt.RearrangeAll();
+ CFX_FloatRect rcContent = vt.GetContentRect();
+ CFX_FloatPoint ptOffset(0.0f, 0.0f);
+ if (!bMultiLine) {
+ ptOffset =
+ CFX_FloatPoint(0.0f, (rcContent.Height() - rcBody.Height()) / 2.0f);
+ }
+ CFX_ByteString sBody = CPVT_GenerateAP::GenerateEditAP(
+ &map, vt.GetIterator(), ptOffset, !bCharArray, subWord);
+ if (sBody.GetLength() > 0) {
+ sAppStream << "/Tx BMC\n"
+ << "q\n";
+ if (rcContent.Width() > rcBody.Width() ||
+ rcContent.Height() > rcBody.Height()) {
+ sAppStream << rcBody.left << " " << rcBody.bottom << " "
+ << rcBody.Width() << " " << rcBody.Height()
+ << " re\nW\nn\n";
+ }
+ sAppStream << "BT\n" << CPVT_GenerateAP::GenerateColorAP(crText, TRUE)
+ << sBody << "ET\n"
+ << "Q\nEMC\n";
+ }
+ } break;
+ case 1: {
+ CFX_WideString swValue =
+ FPDF_GetFieldAttr(pAnnotDict, "V")
+ ? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
+ : CFX_WideString();
+ CPVT_FontMap map(pDoc,
+ pStreamDict ? pStreamDict->GetDictBy("Resources") : NULL,
+ pDefFont, sFontName.Right(sFontName.GetLength() - 1));
+ CPVT_Provider prd(&map);
+ CPDF_VariableText vt;
+ vt.SetProvider(&prd);
+ CFX_FloatRect rcButton = rcBody;
+ rcButton.left = rcButton.right - 13;
+ rcButton.Normalize();
+ CFX_FloatRect rcEdit = rcBody;
+ rcEdit.right = rcButton.left;
+ rcEdit.Normalize();
+ vt.SetPlateRect(rcEdit);
+ if (IsFloatZero(fFontSize)) {
+ vt.SetAutoFontSize(TRUE);
+ } else {
+ vt.SetFontSize(fFontSize);
+ }
+ vt.Initialize();
+ vt.SetText(swValue.c_str());
+ vt.RearrangeAll();
+ CFX_FloatRect rcContent = vt.GetContentRect();
+ CFX_FloatPoint ptOffset =
+ CFX_FloatPoint(0.0f, (rcContent.Height() - rcEdit.Height()) / 2.0f);
+ CFX_ByteString sEdit = CPVT_GenerateAP::GenerateEditAP(
+ &map, vt.GetIterator(), ptOffset, TRUE, 0);
+ if (sEdit.GetLength() > 0) {
+ sAppStream << "/Tx BMC\n"
+ << "q\n";
+ sAppStream << rcEdit.left << " " << rcEdit.bottom << " "
+ << rcEdit.Width() << " " << rcEdit.Height() << " re\nW\nn\n";
+ sAppStream << "BT\n" << CPVT_GenerateAP::GenerateColorAP(crText, TRUE)
+ << sEdit << "ET\n"
+ << "Q\nEMC\n";
+ }
+ CFX_ByteString sButton = CPVT_GenerateAP::GenerateColorAP(
+ CPVT_Color(CPVT_Color::kRGB, 220.0f / 255.0f, 220.0f / 255.0f,
+ 220.0f / 255.0f),
+ TRUE);
+ if (sButton.GetLength() > 0 && !rcButton.IsEmpty()) {
+ sAppStream << "q\n" << sButton;
+ sAppStream << rcButton.left << " " << rcButton.bottom << " "
+ << rcButton.Width() << " " << rcButton.Height() << " re f\n";
+ sAppStream << "Q\n";
+ CFX_ByteString sButtonBorder = CPVT_GenerateAP::GenerateBorderAP(
+ rcButton, 2, CPVT_Color(CPVT_Color::kGray, 0),
+ CPVT_Color(CPVT_Color::kGray, 1),
+ CPVT_Color(CPVT_Color::kGray, 0.5), PBS_BEVELED,
+ CPVT_Dash(3, 0, 0));
+ if (sButtonBorder.GetLength() > 0) {
+ sAppStream << "q\n" << sButtonBorder << "Q\n";
+ }
+ CFX_FloatPoint ptCenter =
+ CFX_FloatPoint((rcButton.left + rcButton.right) / 2,
+ (rcButton.top + rcButton.bottom) / 2);
+ if (IsFloatBigger(rcButton.Width(), 6) &&
+ IsFloatBigger(rcButton.Height(), 6)) {
+ sAppStream << "q\n"
+ << " 0 g\n";
+ sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " m\n";
+ sAppStream << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " l\n";
+ sAppStream << ptCenter.x << " " << ptCenter.y - 1.5f << " l\n";
+ sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " l f\n";
+ sAppStream << sButton << "Q\n";
+ }
+ }
+ } break;
+ case 2: {
+ CPVT_FontMap map(pDoc,
+ pStreamDict ? pStreamDict->GetDictBy("Resources") : NULL,
+ pDefFont, sFontName.Right(sFontName.GetLength() - 1));
+ CPVT_Provider prd(&map);
+ CPDF_Array* pOpts = FPDF_GetFieldAttr(pAnnotDict, "Opt")
+ ? FPDF_GetFieldAttr(pAnnotDict, "Opt")->GetArray()
+ : NULL;
+ CPDF_Array* pSels = FPDF_GetFieldAttr(pAnnotDict, "I")
+ ? FPDF_GetFieldAttr(pAnnotDict, "I")->GetArray()
+ : NULL;
+ int32_t nTop = FPDF_GetFieldAttr(pAnnotDict, "TI")
+ ? FPDF_GetFieldAttr(pAnnotDict, "TI")->GetInteger()
+ : 0;
+ CFX_ByteTextBuf sBody;
+ if (pOpts) {
+ FX_FLOAT fy = rcBody.top;
+ for (int32_t i = nTop, sz = pOpts->GetCount(); i < sz; i++) {
+ if (IsFloatSmaller(fy, rcBody.bottom)) {
+ break;
+ }
+ if (CPDF_Object* pOpt = pOpts->GetElementValue(i)) {
+ CFX_WideString swItem;
+ if (pOpt->IsString())
+ swItem = pOpt->GetUnicodeText();
+ else if (CPDF_Array* pArray = pOpt->AsArray())
+ swItem = pArray->GetElementValue(1)->GetUnicodeText();
+
+ FX_BOOL bSelected = FALSE;
+ if (pSels) {
+ for (FX_DWORD s = 0, ssz = pSels->GetCount(); s < ssz; s++) {
+ if (i == pSels->GetIntegerAt(s)) {
+ bSelected = TRUE;
+ break;
+ }
+ }
+ }
+ CPDF_VariableText vt;
+ vt.SetProvider(&prd);
+ vt.SetPlateRect(
+ CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f));
+ if (IsFloatZero(fFontSize)) {
+ vt.SetFontSize(12.0f);
+ } else {
+ vt.SetFontSize(fFontSize);
+ }
+ vt.Initialize();
+ vt.SetText(swItem.c_str());
+ vt.RearrangeAll();
+ FX_FLOAT fItemHeight = vt.GetContentRect().Height();
+ if (bSelected) {
+ CFX_FloatRect rcItem = CFX_FloatRect(
+ rcBody.left, fy - fItemHeight, rcBody.right, fy);
+ sBody << "q\n" << CPVT_GenerateAP::GenerateColorAP(
+ CPVT_Color(CPVT_Color::kRGB, 0,
+ 51.0f / 255.0f, 113.0f / 255.0f),
+ TRUE)
+ << rcItem.left << " " << rcItem.bottom << " "
+ << rcItem.Width() << " " << rcItem.Height() << " re f\n"
+ << "Q\n";
+ sBody << "BT\n" << CPVT_GenerateAP::GenerateColorAP(
+ CPVT_Color(CPVT_Color::kGray, 1), TRUE)
+ << CPVT_GenerateAP::GenerateEditAP(&map, vt.GetIterator(),
+ CFX_FloatPoint(0.0f, fy),
+ TRUE, 0)
+ << "ET\n";
+ } else {
+ sBody << "BT\n" << CPVT_GenerateAP::GenerateColorAP(crText, TRUE)
+ << CPVT_GenerateAP::GenerateEditAP(&map, vt.GetIterator(),
+ CFX_FloatPoint(0.0f, fy),
+ TRUE, 0)
+ << "ET\n";
+ }
+ fy -= fItemHeight;
+ }
+ }
+ }
+ if (sBody.GetSize() > 0) {
+ sAppStream << "/Tx BMC\n"
+ << "q\n";
+ sAppStream << rcBody.left << " " << rcBody.bottom << " "
+ << rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n";
+ sAppStream << sBody.GetByteString() << "Q\nEMC\n";
+ }
+ } break;
+ }
+ if (pNormalStream) {
+ pNormalStream->SetData((uint8_t*)sAppStream.GetBuffer(),
+ sAppStream.GetSize(), FALSE, FALSE);
+ pStreamDict = pNormalStream->GetDict();
+ if (pStreamDict) {
+ pStreamDict->SetAtMatrix("Matrix", matrix);
+ pStreamDict->SetAtRect("BBox", rcBBox);
+ CPDF_Dictionary* pStreamResList = pStreamDict->GetDictBy("Resources");
+ if (pStreamResList) {
+ CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictBy("Font");
+ if (!pStreamResFontList) {
+ pStreamResFontList = new CPDF_Dictionary;
+ pStreamResList->SetAt("Font", pStreamResFontList);
+ }
+ if (!pStreamResFontList->KeyExist(sFontName)) {
+ pStreamResFontList->SetAtReference(sFontName, pDoc, pFontDict);
+ }
+ } else {
+ pStreamDict->SetAt("Resources", pFormDict->GetDictBy("DR")->Clone());
+ pStreamResList = pStreamDict->GetDictBy("Resources");
+ }
+ }
+ }
+ return TRUE;
+}
+FX_BOOL CPVT_GenerateAP::GenerateTextFieldAP(CPDF_Document* pDoc,
+ CPDF_Dictionary* pAnnotDict) {
+ return GenerateWidgetAP(pDoc, pAnnotDict, 0);
+}
+FX_BOOL CPVT_GenerateAP::GenerateComboBoxAP(CPDF_Document* pDoc,
+ CPDF_Dictionary* pAnnotDict) {
+ return GenerateWidgetAP(pDoc, pAnnotDict, 1);
+}
+FX_BOOL CPVT_GenerateAP::GenerateListBoxAP(CPDF_Document* pDoc,
+ CPDF_Dictionary* pAnnotDict) {
+ return GenerateWidgetAP(pDoc, pAnnotDict, 2);
+}
+CFX_ByteString CPVT_GenerateAP::GenerateEditAP(
+ IPVT_FontMap* pFontMap,
+ IPDF_VariableText_Iterator* pIterator,
+ const CFX_FloatPoint& ptOffset,
+ FX_BOOL bContinuous,
+ FX_WORD SubWord,
+ const CPVT_WordRange* pVisible) {
+ CFX_ByteTextBuf sEditStream, sLineStream, sWords;
+ CFX_FloatPoint ptOld(0.0f, 0.0f), ptNew(0.0f, 0.0f);
+ int32_t nCurFontIndex = -1;
+ if (pIterator) {
+ if (pVisible) {
+ pIterator->SetAt(pVisible->BeginPos);
+ } else {
+ pIterator->SetAt(0);
+ }
+ CPVT_WordPlace oldplace;
+ while (pIterator->NextWord()) {
+ CPVT_WordPlace place = pIterator->GetAt();
+ if (pVisible && place.WordCmp(pVisible->EndPos) > 0) {
+ break;
+ }
+ if (bContinuous) {
+ if (place.LineCmp(oldplace) != 0) {
+ if (sWords.GetSize() > 0) {
+ sLineStream << GetWordRenderString(sWords.GetByteString());
+ sEditStream << sLineStream;
+ sLineStream.Clear();
+ sWords.Clear();
+ }
+ CPVT_Word word;
+ if (pIterator->GetWord(word)) {
+ ptNew = CFX_FloatPoint(word.ptWord.x + ptOffset.x,
+ word.ptWord.y + ptOffset.y);
+ } else {
+ CPVT_Line line;
+ pIterator->GetLine(line);
+ ptNew = CFX_FloatPoint(line.ptLine.x + ptOffset.x,
+ line.ptLine.y + ptOffset.y);
+ }
+ if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
+ sLineStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
+ << " Td\n";
+ ptOld = ptNew;
+ }
+ }
+ CPVT_Word word;
+ if (pIterator->GetWord(word)) {
+ if (word.nFontIndex != nCurFontIndex) {
+ if (sWords.GetSize() > 0) {
+ sLineStream << GetWordRenderString(sWords.GetByteString());
+ sWords.Clear();
+ }
+ sLineStream << GetFontSetString(pFontMap, word.nFontIndex,
+ word.fFontSize);
+ nCurFontIndex = word.nFontIndex;
+ }
+ sWords << GetPDFWordString(pFontMap, nCurFontIndex, word.Word,
+ SubWord);
+ }
+ oldplace = place;
+ } else {
+ CPVT_Word word;
+ if (pIterator->GetWord(word)) {
+ ptNew = CFX_FloatPoint(word.ptWord.x + ptOffset.x,
+ word.ptWord.y + ptOffset.y);
+ if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
+ sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
+ << " Td\n";
+ ptOld = ptNew;
+ }
+ if (word.nFontIndex != nCurFontIndex) {
+ sEditStream << GetFontSetString(pFontMap, word.nFontIndex,
+ word.fFontSize);
+ nCurFontIndex = word.nFontIndex;
+ }
+ sEditStream << GetWordRenderString(
+ GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord));
+ }
+ }
+ }
+ if (sWords.GetSize() > 0) {
+ sLineStream << GetWordRenderString(sWords.GetByteString());
+ sEditStream << sLineStream;
+ sWords.Clear();
+ }
+ }
+ return sEditStream.GetByteString();
+}
+CFX_ByteString CPVT_GenerateAP::GenerateBorderAP(
+ const CFX_FloatRect& rect,
+ FX_FLOAT fWidth,
+ const CPVT_Color& color,
+ const CPVT_Color& crLeftTop,
+ const CPVT_Color& crRightBottom,
+ int32_t nStyle,
+ const CPVT_Dash& dash) {
+ CFX_ByteTextBuf sAppStream;
+ CFX_ByteString sColor;
+ FX_FLOAT fLeft = rect.left;
+ FX_FLOAT fRight = rect.right;
+ FX_FLOAT fTop = rect.top;
+ FX_FLOAT fBottom = rect.bottom;
+ if (fWidth > 0.0f) {
+ FX_FLOAT fHalfWidth = fWidth / 2.0f;
+ switch (nStyle) {
+ default:
+ case PBS_SOLID:
+ sColor = GenerateColorAP(color, TRUE);
+ if (sColor.GetLength() > 0) {
+ sAppStream << sColor;
+ sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
+ << fTop - fBottom << " re\n";
+ sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
+ << fRight - fLeft - fWidth * 2 << " "
+ << fTop - fBottom - fWidth * 2 << " re\n";
+ sAppStream << "f*\n";
+ }
+ break;
+ case PBS_DASH:
+ sColor = GenerateColorAP(color, FALSE);
+ if (sColor.GetLength() > 0) {
+ sAppStream << sColor;
+ sAppStream << fWidth << " w"
+ << " [" << dash.nDash << " " << dash.nGap << "] "
+ << dash.nPhase << " d\n";
+ sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
+ << " m\n";
+ sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2
+ << " l\n";
+ sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2
+ << " l\n";
+ sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
+ << " l\n";
+ sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
+ << " l S\n";
+ }
+ break;
+ case PBS_BEVELED:
+ case PBS_INSET:
+ sColor = GenerateColorAP(crLeftTop, TRUE);
+ if (sColor.GetLength() > 0) {
+ sAppStream << sColor;
+ sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
+ << " m\n";
+ sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth
+ << " l\n";
+ sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
+ << " l\n";
+ sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+ << " l\n";
+ sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+ << " l\n";
+ sAppStream << fLeft + fHalfWidth * 2 << " "
+ << fBottom + fHalfWidth * 2 << " l f\n";
+ }
+ sColor = GenerateColorAP(crRightBottom, TRUE);
+ if (sColor.GetLength() > 0) {
+ sAppStream << sColor;
+ sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
+ << " m\n";
+ sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
+ << " l\n";
+ sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
+ << " l\n";
+ sAppStream << fLeft + fHalfWidth * 2 << " "
+ << fBottom + fHalfWidth * 2 << " l\n";
+ sAppStream << fRight - fHalfWidth * 2 << " "
+ << fBottom + fHalfWidth * 2 << " l\n";
+ sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
+ << " l f\n";
+ }
+ sColor = GenerateColorAP(color, TRUE);
+ if (sColor.GetLength() > 0) {
+ sAppStream << sColor;
+ sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
+ << fTop - fBottom << " re\n";
+ sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
+ << fRight - fLeft - fHalfWidth * 2 << " "
+ << fTop - fBottom - fHalfWidth * 2 << " re f*\n";
+ }
+ break;
+ case PBS_UNDERLINED:
+ sColor = GenerateColorAP(color, FALSE);
+ if (sColor.GetLength() > 0) {
+ sAppStream << sColor;
+ sAppStream << fWidth << " w\n";
+ sAppStream << fLeft << " " << fBottom + fWidth / 2 << " m\n";
+ sAppStream << fRight << " " << fBottom + fWidth / 2 << " l S\n";
+ }
+ break;
+ }
+ }
+ return sAppStream.GetByteString();
+}
+CFX_ByteString CPVT_GenerateAP::GenerateColorAP(const CPVT_Color& color,
+ const FX_BOOL& bFillOrStroke) {
+ CFX_ByteTextBuf sColorStream;
+ switch (color.nColorType) {
+ case CPVT_Color::kRGB:
+ sColorStream << color.fColor1 << " " << color.fColor2 << " "
+ << color.fColor3 << " " << (bFillOrStroke ? "rg" : "RG")
+ << "\n";
+ break;
+ case CPVT_Color::kGray:
+ sColorStream << color.fColor1 << " " << (bFillOrStroke ? "g" : "G")
+ << "\n";
+ break;
+ case CPVT_Color::kCMYK:
+ sColorStream << color.fColor1 << " " << color.fColor2 << " "
+ << color.fColor3 << " " << color.fColor4 << " "
+ << (bFillOrStroke ? "k" : "K") << "\n";
+ break;
+ case CPVT_Color::kTransparent:
+ break;
+ }
+ return sColorStream.GetByteString();
+}
diff --git a/core/fpdfdoc/doc_basic.cpp b/core/fpdfdoc/doc_basic.cpp
new file mode 100644
index 0000000000..b3a6511734
--- /dev/null
+++ b/core/fpdfdoc/doc_basic.cpp
@@ -0,0 +1,510 @@
+// 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 "core/fpdfdoc/doc_utils.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+const int nMaxRecursion = 32;
+int CPDF_Dest::GetPageIndex(CPDF_Document* pDoc) {
+ CPDF_Array* pArray = ToArray(m_pObj);
+ if (!pArray)
+ return 0;
+
+ CPDF_Object* pPage = pArray->GetElementValue(0);
+ if (!pPage)
+ return 0;
+ if (pPage->IsNumber())
+ return pPage->GetInteger();
+ if (!pPage->IsDictionary())
+ return 0;
+ return pDoc->GetPageIndex(pPage->GetObjNum());
+}
+FX_DWORD CPDF_Dest::GetPageObjNum() {
+ CPDF_Array* pArray = ToArray(m_pObj);
+ if (!pArray)
+ return 0;
+
+ CPDF_Object* pPage = pArray->GetElementValue(0);
+ if (!pPage)
+ return 0;
+ if (pPage->IsNumber())
+ return pPage->GetInteger();
+ if (pPage->IsDictionary())
+ return pPage->GetObjNum();
+ return 0;
+}
+const FX_CHAR* g_sZoomModes[] = {"XYZ", "Fit", "FitH", "FitV", "FitR",
+ "FitB", "FitBH", "FitBV", ""};
+int CPDF_Dest::GetZoomMode() {
+ CPDF_Array* pArray = ToArray(m_pObj);
+ if (!pArray)
+ return 0;
+
+ CFX_ByteString mode;
+ CPDF_Object* pObj = pArray->GetElementValue(1);
+ mode = pObj ? pObj->GetString() : CFX_ByteString();
+ int i = 0;
+ while (g_sZoomModes[i][0] != '\0') {
+ if (mode == g_sZoomModes[i]) {
+ return i + 1;
+ }
+ i++;
+ }
+ return 0;
+}
+FX_FLOAT CPDF_Dest::GetParam(int index) {
+ CPDF_Array* pArray = ToArray(m_pObj);
+ return pArray ? pArray->GetNumberAt(2 + index) : 0;
+}
+CFX_ByteString CPDF_Dest::GetRemoteName() {
+ return m_pObj ? m_pObj->GetString() : CFX_ByteString();
+}
+CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc,
+ const CFX_ByteStringC& category) {
+ if (pDoc->GetRoot() && pDoc->GetRoot()->GetDictBy("Names"))
+ m_pRoot = pDoc->GetRoot()->GetDictBy("Names")->GetDictBy(category);
+ else
+ m_pRoot = NULL;
+}
+static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode,
+ const CFX_ByteString& csName,
+ int& nIndex,
+ CPDF_Array** ppFind,
+ int nLevel = 0) {
+ if (nLevel > nMaxRecursion) {
+ return NULL;
+ }
+ CPDF_Array* pLimits = pNode->GetArrayBy("Limits");
+ if (pLimits) {
+ CFX_ByteString csLeft = pLimits->GetStringAt(0);
+ CFX_ByteString csRight = pLimits->GetStringAt(1);
+ if (csLeft.Compare(csRight) > 0) {
+ CFX_ByteString csTmp = csRight;
+ csRight = csLeft;
+ csLeft = csTmp;
+ }
+ if (csName.Compare(csLeft) < 0 || csName.Compare(csRight) > 0) {
+ return NULL;
+ }
+ }
+ CPDF_Array* pNames = pNode->GetArrayBy("Names");
+ if (pNames) {
+ FX_DWORD dwCount = pNames->GetCount() / 2;
+ for (FX_DWORD i = 0; i < dwCount; i++) {
+ CFX_ByteString csValue = pNames->GetStringAt(i * 2);
+ int32_t iCompare = csValue.Compare(csName);
+ if (iCompare <= 0) {
+ if (ppFind) {
+ *ppFind = pNames;
+ }
+ if (iCompare < 0) {
+ continue;
+ }
+ } else {
+ break;
+ }
+ nIndex += i;
+ return pNames->GetElementValue(i * 2 + 1);
+ }
+ nIndex += dwCount;
+ return NULL;
+ }
+ CPDF_Array* pKids = pNode->GetArrayBy("Kids");
+ if (!pKids) {
+ return NULL;
+ }
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ if (!pKid) {
+ continue;
+ }
+ CPDF_Object* pFound =
+ SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1);
+ if (pFound) {
+ return pFound;
+ }
+ }
+ return NULL;
+}
+static CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode,
+ int nIndex,
+ int& nCurIndex,
+ CFX_ByteString& csName,
+ CPDF_Array** ppFind,
+ int nLevel = 0) {
+ if (nLevel > nMaxRecursion) {
+ return NULL;
+ }
+ CPDF_Array* pNames = pNode->GetArrayBy("Names");
+ if (pNames) {
+ int nCount = pNames->GetCount() / 2;
+ if (nIndex >= nCurIndex + nCount) {
+ nCurIndex += nCount;
+ return NULL;
+ }
+ if (ppFind) {
+ *ppFind = pNames;
+ }
+ csName = pNames->GetStringAt((nIndex - nCurIndex) * 2);
+ return pNames->GetElementValue((nIndex - nCurIndex) * 2 + 1);
+ }
+ CPDF_Array* pKids = pNode->GetArrayBy("Kids");
+ if (!pKids) {
+ return NULL;
+ }
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ if (!pKid) {
+ continue;
+ }
+ CPDF_Object* pFound =
+ SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1);
+ if (pFound) {
+ return pFound;
+ }
+ }
+ return NULL;
+}
+static int CountNames(CPDF_Dictionary* pNode, int nLevel = 0) {
+ if (nLevel > nMaxRecursion) {
+ return 0;
+ }
+ CPDF_Array* pNames = pNode->GetArrayBy("Names");
+ if (pNames) {
+ return pNames->GetCount() / 2;
+ }
+ CPDF_Array* pKids = pNode->GetArrayBy("Kids");
+ if (!pKids) {
+ return 0;
+ }
+ int nCount = 0;
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ if (!pKid) {
+ continue;
+ }
+ nCount += CountNames(pKid, nLevel + 1);
+ }
+ return nCount;
+}
+int CPDF_NameTree::GetCount() const {
+ if (!m_pRoot) {
+ return 0;
+ }
+ return ::CountNames(m_pRoot);
+}
+int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const {
+ if (!m_pRoot) {
+ return -1;
+ }
+ int nIndex = 0;
+ if (!SearchNameNode(m_pRoot, csName, nIndex, NULL)) {
+ return -1;
+ }
+ return nIndex;
+}
+CPDF_Object* CPDF_NameTree::LookupValue(int nIndex,
+ CFX_ByteString& csName) const {
+ if (!m_pRoot) {
+ return NULL;
+ }
+ int nCurIndex = 0;
+ return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, NULL);
+}
+CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const {
+ if (!m_pRoot) {
+ return NULL;
+ }
+ int nIndex = 0;
+ return SearchNameNode(m_pRoot, csName, nIndex, NULL);
+}
+CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc,
+ const CFX_ByteStringC& sName) {
+ CPDF_Object* pValue = LookupValue(sName);
+ if (!pValue) {
+ CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDictBy("Dests");
+ if (!pDests)
+ return nullptr;
+ pValue = pDests->GetElementValue(sName);
+ }
+ if (!pValue)
+ return nullptr;
+ if (CPDF_Array* pArray = pValue->AsArray())
+ return pArray;
+ if (CPDF_Dictionary* pDict = pValue->AsDictionary())
+ return pDict->GetArrayBy("D");
+ return nullptr;
+}
+
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ || \
+ _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+static CFX_WideString ChangeSlashToPlatform(const FX_WCHAR* str) {
+ CFX_WideString result;
+ while (*str) {
+ if (*str == '/') {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ result += ':';
+#else
+ result += '\\';
+#endif
+ } else {
+ result += *str;
+ }
+ str++;
+ }
+ return result;
+}
+
+static CFX_WideString ChangeSlashToPDF(const FX_WCHAR* str) {
+ CFX_WideString result;
+ while (*str) {
+ if (*str == '\\' || *str == ':') {
+ result += '/';
+ } else {
+ result += *str;
+ }
+ str++;
+ }
+ return result;
+}
+#endif // _FXM_PLATFORM_APPLE_ || _FXM_PLATFORM_WINDOWS_
+
+CFX_WideString CPDF_FileSpec::DecodeFileName(const CFX_WideStringC& filepath) {
+ if (filepath.GetLength() <= 1)
+ return CFX_WideString();
+
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ if (filepath.Left(sizeof("/Mac") - 1) == CFX_WideStringC(L"/Mac"))
+ return ChangeSlashToPlatform(filepath.GetPtr() + 1);
+ return ChangeSlashToPlatform(filepath.GetPtr());
+#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ if (filepath.GetAt(0) != '/')
+ return ChangeSlashToPlatform(filepath.GetPtr());
+ if (filepath.GetAt(1) == '/')
+ return ChangeSlashToPlatform(filepath.GetPtr() + 1);
+ if (filepath.GetAt(2) == '/') {
+ CFX_WideString result;
+ result += filepath.GetAt(1);
+ result += ':';
+ result += ChangeSlashToPlatform(filepath.GetPtr() + 2);
+ return result;
+ }
+ CFX_WideString result;
+ result += '\\';
+ result += ChangeSlashToPlatform(filepath.GetPtr());
+ return result;
+#else
+ return filepath;
+#endif
+}
+
+bool CPDF_FileSpec::GetFileName(CFX_WideString* csFileName) const {
+ if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
+ *csFileName = pDict->GetUnicodeTextBy("UF");
+ if (csFileName->IsEmpty()) {
+ *csFileName = CFX_WideString::FromLocal(pDict->GetStringBy("F"));
+ }
+ if (pDict->GetStringBy("FS") == "URL")
+ return true;
+ if (csFileName->IsEmpty()) {
+ if (pDict->KeyExist("DOS")) {
+ *csFileName = CFX_WideString::FromLocal(pDict->GetStringBy("DOS"));
+ } else if (pDict->KeyExist("Mac")) {
+ *csFileName = CFX_WideString::FromLocal(pDict->GetStringBy("Mac"));
+ } else if (pDict->KeyExist("Unix")) {
+ *csFileName = CFX_WideString::FromLocal(pDict->GetStringBy("Unix"));
+ } else {
+ return false;
+ }
+ }
+ } else if (m_pObj->IsString()) {
+ *csFileName = CFX_WideString::FromLocal(m_pObj->GetString());
+ } else {
+ return false;
+ }
+ *csFileName = DecodeFileName(*csFileName);
+ return true;
+}
+
+CPDF_FileSpec::CPDF_FileSpec() {
+ m_pObj = new CPDF_Dictionary;
+ m_pObj->AsDictionary()->SetAtName("Type", "Filespec");
+}
+
+CFX_WideString CPDF_FileSpec::EncodeFileName(const CFX_WideStringC& filepath) {
+ if (filepath.GetLength() <= 1) {
+ return CFX_WideString();
+ }
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ if (filepath.GetAt(1) == ':') {
+ CFX_WideString result;
+ result = '/';
+ result += filepath.GetAt(0);
+ if (filepath.GetAt(2) != '\\') {
+ result += '/';
+ }
+ result += ChangeSlashToPDF(filepath.GetPtr() + 2);
+ return result;
+ }
+ if (filepath.GetAt(0) == '\\' && filepath.GetAt(1) == '\\') {
+ return ChangeSlashToPDF(filepath.GetPtr() + 1);
+ }
+ if (filepath.GetAt(0) == '\\') {
+ CFX_WideString result;
+ result = '/';
+ result += ChangeSlashToPDF(filepath.GetPtr());
+ return result;
+ }
+ return ChangeSlashToPDF(filepath.GetPtr());
+#elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ if (filepath.Left(sizeof("Mac") - 1) == FX_WSTRC(L"Mac")) {
+ CFX_WideString result;
+ result = '/';
+ result += ChangeSlashToPDF(filepath.GetPtr());
+ return result;
+ }
+ return ChangeSlashToPDF(filepath.GetPtr());
+#else
+ return filepath;
+#endif
+}
+
+void CPDF_FileSpec::SetFileName(const CFX_WideStringC& wsFileName) {
+ if (!m_pObj)
+ return;
+
+ CFX_WideString wsStr = EncodeFileName(wsFileName);
+ if (m_pObj->IsString()) {
+ m_pObj->SetString(CFX_ByteString::FromUnicode(wsStr));
+ } else if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
+ pDict->SetAtString("F", CFX_ByteString::FromUnicode(wsStr));
+ pDict->SetAtString("UF", PDF_EncodeText(wsStr));
+ }
+}
+
+static CFX_WideString _MakeRoman(int num) {
+ const int arabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
+ const CFX_WideString roman[] = {L"m", L"cm", L"d", L"cd", L"c",
+ L"xc", L"l", L"xl", L"x", L"ix",
+ L"v", L"iv", L"i"};
+ const int nMaxNum = 1000000;
+ num %= nMaxNum;
+ int i = 0;
+ CFX_WideString wsRomanNumber;
+ while (num > 0) {
+ while (num >= arabic[i]) {
+ num = num - arabic[i];
+ wsRomanNumber += roman[i];
+ }
+ i = i + 1;
+ }
+ return wsRomanNumber;
+}
+
+static CFX_WideString _MakeLetters(int num) {
+ if (num == 0) {
+ return CFX_WideString();
+ }
+ CFX_WideString wsLetters;
+ const int nMaxCount = 1000;
+ const int nLetterCount = 26;
+ num -= 1;
+ int count = num / nLetterCount + 1;
+ count %= nMaxCount;
+ FX_WCHAR ch = L'a' + num % nLetterCount;
+ for (int i = 0; i < count; i++) {
+ wsLetters += ch;
+ }
+ return wsLetters;
+}
+static CFX_WideString _GetLabelNumPortion(int num,
+ const CFX_ByteString& bsStyle) {
+ CFX_WideString wsNumPortion;
+ if (bsStyle.IsEmpty()) {
+ return wsNumPortion;
+ }
+ if (bsStyle == "D") {
+ wsNumPortion.Format(L"%d", num);
+ } else if (bsStyle == "R") {
+ wsNumPortion = _MakeRoman(num);
+ wsNumPortion.MakeUpper();
+ } else if (bsStyle == "r") {
+ wsNumPortion = _MakeRoman(num);
+ } else if (bsStyle == "A") {
+ wsNumPortion = _MakeLetters(num);
+ wsNumPortion.MakeUpper();
+ } else if (bsStyle == "a") {
+ wsNumPortion = _MakeLetters(num);
+ }
+ return wsNumPortion;
+}
+CFX_WideString CPDF_PageLabel::GetLabel(int nPage) const {
+ CFX_WideString wsLabel;
+ if (!m_pDocument) {
+ return wsLabel;
+ }
+ CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
+ if (!pPDFRoot) {
+ return wsLabel;
+ }
+ CPDF_Dictionary* pLabels = pPDFRoot->GetDictBy("PageLabels");
+ CPDF_NumberTree numberTree(pLabels);
+ CPDF_Object* pValue = NULL;
+ int n = nPage;
+ while (n >= 0) {
+ pValue = numberTree.LookupValue(n);
+ if (pValue) {
+ break;
+ }
+ n--;
+ }
+ if (pValue) {
+ pValue = pValue->GetDirect();
+ if (CPDF_Dictionary* pLabel = pValue->AsDictionary()) {
+ if (pLabel->KeyExist("P")) {
+ wsLabel += pLabel->GetUnicodeTextBy("P");
+ }
+ CFX_ByteString bsNumberingStyle = pLabel->GetStringBy("S", NULL);
+ int nLabelNum = nPage - n + pLabel->GetIntegerBy("St", 1);
+ CFX_WideString wsNumPortion =
+ _GetLabelNumPortion(nLabelNum, bsNumberingStyle);
+ wsLabel += wsNumPortion;
+ return wsLabel;
+ }
+ }
+ wsLabel.Format(L"%d", nPage + 1);
+ return wsLabel;
+}
+int32_t CPDF_PageLabel::GetPageByLabel(const CFX_ByteStringC& bsLabel) const {
+ if (!m_pDocument) {
+ return -1;
+ }
+ CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot();
+ if (!pPDFRoot) {
+ return -1;
+ }
+ int nPages = m_pDocument->GetPageCount();
+ CFX_ByteString bsLbl;
+ CFX_ByteString bsOrig = bsLabel;
+ for (int i = 0; i < nPages; i++) {
+ bsLbl = PDF_EncodeText(GetLabel(i));
+ if (!bsLbl.Compare(bsOrig)) {
+ return i;
+ }
+ }
+ bsLbl = bsOrig;
+ int nPage = FXSYS_atoi(bsLbl);
+ if (nPage > 0 && nPage <= nPages) {
+ return nPage;
+ }
+ return -1;
+}
+int32_t CPDF_PageLabel::GetPageByLabel(const CFX_WideStringC& wsLabel) const {
+ CFX_ByteString bsLabel = PDF_EncodeText(wsLabel.GetPtr());
+ return GetPageByLabel(bsLabel);
+}
diff --git a/core/fpdfdoc/doc_basic_unittest.cpp b/core/fpdfdoc/doc_basic_unittest.cpp
new file mode 100644
index 0000000000..659643851d
--- /dev/null
+++ b/core/fpdfdoc/doc_basic_unittest.cpp
@@ -0,0 +1,169 @@
+// Copyright 2016 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.
+
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+#include <memory>
+#include <vector>
+
+#include "core/include/fpdfapi/cpdf_name.h"
+#include "core/include/fpdfapi/cpdf_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_support.h"
+
+namespace {
+
+using ScopedObj = std::unique_ptr<CPDF_Object, ReleaseDeleter<CPDF_Object>>;
+using ScopedDict =
+ std::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>>;
+}
+
+TEST(doc_basic_filespec, EncodeDecodeFileName) {
+ std::vector<pdfium::NullTermWstrFuncTestData> test_data = {
+ // Empty src string.
+ {L"", L""},
+ // only file name.
+ {L"test.pdf", L"test.pdf"},
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ // With drive identifier.
+ {L"r:\\pdfdocs\\spec.pdf", L"/r/pdfdocs/spec.pdf"},
+ // Relative path.
+ {L"My Document\\test.pdf", L"My Document/test.pdf"},
+ // Absolute path without drive identifier.
+ {L"\\pdfdocs\\spec.pdf", L"//pdfdocs/spec.pdf"},
+ // Absolute path with double backslashes.
+ {L"\\\\pdfdocs\\spec.pdf", L"/pdfdocs/spec.pdf"},
+// Network resource name. It is not supported yet.
+// {L"pclib/eng:\\pdfdocs\\spec.pdf", L"/pclib/eng/pdfdocs/spec.pdf"},
+#elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ // Absolute path with colon separator.
+ {L"Mac HD:PDFDocs:spec.pdf", L"/Mac HD/PDFDocs/spec.pdf"},
+ // Relative path with colon separator.
+ {L"PDFDocs:spec.pdf", L"PDFDocs/spec.pdf"},
+#else
+ // Relative path.
+ {L"./docs/test.pdf", L"./docs/test.pdf"},
+ // Relative path with parent dir.
+ {L"../test_docs/test.pdf", L"../test_docs/test.pdf"},
+ // Absolute path.
+ {L"/usr/local/home/test.pdf", L"/usr/local/home/test.pdf"},
+#endif
+ };
+ for (const auto& data : test_data) {
+ CFX_WideString encoded_str = CPDF_FileSpec::EncodeFileName(data.input);
+ EXPECT_TRUE(encoded_str.Equal(data.expected));
+ // DecodeFileName is the reverse procedure of EncodeFileName.
+ CFX_WideString decoded_str = CPDF_FileSpec::DecodeFileName(data.expected);
+ EXPECT_TRUE(decoded_str.Equal(data.input));
+ }
+}
+
+TEST(doc_basic_filespec, GetFileName) {
+ {
+ // String object.
+ pdfium::NullTermWstrFuncTestData test_data = {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ L"/C/docs/test.pdf",
+ L"C:\\docs\\test.pdf"
+#elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ L"/Mac HD/docs/test.pdf",
+ L"Mac HD:docs:test.pdf"
+#else
+ L"/docs/test.pdf",
+ L"/docs/test.pdf"
+#endif
+ };
+ ScopedObj str_obj(new CPDF_String(test_data.input));
+ CPDF_FileSpec file_spec(str_obj.get());
+ CFX_WideString file_name;
+ EXPECT_TRUE(file_spec.GetFileName(&file_name));
+ EXPECT_TRUE(file_name.Equal(test_data.expected));
+ }
+ {
+ // Dictionary object.
+ pdfium::NullTermWstrFuncTestData test_data[5] = {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ {L"/C/docs/test.pdf", L"C:\\docs\\test.pdf"},
+ {L"/D/docs/test.pdf", L"D:\\docs\\test.pdf"},
+ {L"/E/docs/test.pdf", L"E:\\docs\\test.pdf"},
+ {L"/F/docs/test.pdf", L"F:\\docs\\test.pdf"},
+ {L"/G/docs/test.pdf", L"G:\\docs\\test.pdf"},
+#elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ {L"/Mac HD/docs1/test.pdf", L"Mac HD:docs1:test.pdf"},
+ {L"/Mac HD/docs2/test.pdf", L"Mac HD:docs2:test.pdf"},
+ {L"/Mac HD/docs3/test.pdf", L"Mac HD:docs3:test.pdf"},
+ {L"/Mac HD/docs4/test.pdf", L"Mac HD:docs4:test.pdf"},
+ {L"/Mac HD/docs5/test.pdf", L"Mac HD:docs5:test.pdf"},
+#else
+ {L"/docs/a/test.pdf", L"/docs/a/test.pdf"},
+ {L"/docs/b/test.pdf", L"/docs/b/test.pdf"},
+ {L"/docs/c/test.pdf", L"/docs/c/test.pdf"},
+ {L"/docs/d/test.pdf", L"/docs/d/test.pdf"},
+ {L"/docs/e/test.pdf", L"/docs/e/test.pdf"},
+#endif
+ };
+ // Keyword fields in reverse order of precedence to retrieve the file name.
+ const char* const keywords[5] = {"Unix", "Mac", "DOS", "F", "UF"};
+ ScopedDict dict_obj(new CPDF_Dictionary);
+ CPDF_FileSpec file_spec(dict_obj.get());
+ CFX_WideString file_name;
+ for (int i = 0; i < 5; ++i) {
+ dict_obj->SetAt(keywords[i], new CPDF_String(test_data[i].input));
+ EXPECT_TRUE(file_spec.GetFileName(&file_name));
+ EXPECT_TRUE(file_name.Equal(test_data[i].expected));
+ }
+
+ // With all the former fields and 'FS' field suggests 'URL' type.
+ dict_obj->SetAtString("FS", "URL");
+ EXPECT_TRUE(file_spec.GetFileName(&file_name));
+ // Url string is not decoded.
+ EXPECT_TRUE(file_name.Equal(test_data[4].input));
+ }
+ {
+ // Invalid object.
+ ScopedObj name_obj(new CPDF_Name("test.pdf"));
+ CPDF_FileSpec file_spec(name_obj.get());
+ CFX_WideString file_name;
+ EXPECT_FALSE(file_spec.GetFileName(&file_name));
+ }
+}
+
+TEST(doc_basic_filespec, SetFileName) {
+ pdfium::NullTermWstrFuncTestData test_data = {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ L"C:\\docs\\test.pdf",
+ L"/C/docs/test.pdf"
+#elif _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ L"Mac HD:docs:test.pdf",
+ L"/Mac HD/docs/test.pdf"
+#else
+ L"/docs/test.pdf",
+ L"/docs/test.pdf"
+#endif
+ };
+ // String object.
+ ScopedObj str_obj(new CPDF_String(L"babababa"));
+ CPDF_FileSpec file_spec1(str_obj.get());
+ file_spec1.SetFileName(test_data.input);
+ // Check internal object value.
+ CFX_ByteString str = CFX_ByteString::FromUnicode(test_data.expected);
+ EXPECT_TRUE(str == str_obj->GetString());
+ // Check we can get the file name back.
+ CFX_WideString file_name;
+ EXPECT_TRUE(file_spec1.GetFileName(&file_name));
+ EXPECT_TRUE(file_name.Equal(test_data.input));
+
+ // Dictionary object.
+ ScopedDict dict_obj(new CPDF_Dictionary);
+ CPDF_FileSpec file_spec2(dict_obj.get());
+ file_spec2.SetFileName(test_data.input);
+ // Check internal object value.
+ file_name = dict_obj->GetUnicodeTextBy("F");
+ EXPECT_TRUE(file_name.Equal(test_data.expected));
+ file_name = dict_obj->GetUnicodeTextBy("UF");
+ EXPECT_TRUE(file_name.Equal(test_data.expected));
+ // Check we can get the file name back.
+ EXPECT_TRUE(file_spec2.GetFileName(&file_name));
+ EXPECT_TRUE(file_name.Equal(test_data.input));
+}
diff --git a/core/fpdfdoc/doc_bookmark.cpp b/core/fpdfdoc/doc_bookmark.cpp
new file mode 100644
index 0000000000..c338e5d48c
--- /dev/null
+++ b/core/fpdfdoc/doc_bookmark.cpp
@@ -0,0 +1,95 @@
+// 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 <memory>
+#include <vector>
+
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_string.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild(
+ const CPDF_Bookmark& parent) const {
+ if (!parent.GetDict()) {
+ CPDF_Dictionary* pRoot = m_pDocument->GetRoot()->GetDictBy("Outlines");
+ if (!pRoot)
+ return CPDF_Bookmark();
+ return CPDF_Bookmark(pRoot->GetDictBy("First"));
+ }
+ return CPDF_Bookmark(parent.GetDict()->GetDictBy("First"));
+}
+
+CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling(
+ const CPDF_Bookmark& bookmark) const {
+ if (!bookmark.GetDict())
+ return CPDF_Bookmark();
+
+ CPDF_Dictionary* pNext = bookmark.GetDict()->GetDictBy("Next");
+ return pNext == bookmark.GetDict() ? CPDF_Bookmark() : CPDF_Bookmark(pNext);
+}
+
+FX_DWORD CPDF_Bookmark::GetColorRef() const {
+ if (!m_pDict) {
+ return 0;
+ }
+ CPDF_Array* pColor = m_pDict->GetArrayBy("C");
+ if (!pColor) {
+ return FXSYS_RGB(0, 0, 0);
+ }
+ int r = FXSYS_round(pColor->GetNumberAt(0) * 255);
+ int g = FXSYS_round(pColor->GetNumberAt(1) * 255);
+ int b = FXSYS_round(pColor->GetNumberAt(2) * 255);
+ return FXSYS_RGB(r, g, b);
+}
+FX_DWORD CPDF_Bookmark::GetFontStyle() const {
+ if (!m_pDict) {
+ return 0;
+ }
+ return m_pDict->GetIntegerBy("F");
+}
+CFX_WideString CPDF_Bookmark::GetTitle() const {
+ if (!m_pDict) {
+ return CFX_WideString();
+ }
+ CPDF_String* pString = ToString(m_pDict->GetElementValue("Title"));
+ if (!pString)
+ return CFX_WideString();
+
+ CFX_WideString title = pString->GetUnicodeText();
+ int len = title.GetLength();
+ if (!len) {
+ return CFX_WideString();
+ }
+ std::unique_ptr<FX_WCHAR[]> buf(new FX_WCHAR[len]);
+ for (int i = 0; i < len; i++) {
+ FX_WCHAR w = title[i];
+ buf[i] = w > 0x20 ? w : 0x20;
+ }
+ return CFX_WideString(buf.get(), len);
+}
+CPDF_Dest CPDF_Bookmark::GetDest(CPDF_Document* pDocument) const {
+ if (!m_pDict)
+ return CPDF_Dest();
+
+ CPDF_Object* pDest = m_pDict->GetElementValue("Dest");
+ if (!pDest)
+ return CPDF_Dest();
+ if (pDest->IsString() || pDest->IsName()) {
+ CPDF_NameTree name_tree(pDocument, "Dests");
+ CFX_ByteStringC name = pDest->GetString();
+ return CPDF_Dest(name_tree.LookupNamedDest(pDocument, name));
+ }
+ if (CPDF_Array* pArray = pDest->AsArray())
+ return CPDF_Dest(pArray);
+ return CPDF_Dest();
+}
+CPDF_Action CPDF_Bookmark::GetAction() const {
+ if (!m_pDict) {
+ return CPDF_Action();
+ }
+ return CPDF_Action(m_pDict->GetDictBy("A"));
+}
diff --git a/core/fpdfdoc/doc_form.cpp b/core/fpdfdoc/doc_form.cpp
new file mode 100644
index 0000000000..c205588335
--- /dev/null
+++ b/core/fpdfdoc/doc_form.cpp
@@ -0,0 +1,1207 @@
+// 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 <vector>
+
+#include "core/fpdfdoc/doc_utils.h"
+#include "core/include/fpdfapi/cfdf_document.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_string.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+const int nMaxRecursion = 32;
+
+const struct SupportFieldEncoding {
+ const FX_CHAR* m_name;
+ FX_WORD m_codePage;
+} g_fieldEncoding[] = {
+ {"BigFive", 950},
+ {"GBK", 936},
+ {"Shift-JIS", 932},
+ {"UHC", 949},
+};
+
+CFX_WideString FPDFDOC_FDF_GetFieldValue(const CPDF_Dictionary& pFieldDict,
+ const CFX_ByteString& bsEncoding) {
+ const CFX_ByteString csBValue = pFieldDict.GetStringBy("V");
+ for (const auto& encoding : g_fieldEncoding) {
+ if (bsEncoding == encoding.m_name)
+ return CFX_WideString::FromCodePage(csBValue, encoding.m_codePage);
+ }
+ CFX_ByteString csTemp = csBValue.Left(2);
+ if (csTemp == "\xFF\xFE" || csTemp == "\xFE\xFF")
+ return PDF_DecodeText(csBValue);
+ return CFX_WideString::FromLocal(csBValue);
+}
+
+} // namespace
+
+class CFieldNameExtractor {
+ public:
+ explicit CFieldNameExtractor(const CFX_WideString& full_name) {
+ m_pStart = full_name.c_str();
+ m_pEnd = m_pStart + full_name.GetLength();
+ m_pCur = m_pStart;
+ }
+ void GetNext(const FX_WCHAR*& pSubName, FX_STRSIZE& size) {
+ pSubName = m_pCur;
+ while (m_pCur < m_pEnd && m_pCur[0] != L'.') {
+ m_pCur++;
+ }
+ size = (FX_STRSIZE)(m_pCur - pSubName);
+ if (m_pCur < m_pEnd && m_pCur[0] == L'.') {
+ m_pCur++;
+ }
+ }
+
+ protected:
+ const FX_WCHAR* m_pStart;
+ const FX_WCHAR* m_pEnd;
+ const FX_WCHAR* m_pCur;
+};
+class CFieldTree {
+ public:
+ struct _Node {
+ _Node* parent;
+ CFX_ArrayTemplate<_Node*> children;
+ CFX_WideString short_name;
+ CPDF_FormField* field_ptr;
+ int CountFields(int nLevel = 0) {
+ if (nLevel > nMaxRecursion) {
+ return 0;
+ }
+ if (field_ptr) {
+ return 1;
+ }
+ int count = 0;
+ for (int i = 0; i < children.GetSize(); i++) {
+ count += children.GetAt(i)->CountFields(nLevel + 1);
+ }
+ return count;
+ }
+ CPDF_FormField* GetField(int* fields_to_go) {
+ if (field_ptr) {
+ if (*fields_to_go == 0) {
+ return field_ptr;
+ }
+ --*fields_to_go;
+ return NULL;
+ }
+ for (int i = 0; i < children.GetSize(); i++) {
+ if (CPDF_FormField* pField = children.GetAt(i)->GetField(fields_to_go))
+ return pField;
+ }
+ return NULL;
+ }
+ CPDF_FormField* GetField(int index) {
+ int fields_to_go = index;
+ return GetField(&fields_to_go);
+ }
+ };
+ CFieldTree();
+ ~CFieldTree();
+ void SetField(const CFX_WideString& full_name, CPDF_FormField* field_ptr);
+ CPDF_FormField* GetField(const CFX_WideString& full_name);
+ CPDF_FormField* RemoveField(const CFX_WideString& full_name);
+ void RemoveAll();
+ _Node* FindNode(const CFX_WideString& full_name);
+ _Node* AddChild(_Node* pParent,
+ const CFX_WideString& short_name,
+ CPDF_FormField* field_ptr);
+ void RemoveNode(_Node* pNode, int nLevel = 0);
+ _Node* _Lookup(_Node* pParent, const CFX_WideString& short_name);
+ _Node m_Root;
+};
+CFieldTree::CFieldTree() {
+ m_Root.parent = NULL;
+ m_Root.field_ptr = NULL;
+}
+CFieldTree::~CFieldTree() {
+ RemoveAll();
+}
+CFieldTree::_Node* CFieldTree::AddChild(_Node* pParent,
+ const CFX_WideString& short_name,
+ CPDF_FormField* field_ptr) {
+ if (!pParent) {
+ return NULL;
+ }
+ _Node* pNode = new _Node;
+ pNode->parent = pParent;
+ pNode->short_name = short_name;
+ pNode->field_ptr = field_ptr;
+ pParent->children.Add(pNode);
+ return pNode;
+}
+void CFieldTree::RemoveNode(_Node* pNode, int nLevel) {
+ if (!pNode) {
+ return;
+ }
+ if (nLevel <= nMaxRecursion) {
+ for (int i = 0; i < pNode->children.GetSize(); i++) {
+ RemoveNode(pNode->children[i], nLevel + 1);
+ }
+ }
+ delete pNode;
+}
+CFieldTree::_Node* CFieldTree::_Lookup(_Node* pParent,
+ const CFX_WideString& short_name) {
+ if (!pParent) {
+ return NULL;
+ }
+ for (int i = 0; i < pParent->children.GetSize(); i++) {
+ _Node* pNode = pParent->children[i];
+ if (pNode->short_name.GetLength() == short_name.GetLength() &&
+ FXSYS_memcmp(pNode->short_name.c_str(), short_name.c_str(),
+ short_name.GetLength() * sizeof(FX_WCHAR)) == 0) {
+ return pNode;
+ }
+ }
+ return NULL;
+}
+void CFieldTree::RemoveAll() {
+ for (int i = 0; i < m_Root.children.GetSize(); i++) {
+ RemoveNode(m_Root.children[i]);
+ }
+}
+void CFieldTree::SetField(const CFX_WideString& full_name,
+ CPDF_FormField* field_ptr) {
+ if (full_name == L"") {
+ return;
+ }
+ CFieldNameExtractor name_extractor(full_name);
+ const FX_WCHAR* pName;
+ FX_STRSIZE nLength;
+ name_extractor.GetNext(pName, nLength);
+ _Node *pNode = &m_Root, *pLast = NULL;
+ while (nLength > 0) {
+ pLast = pNode;
+ CFX_WideString name = CFX_WideString(pName, nLength);
+ pNode = _Lookup(pLast, name);
+ if (!pNode) {
+ pNode = AddChild(pLast, name, NULL);
+ }
+ name_extractor.GetNext(pName, nLength);
+ }
+ if (pNode != &m_Root) {
+ pNode->field_ptr = field_ptr;
+ }
+}
+CPDF_FormField* CFieldTree::GetField(const CFX_WideString& full_name) {
+ if (full_name == L"") {
+ return NULL;
+ }
+ CFieldNameExtractor name_extractor(full_name);
+ const FX_WCHAR* pName;
+ FX_STRSIZE nLength;
+ name_extractor.GetNext(pName, nLength);
+ _Node *pNode = &m_Root, *pLast = NULL;
+ while (nLength > 0 && pNode) {
+ pLast = pNode;
+ CFX_WideString name = CFX_WideString(pName, nLength);
+ pNode = _Lookup(pLast, name);
+ name_extractor.GetNext(pName, nLength);
+ }
+ return pNode ? pNode->field_ptr : NULL;
+}
+CPDF_FormField* CFieldTree::RemoveField(const CFX_WideString& full_name) {
+ if (full_name == L"") {
+ return NULL;
+ }
+ CFieldNameExtractor name_extractor(full_name);
+ const FX_WCHAR* pName;
+ FX_STRSIZE nLength;
+ name_extractor.GetNext(pName, nLength);
+ _Node *pNode = &m_Root, *pLast = NULL;
+ while (nLength > 0 && pNode) {
+ pLast = pNode;
+ CFX_WideString name = CFX_WideString(pName, nLength);
+ pNode = _Lookup(pLast, name);
+ name_extractor.GetNext(pName, nLength);
+ }
+ if (pNode && pNode != &m_Root) {
+ for (int i = 0; i < pLast->children.GetSize(); i++) {
+ if (pNode == pLast->children[i]) {
+ pLast->children.RemoveAt(i);
+ break;
+ }
+ }
+ CPDF_FormField* pField = pNode->field_ptr;
+ RemoveNode(pNode);
+ return pField;
+ }
+ return NULL;
+}
+CFieldTree::_Node* CFieldTree::FindNode(const CFX_WideString& full_name) {
+ if (full_name == L"") {
+ return NULL;
+ }
+ CFieldNameExtractor name_extractor(full_name);
+ const FX_WCHAR* pName;
+ FX_STRSIZE nLength;
+ name_extractor.GetNext(pName, nLength);
+ _Node *pNode = &m_Root, *pLast = NULL;
+ while (nLength > 0 && pNode) {
+ pLast = pNode;
+ CFX_WideString name = CFX_WideString(pName, nLength);
+ pNode = _Lookup(pLast, name);
+ name_extractor.GetNext(pName, nLength);
+ }
+ return pNode;
+}
+CPDF_InterForm::CPDF_InterForm(CPDF_Document* pDocument, FX_BOOL bGenerateAP)
+ : CFX_PrivateData(),
+ m_pDocument(pDocument),
+ m_bGenerateAP(bGenerateAP),
+ m_pFormDict(nullptr),
+ m_pFieldTree(new CFieldTree),
+ m_pFormNotify(nullptr),
+ m_bUpdated(FALSE) {
+ CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
+ if (!pRoot)
+ return;
+
+ m_pFormDict = pRoot->GetDictBy("AcroForm");
+ if (!m_pFormDict)
+ return;
+
+ CPDF_Array* pFields = m_pFormDict->GetArrayBy("Fields");
+ if (!pFields)
+ return;
+
+ int count = pFields->GetCount();
+ for (int i = 0; i < count; i++) {
+ LoadField(pFields->GetDictAt(i));
+ }
+}
+
+CPDF_InterForm::~CPDF_InterForm() {
+ for (auto it : m_ControlMap)
+ delete it.second;
+
+ int nCount = m_pFieldTree->m_Root.CountFields();
+ for (int i = 0; i < nCount; ++i) {
+ delete m_pFieldTree->m_Root.GetField(i);
+ }
+}
+
+FX_BOOL CPDF_InterForm::m_bUpdateAP = TRUE;
+FX_BOOL CPDF_InterForm::UpdatingAPEnabled() {
+ return m_bUpdateAP;
+}
+void CPDF_InterForm::EnableUpdateAP(FX_BOOL bUpdateAP) {
+ m_bUpdateAP = bUpdateAP;
+}
+CFX_ByteString CPDF_InterForm::GenerateNewResourceName(
+ const CPDF_Dictionary* pResDict,
+ const FX_CHAR* csType,
+ int iMinLen,
+ const FX_CHAR* csPrefix) {
+ CFX_ByteString csStr = csPrefix;
+ CFX_ByteString csBType = csType;
+ if (csStr.IsEmpty()) {
+ if (csBType == "ExtGState") {
+ csStr = "GS";
+ } else if (csBType == "ColorSpace") {
+ csStr = "CS";
+ } else if (csBType == "Font") {
+ csStr = "ZiTi";
+ } else {
+ csStr = "Res";
+ }
+ }
+ CFX_ByteString csTmp = csStr;
+ int iCount = csStr.GetLength();
+ int m = 0;
+ if (iMinLen > 0) {
+ csTmp = "";
+ while (m < iMinLen && m < iCount) {
+ csTmp += csStr[m++];
+ }
+ while (m < iMinLen) {
+ csTmp += '0' + m % 10;
+ m++;
+ }
+ } else {
+ m = iCount;
+ }
+ if (!pResDict) {
+ return csTmp;
+ }
+ CPDF_Dictionary* pDict = pResDict->GetDictBy(csType);
+ if (!pDict) {
+ return csTmp;
+ }
+ int num = 0;
+ CFX_ByteString bsNum;
+ while (TRUE) {
+ if (!pDict->KeyExist(csTmp + bsNum)) {
+ return csTmp + bsNum;
+ }
+ if (m < iCount) {
+ csTmp += csStr[m++];
+ } else {
+ bsNum.Format("%d", num++);
+ }
+ m++;
+ }
+ return csTmp;
+}
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+typedef struct PDF_FONTDATA_ {
+ FX_BOOL bFind;
+ LOGFONTA lf;
+} PDF_FONTDATA, FAR* LPDF_FONTDATA;
+static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXA* lpelfe,
+ NEWTEXTMETRICEX* lpntme,
+ DWORD FontType,
+ LPARAM lParam) {
+ if (FontType != 0x004 || strchr(lpelfe->elfLogFont.lfFaceName, '@')) {
+ return 1;
+ }
+ LPDF_FONTDATA pData = (LPDF_FONTDATA)lParam;
+ memcpy(&pData->lf, &lpelfe->elfLogFont, sizeof(LOGFONTA));
+ pData->bFind = TRUE;
+ return 0;
+}
+static FX_BOOL RetrieveSpecificFont(LOGFONTA& lf) {
+ PDF_FONTDATA fd;
+ memset(&fd, 0, sizeof(PDF_FONTDATA));
+ HDC hDC = ::GetDC(NULL);
+ EnumFontFamiliesExA(hDC, &lf, (FONTENUMPROCA)EnumFontFamExProc, (LPARAM)&fd,
+ 0);
+ ::ReleaseDC(NULL, hDC);
+ if (fd.bFind) {
+ memcpy(&lf, &fd.lf, sizeof(LOGFONTA));
+ }
+ return fd.bFind;
+}
+static FX_BOOL RetrieveSpecificFont(uint8_t charSet,
+ uint8_t pitchAndFamily,
+ LPCSTR pcsFontName,
+ LOGFONTA& lf) {
+ memset(&lf, 0, sizeof(LOGFONTA));
+ lf.lfCharSet = charSet;
+ lf.lfPitchAndFamily = pitchAndFamily;
+ if (pcsFontName) {
+ // TODO(dsinclair): Should this be strncpy?
+ strcpy(lf.lfFaceName, pcsFontName);
+ }
+ return RetrieveSpecificFont(lf);
+}
+#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+
+CPDF_Font* CPDF_InterForm::AddStandardFont(CPDF_Document* pDocument,
+ CFX_ByteString csFontName) {
+ if (!pDocument || csFontName.IsEmpty())
+ return nullptr;
+
+ if (csFontName == "ZapfDingbats")
+ return pDocument->AddStandardFont(csFontName.c_str(), nullptr);
+
+ CPDF_FontEncoding encoding(PDFFONT_ENCODING_WINANSI);
+ return pDocument->AddStandardFont(csFontName.c_str(), &encoding);
+}
+
+CFX_ByteString CPDF_InterForm::GetNativeFont(uint8_t charSet, void* pLogFont) {
+ CFX_ByteString csFontName;
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ LOGFONTA lf;
+ FX_BOOL bRet;
+ if (charSet == ANSI_CHARSET) {
+ csFontName = "Helvetica";
+ return csFontName;
+ }
+ bRet = FALSE;
+ if (charSet == SHIFTJIS_CHARSET) {
+ bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
+ "MS Mincho", lf);
+ } else if (charSet == GB2312_CHARSET) {
+ bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, "SimSun",
+ lf);
+ } else if (charSet == CHINESEBIG5_CHARSET) {
+ bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, "MingLiU",
+ lf);
+ }
+ if (!bRet) {
+ bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
+ "Arial Unicode MS", lf);
+ }
+ if (!bRet) {
+ bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE,
+ "Microsoft Sans Serif", lf);
+ }
+ if (!bRet) {
+ bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, NULL, lf);
+ }
+ if (bRet) {
+ if (pLogFont) {
+ memcpy(pLogFont, &lf, sizeof(LOGFONTA));
+ }
+ csFontName = lf.lfFaceName;
+ return csFontName;
+ }
+#endif
+ return csFontName;
+}
+CFX_ByteString CPDF_InterForm::GetNativeFont(void* pLogFont) {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ uint8_t charSet = GetNativeCharSet();
+ return GetNativeFont(charSet, pLogFont);
+#else
+ return CFX_ByteString();
+#endif
+}
+uint8_t CPDF_InterForm::GetNativeCharSet() {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ uint8_t charSet = ANSI_CHARSET;
+ UINT iCodePage = ::GetACP();
+ switch (iCodePage) {
+ case 932:
+ charSet = SHIFTJIS_CHARSET;
+ break;
+ case 936:
+ charSet = GB2312_CHARSET;
+ break;
+ case 950:
+ charSet = CHINESEBIG5_CHARSET;
+ break;
+ case 1252:
+ charSet = ANSI_CHARSET;
+ break;
+ case 874:
+ charSet = THAI_CHARSET;
+ break;
+ case 949:
+ charSet = HANGUL_CHARSET;
+ break;
+ case 1200:
+ charSet = ANSI_CHARSET;
+ break;
+ case 1250:
+ charSet = EASTEUROPE_CHARSET;
+ break;
+ case 1251:
+ charSet = RUSSIAN_CHARSET;
+ break;
+ case 1253:
+ charSet = GREEK_CHARSET;
+ break;
+ case 1254:
+ charSet = TURKISH_CHARSET;
+ break;
+ case 1255:
+ charSet = HEBREW_CHARSET;
+ break;
+ case 1256:
+ charSet = ARABIC_CHARSET;
+ break;
+ case 1257:
+ charSet = BALTIC_CHARSET;
+ break;
+ case 1258:
+ charSet = VIETNAMESE_CHARSET;
+ break;
+ case 1361:
+ charSet = JOHAB_CHARSET;
+ break;
+ }
+ return charSet;
+#else
+ return 0;
+#endif
+}
+
+CPDF_Font* CPDF_InterForm::AddNativeFont(uint8_t charSet,
+ CPDF_Document* pDocument) {
+ if (!pDocument)
+ return nullptr;
+
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
+ LOGFONTA lf;
+ CFX_ByteString csFontName = GetNativeFont(charSet, &lf);
+ if (!csFontName.IsEmpty()) {
+ if (csFontName == "Helvetica")
+ return AddStandardFont(pDocument, csFontName);
+ return pDocument->AddWindowsFont(&lf, FALSE, TRUE);
+ }
+#endif
+ return nullptr;
+}
+
+CPDF_Font* CPDF_InterForm::AddNativeFont(CPDF_Document* pDocument) {
+ return pDocument ? AddNativeFont(GetNativeCharSet(), pDocument) : nullptr;
+}
+
+FX_BOOL CPDF_InterForm::ValidateFieldName(
+ CFX_WideString& csNewFieldName,
+ int iType,
+ const CPDF_FormField* pExcludedField,
+ const CPDF_FormControl* pExcludedControl) {
+ if (csNewFieldName.IsEmpty()) {
+ return FALSE;
+ }
+ int iPos = 0;
+ int iLength = csNewFieldName.GetLength();
+ CFX_WideString csSub;
+ while (TRUE) {
+ while (iPos < iLength &&
+ (csNewFieldName[iPos] == L'.' || csNewFieldName[iPos] == L' ')) {
+ iPos++;
+ }
+ if (iPos < iLength && !csSub.IsEmpty()) {
+ csSub += L'.';
+ }
+ while (iPos < iLength && csNewFieldName[iPos] != L'.') {
+ csSub += csNewFieldName[iPos++];
+ }
+ for (int i = csSub.GetLength() - 1; i > -1; i--) {
+ if (csSub[i] == L' ' || csSub[i] == L'.') {
+ csSub.SetAt(i, L'\0');
+ } else {
+ break;
+ }
+ }
+ FX_DWORD dwCount = m_pFieldTree->m_Root.CountFields();
+ for (FX_DWORD m = 0; m < dwCount; m++) {
+ CPDF_FormField* pField = m_pFieldTree->m_Root.GetField(m);
+ if (!pField) {
+ continue;
+ }
+ if (pField == pExcludedField) {
+ if (pExcludedControl) {
+ if (pField->CountControls() < 2) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+ CFX_WideString csFullName = pField->GetFullName();
+ int iRet = CompareFieldName(csSub, csFullName);
+ if (iRet == 1) {
+ if (pField->GetFieldType() != iType) {
+ return FALSE;
+ }
+ } else if (iRet == 2 && csSub == csNewFieldName) {
+ if (csFullName[iPos] == L'.') {
+ return FALSE;
+ }
+ } else if (iRet == 3 && csSub == csNewFieldName) {
+ if (csNewFieldName[csFullName.GetLength()] == L'.') {
+ return FALSE;
+ }
+ }
+ }
+ if (iPos >= iLength) {
+ break;
+ }
+ }
+ if (csSub.IsEmpty()) {
+ return FALSE;
+ }
+ csNewFieldName = csSub;
+ return TRUE;
+}
+FX_BOOL CPDF_InterForm::ValidateFieldName(CFX_WideString& csNewFieldName,
+ int iType) {
+ return ValidateFieldName(csNewFieldName, iType, NULL, NULL);
+}
+FX_BOOL CPDF_InterForm::ValidateFieldName(const CPDF_FormField* pField,
+ CFX_WideString& csNewFieldName) {
+ return pField && !csNewFieldName.IsEmpty() &&
+ ValidateFieldName(csNewFieldName,
+ ((CPDF_FormField*)pField)->GetFieldType(), pField,
+ NULL);
+}
+FX_BOOL CPDF_InterForm::ValidateFieldName(const CPDF_FormControl* pControl,
+ CFX_WideString& csNewFieldName) {
+ if (!pControl || csNewFieldName.IsEmpty()) {
+ return FALSE;
+ }
+ CPDF_FormField* pField = ((CPDF_FormControl*)pControl)->GetField();
+ return ValidateFieldName(csNewFieldName, pField->GetFieldType(), pField,
+ pControl);
+}
+int CPDF_InterForm::CompareFieldName(const CFX_ByteString& name1,
+ const CFX_ByteString& name2) {
+ const FX_CHAR* ptr1 = name1;
+ const FX_CHAR* ptr2 = name2;
+ if (name1.GetLength() == name2.GetLength()) {
+ return name1 == name2 ? 1 : 0;
+ }
+ int i = 0;
+ while (ptr1[i] == ptr2[i]) {
+ i++;
+ }
+ if (i == name1.GetLength()) {
+ return 2;
+ }
+ if (i == name2.GetLength()) {
+ return 3;
+ }
+ return 0;
+}
+int CPDF_InterForm::CompareFieldName(const CFX_WideString& name1,
+ const CFX_WideString& name2) {
+ const FX_WCHAR* ptr1 = name1.c_str();
+ const FX_WCHAR* ptr2 = name2.c_str();
+ if (name1.GetLength() == name2.GetLength()) {
+ return name1 == name2 ? 1 : 0;
+ }
+ int i = 0;
+ while (ptr1[i] == ptr2[i]) {
+ i++;
+ }
+ if (i == name1.GetLength()) {
+ return 2;
+ }
+ if (i == name2.GetLength()) {
+ return 3;
+ }
+ return 0;
+}
+FX_DWORD CPDF_InterForm::CountFields(const CFX_WideString& csFieldName) {
+ if (csFieldName.IsEmpty()) {
+ return (FX_DWORD)m_pFieldTree->m_Root.CountFields();
+ }
+ CFieldTree::_Node* pNode = m_pFieldTree->FindNode(csFieldName);
+ return pNode ? pNode->CountFields() : 0;
+}
+CPDF_FormField* CPDF_InterForm::GetField(FX_DWORD index,
+ const CFX_WideString& csFieldName) {
+ if (csFieldName == L"") {
+ return m_pFieldTree->m_Root.GetField(index);
+ }
+ CFieldTree::_Node* pNode = m_pFieldTree->FindNode(csFieldName);
+ return pNode ? pNode->GetField(index) : nullptr;
+}
+
+CPDF_FormField* CPDF_InterForm::GetFieldByDict(
+ CPDF_Dictionary* pFieldDict) const {
+ if (!pFieldDict) {
+ return NULL;
+ }
+ CFX_WideString csWName = GetFullName(pFieldDict);
+ return m_pFieldTree->GetField(csWName);
+}
+
+CPDF_FormControl* CPDF_InterForm::GetControlAtPoint(CPDF_Page* pPage,
+ FX_FLOAT pdf_x,
+ FX_FLOAT pdf_y,
+ int* z_order) const {
+ CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayBy("Annots");
+ if (!pAnnotList)
+ return nullptr;
+
+ for (FX_DWORD i = pAnnotList->GetCount(); i > 0; --i) {
+ FX_DWORD annot_index = i - 1;
+ CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(annot_index);
+ if (!pAnnot)
+ continue;
+
+ const auto it = m_ControlMap.find(pAnnot);
+ if (it == m_ControlMap.end())
+ continue;
+
+ CPDF_FormControl* pControl = it->second;
+ CFX_FloatRect rect = pControl->GetRect();
+ if (!rect.Contains(pdf_x, pdf_y))
+ continue;
+
+ if (z_order)
+ *z_order = annot_index;
+ return pControl;
+ }
+ return nullptr;
+}
+
+CPDF_FormControl* CPDF_InterForm::GetControlByDict(
+ const CPDF_Dictionary* pWidgetDict) const {
+ const auto it = m_ControlMap.find(pWidgetDict);
+ return it != m_ControlMap.end() ? it->second : nullptr;
+}
+
+FX_BOOL CPDF_InterForm::NeedConstructAP() {
+ return m_pFormDict && m_pFormDict->GetBooleanBy("NeedAppearances");
+}
+void CPDF_InterForm::NeedConstructAP(FX_BOOL bNeedAP) {
+ if (!m_pFormDict) {
+ InitInterFormDict(m_pFormDict, m_pDocument);
+ }
+ m_pFormDict->SetAtBoolean("NeedAppearances", bNeedAP);
+ m_bGenerateAP = bNeedAP;
+}
+int CPDF_InterForm::CountFieldsInCalculationOrder() {
+ if (!m_pFormDict) {
+ return 0;
+ }
+ CPDF_Array* pArray = m_pFormDict->GetArrayBy("CO");
+ return pArray ? pArray->GetCount() : 0;
+}
+CPDF_FormField* CPDF_InterForm::GetFieldInCalculationOrder(int index) {
+ if (!m_pFormDict || index < 0) {
+ return NULL;
+ }
+ CPDF_Array* pArray = m_pFormDict->GetArrayBy("CO");
+ if (!pArray) {
+ return NULL;
+ }
+ if (CPDF_Dictionary* pElement =
+ ToDictionary(pArray->GetElementValue(index))) {
+ return GetFieldByDict(pElement);
+ }
+ return NULL;
+}
+int CPDF_InterForm::FindFieldInCalculationOrder(const CPDF_FormField* pField) {
+ if (!m_pFormDict || !pField) {
+ return -1;
+ }
+ CPDF_Array* pArray = m_pFormDict->GetArrayBy("CO");
+ if (!pArray) {
+ return -1;
+ }
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++) {
+ CPDF_Object* pElement = pArray->GetElementValue(i);
+ if (pElement == pField->m_pDict) {
+ return i;
+ }
+ }
+ return -1;
+}
+FX_DWORD CPDF_InterForm::CountFormFonts() {
+ return CountInterFormFonts(m_pFormDict);
+}
+CPDF_Font* CPDF_InterForm::GetFormFont(FX_DWORD index,
+ CFX_ByteString& csNameTag) {
+ return GetInterFormFont(m_pFormDict, m_pDocument, index, csNameTag);
+}
+CPDF_Font* CPDF_InterForm::GetFormFont(CFX_ByteString csNameTag) {
+ return GetInterFormFont(m_pFormDict, m_pDocument, csNameTag);
+}
+CPDF_Font* CPDF_InterForm::GetFormFont(CFX_ByteString csFontName,
+ CFX_ByteString& csNameTag) {
+ return GetInterFormFont(m_pFormDict, m_pDocument, csFontName, csNameTag);
+}
+CPDF_Font* CPDF_InterForm::GetNativeFormFont(uint8_t charSet,
+ CFX_ByteString& csNameTag) {
+ return GetNativeInterFormFont(m_pFormDict, m_pDocument, charSet, csNameTag);
+}
+CPDF_Font* CPDF_InterForm::GetNativeFormFont(CFX_ByteString& csNameTag) {
+ return GetNativeInterFormFont(m_pFormDict, m_pDocument, csNameTag);
+}
+FX_BOOL CPDF_InterForm::FindFormFont(const CPDF_Font* pFont,
+ CFX_ByteString& csNameTag) {
+ return FindInterFormFont(m_pFormDict, pFont, csNameTag);
+}
+FX_BOOL CPDF_InterForm::FindFormFont(CFX_ByteString csFontName,
+ CPDF_Font*& pFont,
+ CFX_ByteString& csNameTag) {
+ return FindInterFormFont(m_pFormDict, m_pDocument, csFontName, pFont,
+ csNameTag);
+}
+void CPDF_InterForm::AddFormFont(const CPDF_Font* pFont,
+ CFX_ByteString& csNameTag) {
+ AddInterFormFont(m_pFormDict, m_pDocument, pFont, csNameTag);
+ m_bUpdated = TRUE;
+}
+CPDF_Font* CPDF_InterForm::AddNativeFormFont(uint8_t charSet,
+ CFX_ByteString& csNameTag) {
+ m_bUpdated = TRUE;
+ return AddNativeInterFormFont(m_pFormDict, m_pDocument, charSet, csNameTag);
+}
+CPDF_Font* CPDF_InterForm::AddNativeFormFont(CFX_ByteString& csNameTag) {
+ m_bUpdated = TRUE;
+ return AddNativeInterFormFont(m_pFormDict, m_pDocument, csNameTag);
+}
+void CPDF_InterForm::RemoveFormFont(const CPDF_Font* pFont) {
+ m_bUpdated = TRUE;
+ RemoveInterFormFont(m_pFormDict, pFont);
+}
+void CPDF_InterForm::RemoveFormFont(CFX_ByteString csNameTag) {
+ m_bUpdated = TRUE;
+ RemoveInterFormFont(m_pFormDict, csNameTag);
+}
+
+CPDF_DefaultAppearance CPDF_InterForm::GetDefaultAppearance() {
+ if (!m_pFormDict)
+ return CPDF_DefaultAppearance();
+ return CPDF_DefaultAppearance(m_pFormDict->GetStringBy("DA"));
+}
+
+CPDF_Font* CPDF_InterForm::GetDefaultFormFont() {
+ return GetDefaultInterFormFont(m_pFormDict, m_pDocument);
+}
+int CPDF_InterForm::GetFormAlignment() {
+ return m_pFormDict ? m_pFormDict->GetIntegerBy("Q", 0) : 0;
+}
+
+bool CPDF_InterForm::ResetForm(const std::vector<CPDF_FormField*>& fields,
+ bool bIncludeOrExclude,
+ bool bNotify) {
+ if (bNotify && m_pFormNotify && m_pFormNotify->BeforeFormReset(this) < 0)
+ return false;
+
+ int nCount = m_pFieldTree->m_Root.CountFields();
+ for (int i = 0; i < nCount; ++i) {
+ CPDF_FormField* pField = m_pFieldTree->m_Root.GetField(i);
+ if (!pField)
+ continue;
+
+ if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField))
+ pField->ResetField(bNotify);
+ }
+ if (bNotify && m_pFormNotify)
+ m_pFormNotify->AfterFormReset(this);
+ return true;
+}
+
+bool CPDF_InterForm::ResetForm(bool bNotify) {
+ if (bNotify && m_pFormNotify && m_pFormNotify->BeforeFormReset(this) < 0)
+ return false;
+
+ int nCount = m_pFieldTree->m_Root.CountFields();
+ for (int i = 0; i < nCount; ++i) {
+ CPDF_FormField* pField = m_pFieldTree->m_Root.GetField(i);
+ if (!pField)
+ continue;
+
+ pField->ResetField(bNotify);
+ }
+ if (bNotify && m_pFormNotify)
+ m_pFormNotify->AfterFormReset(this);
+ return true;
+}
+
+void CPDF_InterForm::LoadField(CPDF_Dictionary* pFieldDict, int nLevel) {
+ if (nLevel > nMaxRecursion) {
+ return;
+ }
+ if (!pFieldDict) {
+ return;
+ }
+ FX_DWORD dwParentObjNum = pFieldDict->GetObjNum();
+ CPDF_Array* pKids = pFieldDict->GetArrayBy("Kids");
+ if (!pKids) {
+ AddTerminalField(pFieldDict);
+ return;
+ }
+ CPDF_Dictionary* pFirstKid = pKids->GetDictAt(0);
+ if (!pFirstKid) {
+ return;
+ }
+ if (pFirstKid->KeyExist("T") || pFirstKid->KeyExist("Kids")) {
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pChildDict = pKids->GetDictAt(i);
+ if (pChildDict) {
+ if (pChildDict->GetObjNum() != dwParentObjNum) {
+ LoadField(pChildDict, nLevel + 1);
+ }
+ }
+ }
+ } else {
+ AddTerminalField(pFieldDict);
+ }
+}
+FX_BOOL CPDF_InterForm::HasXFAForm() const {
+ return m_pFormDict && m_pFormDict->GetArrayBy("XFA");
+}
+void CPDF_InterForm::FixPageFields(const CPDF_Page* pPage) {
+ CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
+ if (!pPageDict) {
+ return;
+ }
+ CPDF_Array* pAnnots = pPageDict->GetArrayBy("Annots");
+ if (!pAnnots) {
+ return;
+ }
+ int iAnnotCount = pAnnots->GetCount();
+ for (int i = 0; i < iAnnotCount; i++) {
+ CPDF_Dictionary* pAnnot = pAnnots->GetDictAt(i);
+ if (pAnnot && pAnnot->GetStringBy("Subtype") == "Widget") {
+ LoadField(pAnnot);
+ }
+ }
+}
+CPDF_FormField* CPDF_InterForm::AddTerminalField(CPDF_Dictionary* pFieldDict) {
+ if (!pFieldDict->KeyExist("T")) {
+ return NULL;
+ }
+ CPDF_Dictionary* pDict = pFieldDict;
+ CFX_WideString csWName = GetFullName(pFieldDict);
+ if (csWName.IsEmpty()) {
+ return NULL;
+ }
+ CPDF_FormField* pField = NULL;
+ pField = m_pFieldTree->GetField(csWName);
+ if (!pField) {
+ CPDF_Dictionary* pParent = pFieldDict;
+ if (!pFieldDict->KeyExist("T") &&
+ pFieldDict->GetStringBy("Subtype") == "Widget") {
+ pParent = pFieldDict->GetDictBy("Parent");
+ if (!pParent) {
+ pParent = pFieldDict;
+ }
+ }
+ if (pParent && pParent != pFieldDict && !pParent->KeyExist("FT")) {
+ if (pFieldDict->KeyExist("FT")) {
+ CPDF_Object* pFTValue = pFieldDict->GetElementValue("FT");
+ if (pFTValue) {
+ pParent->SetAt("FT", pFTValue->Clone());
+ }
+ }
+ if (pFieldDict->KeyExist("Ff")) {
+ CPDF_Object* pFfValue = pFieldDict->GetElementValue("Ff");
+ if (pFfValue) {
+ pParent->SetAt("Ff", pFfValue->Clone());
+ }
+ }
+ }
+ pField = new CPDF_FormField(this, pParent);
+ CPDF_Object* pTObj = pDict->GetElement("T");
+ if (ToReference(pTObj)) {
+ CPDF_Object* pClone = pTObj->Clone(TRUE);
+ if (pClone)
+ pDict->SetAt("T", pClone);
+ else
+ pDict->SetAtName("T", "");
+ }
+ m_pFieldTree->SetField(csWName, pField);
+ }
+ CPDF_Array* pKids = pFieldDict->GetArrayBy("Kids");
+ if (!pKids) {
+ if (pFieldDict->GetStringBy("Subtype") == "Widget") {
+ AddControl(pField, pFieldDict);
+ }
+ } else {
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ if (!pKid) {
+ continue;
+ }
+ if (pKid->GetStringBy("Subtype") != "Widget") {
+ continue;
+ }
+ AddControl(pField, pKid);
+ }
+ }
+ return pField;
+}
+CPDF_FormControl* CPDF_InterForm::AddControl(const CPDF_FormField* pField,
+ CPDF_Dictionary* pWidgetDict) {
+ const auto it = m_ControlMap.find(pWidgetDict);
+ if (it != m_ControlMap.end())
+ return it->second;
+
+ CPDF_FormControl* pControl =
+ new CPDF_FormControl((CPDF_FormField*)pField, pWidgetDict);
+ m_ControlMap[pWidgetDict] = pControl;
+ ((CPDF_FormField*)pField)->m_ControlList.Add(pControl);
+ return pControl;
+}
+
+CPDF_FormField* CPDF_InterForm::CheckRequiredFields(
+ const std::vector<CPDF_FormField*>* fields,
+ bool bIncludeOrExclude) const {
+ int nCount = m_pFieldTree->m_Root.CountFields();
+ for (int i = 0; i < nCount; ++i) {
+ CPDF_FormField* pField = m_pFieldTree->m_Root.GetField(i);
+ if (!pField)
+ continue;
+
+ int32_t iType = pField->GetType();
+ if (iType == CPDF_FormField::PushButton ||
+ iType == CPDF_FormField::CheckBox || iType == CPDF_FormField::ListBox) {
+ continue;
+ }
+ FX_DWORD dwFlags = pField->GetFieldFlags();
+ // TODO(thestig): Look up these magic numbers and add constants for them.
+ if (dwFlags & 0x04)
+ continue;
+
+ bool bFind = true;
+ if (fields)
+ bFind = pdfium::ContainsValue(*fields, pField);
+ if (bIncludeOrExclude == bFind) {
+ CPDF_Dictionary* pFieldDict = pField->m_pDict;
+ if ((dwFlags & 0x02) != 0 && pFieldDict->GetStringBy("V").IsEmpty()) {
+ return pField;
+ }
+ }
+ }
+ return nullptr;
+}
+
+CFDF_Document* CPDF_InterForm::ExportToFDF(const CFX_WideStringC& pdf_path,
+ bool bSimpleFileSpec) const {
+ std::vector<CPDF_FormField*> fields;
+ int nCount = m_pFieldTree->m_Root.CountFields();
+ for (int i = 0; i < nCount; ++i)
+ fields.push_back(m_pFieldTree->m_Root.GetField(i));
+ return ExportToFDF(pdf_path, fields, true, bSimpleFileSpec);
+}
+
+CFDF_Document* CPDF_InterForm::ExportToFDF(
+ const CFX_WideStringC& pdf_path,
+ const std::vector<CPDF_FormField*>& fields,
+ bool bIncludeOrExclude,
+ bool bSimpleFileSpec) const {
+ CFDF_Document* pDoc = CFDF_Document::CreateNewDoc();
+ if (!pDoc) {
+ return NULL;
+ }
+ CPDF_Dictionary* pMainDict = pDoc->GetRoot()->GetDictBy("FDF");
+ if (!pdf_path.IsEmpty()) {
+ if (bSimpleFileSpec) {
+ CFX_WideString wsFilePath = CPDF_FileSpec::EncodeFileName(pdf_path);
+ pMainDict->SetAtString("F", CFX_ByteString::FromUnicode(wsFilePath));
+ pMainDict->SetAtString("UF", PDF_EncodeText(wsFilePath));
+ } else {
+ CPDF_FileSpec filespec;
+ filespec.SetFileName(pdf_path);
+ pMainDict->SetAt("F", filespec.GetObj());
+ }
+ }
+ CPDF_Array* pFields = new CPDF_Array;
+ pMainDict->SetAt("Fields", pFields);
+ int nCount = m_pFieldTree->m_Root.CountFields();
+ for (int i = 0; i < nCount; i++) {
+ CPDF_FormField* pField = m_pFieldTree->m_Root.GetField(i);
+ if (!pField || pField->GetType() == CPDF_FormField::PushButton) {
+ continue;
+ }
+ FX_DWORD dwFlags = pField->GetFieldFlags();
+ if (dwFlags & 0x04)
+ continue;
+
+ if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField)) {
+ if ((dwFlags & 0x02) != 0 && pField->m_pDict->GetStringBy("V").IsEmpty())
+ continue;
+
+ CFX_WideString fullname = GetFullName(pField->GetFieldDict());
+ CPDF_Dictionary* pFieldDict = new CPDF_Dictionary;
+ pFieldDict->SetAt("T", new CPDF_String(fullname));
+ if (pField->GetType() == CPDF_FormField::CheckBox ||
+ pField->GetType() == CPDF_FormField::RadioButton) {
+ CFX_WideString csExport = pField->GetCheckValue(FALSE);
+ CFX_ByteString csBExport = PDF_EncodeText(csExport);
+ CPDF_Object* pOpt = FPDF_GetFieldAttr(pField->m_pDict, "Opt");
+ if (pOpt)
+ pFieldDict->SetAtString("V", csBExport);
+ else
+ pFieldDict->SetAtName("V", csBExport);
+ } else {
+ CPDF_Object* pV = FPDF_GetFieldAttr(pField->m_pDict, "V");
+ if (pV)
+ pFieldDict->SetAt("V", pV->Clone(TRUE));
+ }
+ pFields->Add(pFieldDict);
+ }
+ }
+ return pDoc;
+}
+
+void CPDF_InterForm::FDF_ImportField(CPDF_Dictionary* pFieldDict,
+ const CFX_WideString& parent_name,
+ FX_BOOL bNotify,
+ int nLevel) {
+ CFX_WideString name;
+ if (!parent_name.IsEmpty()) {
+ name = parent_name + L".";
+ }
+ name += pFieldDict->GetUnicodeTextBy("T");
+ CPDF_Array* pKids = pFieldDict->GetArrayBy("Kids");
+ if (pKids) {
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ if (!pKid) {
+ continue;
+ }
+ if (nLevel <= nMaxRecursion) {
+ FDF_ImportField(pKid, name, bNotify, nLevel + 1);
+ }
+ }
+ return;
+ }
+ if (!pFieldDict->KeyExist("V")) {
+ return;
+ }
+ CPDF_FormField* pField = m_pFieldTree->GetField(name);
+ if (!pField) {
+ return;
+ }
+ CFX_WideString csWValue =
+ FPDFDOC_FDF_GetFieldValue(*pFieldDict, m_bsEncoding);
+ int iType = pField->GetFieldType();
+ if (bNotify && m_pFormNotify) {
+ int iRet = 0;
+ if (iType == FIELDTYPE_LISTBOX) {
+ iRet = m_pFormNotify->BeforeSelectionChange(pField, csWValue);
+ } else if (iType == FIELDTYPE_COMBOBOX || iType == FIELDTYPE_TEXTFIELD) {
+ iRet = m_pFormNotify->BeforeValueChange(pField, csWValue);
+ }
+ if (iRet < 0) {
+ return;
+ }
+ }
+ pField->SetValue(csWValue);
+ CPDF_FormField::Type eType = pField->GetType();
+ if ((eType == CPDF_FormField::ListBox || eType == CPDF_FormField::ComboBox) &&
+ pFieldDict->KeyExist("Opt")) {
+ pField->m_pDict->SetAt("Opt",
+ pFieldDict->GetElementValue("Opt")->Clone(TRUE));
+ }
+ if (bNotify && m_pFormNotify) {
+ if (iType == FIELDTYPE_CHECKBOX || iType == FIELDTYPE_RADIOBUTTON) {
+ m_pFormNotify->AfterCheckedStatusChange(pField);
+ } else if (iType == FIELDTYPE_LISTBOX) {
+ m_pFormNotify->AfterSelectionChange(pField);
+ } else if (iType == FIELDTYPE_COMBOBOX || iType == FIELDTYPE_TEXTFIELD) {
+ m_pFormNotify->AfterValueChange(pField);
+ }
+ }
+ if (CPDF_InterForm::m_bUpdateAP) {
+ pField->UpdateAP(NULL);
+ }
+}
+FX_BOOL CPDF_InterForm::ImportFromFDF(const CFDF_Document* pFDF,
+ FX_BOOL bNotify) {
+ if (!pFDF) {
+ return FALSE;
+ }
+ CPDF_Dictionary* pMainDict = pFDF->GetRoot()->GetDictBy("FDF");
+ if (!pMainDict) {
+ return FALSE;
+ }
+ CPDF_Array* pFields = pMainDict->GetArrayBy("Fields");
+ if (!pFields) {
+ return FALSE;
+ }
+ m_bsEncoding = pMainDict->GetStringBy("Encoding");
+ if (bNotify && m_pFormNotify) {
+ int iRet = m_pFormNotify->BeforeFormImportData(this);
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ for (FX_DWORD i = 0; i < pFields->GetCount(); i++) {
+ CPDF_Dictionary* pField = pFields->GetDictAt(i);
+ if (!pField) {
+ continue;
+ }
+ FDF_ImportField(pField, L"", bNotify);
+ }
+ if (bNotify && m_pFormNotify) {
+ m_pFormNotify->AfterFormImportData(this);
+ }
+ return TRUE;
+}
+void CPDF_InterForm::SetFormNotify(const CPDF_FormNotify* pNotify) {
+ m_pFormNotify = (CPDF_FormNotify*)pNotify;
+}
diff --git a/core/fpdfdoc/doc_formcontrol.cpp b/core/fpdfdoc/doc_formcontrol.cpp
new file mode 100644
index 0000000000..15ff7b5e64
--- /dev/null
+++ b/core/fpdfdoc/doc_formcontrol.cpp
@@ -0,0 +1,432 @@
+// 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 <algorithm>
+
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+CPDF_FormControl::CPDF_FormControl(CPDF_FormField* pField,
+ CPDF_Dictionary* pWidgetDict) {
+ m_pField = pField;
+ m_pWidgetDict = pWidgetDict;
+ m_pForm = m_pField->m_pForm;
+}
+CFX_FloatRect CPDF_FormControl::GetRect() const {
+ return m_pWidgetDict->GetRectBy("Rect");
+}
+CFX_ByteString CPDF_FormControl::GetOnStateName() const {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CFX_ByteString csOn;
+ CPDF_Dictionary* pAP = m_pWidgetDict->GetDictBy("AP");
+ if (!pAP) {
+ return csOn;
+ }
+ CPDF_Dictionary* pN = pAP->GetDictBy("N");
+ if (!pN) {
+ return csOn;
+ }
+ for (const auto& it : *pN) {
+ if (it.first != "Off") {
+ return it.first;
+ }
+ }
+ return CFX_ByteString();
+}
+void CPDF_FormControl::SetOnStateName(const CFX_ByteString& csOn) {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CFX_ByteString csValue = csOn;
+ if (csValue.IsEmpty()) {
+ csValue = "Yes";
+ }
+ if (csValue == "Off") {
+ csValue = "Yes";
+ }
+ CFX_ByteString csAS = m_pWidgetDict->GetStringBy("AS", "Off");
+ if (csAS != "Off") {
+ m_pWidgetDict->SetAtName("AS", csValue);
+ }
+ CPDF_Dictionary* pAP = m_pWidgetDict->GetDictBy("AP");
+ if (!pAP) {
+ return;
+ }
+ for (const auto& it : *pAP) {
+ CPDF_Object* pObj1 = it.second;
+ if (!pObj1) {
+ continue;
+ }
+ CPDF_Object* pObjDirect1 = pObj1->GetDirect();
+ CPDF_Dictionary* pSubDict = pObjDirect1->AsDictionary();
+ if (!pSubDict)
+ continue;
+
+ auto subdict_it = pSubDict->begin();
+ while (subdict_it != pSubDict->end()) {
+ const CFX_ByteString& csKey2 = subdict_it->first;
+ CPDF_Object* pObj2 = subdict_it->second;
+ ++subdict_it;
+ if (!pObj2) {
+ continue;
+ }
+ if (csKey2 != "Off") {
+ pSubDict->ReplaceKey(csKey2, csValue);
+ break;
+ }
+ }
+ }
+}
+CFX_ByteString CPDF_FormControl::GetCheckedAPState() {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CFX_ByteString csOn = GetOnStateName();
+ if (GetType() == CPDF_FormField::RadioButton ||
+ GetType() == CPDF_FormField::CheckBox) {
+ if (ToArray(FPDF_GetFieldAttr(m_pField->m_pDict, "Opt"))) {
+ int iIndex = m_pField->GetControlIndex(this);
+ csOn.Format("%d", iIndex);
+ }
+ }
+ if (csOn.IsEmpty())
+ csOn = "Yes";
+ return csOn;
+}
+CFX_WideString CPDF_FormControl::GetExportValue() {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CFX_ByteString csOn = GetOnStateName();
+ if (GetType() == CPDF_FormField::RadioButton ||
+ GetType() == CPDF_FormField::CheckBox) {
+ if (CPDF_Array* pArray =
+ ToArray(FPDF_GetFieldAttr(m_pField->m_pDict, "Opt"))) {
+ int iIndex = m_pField->GetControlIndex(this);
+ csOn = pArray->GetStringAt(iIndex);
+ }
+ }
+ if (csOn.IsEmpty()) {
+ csOn = "Yes";
+ }
+ CFX_WideString csWOn = PDF_DecodeText(csOn);
+ return csWOn;
+}
+
+bool CPDF_FormControl::IsChecked() const {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CFX_ByteString csOn = GetOnStateName();
+ CFX_ByteString csAS = m_pWidgetDict->GetStringBy("AS");
+ return csAS == csOn;
+}
+
+bool CPDF_FormControl::IsDefaultChecked() const {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CPDF_Object* pDV = FPDF_GetFieldAttr(m_pField->m_pDict, "DV");
+ if (!pDV) {
+ return FALSE;
+ }
+ CFX_ByteString csDV = pDV->GetString();
+ CFX_ByteString csOn = GetOnStateName();
+ return (csDV == csOn);
+}
+
+void CPDF_FormControl::CheckControl(FX_BOOL bChecked) {
+ ASSERT(GetType() == CPDF_FormField::CheckBox ||
+ GetType() == CPDF_FormField::RadioButton);
+ CFX_ByteString csOn = GetOnStateName();
+ CFX_ByteString csOldAS = m_pWidgetDict->GetStringBy("AS", "Off");
+ CFX_ByteString csAS = "Off";
+ if (bChecked) {
+ csAS = csOn;
+ }
+ if (csOldAS == csAS) {
+ return;
+ }
+ m_pWidgetDict->SetAtName("AS", csAS);
+ m_pForm->m_bUpdated = TRUE;
+}
+CPDF_Stream* FPDFDOC_GetAnnotAP(CPDF_Dictionary* pAnnotDict,
+ CPDF_Annot::AppearanceMode mode);
+void CPDF_FormControl::DrawControl(CFX_RenderDevice* pDevice,
+ CFX_Matrix* pMatrix,
+ CPDF_Page* pPage,
+ CPDF_Annot::AppearanceMode mode,
+ const CPDF_RenderOptions* pOptions) {
+ if (m_pWidgetDict->GetIntegerBy("F") & ANNOTFLAG_HIDDEN) {
+ return;
+ }
+ CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pWidgetDict, mode);
+ if (!pStream) {
+ return;
+ }
+ CFX_FloatRect form_bbox = pStream->GetDict()->GetRectBy("BBox");
+ CFX_Matrix form_matrix = pStream->GetDict()->GetMatrixBy("Matrix");
+ form_matrix.TransformRect(form_bbox);
+ CFX_FloatRect arect = m_pWidgetDict->GetRectBy("Rect");
+ CFX_Matrix matrix;
+ matrix.MatchRect(arect, form_bbox);
+ matrix.Concat(*pMatrix);
+ CPDF_Form form(m_pField->m_pForm->m_pDocument,
+ m_pField->m_pForm->m_pFormDict->GetDictBy("DR"), pStream);
+ form.ParseContent(NULL, NULL, NULL, NULL);
+ CPDF_RenderContext context(pPage);
+ context.AppendLayer(&form, &matrix);
+ context.Render(pDevice, pOptions, nullptr);
+}
+static const FX_CHAR* const g_sHighlightingMode[] = {
+ // Must match order of HiglightingMode enum.
+ "N", "I", "O", "P", "T", nullptr};
+CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode() {
+ if (!m_pWidgetDict) {
+ return Invert;
+ }
+ CFX_ByteString csH = m_pWidgetDict->GetStringBy("H", "I");
+ for (int i = 0; g_sHighlightingMode[i]; ++i) {
+ if (csH.Equal(g_sHighlightingMode[i]))
+ return static_cast<HighlightingMode>(i);
+ }
+ return Invert;
+}
+
+CPDF_ApSettings CPDF_FormControl::GetMK() const {
+ return CPDF_ApSettings(m_pWidgetDict ? m_pWidgetDict->GetDictBy("MK")
+ : nullptr);
+}
+
+bool CPDF_FormControl::HasMKEntry(CFX_ByteString csEntry) const {
+ return GetMK().HasMKEntry(csEntry);
+}
+
+int CPDF_FormControl::GetRotation() {
+ return GetMK().GetRotation();
+}
+
+FX_ARGB CPDF_FormControl::GetColor(int& iColorType, CFX_ByteString csEntry) {
+ return GetMK().GetColor(iColorType, csEntry);
+}
+
+FX_FLOAT CPDF_FormControl::GetOriginalColor(int index, CFX_ByteString csEntry) {
+ return GetMK().GetOriginalColor(index, csEntry);
+}
+
+void CPDF_FormControl::GetOriginalColor(int& iColorType,
+ FX_FLOAT fc[4],
+ CFX_ByteString csEntry) {
+ GetMK().GetOriginalColor(iColorType, fc, csEntry);
+}
+CFX_WideString CPDF_FormControl::GetCaption(CFX_ByteString csEntry) {
+ return GetMK().GetCaption(csEntry);
+}
+
+CPDF_Stream* CPDF_FormControl::GetIcon(CFX_ByteString csEntry) {
+ return GetMK().GetIcon(csEntry);
+}
+
+CPDF_IconFit CPDF_FormControl::GetIconFit() {
+ return GetMK().GetIconFit();
+}
+
+int CPDF_FormControl::GetTextPosition() {
+ return GetMK().GetTextPosition();
+}
+
+CPDF_Action CPDF_FormControl::GetAction() {
+ if (!m_pWidgetDict) {
+ return CPDF_Action();
+ }
+ if (m_pWidgetDict->KeyExist("A")) {
+ return CPDF_Action(m_pWidgetDict->GetDictBy("A"));
+ }
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "A");
+ if (!pObj) {
+ return CPDF_Action();
+ }
+ return CPDF_Action(pObj->GetDict());
+}
+
+CPDF_AAction CPDF_FormControl::GetAdditionalAction() {
+ if (!m_pWidgetDict)
+ return CPDF_AAction();
+
+ if (m_pWidgetDict->KeyExist("AA"))
+ return CPDF_AAction(m_pWidgetDict->GetDictBy("AA"));
+ return m_pField->GetAdditionalAction();
+}
+
+CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() {
+ if (!m_pWidgetDict)
+ return CPDF_DefaultAppearance();
+
+ if (m_pWidgetDict->KeyExist("DA"))
+ return CPDF_DefaultAppearance(m_pWidgetDict->GetStringBy("DA"));
+
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "DA");
+ if (pObj)
+ return CPDF_DefaultAppearance(pObj->GetString());
+ return m_pField->m_pForm->GetDefaultAppearance();
+}
+
+CPDF_Font* CPDF_FormControl::GetDefaultControlFont() {
+ CPDF_DefaultAppearance cDA = GetDefaultAppearance();
+ CFX_ByteString csFontNameTag;
+ FX_FLOAT fFontSize;
+ cDA.GetFont(csFontNameTag, fFontSize);
+ if (csFontNameTag.IsEmpty())
+ return nullptr;
+
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pWidgetDict, "DR");
+ if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
+ CPDF_Dictionary* pFonts = pDict->GetDictBy("Font");
+ if (pFonts) {
+ CPDF_Dictionary* pElement = pFonts->GetDictBy(csFontNameTag);
+ if (pElement) {
+ CPDF_Font* pFont = m_pField->m_pForm->m_pDocument->LoadFont(pElement);
+ if (pFont) {
+ return pFont;
+ }
+ }
+ }
+ }
+ if (CPDF_Font* pFormFont = m_pField->m_pForm->GetFormFont(csFontNameTag))
+ return pFormFont;
+
+ CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictBy("P");
+ pObj = FPDF_GetFieldAttr(pPageDict, "Resources");
+ if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
+ CPDF_Dictionary* pFonts = pDict->GetDictBy("Font");
+ if (pFonts) {
+ CPDF_Dictionary* pElement = pFonts->GetDictBy(csFontNameTag);
+ if (pElement) {
+ CPDF_Font* pFont = m_pField->m_pForm->m_pDocument->LoadFont(pElement);
+ if (pFont) {
+ return pFont;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+int CPDF_FormControl::GetControlAlignment() {
+ if (!m_pWidgetDict) {
+ return 0;
+ }
+ if (m_pWidgetDict->KeyExist("Q")) {
+ return m_pWidgetDict->GetIntegerBy("Q", 0);
+ }
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "Q");
+ if (pObj)
+ return pObj->GetInteger();
+ return m_pField->m_pForm->GetFormAlignment();
+}
+
+CPDF_ApSettings::CPDF_ApSettings(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
+
+bool CPDF_ApSettings::HasMKEntry(const CFX_ByteStringC& csEntry) const {
+ return m_pDict && m_pDict->KeyExist(csEntry);
+}
+
+int CPDF_ApSettings::GetRotation() const {
+ return m_pDict ? m_pDict->GetIntegerBy("R") : 0;
+}
+
+FX_ARGB CPDF_ApSettings::GetColor(int& iColorType,
+ const CFX_ByteStringC& csEntry) const {
+ iColorType = COLORTYPE_TRANSPARENT;
+ if (!m_pDict)
+ return 0;
+
+ CPDF_Array* pEntry = m_pDict->GetArrayBy(csEntry);
+ if (!pEntry)
+ return 0;
+
+ FX_ARGB color = 0;
+ FX_DWORD dwCount = pEntry->GetCount();
+ if (dwCount == 1) {
+ iColorType = COLORTYPE_GRAY;
+ FX_FLOAT g = pEntry->GetNumberAt(0) * 255;
+ color = ArgbEncode(255, (int)g, (int)g, (int)g);
+ } else if (dwCount == 3) {
+ iColorType = COLORTYPE_RGB;
+ FX_FLOAT r = pEntry->GetNumberAt(0) * 255;
+ FX_FLOAT g = pEntry->GetNumberAt(1) * 255;
+ FX_FLOAT b = pEntry->GetNumberAt(2) * 255;
+ color = ArgbEncode(255, (int)r, (int)g, (int)b);
+ } else if (dwCount == 4) {
+ iColorType = COLORTYPE_CMYK;
+ FX_FLOAT c = pEntry->GetNumberAt(0);
+ FX_FLOAT m = pEntry->GetNumberAt(1);
+ FX_FLOAT y = pEntry->GetNumberAt(2);
+ FX_FLOAT k = pEntry->GetNumberAt(3);
+ FX_FLOAT r = 1.0f - std::min(1.0f, c + k);
+ FX_FLOAT g = 1.0f - std::min(1.0f, m + k);
+ FX_FLOAT b = 1.0f - std::min(1.0f, y + k);
+ color = ArgbEncode(255, (int)(r * 255), (int)(g * 255), (int)(b * 255));
+ }
+ return color;
+}
+
+FX_FLOAT CPDF_ApSettings::GetOriginalColor(
+ int index,
+ const CFX_ByteStringC& csEntry) const {
+ if (!m_pDict)
+ return 0;
+
+ CPDF_Array* pEntry = m_pDict->GetArrayBy(csEntry);
+ return pEntry ? pEntry->GetNumberAt(index) : 0;
+}
+
+void CPDF_ApSettings::GetOriginalColor(int& iColorType,
+ FX_FLOAT fc[4],
+ const CFX_ByteStringC& csEntry) const {
+ iColorType = COLORTYPE_TRANSPARENT;
+ for (int i = 0; i < 4; i++) {
+ fc[i] = 0;
+ }
+ if (!m_pDict) {
+ return;
+ }
+ CPDF_Array* pEntry = m_pDict->GetArrayBy(csEntry);
+ if (!pEntry) {
+ return;
+ }
+ FX_DWORD dwCount = pEntry->GetCount();
+ if (dwCount == 1) {
+ iColorType = COLORTYPE_GRAY;
+ fc[0] = pEntry->GetNumberAt(0);
+ } else if (dwCount == 3) {
+ iColorType = COLORTYPE_RGB;
+ fc[0] = pEntry->GetNumberAt(0);
+ fc[1] = pEntry->GetNumberAt(1);
+ fc[2] = pEntry->GetNumberAt(2);
+ } else if (dwCount == 4) {
+ iColorType = COLORTYPE_CMYK;
+ fc[0] = pEntry->GetNumberAt(0);
+ fc[1] = pEntry->GetNumberAt(1);
+ fc[2] = pEntry->GetNumberAt(2);
+ fc[3] = pEntry->GetNumberAt(3);
+ }
+}
+
+CFX_WideString CPDF_ApSettings::GetCaption(
+ const CFX_ByteStringC& csEntry) const {
+ return m_pDict ? m_pDict->GetUnicodeTextBy(csEntry) : CFX_WideString();
+}
+
+CPDF_Stream* CPDF_ApSettings::GetIcon(const CFX_ByteStringC& csEntry) const {
+ return m_pDict ? m_pDict->GetStreamBy(csEntry) : nullptr;
+}
+
+CPDF_IconFit CPDF_ApSettings::GetIconFit() const {
+ return CPDF_IconFit(m_pDict ? m_pDict->GetDictBy("IF") : nullptr);
+}
+
+int CPDF_ApSettings::GetTextPosition() const {
+ return m_pDict ? m_pDict->GetIntegerBy("TP", TEXTPOS_CAPTION)
+ : TEXTPOS_CAPTION;
+}
diff --git a/core/fpdfdoc/doc_formfield.cpp b/core/fpdfdoc/doc_formfield.cpp
new file mode 100644
index 0000000000..7ad8cacea9
--- /dev/null
+++ b/core/fpdfdoc/doc_formfield.cpp
@@ -0,0 +1,1097 @@
+// 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 "core/fpdfdoc/doc_utils.h"
+#include "core/include/fpdfapi/cfdf_document.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_number.h"
+#include "core/include/fpdfapi/cpdf_simple_parser.h"
+#include "core/include/fpdfapi/cpdf_string.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+FX_BOOL PDF_FormField_IsUnison(CPDF_FormField* pField) {
+ FX_BOOL bUnison = FALSE;
+ if (pField->GetType() == CPDF_FormField::CheckBox) {
+ bUnison = TRUE;
+ } else {
+ FX_DWORD dwFlags = pField->GetFieldFlags();
+ bUnison = ((dwFlags & 0x2000000) != 0);
+ }
+ return bUnison;
+}
+CPDF_FormField::CPDF_FormField(CPDF_InterForm* pForm, CPDF_Dictionary* pDict) {
+ m_pDict = pDict;
+ m_Type = Unknown;
+ m_pForm = pForm;
+ m_pFont = NULL;
+ m_FontSize = 0;
+ SyncFieldFlags();
+}
+CPDF_FormField::~CPDF_FormField() {}
+void CPDF_FormField::SyncFieldFlags() {
+ CFX_ByteString type_name = FPDF_GetFieldAttr(m_pDict, "FT")
+ ? FPDF_GetFieldAttr(m_pDict, "FT")->GetString()
+ : CFX_ByteString();
+ FX_DWORD flags = FPDF_GetFieldAttr(m_pDict, "Ff")
+ ? FPDF_GetFieldAttr(m_pDict, "Ff")->GetInteger()
+ : 0;
+ m_Flags = 0;
+ if (flags & 1) {
+ m_Flags |= FORMFIELD_READONLY;
+ }
+ if (flags & 2) {
+ m_Flags |= FORMFIELD_REQUIRED;
+ }
+ if (flags & 4) {
+ m_Flags |= FORMFIELD_NOEXPORT;
+ }
+ if (type_name == "Btn") {
+ if (flags & 0x8000) {
+ m_Type = RadioButton;
+ if (flags & 0x4000) {
+ m_Flags |= FORMRADIO_NOTOGGLEOFF;
+ }
+ if (flags & 0x2000000) {
+ m_Flags |= FORMRADIO_UNISON;
+ }
+ } else if (flags & 0x10000) {
+ m_Type = PushButton;
+ } else {
+ m_Type = CheckBox;
+ }
+ } else if (type_name == "Tx") {
+ if (flags & 0x100000) {
+ m_Type = File;
+ } else if (flags & 0x2000000) {
+ m_Type = RichText;
+ } else {
+ m_Type = Text;
+ if (flags & 0x1000) {
+ m_Flags |= FORMTEXT_MULTILINE;
+ }
+ if (flags & 0x2000) {
+ m_Flags |= FORMTEXT_PASSWORD;
+ }
+ if (flags & 0x800000) {
+ m_Flags |= FORMTEXT_NOSCROLL;
+ }
+ if (flags & 0x100000) {
+ m_Flags |= FORMTEXT_COMB;
+ }
+ }
+ LoadDA();
+ } else if (type_name == "Ch") {
+ if (flags & 0x20000) {
+ m_Type = ComboBox;
+ if (flags & 0x40000) {
+ m_Flags |= FORMCOMBO_EDIT;
+ }
+ } else {
+ m_Type = ListBox;
+ if (flags & 0x200000) {
+ m_Flags |= FORMLIST_MULTISELECT;
+ }
+ }
+ LoadDA();
+ } else if (type_name == "Sig") {
+ m_Type = Sign;
+ }
+}
+CFX_WideString CPDF_FormField::GetFullName() {
+ return ::GetFullName(m_pDict);
+}
+FX_BOOL CPDF_FormField::ResetField(FX_BOOL bNotify) {
+ switch (m_Type) {
+ case CPDF_FormField::CheckBox:
+ case CPDF_FormField::RadioButton: {
+ int iCount = CountControls();
+ if (iCount) {
+ // TODO(weili): Check whether anything special needs to be done for
+ // unison field. Otherwise, merge these branches.
+ if (PDF_FormField_IsUnison(this)) {
+ for (int i = 0; i < iCount; i++) {
+ CheckControl(i, GetControl(i)->IsDefaultChecked(), FALSE);
+ }
+ } else {
+ for (int i = 0; i < iCount; i++) {
+ CheckControl(i, GetControl(i)->IsDefaultChecked(), FALSE);
+ }
+ }
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterCheckedStatusChange(this);
+ }
+ } break;
+ case CPDF_FormField::ComboBox: {
+ CFX_WideString csValue;
+ ClearSelection();
+ int iIndex = GetDefaultSelectedItem();
+ if (iIndex >= 0) {
+ csValue = GetOptionLabel(iIndex);
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ SetItemSelection(iIndex, TRUE);
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ } break;
+ case CPDF_FormField::ListBox: {
+ CFX_WideString csValue;
+ ClearSelection();
+ int iIndex = GetDefaultSelectedItem();
+ if (iIndex >= 0) {
+ csValue = GetOptionLabel(iIndex);
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ SetItemSelection(iIndex, TRUE);
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ }
+ } break;
+ case CPDF_FormField::Text:
+ case CPDF_FormField::RichText:
+ case CPDF_FormField::File:
+ default: {
+ CPDF_Object* pDV = FPDF_GetFieldAttr(m_pDict, "DV");
+ CFX_WideString csDValue;
+ if (pDV) {
+ csDValue = pDV->GetUnicodeText();
+ }
+ CPDF_Object* pV = FPDF_GetFieldAttr(m_pDict, "V");
+ CFX_WideString csValue;
+ if (pV) {
+ csValue = pV->GetUnicodeText();
+ }
+ CPDF_Object* pRV = FPDF_GetFieldAttr(m_pDict, "RV");
+ if (!pRV && (csDValue == csValue)) {
+ return FALSE;
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csDValue);
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ if (pDV) {
+ CPDF_Object* pClone = pDV->Clone();
+ if (!pClone) {
+ return FALSE;
+ }
+ m_pDict->SetAt("V", pClone);
+ if (pRV) {
+ CPDF_Object* pCloneR = pDV->Clone();
+ m_pDict->SetAt("RV", pCloneR);
+ }
+ } else {
+ m_pDict->RemoveAt("V");
+ m_pDict->RemoveAt("RV");
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ } break;
+ }
+ return TRUE;
+}
+int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) {
+ if (!pControl) {
+ return -1;
+ }
+ for (int i = 0; i < m_ControlList.GetSize(); i++) {
+ if (m_ControlList.GetAt(i) == pControl)
+ return i;
+ }
+ return -1;
+}
+int CPDF_FormField::GetFieldType() {
+ switch (m_Type) {
+ case PushButton:
+ return FIELDTYPE_PUSHBUTTON;
+ case CheckBox:
+ return FIELDTYPE_CHECKBOX;
+ case RadioButton:
+ return FIELDTYPE_RADIOBUTTON;
+ case ComboBox:
+ return FIELDTYPE_COMBOBOX;
+ case ListBox:
+ return FIELDTYPE_LISTBOX;
+ case Text:
+ case RichText:
+ case File:
+ return FIELDTYPE_TEXTFIELD;
+ case Sign:
+ return FIELDTYPE_SIGNATURE;
+ default:
+ break;
+ }
+ return FIELDTYPE_UNKNOWN;
+}
+
+CPDF_AAction CPDF_FormField::GetAdditionalAction() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "AA");
+ return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
+}
+
+CFX_WideString CPDF_FormField::GetAlternateName() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "TU");
+ if (!pObj) {
+ return L"";
+ }
+ return pObj->GetUnicodeText();
+}
+CFX_WideString CPDF_FormField::GetMappingName() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "TM");
+ if (!pObj) {
+ return L"";
+ }
+ return pObj->GetUnicodeText();
+}
+FX_DWORD CPDF_FormField::GetFieldFlags() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "Ff");
+ if (!pObj) {
+ return 0;
+ }
+ return pObj->GetInteger();
+}
+CFX_ByteString CPDF_FormField::GetDefaultStyle() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "DS");
+ if (!pObj) {
+ return "";
+ }
+ return pObj->GetString();
+}
+CFX_WideString CPDF_FormField::GetRichTextString() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "RV");
+ if (!pObj) {
+ return L"";
+ }
+ return pObj->GetUnicodeText();
+}
+CFX_WideString CPDF_FormField::GetValue(FX_BOOL bDefault) {
+ if (GetType() == CheckBox || GetType() == RadioButton) {
+ return GetCheckValue(bDefault);
+ }
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, bDefault ? "DV" : "V");
+ if (!pValue) {
+ if (!bDefault) {
+ if (m_Type == RichText) {
+ pValue = FPDF_GetFieldAttr(m_pDict, "V");
+ }
+ if (!pValue && m_Type != Text) {
+ pValue = FPDF_GetFieldAttr(m_pDict, "DV");
+ }
+ }
+ if (!pValue) {
+ return CFX_WideString();
+ }
+ }
+ switch (pValue->GetType()) {
+ case CPDF_Object::STRING:
+ case CPDF_Object::STREAM:
+ return pValue->GetUnicodeText();
+ case CPDF_Object::ARRAY:
+ pValue = pValue->AsArray()->GetElementValue(0);
+ if (pValue)
+ return pValue->GetUnicodeText();
+ break;
+ default:
+ break;
+ }
+ return CFX_WideString();
+}
+CFX_WideString CPDF_FormField::GetValue() {
+ return GetValue(FALSE);
+}
+CFX_WideString CPDF_FormField::GetDefaultValue() {
+ return GetValue(TRUE);
+}
+FX_BOOL CPDF_FormField::SetValue(const CFX_WideString& value,
+ FX_BOOL bDefault,
+ FX_BOOL bNotify) {
+ switch (m_Type) {
+ case CheckBox:
+ case RadioButton: {
+ SetCheckValue(value, bDefault, bNotify);
+ return TRUE;
+ }
+ case File:
+ case RichText:
+ case Text:
+ case ComboBox: {
+ CFX_WideString csValue = value;
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ int iIndex = FindOptionValue(csValue);
+ if (iIndex < 0) {
+ CFX_ByteString bsEncodeText = PDF_EncodeText(csValue);
+ m_pDict->SetAtString(bDefault ? "DV" : "V", bsEncodeText);
+ if (m_Type == RichText && !bDefault) {
+ m_pDict->SetAtString("RV", bsEncodeText);
+ }
+ m_pDict->RemoveAt("I");
+ } else {
+ m_pDict->SetAtString(bDefault ? "DV" : "V", PDF_EncodeText(csValue));
+ if (bDefault) {
+ } else {
+ ClearSelection();
+ SetItemSelection(iIndex, TRUE);
+ }
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ } break;
+ case ListBox: {
+ int iIndex = FindOptionValue(value);
+ if (iIndex < 0) {
+ return FALSE;
+ }
+ if (bDefault && iIndex == GetDefaultSelectedItem()) {
+ return FALSE;
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ CFX_WideString csValue = value;
+ int iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ if (bDefault) {
+ } else {
+ ClearSelection();
+ SetItemSelection(iIndex, TRUE);
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ if (CPDF_InterForm::m_bUpdateAP) {
+ UpdateAP(NULL);
+ }
+ return TRUE;
+}
+FX_BOOL CPDF_FormField::SetValue(const CFX_WideString& value, FX_BOOL bNotify) {
+ return SetValue(value, FALSE, bNotify);
+}
+int CPDF_FormField::GetMaxLen() {
+ if (CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "MaxLen"))
+ return pObj->GetInteger();
+
+ for (int i = 0; i < m_ControlList.GetSize(); i++) {
+ CPDF_FormControl* pControl = m_ControlList.GetAt(i);
+ if (!pControl)
+ continue;
+
+ CPDF_Dictionary* pWidgetDict = pControl->m_pWidgetDict;
+ if (pWidgetDict->KeyExist("MaxLen"))
+ return pWidgetDict->GetIntegerBy("MaxLen");
+ }
+ return 0;
+}
+int CPDF_FormField::CountSelectedItems() {
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, "V");
+ if (!pValue) {
+ pValue = FPDF_GetFieldAttr(m_pDict, "I");
+ if (!pValue)
+ return 0;
+ }
+
+ if (pValue->IsString() || pValue->IsNumber())
+ return pValue->GetString().IsEmpty() ? 0 : 1;
+ if (CPDF_Array* pArray = pValue->AsArray())
+ return pArray->GetCount();
+ return 0;
+}
+int CPDF_FormField::GetSelectedIndex(int index) {
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, "V");
+ if (!pValue) {
+ pValue = FPDF_GetFieldAttr(m_pDict, "I");
+ if (!pValue)
+ return -1;
+ }
+ if (pValue->IsNumber())
+ return pValue->GetInteger();
+
+ CFX_WideString sel_value;
+ if (pValue->IsString()) {
+ if (index != 0)
+ return -1;
+ sel_value = pValue->GetUnicodeText();
+ } else {
+ CPDF_Array* pArray = pValue->AsArray();
+ if (!pArray || index < 0)
+ return -1;
+
+ CPDF_Object* elementValue = pArray->GetElementValue(index);
+ sel_value =
+ elementValue ? elementValue->GetUnicodeText() : CFX_WideString();
+ }
+ if (index < CountSelectedOptions()) {
+ int iOptIndex = GetSelectedOptionIndex(index);
+ CFX_WideString csOpt = GetOptionValue(iOptIndex);
+ if (csOpt == sel_value) {
+ return iOptIndex;
+ }
+ }
+ int nOpts = CountOptions();
+ for (int i = 0; i < nOpts; i++) {
+ if (sel_value == GetOptionValue(i)) {
+ return i;
+ }
+ }
+ return -1;
+}
+FX_BOOL CPDF_FormField::ClearSelection(FX_BOOL bNotify) {
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ CFX_WideString csValue;
+ int iIndex = GetSelectedIndex(0);
+ if (iIndex >= 0) {
+ csValue = GetOptionLabel(iIndex);
+ }
+ if (GetType() == ListBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ }
+ if (GetType() == ComboBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ }
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ m_pDict->RemoveAt("V");
+ m_pDict->RemoveAt("I");
+ if (bNotify && m_pForm->m_pFormNotify) {
+ if (GetType() == ListBox) {
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ }
+ if (GetType() == ComboBox) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ }
+ if (CPDF_InterForm::m_bUpdateAP) {
+ UpdateAP(NULL);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+FX_BOOL CPDF_FormField::IsItemSelected(int index) {
+ ASSERT(GetType() == ComboBox || GetType() == ListBox);
+ if (index < 0 || index >= CountOptions()) {
+ return FALSE;
+ }
+ if (IsOptionSelected(index)) {
+ return TRUE;
+ }
+ CFX_WideString opt_value = GetOptionValue(index);
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, "V");
+ if (!pValue) {
+ pValue = FPDF_GetFieldAttr(m_pDict, "I");
+ if (!pValue) {
+ return FALSE;
+ }
+ }
+
+ if (pValue->IsString())
+ return pValue->GetUnicodeText() == opt_value;
+
+ if (pValue->IsNumber()) {
+ if (pValue->GetString().IsEmpty())
+ return FALSE;
+ return (pValue->GetInteger() == index);
+ }
+
+ CPDF_Array* pArray = pValue->AsArray();
+ if (!pArray)
+ return FALSE;
+
+ int iPos = -1;
+ for (int j = 0; j < CountSelectedOptions(); j++) {
+ if (GetSelectedOptionIndex(j) == index) {
+ iPos = j;
+ break;
+ }
+ }
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++)
+ if (pArray->GetElementValue(i)->GetUnicodeText() == opt_value &&
+ (int)i == iPos) {
+ return TRUE;
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_FormField::SetItemSelection(int index,
+ FX_BOOL bSelected,
+ FX_BOOL bNotify) {
+ ASSERT(GetType() == ComboBox || GetType() == ListBox);
+ if (index < 0 || index >= CountOptions()) {
+ return FALSE;
+ }
+ CFX_WideString opt_value = GetOptionValue(index);
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ if (GetType() == ListBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, opt_value);
+ }
+ if (GetType() == ComboBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, opt_value);
+ }
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ if (!bSelected) {
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, "V");
+ if (pValue) {
+ if (m_Type == ListBox) {
+ SelectOption(index, FALSE);
+ if (pValue->IsString()) {
+ if (pValue->GetUnicodeText() == opt_value) {
+ m_pDict->RemoveAt("V");
+ }
+ } else if (pValue->IsArray()) {
+ CPDF_Array* pArray = new CPDF_Array;
+ int iCount = CountOptions();
+ for (int i = 0; i < iCount; i++) {
+ if (i != index) {
+ if (IsItemSelected(i)) {
+ opt_value = GetOptionValue(i);
+ pArray->AddString(PDF_EncodeText(opt_value));
+ }
+ }
+ }
+ if (pArray->GetCount() < 1) {
+ pArray->Release();
+ } else {
+ m_pDict->SetAt("V", pArray);
+ }
+ }
+ } else if (m_Type == ComboBox) {
+ m_pDict->RemoveAt("V");
+ m_pDict->RemoveAt("I");
+ }
+ }
+ } else {
+ if (m_Type == ListBox) {
+ SelectOption(index, TRUE);
+ if (!(m_Flags & FORMLIST_MULTISELECT)) {
+ m_pDict->SetAtString("V", PDF_EncodeText(opt_value));
+ } else {
+ CPDF_Array* pArray = new CPDF_Array;
+ int iCount = CountOptions();
+ for (int i = 0; i < iCount; i++) {
+ FX_BOOL bSelected;
+ if (i != index) {
+ bSelected = IsItemSelected(i);
+ } else {
+ bSelected = TRUE;
+ }
+ if (bSelected) {
+ opt_value = GetOptionValue(i);
+ pArray->AddString(PDF_EncodeText(opt_value));
+ }
+ }
+ m_pDict->SetAt("V", pArray);
+ }
+ } else if (m_Type == ComboBox) {
+ m_pDict->SetAtString("V", PDF_EncodeText(opt_value));
+ CPDF_Array* pI = new CPDF_Array;
+ pI->AddInteger(index);
+ m_pDict->SetAt("I", pI);
+ }
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ if (GetType() == ListBox) {
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ }
+ if (GetType() == ComboBox) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ }
+ if (CPDF_InterForm::m_bUpdateAP) {
+ UpdateAP(NULL);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+FX_BOOL CPDF_FormField::IsItemDefaultSelected(int index) {
+ ASSERT(GetType() == ComboBox || GetType() == ListBox);
+ if (index < 0 || index >= CountOptions()) {
+ return FALSE;
+ }
+ int iDVIndex = GetDefaultSelectedItem();
+ if (iDVIndex < 0) {
+ return FALSE;
+ }
+ return (iDVIndex == index);
+}
+int CPDF_FormField::GetDefaultSelectedItem() {
+ ASSERT(GetType() == ComboBox || GetType() == ListBox);
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, "DV");
+ if (!pValue) {
+ return -1;
+ }
+ CFX_WideString csDV = pValue->GetUnicodeText();
+ if (csDV.IsEmpty()) {
+ return -1;
+ }
+ int iCount = CountOptions();
+ for (int i = 0; i < iCount; i++) {
+ if (csDV == GetOptionValue(i)) {
+ return i;
+ }
+ }
+ return -1;
+}
+void CPDF_FormField::UpdateAP(CPDF_FormControl* pControl) {
+ if (m_Type == PushButton) {
+ return;
+ }
+ if (m_Type == RadioButton || m_Type == CheckBox) {
+ return;
+ }
+ if (!m_pForm->m_bGenerateAP) {
+ return;
+ }
+ for (int i = 0; i < CountControls(); i++) {
+ CPDF_FormControl* pControl = GetControl(i);
+ FPDF_GenerateAP(m_pForm->m_pDocument, pControl->m_pWidgetDict);
+ }
+}
+int CPDF_FormField::CountOptions() {
+ CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict, "Opt"));
+ return pArray ? pArray->GetCount() : 0;
+}
+CFX_WideString CPDF_FormField::GetOptionText(int index, int sub_index) {
+ CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict, "Opt"));
+ if (!pArray)
+ return CFX_WideString();
+
+ CPDF_Object* pOption = pArray->GetElementValue(index);
+ if (!pOption)
+ return CFX_WideString();
+ if (CPDF_Array* pOptionArray = pOption->AsArray())
+ pOption = pOptionArray->GetElementValue(sub_index);
+
+ CPDF_String* pString = ToString(pOption);
+ return pString ? pString->GetUnicodeText() : CFX_WideString();
+}
+CFX_WideString CPDF_FormField::GetOptionLabel(int index) {
+ return GetOptionText(index, 1);
+}
+CFX_WideString CPDF_FormField::GetOptionValue(int index) {
+ return GetOptionText(index, 0);
+}
+int CPDF_FormField::FindOption(CFX_WideString csOptLabel) {
+ int iCount = CountOptions();
+ for (int i = 0; i < iCount; i++) {
+ CFX_WideString csValue = GetOptionValue(i);
+ if (csValue == csOptLabel) {
+ return i;
+ }
+ }
+ return -1;
+}
+int CPDF_FormField::FindOptionValue(const CFX_WideString& csOptValue,
+ int iStartIndex) {
+ if (iStartIndex < 0) {
+ iStartIndex = 0;
+ }
+ int iCount = CountOptions();
+ for (; iStartIndex < iCount; iStartIndex++) {
+ CFX_WideString csValue = GetOptionValue(iStartIndex);
+ if (csValue == csOptValue) {
+ return iStartIndex;
+ }
+ }
+ return -1;
+}
+#ifdef PDF_ENABLE_XFA
+int CPDF_FormField::InsertOption(CFX_WideString csOptLabel,
+ int index,
+ FX_BOOL bNotify) {
+ if (csOptLabel.IsEmpty())
+ return -1;
+
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ if (GetType() == ListBox)
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csOptLabel);
+ if (GetType() == ComboBox)
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csOptLabel);
+ if (iRet < 0)
+ return -1;
+ }
+
+ CFX_ByteString csStr = PDF_EncodeText(csOptLabel, csOptLabel.GetLength());
+ CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict, "Opt");
+ CPDF_Array* pOpt = ToArray(pValue);
+ if (!pOpt) {
+ pOpt = new CPDF_Array;
+ m_pDict->SetAt("Opt", pOpt);
+ }
+
+ int iCount = (int)pOpt->GetCount();
+ if (index < 0 || index >= iCount) {
+ pOpt->AddString(csStr);
+ index = iCount;
+ } else {
+ CPDF_String* pString = new CPDF_String(csStr, FALSE);
+ pOpt->InsertAt(index, pString);
+ }
+
+ if (bNotify && m_pForm->m_pFormNotify) {
+ if (GetType() == ListBox)
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ if (GetType() == ComboBox)
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return index;
+}
+FX_BOOL CPDF_FormField::ClearOptions(FX_BOOL bNotify) {
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ CFX_WideString csValue;
+ int iIndex = GetSelectedIndex(0);
+ if (iIndex >= 0)
+ csValue = GetOptionLabel(iIndex);
+ if (GetType() == ListBox)
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ if (GetType() == ComboBox)
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ if (iRet < 0)
+ return FALSE;
+ }
+
+ m_pDict->RemoveAt("Opt");
+ m_pDict->RemoveAt("V");
+ m_pDict->RemoveAt("DV");
+ m_pDict->RemoveAt("I");
+ m_pDict->RemoveAt("TI");
+
+ if (bNotify && m_pForm->m_pFormNotify) {
+ if (GetType() == ListBox)
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ if (GetType() == ComboBox)
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+#endif // PDF_ENABLE_XFA
+FX_BOOL CPDF_FormField::CheckControl(int iControlIndex,
+ bool bChecked,
+ bool bNotify) {
+ ASSERT(GetType() == CheckBox || GetType() == RadioButton);
+ CPDF_FormControl* pControl = GetControl(iControlIndex);
+ if (!pControl) {
+ return FALSE;
+ }
+ if (!bChecked && pControl->IsChecked() == bChecked) {
+ return FALSE;
+ }
+ CFX_WideString csWExport = pControl->GetExportValue();
+ CFX_ByteString csBExport = PDF_EncodeText(csWExport);
+ int iCount = CountControls();
+ FX_BOOL bUnison = PDF_FormField_IsUnison(this);
+ for (int i = 0; i < iCount; i++) {
+ CPDF_FormControl* pCtrl = GetControl(i);
+ if (bUnison) {
+ CFX_WideString csEValue = pCtrl->GetExportValue();
+ if (csEValue == csWExport) {
+ if (pCtrl->GetOnStateName() == pControl->GetOnStateName()) {
+ pCtrl->CheckControl(bChecked);
+ } else if (bChecked) {
+ pCtrl->CheckControl(FALSE);
+ }
+ } else if (bChecked) {
+ pCtrl->CheckControl(FALSE);
+ }
+ } else {
+ if (i == iControlIndex) {
+ pCtrl->CheckControl(bChecked);
+ } else if (bChecked) {
+ pCtrl->CheckControl(FALSE);
+ }
+ }
+ }
+ CPDF_Object* pOpt = FPDF_GetFieldAttr(m_pDict, "Opt");
+ if (!ToArray(pOpt)) {
+ if (bChecked) {
+ m_pDict->SetAtName("V", csBExport);
+ } else {
+ CFX_ByteString csV;
+ CPDF_Object* pV = FPDF_GetFieldAttr(m_pDict, "V");
+ if (pV) {
+ csV = pV->GetString();
+ }
+ if (csV == csBExport) {
+ m_pDict->SetAtName("V", "Off");
+ }
+ }
+ } else if (bChecked) {
+ CFX_ByteString csIndex;
+ csIndex.Format("%d", iControlIndex);
+ m_pDict->SetAtName("V", csIndex);
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterCheckedStatusChange(this);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+CFX_WideString CPDF_FormField::GetCheckValue(FX_BOOL bDefault) {
+ ASSERT(GetType() == CheckBox || GetType() == RadioButton);
+ CFX_WideString csExport = L"Off";
+ FX_BOOL bChecked;
+ int iCount = CountControls();
+ for (int i = 0; i < iCount; i++) {
+ CPDF_FormControl* pControl = GetControl(i);
+ if (bDefault) {
+ bChecked = pControl->IsDefaultChecked();
+ } else {
+ bChecked = pControl->IsChecked();
+ }
+ if (bChecked) {
+ csExport = pControl->GetExportValue();
+ break;
+ }
+ }
+ return csExport;
+}
+FX_BOOL CPDF_FormField::SetCheckValue(const CFX_WideString& value,
+ FX_BOOL bDefault,
+ FX_BOOL bNotify) {
+ ASSERT(GetType() == CheckBox || GetType() == RadioButton);
+ int iCount = CountControls();
+ for (int i = 0; i < iCount; i++) {
+ CPDF_FormControl* pControl = GetControl(i);
+ CFX_WideString csExport = pControl->GetExportValue();
+ if (csExport == value) {
+ if (bDefault) {
+ } else {
+ CheckControl(GetControlIndex(pControl), TRUE);
+ }
+ break;
+ } else {
+ if (bDefault) {
+ } else {
+ CheckControl(GetControlIndex(pControl), FALSE);
+ }
+ }
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ m_pForm->m_pFormNotify->AfterCheckedStatusChange(this);
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+int CPDF_FormField::GetTopVisibleIndex() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "TI");
+ if (!pObj) {
+ return 0;
+ }
+ return pObj->GetInteger();
+}
+int CPDF_FormField::CountSelectedOptions() {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "I");
+ if (!pObj) {
+ return 0;
+ }
+ CPDF_Array* pArray = pObj->GetArray();
+ if (!pArray) {
+ return 0;
+ }
+ return (int)pArray->GetCount();
+}
+int CPDF_FormField::GetSelectedOptionIndex(int index) {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "I");
+ if (!pObj) {
+ return -1;
+ }
+ CPDF_Array* pArray = pObj->GetArray();
+ if (!pArray) {
+ return -1;
+ }
+ int iCount = (int)pArray->GetCount();
+ if (iCount > 0 && index < iCount) {
+ return pArray->GetIntegerAt(index);
+ }
+ return -1;
+}
+FX_BOOL CPDF_FormField::IsOptionSelected(int iOptIndex) {
+ CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "I");
+ if (!pObj) {
+ return FALSE;
+ }
+ CPDF_Array* pArray = pObj->GetArray();
+ if (!pArray) {
+ return FALSE;
+ }
+ int iCount = (int)pArray->GetCount();
+ for (int i = 0; i < iCount; i++) {
+ if (pArray->GetIntegerAt(i) == iOptIndex) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_FormField::SelectOption(int iOptIndex,
+ FX_BOOL bSelected,
+ FX_BOOL bNotify) {
+ CPDF_Array* pArray = m_pDict->GetArrayBy("I");
+ if (!pArray) {
+ if (!bSelected) {
+ return TRUE;
+ }
+ pArray = new CPDF_Array;
+ m_pDict->SetAt("I", pArray);
+ }
+ FX_BOOL bReturn = FALSE;
+ for (int i = 0; i < (int)pArray->GetCount(); i++) {
+ int iFind = pArray->GetIntegerAt(i);
+ if (iFind == iOptIndex) {
+ if (bSelected) {
+ return TRUE;
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ CFX_WideString csValue = GetOptionLabel(iOptIndex);
+ if (GetType() == ListBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ }
+ if (GetType() == ComboBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ }
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ pArray->RemoveAt(i);
+ bReturn = TRUE;
+ break;
+ } else if (iFind > iOptIndex) {
+ if (!bSelected) {
+ continue;
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ CFX_WideString csValue = GetOptionLabel(iOptIndex);
+ if (GetType() == ListBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ }
+ if (GetType() == ComboBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ }
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ CPDF_Number* pNum = new CPDF_Number(iOptIndex);
+ pArray->InsertAt(i, pNum);
+ bReturn = TRUE;
+ break;
+ }
+ }
+ if (!bReturn) {
+ if (bSelected) {
+ pArray->AddInteger(iOptIndex);
+ }
+ if (pArray->GetCount() == 0) {
+ m_pDict->RemoveAt("I");
+ }
+ }
+ if (bNotify && m_pForm->m_pFormNotify) {
+ if (GetType() == ListBox) {
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ }
+ if (GetType() == ComboBox) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+FX_BOOL CPDF_FormField::ClearSelectedOptions(FX_BOOL bNotify) {
+ if (bNotify && m_pForm->m_pFormNotify) {
+ int iRet = 0;
+ CFX_WideString csValue;
+ int iIndex = GetSelectedIndex(0);
+ if (iIndex >= 0) {
+ csValue = GetOptionLabel(iIndex);
+ }
+ if (GetType() == ListBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeSelectionChange(this, csValue);
+ }
+ if (GetType() == ComboBox) {
+ iRet = m_pForm->m_pFormNotify->BeforeValueChange(this, csValue);
+ }
+ if (iRet < 0) {
+ return FALSE;
+ }
+ }
+ m_pDict->RemoveAt("I");
+ if (bNotify && m_pForm->m_pFormNotify) {
+ if (GetType() == ListBox) {
+ m_pForm->m_pFormNotify->AfterSelectionChange(this);
+ }
+ if (GetType() == ComboBox) {
+ m_pForm->m_pFormNotify->AfterValueChange(this);
+ }
+ }
+ m_pForm->m_bUpdated = TRUE;
+ return TRUE;
+}
+void CPDF_FormField::LoadDA() {
+ CFX_ByteString DA;
+ if (CPDF_Object* pObj_t = FPDF_GetFieldAttr(m_pDict, "DA")) {
+ DA = pObj_t->GetString();
+ }
+ if (DA.IsEmpty() && m_pForm->m_pFormDict) {
+ DA = m_pForm->m_pFormDict->GetStringBy("DA");
+ }
+ if (DA.IsEmpty()) {
+ return;
+ }
+ CPDF_SimpleParser syntax(DA);
+ syntax.FindTagParamFromStart("Tf", 2);
+ CFX_ByteString font_name = syntax.GetWord();
+ CPDF_Dictionary* pFontDict = NULL;
+ if (m_pForm->m_pFormDict && m_pForm->m_pFormDict->GetDictBy("DR") &&
+ m_pForm->m_pFormDict->GetDictBy("DR")->GetDictBy("Font"))
+ pFontDict = m_pForm->m_pFormDict->GetDictBy("DR")
+ ->GetDictBy("Font")
+ ->GetDictBy(font_name);
+
+ if (!pFontDict) {
+ return;
+ }
+ m_pFont = m_pForm->m_pDocument->LoadFont(pFontDict);
+ m_FontSize = FX_atof(syntax.GetWord());
+}
diff --git a/core/fpdfdoc/doc_link.cpp b/core/fpdfdoc/doc_link.cpp
new file mode 100644
index 0000000000..8aedb40034
--- /dev/null
+++ b/core/fpdfdoc/doc_link.cpp
@@ -0,0 +1,92 @@
+// 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 "core/include/fpdfdoc/fpdf_doc.h"
+
+#include <vector>
+
+#include "core/include/fpdfapi/cpdf_array.h"
+
+CPDF_LinkList::CPDF_LinkList() {}
+
+CPDF_LinkList::~CPDF_LinkList() {}
+
+const std::vector<CPDF_Dictionary*>* CPDF_LinkList::GetPageLinks(
+ CPDF_Page* pPage) {
+ FX_DWORD objnum = pPage->m_pFormDict->GetObjNum();
+ if (objnum == 0)
+ return nullptr;
+
+ auto it = m_PageMap.find(objnum);
+ if (it != m_PageMap.end())
+ return &it->second;
+
+ // std::map::operator[] forces the creation of a map entry.
+ std::vector<CPDF_Dictionary*>& page_link_list = m_PageMap[objnum];
+ LoadPageLinks(pPage, &page_link_list);
+ return &page_link_list;
+}
+
+CPDF_Link CPDF_LinkList::GetLinkAtPoint(CPDF_Page* pPage,
+ FX_FLOAT pdf_x,
+ FX_FLOAT pdf_y,
+ int* z_order) {
+ const std::vector<CPDF_Dictionary*>* pPageLinkList = GetPageLinks(pPage);
+ if (!pPageLinkList)
+ return CPDF_Link();
+
+ for (size_t i = pPageLinkList->size(); i > 0; --i) {
+ size_t annot_index = i - 1;
+ CPDF_Dictionary* pAnnot = (*pPageLinkList)[annot_index];
+ if (!pAnnot)
+ continue;
+
+ CPDF_Link link(pAnnot);
+ CFX_FloatRect rect = link.GetRect();
+ if (!rect.Contains(pdf_x, pdf_y))
+ continue;
+
+ if (z_order)
+ *z_order = annot_index;
+ return link;
+ }
+ return CPDF_Link();
+}
+
+void CPDF_LinkList::LoadPageLinks(CPDF_Page* pPage,
+ std::vector<CPDF_Dictionary*>* pList) {
+ CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayBy("Annots");
+ if (!pAnnotList)
+ return;
+
+ for (FX_DWORD i = 0; i < pAnnotList->GetCount(); ++i) {
+ CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(i);
+ bool add_link = (pAnnot && pAnnot->GetStringBy("Subtype") == "Link");
+ // Add non-links as nullptrs to preserve z-order.
+ pList->push_back(add_link ? pAnnot : nullptr);
+ }
+}
+
+CFX_FloatRect CPDF_Link::GetRect() {
+ return m_pDict->GetRectBy("Rect");
+}
+CPDF_Dest CPDF_Link::GetDest(CPDF_Document* pDoc) {
+ CPDF_Object* pDest = m_pDict->GetElementValue("Dest");
+ if (!pDest)
+ return CPDF_Dest();
+
+ if (pDest->IsString() || pDest->IsName()) {
+ CPDF_NameTree name_tree(pDoc, "Dests");
+ CFX_ByteStringC name = pDest->GetString();
+ return CPDF_Dest(name_tree.LookupNamedDest(pDoc, name));
+ }
+ if (CPDF_Array* pArray = pDest->AsArray())
+ return CPDF_Dest(pArray);
+ return CPDF_Dest();
+}
+CPDF_Action CPDF_Link::GetAction() {
+ return CPDF_Action(m_pDict->GetDictBy("A"));
+}
diff --git a/core/fpdfdoc/doc_metadata.cpp b/core/fpdfdoc/doc_metadata.cpp
new file mode 100644
index 0000000000..78364c4cbf
--- /dev/null
+++ b/core/fpdfdoc/doc_metadata.cpp
@@ -0,0 +1,29 @@
+// 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 "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+#include "core/include/fxcrt/fx_xml.h"
+
+CPDF_Metadata::CPDF_Metadata(CPDF_Document* pDoc) {
+ CPDF_Dictionary* pRoot = pDoc->GetRoot();
+ if (!pRoot)
+ return;
+
+ CPDF_Stream* pStream = pRoot->GetStreamBy("Metadata");
+ if (!pStream)
+ return;
+
+ CPDF_StreamAcc acc;
+ acc.LoadAllData(pStream, FALSE);
+ m_pXmlElement.reset(CXML_Element::Parse(acc.GetData(), acc.GetSize()));
+}
+
+CPDF_Metadata::~CPDF_Metadata() {}
+
+const CXML_Element* CPDF_Metadata::GetRoot() const {
+ return m_pXmlElement.get();
+}
diff --git a/core/fpdfdoc/doc_ocg.cpp b/core/fpdfdoc/doc_ocg.cpp
new file mode 100644
index 0000000000..3af745580c
--- /dev/null
+++ b/core/fpdfdoc/doc_ocg.cpp
@@ -0,0 +1,284 @@
+// 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 "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+static int32_t FPDFDOC_OCG_FindGroup(const CPDF_Object* pObject,
+ const CPDF_Dictionary* pGroupDict) {
+ if (!pObject || !pGroupDict)
+ return -1;
+
+ if (const CPDF_Array* pArray = pObject->AsArray()) {
+ FX_DWORD dwCount = pArray->GetCount();
+ for (FX_DWORD i = 0; i < dwCount; i++) {
+ if (pArray->GetDictAt(i) == pGroupDict)
+ return i;
+ }
+ return -1;
+ }
+ return pObject->GetDict() == pGroupDict ? 0 : -1;
+}
+static FX_BOOL FPDFDOC_OCG_HasIntent(const CPDF_Dictionary* pDict,
+ const CFX_ByteStringC& csElement,
+ const CFX_ByteStringC& csDef = "") {
+ CPDF_Object* pIntent = pDict->GetElementValue("Intent");
+ if (!pIntent) {
+ return csElement == csDef;
+ }
+ CFX_ByteString bsIntent;
+ if (CPDF_Array* pArray = pIntent->AsArray()) {
+ FX_DWORD dwCount = pArray->GetCount();
+ for (FX_DWORD i = 0; i < dwCount; i++) {
+ bsIntent = pArray->GetStringAt(i);
+ if (bsIntent == "All" || bsIntent == csElement)
+ return TRUE;
+ }
+ return FALSE;
+ }
+ bsIntent = pIntent->GetString();
+ return bsIntent == "All" || bsIntent == csElement;
+}
+static CPDF_Dictionary* FPDFDOC_OCG_GetConfig(CPDF_Document* pDoc,
+ const CPDF_Dictionary* pOCGDict,
+ const CFX_ByteStringC& bsState) {
+ FXSYS_assert(pDoc && pOCGDict);
+ CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictBy("OCProperties");
+ if (!pOCProperties) {
+ return NULL;
+ }
+ CPDF_Array* pOCGs = pOCProperties->GetArrayBy("OCGs");
+ if (!pOCGs) {
+ return NULL;
+ }
+ if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
+ return NULL;
+ }
+ CPDF_Dictionary* pConfig = pOCProperties->GetDictBy("D");
+ CPDF_Array* pConfigs = pOCProperties->GetArrayBy("Configs");
+ if (pConfigs) {
+ CPDF_Dictionary* pFind;
+ int32_t iCount = pConfigs->GetCount();
+ for (int32_t i = 0; i < iCount; i++) {
+ pFind = pConfigs->GetDictAt(i);
+ if (!pFind) {
+ continue;
+ }
+ if (!FPDFDOC_OCG_HasIntent(pFind, "View", "View")) {
+ continue;
+ }
+ pConfig = pFind;
+ break;
+ }
+ }
+ return pConfig;
+}
+static CFX_ByteString FPDFDOC_OCG_GetUsageTypeString(
+ CPDF_OCContext::UsageType eType) {
+ CFX_ByteString csState = "View";
+ if (eType == CPDF_OCContext::Design) {
+ csState = "Design";
+ } else if (eType == CPDF_OCContext::Print) {
+ csState = "Print";
+ } else if (eType == CPDF_OCContext::Export) {
+ csState = "Export";
+ }
+ return csState;
+}
+CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType) {
+ FXSYS_assert(pDoc);
+ m_pDocument = pDoc;
+ m_eUsageType = eUsageType;
+}
+CPDF_OCContext::~CPDF_OCContext() {
+ m_OCGStates.clear();
+}
+FX_BOOL CPDF_OCContext::LoadOCGStateFromConfig(const CFX_ByteStringC& csConfig,
+ const CPDF_Dictionary* pOCGDict,
+ FX_BOOL& bValidConfig) const {
+ CPDF_Dictionary* pConfig =
+ FPDFDOC_OCG_GetConfig(m_pDocument, pOCGDict, csConfig);
+ if (!pConfig) {
+ return TRUE;
+ }
+ bValidConfig = TRUE;
+ FX_BOOL bState = pConfig->GetStringBy("BaseState", "ON") != "OFF";
+ CPDF_Array* pArray = pConfig->GetArrayBy("ON");
+ if (pArray) {
+ if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
+ bState = TRUE;
+ }
+ }
+ pArray = pConfig->GetArrayBy("OFF");
+ if (pArray) {
+ if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
+ bState = FALSE;
+ }
+ }
+ pArray = pConfig->GetArrayBy("AS");
+ if (pArray) {
+ CFX_ByteString csFind = csConfig + "State";
+ int32_t iCount = pArray->GetCount();
+ for (int32_t i = 0; i < iCount; i++) {
+ CPDF_Dictionary* pUsage = pArray->GetDictAt(i);
+ if (!pUsage) {
+ continue;
+ }
+ if (pUsage->GetStringBy("Event", "View") != csConfig) {
+ continue;
+ }
+ CPDF_Array* pOCGs = pUsage->GetArrayBy("OCGs");
+ if (!pOCGs) {
+ continue;
+ }
+ if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
+ continue;
+ }
+ CPDF_Dictionary* pState = pUsage->GetDictBy(csConfig);
+ if (!pState) {
+ continue;
+ }
+ bState = pState->GetStringBy(csFind) != "OFF";
+ }
+ }
+ return bState;
+}
+FX_BOOL CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
+ if (!FPDFDOC_OCG_HasIntent(pOCGDict, "View", "View")) {
+ return TRUE;
+ }
+ CFX_ByteString csState = FPDFDOC_OCG_GetUsageTypeString(m_eUsageType);
+ CPDF_Dictionary* pUsage = pOCGDict->GetDictBy("Usage");
+ if (pUsage) {
+ CPDF_Dictionary* pState = pUsage->GetDictBy(csState);
+ if (pState) {
+ CFX_ByteString csFind = csState + "State";
+ if (pState->KeyExist(csFind)) {
+ return pState->GetStringBy(csFind) != "OFF";
+ }
+ }
+ if (csState != "View") {
+ pState = pUsage->GetDictBy("View");
+ if (pState && pState->KeyExist("ViewState")) {
+ return pState->GetStringBy("ViewState") != "OFF";
+ }
+ }
+ }
+ FX_BOOL bDefValid = FALSE;
+ return LoadOCGStateFromConfig(csState, pOCGDict, bDefValid);
+}
+
+FX_BOOL CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) {
+ if (!pOCGDict)
+ return FALSE;
+
+ const auto it = m_OCGStates.find(pOCGDict);
+ if (it != m_OCGStates.end())
+ return it->second;
+
+ FX_BOOL bState = LoadOCGState(pOCGDict);
+ m_OCGStates[pOCGDict] = bState;
+ return bState;
+}
+
+FX_BOOL CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression,
+ FX_BOOL bFromConfig,
+ int nLevel) {
+ if (nLevel > 32) {
+ return FALSE;
+ }
+ if (!pExpression) {
+ return FALSE;
+ }
+ int32_t iCount = pExpression->GetCount();
+ CPDF_Object* pOCGObj;
+ CFX_ByteString csOperator = pExpression->GetStringAt(0);
+ if (csOperator == "Not") {
+ pOCGObj = pExpression->GetElementValue(1);
+ if (!pOCGObj)
+ return FALSE;
+ if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
+ return !(bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict));
+ if (CPDF_Array* pArray = pOCGObj->AsArray())
+ return !GetOCGVE(pArray, bFromConfig, nLevel + 1);
+ return FALSE;
+ }
+ if (csOperator == "Or" || csOperator == "And") {
+ FX_BOOL bValue = FALSE;
+ for (int32_t i = 1; i < iCount; i++) {
+ pOCGObj = pExpression->GetElementValue(1);
+ if (!pOCGObj) {
+ continue;
+ }
+ FX_BOOL bItem = FALSE;
+ if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
+ bItem = bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
+ else if (CPDF_Array* pArray = pOCGObj->AsArray())
+ bItem = GetOCGVE(pArray, bFromConfig, nLevel + 1);
+
+ if (i == 1) {
+ bValue = bItem;
+ } else {
+ if (csOperator == "Or") {
+ bValue = bValue || bItem;
+ } else {
+ bValue = bValue && bItem;
+ }
+ }
+ }
+ return bValue;
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict,
+ FX_BOOL bFromConfig) {
+ CPDF_Array* pVE = pOCMDDict->GetArrayBy("VE");
+ if (pVE) {
+ return GetOCGVE(pVE, bFromConfig);
+ }
+ CFX_ByteString csP = pOCMDDict->GetStringBy("P", "AnyOn");
+ CPDF_Object* pOCGObj = pOCMDDict->GetElementValue("OCGs");
+ if (!pOCGObj)
+ return TRUE;
+ if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
+ return bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
+
+ CPDF_Array* pArray = pOCGObj->AsArray();
+ if (!pArray)
+ return TRUE;
+
+ FX_BOOL bState = FALSE;
+ if (csP == "AllOn" || csP == "AllOff") {
+ bState = TRUE;
+ }
+ int32_t iCount = pArray->GetCount();
+ for (int32_t i = 0; i < iCount; i++) {
+ FX_BOOL bItem = TRUE;
+ CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
+ if (pItemDict)
+ bItem = bFromConfig ? LoadOCGState(pItemDict) : GetOCGVisible(pItemDict);
+
+ if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
+ return TRUE;
+ if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
+ return FALSE;
+ }
+ return bState;
+}
+FX_BOOL CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) {
+ if (!pOCGDict) {
+ return TRUE;
+ }
+ CFX_ByteString csType = pOCGDict->GetStringBy("Type", "OCG");
+ if (csType == "OCG") {
+ return GetOCGVisible(pOCGDict);
+ }
+ return LoadOCMDState(pOCGDict, FALSE);
+}
+void CPDF_OCContext::ResetOCContext() {
+ m_OCGStates.clear();
+}
diff --git a/core/fpdfdoc/doc_tagged.cpp b/core/fpdfdoc/doc_tagged.cpp
new file mode 100644
index 0000000000..015f4db878
--- /dev/null
+++ b/core/fpdfdoc/doc_tagged.cpp
@@ -0,0 +1,440 @@
+// 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 <map>
+
+#include "core/fpdfdoc/doc_utils.h"
+#include "core/fpdfdoc/tagged_int.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_dictionary.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_reference.h"
+#include "core/include/fpdfapi/fpdf_page.h"
+#include "core/include/fpdfdoc/fpdf_tagged.h"
+
+const int nMaxRecursion = 32;
+static FX_BOOL IsTagged(const CPDF_Document* pDoc) {
+ CPDF_Dictionary* pCatalog = pDoc->GetRoot();
+ CPDF_Dictionary* pMarkInfo = pCatalog->GetDictBy("MarkInfo");
+ return pMarkInfo && pMarkInfo->GetIntegerBy("Marked");
+}
+CPDF_StructTree* CPDF_StructTree::LoadPage(const CPDF_Document* pDoc,
+ const CPDF_Dictionary* pPageDict) {
+ if (!IsTagged(pDoc)) {
+ return NULL;
+ }
+ CPDF_StructTreeImpl* pTree = new CPDF_StructTreeImpl(pDoc);
+ pTree->LoadPageTree(pPageDict);
+ return pTree;
+}
+CPDF_StructTree* CPDF_StructTree::LoadDoc(const CPDF_Document* pDoc) {
+ if (!IsTagged(pDoc)) {
+ return NULL;
+ }
+ CPDF_StructTreeImpl* pTree = new CPDF_StructTreeImpl(pDoc);
+ pTree->LoadDocTree();
+ return pTree;
+}
+CPDF_StructTreeImpl::CPDF_StructTreeImpl(const CPDF_Document* pDoc) {
+ CPDF_Dictionary* pCatalog = pDoc->GetRoot();
+ m_pTreeRoot = pCatalog->GetDictBy("StructTreeRoot");
+ if (!m_pTreeRoot) {
+ return;
+ }
+ m_pRoleMap = m_pTreeRoot->GetDictBy("RoleMap");
+}
+CPDF_StructTreeImpl::~CPDF_StructTreeImpl() {
+ for (int i = 0; i < m_Kids.GetSize(); i++)
+ if (m_Kids[i]) {
+ m_Kids[i]->Release();
+ }
+}
+void CPDF_StructTreeImpl::LoadDocTree() {
+ m_pPage = nullptr;
+ if (!m_pTreeRoot)
+ return;
+
+ CPDF_Object* pKids = m_pTreeRoot->GetElementValue("K");
+ if (!pKids)
+ return;
+ if (CPDF_Dictionary* pDict = pKids->AsDictionary()) {
+ CPDF_StructElementImpl* pStructElementImpl =
+ new CPDF_StructElementImpl(this, nullptr, pDict);
+ m_Kids.Add(pStructElementImpl);
+ return;
+ }
+ CPDF_Array* pArray = pKids->AsArray();
+ if (!pArray)
+ return;
+
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pArray->GetDictAt(i);
+ CPDF_StructElementImpl* pStructElementImpl =
+ new CPDF_StructElementImpl(this, nullptr, pKid);
+ m_Kids.Add(pStructElementImpl);
+ }
+}
+void CPDF_StructTreeImpl::LoadPageTree(const CPDF_Dictionary* pPageDict) {
+ m_pPage = pPageDict;
+ if (!m_pTreeRoot)
+ return;
+
+ CPDF_Object* pKids = m_pTreeRoot->GetElementValue("K");
+ if (!pKids)
+ return;
+
+ FX_DWORD dwKids = 0;
+ if (pKids->IsDictionary())
+ dwKids = 1;
+ else if (CPDF_Array* pArray = pKids->AsArray())
+ dwKids = pArray->GetCount();
+ else
+ return;
+
+ FX_DWORD i;
+ m_Kids.SetSize(dwKids);
+ for (i = 0; i < dwKids; i++) {
+ m_Kids[i] = NULL;
+ }
+ std::map<CPDF_Dictionary*, CPDF_StructElementImpl*> element_map;
+ CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDictBy("ParentTree");
+ if (!pParentTree) {
+ return;
+ }
+ CPDF_NumberTree parent_tree(pParentTree);
+ int parents_id = pPageDict->GetIntegerBy("StructParents", -1);
+ if (parents_id >= 0) {
+ CPDF_Array* pParentArray = ToArray(parent_tree.LookupValue(parents_id));
+ if (!pParentArray)
+ return;
+
+ for (i = 0; i < pParentArray->GetCount(); i++) {
+ CPDF_Dictionary* pParent = pParentArray->GetDictAt(i);
+ if (!pParent) {
+ continue;
+ }
+ AddPageNode(pParent, element_map);
+ }
+ }
+}
+CPDF_StructElementImpl* CPDF_StructTreeImpl::AddPageNode(
+ CPDF_Dictionary* pDict,
+ std::map<CPDF_Dictionary*, CPDF_StructElementImpl*>& map,
+ int nLevel) {
+ if (nLevel > nMaxRecursion)
+ return NULL;
+
+ auto it = map.find(pDict);
+ if (it != map.end())
+ return it->second;
+
+ CPDF_StructElementImpl* pElement =
+ new CPDF_StructElementImpl(this, NULL, pDict);
+ map[pDict] = pElement;
+ CPDF_Dictionary* pParent = pDict->GetDictBy("P");
+ if (!pParent || pParent->GetStringBy("Type") == "StructTreeRoot") {
+ if (!AddTopLevelNode(pDict, pElement)) {
+ pElement->Release();
+ map.erase(pDict);
+ }
+ } else {
+ CPDF_StructElementImpl* pParentElement =
+ AddPageNode(pParent, map, nLevel + 1);
+ FX_BOOL bSave = FALSE;
+ for (int i = 0; i < pParentElement->m_Kids.GetSize(); i++) {
+ if (pParentElement->m_Kids[i].m_Type != CPDF_StructKid::Element) {
+ continue;
+ }
+ if (pParentElement->m_Kids[i].m_Element.m_pDict != pDict) {
+ continue;
+ }
+ pParentElement->m_Kids[i].m_Element.m_pElement = pElement->Retain();
+ bSave = TRUE;
+ }
+ if (!bSave) {
+ pElement->Release();
+ map.erase(pDict);
+ }
+ }
+ return pElement;
+}
+FX_BOOL CPDF_StructTreeImpl::AddTopLevelNode(CPDF_Dictionary* pDict,
+ CPDF_StructElementImpl* pElement) {
+ CPDF_Object* pObj = m_pTreeRoot->GetElementValue("K");
+ if (!pObj) {
+ return FALSE;
+ }
+ if (pObj->IsDictionary()) {
+ if (pObj->GetObjNum() == pDict->GetObjNum()) {
+ if (m_Kids[0]) {
+ m_Kids[0]->Release();
+ }
+ m_Kids[0] = pElement->Retain();
+ } else {
+ return FALSE;
+ }
+ }
+ if (CPDF_Array* pTopKids = pObj->AsArray()) {
+ FX_DWORD i;
+ FX_BOOL bSave = FALSE;
+ for (i = 0; i < pTopKids->GetCount(); i++) {
+ CPDF_Reference* pKidRef = ToReference(pTopKids->GetElement(i));
+ if (!pKidRef)
+ continue;
+ if (pKidRef->GetRefObjNum() != pDict->GetObjNum())
+ continue;
+
+ if (m_Kids[i])
+ m_Kids[i]->Release();
+ m_Kids[i] = pElement->Retain();
+ bSave = TRUE;
+ }
+ if (!bSave)
+ return FALSE;
+ }
+ return TRUE;
+}
+CPDF_StructElementImpl::CPDF_StructElementImpl(CPDF_StructTreeImpl* pTree,
+ CPDF_StructElementImpl* pParent,
+ CPDF_Dictionary* pDict)
+ : m_RefCount(0) {
+ m_pTree = pTree;
+ m_pDict = pDict;
+ m_Type = pDict->GetStringBy("S");
+ if (pTree->m_pRoleMap) {
+ CFX_ByteString mapped = pTree->m_pRoleMap->GetStringBy(m_Type);
+ if (!mapped.IsEmpty()) {
+ m_Type = mapped;
+ }
+ }
+ m_pParent = pParent;
+ LoadKids(pDict);
+}
+CPDF_StructElementImpl::~CPDF_StructElementImpl() {
+ for (int i = 0; i < m_Kids.GetSize(); i++) {
+ if (m_Kids[i].m_Type == CPDF_StructKid::Element &&
+ m_Kids[i].m_Element.m_pElement) {
+ ((CPDF_StructElementImpl*)m_Kids[i].m_Element.m_pElement)->Release();
+ }
+ }
+}
+CPDF_StructElementImpl* CPDF_StructElementImpl::Retain() {
+ m_RefCount++;
+ return this;
+}
+void CPDF_StructElementImpl::Release() {
+ if (--m_RefCount < 1) {
+ delete this;
+ }
+}
+void CPDF_StructElementImpl::LoadKids(CPDF_Dictionary* pDict) {
+ CPDF_Object* pObj = pDict->GetElement("Pg");
+ FX_DWORD PageObjNum = 0;
+ if (CPDF_Reference* pRef = ToReference(pObj))
+ PageObjNum = pRef->GetRefObjNum();
+
+ CPDF_Object* pKids = pDict->GetElementValue("K");
+ if (!pKids)
+ return;
+
+ if (CPDF_Array* pArray = pKids->AsArray()) {
+ m_Kids.SetSize(pArray->GetCount());
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++) {
+ CPDF_Object* pKid = pArray->GetElementValue(i);
+ LoadKid(PageObjNum, pKid, &m_Kids[i]);
+ }
+ } else {
+ m_Kids.SetSize(1);
+ LoadKid(PageObjNum, pKids, &m_Kids[0]);
+ }
+}
+void CPDF_StructElementImpl::LoadKid(FX_DWORD PageObjNum,
+ CPDF_Object* pKidObj,
+ CPDF_StructKid* pKid) {
+ pKid->m_Type = CPDF_StructKid::Invalid;
+ if (!pKidObj)
+ return;
+
+ if (pKidObj->IsNumber()) {
+ if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {
+ return;
+ }
+ pKid->m_Type = CPDF_StructKid::PageContent;
+ pKid->m_PageContent.m_ContentId = pKidObj->GetInteger();
+ pKid->m_PageContent.m_PageObjNum = PageObjNum;
+ return;
+ }
+
+ CPDF_Dictionary* pKidDict = pKidObj->AsDictionary();
+ if (!pKidDict)
+ return;
+
+ if (CPDF_Reference* pRef = ToReference(pKidDict->GetElement("Pg")))
+ PageObjNum = pRef->GetRefObjNum();
+
+ CFX_ByteString type = pKidDict->GetStringBy("Type");
+ if (type == "MCR") {
+ if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {
+ return;
+ }
+ pKid->m_Type = CPDF_StructKid::StreamContent;
+ if (CPDF_Reference* pRef = ToReference(pKidDict->GetElement("Stm"))) {
+ pKid->m_StreamContent.m_RefObjNum = pRef->GetRefObjNum();
+ } else {
+ pKid->m_StreamContent.m_RefObjNum = 0;
+ }
+ pKid->m_StreamContent.m_PageObjNum = PageObjNum;
+ pKid->m_StreamContent.m_ContentId = pKidDict->GetIntegerBy("MCID");
+ } else if (type == "OBJR") {
+ if (m_pTree->m_pPage && m_pTree->m_pPage->GetObjNum() != PageObjNum) {
+ return;
+ }
+ pKid->m_Type = CPDF_StructKid::Object;
+ if (CPDF_Reference* pObj = ToReference(pKidDict->GetElement("Obj"))) {
+ pKid->m_Object.m_RefObjNum = pObj->GetRefObjNum();
+ } else {
+ pKid->m_Object.m_RefObjNum = 0;
+ }
+ pKid->m_Object.m_PageObjNum = PageObjNum;
+ } else {
+ pKid->m_Type = CPDF_StructKid::Element;
+ pKid->m_Element.m_pDict = pKidDict;
+ if (!m_pTree->m_pPage) {
+ pKid->m_Element.m_pElement =
+ new CPDF_StructElementImpl(m_pTree, this, pKidDict);
+ } else {
+ pKid->m_Element.m_pElement = NULL;
+ }
+ }
+}
+static CPDF_Dictionary* FindAttrDict(CPDF_Object* pAttrs,
+ const CFX_ByteStringC& owner,
+ FX_FLOAT nLevel = 0.0F) {
+ if (nLevel > nMaxRecursion)
+ return nullptr;
+ if (!pAttrs)
+ return nullptr;
+
+ CPDF_Dictionary* pDict = nullptr;
+ if (pAttrs->IsDictionary()) {
+ pDict = pAttrs->AsDictionary();
+ } else if (CPDF_Stream* pStream = pAttrs->AsStream()) {
+ pDict = pStream->GetDict();
+ } else if (CPDF_Array* pArray = pAttrs->AsArray()) {
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++) {
+ CPDF_Object* pElement = pArray->GetElementValue(i);
+ pDict = FindAttrDict(pElement, owner, nLevel + 1);
+ if (pDict)
+ return pDict;
+ }
+ }
+ if (pDict && pDict->GetStringBy("O") == owner)
+ return pDict;
+ return nullptr;
+}
+CPDF_Object* CPDF_StructElementImpl::GetAttr(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_BOOL bInheritable,
+ FX_FLOAT fLevel) {
+ if (fLevel > nMaxRecursion) {
+ return NULL;
+ }
+ if (bInheritable) {
+ CPDF_Object* pAttr = GetAttr(owner, name, FALSE);
+ if (pAttr) {
+ return pAttr;
+ }
+ if (!m_pParent) {
+ return NULL;
+ }
+ return m_pParent->GetAttr(owner, name, TRUE, fLevel + 1);
+ }
+ CPDF_Object* pA = m_pDict->GetElementValue("A");
+ if (pA) {
+ CPDF_Dictionary* pAttrDict = FindAttrDict(pA, owner);
+ if (pAttrDict) {
+ CPDF_Object* pAttr = pAttrDict->GetElementValue(name);
+ if (pAttr) {
+ return pAttr;
+ }
+ }
+ }
+ CPDF_Object* pC = m_pDict->GetElementValue("C");
+ if (!pC)
+ return nullptr;
+
+ CPDF_Dictionary* pClassMap = m_pTree->m_pTreeRoot->GetDictBy("ClassMap");
+ if (!pClassMap)
+ return nullptr;
+
+ if (CPDF_Array* pArray = pC->AsArray()) {
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++) {
+ CFX_ByteString class_name = pArray->GetStringAt(i);
+ CPDF_Dictionary* pClassDict = pClassMap->GetDictBy(class_name);
+ if (pClassDict && pClassDict->GetStringBy("O") == owner)
+ return pClassDict->GetElementValue(name);
+ }
+ return nullptr;
+ }
+ CFX_ByteString class_name = pC->GetString();
+ CPDF_Dictionary* pClassDict = pClassMap->GetDictBy(class_name);
+ if (pClassDict && pClassDict->GetStringBy("O") == owner)
+ return pClassDict->GetElementValue(name);
+ return nullptr;
+}
+CPDF_Object* CPDF_StructElementImpl::GetAttr(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_BOOL bInheritable,
+ int subindex) {
+ CPDF_Object* pAttr = GetAttr(owner, name, bInheritable);
+ CPDF_Array* pArray = ToArray(pAttr);
+ if (!pArray || subindex == -1)
+ return pAttr;
+
+ if (subindex >= static_cast<int>(pArray->GetCount()))
+ return pAttr;
+ return pArray->GetElementValue(subindex);
+}
+CFX_ByteString CPDF_StructElementImpl::GetName(
+ const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ const CFX_ByteStringC& default_value,
+ FX_BOOL bInheritable,
+ int subindex) {
+ CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+ if (ToName(pAttr))
+ return pAttr->GetString();
+ return default_value;
+}
+
+FX_ARGB CPDF_StructElementImpl::GetColor(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_ARGB default_value,
+ FX_BOOL bInheritable,
+ int subindex) {
+ CPDF_Array* pArray = ToArray(GetAttr(owner, name, bInheritable, subindex));
+ if (!pArray)
+ return default_value;
+ return 0xff000000 | ((int)(pArray->GetNumberAt(0) * 255) << 16) |
+ ((int)(pArray->GetNumberAt(1) * 255) << 8) |
+ (int)(pArray->GetNumberAt(2) * 255);
+}
+FX_FLOAT CPDF_StructElementImpl::GetNumber(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_FLOAT default_value,
+ FX_BOOL bInheritable,
+ int subindex) {
+ CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+ return ToNumber(pAttr) ? pAttr->GetNumber() : default_value;
+}
+int CPDF_StructElementImpl::GetInteger(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ int default_value,
+ FX_BOOL bInheritable,
+ int subindex) {
+ CPDF_Object* pAttr = GetAttr(owner, name, bInheritable, subindex);
+ return ToNumber(pAttr) ? pAttr->GetInteger() : default_value;
+}
diff --git a/core/fpdfdoc/doc_utils.cpp b/core/fpdfdoc/doc_utils.cpp
new file mode 100644
index 0000000000..5160c1115c
--- /dev/null
+++ b/core/fpdfdoc/doc_utils.cpp
@@ -0,0 +1,753 @@
+// 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 <algorithm>
+#include <vector>
+
+#include "core/fpdfdoc/doc_utils.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/cpdf_simple_parser.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+namespace {
+
+const int FPDFDOC_UTILS_MAXRECURSION = 32;
+
+CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) {
+ CPDF_Array* pLimits = pNode->GetArrayBy("Limits");
+ if (pLimits &&
+ (num < pLimits->GetIntegerAt(0) || num > pLimits->GetIntegerAt(1))) {
+ return NULL;
+ }
+ CPDF_Array* pNumbers = pNode->GetArrayBy("Nums");
+ if (pNumbers) {
+ FX_DWORD dwCount = pNumbers->GetCount() / 2;
+ for (FX_DWORD i = 0; i < dwCount; i++) {
+ int index = pNumbers->GetIntegerAt(i * 2);
+ if (num == index) {
+ return pNumbers->GetElementValue(i * 2 + 1);
+ }
+ if (index > num) {
+ break;
+ }
+ }
+ return NULL;
+ }
+ CPDF_Array* pKids = pNode->GetArrayBy("Kids");
+ if (!pKids) {
+ return NULL;
+ }
+ for (FX_DWORD i = 0; i < pKids->GetCount(); i++) {
+ CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ if (!pKid) {
+ continue;
+ }
+ CPDF_Object* pFound = SearchNumberNode(pKid, num);
+ if (pFound) {
+ return pFound;
+ }
+ }
+ return NULL;
+}
+
+} // namespace
+
+CPDF_Object* CPDF_NumberTree::LookupValue(int num) const {
+ return SearchNumberNode(m_pRoot, num);
+}
+
+CFX_WideString GetFullName(CPDF_Dictionary* pFieldDict) {
+ CFX_WideString full_name;
+ CPDF_Dictionary* pLevel = pFieldDict;
+ while (pLevel) {
+ CFX_WideString short_name = pLevel->GetUnicodeTextBy("T");
+ if (short_name != L"") {
+ if (full_name == L"") {
+ full_name = short_name;
+ } else {
+ full_name = short_name + L"." + full_name;
+ }
+ }
+ pLevel = pLevel->GetDictBy("Parent");
+ }
+ return full_name;
+}
+FX_BOOL CPDF_DefaultAppearance::HasFont() {
+ if (m_csDA.IsEmpty()) {
+ return FALSE;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ return syntax.FindTagParamFromStart("Tf", 2);
+}
+CFX_ByteString CPDF_DefaultAppearance::GetFontString() {
+ CFX_ByteString csFont;
+ if (m_csDA.IsEmpty()) {
+ return csFont;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart("Tf", 2)) {
+ csFont += (CFX_ByteString)syntax.GetWord();
+ csFont += " ";
+ csFont += (CFX_ByteString)syntax.GetWord();
+ csFont += " ";
+ csFont += (CFX_ByteString)syntax.GetWord();
+ }
+ return csFont;
+}
+void CPDF_DefaultAppearance::GetFont(CFX_ByteString& csFontNameTag,
+ FX_FLOAT& fFontSize) {
+ csFontNameTag = "";
+ fFontSize = 0;
+ if (m_csDA.IsEmpty()) {
+ return;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart("Tf", 2)) {
+ csFontNameTag = (CFX_ByteString)syntax.GetWord();
+ csFontNameTag.Delete(0, 1);
+ fFontSize = FX_atof((CFX_ByteString)syntax.GetWord());
+ }
+ csFontNameTag = PDF_NameDecode(csFontNameTag);
+}
+FX_BOOL CPDF_DefaultAppearance::HasColor(FX_BOOL bStrokingOperation) {
+ if (m_csDA.IsEmpty()) {
+ return FALSE;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
+ return TRUE;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
+ return TRUE;
+ }
+ return syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4);
+}
+CFX_ByteString CPDF_DefaultAppearance::GetColorString(
+ FX_BOOL bStrokingOperation) {
+ CFX_ByteString csColor;
+ if (m_csDA.IsEmpty()) {
+ return csColor;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ return csColor;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ return csColor;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4)) {
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ csColor += " ";
+ csColor += (CFX_ByteString)syntax.GetWord();
+ }
+ return csColor;
+}
+void CPDF_DefaultAppearance::GetColor(int& iColorType,
+ FX_FLOAT fc[4],
+ FX_BOOL bStrokingOperation) {
+ iColorType = COLORTYPE_TRANSPARENT;
+ for (int c = 0; c < 4; c++) {
+ fc[c] = 0;
+ }
+ if (m_csDA.IsEmpty()) {
+ return;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
+ iColorType = COLORTYPE_GRAY;
+ fc[0] = FX_atof((CFX_ByteString)syntax.GetWord());
+ return;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
+ iColorType = COLORTYPE_RGB;
+ fc[0] = FX_atof((CFX_ByteString)syntax.GetWord());
+ fc[1] = FX_atof((CFX_ByteString)syntax.GetWord());
+ fc[2] = FX_atof((CFX_ByteString)syntax.GetWord());
+ return;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4)) {
+ iColorType = COLORTYPE_CMYK;
+ fc[0] = FX_atof((CFX_ByteString)syntax.GetWord());
+ fc[1] = FX_atof((CFX_ByteString)syntax.GetWord());
+ fc[2] = FX_atof((CFX_ByteString)syntax.GetWord());
+ fc[3] = FX_atof((CFX_ByteString)syntax.GetWord());
+ }
+}
+void CPDF_DefaultAppearance::GetColor(FX_ARGB& color,
+ int& iColorType,
+ FX_BOOL bStrokingOperation) {
+ color = 0;
+ iColorType = COLORTYPE_TRANSPARENT;
+ if (m_csDA.IsEmpty()) {
+ return;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
+ iColorType = COLORTYPE_GRAY;
+ FX_FLOAT g = FX_atof((CFX_ByteString)syntax.GetWord()) * 255 + 0.5f;
+ color = ArgbEncode(255, (int)g, (int)g, (int)g);
+ return;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
+ iColorType = COLORTYPE_RGB;
+ FX_FLOAT r = FX_atof((CFX_ByteString)syntax.GetWord()) * 255 + 0.5f;
+ FX_FLOAT g = FX_atof((CFX_ByteString)syntax.GetWord()) * 255 + 0.5f;
+ FX_FLOAT b = FX_atof((CFX_ByteString)syntax.GetWord()) * 255 + 0.5f;
+ color = ArgbEncode(255, (int)r, (int)g, (int)b);
+ return;
+ }
+ if (syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4)) {
+ iColorType = COLORTYPE_CMYK;
+ FX_FLOAT c = FX_atof((CFX_ByteString)syntax.GetWord());
+ FX_FLOAT m = FX_atof((CFX_ByteString)syntax.GetWord());
+ FX_FLOAT y = FX_atof((CFX_ByteString)syntax.GetWord());
+ FX_FLOAT k = FX_atof((CFX_ByteString)syntax.GetWord());
+ FX_FLOAT r = 1.0f - std::min(1.0f, c + k);
+ FX_FLOAT g = 1.0f - std::min(1.0f, m + k);
+ FX_FLOAT b = 1.0f - std::min(1.0f, y + k);
+ color = ArgbEncode(255, (int)(r * 255 + 0.5f), (int)(g * 255 + 0.5f),
+ (int)(b * 255 + 0.5f));
+ }
+}
+FX_BOOL CPDF_DefaultAppearance::HasTextMatrix() {
+ if (m_csDA.IsEmpty()) {
+ return FALSE;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ return syntax.FindTagParamFromStart("Tm", 6);
+}
+CFX_ByteString CPDF_DefaultAppearance::GetTextMatrixString() {
+ CFX_ByteString csTM;
+ if (m_csDA.IsEmpty()) {
+ return csTM;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart("Tm", 6)) {
+ for (int i = 0; i < 6; i++) {
+ csTM += (CFX_ByteString)syntax.GetWord();
+ csTM += " ";
+ }
+ csTM += (CFX_ByteString)syntax.GetWord();
+ }
+ return csTM;
+}
+CFX_Matrix CPDF_DefaultAppearance::GetTextMatrix() {
+ CFX_Matrix tm;
+ if (m_csDA.IsEmpty()) {
+ return tm;
+ }
+ CPDF_SimpleParser syntax(m_csDA);
+ if (syntax.FindTagParamFromStart("Tm", 6)) {
+ FX_FLOAT f[6];
+ for (int i = 0; i < 6; i++) {
+ f[i] = FX_atof((CFX_ByteString)syntax.GetWord());
+ }
+ tm.Set(f[0], f[1], f[2], f[3], f[4], f[5]);
+ }
+ return tm;
+}
+void InitInterFormDict(CPDF_Dictionary*& pFormDict, CPDF_Document* pDocument) {
+ if (!pDocument) {
+ return;
+ }
+ if (!pFormDict) {
+ pFormDict = new CPDF_Dictionary;
+ FX_DWORD dwObjNum = pDocument->AddIndirectObject(pFormDict);
+ CPDF_Dictionary* pRoot = pDocument->GetRoot();
+ pRoot->SetAtReference("AcroForm", pDocument, dwObjNum);
+ }
+ CFX_ByteString csDA;
+ if (!pFormDict->KeyExist("DR")) {
+ CPDF_Font* pFont = NULL;
+ CFX_ByteString csBaseName, csDefault;
+ uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
+ pFont = CPDF_InterForm::AddStandardFont(pDocument, "Helvetica");
+ if (pFont) {
+ AddInterFormFont(pFormDict, pDocument, pFont, csBaseName);
+ csDefault = csBaseName;
+ }
+ if (charSet != 0) {
+ CFX_ByteString csFontName = CPDF_InterForm::GetNativeFont(charSet, NULL);
+ if (!pFont || csFontName != "Helvetica") {
+ pFont = CPDF_InterForm::AddNativeFont(pDocument);
+ if (pFont) {
+ csBaseName = "";
+ AddInterFormFont(pFormDict, pDocument, pFont, csBaseName);
+ csDefault = csBaseName;
+ }
+ }
+ }
+ if (pFont) {
+ csDA = "/" + PDF_NameEncode(csDefault) + " 0 Tf";
+ }
+ }
+ if (!csDA.IsEmpty()) {
+ csDA += " ";
+ }
+ csDA += "0 g";
+ if (!pFormDict->KeyExist("DA")) {
+ pFormDict->SetAtString("DA", csDA);
+ }
+}
+FX_DWORD CountInterFormFonts(CPDF_Dictionary* pFormDict) {
+ if (!pFormDict) {
+ return 0;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return 0;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return 0;
+ }
+ FX_DWORD dwCount = 0;
+ for (const auto& it : *pFonts) {
+ CPDF_Object* pObj = it.second;
+ if (!pObj) {
+ continue;
+ }
+ if (CPDF_Dictionary* pDirect = ToDictionary(pObj->GetDirect())) {
+ if (pDirect->GetStringBy("Type") == "Font") {
+ dwCount++;
+ }
+ }
+ }
+ return dwCount;
+}
+CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ FX_DWORD index,
+ CFX_ByteString& csNameTag) {
+ if (!pFormDict) {
+ return NULL;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return NULL;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return NULL;
+ }
+ FX_DWORD dwCount = 0;
+ for (const auto& it : *pFonts) {
+ const CFX_ByteString& csKey = it.first;
+ CPDF_Object* pObj = it.second;
+ if (!pObj) {
+ continue;
+ }
+ CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
+ if (!pElement)
+ continue;
+ if (pElement->GetStringBy("Type") != "Font")
+ continue;
+ if (dwCount == index) {
+ csNameTag = csKey;
+ return pDocument->LoadFont(pElement);
+ }
+ dwCount++;
+ }
+ return NULL;
+}
+CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString csNameTag) {
+ CFX_ByteString csAlias = PDF_NameDecode(csNameTag);
+ if (!pFormDict || csAlias.IsEmpty()) {
+ return NULL;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return NULL;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return NULL;
+ }
+ CPDF_Dictionary* pElement = pFonts->GetDictBy(csAlias);
+ if (!pElement) {
+ return NULL;
+ }
+ if (pElement->GetStringBy("Type") == "Font") {
+ return pDocument->LoadFont(pElement);
+ }
+ return NULL;
+}
+CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString csFontName,
+ CFX_ByteString& csNameTag) {
+ if (!pFormDict || csFontName.IsEmpty()) {
+ return NULL;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return NULL;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return NULL;
+ }
+ for (const auto& it : *pFonts) {
+ const CFX_ByteString& csKey = it.first;
+ CPDF_Object* pObj = it.second;
+ if (!pObj) {
+ continue;
+ }
+ CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
+ if (!pElement)
+ continue;
+ if (pElement->GetStringBy("Type") != "Font")
+ continue;
+
+ CPDF_Font* pFind = pDocument->LoadFont(pElement);
+ if (!pFind)
+ continue;
+
+ CFX_ByteString csBaseFont;
+ csBaseFont = pFind->GetBaseFont();
+ csBaseFont.Remove(' ');
+ if (csBaseFont == csFontName) {
+ csNameTag = csKey;
+ return pFind;
+ }
+ }
+ return NULL;
+}
+CPDF_Font* GetNativeInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ uint8_t charSet,
+ CFX_ByteString& csNameTag) {
+ if (!pFormDict) {
+ return NULL;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return NULL;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return NULL;
+ }
+ for (const auto& it : *pFonts) {
+ const CFX_ByteString& csKey = it.first;
+ CPDF_Object* pObj = it.second;
+ if (!pObj) {
+ continue;
+ }
+ CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
+ if (!pElement)
+ continue;
+ if (pElement->GetStringBy("Type") != "Font")
+ continue;
+ CPDF_Font* pFind = pDocument->LoadFont(pElement);
+ if (!pFind) {
+ continue;
+ }
+ CFX_SubstFont* pSubst = (CFX_SubstFont*)pFind->GetSubstFont();
+ if (!pSubst) {
+ continue;
+ }
+ if (pSubst->m_Charset == (int)charSet) {
+ csNameTag = csKey;
+ return pFind;
+ }
+ }
+ return NULL;
+}
+CPDF_Font* GetNativeInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString& csNameTag) {
+ csNameTag = "";
+ uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
+ CFX_SubstFont* pSubst;
+ CPDF_Font* pFont = GetDefaultInterFormFont(pFormDict, pDocument);
+ if (pFont) {
+ pSubst = (CFX_SubstFont*)pFont->GetSubstFont();
+ if (pSubst && pSubst->m_Charset == (int)charSet) {
+ FindInterFormFont(pFormDict, pFont, csNameTag);
+ return pFont;
+ }
+ }
+ return GetNativeInterFormFont(pFormDict, pDocument, charSet, csNameTag);
+}
+FX_BOOL FindInterFormFont(CPDF_Dictionary* pFormDict,
+ const CPDF_Font* pFont,
+ CFX_ByteString& csNameTag) {
+ if (!pFormDict || !pFont) {
+ return FALSE;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return FALSE;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return FALSE;
+ }
+ for (const auto& it : *pFonts) {
+ const CFX_ByteString& csKey = it.first;
+ CPDF_Object* pObj = it.second;
+ if (!pObj) {
+ continue;
+ }
+ CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
+ if (!pElement)
+ continue;
+ if (pElement->GetStringBy("Type") != "Font") {
+ continue;
+ }
+ if (pFont->GetFontDict() == pElement) {
+ csNameTag = csKey;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL FindInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString csFontName,
+ CPDF_Font*& pFont,
+ CFX_ByteString& csNameTag) {
+ if (!pFormDict) {
+ return FALSE;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return FALSE;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return FALSE;
+ }
+ if (csFontName.GetLength() > 0) {
+ csFontName.Remove(' ');
+ }
+ for (const auto& it : *pFonts) {
+ const CFX_ByteString& csKey = it.first;
+ CPDF_Object* pObj = it.second;
+ if (!pObj) {
+ continue;
+ }
+ CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
+ if (!pElement)
+ continue;
+ if (pElement->GetStringBy("Type") != "Font") {
+ continue;
+ }
+ pFont = pDocument->LoadFont(pElement);
+ if (!pFont) {
+ continue;
+ }
+ CFX_ByteString csBaseFont;
+ csBaseFont = pFont->GetBaseFont();
+ csBaseFont.Remove(' ');
+ if (csBaseFont == csFontName) {
+ csNameTag = csKey;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+void AddInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ const CPDF_Font* pFont,
+ CFX_ByteString& csNameTag) {
+ if (!pFont) {
+ return;
+ }
+ if (!pFormDict) {
+ InitInterFormDict(pFormDict, pDocument);
+ }
+ CFX_ByteString csTag;
+ if (FindInterFormFont(pFormDict, pFont, csTag)) {
+ csNameTag = csTag;
+ return;
+ }
+ if (!pFormDict) {
+ InitInterFormDict(pFormDict, pDocument);
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ pDR = new CPDF_Dictionary;
+ pFormDict->SetAt("DR", pDR);
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ pFonts = new CPDF_Dictionary;
+ pDR->SetAt("Font", pFonts);
+ }
+ if (csNameTag.IsEmpty()) {
+ csNameTag = pFont->GetBaseFont();
+ }
+ csNameTag.Remove(' ');
+ csNameTag =
+ CPDF_InterForm::GenerateNewResourceName(pDR, "Font", 4, csNameTag);
+ pFonts->SetAtReference(csNameTag, pDocument, pFont->GetFontDict());
+}
+CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ uint8_t charSet,
+ CFX_ByteString& csNameTag) {
+ if (!pFormDict) {
+ InitInterFormDict(pFormDict, pDocument);
+ }
+ CFX_ByteString csTemp;
+ CPDF_Font* pFont =
+ GetNativeInterFormFont(pFormDict, pDocument, charSet, csTemp);
+ if (pFont) {
+ csNameTag = csTemp;
+ return pFont;
+ }
+ CFX_ByteString csFontName = CPDF_InterForm::GetNativeFont(charSet);
+ if (!csFontName.IsEmpty()) {
+ if (FindInterFormFont(pFormDict, pDocument, csFontName, pFont, csNameTag)) {
+ return pFont;
+ }
+ }
+ pFont = CPDF_InterForm::AddNativeFont(charSet, pDocument);
+ if (pFont) {
+ AddInterFormFont(pFormDict, pDocument, pFont, csNameTag);
+ }
+ return pFont;
+}
+CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString& csNameTag) {
+ uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
+ return AddNativeInterFormFont(pFormDict, pDocument, charSet, csNameTag);
+}
+void RemoveInterFormFont(CPDF_Dictionary* pFormDict, const CPDF_Font* pFont) {
+ if (!pFormDict || !pFont) {
+ return;
+ }
+ CFX_ByteString csTag;
+ if (!FindInterFormFont(pFormDict, pFont, csTag)) {
+ return;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ pFonts->RemoveAt(csTag);
+}
+void RemoveInterFormFont(CPDF_Dictionary* pFormDict, CFX_ByteString csNameTag) {
+ if (!pFormDict || csNameTag.IsEmpty()) {
+ return;
+ }
+ CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
+ if (!pDR) {
+ return;
+ }
+ CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
+ if (!pFonts) {
+ return;
+ }
+ pFonts->RemoveAt(csNameTag);
+}
+
+CPDF_Font* GetDefaultInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument) {
+ if (!pFormDict) {
+ return NULL;
+ }
+ CPDF_DefaultAppearance cDA(pFormDict->GetStringBy("DA"));
+ CFX_ByteString csFontNameTag;
+ FX_FLOAT fFontSize;
+ cDA.GetFont(csFontNameTag, fFontSize);
+ return GetInterFormFont(pFormDict, pDocument, csFontNameTag);
+}
+
+CPDF_IconFit::ScaleMethod CPDF_IconFit::GetScaleMethod() {
+ if (!m_pDict) {
+ return Always;
+ }
+ CFX_ByteString csSW = m_pDict->GetStringBy("SW", "A");
+ if (csSW == "B") {
+ return Bigger;
+ }
+ if (csSW == "S") {
+ return Smaller;
+ }
+ if (csSW == "N") {
+ return Never;
+ }
+ return Always;
+}
+FX_BOOL CPDF_IconFit::IsProportionalScale() {
+ if (!m_pDict) {
+ return TRUE;
+ }
+ return m_pDict->GetStringBy("S", "P") != "A";
+}
+void CPDF_IconFit::GetIconPosition(FX_FLOAT& fLeft, FX_FLOAT& fBottom) {
+ fLeft = fBottom = 0.5;
+ if (!m_pDict) {
+ return;
+ }
+ CPDF_Array* pA = m_pDict->GetArrayBy("A");
+ if (pA) {
+ FX_DWORD dwCount = pA->GetCount();
+ if (dwCount > 0) {
+ fLeft = pA->GetNumberAt(0);
+ }
+ if (dwCount > 1) {
+ fBottom = pA->GetNumberAt(1);
+ }
+ }
+}
+FX_BOOL CPDF_IconFit::GetFittingBounds() {
+ if (!m_pDict) {
+ return FALSE;
+ }
+ return m_pDict->GetBooleanBy("FB");
+}
+
+std::vector<bool> SaveCheckedFieldStatus(CPDF_FormField* pField) {
+ std::vector<bool> result;
+ int iCount = pField->CountControls();
+ for (int i = 0; i < iCount; ++i) {
+ if (CPDF_FormControl* pControl = pField->GetControl(i))
+ result.push_back(pControl->IsChecked());
+ }
+ return result;
+}
+
+CPDF_Object* FPDF_GetFieldAttr(CPDF_Dictionary* pFieldDict,
+ const FX_CHAR* name,
+ int nLevel) {
+ if (nLevel > FPDFDOC_UTILS_MAXRECURSION) {
+ return NULL;
+ }
+ if (!pFieldDict) {
+ return NULL;
+ }
+ CPDF_Object* pAttr = pFieldDict->GetElementValue(name);
+ if (pAttr) {
+ return pAttr;
+ }
+ CPDF_Dictionary* pParent = pFieldDict->GetDictBy("Parent");
+ if (!pParent) {
+ return NULL;
+ }
+ return FPDF_GetFieldAttr(pParent, name, nLevel + 1);
+}
diff --git a/core/fpdfdoc/doc_utils.h b/core/fpdfdoc/doc_utils.h
new file mode 100644
index 0000000000..ed687943da
--- /dev/null
+++ b/core/fpdfdoc/doc_utils.h
@@ -0,0 +1,80 @@
+// Copyright 2015 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
+
+#ifndef CORE_FPDFDOC_DOC_UTILS_H_
+#define CORE_FPDFDOC_DOC_UTILS_H_
+
+#include <vector>
+
+#include "core/include/fpdfapi/cpdf_parser.h"
+#include "core/include/fpdfapi/fpdf_resource.h"
+
+class CPDF_Dictionary;
+class CPDF_FormField;
+
+class CPDF_NumberTree {
+ public:
+ CPDF_NumberTree(CPDF_Dictionary* pRoot) : m_pRoot(pRoot) {}
+ CPDF_Object* LookupValue(int num) const;
+
+ protected:
+ CPDF_Dictionary* const m_pRoot;
+};
+
+CFX_WideString GetFullName(CPDF_Dictionary* pFieldDict);
+void InitInterFormDict(CPDF_Dictionary*& pFormDict, CPDF_Document* pDocument);
+FX_DWORD CountInterFormFonts(CPDF_Dictionary* pFormDict);
+CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ FX_DWORD index,
+ CFX_ByteString& csNameTag);
+CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString csNameTag);
+CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString csFontName,
+ CFX_ByteString& csNameTag);
+CPDF_Font* GetNativeInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ uint8_t charSet,
+ CFX_ByteString& csNameTag);
+CPDF_Font* GetNativeInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString& csNameTag);
+FX_BOOL FindInterFormFont(CPDF_Dictionary* pFormDict,
+ const CPDF_Font* pFont,
+ CFX_ByteString& csNameTag);
+FX_BOOL FindInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString csFontName,
+ CPDF_Font*& pFont,
+ CFX_ByteString& csNameTag);
+void AddInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ const CPDF_Font* pFont,
+ CFX_ByteString& csNameTag);
+CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ uint8_t charSet,
+ CFX_ByteString& csNameTag);
+CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ CFX_ByteString& csNameTag);
+void RemoveInterFormFont(CPDF_Dictionary* pFormDict, const CPDF_Font* pFont);
+void RemoveInterFormFont(CPDF_Dictionary* pFormDict, CFX_ByteString csNameTag);
+CPDF_Font* GetDefaultInterFormFont(CPDF_Dictionary* pFormDict,
+ CPDF_Document* pDocument);
+void SetDefaultInterFormFont(CPDF_Dictionary*& pFormDict,
+ CPDF_Document* pDocument,
+ const CPDF_Font* pFont);
+std::vector<bool> SaveCheckedFieldStatus(CPDF_FormField* pField);
+FX_BOOL NeedPDFEncodeForFieldFullName(const CFX_WideString& csFieldName);
+FX_BOOL NeedPDFEncodeForFieldTree(CPDF_Dictionary* pFieldDict, int nLevel = 0);
+void EncodeFieldName(const CFX_WideString& csName, CFX_ByteString& csT);
+void UpdateEncodeFieldName(CPDF_Dictionary* pFieldDict, int nLevel = 0);
+
+#endif // CORE_FPDFDOC_DOC_UTILS_H_
diff --git a/core/fpdfdoc/doc_viewerPreferences.cpp b/core/fpdfdoc/doc_viewerPreferences.cpp
new file mode 100644
index 0000000000..582f3a5a93
--- /dev/null
+++ b/core/fpdfdoc/doc_viewerPreferences.cpp
@@ -0,0 +1,54 @@
+// 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 "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+
+CPDF_ViewerPreferences::CPDF_ViewerPreferences(CPDF_Document* pDoc)
+ : m_pDoc(pDoc) {}
+CPDF_ViewerPreferences::~CPDF_ViewerPreferences() {}
+FX_BOOL CPDF_ViewerPreferences::IsDirectionR2L() const {
+ CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+ pDict = pDict->GetDictBy("ViewerPreferences");
+ if (!pDict) {
+ return FALSE;
+ }
+ return "R2L" == pDict->GetStringBy("Direction");
+}
+FX_BOOL CPDF_ViewerPreferences::PrintScaling() const {
+ CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+ pDict = pDict->GetDictBy("ViewerPreferences");
+ if (!pDict) {
+ return TRUE;
+ }
+ return "None" != pDict->GetStringBy("PrintScaling");
+}
+int32_t CPDF_ViewerPreferences::NumCopies() const {
+ CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+ pDict = pDict->GetDictBy("ViewerPreferences");
+ if (!pDict) {
+ return 1;
+ }
+ return pDict->GetIntegerBy("NumCopies");
+}
+CPDF_Array* CPDF_ViewerPreferences::PrintPageRange() const {
+ CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+ CPDF_Array* pRange = NULL;
+ pDict = pDict->GetDictBy("ViewerPreferences");
+ if (!pDict) {
+ return pRange;
+ }
+ pRange = pDict->GetArrayBy("PrintPageRange");
+ return pRange;
+}
+CFX_ByteString CPDF_ViewerPreferences::Duplex() const {
+ CPDF_Dictionary* pDict = m_pDoc->GetRoot();
+ pDict = pDict->GetDictBy("ViewerPreferences");
+ if (!pDict) {
+ return "None";
+ }
+ return pDict->GetStringBy("Duplex");
+}
diff --git a/core/fpdfdoc/doc_vt.cpp b/core/fpdfdoc/doc_vt.cpp
new file mode 100644
index 0000000000..dddc57f2dd
--- /dev/null
+++ b/core/fpdfdoc/doc_vt.cpp
@@ -0,0 +1,1825 @@
+// 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 <algorithm>
+
+#include "core/fpdfdoc/pdf_vt.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+#include "core/include/fpdfdoc/fpdf_vt.h"
+
+const uint8_t gFontSizeSteps[] = {4, 6, 8, 9, 10, 12, 14, 18, 20,
+ 25, 30, 35, 40, 45, 50, 55, 60, 70,
+ 80, 90, 100, 110, 120, 130, 144};
+#define PVT_RETURN_LENGTH 1
+#define PVT_DEFAULT_FONTSIZE 18.0f
+#define PVTWORD_SCRIPT_NORMAL 0
+#define PVTWORD_SCRIPT_SUPER 1
+#define PVTWORD_SCRIPT_SUB 2
+#define PVT_FONTSCALE 0.001f
+#define PVT_PERCENT 0.01f
+#define PVT_HALF 0.5f
+CLine::CLine() {}
+CLine::~CLine() {}
+CPVT_WordPlace CLine::GetBeginWordPlace() const {
+ return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex, -1);
+}
+CPVT_WordPlace CLine::GetEndWordPlace() const {
+ return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex,
+ m_LineInfo.nEndWordIndex);
+}
+CPVT_WordPlace CLine::GetPrevWordPlace(const CPVT_WordPlace& place) const {
+ if (place.nWordIndex > m_LineInfo.nEndWordIndex) {
+ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+ m_LineInfo.nEndWordIndex);
+ }
+ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+ place.nWordIndex - 1);
+}
+CPVT_WordPlace CLine::GetNextWordPlace(const CPVT_WordPlace& place) const {
+ if (place.nWordIndex < m_LineInfo.nBeginWordIndex) {
+ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+ m_LineInfo.nBeginWordIndex);
+ }
+ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex,
+ place.nWordIndex + 1);
+}
+CSection::CSection(CPDF_VariableText* pVT) : m_pVT(pVT) {}
+CSection::~CSection() {
+ ResetAll();
+}
+void CSection::ResetAll() {
+ ResetWordArray();
+ ResetLineArray();
+}
+void CSection::ResetLineArray() {
+ m_LineArray.RemoveAll();
+}
+void CSection::ResetWordArray() {
+ for (int32_t i = 0, sz = m_WordArray.GetSize(); i < sz; i++) {
+ delete m_WordArray.GetAt(i);
+ }
+ m_WordArray.RemoveAll();
+}
+void CSection::ResetLinePlace() {
+ for (int32_t i = 0, sz = m_LineArray.GetSize(); i < sz; i++) {
+ if (CLine* pLine = m_LineArray.GetAt(i)) {
+ pLine->LinePlace = CPVT_WordPlace(SecPlace.nSecIndex, i, -1);
+ }
+ }
+}
+CPVT_WordPlace CSection::AddWord(const CPVT_WordPlace& place,
+ const CPVT_WordInfo& wordinfo) {
+ CPVT_WordInfo* pWord = new CPVT_WordInfo(wordinfo);
+ int32_t nWordIndex =
+ std::max(std::min(place.nWordIndex, m_WordArray.GetSize()), 0);
+ if (nWordIndex == m_WordArray.GetSize()) {
+ m_WordArray.Add(pWord);
+ } else {
+ m_WordArray.InsertAt(nWordIndex, pWord);
+ }
+ return place;
+}
+CPVT_WordPlace CSection::AddLine(const CPVT_LineInfo& lineinfo) {
+ return CPVT_WordPlace(SecPlace.nSecIndex, m_LineArray.Add(lineinfo), -1);
+}
+CPVT_FloatRect CSection::Rearrange() {
+ if (m_pVT->m_nCharArray > 0) {
+ return CTypeset(this).CharArray();
+ }
+ return CTypeset(this).Typeset();
+}
+CPVT_Size CSection::GetSectionSize(FX_FLOAT fFontSize) {
+ return CTypeset(this).GetEditSize(fFontSize);
+}
+CPVT_WordPlace CSection::GetBeginWordPlace() const {
+ if (CLine* pLine = m_LineArray.GetAt(0)) {
+ return pLine->GetBeginWordPlace();
+ }
+ return SecPlace;
+}
+CPVT_WordPlace CSection::GetEndWordPlace() const {
+ if (CLine* pLine = m_LineArray.GetAt(m_LineArray.GetSize() - 1)) {
+ return pLine->GetEndWordPlace();
+ }
+ return SecPlace;
+}
+CPVT_WordPlace CSection::GetPrevWordPlace(const CPVT_WordPlace& place) const {
+ if (place.nLineIndex < 0) {
+ return GetBeginWordPlace();
+ }
+ if (place.nLineIndex >= m_LineArray.GetSize()) {
+ return GetEndWordPlace();
+ }
+ if (CLine* pLine = m_LineArray.GetAt(place.nLineIndex)) {
+ if (place.nWordIndex == pLine->m_LineInfo.nBeginWordIndex) {
+ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
+ }
+ if (place.nWordIndex < pLine->m_LineInfo.nBeginWordIndex) {
+ if (CLine* pPrevLine = m_LineArray.GetAt(place.nLineIndex - 1)) {
+ return pPrevLine->GetEndWordPlace();
+ }
+ } else {
+ return pLine->GetPrevWordPlace(place);
+ }
+ }
+ return place;
+}
+CPVT_WordPlace CSection::GetNextWordPlace(const CPVT_WordPlace& place) const {
+ if (place.nLineIndex < 0) {
+ return GetBeginWordPlace();
+ }
+ if (place.nLineIndex >= m_LineArray.GetSize()) {
+ return GetEndWordPlace();
+ }
+ if (CLine* pLine = m_LineArray.GetAt(place.nLineIndex)) {
+ if (place.nWordIndex >= pLine->m_LineInfo.nEndWordIndex) {
+ if (CLine* pNextLine = m_LineArray.GetAt(place.nLineIndex + 1)) {
+ return pNextLine->GetBeginWordPlace();
+ }
+ } else {
+ return pLine->GetNextWordPlace(place);
+ }
+ }
+ return place;
+}
+void CSection::UpdateWordPlace(CPVT_WordPlace& place) const {
+ int32_t nLeft = 0;
+ int32_t nRight = m_LineArray.GetSize() - 1;
+ int32_t nMid = (nLeft + nRight) / 2;
+ while (nLeft <= nRight) {
+ if (CLine* pLine = m_LineArray.GetAt(nMid)) {
+ if (place.nWordIndex < pLine->m_LineInfo.nBeginWordIndex) {
+ nRight = nMid - 1;
+ nMid = (nLeft + nRight) / 2;
+ } else if (place.nWordIndex > pLine->m_LineInfo.nEndWordIndex) {
+ nLeft = nMid + 1;
+ nMid = (nLeft + nRight) / 2;
+ } else {
+ place.nLineIndex = nMid;
+ return;
+ }
+ } else {
+ break;
+ }
+ }
+}
+CPVT_WordPlace CSection::SearchWordPlace(const CFX_FloatPoint& point) const {
+ ASSERT(m_pVT);
+ CPVT_WordPlace place = GetBeginWordPlace();
+ FX_BOOL bUp = TRUE;
+ FX_BOOL bDown = TRUE;
+ int32_t nLeft = 0;
+ int32_t nRight = m_LineArray.GetSize() - 1;
+ int32_t nMid = m_LineArray.GetSize() / 2;
+ FX_FLOAT fTop = 0;
+ FX_FLOAT fBottom = 0;
+ while (nLeft <= nRight) {
+ if (CLine* pLine = m_LineArray.GetAt(nMid)) {
+ fTop = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineAscent -
+ m_pVT->GetLineLeading(m_SecInfo);
+ fBottom = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineDescent;
+ if (IsFloatBigger(point.y, fTop)) {
+ bUp = FALSE;
+ }
+ if (IsFloatSmaller(point.y, fBottom)) {
+ bDown = FALSE;
+ }
+ if (IsFloatSmaller(point.y, fTop)) {
+ nRight = nMid - 1;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ } else if (IsFloatBigger(point.y, fBottom)) {
+ nLeft = nMid + 1;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ } else {
+ place = SearchWordPlace(
+ point.x,
+ CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()),
+ pLine->GetEndWordPlace()));
+ place.nLineIndex = nMid;
+ return place;
+ }
+ }
+ }
+ if (bUp) {
+ place = GetBeginWordPlace();
+ }
+ if (bDown) {
+ place = GetEndWordPlace();
+ }
+ return place;
+}
+CPVT_WordPlace CSection::SearchWordPlace(
+ FX_FLOAT fx,
+ const CPVT_WordPlace& lineplace) const {
+ if (CLine* pLine = m_LineArray.GetAt(lineplace.nLineIndex)) {
+ return SearchWordPlace(
+ fx - m_SecInfo.rcSection.left,
+ CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()),
+ pLine->GetEndWordPlace()));
+ }
+ return GetBeginWordPlace();
+}
+CPVT_WordPlace CSection::SearchWordPlace(FX_FLOAT fx,
+ const CPVT_WordRange& range) const {
+ CPVT_WordPlace wordplace = range.BeginPos;
+ wordplace.nWordIndex = -1;
+ if (!m_pVT) {
+ return wordplace;
+ }
+ int32_t nLeft = range.BeginPos.nWordIndex;
+ int32_t nRight = range.EndPos.nWordIndex + 1;
+ int32_t nMid = (nLeft + nRight) / 2;
+ while (nLeft < nRight) {
+ if (nMid == nLeft) {
+ break;
+ }
+ if (nMid == nRight) {
+ nMid--;
+ break;
+ }
+ if (CPVT_WordInfo* pWord = m_WordArray.GetAt(nMid)) {
+ if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * PVT_HALF) {
+ nLeft = nMid;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ } else {
+ nRight = nMid;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ }
+ } else {
+ break;
+ }
+ }
+ if (CPVT_WordInfo* pWord = m_WordArray.GetAt(nMid)) {
+ if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * PVT_HALF) {
+ wordplace.nWordIndex = nMid;
+ }
+ }
+ return wordplace;
+}
+void CSection::ClearLeftWords(int32_t nWordIndex) {
+ for (int32_t i = nWordIndex; i >= 0; i--) {
+ delete m_WordArray.GetAt(i);
+ m_WordArray.RemoveAt(i);
+ }
+}
+void CSection::ClearRightWords(int32_t nWordIndex) {
+ for (int32_t i = m_WordArray.GetSize() - 1; i > nWordIndex; i--) {
+ delete m_WordArray.GetAt(i);
+ m_WordArray.RemoveAt(i);
+ }
+}
+void CSection::ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex) {
+ for (int32_t i = nEndIndex; i > nBeginIndex; i--) {
+ delete m_WordArray.GetAt(i);
+ m_WordArray.RemoveAt(i);
+ }
+}
+void CSection::ClearWords(const CPVT_WordRange& PlaceRange) {
+ CPVT_WordPlace SecBeginPos = GetBeginWordPlace();
+ CPVT_WordPlace SecEndPos = GetEndWordPlace();
+ if (PlaceRange.BeginPos.WordCmp(SecBeginPos) >= 0) {
+ if (PlaceRange.EndPos.WordCmp(SecEndPos) <= 0) {
+ ClearMidWords(PlaceRange.BeginPos.nWordIndex,
+ PlaceRange.EndPos.nWordIndex);
+ } else {
+ ClearRightWords(PlaceRange.BeginPos.nWordIndex);
+ }
+ } else if (PlaceRange.EndPos.WordCmp(SecEndPos) <= 0) {
+ ClearLeftWords(PlaceRange.EndPos.nWordIndex);
+ } else {
+ ResetWordArray();
+ }
+}
+void CSection::ClearWord(const CPVT_WordPlace& place) {
+ delete m_WordArray.GetAt(place.nWordIndex);
+ m_WordArray.RemoveAt(place.nWordIndex);
+}
+CTypeset::CTypeset(CSection* pSection)
+ : m_rcRet(0.0f, 0.0f, 0.0f, 0.0f),
+ m_pVT(pSection->m_pVT),
+ m_pSection(pSection) {}
+CTypeset::~CTypeset() {}
+CPVT_FloatRect CTypeset::CharArray() {
+ ASSERT(m_pSection);
+ FX_FLOAT fLineAscent =
+ m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), m_pVT->GetFontSize());
+ FX_FLOAT fLineDescent =
+ m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), m_pVT->GetFontSize());
+ m_rcRet.Default();
+ FX_FLOAT x = 0.0f, y = 0.0f;
+ FX_FLOAT fNextWidth;
+ int32_t nStart = 0;
+ FX_FLOAT fNodeWidth = m_pVT->GetPlateWidth() /
+ (m_pVT->m_nCharArray <= 0 ? 1 : m_pVT->m_nCharArray);
+ if (CLine* pLine = m_pSection->m_LineArray.GetAt(0)) {
+ x = 0.0f;
+ y += m_pVT->GetLineLeading(m_pSection->m_SecInfo);
+ y += fLineAscent;
+ nStart = 0;
+ switch (m_pVT->GetAlignment(m_pSection->m_SecInfo)) {
+ case 0:
+ pLine->m_LineInfo.fLineX = fNodeWidth * PVT_HALF;
+ break;
+ case 1:
+ nStart = (m_pVT->m_nCharArray - m_pSection->m_WordArray.GetSize()) / 2;
+ pLine->m_LineInfo.fLineX = fNodeWidth * nStart - fNodeWidth * PVT_HALF;
+ break;
+ case 2:
+ nStart = m_pVT->m_nCharArray - m_pSection->m_WordArray.GetSize();
+ pLine->m_LineInfo.fLineX = fNodeWidth * nStart - fNodeWidth * PVT_HALF;
+ break;
+ }
+ for (int32_t w = 0, sz = m_pSection->m_WordArray.GetSize(); w < sz; w++) {
+ if (w >= m_pVT->m_nCharArray) {
+ break;
+ }
+ fNextWidth = 0;
+ if (CPVT_WordInfo* pNextWord =
+ (CPVT_WordInfo*)m_pSection->m_WordArray.GetAt(w + 1)) {
+ pNextWord->fWordTail = 0;
+ fNextWidth = m_pVT->GetWordWidth(*pNextWord);
+ }
+ if (CPVT_WordInfo* pWord =
+ (CPVT_WordInfo*)m_pSection->m_WordArray.GetAt(w)) {
+ pWord->fWordTail = 0;
+ FX_FLOAT fWordWidth = m_pVT->GetWordWidth(*pWord);
+ FX_FLOAT fWordAscent = m_pVT->GetWordAscent(*pWord);
+ FX_FLOAT fWordDescent = m_pVT->GetWordDescent(*pWord);
+ x = (FX_FLOAT)(fNodeWidth * (w + nStart + 0.5) - fWordWidth * PVT_HALF);
+ pWord->fWordX = x;
+ pWord->fWordY = y;
+ if (w == 0) {
+ pLine->m_LineInfo.fLineX = x;
+ }
+ if (w != m_pSection->m_WordArray.GetSize() - 1) {
+ pWord->fWordTail =
+ (fNodeWidth - (fWordWidth + fNextWidth) * PVT_HALF > 0
+ ? fNodeWidth - (fWordWidth + fNextWidth) * PVT_HALF
+ : 0);
+ } else {
+ pWord->fWordTail = 0;
+ }
+ x += fWordWidth;
+ fLineAscent = std::max(fLineAscent, fWordAscent);
+ fLineDescent = std::min(fLineDescent, fWordDescent);
+ }
+ }
+ pLine->m_LineInfo.nBeginWordIndex = 0;
+ pLine->m_LineInfo.nEndWordIndex = m_pSection->m_WordArray.GetSize() - 1;
+ pLine->m_LineInfo.fLineY = y;
+ pLine->m_LineInfo.fLineWidth = x - pLine->m_LineInfo.fLineX;
+ pLine->m_LineInfo.fLineAscent = fLineAscent;
+ pLine->m_LineInfo.fLineDescent = fLineDescent;
+ y += (-fLineDescent);
+ }
+ return m_rcRet = CPVT_FloatRect(0, 0, x, y);
+}
+CPVT_Size CTypeset::GetEditSize(FX_FLOAT fFontSize) {
+ ASSERT(m_pSection);
+ ASSERT(m_pVT);
+ SplitLines(FALSE, fFontSize);
+ return CPVT_Size(m_rcRet.Width(), m_rcRet.Height());
+}
+CPVT_FloatRect CTypeset::Typeset() {
+ ASSERT(m_pVT);
+ m_pSection->m_LineArray.Empty();
+ SplitLines(TRUE, 0.0f);
+ m_pSection->m_LineArray.Clear();
+ OutputLines();
+ return m_rcRet;
+}
+static int special_chars[128] = {
+ 0x0000, 0x000C, 0x0008, 0x000C, 0x0008, 0x0000, 0x0020, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000,
+ 0x0010, 0x0000, 0x0000, 0x0028, 0x000C, 0x0008, 0x0000, 0x0000, 0x0028,
+ 0x0028, 0x0028, 0x0028, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0002, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000,
+ 0x0008, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x000C, 0x0000, 0x0008, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x000C, 0x0000, 0x0008,
+ 0x0000, 0x0000,
+};
+static FX_BOOL IsLatin(FX_WORD word) {
+ if (word <= 0x007F) {
+ if (special_chars[word] & 0x0001) {
+ return TRUE;
+ }
+ }
+ if ((word >= 0x00C0 && word <= 0x00FF) ||
+ (word >= 0x0100 && word <= 0x024F) ||
+ (word >= 0x1E00 && word <= 0x1EFF) ||
+ (word >= 0x2C60 && word <= 0x2C7F) ||
+ (word >= 0xA720 && word <= 0xA7FF) ||
+ (word >= 0xFF21 && word <= 0xFF3A) ||
+ (word >= 0xFF41 && word <= 0xFF5A)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+static FX_BOOL IsDigit(FX_DWORD word) {
+ return word >= 0x0030 && word <= 0x0039;
+}
+static FX_BOOL IsCJK(FX_DWORD word) {
+ if ((word >= 0x1100 && word <= 0x11FF) ||
+ (word >= 0x2E80 && word <= 0x2FFF) ||
+ (word >= 0x3040 && word <= 0x9FBF) ||
+ (word >= 0xAC00 && word <= 0xD7AF) ||
+ (word >= 0xF900 && word <= 0xFAFF) ||
+ (word >= 0xFE30 && word <= 0xFE4F) ||
+ (word >= 0x20000 && word <= 0x2A6DF) ||
+ (word >= 0x2F800 && word <= 0x2FA1F)) {
+ return TRUE;
+ }
+ if (word >= 0x3000 && word <= 0x303F) {
+ if (word == 0x3005 || word == 0x3006 || word == 0x3021 || word == 0x3022 ||
+ word == 0x3023 || word == 0x3024 || word == 0x3025 || word == 0x3026 ||
+ word == 0x3027 || word == 0x3028 || word == 0x3029 || word == 0x3031 ||
+ word == 0x3032 || word == 0x3033 || word == 0x3034 || word == 0x3035) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+ if (word >= 0xFF66 && word <= 0xFF9D) {
+ return TRUE;
+ }
+ return FALSE;
+}
+static FX_BOOL IsPunctuation(FX_DWORD word) {
+ if (word <= 0x007F) {
+ if ((special_chars[word] >> 3) & 1) {
+ return TRUE;
+ }
+ } else if (word >= 0x0080 && word <= 0x00FF) {
+ if (word == 0x0082 || word == 0x0084 || word == 0x0085 || word == 0x0091 ||
+ word == 0x0092 || word == 0x0093 || word <= 0x0094 || word == 0x0096 ||
+ word == 0x00B4 || word == 0x00B8) {
+ return TRUE;
+ }
+ } else if (word >= 0x2000 && word <= 0x206F) {
+ if (word == 0x2010 || word == 0x2011 || word == 0x2012 || word == 0x2013 ||
+ word == 0x2018 || word == 0x2019 || word == 0x201A || word == 0x201B ||
+ word == 0x201C || word == 0x201D || word == 0x201E || word == 0x201F ||
+ word == 0x2032 || word == 0x2033 || word == 0x2034 || word == 0x2035 ||
+ word == 0x2036 || word == 0x2037 || word == 0x203C || word == 0x203D ||
+ word == 0x203E || word == 0x2044) {
+ return TRUE;
+ }
+ } else if (word >= 0x3000 && word <= 0x303F) {
+ if (word == 0x3001 || word == 0x3002 || word == 0x3003 || word == 0x3005 ||
+ word == 0x3009 || word == 0x300A || word == 0x300B || word == 0x300C ||
+ word == 0x300D || word == 0x300F || word == 0x300E || word == 0x3010 ||
+ word == 0x3011 || word == 0x3014 || word == 0x3015 || word == 0x3016 ||
+ word == 0x3017 || word == 0x3018 || word == 0x3019 || word == 0x301A ||
+ word == 0x301B || word == 0x301D || word == 0x301E || word == 0x301F) {
+ return TRUE;
+ }
+ } else if (word >= 0xFE50 && word <= 0xFE6F) {
+ if ((word >= 0xFE50 && word <= 0xFE5E) || word == 0xFE63) {
+ return TRUE;
+ }
+ } else if (word >= 0xFF00 && word <= 0xFFEF) {
+ if (word == 0xFF01 || word == 0xFF02 || word == 0xFF07 || word == 0xFF08 ||
+ word == 0xFF09 || word == 0xFF0C || word == 0xFF0E || word == 0xFF0F ||
+ word == 0xFF1A || word == 0xFF1B || word == 0xFF1F || word == 0xFF3B ||
+ word == 0xFF3D || word == 0xFF40 || word == 0xFF5B || word == 0xFF5C ||
+ word == 0xFF5D || word == 0xFF61 || word == 0xFF62 || word == 0xFF63 ||
+ word == 0xFF64 || word == 0xFF65 || word == 0xFF9E || word == 0xFF9F) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static FX_BOOL IsConnectiveSymbol(FX_DWORD word) {
+ if (word <= 0x007F) {
+ if ((special_chars[word] >> 5) & 1) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static FX_BOOL IsOpenStylePunctuation(FX_DWORD word) {
+ if (word <= 0x007F) {
+ if ((special_chars[word] >> 2) & 1) {
+ return TRUE;
+ }
+ } else if (word == 0x300A || word == 0x300C || word == 0x300E ||
+ word == 0x3010 || word == 0x3014 || word == 0x3016 ||
+ word == 0x3018 || word == 0x301A || word == 0xFF08 ||
+ word == 0xFF3B || word == 0xFF5B || word == 0xFF62) {
+ return TRUE;
+ }
+ return FALSE;
+}
+static FX_BOOL IsCurrencySymbol(FX_WORD word) {
+ if (word == 0x0024 || word == 0x0080 || word == 0x00A2 || word == 0x00A3 ||
+ word == 0x00A4 || word == 0x00A5 || (word >= 0x20A0 && word <= 0x20CF) ||
+ word == 0xFE69 || word == 0xFF04 || word == 0xFFE0 || word == 0xFFE1 ||
+ word == 0xFFE5 || word == 0xFFE6) {
+ return TRUE;
+ }
+ return FALSE;
+}
+static FX_BOOL IsPrefixSymbol(FX_WORD word) {
+ if (IsCurrencySymbol(word)) {
+ return TRUE;
+ }
+ if (word == 0x2116) {
+ return TRUE;
+ }
+ return FALSE;
+}
+static FX_BOOL IsSpace(FX_WORD word) {
+ return word == 0x0020 || word == 0x3000;
+}
+static FX_BOOL NeedDivision(FX_WORD prevWord, FX_WORD curWord) {
+ if ((IsLatin(prevWord) || IsDigit(prevWord)) &&
+ (IsLatin(curWord) || IsDigit(curWord))) {
+ return FALSE;
+ }
+ if (IsSpace(curWord) || IsPunctuation(curWord)) {
+ return FALSE;
+ }
+ if (IsConnectiveSymbol(prevWord) || IsConnectiveSymbol(curWord)) {
+ return FALSE;
+ }
+ if (IsSpace(prevWord) || IsPunctuation(prevWord)) {
+ return TRUE;
+ }
+ if (IsPrefixSymbol(prevWord)) {
+ return FALSE;
+ }
+ if (IsPrefixSymbol(curWord) || IsCJK(curWord)) {
+ return TRUE;
+ }
+ if (IsCJK(prevWord)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+void CTypeset::SplitLines(FX_BOOL bTypeset, FX_FLOAT fFontSize) {
+ ASSERT(m_pVT);
+ ASSERT(m_pSection);
+ int32_t nLineHead = 0;
+ int32_t nLineTail = 0;
+ FX_FLOAT fMaxX = 0.0f, fMaxY = 0.0f;
+ FX_FLOAT fLineWidth = 0.0f, fBackupLineWidth = 0.0f;
+ FX_FLOAT fLineAscent = 0.0f, fBackupLineAscent = 0.0f;
+ FX_FLOAT fLineDescent = 0.0f, fBackupLineDescent = 0.0f;
+ int32_t nWordStartPos = 0;
+ FX_BOOL bFullWord = FALSE;
+ int32_t nLineFullWordIndex = 0;
+ int32_t nCharIndex = 0;
+ CPVT_LineInfo line;
+ FX_FLOAT fWordWidth = 0;
+ FX_FLOAT fTypesetWidth = std::max(
+ m_pVT->GetPlateWidth() - m_pVT->GetLineIndent(m_pSection->m_SecInfo),
+ 0.0f);
+ int32_t nTotalWords = m_pSection->m_WordArray.GetSize();
+ FX_BOOL bOpened = FALSE;
+ if (nTotalWords > 0) {
+ int32_t i = 0;
+ while (i < nTotalWords) {
+ CPVT_WordInfo* pWord = m_pSection->m_WordArray.GetAt(i);
+ CPVT_WordInfo* pOldWord = pWord;
+ if (i > 0) {
+ pOldWord = m_pSection->m_WordArray.GetAt(i - 1);
+ }
+ if (pWord) {
+ if (bTypeset) {
+ fLineAscent =
+ std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, TRUE));
+ fLineDescent =
+ std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, TRUE));
+ fWordWidth = m_pVT->GetWordWidth(*pWord);
+ } else {
+ fLineAscent =
+ std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, fFontSize));
+ fLineDescent =
+ std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, fFontSize));
+ fWordWidth = m_pVT->GetWordWidth(
+ pWord->nFontIndex, pWord->Word, m_pVT->m_wSubWord,
+ m_pVT->m_fCharSpace, m_pVT->m_nHorzScale, fFontSize,
+ pWord->fWordTail, 0);
+ }
+ if (!bOpened) {
+ if (IsOpenStylePunctuation(pWord->Word)) {
+ bOpened = TRUE;
+ bFullWord = TRUE;
+ } else if (pOldWord) {
+ if (NeedDivision(pOldWord->Word, pWord->Word)) {
+ bFullWord = TRUE;
+ }
+ }
+ } else {
+ if (!IsSpace(pWord->Word) && !IsOpenStylePunctuation(pWord->Word)) {
+ bOpened = FALSE;
+ }
+ }
+ if (bFullWord) {
+ bFullWord = FALSE;
+ if (nCharIndex > 0) {
+ nLineFullWordIndex++;
+ }
+ nWordStartPos = i;
+ fBackupLineWidth = fLineWidth;
+ fBackupLineAscent = fLineAscent;
+ fBackupLineDescent = fLineDescent;
+ }
+ nCharIndex++;
+ }
+ if (m_pVT->m_bLimitWidth && fTypesetWidth > 0 &&
+ fLineWidth + fWordWidth > fTypesetWidth) {
+ if (nLineFullWordIndex > 0) {
+ i = nWordStartPos;
+ fLineWidth = fBackupLineWidth;
+ fLineAscent = fBackupLineAscent;
+ fLineDescent = fBackupLineDescent;
+ }
+ if (nCharIndex == 1) {
+ fLineWidth = fWordWidth;
+ i++;
+ }
+ nLineTail = i - 1;
+ if (bTypeset) {
+ line.nBeginWordIndex = nLineHead;
+ line.nEndWordIndex = nLineTail;
+ line.nTotalWord = nLineTail - nLineHead + 1;
+ line.fLineWidth = fLineWidth;
+ line.fLineAscent = fLineAscent;
+ line.fLineDescent = fLineDescent;
+ m_pSection->AddLine(line);
+ }
+ fMaxY += (fLineAscent + m_pVT->GetLineLeading(m_pSection->m_SecInfo));
+ fMaxY += (-fLineDescent);
+ fMaxX = std::max(fLineWidth, fMaxX);
+ nLineHead = i;
+ fLineWidth = 0.0f;
+ fLineAscent = 0.0f;
+ fLineDescent = 0.0f;
+ nCharIndex = 0;
+ nLineFullWordIndex = 0;
+ bFullWord = FALSE;
+ } else {
+ fLineWidth += fWordWidth;
+ i++;
+ }
+ }
+ if (nLineHead <= nTotalWords - 1) {
+ nLineTail = nTotalWords - 1;
+ if (bTypeset) {
+ line.nBeginWordIndex = nLineHead;
+ line.nEndWordIndex = nLineTail;
+ line.nTotalWord = nLineTail - nLineHead + 1;
+ line.fLineWidth = fLineWidth;
+ line.fLineAscent = fLineAscent;
+ line.fLineDescent = fLineDescent;
+ m_pSection->AddLine(line);
+ }
+ fMaxY += (fLineAscent + m_pVT->GetLineLeading(m_pSection->m_SecInfo));
+ fMaxY += (-fLineDescent);
+ fMaxX = std::max(fLineWidth, fMaxX);
+ }
+ } else {
+ if (bTypeset) {
+ fLineAscent = m_pVT->GetLineAscent(m_pSection->m_SecInfo);
+ fLineDescent = m_pVT->GetLineDescent(m_pSection->m_SecInfo);
+ } else {
+ fLineAscent =
+ m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), fFontSize);
+ fLineDescent =
+ m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), fFontSize);
+ }
+ if (bTypeset) {
+ line.nBeginWordIndex = -1;
+ line.nEndWordIndex = -1;
+ line.nTotalWord = 0;
+ line.fLineWidth = 0;
+ line.fLineAscent = fLineAscent;
+ line.fLineDescent = fLineDescent;
+ m_pSection->AddLine(line);
+ }
+ fMaxY += (m_pVT->GetLineLeading(m_pSection->m_SecInfo) + fLineAscent +
+ (-fLineDescent));
+ }
+ m_rcRet = CPVT_FloatRect(0, 0, fMaxX, fMaxY);
+}
+void CTypeset::OutputLines() {
+ ASSERT(m_pVT);
+ ASSERT(m_pSection);
+ FX_FLOAT fMinX = 0.0f, fMinY = 0.0f, fMaxX = 0.0f, fMaxY = 0.0f;
+ FX_FLOAT fPosX = 0.0f, fPosY = 0.0f;
+ FX_FLOAT fLineIndent = m_pVT->GetLineIndent(m_pSection->m_SecInfo);
+ FX_FLOAT fTypesetWidth = std::max(m_pVT->GetPlateWidth() - fLineIndent, 0.0f);
+ switch (m_pVT->GetAlignment(m_pSection->m_SecInfo)) {
+ default:
+ case 0:
+ fMinX = 0.0f;
+ break;
+ case 1:
+ fMinX = (fTypesetWidth - m_rcRet.Width()) * PVT_HALF;
+ break;
+ case 2:
+ fMinX = fTypesetWidth - m_rcRet.Width();
+ break;
+ }
+ fMaxX = fMinX + m_rcRet.Width();
+ fMinY = 0.0f;
+ fMaxY = m_rcRet.Height();
+ int32_t nTotalLines = m_pSection->m_LineArray.GetSize();
+ if (nTotalLines > 0) {
+ m_pSection->m_SecInfo.nTotalLine = nTotalLines;
+ for (int32_t l = 0; l < nTotalLines; l++) {
+ if (CLine* pLine = m_pSection->m_LineArray.GetAt(l)) {
+ switch (m_pVT->GetAlignment(m_pSection->m_SecInfo)) {
+ default:
+ case 0:
+ fPosX = 0;
+ break;
+ case 1:
+ fPosX = (fTypesetWidth - pLine->m_LineInfo.fLineWidth) * PVT_HALF;
+ break;
+ case 2:
+ fPosX = fTypesetWidth - pLine->m_LineInfo.fLineWidth;
+ break;
+ }
+ fPosX += fLineIndent;
+ fPosY += m_pVT->GetLineLeading(m_pSection->m_SecInfo);
+ fPosY += pLine->m_LineInfo.fLineAscent;
+ pLine->m_LineInfo.fLineX = fPosX - fMinX;
+ pLine->m_LineInfo.fLineY = fPosY - fMinY;
+ for (int32_t w = pLine->m_LineInfo.nBeginWordIndex;
+ w <= pLine->m_LineInfo.nEndWordIndex; w++) {
+ if (CPVT_WordInfo* pWord = m_pSection->m_WordArray.GetAt(w)) {
+ pWord->fWordX = fPosX - fMinX;
+ if (pWord->pWordProps) {
+ switch (pWord->pWordProps->nScriptType) {
+ default:
+ case PVTWORD_SCRIPT_NORMAL:
+ pWord->fWordY = fPosY - fMinY;
+ break;
+ case PVTWORD_SCRIPT_SUPER:
+ pWord->fWordY = fPosY - m_pVT->GetWordAscent(*pWord) - fMinY;
+ break;
+ case PVTWORD_SCRIPT_SUB:
+ pWord->fWordY = fPosY - m_pVT->GetWordDescent(*pWord) - fMinY;
+ break;
+ }
+ } else {
+ pWord->fWordY = fPosY - fMinY;
+ }
+ fPosX += m_pVT->GetWordWidth(*pWord);
+ }
+ }
+ fPosY += (-pLine->m_LineInfo.fLineDescent);
+ }
+ }
+ }
+ m_rcRet = CPVT_FloatRect(fMinX, fMinY, fMaxX, fMaxY);
+}
+CPDF_VariableText::CPDF_VariableText()
+ : m_nLimitChar(0),
+ m_nCharArray(0),
+ m_bMultiLine(FALSE),
+ m_bLimitWidth(FALSE),
+ m_bAutoFontSize(FALSE),
+ m_nAlignment(0),
+ m_fLineLeading(0.0f),
+ m_fCharSpace(0.0f),
+ m_nHorzScale(100),
+ m_wSubWord(0),
+ m_fFontSize(0.0f),
+ m_bInitial(FALSE),
+ m_bRichText(FALSE),
+ m_pVTProvider(NULL),
+ m_pVTIterator(NULL) {}
+CPDF_VariableText::~CPDF_VariableText() {
+ delete m_pVTIterator;
+ m_pVTIterator = NULL;
+ ResetAll();
+}
+void CPDF_VariableText::Initialize() {
+ if (!m_bInitial) {
+ CPVT_SectionInfo secinfo;
+ if (m_bRichText) {
+ secinfo.pSecProps = new CPVT_SecProps(0.0f, 0.0f, 0);
+ secinfo.pWordProps = new CPVT_WordProps(GetDefaultFontIndex(),
+ PVT_DEFAULT_FONTSIZE, 0, 0, 0);
+ }
+ CPVT_WordPlace place;
+ place.nSecIndex = 0;
+ AddSection(place, secinfo);
+ CPVT_LineInfo lineinfo;
+ lineinfo.fLineAscent = GetFontAscent(GetDefaultFontIndex(), GetFontSize());
+ lineinfo.fLineDescent =
+ GetFontDescent(GetDefaultFontIndex(), GetFontSize());
+ AddLine(place, lineinfo);
+ if (CSection* pSection = m_SectionArray.GetAt(0)) {
+ pSection->ResetLinePlace();
+ }
+ m_bInitial = TRUE;
+ }
+}
+void CPDF_VariableText::ResetAll() {
+ m_bInitial = FALSE;
+ ResetSectionArray();
+}
+CPVT_WordPlace CPDF_VariableText::InsertWord(const CPVT_WordPlace& place,
+ FX_WORD word,
+ int32_t charset,
+ const CPVT_WordProps* pWordProps) {
+ int32_t nTotlaWords = GetTotalWords();
+ if (m_nLimitChar > 0 && nTotlaWords >= m_nLimitChar) {
+ return place;
+ }
+ if (m_nCharArray > 0 && nTotlaWords >= m_nCharArray) {
+ return place;
+ }
+ CPVT_WordPlace newplace = place;
+ newplace.nWordIndex++;
+ if (m_bRichText) {
+ CPVT_WordProps* pNewProps =
+ pWordProps ? new CPVT_WordProps(*pWordProps) : new CPVT_WordProps();
+ pNewProps->nFontIndex =
+ GetWordFontIndex(word, charset, pWordProps->nFontIndex);
+ return AddWord(newplace, CPVT_WordInfo(word, charset, -1, pNewProps));
+ }
+ int32_t nFontIndex =
+ GetSubWord() > 0 ? GetDefaultFontIndex()
+ : GetWordFontIndex(word, charset, GetDefaultFontIndex());
+ return AddWord(newplace, CPVT_WordInfo(word, charset, nFontIndex, NULL));
+}
+CPVT_WordPlace CPDF_VariableText::InsertSection(
+ const CPVT_WordPlace& place,
+ const CPVT_SecProps* pSecProps,
+ const CPVT_WordProps* pWordProps) {
+ int32_t nTotlaWords = GetTotalWords();
+ if (m_nLimitChar > 0 && nTotlaWords >= m_nLimitChar) {
+ return place;
+ }
+ if (m_nCharArray > 0 && nTotlaWords >= m_nCharArray) {
+ return place;
+ }
+ if (!m_bMultiLine) {
+ return place;
+ }
+ CPVT_WordPlace wordplace = place;
+ UpdateWordPlace(wordplace);
+ CPVT_WordPlace newplace = place;
+ if (CSection* pSection = m_SectionArray.GetAt(wordplace.nSecIndex)) {
+ CPVT_WordPlace NewPlace(wordplace.nSecIndex + 1, 0, -1);
+ CPVT_SectionInfo secinfo;
+ if (m_bRichText) {
+ if (pSecProps) {
+ secinfo.pSecProps = new CPVT_SecProps(*pSecProps);
+ }
+ if (pWordProps) {
+ secinfo.pWordProps = new CPVT_WordProps(*pWordProps);
+ }
+ }
+ AddSection(NewPlace, secinfo);
+ newplace = NewPlace;
+ if (CSection* pNewSection = m_SectionArray.GetAt(NewPlace.nSecIndex)) {
+ for (int32_t w = wordplace.nWordIndex + 1,
+ sz = pSection->m_WordArray.GetSize();
+ w < sz; w++) {
+ if (CPVT_WordInfo* pWord = pSection->m_WordArray.GetAt(w)) {
+ NewPlace.nWordIndex++;
+ pNewSection->AddWord(NewPlace, *pWord);
+ }
+ }
+ }
+ ClearSectionRightWords(wordplace);
+ }
+ return newplace;
+}
+CPVT_WordPlace CPDF_VariableText::InsertText(const CPVT_WordPlace& place,
+ const FX_WCHAR* text,
+ int32_t charset,
+ const CPVT_SecProps* pSecProps,
+ const CPVT_WordProps* pProps) {
+ CFX_WideString swText = text;
+ CPVT_WordPlace wp = place;
+ for (int32_t i = 0, sz = swText.GetLength(); i < sz; i++) {
+ CPVT_WordPlace oldwp = wp;
+ FX_WORD word = swText.GetAt(i);
+ switch (word) {
+ case 0x0D:
+ if (m_bMultiLine) {
+ if (swText.GetAt(i + 1) == 0x0A) {
+ i += 1;
+ }
+ wp = InsertSection(wp, pSecProps, pProps);
+ }
+ break;
+ case 0x0A:
+ if (m_bMultiLine) {
+ if (swText.GetAt(i + 1) == 0x0D) {
+ i += 1;
+ }
+ wp = InsertSection(wp, pSecProps, pProps);
+ }
+ break;
+ case 0x09:
+ word = 0x20;
+ default:
+ wp = InsertWord(wp, word, charset, pProps);
+ break;
+ }
+ if (wp == oldwp) {
+ break;
+ }
+ }
+ return wp;
+}
+CPVT_WordPlace CPDF_VariableText::DeleteWords(
+ const CPVT_WordRange& PlaceRange) {
+ FX_BOOL bLastSecPos = FALSE;
+ if (CSection* pSection = m_SectionArray.GetAt(PlaceRange.EndPos.nSecIndex)) {
+ bLastSecPos = (PlaceRange.EndPos == pSection->GetEndWordPlace());
+ }
+ ClearWords(PlaceRange);
+ if (PlaceRange.BeginPos.nSecIndex != PlaceRange.EndPos.nSecIndex) {
+ ClearEmptySections(PlaceRange);
+ if (!bLastSecPos) {
+ LinkLatterSection(PlaceRange.BeginPos);
+ }
+ }
+ return PlaceRange.BeginPos;
+}
+CPVT_WordPlace CPDF_VariableText::DeleteWord(const CPVT_WordPlace& place) {
+ return ClearRightWord(AdjustLineHeader(place, TRUE));
+}
+CPVT_WordPlace CPDF_VariableText::BackSpaceWord(const CPVT_WordPlace& place) {
+ return ClearLeftWord(AdjustLineHeader(place, TRUE));
+}
+void CPDF_VariableText::SetText(const FX_WCHAR* text,
+ int32_t charset,
+ const CPVT_SecProps* pSecProps,
+ const CPVT_WordProps* pWordProps) {
+ DeleteWords(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
+ CFX_WideString swText = text;
+ CPVT_WordPlace wp(0, 0, -1);
+ CPVT_SectionInfo secinfo;
+ if (m_bRichText) {
+ if (pSecProps) {
+ secinfo.pSecProps = new CPVT_SecProps(*pSecProps);
+ }
+ if (pWordProps) {
+ secinfo.pWordProps = new CPVT_WordProps(*pWordProps);
+ }
+ }
+ if (CSection* pSection = m_SectionArray.GetAt(0)) {
+ pSection->m_SecInfo = secinfo;
+ }
+ int32_t nCharCount = 0;
+ for (int32_t i = 0, sz = swText.GetLength(); i < sz; i++) {
+ if (m_nLimitChar > 0 && nCharCount >= m_nLimitChar) {
+ break;
+ }
+ if (m_nCharArray > 0 && nCharCount >= m_nCharArray) {
+ break;
+ }
+ FX_WORD word = swText.GetAt(i);
+ switch (word) {
+ case 0x0D:
+ if (m_bMultiLine) {
+ if (swText.GetAt(i + 1) == 0x0A) {
+ i += 1;
+ }
+ wp.nSecIndex++;
+ wp.nLineIndex = 0;
+ wp.nWordIndex = -1;
+ AddSection(wp, secinfo);
+ }
+ break;
+ case 0x0A:
+ if (m_bMultiLine) {
+ if (swText.GetAt(i + 1) == 0x0D) {
+ i += 1;
+ }
+ wp.nSecIndex++;
+ wp.nLineIndex = 0;
+ wp.nWordIndex = -1;
+ AddSection(wp, secinfo);
+ }
+ break;
+ case 0x09:
+ word = 0x20;
+ default:
+ wp = InsertWord(wp, word, charset, pWordProps);
+ break;
+ }
+ nCharCount++;
+ }
+}
+void CPDF_VariableText::UpdateWordPlace(CPVT_WordPlace& place) const {
+ if (place.nSecIndex < 0) {
+ place = GetBeginWordPlace();
+ }
+ if (place.nSecIndex >= m_SectionArray.GetSize()) {
+ place = GetEndWordPlace();
+ }
+ place = AdjustLineHeader(place, TRUE);
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ pSection->UpdateWordPlace(place);
+ }
+}
+int32_t CPDF_VariableText::WordPlaceToWordIndex(
+ const CPVT_WordPlace& place) const {
+ CPVT_WordPlace newplace = place;
+ UpdateWordPlace(newplace);
+ int32_t nIndex = 0;
+ int32_t i = 0;
+ int32_t sz = 0;
+ for (i = 0, sz = m_SectionArray.GetSize(); i < sz && i < newplace.nSecIndex;
+ i++) {
+ if (CSection* pSection = m_SectionArray.GetAt(i)) {
+ nIndex += pSection->m_WordArray.GetSize();
+ if (i != m_SectionArray.GetSize() - 1) {
+ nIndex += PVT_RETURN_LENGTH;
+ }
+ }
+ }
+ if (i >= 0 && i < m_SectionArray.GetSize()) {
+ nIndex += newplace.nWordIndex + PVT_RETURN_LENGTH;
+ }
+ return nIndex;
+}
+CPVT_WordPlace CPDF_VariableText::WordIndexToWordPlace(int32_t index) const {
+ CPVT_WordPlace place = GetBeginWordPlace();
+ int32_t nOldIndex = 0, nIndex = 0;
+ FX_BOOL bFind = FALSE;
+ for (int32_t i = 0, sz = m_SectionArray.GetSize(); i < sz; i++) {
+ if (CSection* pSection = m_SectionArray.GetAt(i)) {
+ nIndex += pSection->m_WordArray.GetSize();
+ if (nIndex == index) {
+ place = pSection->GetEndWordPlace();
+ bFind = TRUE;
+ break;
+ } else if (nIndex > index) {
+ place.nSecIndex = i;
+ place.nWordIndex = index - nOldIndex - 1;
+ pSection->UpdateWordPlace(place);
+ bFind = TRUE;
+ break;
+ }
+ if (i != m_SectionArray.GetSize() - 1) {
+ nIndex += PVT_RETURN_LENGTH;
+ }
+ nOldIndex = nIndex;
+ }
+ }
+ if (!bFind) {
+ place = GetEndWordPlace();
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::GetBeginWordPlace() const {
+ return m_bInitial ? CPVT_WordPlace(0, 0, -1) : CPVT_WordPlace();
+}
+CPVT_WordPlace CPDF_VariableText::GetEndWordPlace() const {
+ if (CSection* pSection = m_SectionArray.GetAt(m_SectionArray.GetSize() - 1)) {
+ return pSection->GetEndWordPlace();
+ }
+ return CPVT_WordPlace();
+}
+CPVT_WordPlace CPDF_VariableText::GetPrevWordPlace(
+ const CPVT_WordPlace& place) const {
+ if (place.nSecIndex < 0) {
+ return GetBeginWordPlace();
+ }
+ if (place.nSecIndex >= m_SectionArray.GetSize()) {
+ return GetEndWordPlace();
+ }
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (place.WordCmp(pSection->GetBeginWordPlace()) <= 0) {
+ if (CSection* pPrevSection = m_SectionArray.GetAt(place.nSecIndex - 1)) {
+ return pPrevSection->GetEndWordPlace();
+ }
+ return GetBeginWordPlace();
+ }
+ return pSection->GetPrevWordPlace(place);
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::GetNextWordPlace(
+ const CPVT_WordPlace& place) const {
+ if (place.nSecIndex < 0) {
+ return GetBeginWordPlace();
+ }
+ if (place.nSecIndex >= m_SectionArray.GetSize()) {
+ return GetEndWordPlace();
+ }
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (place.WordCmp(pSection->GetEndWordPlace()) >= 0) {
+ if (CSection* pNextSection = m_SectionArray.GetAt(place.nSecIndex + 1)) {
+ return pNextSection->GetBeginWordPlace();
+ }
+ return GetEndWordPlace();
+ }
+ return pSection->GetNextWordPlace(place);
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::SearchWordPlace(
+ const CFX_FloatPoint& point) const {
+ CFX_FloatPoint pt = OutToIn(point);
+ CPVT_WordPlace place = GetBeginWordPlace();
+ int32_t nLeft = 0;
+ int32_t nRight = m_SectionArray.GetSize() - 1;
+ int32_t nMid = m_SectionArray.GetSize() / 2;
+ FX_BOOL bUp = TRUE;
+ FX_BOOL bDown = TRUE;
+ while (nLeft <= nRight) {
+ if (CSection* pSection = m_SectionArray.GetAt(nMid)) {
+ if (IsFloatBigger(pt.y, pSection->m_SecInfo.rcSection.top)) {
+ bUp = FALSE;
+ }
+ if (IsFloatBigger(pSection->m_SecInfo.rcSection.bottom, pt.y)) {
+ bDown = FALSE;
+ }
+ if (IsFloatSmaller(pt.y, pSection->m_SecInfo.rcSection.top)) {
+ nRight = nMid - 1;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ } else if (IsFloatBigger(pt.y, pSection->m_SecInfo.rcSection.bottom)) {
+ nLeft = nMid + 1;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ } else {
+ place = pSection->SearchWordPlace(
+ CFX_FloatPoint(pt.x - pSection->m_SecInfo.rcSection.left,
+ pt.y - pSection->m_SecInfo.rcSection.top));
+ place.nSecIndex = nMid;
+ return place;
+ }
+ } else {
+ break;
+ }
+ }
+ if (bUp) {
+ place = GetBeginWordPlace();
+ }
+ if (bDown) {
+ place = GetEndWordPlace();
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::GetUpWordPlace(
+ const CPVT_WordPlace& place,
+ const CFX_FloatPoint& point) const {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ CPVT_WordPlace temp = place;
+ CFX_FloatPoint pt = OutToIn(point);
+ if (temp.nLineIndex-- > 0) {
+ return pSection->SearchWordPlace(
+ pt.x - pSection->m_SecInfo.rcSection.left, temp);
+ }
+ if (temp.nSecIndex-- > 0) {
+ if (CSection* pLastSection = m_SectionArray.GetAt(temp.nSecIndex)) {
+ temp.nLineIndex = pLastSection->m_LineArray.GetSize() - 1;
+ return pLastSection->SearchWordPlace(
+ pt.x - pLastSection->m_SecInfo.rcSection.left, temp);
+ }
+ }
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::GetDownWordPlace(
+ const CPVT_WordPlace& place,
+ const CFX_FloatPoint& point) const {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ CPVT_WordPlace temp = place;
+ CFX_FloatPoint pt = OutToIn(point);
+ if (temp.nLineIndex++ < pSection->m_LineArray.GetSize() - 1) {
+ return pSection->SearchWordPlace(
+ pt.x - pSection->m_SecInfo.rcSection.left, temp);
+ }
+ if (temp.nSecIndex++ < m_SectionArray.GetSize() - 1) {
+ if (CSection* pNextSection = m_SectionArray.GetAt(temp.nSecIndex)) {
+ temp.nLineIndex = 0;
+ return pNextSection->SearchWordPlace(
+ pt.x - pSection->m_SecInfo.rcSection.left, temp);
+ }
+ }
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::GetLineBeginPlace(
+ const CPVT_WordPlace& place) const {
+ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1);
+}
+CPVT_WordPlace CPDF_VariableText::GetLineEndPlace(
+ const CPVT_WordPlace& place) const {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (CLine* pLine = pSection->m_LineArray.GetAt(place.nLineIndex))
+ return pLine->GetEndWordPlace();
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::GetSectionBeginPlace(
+ const CPVT_WordPlace& place) const {
+ return CPVT_WordPlace(place.nSecIndex, 0, -1);
+}
+CPVT_WordPlace CPDF_VariableText::GetSectionEndPlace(
+ const CPVT_WordPlace& place) const {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ return pSection->GetEndWordPlace();
+ }
+ return place;
+}
+int32_t CPDF_VariableText::GetTotalWords() const {
+ int32_t nTotal = 0;
+ for (int32_t i = 0, sz = m_SectionArray.GetSize(); i < sz; i++)
+ if (CSection* pSection = m_SectionArray.GetAt(i)) {
+ nTotal += (pSection->m_WordArray.GetSize() + PVT_RETURN_LENGTH);
+ }
+ return nTotal - PVT_RETURN_LENGTH;
+}
+void CPDF_VariableText::ResetSectionArray() {
+ for (int32_t s = 0, sz = m_SectionArray.GetSize(); s < sz; s++) {
+ delete m_SectionArray.GetAt(s);
+ }
+ m_SectionArray.RemoveAll();
+}
+CPVT_WordPlace CPDF_VariableText::AddSection(const CPVT_WordPlace& place,
+ const CPVT_SectionInfo& secinfo) {
+ if (IsValid() && !m_bMultiLine) {
+ return place;
+ }
+ int32_t nSecIndex =
+ std::max(std::min(place.nSecIndex, m_SectionArray.GetSize()), 0);
+ CSection* pSection = new CSection(this);
+ pSection->m_SecInfo = secinfo;
+ pSection->SecPlace.nSecIndex = nSecIndex;
+ if (nSecIndex == m_SectionArray.GetSize()) {
+ m_SectionArray.Add(pSection);
+ } else {
+ m_SectionArray.InsertAt(nSecIndex, pSection);
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::AddLine(const CPVT_WordPlace& place,
+ const CPVT_LineInfo& lineinfo) {
+ if (m_SectionArray.IsEmpty()) {
+ return place;
+ }
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ return pSection->AddLine(lineinfo);
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::AddWord(const CPVT_WordPlace& place,
+ const CPVT_WordInfo& wordinfo) {
+ if (m_SectionArray.GetSize() <= 0) {
+ return place;
+ }
+ CPVT_WordPlace newplace = place;
+ newplace.nSecIndex =
+ std::max(std::min(newplace.nSecIndex, m_SectionArray.GetSize() - 1), 0);
+ if (CSection* pSection = m_SectionArray.GetAt(newplace.nSecIndex)) {
+ return pSection->AddWord(newplace, wordinfo);
+ }
+ return place;
+}
+FX_BOOL CPDF_VariableText::GetWordInfo(const CPVT_WordPlace& place,
+ CPVT_WordInfo& wordinfo) {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (CPVT_WordInfo* pWord = pSection->m_WordArray.GetAt(place.nWordIndex)) {
+ wordinfo = *pWord;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText::SetWordInfo(const CPVT_WordPlace& place,
+ const CPVT_WordInfo& wordinfo) {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (CPVT_WordInfo* pWord = pSection->m_WordArray.GetAt(place.nWordIndex)) {
+ *pWord = wordinfo;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText::GetLineInfo(const CPVT_WordPlace& place,
+ CPVT_LineInfo& lineinfo) {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (CLine* pLine = pSection->m_LineArray.GetAt(place.nLineIndex)) {
+ lineinfo = pLine->m_LineInfo;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText::GetSectionInfo(const CPVT_WordPlace& place,
+ CPVT_SectionInfo& secinfo) {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ secinfo = pSection->m_SecInfo;
+ return TRUE;
+ }
+ return FALSE;
+}
+CFX_FloatRect CPDF_VariableText::GetContentRect() const {
+ return InToOut(CPVT_FloatRect(CPDF_EditContainer::GetContentRect()));
+}
+FX_FLOAT CPDF_VariableText::GetWordFontSize(const CPVT_WordInfo& WordInfo,
+ FX_BOOL bFactFontSize) {
+ return m_bRichText && WordInfo.pWordProps
+ ? (WordInfo.pWordProps->nScriptType == PVTWORD_SCRIPT_NORMAL ||
+ bFactFontSize
+ ? WordInfo.pWordProps->fFontSize
+ : WordInfo.pWordProps->fFontSize * PVT_HALF)
+ : GetFontSize();
+}
+int32_t CPDF_VariableText::GetWordFontIndex(const CPVT_WordInfo& WordInfo) {
+ return m_bRichText && WordInfo.pWordProps ? WordInfo.pWordProps->nFontIndex
+ : WordInfo.nFontIndex;
+}
+FX_FLOAT CPDF_VariableText::GetWordWidth(int32_t nFontIndex,
+ FX_WORD Word,
+ FX_WORD SubWord,
+ FX_FLOAT fCharSpace,
+ int32_t nHorzScale,
+ FX_FLOAT fFontSize,
+ FX_FLOAT fWordTail,
+ int32_t nWordStyle) {
+ return (GetCharWidth(nFontIndex, Word, SubWord, nWordStyle) * fFontSize *
+ PVT_FONTSCALE +
+ fCharSpace) *
+ nHorzScale * PVT_PERCENT +
+ fWordTail;
+}
+FX_FLOAT CPDF_VariableText::GetWordWidth(const CPVT_WordInfo& WordInfo) {
+ return GetWordWidth(
+ GetWordFontIndex(WordInfo), WordInfo.Word, GetSubWord(),
+ GetCharSpace(WordInfo), GetHorzScale(WordInfo), GetWordFontSize(WordInfo),
+ WordInfo.fWordTail,
+ WordInfo.pWordProps ? WordInfo.pWordProps->nWordStyle : 0);
+}
+FX_FLOAT CPDF_VariableText::GetLineAscent(const CPVT_SectionInfo& SecInfo) {
+ return m_bRichText && SecInfo.pWordProps
+ ? GetFontAscent(SecInfo.pWordProps->nFontIndex,
+ SecInfo.pWordProps->fFontSize)
+ : GetFontAscent(GetDefaultFontIndex(), GetFontSize());
+}
+FX_FLOAT CPDF_VariableText::GetLineDescent(const CPVT_SectionInfo& SecInfo) {
+ return m_bRichText && SecInfo.pWordProps
+ ? GetFontDescent(SecInfo.pWordProps->nFontIndex,
+ SecInfo.pWordProps->fFontSize)
+ : GetFontDescent(GetDefaultFontIndex(), GetFontSize());
+}
+FX_FLOAT CPDF_VariableText::GetFontAscent(int32_t nFontIndex,
+ FX_FLOAT fFontSize) {
+ return (FX_FLOAT)GetTypeAscent(nFontIndex) * fFontSize * PVT_FONTSCALE;
+}
+FX_FLOAT CPDF_VariableText::GetFontDescent(int32_t nFontIndex,
+ FX_FLOAT fFontSize) {
+ return (FX_FLOAT)GetTypeDescent(nFontIndex) * fFontSize * PVT_FONTSCALE;
+}
+FX_FLOAT CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo,
+ FX_FLOAT fFontSize) {
+ return GetFontAscent(GetWordFontIndex(WordInfo), fFontSize);
+}
+FX_FLOAT CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo,
+ FX_FLOAT fFontSize) {
+ return GetFontDescent(GetWordFontIndex(WordInfo), fFontSize);
+}
+FX_FLOAT CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo,
+ FX_BOOL bFactFontSize) {
+ return GetFontAscent(GetWordFontIndex(WordInfo),
+ GetWordFontSize(WordInfo, bFactFontSize));
+}
+FX_FLOAT CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo,
+ FX_BOOL bFactFontSize) {
+ return GetFontDescent(GetWordFontIndex(WordInfo),
+ GetWordFontSize(WordInfo, bFactFontSize));
+}
+FX_FLOAT CPDF_VariableText::GetLineLeading(const CPVT_SectionInfo& SecInfo) {
+ return m_bRichText && SecInfo.pSecProps ? SecInfo.pSecProps->fLineLeading
+ : m_fLineLeading;
+}
+FX_FLOAT CPDF_VariableText::GetLineIndent(const CPVT_SectionInfo& SecInfo) {
+ return m_bRichText && SecInfo.pSecProps ? SecInfo.pSecProps->fLineIndent
+ : 0.0f;
+}
+int32_t CPDF_VariableText::GetAlignment(const CPVT_SectionInfo& SecInfo) {
+ return m_bRichText && SecInfo.pSecProps ? SecInfo.pSecProps->nAlignment
+ : m_nAlignment;
+}
+FX_FLOAT CPDF_VariableText::GetCharSpace(const CPVT_WordInfo& WordInfo) {
+ return m_bRichText && WordInfo.pWordProps ? WordInfo.pWordProps->fCharSpace
+ : m_fCharSpace;
+}
+int32_t CPDF_VariableText::GetHorzScale(const CPVT_WordInfo& WordInfo) {
+ return m_bRichText && WordInfo.pWordProps ? WordInfo.pWordProps->nHorzScale
+ : m_nHorzScale;
+}
+void CPDF_VariableText::ClearSectionRightWords(const CPVT_WordPlace& place) {
+ CPVT_WordPlace wordplace = AdjustLineHeader(place, TRUE);
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ for (int32_t w = pSection->m_WordArray.GetSize() - 1;
+ w > wordplace.nWordIndex; w--) {
+ delete pSection->m_WordArray.GetAt(w);
+ pSection->m_WordArray.RemoveAt(w);
+ }
+ }
+}
+CPVT_WordPlace CPDF_VariableText::AdjustLineHeader(const CPVT_WordPlace& place,
+ FX_BOOL bPrevOrNext) const {
+ if (place.nWordIndex < 0 && place.nLineIndex > 0) {
+ return bPrevOrNext ? GetPrevWordPlace(place) : GetNextWordPlace(place);
+ }
+ return place;
+}
+FX_BOOL CPDF_VariableText::ClearEmptySection(const CPVT_WordPlace& place) {
+ if (place.nSecIndex == 0 && m_SectionArray.GetSize() == 1) {
+ return FALSE;
+ }
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ if (pSection->m_WordArray.GetSize() == 0) {
+ delete pSection;
+ m_SectionArray.RemoveAt(place.nSecIndex);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+void CPDF_VariableText::ClearEmptySections(const CPVT_WordRange& PlaceRange) {
+ CPVT_WordPlace wordplace;
+ for (int32_t s = PlaceRange.EndPos.nSecIndex;
+ s > PlaceRange.BeginPos.nSecIndex; s--) {
+ wordplace.nSecIndex = s;
+ ClearEmptySection(wordplace);
+ }
+}
+void CPDF_VariableText::LinkLatterSection(const CPVT_WordPlace& place) {
+ CPVT_WordPlace oldplace = AdjustLineHeader(place, TRUE);
+ if (CSection* pNextSection = m_SectionArray.GetAt(place.nSecIndex + 1)) {
+ if (CSection* pSection = m_SectionArray.GetAt(oldplace.nSecIndex)) {
+ for (int32_t w = 0, sz = pNextSection->m_WordArray.GetSize(); w < sz;
+ w++) {
+ if (CPVT_WordInfo* pWord = pNextSection->m_WordArray.GetAt(w)) {
+ oldplace.nWordIndex++;
+ pSection->AddWord(oldplace, *pWord);
+ }
+ }
+ }
+ delete pNextSection;
+ m_SectionArray.RemoveAt(place.nSecIndex + 1);
+ }
+}
+void CPDF_VariableText::ClearWords(const CPVT_WordRange& PlaceRange) {
+ CPVT_WordRange NewRange;
+ NewRange.BeginPos = AdjustLineHeader(PlaceRange.BeginPos, TRUE);
+ NewRange.EndPos = AdjustLineHeader(PlaceRange.EndPos, TRUE);
+ for (int32_t s = NewRange.EndPos.nSecIndex; s >= NewRange.BeginPos.nSecIndex;
+ s--) {
+ if (CSection* pSection = m_SectionArray.GetAt(s)) {
+ pSection->ClearWords(NewRange);
+ }
+ }
+}
+CPVT_WordPlace CPDF_VariableText::ClearLeftWord(const CPVT_WordPlace& place) {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ CPVT_WordPlace leftplace = GetPrevWordPlace(place);
+ if (leftplace != place) {
+ if (leftplace.nSecIndex != place.nSecIndex) {
+ if (pSection->m_WordArray.GetSize() == 0) {
+ ClearEmptySection(place);
+ } else {
+ LinkLatterSection(leftplace);
+ }
+ } else {
+ pSection->ClearWord(place);
+ }
+ }
+ return leftplace;
+ }
+ return place;
+}
+CPVT_WordPlace CPDF_VariableText::ClearRightWord(const CPVT_WordPlace& place) {
+ if (CSection* pSection = m_SectionArray.GetAt(place.nSecIndex)) {
+ CPVT_WordPlace rightplace =
+ AdjustLineHeader(GetNextWordPlace(place), FALSE);
+ if (rightplace != place) {
+ if (rightplace.nSecIndex != place.nSecIndex) {
+ LinkLatterSection(place);
+ } else {
+ pSection->ClearWord(rightplace);
+ }
+ }
+ }
+ return place;
+}
+void CPDF_VariableText::RearrangeAll() {
+ Rearrange(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
+}
+void CPDF_VariableText::RearrangePart(const CPVT_WordRange& PlaceRange) {
+ Rearrange(PlaceRange);
+}
+CPVT_FloatRect CPDF_VariableText::Rearrange(const CPVT_WordRange& PlaceRange) {
+ CPVT_FloatRect rcRet;
+ if (IsValid()) {
+ if (m_bAutoFontSize) {
+ SetFontSize(GetAutoFontSize());
+ rcRet = RearrangeSections(
+ CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace()));
+ } else {
+ rcRet = RearrangeSections(PlaceRange);
+ }
+ }
+ SetContentRect(rcRet);
+ return rcRet;
+}
+FX_FLOAT CPDF_VariableText::GetAutoFontSize() {
+ int32_t nTotal = sizeof(gFontSizeSteps) / sizeof(uint8_t);
+ if (IsMultiLine()) {
+ nTotal /= 4;
+ }
+ if (nTotal <= 0) {
+ return 0;
+ }
+ if (GetPlateWidth() <= 0) {
+ return 0;
+ }
+ int32_t nLeft = 0;
+ int32_t nRight = nTotal - 1;
+ int32_t nMid = nTotal / 2;
+ while (nLeft <= nRight) {
+ if (IsBigger(gFontSizeSteps[nMid])) {
+ nRight = nMid - 1;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ } else {
+ nLeft = nMid + 1;
+ nMid = (nLeft + nRight) / 2;
+ continue;
+ }
+ }
+ return (FX_FLOAT)gFontSizeSteps[nMid];
+}
+FX_BOOL CPDF_VariableText::IsBigger(FX_FLOAT fFontSize) {
+ FX_BOOL bBigger = FALSE;
+ CPVT_Size szTotal;
+ for (int32_t s = 0, sz = m_SectionArray.GetSize(); s < sz; s++) {
+ if (CSection* pSection = m_SectionArray.GetAt(s)) {
+ CPVT_Size size = pSection->GetSectionSize(fFontSize);
+ szTotal.x = std::max(size.x, szTotal.x);
+ szTotal.y += size.y;
+ if (IsFloatBigger(szTotal.x, GetPlateWidth()) ||
+ IsFloatBigger(szTotal.y, GetPlateHeight())) {
+ bBigger = TRUE;
+ break;
+ }
+ }
+ }
+ return bBigger;
+}
+CPVT_FloatRect CPDF_VariableText::RearrangeSections(
+ const CPVT_WordRange& PlaceRange) {
+ CPVT_WordPlace place;
+ FX_FLOAT fPosY = 0;
+ FX_FLOAT fOldHeight;
+ int32_t nSSecIndex = PlaceRange.BeginPos.nSecIndex;
+ int32_t nESecIndex = PlaceRange.EndPos.nSecIndex;
+ CPVT_FloatRect rcRet;
+ for (int32_t s = 0, sz = m_SectionArray.GetSize(); s < sz; s++) {
+ place.nSecIndex = s;
+ if (CSection* pSection = m_SectionArray.GetAt(s)) {
+ pSection->SecPlace = place;
+ CPVT_FloatRect rcSec = pSection->m_SecInfo.rcSection;
+ if (s >= nSSecIndex) {
+ if (s <= nESecIndex) {
+ rcSec = pSection->Rearrange();
+ rcSec.top += fPosY;
+ rcSec.bottom += fPosY;
+ } else {
+ fOldHeight = pSection->m_SecInfo.rcSection.bottom -
+ pSection->m_SecInfo.rcSection.top;
+ rcSec.top = fPosY;
+ rcSec.bottom = fPosY + fOldHeight;
+ }
+ pSection->m_SecInfo.rcSection = rcSec;
+ pSection->ResetLinePlace();
+ }
+ if (s == 0) {
+ rcRet = rcSec;
+ } else {
+ rcRet.left = std::min(rcSec.left, rcRet.left);
+ rcRet.top = std::min(rcSec.top, rcRet.top);
+ rcRet.right = std::max(rcSec.right, rcRet.right);
+ rcRet.bottom = std::max(rcSec.bottom, rcRet.bottom);
+ }
+ fPosY += rcSec.Height();
+ }
+ }
+ return rcRet;
+}
+int32_t CPDF_VariableText::GetCharWidth(int32_t nFontIndex,
+ FX_WORD Word,
+ FX_WORD SubWord,
+ int32_t nWordStyle) {
+ if (!m_pVTProvider) {
+ return 0;
+ }
+ if (SubWord > 0) {
+ return m_pVTProvider->GetCharWidth(nFontIndex, SubWord, nWordStyle);
+ }
+ return m_pVTProvider->GetCharWidth(nFontIndex, Word, nWordStyle);
+}
+int32_t CPDF_VariableText::GetTypeAscent(int32_t nFontIndex) {
+ return m_pVTProvider ? m_pVTProvider->GetTypeAscent(nFontIndex) : 0;
+}
+int32_t CPDF_VariableText::GetTypeDescent(int32_t nFontIndex) {
+ return m_pVTProvider ? m_pVTProvider->GetTypeDescent(nFontIndex) : 0;
+}
+int32_t CPDF_VariableText::GetWordFontIndex(FX_WORD word,
+ int32_t charset,
+ int32_t nFontIndex) {
+ return m_pVTProvider
+ ? m_pVTProvider->GetWordFontIndex(word, charset, nFontIndex)
+ : -1;
+}
+int32_t CPDF_VariableText::GetDefaultFontIndex() {
+ return m_pVTProvider ? m_pVTProvider->GetDefaultFontIndex() : -1;
+}
+FX_BOOL CPDF_VariableText::IsLatinWord(FX_WORD word) {
+ return m_pVTProvider ? m_pVTProvider->IsLatinWord(word) : FALSE;
+}
+IPDF_VariableText_Iterator* CPDF_VariableText::GetIterator() {
+ if (!m_pVTIterator) {
+ m_pVTIterator = new CPDF_VariableText_Iterator(this);
+ }
+ return m_pVTIterator;
+}
+IPDF_VariableText_Provider* CPDF_VariableText::SetProvider(
+ IPDF_VariableText_Provider* pProvider) {
+ IPDF_VariableText_Provider* pOld = m_pVTProvider;
+ m_pVTProvider = pProvider;
+ return pOld;
+}
+CPDF_VariableText_Iterator::CPDF_VariableText_Iterator(CPDF_VariableText* pVT)
+ : m_CurPos(-1, -1, -1), m_pVT(pVT) {}
+CPDF_VariableText_Iterator::~CPDF_VariableText_Iterator() {}
+void CPDF_VariableText_Iterator::SetAt(int32_t nWordIndex) {
+ m_CurPos = m_pVT->WordIndexToWordPlace(nWordIndex);
+}
+void CPDF_VariableText_Iterator::SetAt(const CPVT_WordPlace& place) {
+ ASSERT(m_pVT);
+ m_CurPos = place;
+}
+FX_BOOL CPDF_VariableText_Iterator::NextWord() {
+ if (m_CurPos == m_pVT->GetEndWordPlace()) {
+ return FALSE;
+ }
+ m_CurPos = m_pVT->GetNextWordPlace(m_CurPos);
+ return TRUE;
+}
+FX_BOOL CPDF_VariableText_Iterator::PrevWord() {
+ if (m_CurPos == m_pVT->GetBeginWordPlace()) {
+ return FALSE;
+ }
+ m_CurPos = m_pVT->GetPrevWordPlace(m_CurPos);
+ return TRUE;
+}
+FX_BOOL CPDF_VariableText_Iterator::NextLine() {
+ if (CSection* pSection = m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ if (m_CurPos.nLineIndex < pSection->m_LineArray.GetSize() - 1) {
+ m_CurPos =
+ CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex + 1, -1);
+ return TRUE;
+ }
+ if (m_CurPos.nSecIndex < m_pVT->m_SectionArray.GetSize() - 1) {
+ m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::PrevLine() {
+ if (m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ if (m_CurPos.nLineIndex > 0) {
+ m_CurPos =
+ CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex - 1, -1);
+ return TRUE;
+ }
+ if (m_CurPos.nSecIndex > 0) {
+ if (CSection* pLastSection =
+ m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex - 1)) {
+ m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex - 1,
+ pLastSection->m_LineArray.GetSize() - 1, -1);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::NextSection() {
+ if (m_CurPos.nSecIndex < m_pVT->m_SectionArray.GetSize() - 1) {
+ m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1);
+ return TRUE;
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::PrevSection() {
+ ASSERT(m_pVT);
+ if (m_CurPos.nSecIndex > 0) {
+ m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex - 1, 0, -1);
+ return TRUE;
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::GetWord(CPVT_Word& word) const {
+ word.WordPlace = m_CurPos;
+ if (CSection* pSection = m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ if (pSection->m_LineArray.GetAt(m_CurPos.nLineIndex)) {
+ if (CPVT_WordInfo* pWord =
+ pSection->m_WordArray.GetAt(m_CurPos.nWordIndex)) {
+ word.Word = pWord->Word;
+ word.nCharset = pWord->nCharset;
+ word.fWidth = m_pVT->GetWordWidth(*pWord);
+ word.ptWord = m_pVT->InToOut(
+ CFX_FloatPoint(pWord->fWordX + pSection->m_SecInfo.rcSection.left,
+ pWord->fWordY + pSection->m_SecInfo.rcSection.top));
+ word.fAscent = m_pVT->GetWordAscent(*pWord);
+ word.fDescent = m_pVT->GetWordDescent(*pWord);
+ if (pWord->pWordProps) {
+ word.WordProps = *pWord->pWordProps;
+ }
+ word.nFontIndex = m_pVT->GetWordFontIndex(*pWord);
+ word.fFontSize = m_pVT->GetWordFontSize(*pWord);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::SetWord(const CPVT_Word& word) {
+ if (CSection* pSection = m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ if (CPVT_WordInfo* pWord =
+ pSection->m_WordArray.GetAt(m_CurPos.nWordIndex)) {
+ if (pWord->pWordProps) {
+ *pWord->pWordProps = word.WordProps;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::GetLine(CPVT_Line& line) const {
+ ASSERT(m_pVT);
+ line.lineplace = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex, -1);
+ if (CSection* pSection = m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ if (CLine* pLine = pSection->m_LineArray.GetAt(m_CurPos.nLineIndex)) {
+ line.ptLine = m_pVT->InToOut(CFX_FloatPoint(
+ pLine->m_LineInfo.fLineX + pSection->m_SecInfo.rcSection.left,
+ pLine->m_LineInfo.fLineY + pSection->m_SecInfo.rcSection.top));
+ line.fLineWidth = pLine->m_LineInfo.fLineWidth;
+ line.fLineAscent = pLine->m_LineInfo.fLineAscent;
+ line.fLineDescent = pLine->m_LineInfo.fLineDescent;
+ line.lineEnd = pLine->GetEndWordPlace();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::GetSection(CPVT_Section& section) const {
+ section.secplace = CPVT_WordPlace(m_CurPos.nSecIndex, 0, -1);
+ if (CSection* pSection = m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ section.rcSection = m_pVT->InToOut(pSection->m_SecInfo.rcSection);
+ if (pSection->m_SecInfo.pSecProps) {
+ section.SecProps = *pSection->m_SecInfo.pSecProps;
+ }
+ if (pSection->m_SecInfo.pWordProps) {
+ section.WordProps = *pSection->m_SecInfo.pWordProps;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_VariableText_Iterator::SetSection(const CPVT_Section& section) {
+ if (CSection* pSection = m_pVT->m_SectionArray.GetAt(m_CurPos.nSecIndex)) {
+ if (pSection->m_SecInfo.pSecProps) {
+ *pSection->m_SecInfo.pSecProps = section.SecProps;
+ }
+ if (pSection->m_SecInfo.pWordProps) {
+ *pSection->m_SecInfo.pWordProps = section.WordProps;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/core/fpdfdoc/doc_vtmodule.cpp b/core/fpdfdoc/doc_vtmodule.cpp
new file mode 100644
index 0000000000..85e19ffd08
--- /dev/null
+++ b/core/fpdfdoc/doc_vtmodule.cpp
@@ -0,0 +1,16 @@
+// 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 "core/fpdfdoc/pdf_vt.h"
+#include "core/include/fpdfdoc/fpdf_doc.h"
+#include "core/include/fpdfdoc/fpdf_vt.h"
+
+IPDF_VariableText* IPDF_VariableText::NewVariableText() {
+ return new CPDF_VariableText();
+}
+void IPDF_VariableText::DelVariableText(IPDF_VariableText* pVT) {
+ delete (CPDF_VariableText*)pVT;
+}
diff --git a/core/fpdfdoc/pdf_vt.h b/core/fpdfdoc/pdf_vt.h
new file mode 100644
index 0000000000..53c48af4ec
--- /dev/null
+++ b/core/fpdfdoc/pdf_vt.h
@@ -0,0 +1,554 @@
+// 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
+
+#ifndef CORE_FPDFDOC_PDF_VT_H_
+#define CORE_FPDFDOC_PDF_VT_H_
+
+#include "core/include/fpdfdoc/fpdf_vt.h"
+
+class CPDF_VariableText;
+class CPDF_VariableText_Iterator;
+
+#define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001)
+#define IsFloatBigger(fa, fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb)))
+#define IsFloatSmaller(fa, fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb)))
+
+class CPVT_Size {
+ public:
+ CPVT_Size() : x(0.0f), y(0.0f) {}
+ CPVT_Size(FX_FLOAT other_x, FX_FLOAT other_y) {
+ x = other_x;
+ y = other_y;
+ }
+ FX_FLOAT x, y;
+};
+class CPVT_FloatRect : public CFX_FloatRect {
+ public:
+ CPVT_FloatRect() { left = top = right = bottom = 0.0f; }
+ CPVT_FloatRect(FX_FLOAT other_left,
+ FX_FLOAT other_top,
+ FX_FLOAT other_right,
+ FX_FLOAT other_bottom) {
+ left = other_left;
+ top = other_top;
+ right = other_right;
+ bottom = other_bottom;
+ }
+ explicit CPVT_FloatRect(const CFX_FloatRect& rect) {
+ left = rect.left;
+ top = rect.top;
+ right = rect.right;
+ bottom = rect.bottom;
+ }
+ void Default() { left = top = right = bottom = 0.0f; }
+ FX_FLOAT Height() const {
+ if (top > bottom)
+ return top - bottom;
+ return bottom - top;
+ }
+};
+struct CPVT_SectionInfo {
+ CPVT_SectionInfo()
+ : rcSection(), nTotalLine(0), pSecProps(NULL), pWordProps(NULL) {}
+ virtual ~CPVT_SectionInfo() {
+ delete pSecProps;
+ delete pWordProps;
+ }
+ CPVT_SectionInfo(const CPVT_SectionInfo& other)
+ : rcSection(), nTotalLine(0), pSecProps(NULL), pWordProps(NULL) {
+ operator=(other);
+ }
+ void operator=(const CPVT_SectionInfo& other) {
+ if (this == &other) {
+ return;
+ }
+ rcSection = other.rcSection;
+ nTotalLine = other.nTotalLine;
+ if (other.pSecProps) {
+ if (pSecProps) {
+ *pSecProps = *other.pSecProps;
+ } else {
+ pSecProps = new CPVT_SecProps(*other.pSecProps);
+ }
+ }
+ if (other.pWordProps) {
+ if (pWordProps) {
+ *pWordProps = *other.pWordProps;
+ } else {
+ pWordProps = new CPVT_WordProps(*other.pWordProps);
+ }
+ }
+ }
+ CPVT_FloatRect rcSection;
+ int32_t nTotalLine;
+ CPVT_SecProps* pSecProps;
+ CPVT_WordProps* pWordProps;
+};
+struct CPVT_LineInfo {
+ CPVT_LineInfo()
+ : nTotalWord(0),
+ nBeginWordIndex(-1),
+ nEndWordIndex(-1),
+ fLineX(0.0f),
+ fLineY(0.0f),
+ fLineWidth(0.0f),
+ fLineAscent(0.0f),
+ fLineDescent(0.0f) {}
+ int32_t nTotalWord;
+ int32_t nBeginWordIndex;
+ int32_t nEndWordIndex;
+ FX_FLOAT fLineX;
+ FX_FLOAT fLineY;
+ FX_FLOAT fLineWidth;
+ FX_FLOAT fLineAscent;
+ FX_FLOAT fLineDescent;
+};
+struct CPVT_WordInfo {
+ CPVT_WordInfo()
+ : Word(0),
+ nCharset(0),
+ fWordX(0.0f),
+ fWordY(0.0f),
+ fWordTail(0.0f),
+ nFontIndex(-1),
+ pWordProps(NULL) {}
+ CPVT_WordInfo(FX_WORD word,
+ int32_t charset,
+ int32_t fontIndex,
+ CPVT_WordProps* pProps)
+ : Word(word),
+ nCharset(charset),
+ fWordX(0.0f),
+ fWordY(0.0f),
+ fWordTail(0.0f),
+ nFontIndex(fontIndex),
+ pWordProps(pProps) {}
+ virtual ~CPVT_WordInfo() { delete pWordProps; }
+ CPVT_WordInfo(const CPVT_WordInfo& word)
+ : Word(0),
+ nCharset(0),
+ fWordX(0.0f),
+ fWordY(0.0f),
+ fWordTail(0.0f),
+ nFontIndex(-1),
+ pWordProps(NULL) {
+ operator=(word);
+ }
+ void operator=(const CPVT_WordInfo& word) {
+ if (this == &word) {
+ return;
+ }
+ Word = word.Word;
+ nCharset = word.nCharset;
+ nFontIndex = word.nFontIndex;
+ if (word.pWordProps) {
+ if (pWordProps) {
+ *pWordProps = *word.pWordProps;
+ } else {
+ pWordProps = new CPVT_WordProps(*word.pWordProps);
+ }
+ }
+ }
+ FX_WORD Word;
+ int32_t nCharset;
+ FX_FLOAT fWordX;
+ FX_FLOAT fWordY;
+ FX_FLOAT fWordTail;
+ int32_t nFontIndex;
+ CPVT_WordProps* pWordProps;
+};
+struct CPVT_FloatRange {
+ CPVT_FloatRange() : fMin(0.0f), fMax(0.0f) {}
+ CPVT_FloatRange(FX_FLOAT min, FX_FLOAT max) : fMin(min), fMax(max) {}
+ FX_FLOAT Range() const { return fMax - fMin; }
+ FX_FLOAT fMin, fMax;
+};
+template <class TYPE>
+class CPVT_ArrayTemplate : public CFX_ArrayTemplate<TYPE> {
+ public:
+ FX_BOOL IsEmpty() { return CFX_ArrayTemplate<TYPE>::GetSize() <= 0; }
+ TYPE GetAt(int nIndex) const {
+ if (nIndex >= 0 && nIndex < CFX_ArrayTemplate<TYPE>::GetSize()) {
+ return CFX_ArrayTemplate<TYPE>::GetAt(nIndex);
+ }
+ return NULL;
+ }
+ void RemoveAt(int nIndex) {
+ if (nIndex >= 0 && nIndex < CFX_ArrayTemplate<TYPE>::GetSize()) {
+ CFX_ArrayTemplate<TYPE>::RemoveAt(nIndex);
+ }
+ }
+};
+class CLine {
+ public:
+ CLine();
+ virtual ~CLine();
+ CPVT_WordPlace GetBeginWordPlace() const;
+ CPVT_WordPlace GetEndWordPlace() const;
+ CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
+ CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
+ CPVT_WordPlace LinePlace;
+ CPVT_LineInfo m_LineInfo;
+};
+class CLines {
+ public:
+ CLines() : m_nTotal(0) {}
+ virtual ~CLines() { RemoveAll(); }
+ int32_t GetSize() const { return m_Lines.GetSize(); }
+ CLine* GetAt(int32_t nIndex) const { return m_Lines.GetAt(nIndex); }
+ void Empty() { m_nTotal = 0; }
+ void RemoveAll() {
+ for (int32_t i = 0, sz = GetSize(); i < sz; i++) {
+ delete GetAt(i);
+ }
+ m_Lines.RemoveAll();
+ m_nTotal = 0;
+ }
+ int32_t Add(const CPVT_LineInfo& lineinfo) {
+ if (m_nTotal >= GetSize()) {
+ CLine* pLine = new CLine;
+ pLine->m_LineInfo = lineinfo;
+ m_Lines.Add(pLine);
+ } else if (CLine* pLine = GetAt(m_nTotal)) {
+ pLine->m_LineInfo = lineinfo;
+ }
+ return m_nTotal++;
+ }
+ void Clear() {
+ for (int32_t i = GetSize() - 1; i >= m_nTotal; i--) {
+ delete GetAt(i);
+ m_Lines.RemoveAt(i);
+ }
+ }
+
+ private:
+ CPVT_ArrayTemplate<CLine*> m_Lines;
+ int32_t m_nTotal;
+};
+class CSection {
+ friend class CTypeset;
+
+ public:
+ explicit CSection(CPDF_VariableText* pVT);
+ virtual ~CSection();
+ void ResetAll();
+ void ResetLineArray();
+ void ResetWordArray();
+ void ResetLinePlace();
+ CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
+ const CPVT_WordInfo& wordinfo);
+ CPVT_WordPlace AddLine(const CPVT_LineInfo& lineinfo);
+ void ClearWords(const CPVT_WordRange& PlaceRange);
+ void ClearWord(const CPVT_WordPlace& place);
+ CPVT_FloatRect Rearrange();
+ CPVT_Size GetSectionSize(FX_FLOAT fFontSize);
+ CPVT_WordPlace GetBeginWordPlace() const;
+ CPVT_WordPlace GetEndWordPlace() const;
+ CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const;
+ CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const;
+ void UpdateWordPlace(CPVT_WordPlace& place) const;
+ CPVT_WordPlace SearchWordPlace(const CFX_FloatPoint& point) const;
+ CPVT_WordPlace SearchWordPlace(FX_FLOAT fx,
+ const CPVT_WordPlace& lineplace) const;
+ CPVT_WordPlace SearchWordPlace(FX_FLOAT fx,
+ const CPVT_WordRange& range) const;
+
+ public:
+ CPVT_WordPlace SecPlace;
+ CPVT_SectionInfo m_SecInfo;
+ CLines m_LineArray;
+ CPVT_ArrayTemplate<CPVT_WordInfo*> m_WordArray;
+
+ private:
+ void ClearLeftWords(int32_t nWordIndex);
+ void ClearRightWords(int32_t nWordIndex);
+ void ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex);
+
+ CPDF_VariableText* m_pVT;
+};
+class CTypeset {
+ public:
+ explicit CTypeset(CSection* pSection);
+ virtual ~CTypeset();
+ CPVT_Size GetEditSize(FX_FLOAT fFontSize);
+ CPVT_FloatRect Typeset();
+ CPVT_FloatRect CharArray();
+
+ private:
+ void SplitLines(FX_BOOL bTypeset, FX_FLOAT fFontSize);
+ void OutputLines();
+
+ CPVT_FloatRect m_rcRet;
+ CPDF_VariableText* m_pVT;
+ CSection* m_pSection;
+};
+class CPDF_EditContainer {
+ public:
+ CPDF_EditContainer() : m_rcPlate(0, 0, 0, 0), m_rcContent(0, 0, 0, 0) {}
+ virtual ~CPDF_EditContainer() {}
+ virtual void SetPlateRect(const CFX_FloatRect& rect) { m_rcPlate = rect; }
+ virtual const CFX_FloatRect& GetPlateRect() const { return m_rcPlate; }
+ virtual void SetContentRect(const CPVT_FloatRect& rect) {
+ m_rcContent = rect;
+ }
+ virtual CFX_FloatRect GetContentRect() const { return m_rcContent; }
+ FX_FLOAT GetPlateWidth() const { return m_rcPlate.right - m_rcPlate.left; }
+ FX_FLOAT GetPlateHeight() const { return m_rcPlate.top - m_rcPlate.bottom; }
+ CPVT_Size GetPlateSize() const {
+ return CPVT_Size(GetPlateWidth(), GetPlateHeight());
+ }
+ CFX_FloatPoint GetBTPoint() const {
+ return CFX_FloatPoint(m_rcPlate.left, m_rcPlate.top);
+ }
+ CFX_FloatPoint GetETPoint() const {
+ return CFX_FloatPoint(m_rcPlate.right, m_rcPlate.bottom);
+ }
+ inline CFX_FloatPoint InToOut(const CFX_FloatPoint& point) const {
+ return CFX_FloatPoint(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
+ }
+ inline CFX_FloatPoint OutToIn(const CFX_FloatPoint& point) const {
+ return CFX_FloatPoint(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
+ }
+ inline CFX_FloatRect InToOut(const CPVT_FloatRect& rect) const {
+ CFX_FloatPoint ptLeftTop = InToOut(CFX_FloatPoint(rect.left, rect.top));
+ CFX_FloatPoint ptRightBottom =
+ InToOut(CFX_FloatPoint(rect.right, rect.bottom));
+ return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
+ ptLeftTop.y);
+ }
+ inline CPVT_FloatRect OutToIn(const CFX_FloatRect& rect) const {
+ CFX_FloatPoint ptLeftTop = OutToIn(CFX_FloatPoint(rect.left, rect.top));
+ CFX_FloatPoint ptRightBottom =
+ OutToIn(CFX_FloatPoint(rect.right, rect.bottom));
+ return CPVT_FloatRect(ptLeftTop.x, ptLeftTop.y, ptRightBottom.x,
+ ptRightBottom.y);
+ }
+
+ private:
+ CFX_FloatRect m_rcPlate;
+ CPVT_FloatRect m_rcContent;
+};
+
+class CPDF_VariableText : public IPDF_VariableText, private CPDF_EditContainer {
+ friend class CTypeset;
+ friend class CSection;
+ friend class CPDF_VariableText_Iterator;
+
+ public:
+ CPDF_VariableText();
+ ~CPDF_VariableText() override;
+
+ // IPDF_VariableText
+ IPDF_VariableText_Provider* SetProvider(
+ IPDF_VariableText_Provider* pProvider) override;
+ IPDF_VariableText_Iterator* GetIterator() override;
+ void SetPlateRect(const CFX_FloatRect& rect) override {
+ CPDF_EditContainer::SetPlateRect(rect);
+ }
+ void SetAlignment(int32_t nFormat = 0) override { m_nAlignment = nFormat; }
+ void SetPasswordChar(FX_WORD wSubWord = '*') override {
+ m_wSubWord = wSubWord;
+ }
+ void SetLimitChar(int32_t nLimitChar = 0) override {
+ m_nLimitChar = nLimitChar;
+ }
+ void SetCharSpace(FX_FLOAT fCharSpace = 0.0f) override {
+ m_fCharSpace = fCharSpace;
+ }
+ void SetHorzScale(int32_t nHorzScale = 100) override {
+ m_nHorzScale = nHorzScale;
+ }
+ void SetMultiLine(FX_BOOL bMultiLine = TRUE) override {
+ m_bMultiLine = bMultiLine;
+ }
+ void SetAutoReturn(FX_BOOL bAuto = TRUE) override { m_bLimitWidth = bAuto; }
+ void SetFontSize(FX_FLOAT fFontSize) override { m_fFontSize = fFontSize; }
+ void SetCharArray(int32_t nCharArray = 0) override {
+ m_nCharArray = nCharArray;
+ }
+ void SetAutoFontSize(FX_BOOL bAuto = TRUE) override {
+ m_bAutoFontSize = bAuto;
+ }
+ void SetRichText(FX_BOOL bRichText) override { m_bRichText = bRichText; }
+ void SetLineLeading(FX_FLOAT fLineLeading) override {
+ m_fLineLeading = fLineLeading;
+ }
+ void Initialize() override;
+ FX_BOOL IsValid() const override { return m_bInitial; }
+ FX_BOOL IsRichText() const override { return m_bRichText; }
+ void RearrangeAll() override;
+ void RearrangePart(const CPVT_WordRange& PlaceRange) override;
+ void ResetAll() override;
+ void SetText(const FX_WCHAR* text,
+ int32_t charset = 1,
+ const CPVT_SecProps* pSecProps = NULL,
+ const CPVT_WordProps* pWordProps = NULL) override;
+ CPVT_WordPlace InsertWord(const CPVT_WordPlace& place,
+ FX_WORD word,
+ int32_t charset = 1,
+ const CPVT_WordProps* pWordProps = NULL) override;
+ CPVT_WordPlace InsertSection(
+ const CPVT_WordPlace& place,
+ const CPVT_SecProps* pSecProps = NULL,
+ const CPVT_WordProps* pWordProps = NULL) override;
+ CPVT_WordPlace InsertText(const CPVT_WordPlace& place,
+ const FX_WCHAR* text,
+ int32_t charset = 1,
+ const CPVT_SecProps* pSecProps = NULL,
+ const CPVT_WordProps* pWordProps = NULL) override;
+ CPVT_WordPlace DeleteWords(const CPVT_WordRange& PlaceRange) override;
+ CPVT_WordPlace DeleteWord(const CPVT_WordPlace& place) override;
+ CPVT_WordPlace BackSpaceWord(const CPVT_WordPlace& place) override;
+ const CFX_FloatRect& GetPlateRect() const override {
+ return CPDF_EditContainer::GetPlateRect();
+ }
+ CFX_FloatRect GetContentRect() const override;
+ int32_t GetTotalWords() const override;
+ FX_FLOAT GetFontSize() const override { return m_fFontSize; }
+ int32_t GetAlignment() const override { return m_nAlignment; }
+ FX_WORD GetPasswordChar() const override { return GetSubWord(); }
+ int32_t GetCharArray() const override { return m_nCharArray; }
+ int32_t GetLimitChar() const override { return m_nLimitChar; }
+ FX_BOOL IsMultiLine() const override { return m_bMultiLine; }
+ int32_t GetHorzScale() const override { return m_nHorzScale; }
+ FX_FLOAT GetCharSpace() const override { return m_fCharSpace; }
+ CPVT_WordPlace GetBeginWordPlace() const override;
+ CPVT_WordPlace GetEndWordPlace() const override;
+ CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const override;
+ CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const override;
+ CPVT_WordPlace SearchWordPlace(const CFX_FloatPoint& point) const override;
+ CPVT_WordPlace GetUpWordPlace(const CPVT_WordPlace& place,
+ const CFX_FloatPoint& point) const override;
+ CPVT_WordPlace GetDownWordPlace(const CPVT_WordPlace& place,
+ const CFX_FloatPoint& point) const override;
+ CPVT_WordPlace GetLineBeginPlace(const CPVT_WordPlace& place) const override;
+ CPVT_WordPlace GetLineEndPlace(const CPVT_WordPlace& place) const override;
+ CPVT_WordPlace GetSectionBeginPlace(
+ const CPVT_WordPlace& place) const override;
+ CPVT_WordPlace GetSectionEndPlace(const CPVT_WordPlace& place) const override;
+ void UpdateWordPlace(CPVT_WordPlace& place) const override;
+ CPVT_WordPlace AdjustLineHeader(const CPVT_WordPlace& place,
+ FX_BOOL bPrevOrNext) const override;
+ int32_t WordPlaceToWordIndex(const CPVT_WordPlace& place) const override;
+ CPVT_WordPlace WordIndexToWordPlace(int32_t index) const override;
+
+ FX_WORD GetSubWord() const { return m_wSubWord; }
+
+ private:
+ int32_t GetCharWidth(int32_t nFontIndex,
+ FX_WORD Word,
+ FX_WORD SubWord,
+ int32_t nWordStyle);
+ int32_t GetTypeAscent(int32_t nFontIndex);
+ int32_t GetTypeDescent(int32_t nFontIndex);
+ int32_t GetWordFontIndex(FX_WORD word, int32_t charset, int32_t nFontIndex);
+ int32_t GetDefaultFontIndex();
+ FX_BOOL IsLatinWord(FX_WORD word);
+
+ CPVT_WordPlace AddSection(const CPVT_WordPlace& place,
+ const CPVT_SectionInfo& secinfo);
+ CPVT_WordPlace AddLine(const CPVT_WordPlace& place,
+ const CPVT_LineInfo& lineinfo);
+ CPVT_WordPlace AddWord(const CPVT_WordPlace& place,
+ const CPVT_WordInfo& wordinfo);
+ FX_BOOL GetWordInfo(const CPVT_WordPlace& place, CPVT_WordInfo& wordinfo);
+ FX_BOOL SetWordInfo(const CPVT_WordPlace& place,
+ const CPVT_WordInfo& wordinfo);
+ FX_BOOL GetLineInfo(const CPVT_WordPlace& place, CPVT_LineInfo& lineinfo);
+ FX_BOOL GetSectionInfo(const CPVT_WordPlace& place,
+ CPVT_SectionInfo& secinfo);
+ FX_FLOAT GetWordFontSize(const CPVT_WordInfo& WordInfo,
+ FX_BOOL bFactFontSize = FALSE);
+ FX_FLOAT GetWordWidth(int32_t nFontIndex,
+ FX_WORD Word,
+ FX_WORD SubWord,
+ FX_FLOAT fCharSpace,
+ int32_t nHorzScale,
+ FX_FLOAT fFontSize,
+ FX_FLOAT fWordTail,
+ int32_t nWordStyle);
+ FX_FLOAT GetWordWidth(const CPVT_WordInfo& WordInfo);
+ FX_FLOAT GetWordAscent(const CPVT_WordInfo& WordInfo, FX_FLOAT fFontSize);
+ FX_FLOAT GetWordDescent(const CPVT_WordInfo& WordInfo, FX_FLOAT fFontSize);
+ FX_FLOAT GetWordAscent(const CPVT_WordInfo& WordInfo,
+ FX_BOOL bFactFontSize = FALSE);
+ FX_FLOAT GetWordDescent(const CPVT_WordInfo& WordInfo,
+ FX_BOOL bFactFontSize = FALSE);
+ FX_FLOAT GetLineAscent(const CPVT_SectionInfo& SecInfo);
+ FX_FLOAT GetLineDescent(const CPVT_SectionInfo& SecInfo);
+ FX_FLOAT GetFontAscent(int32_t nFontIndex, FX_FLOAT fFontSize);
+ FX_FLOAT GetFontDescent(int32_t nFontIndex, FX_FLOAT fFontSize);
+ int32_t GetWordFontIndex(const CPVT_WordInfo& WordInfo);
+ FX_FLOAT GetCharSpace(const CPVT_WordInfo& WordInfo);
+ int32_t GetHorzScale(const CPVT_WordInfo& WordInfo);
+ FX_FLOAT GetLineLeading(const CPVT_SectionInfo& SecInfo);
+ FX_FLOAT GetLineIndent(const CPVT_SectionInfo& SecInfo);
+ int32_t GetAlignment(const CPVT_SectionInfo& SecInfo);
+
+ void ClearSectionRightWords(const CPVT_WordPlace& place);
+
+ FX_BOOL ClearEmptySection(const CPVT_WordPlace& place);
+ void ClearEmptySections(const CPVT_WordRange& PlaceRange);
+ void LinkLatterSection(const CPVT_WordPlace& place);
+ void ClearWords(const CPVT_WordRange& PlaceRange);
+ CPVT_WordPlace ClearLeftWord(const CPVT_WordPlace& place);
+ CPVT_WordPlace ClearRightWord(const CPVT_WordPlace& place);
+
+ private:
+ CPVT_FloatRect Rearrange(const CPVT_WordRange& PlaceRange);
+ FX_FLOAT GetAutoFontSize();
+ FX_BOOL IsBigger(FX_FLOAT fFontSize);
+ CPVT_FloatRect RearrangeSections(const CPVT_WordRange& PlaceRange);
+
+ void ResetSectionArray();
+
+ CPVT_ArrayTemplate<CSection*> m_SectionArray;
+ int32_t m_nLimitChar;
+ int32_t m_nCharArray;
+ FX_BOOL m_bMultiLine;
+ FX_BOOL m_bLimitWidth;
+ FX_BOOL m_bAutoFontSize;
+ int32_t m_nAlignment;
+ FX_FLOAT m_fLineLeading;
+ FX_FLOAT m_fCharSpace;
+ int32_t m_nHorzScale;
+ FX_WORD m_wSubWord;
+ FX_FLOAT m_fFontSize;
+
+ private:
+ FX_BOOL m_bInitial;
+ FX_BOOL m_bRichText;
+ IPDF_VariableText_Provider* m_pVTProvider;
+ CPDF_VariableText_Iterator* m_pVTIterator;
+};
+
+class CPDF_VariableText_Iterator : public IPDF_VariableText_Iterator {
+ public:
+ explicit CPDF_VariableText_Iterator(CPDF_VariableText* pVT);
+ ~CPDF_VariableText_Iterator() override;
+
+ // IPDF_VariableText_Iterator
+ FX_BOOL NextWord() override;
+ FX_BOOL PrevWord() override;
+ FX_BOOL NextLine() override;
+ FX_BOOL PrevLine() override;
+ FX_BOOL NextSection() override;
+ FX_BOOL PrevSection() override;
+ FX_BOOL SetWord(const CPVT_Word& word) override;
+ FX_BOOL GetWord(CPVT_Word& word) const override;
+ FX_BOOL GetLine(CPVT_Line& line) const override;
+ FX_BOOL GetSection(CPVT_Section& section) const override;
+ FX_BOOL SetSection(const CPVT_Section& section) override;
+ void SetAt(int32_t nWordIndex) override;
+ void SetAt(const CPVT_WordPlace& place) override;
+ const CPVT_WordPlace& GetAt() const override { return m_CurPos; }
+
+ private:
+ CPVT_WordPlace m_CurPos;
+ CPDF_VariableText* m_pVT;
+};
+
+#endif // CORE_FPDFDOC_PDF_VT_H_
diff --git a/core/fpdfdoc/tagged_int.h b/core/fpdfdoc/tagged_int.h
new file mode 100644
index 0000000000..e930f613b1
--- /dev/null
+++ b/core/fpdfdoc/tagged_int.h
@@ -0,0 +1,106 @@
+// 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
+
+#ifndef CORE_FPDFDOC_TAGGED_INT_H_
+#define CORE_FPDFDOC_TAGGED_INT_H_
+
+#include <map>
+
+#include "core/include/fpdfdoc/fpdf_tagged.h"
+
+class CPDF_StructElementImpl;
+
+class CPDF_StructTreeImpl : public CPDF_StructTree {
+ public:
+ explicit CPDF_StructTreeImpl(const CPDF_Document* pDoc);
+ ~CPDF_StructTreeImpl() override;
+
+ // CPDF_StructTree
+ int CountTopElements() const override { return m_Kids.GetSize(); }
+ CPDF_StructElement* GetTopElement(int i) const override {
+ return (CPDF_StructElement*)m_Kids.GetAt(i);
+ }
+
+ void LoadDocTree();
+ void LoadPageTree(const CPDF_Dictionary* pPageDict);
+ CPDF_StructElementImpl* AddPageNode(
+ CPDF_Dictionary* pElement,
+ std::map<CPDF_Dictionary*, CPDF_StructElementImpl*>& map,
+ int nLevel = 0);
+ FX_BOOL AddTopLevelNode(CPDF_Dictionary* pDict,
+ CPDF_StructElementImpl* pElement);
+
+ protected:
+ const CPDF_Dictionary* m_pTreeRoot;
+ const CPDF_Dictionary* m_pRoleMap;
+ const CPDF_Dictionary* m_pPage;
+ CFX_ArrayTemplate<CPDF_StructElementImpl*> m_Kids;
+ friend class CPDF_StructElementImpl;
+};
+
+class CPDF_StructElementImpl final : public CPDF_StructElement {
+ public:
+ CPDF_StructElementImpl(CPDF_StructTreeImpl* pTree,
+ CPDF_StructElementImpl* pParent,
+ CPDF_Dictionary* pDict);
+
+ // CPDF_StructTreeImpl
+ CPDF_StructTree* GetTree() const override { return m_pTree; }
+ const CFX_ByteString& GetType() const override { return m_Type; }
+ CPDF_StructElement* GetParent() const override { return m_pParent; }
+ CPDF_Dictionary* GetDict() const override { return m_pDict; }
+ int CountKids() const override { return m_Kids.GetSize(); }
+ const CPDF_StructKid& GetKid(int index) const override {
+ return m_Kids.GetData()[index];
+ }
+ CPDF_Object* GetAttr(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_BOOL bInheritable = FALSE,
+ FX_FLOAT fLevel = 0.0F) override;
+ CFX_ByteString GetName(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ const CFX_ByteStringC& default_value,
+ FX_BOOL bInheritable = FALSE,
+ int subindex = -1) override;
+ FX_ARGB GetColor(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_ARGB default_value,
+ FX_BOOL bInheritable = FALSE,
+ int subindex = -1) override;
+ FX_FLOAT GetNumber(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_FLOAT default_value,
+ FX_BOOL bInheritable = FALSE,
+ int subindex = -1) override;
+ int GetInteger(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ int default_value,
+ FX_BOOL bInheritable = FALSE,
+ int subindex = -1) override;
+
+ void LoadKids(CPDF_Dictionary* pDict);
+ void LoadKid(FX_DWORD PageObjNum, CPDF_Object* pObj, CPDF_StructKid* pKid);
+ CPDF_Object* GetAttr(const CFX_ByteStringC& owner,
+ const CFX_ByteStringC& name,
+ FX_BOOL bInheritable,
+ int subindex);
+ CPDF_StructElementImpl* Retain();
+ void Release();
+
+ protected:
+ ~CPDF_StructElementImpl() override;
+
+ CPDF_StructTreeImpl* m_pTree;
+ CFX_ByteString m_Type;
+ CPDF_StructElementImpl* m_pParent;
+ CPDF_Dictionary* m_pDict;
+ CFX_ArrayTemplate<CPDF_StructKid> m_Kids;
+ int m_RefCount;
+
+ friend class CPDF_StructTreeImpl;
+};
+
+#endif // CORE_FPDFDOC_TAGGED_INT_H_