From d03ca4214e6db7123e1c2d0ea58e34f7cf67a568 Mon Sep 17 00:00:00 2001 From: Nicolas Pena Date: Mon, 6 Mar 2017 13:54:33 -0500 Subject: Add public method FPDFText_LoadFont to load some types of fonts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new method replaces the previous one for loading only type1 fonts. This will create the font dictionary with the very basics needed, according to the tables in chapter 5 of the PDF spec. The tests for now are only checking that the information is getting passed on properly. Followup: adding text with those fonts, generating the contents (doing that will allow testing whether the parts that I've skipped like Encoding and cmaps are needed or not). BUG=pdfium:667 Change-Id: Id1a61501e09542804a391552fd002f2caed41939 Reviewed-on: https://pdfium-review.googlesource.com/2915 Commit-Queue: Nicolás Peña Reviewed-by: Tom Sepez --- fpdfsdk/fpdfedit_embeddertest.cpp | 323 ++++++++++++++++++++++++++++++-------- fpdfsdk/fpdfedittext.cpp | 277 +++++++++++++++++++++++--------- fpdfsdk/fpdfview_c_api_test.c | 2 +- public/fpdf_edit.h | 22 ++- 4 files changed, 480 insertions(+), 144 deletions(-) diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp index 53554a1822..b21be2df1b 100644 --- a/fpdfsdk/fpdfedit_embeddertest.cpp +++ b/fpdfsdk/fpdfedit_embeddertest.cpp @@ -4,11 +4,13 @@ #include #include +#include #include "core/fpdfapi/font/cpdf_font.h" #include "core/fpdfapi/page/cpdf_page.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fxcrt/fx_system.h" #include "fpdfsdk/fsdk_define.h" @@ -19,7 +21,106 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/test_support.h" -class FPDFEditEmbeddertest : public EmbedderTest, public TestSaver {}; +class FPDFEditEmbeddertest : public EmbedderTest, public TestSaver { + protected: + FPDF_DOCUMENT CreateNewDocument() { + document_ = FPDF_CreateNewDocument(); + cpdf_doc_ = reinterpret_cast(document_); + return document_; + } + + void CheckFontDescriptor(CPDF_Dictionary* font_dict, + int font_type, + bool bold, + bool italic, + uint32_t size, + const uint8_t* data) { + CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor"); + ASSERT_TRUE(font_desc); + EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type")); + EXPECT_EQ(font_dict->GetStringFor("BaseFont"), + font_desc->GetStringFor("FontName")); + + // Check that the font descriptor has the required keys according to spec + // 1.7 Table 5.19 + ASSERT_TRUE(font_desc->KeyExist("Flags")); + int font_flags = font_desc->GetIntegerFor("Flags"); + if (bold) + EXPECT_TRUE(font_flags & FXFONT_BOLD); + else + EXPECT_FALSE(font_flags & FXFONT_BOLD); + if (italic) + EXPECT_TRUE(font_flags & FXFONT_ITALIC); + else + EXPECT_FALSE(font_flags & FXFONT_ITALIC); + EXPECT_TRUE(font_flags & FXFONT_NONSYMBOLIC); + ASSERT_TRUE(font_desc->KeyExist("FontBBox")); + EXPECT_EQ(4U, font_desc->GetArrayFor("FontBBox")->GetCount()); + EXPECT_TRUE(font_desc->KeyExist("ItalicAngle")); + EXPECT_TRUE(font_desc->KeyExist("Ascent")); + EXPECT_TRUE(font_desc->KeyExist("Descent")); + EXPECT_TRUE(font_desc->KeyExist("CapHeight")); + EXPECT_TRUE(font_desc->KeyExist("StemV")); + CFX_ByteString present("FontFile"); + CFX_ByteString absent("FontFile2"); + if (font_type == FPDF_FONT_TRUETYPE) + std::swap(present, absent); + EXPECT_TRUE(font_desc->KeyExist(present)); + EXPECT_FALSE(font_desc->KeyExist(absent)); + + // Check that the font stream is the one that was provided + CPDF_Stream* font_stream = font_desc->GetStreamFor(present); + ASSERT_EQ(size, font_stream->GetRawSize()); + uint8_t* stream_data = font_stream->GetRawData(); + for (size_t j = 0; j < size; j++) + EXPECT_EQ(data[j], stream_data[j]) << " at byte " << j; + } + + void CheckCompositeFontWidths(CPDF_Array* widths_array, + CPDF_Font* typed_font) { + // Check that W array is in a format that conforms to PDF spec 1.7 section + // "Glyph Metrics in CIDFonts" (these checks are not + // implementation-specific). + EXPECT_GT(widths_array->GetCount(), 1U); + int num_cids_checked = 0; + int cur_cid = 0; + for (size_t idx = 0; idx < widths_array->GetCount(); idx++) { + int cid = widths_array->GetNumberAt(idx); + EXPECT_GE(cid, cur_cid); + ASSERT_FALSE(++idx == widths_array->GetCount()); + CPDF_Object* next = widths_array->GetObjectAt(idx); + if (next->IsArray()) { + // We are in the c [w1 w2 ...] case + CPDF_Array* arr = next->AsArray(); + int cnt = static_cast(arr->GetCount()); + size_t inner_idx = 0; + for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) { + int width = arr->GetNumberAt(inner_idx++); + EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid " + << cur_cid; + } + num_cids_checked += cnt; + continue; + } + // Otherwise, are in the c_first c_last w case. + ASSERT_TRUE(next->IsNumber()); + int last_cid = next->AsNumber()->GetInteger(); + ASSERT_FALSE(++idx == widths_array->GetCount()); + int width = widths_array->GetNumberAt(idx); + for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) { + EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid " + << cur_cid; + } + num_cids_checked += last_cid - cid + 1; + } + // Make sure we have a good amount of cids described + EXPECT_GT(num_cids_checked, 900); + } + CPDF_Document* cpdf_doc() { return cpdf_doc_; } + + private: + CPDF_Document* cpdf_doc_; +}; namespace { @@ -157,8 +258,7 @@ TEST_F(FPDFEditEmbeddertest, RasterizePDF) { TEST_F(FPDFEditEmbeddertest, AddPaths) { // Start with a blank page - FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); - FPDF_PAGE page = FPDFPage_New(doc, 0, 612, 792); + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); // We will first add a red rectangle FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20); @@ -216,9 +316,8 @@ TEST_F(FPDFEditEmbeddertest, AddPaths) { FPDFBitmap_Destroy(page_bitmap); // Now save the result, closing the page and document - EXPECT_TRUE(FPDF_SaveAsCopy(doc, this, 0)); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); FPDF_ClosePage(page); - FPDF_CloseDocument(doc); std::string new_file = GetString(); // Render the saved result @@ -276,8 +375,7 @@ TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) { TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) { // Start with a blank page - FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); - FPDF_PAGE page = FPDFPage_New(doc, 0, 612, 792); + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); // Add a large stroked rectangle (fill color should not affect it). FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400); @@ -322,16 +420,14 @@ TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) { CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b"); FPDFBitmap_Destroy(page_bitmap); FPDF_ClosePage(page); - FPDF_CloseDocument(doc); } TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { // Start with a blank page - FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); - FPDF_PAGE page = FPDFPage_New(doc, 0, 612, 792); + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); // Add some text to the page - FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(doc, "Arial", 12.0f); + FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); EXPECT_TRUE(text1); EXPECT_TRUE(FPDFText_SetText(text1, "I'm at the bottom of the page")); FPDFPageObj_Transform(text1, 1, 0, 0, 1, 20, 20); @@ -348,7 +444,7 @@ TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { // Try another font FPDF_PAGEOBJECT text2 = - FPDFPageObj_NewTextObj(doc, "TimesNewRomanBold", 15.0f); + FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f); EXPECT_TRUE(text2); EXPECT_TRUE(FPDFText_SetText(text2, "Hi, I'm Bold. Times New Roman Bold.")); FPDFPageObj_Transform(text2, 1, 0, 0, 1, 100, 600); @@ -364,7 +460,8 @@ TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { FPDFBitmap_Destroy(page_bitmap); // And some randomly transformed text - FPDF_PAGEOBJECT text3 = FPDFPageObj_NewTextObj(doc, "Courier-Bold", 20.0f); + FPDF_PAGEOBJECT text3 = + FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f); EXPECT_TRUE(text3); EXPECT_TRUE(FPDFText_SetText(text3, "Can you read me? <:)>")); FPDFPageObj_Transform(text3, 1, 1.5, 2, 0.5, 200, 200); @@ -382,13 +479,11 @@ TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { // TODO(npm): Why are there issues with text rotated by 90 degrees? // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this. FPDF_ClosePage(page); - FPDF_CloseDocument(doc); } TEST_F(FPDFEditEmbeddertest, DoubleGenerating) { // Start with a blank page - FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); - FPDF_PAGE page = FPDFPage_New(doc, 0, 612, 792); + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); // Add a red rectangle with some non-default alpha FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100); @@ -427,7 +522,7 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) { FPDFBitmap_Destroy(page_bitmap); // Add some text to the page - FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(doc, "Arial", 12.0f); + FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); EXPECT_TRUE(FPDFText_SetText(text, "Something something #text# something")); FPDFPageObj_Transform(text, 1, 0, 0, 1, 300, 300); FPDFPage_InsertObject(page, text); @@ -441,65 +536,161 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) { EXPECT_EQ(2, static_cast(graphics_dict->GetCount())); EXPECT_EQ(1, static_cast(font_dict->GetCount())); FPDF_ClosePage(page); - FPDF_CloseDocument(doc); } -TEST_F(FPDFEditEmbeddertest, Type1Font) { - // Create a new document - FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); - CPDF_Document* document = reinterpret_cast(doc); - - // Get Times New Roman Bold as a Type 1 font - CPDF_Font* times_bold = CPDF_Font::GetStockFont(document, "Times-Bold"); - uint8_t* data = times_bold->m_Font.GetFontData(); - uint32_t size = times_bold->m_Font.GetSize(); - FPDF_FONT font = FPDFText_LoadType1Font(doc, data, size); +TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) { + CreateNewDocument(); + // TODO(npm): use other fonts after disallowing loading any font as any type + const CPDF_Font* stock_font = + CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold"); + const uint8_t* data = stock_font->m_Font.GetFontData(); + const uint32_t size = stock_font->m_Font.GetSize(); + FPDF_FONT font = + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false); ASSERT_TRUE(font); - CPDF_Font* type1_font = reinterpret_cast(font); - EXPECT_TRUE(type1_font->IsType1Font()); + CPDF_Font* typed_font = reinterpret_cast(font); + EXPECT_TRUE(typed_font->IsType1Font()); - // Check that the font dictionary has the required keys according to the spec - CPDF_Dictionary* font_dict = type1_font->GetFontDict(); + CPDF_Dictionary* font_dict = typed_font->GetFontDict(); EXPECT_EQ("Font", font_dict->GetStringFor("Type")); EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype")); EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont")); ASSERT_TRUE(font_dict->KeyExist("FirstChar")); ASSERT_TRUE(font_dict->KeyExist("LastChar")); EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar")); - EXPECT_EQ(65532, font_dict->GetIntegerFor("LastChar")); - ASSERT_TRUE(font_dict->KeyExist("Widths")); + EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar")); + CPDF_Array* widths_array = font_dict->GetArrayFor("Widths"); - EXPECT_EQ(65501U, widths_array->GetCount()); + ASSERT_TRUE(widths_array); + ASSERT_EQ(224U, widths_array->GetCount()); EXPECT_EQ(250, widths_array->GetNumberAt(0)); - EXPECT_EQ(0, widths_array->GetNumberAt(8172)); - EXPECT_EQ(1000, widths_array->GetNumberAt(65500)); - ASSERT_TRUE(font_dict->KeyExist("FontDescriptor")); - CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor"); - EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type")); - EXPECT_EQ(font_dict->GetStringFor("BaseFont"), - font_desc->GetStringFor("FontName")); - - // Check that the font descriptor has the required keys according to the spec - ASSERT_TRUE(font_desc->KeyExist("Flags")); - int font_flags = font_desc->GetIntegerFor("Flags"); - EXPECT_TRUE(font_flags & FXFONT_BOLD); - EXPECT_TRUE(font_flags & FXFONT_NONSYMBOLIC); - ASSERT_TRUE(font_desc->KeyExist("FontBBox")); - EXPECT_EQ(4U, font_desc->GetArrayFor("FontBBox")->GetCount()); - EXPECT_TRUE(font_desc->KeyExist("ItalicAngle")); - EXPECT_TRUE(font_desc->KeyExist("Ascent")); - EXPECT_TRUE(font_desc->KeyExist("Descent")); - EXPECT_TRUE(font_desc->KeyExist("CapHeight")); - EXPECT_TRUE(font_desc->KeyExist("StemV")); - ASSERT_TRUE(font_desc->KeyExist("FontFile")); - - // Check that the font stream is the one that was provided - CPDF_Stream* font_stream = font_desc->GetStreamFor("FontFile"); - ASSERT_EQ(size, font_stream->GetRawSize()); - uint8_t* stream_data = font_stream->GetRawData(); - for (size_t i = 0; i < size; i++) - EXPECT_EQ(data[i], stream_data[i]); - - // Close document - FPDF_CloseDocument(doc); + EXPECT_EQ(569, widths_array->GetNumberAt(11)); + EXPECT_EQ(500, widths_array->GetNumberAt(223)); + CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data); +} + +TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) { + CreateNewDocument(); + const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier"); + const uint8_t* data = stock_font->m_Font.GetFontData(); + const uint32_t size = stock_font->m_Font.GetSize(); + FPDF_FONT font = + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false); + ASSERT_TRUE(font); + CPDF_Font* typed_font = reinterpret_cast(font); + EXPECT_TRUE(typed_font->IsTrueTypeFont()); + + CPDF_Dictionary* font_dict = typed_font->GetFontDict(); + EXPECT_EQ("Font", font_dict->GetStringFor("Type")); + EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype")); + EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont")); + ASSERT_TRUE(font_dict->KeyExist("FirstChar")); + ASSERT_TRUE(font_dict->KeyExist("LastChar")); + EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar")); + EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar")); + + CPDF_Array* widths_array = font_dict->GetArrayFor("Widths"); + ASSERT_TRUE(widths_array); + ASSERT_EQ(224U, widths_array->GetCount()); + EXPECT_EQ(600, widths_array->GetNumberAt(33)); + EXPECT_EQ(600, widths_array->GetNumberAt(74)); + EXPECT_EQ(600, widths_array->GetNumberAt(223)); + CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data); +} + +TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) { + CreateNewDocument(); + const CPDF_Font* stock_font = + CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman"); + const uint8_t* data = stock_font->m_Font.GetFontData(); + const uint32_t size = stock_font->m_Font.GetSize(); + FPDF_FONT font = + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1); + ASSERT_TRUE(font); + CPDF_Font* typed_font = reinterpret_cast(font); + EXPECT_TRUE(typed_font->IsCIDFont()); + + // Check font dictionary entries + CPDF_Dictionary* font_dict = typed_font->GetFontDict(); + EXPECT_EQ("Font", font_dict->GetStringFor("Type")); + EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype")); + EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont")); + EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding")); + CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts"); + ASSERT_TRUE(descendant_array); + EXPECT_EQ(1U, descendant_array->GetCount()); + + // Check the CIDFontDict + CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0); + EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type")); + EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype")); + EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont")); + CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo"); + ASSERT_TRUE(cidinfo_dict); + EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry")); + EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering")); + EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement")); + CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data); + + // Check widths + CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W"); + ASSERT_TRUE(widths_array); + // Note: widths can be described in different ways in the widths array. The + // following checks are specific to our current implementation. + EXPECT_EQ(32, widths_array->GetNumberAt(0)); + CPDF_Array* arr = widths_array->GetArrayAt(1); + ASSERT_TRUE(arr); + // This font support chars 32 to 126 + EXPECT_EQ(95U, arr->GetCount()); + EXPECT_EQ(250, arr->GetNumberAt(0)); + EXPECT_EQ(610, arr->GetNumberAt(44)); + EXPECT_EQ(541, arr->GetNumberAt(94)); + // Next range: 160 - 383 + EXPECT_EQ(160, widths_array->GetNumberAt(2)); + arr = widths_array->GetArrayAt(3); + ASSERT_TRUE(arr); + + CheckCompositeFontWidths(widths_array, typed_font); +} + +TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) { + CreateNewDocument(); + const CPDF_Font* stock_font = + CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique"); + const uint8_t* data = stock_font->m_Font.GetFontData(); + const uint32_t size = stock_font->m_Font.GetSize(); + + FPDF_FONT font = + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1); + ASSERT_TRUE(font); + CPDF_Font* typed_font = reinterpret_cast(font); + EXPECT_TRUE(typed_font->IsCIDFont()); + + // Check font dictionary entries + CPDF_Dictionary* font_dict = typed_font->GetFontDict(); + EXPECT_EQ("Font", font_dict->GetStringFor("Type")); + EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype")); + EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont")); + EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding")); + CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts"); + ASSERT_TRUE(descendant_array); + EXPECT_EQ(1U, descendant_array->GetCount()); + + // Check the CIDFontDict + CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0); + EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type")); + EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype")); + EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont")); + CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo"); + ASSERT_TRUE(cidinfo_dict); + EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry")); + EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering")); + EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement")); + CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size, + data); + + // Check widths + CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W"); + ASSERT_TRUE(widths_array); + CheckCompositeFontWidths(widths_array, typed_font); } diff --git a/fpdfsdk/fpdfedittext.cpp b/fpdfsdk/fpdfedittext.cpp index 8bf0a0ac46..5ce4be65a1 100644 --- a/fpdfsdk/fpdfedittext.cpp +++ b/fpdfsdk/fpdfedittext.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include "core/fpdfapi/cpdf_modulemgr.h" @@ -20,87 +21,28 @@ #include "fpdfsdk/fsdk_define.h" #include "public/fpdf_edit.h" -DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, - FPDF_BYTESTRING font, - float font_size) { - CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); - if (!pDoc) - return nullptr; - - CPDF_Font* pFont = CPDF_Font::GetStockFont(pDoc, CFX_ByteStringC(font)); - if (!pFont) - return nullptr; +namespace { - CPDF_TextObject* pTextObj = new CPDF_TextObject; - pTextObj->m_TextState.SetFont(pFont); - pTextObj->m_TextState.SetFontSize(font_size); - pTextObj->DefaultStates(); - return pTextObj; -} - -DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetText(FPDF_PAGEOBJECT text_object, - FPDF_BYTESTRING text) { - if (!text_object) - return false; - - auto pTextObj = reinterpret_cast(text_object); - pTextObj->SetText(CFX_ByteString(text)); - return true; -} - -DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadType1Font(FPDF_DOCUMENT document, - const uint8_t* data, - uint32_t size) { - CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); - if (!pDoc || !data || size == 0) - return nullptr; - - auto pFont = pdfium::MakeUnique(); - - // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format? - if (!pFont->LoadEmbedded(data, size)) - return nullptr; - - CPDF_Dictionary* fontDict = pDoc->NewIndirect(); - fontDict->SetNewFor("Type", "Font"); - fontDict->SetNewFor("Subtype", "Type1"); - CFX_ByteString name = pFont->GetFaceName(); - if (name.IsEmpty()) - name = "Unnamed"; - fontDict->SetNewFor("BaseFont", name); - - uint32_t glyphIndex; - int currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex); - fontDict->SetNewFor("FirstChar", currentChar); - int nextChar; - CPDF_Array* widthsArray = pDoc->NewIndirect(); - while (true) { - int width = pFont->GetGlyphWidth(glyphIndex); - widthsArray->AddNew(width); - nextChar = FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex); - if (glyphIndex == 0) - break; - for (int i = currentChar + 1; i < nextChar; i++) - widthsArray->AddNew(0); - currentChar = nextChar; - } - fontDict->SetNewFor("LastChar", currentChar); - fontDict->SetNewFor("Widths", pDoc, widthsArray->GetObjNum()); +CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc, + const CFX_ByteString& font_name, + CFX_Font* pFont, + const uint8_t* data, + uint32_t size, + int font_type) { CPDF_Dictionary* fontDesc = pDoc->NewIndirect(); fontDesc->SetNewFor("Type", "FontDescriptor"); - fontDesc->SetNewFor("FontName", name); + fontDesc->SetNewFor("FontName", font_name); int flags = 0; if (FXFT_Is_Face_fixedwidth(pFont->GetFace())) flags |= FXFONT_FIXED_PITCH; - if (name.Find("Serif") > -1) + if (font_name.Find("Serif") > -1) flags |= FXFONT_SERIF; if (FXFT_Is_Face_Italic(pFont->GetFace())) flags |= FXFONT_ITALIC; if (FXFT_Is_Face_Bold(pFont->GetFace())) flags |= FXFONT_BOLD; - // TODO(npm): How do I know if a Type1 font is symbolic, script, allcap, - // smallcap + // TODO(npm): How do I know if a font is symbolic, script, allcap, smallcap flags |= FXFONT_NONSYMBOLIC; fontDesc->SetNewFor("Flags", flags); @@ -125,8 +67,203 @@ DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadType1Font(FPDF_DOCUMENT document, CPDF_Stream* pStream = pDoc->NewIndirect(); pStream->SetData(data, size); - fontDesc->SetNewFor("FontFile", pDoc, pStream->GetObjNum()); + CFX_ByteString fontFile = + font_type == FPDF_FONT_TYPE1 ? "FontFile" : "FontFile2"; + fontDesc->SetNewFor(fontFile, pDoc, pStream->GetObjNum()); + return fontDesc; +} + +void* LoadSimpleFont(CPDF_Document* pDoc, + std::unique_ptr pFont, + const uint8_t* data, + uint32_t size, + int font_type) { + CPDF_Dictionary* fontDict = pDoc->NewIndirect(); + fontDict->SetNewFor("Type", "Font"); + fontDict->SetNewFor( + "Subtype", font_type == FPDF_FONT_TYPE1 ? "Type1" : "TrueType"); + CFX_ByteString name = pFont->GetFaceName(); + if (name.IsEmpty()) + name = "Unnamed"; + fontDict->SetNewFor("BaseFont", name); + + uint32_t glyphIndex; + int currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex); + fontDict->SetNewFor("FirstChar", currentChar); + CPDF_Array* widthsArray = pDoc->NewIndirect(); + while (true) { + int width = pFont->GetGlyphWidth(glyphIndex); + widthsArray->AddNew(width); + int nextChar = + FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex); + // Simple fonts have 1-byte charcodes only. + if (nextChar > 0xff || glyphIndex == 0) + break; + for (int i = currentChar + 1; i < nextChar; i++) + widthsArray->AddNew(0); + currentChar = nextChar; + } + fontDict->SetNewFor("LastChar", currentChar); + fontDict->SetNewFor("Widths", pDoc, widthsArray->GetObjNum()); + CPDF_Dictionary* fontDesc = + LoadFontDesc(pDoc, name, pFont.get(), data, size, font_type); + fontDict->SetNewFor("FontDescriptor", pDoc, fontDesc->GetObjNum()); return pDoc->LoadFont(fontDict); } + +void* LoadCompositeFont(CPDF_Document* pDoc, + std::unique_ptr pFont, + const uint8_t* data, + uint32_t size, + int font_type) { + CPDF_Dictionary* fontDict = pDoc->NewIndirect(); + fontDict->SetNewFor("Type", "Font"); + fontDict->SetNewFor("Subtype", "Type0"); + // TODO(npm): Get the correct encoding, if it's not identity. + CFX_ByteString encoding = "Identity-H"; + fontDict->SetNewFor("Encoding", encoding); + CFX_ByteString name = pFont->GetFaceName(); + if (name.IsEmpty()) + name = "Unnamed"; + fontDict->SetNewFor( + "BaseFont", font_type == FPDF_FONT_TYPE1 ? name + "-" + encoding : name); + + CPDF_Dictionary* pCIDFont = pDoc->NewIndirect(); + pCIDFont->SetNewFor("Type", "Font"); + pCIDFont->SetNewFor("Subtype", font_type == FPDF_FONT_TYPE1 + ? "CIDFontType0" + : "CIDFontType2"); + pCIDFont->SetNewFor("BaseFont", name); + + // TODO(npm): Maybe use FT_Get_CID_Registry_Ordering_Supplement to get the + // CIDSystemInfo + CPDF_Dictionary* pCIDSystemInfo = pDoc->NewIndirect(); + pCIDSystemInfo->SetNewFor("Registry", "Adobe"); + pCIDSystemInfo->SetNewFor("Ordering", "Identity"); + pCIDSystemInfo->SetNewFor("Supplement", 0); + pCIDFont->SetNewFor("CIDSystemInfo", pDoc, + pCIDSystemInfo->GetObjNum()); + + CPDF_Dictionary* fontDesc = + LoadFontDesc(pDoc, name, pFont.get(), data, size, font_type); + pCIDFont->SetNewFor("FontDescriptor", pDoc, + fontDesc->GetObjNum()); + + uint32_t glyphIndex; + int currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex); + CPDF_Array* widthsArray = pDoc->NewIndirect(); + while (true) { + int width = pFont->GetGlyphWidth(glyphIndex); + int nextChar = + FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex); + if (glyphIndex == 0) { + // Only one char left, use format c [w] + auto oneW = pdfium::MakeUnique(); + oneW->AddNew(width); + widthsArray->AddNew(currentChar); + widthsArray->Add(std::move(oneW)); + break; + } + int nextWidth = pFont->GetGlyphWidth(glyphIndex); + if (nextChar == currentChar + 1 && nextWidth == width) { + // The array can have a group c_first c_last w: all CIDs in the range from + // c_first to c_last will have width w + widthsArray->AddNew(currentChar); + currentChar = nextChar; + while (true) { + nextChar = + FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex); + if (glyphIndex == 0) + break; + nextWidth = pFont->GetGlyphWidth(glyphIndex); + if (nextChar != currentChar + 1 || nextWidth != width) + break; + currentChar = nextChar; + } + widthsArray->AddNew(currentChar); + widthsArray->AddNew(width); + } else { + // Otherwise we can have a group of the form c [w1 w2 ...]: c has width + // w1, c+1 has width w2, etc. + widthsArray->AddNew(currentChar); + auto curWidthArray = pdfium::MakeUnique(); + curWidthArray->AddNew(width); + while (nextChar == currentChar + 1) { + curWidthArray->AddNew(nextWidth); + currentChar = nextChar; + nextChar = + FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex); + if (glyphIndex == 0) + break; + nextWidth = pFont->GetGlyphWidth(glyphIndex); + } + widthsArray->Add(std::move(curWidthArray)); + } + if (glyphIndex == 0) + break; + currentChar = nextChar; + } + pCIDFont->SetNewFor("W", pDoc, widthsArray->GetObjNum()); + // TODO(npm): Support vertical writing + + auto pDescendant = pdfium::MakeUnique(); + pDescendant->AddNew(pDoc, pCIDFont->GetObjNum()); + fontDict->SetFor("DescendantFonts", std::move(pDescendant)); + // TODO(npm): do we need a ToUnicode? + return pDoc->LoadFont(fontDict); +} + +} // namespace + +DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, + FPDF_BYTESTRING font, + float font_size) { + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); + if (!pDoc) + return nullptr; + + CPDF_Font* pFont = CPDF_Font::GetStockFont(pDoc, CFX_ByteStringC(font)); + if (!pFont) + return nullptr; + + CPDF_TextObject* pTextObj = new CPDF_TextObject; + pTextObj->m_TextState.SetFont(pFont); + pTextObj->m_TextState.SetFontSize(font_size); + pTextObj->DefaultStates(); + return pTextObj; +} + +DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetText(FPDF_PAGEOBJECT text_object, + FPDF_BYTESTRING text) { + if (!text_object) + return false; + + auto pTextObj = reinterpret_cast(text_object); + pTextObj->SetText(CFX_ByteString(text)); + return true; +} + +DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadFont(FPDF_DOCUMENT document, + const uint8_t* data, + uint32_t size, + int font_type, + FPDF_BOOL cid) { + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); + if (!pDoc || !data || size == 0 || + (font_type != FPDF_FONT_TYPE1 && font_type != FPDF_FONT_TRUETYPE)) { + return nullptr; + } + + auto pFont = pdfium::MakeUnique(); + + // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format? Otherwise, we + // are allowing giving any font that can be loaded on freetype and setting it + // as any font type. + if (!pFont->LoadEmbedded(data, size)) + return nullptr; + + return cid ? LoadCompositeFont(pDoc, std::move(pFont), data, size, font_type) + : LoadSimpleFont(pDoc, std::move(pFont), data, size, font_type); +} diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c index 54a33252b3..c603f59acc 100644 --- a/fpdfsdk/fpdfview_c_api_test.c +++ b/fpdfsdk/fpdfview_c_api_test.c @@ -98,7 +98,7 @@ int CheckPDFiumCApi() { CHK(FPDFPath_SetDrawMode); CHK(FPDFPageObj_NewTextObj); CHK(FPDFText_SetText); - CHK(FPDFText_LoadType1Font); + CHK(FPDFText_LoadFont); // fpdf_ext.h CHK(FSDK_SetUnSpObjProcessHandler); diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h index 3350b5c6a7..423229ab33 100644 --- a/public/fpdf_edit.h +++ b/public/fpdf_edit.h @@ -30,6 +30,9 @@ #define FPDF_FILLMODE_ALTERNATE 1 #define FPDF_FILLMODE_WINDING 2 +#define FPDF_FONT_TYPE1 1 +#define FPDF_FONT_TRUETYPE 2 + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -406,17 +409,22 @@ DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetText(FPDF_PAGEOBJECT text_object, FPDF_BYTESTRING text); -// Returns a type 1 font object loaded from a stream of data. The font is loaded +// Returns a font object loaded from a stream of data. The font is loaded // into the document. The caller does not need to free the returned object. // -// document - handle to the document. -// data - the stream of data, which will be copied by the font object. -// size - size of the stream, in bytes. +// document - handle to the document. +// data - the stream of data, which will be copied by the font object. +// size - size of the stream, in bytes. +// font_type - FPDF_FONT_TYPE1 or FPDF_FONT_TRUETYPE depending on the font +// type. +// cid - a boolean specifying if the font is a CID font or not. // // Returns NULL on failure -DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadType1Font(FPDF_DOCUMENT document, - const uint8_t* data, - uint32_t size); +DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadFont(FPDF_DOCUMENT document, + const uint8_t* data, + uint32_t size, + int font_type, + FPDF_BOOL cid); #ifdef __cplusplus } // extern "C" -- cgit v1.2.3