summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Pena <npm@chromium.org>2017-03-06 13:54:33 -0500
committerChromium commit bot <commit-bot@chromium.org>2017-03-06 21:02:43 +0000
commitd03ca4214e6db7123e1c2d0ea58e34f7cf67a568 (patch)
tree2f34c4d09095faddbb0e9951757bbadf6f2635f6
parentf6d0146200beec76f3d8676e22562d1acbc83d91 (diff)
downloadpdfium-d03ca4214e6db7123e1c2d0ea58e34f7cf67a568.tar.xz
Add public method FPDFText_LoadFont to load some types of fonts.
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 <npm@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org>
-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"