Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion core/fpdfdoc/cpdf_richtextparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,11 @@ void CPDF_RichTextParser::DefaultsFromAnnotation(
if (!annot_dict) {
return;
}
// A comment draws nothing: the reader's defaults stand.
const ByteString subtype = annot_dict->GetNameFor("Subtype");
if (subtype != "FreeText" && subtype != "Widget") {
return;
}

// /DA: font name (→ face through /DR), size, colour.
CPDF_DefaultAppearance da(annot_dict, acroform_dict);
Expand Down Expand Up @@ -1321,7 +1326,7 @@ void CPDF_RichTextParser::DefaultsFromAnnotation(
}
defaults = ApplyRichTextStyleDelta(defaults, ds_delta);
}
if (defaults.size <= 0 && annot_dict->GetNameFor("Subtype") == "FreeText") {
if (defaults.size <= 0 && subtype == "FreeText") {
defaults.size = 12.0f; // DA size 0 (auto) has no meaning for rich text
}
}
Expand Down
13 changes: 9 additions & 4 deletions core/fpdfdoc/cpdf_richtextparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,20 @@ class CPDF_RichTextParser {
const CPDF_RichTextStyle& defaults,
const CPDF_RichTextParagraphProps& paragraph_defaults);

// The document an annotation holds: /RC (or /RV for widgets) over
// DA ∪ DS defaults, else its /Contents (or /V) as plain text.
// The document an annotation holds: /RC (or /RV for widgets) over its
// defaults (DefaultsFromAnnotation()), else its /Contents (or /V) as plain
// text.
static CPDF_RichTextDocument FromAnnotation(
const CPDF_Dictionary* annot_dict,
const CPDF_Dictionary* acroform_dict,
const CPDF_Document* doc = nullptr);

// DA ∪ DS alone (no /RC): the defaults an imported XHTML body style is
// applied on top of.
// The defaults an annotation's rich text is read over, and an imported
// XHTML body style is applied on top of. A free text's and a widget's
// rich text is the text they draw, so its defaults are DA ∪ DS (and /Q,
// /TextColor). Any other annotation's rich text is its comment, which
// draws nothing: its defaults are the reader's (CPDF_RichTextStyle{}),
// whatever a /DA says. A redact's /DA styles its overlay text.
static void DefaultsFromAnnotation(
const CPDF_Dictionary* annot_dict,
const CPDF_Dictionary* acroform_dict,
Expand Down
49 changes: 49 additions & 0 deletions fpdfsdk/epdf_annot_embeddertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4288,6 +4288,55 @@ TEST_F(EPDFAnnotEmbedderTest, SetRichTextJSONRefusesKindsThatArentMarkup) {
}
}

// A redact's /DA styles its overlay text, not its comment: a comment that
// states no style reads in the reader's defaults, written by JSON or
// imported from XHTML, and the overlay keeps its /DA.
TEST_F(EPDFAnnotEmbedderTest, RedactCommentReadsOverTheReadersDefaults) {
embedpdf_test::ScopedTestDocument doc(NewTestDocument());
ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 400, 400));
ScopedFPDFAnnotation annot(
FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_REDACT));
ASSERT_TRUE(annot);
const FS_RECTF rect{50.0f, 320.0f, 200.0f, 280.0f};
ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
ASSERT_TRUE(EPDFAnnot_SetDefaultAppearance(annot.get(), FPDF_FONT_COURIER,
10.0f, 255, 0, 0));
const std::string reader_body =
"\"body\":{\"family\":\"\",\"weight\":400,\"italic\":false,"
"\"size\":12,\"color\":\"#000000\",";

ASSERT_TRUE(EPDFAnnot_SetRichTextJSON(
annot.get(),
"{\"paragraphs\":[{\"runs\":[{\"text\":\"Approved by \"},"
"{\"text\":\"legal\",\"style\":{\"weight\":700}}]}]}"));
const CPDF_Dictionary* dict =
CPDFAnnotContextFromFPDFAnnotation(annot.get())->GetAnnotDict();
EXPECT_TRUE(dict->GetUnicodeTextFor("RC").Contains(
L"xfa:spec=\"2.0.2\"><p dir=\"ltr\">Approved by "));
std::string json = GetRichTextJson(annot.get());
EXPECT_NE(std::string::npos, json.find("\"source\":\"rc\"")) << json;
EXPECT_NE(std::string::npos, json.find(reader_body)) << json;

