// 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 <algorithm>
#include <memory>
#include <sstream>
#include <utility>
#include "core/fpdfapi/font/cpdf_font.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_boolean.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "core/fpdfapi/parser/cpdf_simple_parser.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
#include "core/fpdfdoc/cpdf_annot.h"
#include "core/fpdfdoc/cpdf_formfield.h"
#include "core/fpdfdoc/cpvt_fontmap.h"
#include "core/fpdfdoc/cpvt_word.h"
#include "third_party/base/ptr_util.h"
struct CPVT_Dash {
CPVT_Dash(int32_t dash, int32_t gap, int32_t phase)
: nDash(dash), nGap(gap), nPhase(phase) {}
int32_t nDash;
int32_t nGap;
int32_t nPhase;
};
namespace {
ByteString GetPDFWordString(IPVT_FontMap* pFontMap,
int32_t nFontIndex,
uint16_t Word,
uint16_t SubWord) {
if (SubWord > 0)
return ByteString::Format("%c", SubWord);
if (!pFontMap)
return "";
CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex);
if (!pPDFFont)
return "";
if (pPDFFont->GetBaseFont().Compare("Symbol") == 0 ||
pPDFFont->GetBaseFont().Compare("ZapfDingbats") == 0) {
return ByteString::Format("%c", Word);
}
ByteString sWord;
uint32_t dwCharCode = pPDFFont->CharCodeFromUnicode(Word);
if (dwCharCode != CPDF_Font::kInvalidCharCode)
pPDFFont->AppendChar(&sWord, dwCharCode);
return sWord;
}
ByteString GetWordRenderString(const ByteString& strWords) {
if (strWords.GetLength() > 0)
return PDF_EncodeString(strWords, false) + " Tj\n";
return "";
}
ByteString GetFontSetString(IPVT_FontMap* pFontMap,
int32_t nFontIndex,
float fFontSize) {
std::ostringstream sRet;
if (pFontMap) {
ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
if (sFontAlias.GetLength() > 0 && fFontSize > 0)
sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
}
return ByteString(sRet);
}
ByteString GenerateEditAP(IPVT_FontMap* pFontMap,
CPDF_VariableText::Iterator* pIterator,
const CFX_PointF& ptOffset,
bool bContinuous,
uint16_t SubWord) {
std::ostringstream sEditStream;
std::ostringstream sLineStream;
std::ostringstream sWords;
CFX_PointF ptOld;
CFX_PointF ptNew;
int32_t nCurFontIndex = -1;
CPVT_WordPlace oldplace;
pIterator->SetAt(0);
while (pIterator->NextWord()) {
CPVT_WordPlace place = pIterator->GetWordPlace();
if (bContinuous) {
if (place.LineCmp(oldplace) != 0) {
if (sWords.tellp() > 0) {
sLineStream << GetWordRenderString(ByteString(sWords));
sEditStream << sLineStream.str();
sLineStream.str("");
sWords.str("");
}
CPVT_Word word;
if (pIterator->GetWord(word)) {
ptNew = CFX_PointF(word.ptWord.x + ptOffset.x,
word.ptWord.y + ptOffset.y);
} else {
CPVT_Line line;
pIterator->GetLine(line);
ptNew = CFX_PointF(line.ptLine.x + ptOffset.x,
line.ptLine.y + ptOffset.y);
}
if (ptNew != ptOld) {
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.tellp() > 0) {
sLineStream << GetWordRenderString(ByteString(sWords));
sWords.str("");
}
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_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
if (ptNew != ptOld) {
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.tellp() > 0) {
sLineStream << GetWordRenderString(ByteString(sWords));
sEditStream << sLineStream.str();
sWords.str("");
}
return ByteString(sEditStream);
}
ByteString GenerateColorAP(const CFX_Color& color, PaintOperation nOperation) {
std::ostringstream sColorStream;
switch (color.nColorType) {
case CFX_Color::kRGB:
sColorStream << color.fColor1 << " " << color.fColor2 << " "
<< color.fColor3 << " "
<< (nOperation == PaintOperation::STROKE ? "RG" : "rg")
<< "\n";
break;
case CFX_Color::kGray:
sColorStream << color.fColor1 << " "
<< (nOperation == PaintOperation::STROKE ? "G" : "g")
<< "\n";
break;
case CFX_Color::kCMYK:
sColorStream << color.fColor1 << " " << color.fColor2 << " "
<< color.fColor3 << " " << color.fColor4 << " "
<< (nOperation == PaintOperation::STROKE ? "K" : "k")
<< "\n";
break;
case CFX_Color::kTransparent:
break;
}
return ByteString(sColorStream);
}
ByteString GenerateBorderAP(const CFX_FloatRect& rect,
float fWidth,
const CFX_Color& color,
const CFX_Color& crLeftTop,
const CFX_Color& crRightBottom,
BorderStyle nStyle,
const CPVT_Dash& dash) {
std::ostringstream sAppStream;
ByteString sColor;
float fLeft = rect.left;
float fRight = rect.right;
float fTop = rect.top;
float fBottom = rect.bottom;
if (fWidth > 0.0f) {
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 ByteString(sAppStream);
}
ByteString GetColorStringWithDefault(CPDF_Array* pColor,
const CFX_Color& crDefaultColor,
PaintOperation nOperation) {
if (pColor) {
CFX_Color color = CFX_Color::ParseColor(*pColor);
return GenerateColorAP(color, nOperation);
}
return GenerateColorAP(crDefaultColor, nOperation);
}
float GetBorderWidth(const CPDF_Dictionary& pAnnotDict) {
if (CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
if (pBorderStyleDict->KeyExist("W"))
return pBorderStyleDict->GetNumberFor("W");
}
if (CPDF_Array* pBorderArray = pAnnotDict.GetArrayFor("Border")) {
if (pBorderArray->GetCount() > 2)
return pBorderArray->GetNumberAt(2);
}
return 1;
}
CPDF_Array* GetDashArray(const CPDF_Dictionary& pAnnotDict) {
if (CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
if (pBorderStyleDict->GetStringFor("S") == "D")
return pBorderStyleDict->GetArrayFor("D");
}
if (CPDF_Array* pBorderArray = pAnnotDict.GetArrayFor("Border")) {
if (pBorderArray->GetCount() == 4)
return pBorderArray->GetArrayAt(3);
}
return nullptr;
}
ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) {
CPDF_Array* pDashArray = GetDashArray(pAnnotDict);
if (!pDashArray || pDashArray->IsEmpty())
return ByteString();
// Support maximum of ten elements in the dash array.
size_t pDashArrayCount = std::min<size_t>(pDashArray->GetCount(), 10);
std::ostringstream sDashStream;
sDashStream << "[";
for (size_t i = 0; i < pDashArrayCount; ++i)
sDashStream << pDashArray->GetNumberAt(i) << " ";
sDashStream << "] 0 d\n";
return ByteString(sDashStream);
}
ByteString GetPopupContentsString(CPDF_Document* pDoc,
const CPDF_Dictionary& pAnnotDict,
CPDF_Font* pDefFont,
const ByteString& sFontName) {
WideString swValue(pAnnotDict.GetUnicodeTextFor("T"));
swValue += L'\n';
swValue += pAnnotDict.GetUnicodeTextFor("Contents");
CPVT_FontMap map(pDoc, nullptr, pDefFont, sFontName);
CPDF_VariableText::Provider prd(&map);
CPDF_VariableText vt;
vt.SetProvider(&prd);
vt.SetPlateRect(pAnnotDict.GetRectFor("Rect"));
vt.SetFontSize(12);
vt.SetAutoReturn(true);
vt.SetMultiLine(true);
vt.Initialize();
vt.SetText(swValue);
vt.RearrangeAll();
CFX_PointF ptOffset(3.0f, -3.0f);
ByteString sContent =
GenerateEditAP(&map, vt.GetIterator(), ptOffset, false, 0);
if (sContent.IsEmpty())
return ByteString();
std::ostringstream sAppStream;
sAppStream << "BT\n"
<< GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::FILL)
<< sContent << "ET\n"
<< "Q\n";
return ByteString(sAppStream);
}
std::unique_ptr<CPDF_Dictionary> GenerateResourceFontDict(
CPDF_Document* pDoc,
const ByteString& sFontDictName) {
CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
pFontDict->SetNewFor<CPDF_Name>("BaseFont", "Helvetica");
pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
auto pResourceFontDict =
pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
pResourceFontDict->SetNewFor<CPDF_Reference>(sFontDictName, pDoc,
pFontDict->GetObjNum());
return pResourceFontDict;
}
ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) {
if (bIsStrokeRect)
return bIsFillRect ? "b" : "s";
return bIsFillRect ? "f" : "n";
}
ByteString GenerateTextSymbolAP(const CFX_FloatRect& rect) {
std::ostringstream sAppStream;
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 1, 1, 0),
PaintOperation::FILL);
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
const float fBorderWidth = 1;
sAppStream << fBorderWidth << " w\n";
const float fHalfWidth = fBorderWidth / 2;
const float fTipDelta = 4;
CFX_FloatRect outerRect1 = rect;
outerRect1.Deflate(fHalfWidth, fHalfWidth);
outerRect1.bottom += fTipDelta;
CFX_FloatRect outerRect2 = outerRect1;
outerRect2.left += fTipDelta;
outerRect2.right = outerRect2.left + fTipDelta;
outerRect2.top = outerRect2.bottom - fTipDelta;
float outerRect2Middle = (outerRect2.left + outerRect2.right) / 2;
// Draw outer boxes.
sAppStream << outerRect1.left << " " << outerRect1.bottom << " m\n"
<< outerRect1.left << " " << outerRect1.top << " l\n"
<< outerRect1.right << " " << outerRect1.top << " l\n"
<< outerRect1.right << " " << outerRect1.bottom << " l\n"
<< outerRect2.right << " " << outerRect2.bottom << " l\n"
<< outerRect2Middle << " " << outerRect2.top << " l\n"
<< outerRect2.left << " " << outerRect2.bottom << " l\n"
<< outerRect1.left << " " << outerRect1.bottom << " l\n";
// Draw inner lines.
CFX_FloatRect lineRect = outerRect1;
const float fXDelta = 2;
const float fYDelta = (lineRect.top - lineRect.bottom) / 4;
lineRect.left += fXDelta;
lineRect.right -= fXDelta;
for (int i = 0; i < 3; ++i) {
lineRect.top -= fYDelta;
sAppStream << lineRect.left << " " << lineRect.top << " m\n"
<< lineRect.right << " " << lineRect.top << " l\n";
}
sAppStream << "B*\n";
return ByteString(sAppStream);
}
std::unique_ptr<CPDF_Dictionary> GenerateExtGStateDict(
const CPDF_Dictionary& pAnnotDict,
const ByteString& sExtGSDictName,
const ByteString& sBlendMode) {
auto pGSDict =
pdfium::MakeUnique<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
pGSDict->SetNewFor<CPDF_String>("Type", "ExtGState", false);
float fOpacity =
pAnnotDict.KeyExist("CA") ? pAnnotDict.GetNumberFor("CA") : 1;
pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
pGSDict->SetNewFor<CPDF_String>("BM", sBlendMode, false);
auto pExtGStateDict =
pdfium::MakeUnique<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
pExtGStateDict->SetFor(sExtGSDictName, std::move(pGSDict));
return pExtGStateDict;
}
std::unique_ptr<CPDF_Dictionary> GenerateResourceDict(
CPDF_Document* pDoc,
std::unique_ptr<CPDF_Dictionary> pExtGStateDict,
std::unique_ptr<CPDF_Dictionary> pResourceFontDict) {
auto pResourceDict =
pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
if (pExtGStateDict)
pResourceDict->SetFor("ExtGState", std::move(pExtGStateDict));
if (pResourceFontDict)
pResourceDict->SetFor("Font", std::move(pResourceFontDict));
return pResourceDict;
}
void GenerateAndSetAPDict(CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict,
std::ostringstream* psAppStream,
std::unique_ptr<CPDF_Dictionary> pResourceDict,
bool bIsTextMarkupAnnotation) {
CPDF_Stream* pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
pNormalStream->SetData(psAppStream);
CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor("AP");
if (!pAPDict)
pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
pStreamDict->SetNewFor<CPDF_String>("Subtype", "Form", false);
pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
CFX_FloatRect rect = bIsTextMarkupAnnotation
? CPDF_Annot::RectFromQuadPoints(pAnnotDict)
: pAnnotDict->GetRectFor("Rect");
pStreamDict->SetRectFor("BBox", rect);
pStreamDict->SetFor("Resources", std::move(pResourceDict));
}
bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
sAppStream << GetColorStringWithDefault(
pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL);
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
float fBorderWidth = GetBorderWidth(*pAnnotDict);
bool bIsStrokeRect = fBorderWidth > 0;
if (bIsStrokeRect) {
sAppStream << fBorderWidth << " w ";
sAppStream << GetDashPatternString(*pAnnotDict);
}
CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
rect.Normalize();
if (bIsStrokeRect) {
// Deflating rect because stroking a path entails painting all points whose
// perpendicular distance from the path in user space is less than or equal
// to half the line width.
rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
}
const float fMiddleX = (rect.left + rect.right) / 2;
const float fMiddleY = (rect.top + rect.bottom) / 2;
// |fL| is precalculated approximate value of 4 * tan((3.14 / 2) / 4) / 3,
// where |fL| * radius is a good approximation of control points for
// arc with 90 degrees.
const float fL = 0.5523f;
const float fDeltaX = fL * rect.Width() / 2.0;
const float fDeltaY = fL * rect.Height() / 2.0;
// Starting point
sAppStream << fMiddleX << " " << rect.top << " m\n";
// First Bezier Curve
sAppStream << fMiddleX + fDeltaX << " " << rect.top << " " << rect.right
<< " " << fMiddleY + fDeltaY << " " << rect.right << " "
<< fMiddleY << " c\n";
// Second Bezier Curve
sAppStream << rect.right << " " << fMiddleY - fDeltaY << " "
<< fMiddleX + fDeltaX << " " << rect.bottom << " " << fMiddleX
<< " " << rect.bottom << " c\n";
// Third Bezier Curve
sAppStream << fMiddleX - fDeltaX << " " << rect.bottom << " " << rect.left
<< " " << fMiddleY - fDeltaY << " " << rect.left << " " << fMiddleY
<< " c\n";
// Fourth Bezier Curve
sAppStream << rect.left << " " << fMiddleY + fDeltaY << " "
<< fMiddleX - fDeltaX << " " << rect.top << " " << fMiddleX << " "
<< rect.top << " c\n";
bool bIsFillRect = pInteriorColor && !pInteriorColor->IsEmpty();
sAppStream << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
false /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateHighlightAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 1, 1, 0),
PaintOperation::FILL);
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
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";
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Multiply");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
true /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
float fBorderWidth = GetBorderWidth(*pAnnotDict);
bool bIsStroke = fBorderWidth > 0;
if (!bIsStroke)
return false;
CPDF_Array* pInkList = pAnnotDict->GetArrayFor("InkList");
if (!pInkList || pInkList->IsEmpty())
return false;
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
sAppStream << fBorderWidth << " w ";
sAppStream << GetDashPatternString(*pAnnotDict);
// Set inflated rect as a new rect because paths near the border with large
// width should not be clipped to the original rect.
CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
pAnnotDict->SetRectFor("Rect", rect);
for (size_t i = 0; i < pInkList->GetCount(); i++) {
CPDF_Array* pInkCoordList = pInkList->GetArrayAt(i);
if (!pInkCoordList || pInkCoordList->GetCount() < 2)
continue;
sAppStream << pInkCoordList->GetNumberAt(0) << " "
<< pInkCoordList->GetNumberAt(1) << " m ";
for (size_t j = 0; j < pInkCoordList->GetCount() - 1; j += 2) {
sAppStream << pInkCoordList->GetNumberAt(j) << " "
<< pInkCoordList->GetNumberAt(j + 1) << " l ";
}
sAppStream << "S\n";
}
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
false /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateTextAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
const float fNoteLength = 20;
CFX_FloatRect noteRect(rect.left, rect.bottom, rect.left + fNoteLength,
rect.bottom + fNoteLength);
pAnnotDict->SetRectFor("Rect", noteRect);
sAppStream << GenerateTextSymbolAP(noteRect);
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
false /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateUnderlineAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
rect.Normalize();
float fLineWidth = 1.0;
sAppStream << fLineWidth << " w " << rect.left << " "
<< rect.bottom + fLineWidth << " m " << rect.right << " "
<< rect.bottom + fLineWidth << " l S\n";
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
true /*IsTextMarkupAnnotation*/);
return true;
}
bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs\n";
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 1, 1, 0),
PaintOperation::FILL);
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
const float fBorderWidth = 1;
sAppStream << fBorderWidth << " w\n";
CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
rect.Normalize();
rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
<< rect.Height() << " re b\n";
ByteString sFontName = "FONT";
auto pResourceFontDict = GenerateResourceFontDict(pDoc, sFontName);
CPDF_Font* pDefFont = pDoc->LoadFont(pResourceFontDict.get());
if (!pDefFont)
return false;
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict = GenerateResourceDict(pDoc, std::move(pResourceFontDict),
std::move(pExtGStateDict));
sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, pDefFont, sFontName);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
false /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
sAppStream << GetColorStringWithDefault(
pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL);
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
float fBorderWidth = GetBorderWidth(*pAnnotDict);
bool bIsStrokeRect = fBorderWidth > 0;
if (bIsStrokeRect) {
sAppStream << fBorderWidth << " w ";
sAppStream << GetDashPatternString(*pAnnotDict);
}
CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
rect.Normalize();
if (bIsStrokeRect) {
// Deflating rect because stroking a path entails painting all points whose
// perpendicular distance from the path in user space is less than or equal
// to half the line width.
rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
}
bool bIsFillRect = pInteriorColor && (pInteriorColor->GetCount() > 0);
sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
<< rect.Height() << " re "
<< GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
false /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateSquigglyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
rect.Normalize();
float fLineWidth = 1.0;
sAppStream << fLineWidth << " w ";
const float fDelta = 2.0;
const float fTop = rect.bottom + fDelta;
const float fBottom = rect.bottom;
sAppStream << rect.left << " " << fTop << " m ";
float fX = rect.left + fDelta;
bool isUpwards = false;
while (fX < rect.right) {
sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
fX += fDelta;
isUpwards = !isUpwards;
}
float fRemainder = rect.right - (fX - fDelta);
if (isUpwards)
sAppStream << rect.right << " " << fBottom + fRemainder << " l ";
else
sAppStream << rect.right << " " << fTop - fRemainder << " l ";
sAppStream << "S\n";
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
true /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateStrikeOutAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
std::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
CFX_Color(CFX_Color::kRGB, 0, 0, 0),
PaintOperation::STROKE);
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
rect.Normalize();
float fLineWidth = 1.0;
float fY = (rect.top + rect.bottom) / 2;
sAppStream << fLineWidth << " w " << rect.left << " " << fY << " m "
<< rect.right << " " << fY << " l S\n";
auto pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
true /*IsTextMarkupAnnotation*/);
return true;
}
} // namespace
// static
void CPVT_GenerateAP::GenerateFormAP(Type type,
CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict) {
const CPDF_Dictionary* pRootDict = pDoc->GetRoot();
if (!pRootDict)
return;
const CPDF_Dictionary* pFormDict = pRootDict->GetDictFor("AcroForm");
if (!pFormDict)
return;
ByteString DA;
if (CPDF_Object* pDAObj = FPDF_GetFieldAttr(pAnnotDict, "DA"))
DA = pDAObj->GetString();
if (DA.IsEmpty())
DA = pFormDict->GetStringFor("DA");
if (DA.IsEmpty())
return;
CPDF_SimpleParser syntax(DA.AsStringView());
syntax.FindTagParamFromStart("Tf", 2);
ByteString sFontName(syntax.GetWord());
sFontName = PDF_NameDecode(sFontName);
if (sFontName.IsEmpty())
return;
float fFontSize = FX_atof(syntax.GetWord());
CFX_Color crText = CFX_Color::ParseColor(DA);
CPDF_Dictionary* pDRDict = pFormDict->GetDictFor("DR");
if (!pDRDict)
return;
CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font");
if (!pDRFontDict)
return;
CPDF_Dictionary* pFontDict =
pDRFontDict->GetDictFor(sFontName.Right(sFontName.GetLength() - 1));
if (!pFontDict) {
pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
pFontDict->SetNewFor<CPDF_Name>("BaseFont", "Helvetica");
pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
pDRFontDict->SetNewFor<CPDF_Reference>(
sFontName.Right(sFontName.GetLength() - 1), pDoc,
pFontDict->GetObjNum());
}
CPDF_Font* pDefFont = pDoc->LoadFont(pFontDict);
if (!pDefFont)
return;
CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor("Rect");
int32_t nRotate = 0;
if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK"))
nRotate = pMKDict->GetIntegerFor("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;
float fBorderWidth = 1;
CPVT_Dash dsBorder(3, 0, 0);
CFX_Color crLeftTop;
CFX_Color crRightBottom;
if (CPDF_Dictionary* pBSDict = pAnnotDict->GetDictFor("BS")) {
if (pBSDict->KeyExist("W"))
fBorderWidth = pBSDict->GetNumberFor("W");
if (CPDF_Array* pArray = pBSDict->GetArrayFor("D")) {
dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1),
pArray->GetIntegerAt(2));
}
if (pBSDict->GetStringFor("S").GetLength()) {
switch (pBSDict->GetStringFor("S")[0]) {
case 'S':
nBorderStyle = BorderStyle::SOLID;
break;
case 'D':
nBorderStyle = BorderStyle::DASH;
break;
case 'B':
nBorderStyle = BorderStyle::BEVELED;
fBorderWidth *= 2;
crLeftTop = CFX_Color(CFX_Color::kGray, 1);
crRightBottom = CFX_Color(CFX_Color::kGray, 0.5);
break;
case 'I':
nBorderStyle = BorderStyle::INSET;
fBorderWidth *= 2;
crLeftTop = CFX_Color(CFX_Color::kGray, 0.5);
crRightBottom = CFX_Color(CFX_Color::kGray, 0.75);
break;
case 'U':
nBorderStyle = BorderStyle::UNDERLINE;
break;
}
}
}
CFX_Color crBorder;
CFX_Color crBG;
if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK")) {
if (CPDF_Array* pArray = pMKDict->GetArrayFor("BC"))
crBorder = CFX_Color::ParseColor(*pArray);
if (CPDF_Array* pArray = pMKDict->GetArrayFor("BG"))
crBG = CFX_Color::ParseColor(*pArray);
}
std::ostringstream sAppStream;
ByteString sBG = 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";
}
ByteString sBorderStream =
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->GetDictFor("AP");
if (!pAPDict)
pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
CPDF_Stream* pNormalStream = pAPDict->GetStreamFor("N");
if (!pNormalStream) {
pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
}
CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
if (pStreamDict) {
pStreamDict->SetMatrixFor("Matrix", matrix);
pStreamDict->SetRectFor("BBox", rcBBox);
CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
if (pStreamResList) {
CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
if (!pStreamResFontList)
pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
if (!pStreamResFontList->KeyExist(sFontName)) {
pStreamResFontList->SetNewFor<CPDF_Reference>(sFontName, pDoc,
pFontDict->GetObjNum());
}
} else {
pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
pStreamResList = pStreamDict->GetDictFor("Resources");
}
}
switch (type) {
case CPVT_GenerateAP::kTextField: {
WideString swValue =
FPDF_GetFieldAttr(pAnnotDict, "V")
? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
: 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->GetDictFor("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);
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);
}
bool bCharArray = (dwFlags >> 24) & 1;
if (bCharArray)
vt.SetCharArray(dwMaxLen);
else
vt.SetLimitChar(dwMaxLen);
vt.Initialize();
vt.SetText(swValue);
vt.RearrangeAll();
CFX_FloatRect rcContent = vt.GetContentRect();
CFX_PointF ptOffset;
if (!bMultiLine) {
ptOffset =
CFX_PointF(0.0f, (rcContent.Height() - rcBody.Height()) / 2.0f);
}
ByteString sBody = 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"
<< GenerateColorAP(crText, PaintOperation::FILL) << sBody
<< "ET\n"
<< "Q\nEMC\n";
}
break;
}
case CPVT_GenerateAP::kComboBox: {
WideString swValue =
FPDF_GetFieldAttr(pAnnotDict, "V")
? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
: WideString();
CPVT_FontMap map(
pDoc, pStreamDict ? pStreamDict->GetDictFor("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);
vt.RearrangeAll();
CFX_FloatRect rcContent = vt.GetContentRect();
CFX_PointF ptOffset =
CFX_PointF(0.0f, (rcContent.Height() - rcEdit.Height()) / 2.0f);
ByteString sEdit =
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"
<< GenerateColorAP(crText, PaintOperation::FILL) << sEdit
<< "ET\n"
<< "Q\nEMC\n";
}
ByteString sButton =
GenerateColorAP(CFX_Color(CFX_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";
ByteString sButtonBorder = GenerateBorderAP(
rcButton, 2, CFX_Color(CFX_Color::kGray, 0),
CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5),
BorderStyle::BEVELED, CPVT_Dash(3, 0, 0));
if (sButtonBorder.GetLength() > 0)
sAppStream << "q\n" << sButtonBorder << "Q\n";
CFX_PointF ptCenter = CFX_PointF((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 CPVT_GenerateAP::kListBox: {
CPVT_FontMap map(
pDoc, pStreamDict ? pStreamDict->GetDictFor("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;
std::ostringstream sBody;
if (pOpts) {
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)) {
WideString swItem;
if (pOpt->IsString())
swItem = pOpt->GetUnicodeText();
else if (CPDF_Array* pArray = pOpt->AsArray())
swItem = pArray->GetDirectObjectAt(1)->GetUnicodeText();
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);
vt.RearrangeAll();
float fItemHeight = vt.GetContentRect().Height();
if (bSelected) {
CFX_FloatRect rcItem = CFX_FloatRect(
rcBody.left, fy - fItemHeight, rcBody.right, fy);
sBody << "q\n"
<< GenerateColorAP(
CFX_Color(CFX_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"
<< GenerateColorAP(CFX_Color(CFX_Color::kGray, 1),
PaintOperation::FILL)
<< GenerateEditAP(&map, vt.GetIterator(),
CFX_PointF(0.0f, fy), true, 0)
<< "ET\n";
} else {
sBody << "BT\n"
<< GenerateColorAP(crText, PaintOperation::FILL)
<< GenerateEditAP(&map, vt.GetIterator(),
CFX_PointF(0.0f, fy), true, 0)
<< "ET\n";
}
fy -= fItemHeight;
}
}
}
if (sBody.tellp() > 0) {
sAppStream << "/Tx BMC\nq\n"
<< rcBody.left << " " << rcBody.bottom << " "
<< rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n"
<< sBody.str() << "Q\nEMC\n";
}
break;
}
}
if (pNormalStream) {
pNormalStream->SetDataAndRemoveFilter(&sAppStream);
pStreamDict = pNormalStream->GetDict();
if (pStreamDict) {
pStreamDict->SetMatrixFor("Matrix", matrix);
pStreamDict->SetRectFor("BBox", rcBBox);
CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
if (pStreamResList) {
CPDF_Dictionary* pStreamResFontList =
pStreamResList->GetDictFor("Font");
if (!pStreamResFontList) {
pStreamResFontList =
pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
}
if (!pStreamResFontList->KeyExist(sFontName)) {
pStreamResFontList->SetNewFor<CPDF_Reference>(sFontName, pDoc,
pFontDict->GetObjNum());
}
} else {
pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
pStreamResList = pStreamDict->GetDictFor("Resources");
}
}
}
return;
}
// static
void CPVT_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict) {
auto pExtGStateDict = GenerateExtGStateDict(*pAnnotDict, "GS", "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
std::ostringstream sStream;
GenerateAndSetAPDict(pDoc, pAnnotDict, &sStream, std::move(pResourceDict),
false);
}
// static
bool CPVT_GenerateAP::GenerateAnnotAP(CPDF_Annot::Subtype subtype,
CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict) {
switch (subtype) {
case CPDF_Annot::Subtype::CIRCLE:
return GenerateCircleAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::HIGHLIGHT:
return GenerateHighlightAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::INK:
return GenerateInkAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::POPUP:
return GeneratePopupAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::SQUARE:
return GenerateSquareAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::SQUIGGLY:
return GenerateSquigglyAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::STRIKEOUT:
return GenerateStrikeOutAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::TEXT:
return GenerateTextAP(pDoc, pAnnotDict);
case CPDF_Annot::Subtype::UNDERLINE:
return GenerateUnderlineAP(pDoc, pAnnotDict);
default:
return false;
}
}