| // 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. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "core/fpdfdoc/cpvt_generateap.h" |
| |
| #include "core/fpdfapi/fpdf_font/include/cpdf_font.h" |
| #include "core/fpdfapi/fpdf_parser/include/cpdf_dictionary.h" |
| #include "core/fpdfapi/fpdf_parser/include/cpdf_document.h" |
| #include "core/fpdfapi/fpdf_parser/include/cpdf_simple_parser.h" |
| #include "core/fpdfapi/fpdf_parser/include/cpdf_stream.h" |
| #include "core/fpdfapi/fpdf_parser/include/fpdf_parser_decode.h" |
| #include "core/fpdfdoc/cpvt_color.h" |
| #include "core/fpdfdoc/cpvt_fontmap.h" |
| #include "core/fpdfdoc/include/cpdf_formfield.h" |
| #include "core/fpdfdoc/include/cpvt_word.h" |
| |
| namespace { |
| |
| bool GenerateWidgetAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict, |
| const int32_t& nWidgetType) { |
| CPDF_Dictionary* pFormDict = nullptr; |
| 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.AsStringC()); |
| 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 = CPVT_Color::ParseColor(DA); |
| FX_BOOL bUseFormRes = FALSE; |
| CPDF_Dictionary* pFontDict = nullptr; |
| CPDF_Dictionary* pDRDict = pAnnotDict->GetDictBy("DR"); |
| if (!pDRDict) { |
| pDRDict = pFormDict->GetDictBy("DR"); |
| bUseFormRes = TRUE; |
| } |
| CPDF_Dictionary* pDRFontDict = pDRDict ? pDRDict->GetDictBy("Font") : nullptr; |
| if (pDRFontDict) { |
| 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; |
| } |
| |
| BorderStyle nBorderStyle = BorderStyle::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 = BorderStyle::SOLID; |
| break; |
| case 'D': |
| nBorderStyle = BorderStyle::DASH; |
| break; |
| case 'B': |
| nBorderStyle = BorderStyle::BEVELED; |
| fBorderWidth *= 2; |
| crLeftTop = CPVT_Color(CPVT_Color::kGray, 1); |
| crRightBottom = CPVT_Color(CPVT_Color::kGray, 0.5); |
| break; |
| case 'I': |
| nBorderStyle = BorderStyle::INSET; |
| fBorderWidth *= 2; |
| crLeftTop = CPVT_Color(CPVT_Color::kGray, 0.5); |
| crRightBottom = CPVT_Color(CPVT_Color::kGray, 0.75); |
| break; |
| case 'U': |
| nBorderStyle = BorderStyle::UNDERLINE; |
| break; |
| } |
| } |
| CPVT_Color crBorder, crBG; |
| if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictBy("MK")) { |
| if (CPDF_Array* pArray = pMKDict->GetArrayBy("BC")) |
| crBorder = CPVT_Color::ParseColor(*pArray); |
| if (CPDF_Array* pArray = pMKDict->GetArrayBy("BG")) |
| crBG = CPVT_Color::ParseColor(*pArray); |
| } |
| CFX_ByteTextBuf sAppStream; |
| CFX_ByteString sBG = |
| CPVT_GenerateAP::GenerateColorAP(crBG, PaintOperation::FILL); |
| 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; |
| uint32_t dwFlags = FPDF_GetFieldAttr(pAnnotDict, "Ff") |
| ? FPDF_GetFieldAttr(pAnnotDict, "Ff")->GetInteger() |
| : 0; |
| uint32_t dwMaxLen = |
| FPDF_GetFieldAttr(pAnnotDict, "MaxLen") |
| ? FPDF_GetFieldAttr(pAnnotDict, "MaxLen")->GetInteger() |
| : 0; |
| CPVT_FontMap map( |
| pDoc, pStreamDict ? pStreamDict->GetDictBy("Resources") : nullptr, |
| pDefFont, sFontName.Right(sFontName.GetLength() - 1)); |
| CPDF_VariableText::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); |
| } |
| uint16_t 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, |
| PaintOperation::FILL) |
| << 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") : nullptr, |
| pDefFont, sFontName.Right(sFontName.GetLength() - 1)); |
| CPDF_VariableText::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, |
| PaintOperation::FILL) |
| << 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), |
| PaintOperation::FILL); |
| 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), BorderStyle::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") : nullptr, |
| pDefFont, sFontName.Right(sFontName.GetLength() - 1)); |
| CPDF_VariableText::Provider prd(&map); |
| CPDF_Array* pOpts = ToArray(FPDF_GetFieldAttr(pAnnotDict, "Opt")); |
| CPDF_Array* pSels = ToArray(FPDF_GetFieldAttr(pAnnotDict, "I")); |
| CPDF_Object* pTi = FPDF_GetFieldAttr(pAnnotDict, "TI"); |
| int32_t nTop = pTi ? pTi->GetInteger() : 0; |
| CFX_ByteTextBuf sBody; |
| if (pOpts) { |
| FX_FLOAT fy = rcBody.top; |
| for (size_t i = nTop, sz = pOpts->GetCount(); i < sz; i++) { |
| if (IsFloatSmaller(fy, rcBody.bottom)) |
| break; |
| |
| if (CPDF_Object* pOpt = pOpts->GetDirectObjectAt(i)) { |
| CFX_WideString swItem; |
| if (pOpt->IsString()) |
| swItem = pOpt->GetUnicodeText(); |
| else if (CPDF_Array* pArray = pOpt->AsArray()) |
| swItem = pArray->GetDirectObjectAt(1)->GetUnicodeText(); |
| |
| FX_BOOL bSelected = FALSE; |
| if (pSels) { |
| for (size_t s = 0, ssz = pSels->GetCount(); s < ssz; s++) { |
| int value = pSels->GetIntegerAt(s); |
| if (value >= 0 && i == static_cast<size_t>(value)) { |
| bSelected = TRUE; |
| break; |
| } |
| } |
| } |
| CPDF_VariableText vt; |
| vt.SetProvider(&prd); |
| vt.SetPlateRect( |
| CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f)); |
| vt.SetFontSize(IsFloatZero(fFontSize) ? 12.0f : 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), |
| PaintOperation::FILL) |
| << 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), |
| PaintOperation::FILL) |
| << CPVT_GenerateAP::GenerateEditAP(&map, vt.GetIterator(), |
| CFX_FloatPoint(0.0f, fy), |
| TRUE, 0) |
| << "ET\n"; |
| } else { |
| sBody << "BT\n" |
| << CPVT_GenerateAP::GenerateColorAP(crText, |
| PaintOperation::FILL) |
| << CPVT_GenerateAP::GenerateEditAP(&map, vt.GetIterator(), |
| CFX_FloatPoint(0.0f, fy), |
| TRUE, 0) |
| << "ET\n"; |
| } |
| fy -= fItemHeight; |
| } |
| } |
| } |
| if (sBody.GetSize() > 0) { |
| sAppStream << "/Tx BMC\nq\n" |
| << rcBody.left << " " << rcBody.bottom << " " |
| << rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n" |
| << sBody.AsStringC() << "Q\nEMC\n"; |
| } |
| } break; |
| } |
| if (pNormalStream) { |
| pNormalStream->SetData(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; |
| } |
| |
| CFX_ByteString GetColorStringWithDefault(CPDF_Dictionary* pAnnotDict, |
| const CPVT_Color& crDefaultColor, |
| PaintOperation nOperation) { |
| if (CPDF_Array* pColor = pAnnotDict->GetArrayBy("C")) { |
| CPVT_Color color = CPVT_Color::ParseColor(*pColor); |
| return CPVT_GenerateAP::GenerateColorAP(color, nOperation); |
| } |
| |
| return CPVT_GenerateAP::GenerateColorAP(crDefaultColor, nOperation); |
| } |
| |
| CPDF_Dictionary* GenerateExtGStateDict(const CPDF_Dictionary& pAnnotDict, |
| const CFX_ByteString& sExtGSDictName, |
| const CFX_ByteString& sBlendMode) { |
| CPDF_Dictionary* pGSDict = new CPDF_Dictionary; |
| pGSDict->SetAtString("Type", "ExtGState"); |
| |
| FX_FLOAT fOpacity = |
| pAnnotDict.KeyExist("CA") ? pAnnotDict.GetNumberBy("CA") : 1; |
| pGSDict->SetAtNumber("CA", fOpacity); |
| pGSDict->SetAtNumber("ca", fOpacity); |
| pGSDict->SetAtBoolean("AIS", false); |
| pGSDict->SetAtString("BM", sBlendMode); |
| |
| CPDF_Dictionary* pExtGStateDict = new CPDF_Dictionary; |
| pExtGStateDict->SetAt(sExtGSDictName, pGSDict); |
| |
| return pExtGStateDict; |
| } |
| |
| // Takes ownership of |pExtGStateDict|. |
| void GenerateAndSetAPDict(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict, |
| const CFX_ByteTextBuf& sAppStream, |
| CPDF_Dictionary* pExtGStateDict) { |
| CPDF_Dictionary* pAPDict = new CPDF_Dictionary; |
| pAnnotDict->SetAt("AP", pAPDict); |
| |
| CPDF_Stream* pNormalStream = new CPDF_Stream(nullptr, 0, nullptr); |
| int32_t objnum = pDoc->AddIndirectObject(pNormalStream); |
| pAnnotDict->GetDictBy("AP")->SetAtReference("N", pDoc, objnum); |
| |
| pNormalStream->SetData(sAppStream.GetBuffer(), sAppStream.GetSize(), FALSE, |
| FALSE); |
| |
| CPDF_Dictionary* pStreamDict = pNormalStream->GetDict(); |
| pStreamDict->SetAtInteger("FormType", 1); |
| pStreamDict->SetAtString("Subtype", "Form"); |
| pStreamDict->SetAtMatrix("Matrix", CFX_Matrix()); |
| |
| CFX_FloatRect rect = pAnnotDict->GetRectBy("Rect"); |
| pStreamDict->SetAtRect("BBox", rect); |
| |
| CPDF_Dictionary* pResourceDict = new CPDF_Dictionary; |
| pResourceDict->SetAt("ExtGState", pExtGStateDict); |
| |
| pStreamDict->SetAt("Resources", pResourceDict); |
| } |
| |
| } // namespace |
| |
| bool FPDF_GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { |
| if (!pAnnotDict || pAnnotDict->GetStringBy("Subtype") != "Widget") |
| return false; |
| |
| CFX_ByteString field_type = FPDF_GetFieldAttr(pAnnotDict, "FT")->GetString(); |
| uint32_t 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; |
| } |
| |
| // Static. |
| bool CPVT_GenerateAP::GenerateComboBoxAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| return GenerateWidgetAP(pDoc, pAnnotDict, 1); |
| } |
| |
| // Static. |
| bool CPVT_GenerateAP::GenerateListBoxAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| return GenerateWidgetAP(pDoc, pAnnotDict, 2); |
| } |
| |
| // Static. |
| bool CPVT_GenerateAP::GenerateTextFieldAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| return GenerateWidgetAP(pDoc, pAnnotDict, 0); |
| } |
| |
| bool CPVT_GenerateAP::GenerateHighlightAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| // If AP dictionary exists, we use the appearance defined in the |
| // existing AP dictionary. |
| if (pAnnotDict->KeyExist("AP")) |
| return false; |
| |
| CFX_ByteTextBuf sAppStream; |
| CFX_ByteString sExtGSDictName = "GS"; |
| sAppStream << "/" << sExtGSDictName << " gs "; |
| |
| sAppStream << GetColorStringWithDefault( |
| pAnnotDict, CPVT_Color(CPVT_Color::kRGB, 1, 1, 0), PaintOperation::FILL); |
| |
| CFX_FloatRect rect = pAnnotDict->GetRectBy("Rect"); |
| rect.Normalize(); |
| |
| sAppStream << rect.left << " " << rect.top << " m " << rect.right << " " |
| << rect.top << " l " << rect.right << " " << rect.bottom << " l " |
| << rect.left << " " << rect.bottom << " l " |
| << "h f\n"; |
| |
| CPDF_Dictionary* pExtGStateDict = |
| GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Multiply"); |
| GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, pExtGStateDict); |
| |
| return true; |
| } |
| |
| bool CPVT_GenerateAP::GenerateUnderlineAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| // If AP dictionary exists, we use the appearance defined in the |
| // existing AP dictionary. |
| if (pAnnotDict->KeyExist("AP")) |
| return false; |
| |
| CFX_ByteTextBuf sAppStream; |
| CFX_ByteString sExtGSDictName = "GS"; |
| sAppStream << "/" << sExtGSDictName << " gs "; |
| |
| sAppStream << GetColorStringWithDefault(pAnnotDict, |
| CPVT_Color(CPVT_Color::kRGB, 0, 0, 0), |
| PaintOperation::STROKE); |
| |
| CFX_FloatRect rect = pAnnotDict->GetRectBy("Rect"); |
| rect.Normalize(); |
| |
| FX_FLOAT fLineWidth = 1.0; |
| sAppStream << fLineWidth << " w " << rect.left << " " |
| << rect.bottom + fLineWidth << " m " << rect.right << " " |
| << rect.bottom + fLineWidth << " l S\n"; |
| |
| CPDF_Dictionary* pExtGStateDict = |
| GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal"); |
| GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, pExtGStateDict); |
| return true; |
| } |
| |
| bool CPVT_GenerateAP::GenerateSquigglyAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| // If AP dictionary exists, we use the appearance defined in the |
| // existing AP dictionary. |
| if (pAnnotDict->KeyExist("AP")) |
| return false; |
| |
| CFX_ByteTextBuf sAppStream; |
| CFX_ByteString sExtGSDictName = "GS"; |
| sAppStream << "/" << sExtGSDictName << " gs "; |
| |
| sAppStream << GetColorStringWithDefault(pAnnotDict, |
| CPVT_Color(CPVT_Color::kRGB, 0, 0, 0), |
| PaintOperation::STROKE); |
| |
| CFX_FloatRect rect = pAnnotDict->GetRectBy("Rect"); |
| rect.Normalize(); |
| |
| FX_FLOAT fLineWidth = 1.0; |
| sAppStream << fLineWidth << " w "; |
| |
| const FX_FLOAT fDelta = 2.0; |
| const FX_FLOAT fTop = rect.bottom + fDelta; |
| const FX_FLOAT fBottom = rect.bottom; |
| |
| sAppStream << rect.left << " " << fTop << " m "; |
| |
| FX_FLOAT fX = rect.left + fDelta; |
| bool isUpwards = false; |
| |
| while (fX < rect.right) { |
| sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l "; |
| |
| fX += fDelta; |
| isUpwards = !isUpwards; |
| } |
| |
| FX_FLOAT fRemainder = rect.right - (fX - fDelta); |
| if (isUpwards) |
| sAppStream << rect.right << " " << fBottom + fRemainder << " l "; |
| else |
| sAppStream << rect.right << " " << fTop - fRemainder << " l "; |
| |
| sAppStream << "S\n"; |
| |
| CPDF_Dictionary* pExtGStateDict = |
| GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal"); |
| GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, pExtGStateDict); |
| return true; |
| } |
| |
| bool CPVT_GenerateAP::GenerateStrikeOutAP(CPDF_Document* pDoc, |
| CPDF_Dictionary* pAnnotDict) { |
| // If AP dictionary exists, we use the appearance defined in the |
| // existing AP dictionary. |
| if (pAnnotDict->KeyExist("AP")) |
| return false; |
| |
| CFX_ByteTextBuf sAppStream; |
| CFX_ByteString sExtGSDictName = "GS"; |
| sAppStream << "/" << sExtGSDictName << " gs "; |
| |
| sAppStream << GetColorStringWithDefault(pAnnotDict, |
| CPVT_Color(CPVT_Color::kRGB, 0, 0, 0), |
| PaintOperation::STROKE); |
| |
| CFX_FloatRect rect = pAnnotDict->GetRectBy("Rect"); |
| rect.Normalize(); |
| |
| FX_FLOAT fLineWidth = 1.0; |
| FX_FLOAT fY = (rect.top + rect.bottom) / 2; |
| sAppStream << fLineWidth << " w " << rect.left << " " << fY << " m " |
| << rect.right << " " << fY << " l S\n"; |
| |
| CPDF_Dictionary* pExtGStateDict = |
| GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal"); |
| GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, pExtGStateDict); |
| return true; |
| } |
| |
| // Static. |
| CFX_ByteString CPVT_GenerateAP::GenerateEditAP( |
| IPVT_FontMap* pFontMap, |
| CPDF_VariableText::Iterator* pIterator, |
| const CFX_FloatPoint& ptOffset, |
| FX_BOOL bContinuous, |
| uint16_t SubWord) { |
| CFX_ByteTextBuf sEditStream; |
| CFX_ByteTextBuf sLineStream; |
| CFX_ByteTextBuf sWords; |
| CFX_FloatPoint ptOld(0.0f, 0.0f); |
| CFX_FloatPoint ptNew(0.0f, 0.0f); |
| int32_t nCurFontIndex = -1; |
| |
| pIterator->SetAt(0); |
| |
| CPVT_WordPlace oldplace; |
| while (pIterator->NextWord()) { |
| CPVT_WordPlace place = pIterator->GetAt(); |
| if (bContinuous) { |
| if (place.LineCmp(oldplace) != 0) { |
| if (sWords.GetSize() > 0) { |
| sLineStream << GetWordRenderString(sWords.MakeString()); |
| 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.MakeString()); |
| 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.MakeString()); |
| sEditStream << sLineStream; |
| sWords.Clear(); |
| } |
| return sEditStream.MakeString(); |
| } |
| |
| // Static. |
| CFX_ByteString CPVT_GenerateAP::GenerateBorderAP( |
| const CFX_FloatRect& rect, |
| FX_FLOAT fWidth, |
| const CPVT_Color& color, |
| const CPVT_Color& crLeftTop, |
| const CPVT_Color& crRightBottom, |
| BorderStyle 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 BorderStyle::SOLID: |
| sColor = GenerateColorAP(color, PaintOperation::FILL); |
| 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 BorderStyle::DASH: |
| sColor = GenerateColorAP(color, PaintOperation::STROKE); |
| 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 BorderStyle::BEVELED: |
| case BorderStyle::INSET: |
| sColor = GenerateColorAP(crLeftTop, PaintOperation::FILL); |
| 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, PaintOperation::FILL); |
| 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, PaintOperation::FILL); |
| 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 BorderStyle::UNDERLINE: |
| sColor = GenerateColorAP(color, PaintOperation::STROKE); |
| 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.MakeString(); |
| } |
| |
| // Static. |
| CFX_ByteString CPVT_GenerateAP::GenerateColorAP(const CPVT_Color& color, |
| PaintOperation nOperation) { |
| CFX_ByteTextBuf sColorStream; |
| switch (color.nColorType) { |
| case CPVT_Color::kRGB: |
| sColorStream << color.fColor1 << " " << color.fColor2 << " " |
| << color.fColor3 << " " |
| << (nOperation == PaintOperation::STROKE ? "RG" : "rg") |
| << "\n"; |
| break; |
| case CPVT_Color::kGray: |
| sColorStream << color.fColor1 << " " |
| << (nOperation == PaintOperation::STROKE ? "G" : "g") |
| << "\n"; |
| break; |
| case CPVT_Color::kCMYK: |
| sColorStream << color.fColor1 << " " << color.fColor2 << " " |
| << color.fColor3 << " " << color.fColor4 << " " |
| << (nOperation == PaintOperation::STROKE ? "K" : "k") |
| << "\n"; |
| break; |
| case CPVT_Color::kTransparent: |
| break; |
| } |
| return sColorStream.MakeString(); |
| } |
| |
| // Static. |
| CFX_ByteString CPVT_GenerateAP::GetPDFWordString(IPVT_FontMap* pFontMap, |
| int32_t nFontIndex, |
| uint16_t Word, |
| uint16_t 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 { |
| uint32_t dwCharCode = pPDFFont->CharCodeFromUnicode(Word); |
| if (dwCharCode != CPDF_Font::kInvalidCharCode) |
| pPDFFont->AppendChar(sWord, dwCharCode); |
| } |
| } |
| return sWord; |
| } |
| |
| // Static. |
| CFX_ByteString CPVT_GenerateAP::GetWordRenderString( |
| const CFX_ByteString& strWords) { |
| if (strWords.GetLength() > 0) |
| return PDF_EncodeString(strWords) + " Tj\n"; |
| return ""; |
| } |
| |
| // Static. |
| CFX_ByteString CPVT_GenerateAP::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.MakeString(); |
| } |