ScopedFPDFWideString xhtml = GetFPDFWideString(
L"<body xmlns=\"http://www.w3.org/1999/xhtml\"><p>Imported</p></body>");
ASSERT_TRUE(EPDFAnnot_SetRichTextXHTML(annot.get(), xhtml.get()));
EXPECT_TRUE(dict->GetUnicodeTextFor("RC").Contains(
L"xfa:spec=\"2.0.2\"><p dir=\"ltr\">Imported</p>"));
json = GetRichTextJson(annot.get());
EXPECT_NE(std::string::npos, json.find(reader_body)) << json;

FPDF_STANDARD_FONT font = FPDF_FONT_UNKNOWN;
float font_size = 0;
unsigned int r = 0;
unsigned int g = 0;
unsigned int b = 0;
ASSERT_TRUE(EPDFAnnot_GetDefaultAppearance(annot.get(), &font, &font_size, &r,
&g, &b));
EXPECT_EQ(FPDF_FONT_COURIER, font);
EXPECT_EQ(10.0f, font_size);
EXPECT_EQ(255u, r);
}

TEST_F(EPDFAnnotEmbedderTest, SetRichTextJSONWithRegisteredBodyFont) {
ScopedRegisteredFonts scoped_fonts;
std::vector<uint8_t> roboto = LoadRobotoFontData();
Expand Down
7 changes: 4 additions & 3 deletions public/epdf_annot.h
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,8 @@ EPDFAnnot_GetRichContent(FPDF_ANNOTATION annot,

// Experimental EmbedPDF Extension API.
// The rich text of a FreeText annotation (/RC over /DS and /DA), of another
// markup annotation's comment (/RC), or of a rich text field (/RV), as UTF-8
// markup annotation's comment (/RC over the reader's defaults, whatever its
// /DA says), or of a rich text field (/RV over /DA), as UTF-8
// JSON: {"source":"rc"|"contents","body":{...},
// "paragraphs":[{"align","dir","runs":[{"text","style":{deltas}}]}],
// "diagnostics":[{"code","detail"}]}. When there is no rich text the document
Expand Down Expand Up @@ -535,8 +536,8 @@ EPDFAnnot_SetRichTextJSON(FPDF_ANNOTATION annot, FPDF_BYTESTRING json_utf8);

// Experimental EmbedPDF Extension API (rich text, Phase D).
// Import raw XHTML (the XFA rich text subset, e.g. another producer's /RC)
// over the annotation's DA and DS defaults; same effects and failures as
// EPDFAnnot_SetRichTextJSON(). Malformed XML returns false.
// over the defaults EPDFAnnot_GetRichTextJSON() reads it over; same effects
// and failures as EPDFAnnot_SetRichTextJSON(). Malformed XML returns false.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
EPDFAnnot_SetRichTextXHTML(FPDF_ANNOTATION annot, FPDF_WIDESTRING xhtml);

Expand Down
1 change: 1 addition & 0 deletions testing/embedpdf_layer_mode_writers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ EPDFAnnotEmbedderTest.OpacityIsStoredAsGiven 3
EPDFAnnotEmbedderTest.PolylineKneePastTheMiterLimitIsBeveled 4
EPDFAnnotEmbedderTest.PreviewAndPrintFallbackDoesNotAuthorUntilAuthorized 25
EPDFAnnotEmbedderTest.ReadPurityRenderMarkupAnnotWithContents 1
EPDFAnnotEmbedderTest.RedactCommentReadsOverTheReadersDefaults 6
EPDFAnnotEmbedderTest.RedactInQuadsMultipleRegionsOnOneTextObject 3
EPDFAnnotEmbedderTest.RedactInQuadsRemovesTextInsideFormXObject 5
EPDFAnnotEmbedderTest.RedactLabelRegisteredFontInstallsRealDrEntry 12
Expand Down