summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fpdfsdk/fpdfedit_embeddertest.cpp323
-rw-r--r--fpdfsdk/fpdfedittext.cpp277
-rw-r--r--fpdfsdk/fpdfview_c_api_test.c2
-rw-r--r--public/fpdf_edit.h22
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 <memory>
#include <string>
+#include <utility>
#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<CPDF_Document*>(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<int>(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<int>(graphics_dict->GetCount()));
EXPECT_EQ(1, static_cast<int>(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<CPDF_Document*>(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<CPDF_Font*>(font);
- EXPECT_TRUE(type1_font->IsType1Font());
+ CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(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<CPDF_Font*>(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<CPDF_Font*>(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<CPDF_Font*>(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 <memory>
#include <utility>
#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<CPDF_TextObject*>(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<CFX_Font>();
-
- // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format?
- if (!pFont->LoadEmbedded(data, size))
- return nullptr;
-
- CPDF_Dictionary* fontDict = pDoc->NewIndirect<CPDF_Dictionary>();
- fontDict->SetNewFor<CPDF_Name>("Type", "Font");
- fontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
- CFX_ByteString name = pFont->GetFaceName();
- if (name.IsEmpty())
- name = "Unnamed";
- fontDict->SetNewFor<CPDF_Name>("BaseFont", name);
-
- uint32_t glyphIndex;
- int currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex);
- fontDict->SetNewFor<CPDF_Number>("FirstChar", currentChar);
- int nextChar;
- CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
- while (true) {
- int width = pFont->GetGlyphWidth(glyphIndex);
- widthsArray->AddNew<CPDF_Number>(width);
- nextChar = FXFT_Get_Next_Char(pFont->GetFace(), currentChar, &glyphIndex);
- if (glyphIndex == 0)
- break;
- for (int i = currentChar + 1; i < nextChar; i++)
- widthsArray->AddNew<CPDF_Number>(0);
- currentChar = nextChar;
- }
- fontDict->SetNewFor<CPDF_Number>("LastChar", currentChar);
- fontDict->SetNewFor<CPDF_Reference>("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<CPDF_Dictionary>();
fontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
- fontDesc->SetNewFor<CPDF_Name>("FontName", name);
+ fontDesc->SetNewFor<CPDF_Name>("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<CPDF_Number>("Flags", flags);
@@ -125,8 +67,203 @@ DLLEXPORT FPDF_FONT STDCALL FPDFText_LoadType1Font(FPDF_DOCUMENT document,
CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>();
pStream->SetData(data, size);
- fontDesc->SetNewFor<CPDF_Reference>("FontFile", pDoc, pStream->GetObjNum());
+ CFX_ByteString fontFile =
+ font_type == FPDF_FONT_TYPE1 ? "FontFile" : "FontFile2";
+ fontDesc->SetNewFor<CPDF_Reference>(fontFile, pDoc, pStream->GetObjNum());
+ return fontDesc;
+}
+
+void* LoadSimpleFont(CPDF_Document* pDoc,
+ std::unique_ptr<CFX_Font> pFont,
+ const uint8_t* data,
+ uint32_t size,
+ int font_type) {
+ CPDF_Dictionary* fontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+ fontDict->SetNewFor<CPDF_Name>("Type", "Font");
+ fontDict->SetNewFor<CPDF_Name>(
+ "Subtype", font_type == FPDF_FONT_TYPE1 ? "Type1" : "TrueType");
+ CFX_ByteString name = pFont->GetFaceName();
+ if (name.IsEmpty())
+ name = "Unnamed";
+ fontDict->SetNewFor<CPDF_Name>("BaseFont", name);
+
+ uint32_t glyphIndex;
+ int currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex);
+ fontDict->SetNewFor<CPDF_Number>("FirstChar", currentChar);
+ CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
+ while (true) {
+ int width = pFont->GetGlyphWidth(glyphIndex);
+ widthsArray->AddNew<CPDF_Number>(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<CPDF_Number>(0);
+ currentChar = nextChar;
+ }
+ fontDict->SetNewFor<CPDF_Number>("LastChar", currentChar);
+ fontDict->SetNewFor<CPDF_Reference>("Widths", pDoc, widthsArray->GetObjNum());
+ CPDF_Dictionary* fontDesc =
+ LoadFontDesc(pDoc, name, pFont.get(), data, size, font_type);
+
fontDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
fontDesc->GetObjNum());
return pDoc->LoadFont(fontDict);
}
+
+void* LoadCompositeFont(CPDF_Document* pDoc,
+ std::unique_ptr<CFX_Font> pFont,
+ const uint8_t* data,
+ uint32_t size,
+ int font_type) {
+ CPDF_Dictionary* fontDict = pDoc->NewIndirect<CPDF_Dictionary>();
+ fontDict->SetNewFor<CPDF_Name>("Type", "Font");
+ fontDict->SetNewFor<CPDF_Name>("Subtype", "Type0");
+ // TODO(npm): Get the correct encoding, if it's not identity.
+ CFX_ByteString encoding = "Identity-H";
+ fontDict->SetNewFor<CPDF_Name>("Encoding", encoding);
+ CFX_ByteString name = pFont->GetFaceName();
+ if (name.IsEmpty())
+ name = "Unnamed";
+ fontDict->SetNewFor<CPDF_Name>(
+ "BaseFont", font_type == FPDF_FONT_TYPE1 ? name + "-" + encoding : name);
+
+ CPDF_Dictionary* pCIDFont = pDoc->NewIndirect<CPDF_Dictionary>();
+ pCIDFont->SetNewFor<CPDF_Name>("Type", "Font");
+ pCIDFont->SetNewFor<CPDF_Name>("Subtype", font_type == FPDF_FONT_TYPE1
+ ? "CIDFontType0"
+ : "CIDFontType2");
+ pCIDFont->SetNewFor<CPDF_Name>("BaseFont", name);
+
+ // TODO(npm): Maybe use FT_Get_CID_Registry_Ordering_Supplement to get the
+ // CIDSystemInfo
+ CPDF_Dictionary* pCIDSystemInfo = pDoc->NewIndirect<CPDF_Dictionary>();
+ pCIDSystemInfo->SetNewFor<CPDF_Name>("Registry", "Adobe");
+ pCIDSystemInfo->SetNewFor<CPDF_Name>("Ordering", "Identity");
+ pCIDSystemInfo->SetNewFor<CPDF_Number>("Supplement", 0);
+ pCIDFont->SetNewFor<CPDF_Reference>("CIDSystemInfo", pDoc,
+ pCIDSystemInfo->GetObjNum());
+
+ CPDF_Dictionary* fontDesc =
+ LoadFontDesc(pDoc, name, pFont.get(), data, size, font_type);
+ pCIDFont->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc,
+ fontDesc->GetObjNum());
+
+ uint32_t glyphIndex;
+ int currentChar = FXFT_Get_First_Char(pFont->GetFace(), &glyphIndex);
+ CPDF_Array* widthsArray = pDoc->NewIndirect<CPDF_Array>();
+ 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<CPDF_Array>();
+ oneW->AddNew<CPDF_Number>(width);
+ widthsArray->AddNew<CPDF_Number>(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<CPDF_Number>(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<CPDF_Number>(currentChar);
+ widthsArray->AddNew<CPDF_Number>(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<CPDF_Number>(currentChar);
+ auto curWidthArray = pdfium::MakeUnique<CPDF_Array>();
+ curWidthArray->AddNew<CPDF_Number>(width);
+ while (nextChar == currentChar + 1) {
+ curWidthArray->AddNew<CPDF_Number>(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<CPDF_Reference>("W", pDoc, widthsArray->GetObjNum());
+ // TODO(npm): Support vertical writing
+
+ auto pDescendant = pdfium::MakeUnique<CPDF_Array>();
+ pDescendant->AddNew<CPDF_Reference>(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<CPDF_TextObject*>(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<CFX_Font>();
+
+ // 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"