From b31618571938e4873dcf1cdd44eeedb40caa5bd7 Mon Sep 17 00:00:00 2001 From: Nicolas Pena Date: Tue, 2 May 2017 14:12:50 -0400 Subject: Add API to create a text object using a loaded font. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is already a method to add text from standard font, this CL adds an option to add text using a loaded font. The font set into a text object is ref counted and may be released, so call LoadFont on this new text obj, and add a method to close the font. This CL also improves the SetText method so that it now uses a WideString, in preparation for CID fonts with non-Latin characters. Bug: pdfium:667 Change-Id: I6829d702357d2a898a12f5297e4fd2ec993a9891 Reviewed-on: https://pdfium-review.googlesource.com/4770 Reviewed-by: Tom Sepez Commit-Queue: Nicolás Peña --- fpdfsdk/fpdfedit_embeddertest.cpp | 152 +++++++++++++++++++++++++++++--------- fpdfsdk/fpdfedittext.cpp | 41 +++++++++- fpdfsdk/fpdfview_c_api_test.c | 2 + public/cpp/fpdf_deleters.h | 5 ++ public/fpdf_edit.h | 23 +++++- 5 files changed, 184 insertions(+), 39 deletions(-) diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp index d62f5ca85c..4bf68cfe5e 100644 --- a/fpdfsdk/fpdfedit_embeddertest.cpp +++ b/fpdfsdk/fpdfedit_embeddertest.cpp @@ -14,6 +14,7 @@ #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fxcrt/fx_system.h" #include "fpdfsdk/fsdk_define.h" +#include "public/cpp/fpdf_deleters.h" #include "public/fpdf_edit.h" #include "public/fpdfview.h" #include "testing/embedder_test.h" @@ -423,11 +424,14 @@ TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); // Add some text to the page - 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); - FPDFPage_InsertObject(page, text1); + FPDF_PAGEOBJECT text_object1 = + FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); + EXPECT_TRUE(text_object1); + std::unique_ptr text1 = + GetFPDFWideString(L"I'm at the bottom of the page"); + EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get())); + FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20); + FPDFPage_InsertObject(page, text_object1); EXPECT_TRUE(FPDFPage_GenerateContent(page)); FPDF_BITMAP page_bitmap = RenderPage(page); #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ @@ -439,12 +443,14 @@ TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { FPDFBitmap_Destroy(page_bitmap); // Try another font - FPDF_PAGEOBJECT text2 = + FPDF_PAGEOBJECT text_object2 = 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); - FPDFPage_InsertObject(page, text2); + EXPECT_TRUE(text_object2); + std::unique_ptr text2 = + GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold."); + EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get())); + FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600); + FPDFPage_InsertObject(page, text_object2); EXPECT_TRUE(FPDFPage_GenerateContent(page)); page_bitmap = RenderPage(page); #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ @@ -458,12 +464,14 @@ TEST_F(FPDFEditEmbeddertest, AddStandardFontText) { FPDFBitmap_Destroy(page_bitmap); // And some randomly transformed text - FPDF_PAGEOBJECT text3 = + FPDF_PAGEOBJECT text_object3 = 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); - FPDFPage_InsertObject(page, text3); + EXPECT_TRUE(text_object3); + std::unique_ptr text3 = + GetFPDFWideString(L"Can you read me? <:)>"); + EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get())); + FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200); + FPDFPage_InsertObject(page, text_object3); EXPECT_TRUE(FPDFPage_GenerateContent(page)); page_bitmap = RenderPage(page); #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ @@ -522,10 +530,13 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) { FPDFBitmap_Destroy(page_bitmap); // Add some text to the page - 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); + FPDF_PAGEOBJECT text_object = + FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); + std::unique_ptr text = + GetFPDFWideString(L"Something something #text# something"); + EXPECT_TRUE(FPDFText_SetText(text_object, text.get())); + FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300); + FPDFPage_InsertObject(page, text_object); EXPECT_TRUE(FPDFPage_GenerateContent(page)); CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font"); ASSERT_TRUE(font_dict); @@ -545,10 +556,10 @@ TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) { 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* typed_font = reinterpret_cast(font); + std::unique_ptr font( + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false)); + ASSERT_TRUE(font.get()); + CPDF_Font* typed_font = reinterpret_cast(font.get()); EXPECT_TRUE(typed_font->IsType1Font()); CPDF_Dictionary* font_dict = typed_font->GetFontDict(); @@ -574,10 +585,10 @@ TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) { 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); + std::unique_ptr font( + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false)); + ASSERT_TRUE(font.get()); + CPDF_Font* typed_font = reinterpret_cast(font.get()); EXPECT_TRUE(typed_font->IsTrueTypeFont()); CPDF_Dictionary* font_dict = typed_font->GetFontDict(); @@ -604,10 +615,10 @@ TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) { 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); + std::unique_ptr font( + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1)); + ASSERT_TRUE(font.get()); + CPDF_Font* typed_font = reinterpret_cast(font.get()); EXPECT_TRUE(typed_font->IsCIDFont()); // Check font dictionary entries @@ -660,10 +671,10 @@ TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) { 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); + std::unique_ptr font( + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1)); + ASSERT_TRUE(font.get()); + CPDF_Font* typed_font = reinterpret_cast(font.get()); EXPECT_TRUE(typed_font->IsCIDFont()); // Check font dictionary entries @@ -704,3 +715,74 @@ TEST_F(FPDFEditEmbeddertest, NormalizeNegativeRotation) { EXPECT_EQ(3, FPDFPage_GetRotation(page)); UnloadPage(page); } + +TEST_F(FPDFEditEmbeddertest, AddTrueTypeFontText) { + // Start with a blank page + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + { + const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Arial"); + const uint8_t* data = stock_font->m_Font.GetFontData(); + const uint32_t size = stock_font->m_Font.GetSize(); + std::unique_ptr font( + FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 0)); + ASSERT_TRUE(font.get()); + + // Add some text to the page + FPDF_PAGEOBJECT text_object = + FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f); + EXPECT_TRUE(text_object); + std::unique_ptr text = + GetFPDFWideString(L"I am testing my loaded font, WEE."); + EXPECT_TRUE(FPDFText_SetText(text_object, text.get())); + FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400); + FPDFPage_InsertObject(page, text_object); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + FPDF_BITMAP page_bitmap = RenderPage(page); +#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ + const char md5[] = "17d2b6cd574cf66170b09c8927529a94"; +#else + const char md5[] = "28e5b10743660dcdfd1618db47b39d32"; +#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ + CompareBitmap(page_bitmap, 612, 792, md5); + FPDFBitmap_Destroy(page_bitmap); + + // Add some more text, same font + FPDF_PAGEOBJECT text_object2 = + FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f); + std::unique_ptr text2 = + GetFPDFWideString(L"Bigger font size"); + EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get())); + FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200); + FPDFPage_InsertObject(page, text_object2); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + } + FPDF_BITMAP page_bitmap2 = RenderPage(page); +#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ + const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea"; +#else + const char md5_2[] = "a068eef4110d607f77c87ea8340fa2a5"; +#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ + CompareBitmap(page_bitmap2, 612, 792, md5_2); + FPDFBitmap_Destroy(page_bitmap2); + + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + FPDF_ClosePage(page); + std::string new_file = GetString(); + + // Render the saved result + FPDF_FILEACCESS file_access; + memset(&file_access, 0, sizeof(file_access)); + file_access.m_FileLen = new_file.size(); + file_access.m_GetBlock = GetBlockFromString; + file_access.m_Param = &new_file; + FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr); + ASSERT_NE(nullptr, new_doc); + EXPECT_EQ(1, FPDF_GetPageCount(new_doc)); + FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0); + ASSERT_NE(nullptr, new_page); + FPDF_BITMAP new_bitmap = RenderPage(new_page); + CompareBitmap(new_bitmap, 612, 792, md5_2); + FPDFBitmap_Destroy(new_bitmap); + FPDF_ClosePage(new_page); + FPDF_CloseDocument(new_doc); +} diff --git a/fpdfsdk/fpdfedittext.cpp b/fpdfsdk/fpdfedittext.cpp index aec6050d21..f4e1d66bc1 100644 --- a/fpdfsdk/fpdfedittext.cpp +++ b/fpdfsdk/fpdfedittext.cpp @@ -8,6 +8,7 @@ #include "core/fpdfapi/cpdf_modulemgr.h" #include "core/fpdfapi/font/cpdf_font.h" #include "core/fpdfapi/font/cpdf_type1font.h" +#include "core/fpdfapi/page/cpdf_docpagedata.h" #include "core/fpdfapi/page/cpdf_textobject.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" @@ -236,12 +237,20 @@ DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, } DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetText(FPDF_PAGEOBJECT text_object, - FPDF_BYTESTRING text) { + FPDF_WIDESTRING text) { if (!text_object) return false; auto* pTextObj = reinterpret_cast(text_object); - pTextObj->SetText(CFX_ByteString(text)); + FX_STRSIZE len = CFX_WideString::WStringLength(text); + CFX_WideString encodedText = CFX_WideString::FromUTF16LE(text, len); + CFX_ByteString byteText; + for (int i = 0; i < encodedText.GetLength(); ++i) { + uint32_t charcode = + pTextObj->GetFont()->CharCodeFromUnicode(encodedText[i]); + pTextObj->GetFont()->AppendChar(&byteText, charcode); + } + pTextObj->SetText(byteText); return true; } @@ -267,3 +276,31 @@ DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadFont(FPDF_DOCUMENT document, return cid ? LoadCompositeFont(pDoc, std::move(pFont), data, size, font_type) : LoadSimpleFont(pDoc, std::move(pFont), data, size, font_type); } + +DLLEXPORT void STDCALL FPDFFont_Close(FPDF_FONT font) { + if (!font) + return; + + CPDF_Font* cpdf_font = reinterpret_cast(font); + CPDF_Document* pDoc = cpdf_font->m_pDocument; + CPDF_DocPageData* pPageData = pDoc ? pDoc->GetPageData() : nullptr; + if (pPageData && !pPageData->IsForceClear()) + pPageData->ReleaseFont(cpdf_font->GetFontDict()); +} + +DLLEXPORT FPDF_PAGEOBJECT STDCALL +FPDFPageObj_CreateTextObj(FPDF_DOCUMENT document, + FPDF_FONT font, + float font_size) { + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); + if (!pDoc || !font) + return nullptr; + + CPDF_Font* pFont = reinterpret_cast(font); + + auto pTextObj = pdfium::MakeUnique(); + pTextObj->m_TextState.SetFont(pDoc->LoadFont(pFont->GetFontDict())); + pTextObj->m_TextState.SetFontSize(font_size); + pTextObj->DefaultStates(); + return pTextObj.release(); +} diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c index b4da951947..1606abbf08 100644 --- a/fpdfsdk/fpdfview_c_api_test.c +++ b/fpdfsdk/fpdfview_c_api_test.c @@ -102,6 +102,8 @@ int CheckPDFiumCApi() { CHK(FPDFPageObj_NewTextObj); CHK(FPDFText_SetText); CHK(FPDFText_LoadFont); + CHK(FPDFFont_Close); + CHK(FPDFPageObj_CreateTextObj); // fpdf_ext.h CHK(FSDK_SetUnSpObjProcessHandler); diff --git a/public/cpp/fpdf_deleters.h b/public/cpp/fpdf_deleters.h index d56daf5c55..238ef30e62 100644 --- a/public/cpp/fpdf_deleters.h +++ b/public/cpp/fpdf_deleters.h @@ -6,6 +6,7 @@ #define PUBLIC_CPP_FPDF_DELETERS_H_ #include "public/fpdf_dataavail.h" +#include "public/fpdf_edit.h" #include "public/fpdf_formfill.h" #include "public/fpdf_structtree.h" #include "public/fpdf_text.h" @@ -43,4 +44,8 @@ struct FPDFStructTreeDeleter { inline void operator()(FPDF_STRUCTTREE tree) { FPDF_StructTree_Close(tree); } }; +struct FPDFFontDeleter { + inline void operator()(FPDF_FONT font) { FPDFFont_Close(font); } +}; + #endif // PUBLIC_CPP_FPDF_DELETERS_H_ diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h index 4d06c2a0c8..5784b90feb 100644 --- a/public/fpdf_edit.h +++ b/public/fpdf_edit.h @@ -436,11 +436,11 @@ DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, // Set the text for a textobject. If it had text, it will be replaced. // // text_object - handle to the text object. -// text - string containing the text to be added. +// text - the UTF-16LE encoded string containing the text to be added. // // Returns TRUE on success DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetText(FPDF_PAGEOBJECT text_object, - FPDF_BYTESTRING text); + FPDF_WIDESTRING text); // 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. @@ -452,6 +452,8 @@ DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetText(FPDF_PAGEOBJECT text_object, // type. // cid - a boolean specifying if the font is a CID font or not. // +// The loaded font can be closed using FPDF_Font_Close. +// // Returns NULL on failure DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadFont(FPDF_DOCUMENT document, const uint8_t* data, @@ -459,6 +461,23 @@ DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadFont(FPDF_DOCUMENT document, int font_type, FPDF_BOOL cid); +// Close a loaded PDF font. +// +// font - Handle to the loaded font. +DLLEXPORT void STDCALL FPDFFont_Close(FPDF_FONT font); + +// Create a new text object using a loaded font. +// +// document - handle to the document. +// font - handle to the font object. +// font_size - the font size for the new text object. +// +// Returns a handle to a new text object, or NULL on failure +DLLEXPORT FPDF_PAGEOBJECT STDCALL +FPDFPageObj_CreateTextObj(FPDF_DOCUMENT document, + FPDF_FONT font, + float font_size); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus -- cgit v1.2.3