diff --git a/core/fpdfdoc/cpdf_richtextparser.cpp b/core/fpdfdoc/cpdf_richtextparser.cpp index bca8d00b8..42ae7be2d 100644 --- a/core/fpdfdoc/cpdf_richtextparser.cpp +++ b/core/fpdfdoc/cpdf_richtextparser.cpp @@ -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); @@ -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 } } diff --git a/core/fpdfdoc/cpdf_richtextparser.h b/core/fpdfdoc/cpdf_richtextparser.h index ec1474724..5f84253cd 100644 --- a/core/fpdfdoc/cpdf_richtextparser.h +++ b/core/fpdfdoc/cpdf_richtextparser.h @@ -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, diff --git a/fpdfsdk/epdf_annot_embeddertest.cpp b/fpdfsdk/epdf_annot_embeddertest.cpp index 475b1cf5b..c67ccef71 100644 --- a/fpdfsdk/epdf_annot_embeddertest.cpp +++ b/fpdfsdk/epdf_annot_embeddertest.cpp @@ -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\">
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"
Imported
"); + ASSERT_TRUE(EPDFAnnot_SetRichTextXHTML(annot.get(), xhtml.get())); + EXPECT_TRUE(dict->GetUnicodeTextFor("RC").Contains( + L"xfa:spec=\"2.0.2\">Imported
")); + 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