blob: d92d58045340e301f6ec89e0c499c4d2f79db6b8 [file] [log] [blame]
// 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/cpdf_generateap.h"
#include <algorithm>
#include <sstream>
#include <utility>
#include "constants/annotation_common.h"
#include "constants/appearance.h"
#include "constants/font_encodings.h"
#include "constants/form_fields.h"
#include "core/fpdfapi/font/cpdf_font.h"
#include "core/fpdfapi/page/cpdf_docpagedata.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_stream.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
#include "core/fpdfapi/parser/fpdf_parser_utility.h"
#include "core/fpdfdoc/cpdf_annot.h"
#include "core/fpdfdoc/cpdf_color_utils.h"
#include "core/fpdfdoc/cpdf_defaultappearance.h"
#include "core/fpdfdoc/cpdf_formfield.h"
#include "core/fpdfdoc/cpvt_fontmap.h"
#include "core/fpdfdoc/cpvt_variabletext.h"
#include "core/fpdfdoc/cpvt_word.h"
#include "core/fxcrt/fx_string_wrappers.h"
#include "core/fxge/cfx_renderdevice.h"
namespace {
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;
};
enum class PaintOperation { kStroke, kFill };
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 ByteString();
RetainPtr<CPDF_Font> pPDFFont = pFontMap->GetPDFFont(nFontIndex);
if (!pPDFFont)
return ByteString();
if (pPDFFont->GetBaseFontName() == "Symbol" ||
pPDFFont->GetBaseFontName() == "ZapfDingbats") {
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(ByteStringView strWords) {
if (strWords.IsEmpty())
return ByteString();
return PDF_EncodeString(strWords) + " Tj\n";
}
ByteString GetFontSetString(IPVT_FontMap* pFontMap,
int32_t nFontIndex,
float fFontSize) {
fxcrt::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,
CPVT_VariableText::Iterator* pIterator,
const CFX_PointF& ptOffset,
bool bContinuous,
uint16_t SubWord) {
fxcrt::ostringstream sEditStream;
fxcrt::ostringstream sLineStream;
CFX_PointF ptOld;
CFX_PointF ptNew;
int32_t nCurFontIndex = -1;
CPVT_WordPlace oldplace;
ByteString sWords;
pIterator->SetAt(0);
while (pIterator->NextWord()) {
CPVT_WordPlace place = pIterator->GetWordPlace();
if (bContinuous) {
if (place.LineCmp(oldplace) != 0) {
if (!sWords.IsEmpty()) {
sLineStream << GetWordRenderString(sWords.AsStringView());
sEditStream << sLineStream.str();
sLineStream.str("");
sWords.clear();
}
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.IsEmpty()) {
sLineStream << GetWordRenderString(sWords.AsStringView());
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_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)
.AsStringView());
}
}
}
if (!sWords.IsEmpty()) {
sLineStream << GetWordRenderString(sWords.AsStringView());
sEditStream << sLineStream.str();
}
return ByteString(sEditStream);
}
ByteString GenerateColorAP(const CFX_Color& color, PaintOperation nOperation) {
fxcrt::ostringstream sColorStream;
switch (color.nColorType) {
case CFX_Color::Type::kRGB:
sColorStream << color.fColor1 << " " << color.fColor2 << " "
<< color.fColor3 << " "
<< (nOperation == PaintOperation::kStroke ? "RG" : "rg")
<< "\n";
break;
case CFX_Color::Type::kGray:
sColorStream << color.fColor1 << " "
<< (nOperation == PaintOperation::kStroke ? "G" : "g")
<< "\n";
break;
case CFX_Color::Type::kCMYK:
sColorStream << color.fColor1 << " " << color.fColor2 << " "
<< color.fColor3 << " " << color.fColor4 << " "
<< (nOperation == PaintOperation::kStroke ? "K" : "k")
<< "\n";
break;
case CFX_Color::Type::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) {
fxcrt::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::kSolid:
sColor = GenerateColorAP(color, PaintOperation::kFill);
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::kDash:
sColor = GenerateColorAP(color, PaintOperation::kStroke);
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::kBeveled:
case BorderStyle::kInset:
sColor = GenerateColorAP(crLeftTop, PaintOperation::kFill);
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::kFill);
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::kFill);
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::kUnderline:
sColor = GenerateColorAP(color, PaintOperation::kStroke);
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(const CPDF_Array* pColor,
const CFX_Color& crDefaultColor,
PaintOperation nOperation) {
if (pColor) {
CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
return GenerateColorAP(color, nOperation);
}
return GenerateColorAP(crDefaultColor, nOperation);
}
float GetBorderWidth(const CPDF_Dictionary& pAnnotDict) {
if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
if (pBorderStyleDict->KeyExist("W"))
return pBorderStyleDict->GetNumberFor("W");
}
if (const CPDF_Array* pBorderArray =
pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) {
if (pBorderArray->size() > 2)
return pBorderArray->GetNumberAt(2);
}
return 1;
}
const CPDF_Array* GetDashArray(const CPDF_Dictionary& pAnnotDict) {
if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
if (pBorderStyleDict->GetStringFor("S") == "D")
return pBorderStyleDict->GetArrayFor("D");
}
if (const CPDF_Array* pBorderArray =
pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) {
if (pBorderArray->size() == 4)
return pBorderArray->GetArrayAt(3);
}
return nullptr;
}
ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) {
const 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->size(), 10);
fxcrt::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,
RetainPtr<CPDF_Font> pDefFont,
const ByteString& sFontName) {
WideString swValue(pAnnotDict.GetUnicodeTextFor(pdfium::form_fields::kT));
swValue += L'\n';
swValue += pAnnotDict.GetUnicodeTextFor(pdfium::annotation::kContents);
CPVT_FontMap map(pDoc, nullptr, std::move(pDefFont), sFontName);
CPVT_VariableText::Provider prd(&map);
CPVT_VariableText vt(&prd);
vt.SetPlateRect(pAnnotDict.GetRectFor(pdfium::annotation::kRect));
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();
ByteString sColorAP = GenerateColorAP(
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kFill);
return ByteString{"BT\n", sColorAP.AsStringView(), sContent.AsStringView(),
"ET\n", "Q\n"};
}
RetainPtr<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", CFX_Font::kDefaultAnsiFontName);
pFontDict->SetNewFor<CPDF_Name>("Encoding",
pdfium::font_encodings::kWinAnsiEncoding);
auto pResourceFontDict = pDoc->New<CPDF_Dictionary>();
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) {
fxcrt::ostringstream sAppStream;
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0),
PaintOperation::kFill);
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0),
PaintOperation::kStroke);
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);
}
RetainPtr<CPDF_Dictionary> GenerateExtGStateDict(
const CPDF_Dictionary& pAnnotDict,
const ByteString& sExtGSDictName,
const ByteString& sBlendMode) {
auto pGSDict =
pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
pGSDict->SetNewFor<CPDF_Name>("Type", "ExtGState");
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_Name>("BM", sBlendMode);
auto pExtGStateDict =
pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
pExtGStateDict->SetFor(sExtGSDictName, pGSDict);
return pExtGStateDict;
}
RetainPtr<CPDF_Dictionary> GenerateResourceDict(
CPDF_Document* pDoc,
RetainPtr<CPDF_Dictionary> pExtGStateDict,
RetainPtr<CPDF_Dictionary> pResourceFontDict) {
auto pResourceDict = pDoc->New<CPDF_Dictionary>();
if (pExtGStateDict)
pResourceDict->SetFor("ExtGState", pExtGStateDict);
if (pResourceFontDict)
pResourceDict->SetFor("Font", pResourceFontDict);
return pResourceDict;
}
void GenerateAndSetAPDict(CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict,
fxcrt::ostringstream* psAppStream,
RetainPtr<CPDF_Dictionary> pResourceDict,
bool bIsTextMarkupAnnotation) {
CPDF_Stream* pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
pNormalStream->SetDataFromStringstream(psAppStream);
RetainPtr<CPDF_Dictionary> pAPDict =
pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
RetainPtr<CPDF_Dictionary> pStreamDict = pNormalStream->GetMutableDict();
pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
CFX_FloatRect rect = bIsTextMarkupAnnotation
? CPDF_Annot::BoundingRectFromQuadPoints(pAnnotDict)
: pAnnotDict->GetRectFor(pdfium::annotation::kRect);
pStreamDict->SetRectFor("BBox", rect);
pStreamDict->SetFor("Resources", pResourceDict);
}
bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
const CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
sAppStream << GetColorStringWithDefault(
pInteriorColor, CFX_Color(CFX_Color::Type::kTransparent),
PaintOperation::kFill);
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
float fBorderWidth = GetBorderWidth(*pAnnotDict);
bool bIsStrokeRect = fBorderWidth > 0;
if (bIsStrokeRect) {
sAppStream << fBorderWidth << " w ";
sAppStream << GetDashPatternString(*pAnnotDict);
}
CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
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) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0), PaintOperation::kFill);
const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
if (pArray) {
size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
for (size_t i = 0; i < nQuadPointCount; ++i) {
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
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;
const CPDF_Array* pInkList = pAnnotDict->GetArrayFor("InkList");
if (!pInkList || pInkList->IsEmpty())
return false;
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
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(pdfium::annotation::kRect);
rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
pAnnotDict->SetRectFor(pdfium::annotation::kRect, rect);
for (size_t i = 0; i < pInkList->size(); i++) {
const CPDF_Array* pInkCoordList = pInkList->GetArrayAt(i);
if (!pInkCoordList || pInkCoordList->size() < 2)
continue;
sAppStream << pInkCoordList->GetNumberAt(0) << " "
<< pInkCoordList->GetNumberAt(1) << " m ";
for (size_t j = 0; j < pInkCoordList->size() - 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) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
const float fNoteLength = 20;
CFX_FloatRect noteRect(rect.left, rect.bottom, rect.left + fNoteLength,
rect.bottom + fNoteLength);
pAnnotDict->SetRectFor(pdfium::annotation::kRect, 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) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
if (pArray) {
static constexpr float kLineWidth = 1.0f;
sAppStream << kLineWidth << " w ";
size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
for (size_t i = 0; i < nQuadPointCount; ++i) {
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
rect.Normalize();
sAppStream << rect.left << " " << rect.bottom + kLineWidth << " m "
<< rect.right << " " << rect.bottom + kLineWidth << " 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) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs\n";
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0),
PaintOperation::kFill);
sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0),
PaintOperation::kStroke);
const float fBorderWidth = 1;
sAppStream << fBorderWidth << " w\n";
CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
rect.Normalize();
rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
<< rect.Height() << " re b\n";
ByteString sFontName = "FONT";
RetainPtr<CPDF_Dictionary> pResourceFontDict =
GenerateResourceFontDict(pDoc, sFontName);
auto* pData = CPDF_DocPageData::FromDocument(pDoc);
RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pResourceFontDict);
if (!pDefFont)
return false;
RetainPtr<CPDF_Dictionary> pExtGStateDict =
GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
RetainPtr<CPDF_Dictionary> pResourceDict = GenerateResourceDict(
pDoc, std::move(pExtGStateDict), std::move(pResourceFontDict));
sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, std::move(pDefFont),
sFontName);
GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
false /*IsTextMarkupAnnotation*/);
return true;
}
bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
const CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
sAppStream << GetColorStringWithDefault(
pInteriorColor, CFX_Color(CFX_Color::Type::kTransparent),
PaintOperation::kFill);
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
float fBorderWidth = GetBorderWidth(*pAnnotDict);
bool bIsStrokeRect = fBorderWidth > 0;
if (bIsStrokeRect) {
sAppStream << fBorderWidth << " w ";
sAppStream << GetDashPatternString(*pAnnotDict);
}
CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
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->size() > 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) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
if (pArray) {
static constexpr float kLineWidth = 1.0f;
static constexpr float kDelta = 2.0f;
sAppStream << kLineWidth << " w ";
size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
for (size_t i = 0; i < nQuadPointCount; ++i) {
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
rect.Normalize();
const float fTop = rect.bottom + kDelta;
const float fBottom = rect.bottom;
sAppStream << rect.left << " " << fTop << " m ";
float fX = rect.left + kDelta;
bool isUpwards = false;
while (fX < rect.right) {
sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
fX += kDelta;
isUpwards = !isUpwards;
}
float fRemainder = rect.right - (fX - kDelta);
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) {
fxcrt::ostringstream sAppStream;
ByteString sExtGSDictName = "GS";
sAppStream << "/" << sExtGSDictName << " gs ";
sAppStream << GetColorStringWithDefault(
pAnnotDict->GetArrayFor(pdfium::annotation::kC),
CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
if (pArray) {
static constexpr float kLineWidth = 1.0f;
size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray);
for (size_t i = 0; i < nQuadPointCount; ++i) {
CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
rect.Normalize();
float fY = (rect.top + rect.bottom) / 2;
sAppStream << kLineWidth << " 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 CPDF_GenerateAP::GenerateFormAP(CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict,
FormType type) {
RetainPtr<CPDF_Dictionary> pRootDict = pDoc->GetMutableRoot();
if (!pRootDict)
return;
RetainPtr<CPDF_Dictionary> pFormDict =
pRootDict->GetMutableDictFor("AcroForm");
if (!pFormDict)
return;
ByteString DA;
if (CPDF_Object* pDAObj = CPDF_FormField::GetFieldAttr(pAnnotDict, "DA"))
DA = pDAObj->GetString();
if (DA.IsEmpty())
DA = pFormDict->GetStringFor("DA");
if (DA.IsEmpty())
return;
CPDF_DefaultAppearance appearance(DA);
float fFontSize = 0;
absl::optional<ByteString> font = appearance.GetFont(&fFontSize);
if (!font.has_value())
return;
ByteString font_name = font.value();
CFX_Color crText = fpdfdoc::CFXColorFromString(DA);
RetainPtr<CPDF_Dictionary> pDRDict = pFormDict->GetMutableDictFor("DR");
if (!pDRDict)
return;
RetainPtr<CPDF_Dictionary> pDRFontDict = pDRDict->GetMutableDictFor("Font");
if (!ValidateFontResourceDict(pDRFontDict.Get()))
return;
RetainPtr<CPDF_Dictionary> pFontDict =
pDRFontDict->GetMutableDictFor(font_name);
if (!pFontDict) {
pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
pFontDict->SetNewFor<CPDF_Name>("Encoding",
pdfium::font_encodings::kWinAnsiEncoding);
pDRFontDict->SetNewFor<CPDF_Reference>(font_name, pDoc,
pFontDict->GetObjNum());
}
auto* pData = CPDF_DocPageData::FromDocument(pDoc);
RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pFontDict);
if (!pDefFont)
return;
CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
const CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK");
int32_t nRotate =
pMKDict ? pMKDict->GetIntegerFor(pdfium::appearance::kR) : 0;
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::kSolid;
float fBorderWidth = 1;
CPVT_Dash dsBorder(3, 0, 0);
CFX_Color crLeftTop;
CFX_Color crRightBottom;
if (const CPDF_Dictionary* pBSDict = pAnnotDict->GetDictFor("BS")) {
if (pBSDict->KeyExist("W"))
fBorderWidth = pBSDict->GetNumberFor("W");
if (const 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::kSolid;
break;
case 'D':
nBorderStyle = BorderStyle::kDash;
break;
case 'B':
nBorderStyle = BorderStyle::kBeveled;
fBorderWidth *= 2;
crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.5);
break;
case 'I':
nBorderStyle = BorderStyle::kInset;
fBorderWidth *= 2;
crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
break;
case 'U':
nBorderStyle = BorderStyle::kUnderline;
break;
}
}
}
CFX_Color crBorder;
CFX_Color crBG;
if (pMKDict) {
const CPDF_Array* pArray = pMKDict->GetArrayFor(pdfium::appearance::kBC);
if (pArray)
crBorder = fpdfdoc::CFXColorFromArray(*pArray);
pArray = pMKDict->GetArrayFor(pdfium::appearance::kBG);
if (pArray)
crBG = fpdfdoc::CFXColorFromArray(*pArray);
}
fxcrt::ostringstream sAppStream;
ByteString sBG = GenerateColorAP(crBG, PaintOperation::kFill);
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();
RetainPtr<CPDF_Dictionary> pAPDict =
pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
RetainPtr<CPDF_Stream> pNormalStream = pAPDict->GetMutableStreamFor("N");
if (!pNormalStream) {
pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
}
RetainPtr<CPDF_Dictionary> pStreamDict = pNormalStream->GetMutableDict();
if (pStreamDict) {
RetainPtr<CPDF_Dictionary> pStreamResList =
pStreamDict->GetMutableDictFor("Resources");
if (pStreamResList) {
RetainPtr<CPDF_Dictionary> pStreamResFontList =
pStreamResList->GetMutableDictFor("Font");
if (pStreamResFontList) {
if (!ValidateFontResourceDict(pStreamResFontList.Get()))
return;
} else {
pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
}
if (!pStreamResFontList->KeyExist(font_name)) {
pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
pFontDict->GetObjNum());
}
} else {
pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
}
pStreamDict->SetMatrixFor("Matrix", matrix);
pStreamDict->SetRectFor("BBox", rcBBox);
}
CPVT_FontMap map(
pDoc,
pStreamDict ? pStreamDict->GetMutableDictFor("Resources").Get() : nullptr,
std::move(pDefFont), font_name);
CPVT_VariableText::Provider prd(&map);
switch (type) {
case CPDF_GenerateAP::kTextField: {
const CPDF_Object* pV =
CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV);
WideString swValue = pV ? pV->GetUnicodeText() : WideString();
const CPDF_Object* pQ = CPDF_FormField::GetFieldAttr(pAnnotDict, "Q");
int32_t nAlign = pQ ? pQ->GetInteger() : 0;
const CPDF_Object* pFf =
CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf);
uint32_t dwFlags = pFf ? pFf->GetInteger() : 0;
const CPDF_Object* pMaxLen =
CPDF_FormField::GetFieldAttr(pAnnotDict, "MaxLen");
uint32_t dwMaxLen = pMaxLen ? pMaxLen->GetInteger() : 0;
CPVT_VariableText vt(&prd);
vt.SetPlateRect(rcBody);
vt.SetAlignment(nAlign);
if (FXSYS_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::kFill) << sBody
<< "ET\n"
<< "Q\nEMC\n";
}
break;
}
case CPDF_GenerateAP::kComboBox: {
const CPDF_Object* pV =
CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV);
WideString swValue = pV ? pV->GetUnicodeText() : WideString();
CPVT_VariableText vt(&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 (FXSYS_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::kFill) << sEdit
<< "ET\n"
<< "Q\nEMC\n";
}
ByteString sButton =
GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f,
220.0f / 255.0f, 220.0f / 255.0f),
PaintOperation::kFill);
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::Type::kGray, 0),
CFX_Color(CFX_Color::Type::kGray, 1),
CFX_Color(CFX_Color::Type::kGray, 0.5),
BorderStyle::kBeveled, 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 (FXSYS_IsFloatBigger(rcButton.Width(), 6) &&
FXSYS_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 CPDF_GenerateAP::kListBox: {
CPDF_Array* pOpts =
ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "Opt"));
CPDF_Array* pSels =
ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "I"));
CPDF_Object* pTi = CPDF_FormField::GetFieldAttr(pAnnotDict, "TI");
int32_t nTop = pTi ? pTi->GetInteger() : 0;
fxcrt::ostringstream sBody;
if (pOpts) {
float fy = rcBody.top;
for (size_t i = nTop, sz = pOpts->size(); i < sz; i++) {
if (FXSYS_IsFloatSmaller(fy, rcBody.bottom))
break;
if (const CPDF_Object* pOpt = pOpts->GetDirectObjectAt(i)) {
WideString swItem;
if (pOpt->IsString()) {
swItem = pOpt->GetUnicodeText();
} else if (const CPDF_Array* pArray = pOpt->AsArray()) {
const CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(1);
if (pDirectObj)
swItem = pDirectObj->GetUnicodeText();
}
bool bSelected = false;
if (pSels) {
for (size_t s = 0, ssz = pSels->size(); s < ssz; s++) {
int value = pSels->GetIntegerAt(s);
if (value >= 0 && i == static_cast<size_t>(value)) {
bSelected = true;
break;
}
}
}
CPVT_VariableText vt(&prd);
vt.SetPlateRect(
CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f));
vt.SetFontSize(FXSYS_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::Type::kRGB, 0, 51.0f / 255.0f,
113.0f / 255.0f),
PaintOperation::kFill)
<< rcItem.left << " " << rcItem.bottom << " "
<< rcItem.Width() << " " << rcItem.Height() << " re f\n"
<< "Q\n";
sBody << "BT\n"
<< GenerateColorAP(CFX_Color(CFX_Color::Type::kGray, 1),
PaintOperation::kFill)
<< GenerateEditAP(&map, vt.GetIterator(),
CFX_PointF(0.0f, fy), true, 0)
<< "ET\n";
} else {
sBody << "BT\n"
<< GenerateColorAP(crText, PaintOperation::kFill)
<< 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)
return;
pNormalStream->SetDataFromStringstreamAndRemoveFilter(&sAppStream);
pStreamDict = pNormalStream->GetMutableDict();
if (!pStreamDict)
return;
pStreamDict->SetMatrixFor("Matrix", matrix);
pStreamDict->SetRectFor("BBox", rcBBox);
RetainPtr<CPDF_Dictionary> pStreamResList =
pStreamDict->GetMutableDictFor("Resources");
if (!pStreamResList) {
pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
return;
}
RetainPtr<CPDF_Dictionary> pStreamResFontList =
pStreamResList->GetMutableDictFor("Font");
if (pStreamResFontList) {
if (!ValidateFontResourceDict(pStreamResFontList.Get()))
return;
} else {
pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
}
if (!pStreamResFontList->KeyExist(font_name)) {
pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
pFontDict->GetObjNum());
}
}
// static
void CPDF_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict) {
auto pExtGStateDict = GenerateExtGStateDict(*pAnnotDict, "GS", "Normal");
auto pResourceDict =
GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
fxcrt::ostringstream sStream;
GenerateAndSetAPDict(pDoc, pAnnotDict, &sStream, std::move(pResourceDict),
false);
}
// static
bool CPDF_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc,
CPDF_Dictionary* pAnnotDict,
CPDF_Annot::Subtype subtype) {
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;
}
}