Fix CID Font /W array output error
Function CreateWidthsArray being invoked from
function LoadCustomCompositeFont misproduces continuous array
when actual font character sequence contains gaps.
Bug: 377948405
Change-Id: Ic97178aa46f4c582c3cce99054712ec47be62e4c
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/125730
Commit-Queue: Thomas Sepez <tsepez@google.com>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
diff --git a/AUTHORS b/AUTHORS
index 652f732..5b79420 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,6 +19,7 @@
Chery Cherian <cherycherian@gmail.com>
Claudio DeSouza <claudiomdsjr@gmail.com>
Dan Ilan <danilan@gmail.com>
+Dmitrii Chunikhin <chunikhindf@gmail.com>
Dorian Rudolph <dorianrudo97@gmail.com>
Felix Kauselmann <licorn@gmail.com>
GiWan Go <gogil@stealien.com>
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index 8d3512d..95fc846 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -5354,3 +5354,44 @@
CloseDocument();
}
}
+
+// Regression test for https://issues.chromium.org/377948405
+// fonts/bug_377948405.ttf was made with harfbuzz* subset utility:
+// > hb-subset NotoSans-Regular.ttf -u 41,C0,C4-C6,C8 -o bug_377948405.ttf
+// Font W array state before bug fix: [ 1[ 639 639 639 639 881 556]]
+// Font W array state after bug fix: [ 1[ 639] 5 7 639 8[ 881 556]]
+// *At the moment of the bug detection harfbuzz version was 10.1.0
+TEST_F(FPDFEditEmbedderTest, Bug377948405) {
+ CreateEmptyDocument();
+ ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
+ std::string font_path =
+ PathService::GetTestFilePath("fonts/bug_377948405.ttf");
+ ASSERT_FALSE(font_path.empty());
+
+ std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
+ ASSERT_FALSE(font_data.empty());
+
+ ScopedFPDFFont font(FPDFText_LoadFont(
+ document(), font_data.data(), font_data.size(), FPDF_FONT_TRUETYPE, 1));
+ ASSERT_TRUE(font);
+ CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
+ EXPECT_TRUE(typed_font->IsCIDFont());
+
+ // Get font descendant
+ RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
+ ASSERT_TRUE(font_dict);
+ RetainPtr<const CPDF_Array> descendant_array =
+ font_dict->GetArrayFor("DescendantFonts");
+ ASSERT_TRUE(descendant_array);
+ EXPECT_EQ(1u, descendant_array->size());
+
+ // Check the CIDFontDict width array
+ RetainPtr<const CPDF_Dictionary> cidfont_dict =
+ descendant_array->GetDictAt(0);
+ ASSERT_TRUE(cidfont_dict);
+ RetainPtr<const CPDF_Array> widths_array = cidfont_dict->GetArrayFor("W");
+ ASSERT_TRUE(widths_array);
+
+ EXPECT_EQ(widths_array->GetIntegerAt(0), 1);
+ ASSERT_EQ(widths_array->GetIntegerAt(2), 5);
+}
diff --git a/fpdfsdk/fpdf_edittext.cpp b/fpdfsdk/fpdf_edittext.cpp
index 8b2642b..f849a7b 100644
--- a/fpdfsdk/fpdf_edittext.cpp
+++ b/fpdfsdk/fpdf_edittext.cpp
@@ -180,49 +180,33 @@
const std::map<uint32_t, uint32_t>& widths) {
auto widths_array = doc->NewIndirect<CPDF_Array>();
for (auto it = widths.begin(); it != widths.end(); ++it) {
- int ch = it->first;
- int w = it->second;
- if (std::next(it) == widths.end()) {
- // Only one char left, use format c [w]
- auto single_w_array = pdfium::MakeRetain<CPDF_Array>();
- single_w_array->AppendNew<CPDF_Number>(w);
- widths_array->AppendNew<CPDF_Number>(ch);
- widths_array->Append(std::move(single_w_array));
- break;
- }
- ++it;
- int next_ch = it->first;
- int next_w = it->second;
- if (next_ch == ch + 1 && next_w == w) {
+ auto next_it = std::next(it);
+
+ if (next_it != widths.end() && next_it->first == it->first + 1 &&
+ next_it->second == it->second) {
// 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
- widths_array->AppendNew<CPDF_Number>(ch);
- ch = next_ch;
- while (true) {
- auto next_it = std::next(it);
- if (next_it == widths.end() || next_it->first != it->first + 1 ||
- next_it->second != it->second) {
- break;
- }
- ++it;
- ch = it->first;
+ widths_array->AppendNew<CPDF_Number>(static_cast<int>(it->first));
+
+ while (next_it != widths.end() && next_it->first == it->first + 1 &&
+ next_it->second == it->second) {
+ it = next_it;
+ next_it = std::next(it);
}
- widths_array->AppendNew<CPDF_Number>(ch);
- widths_array->AppendNew<CPDF_Number>(w);
+ widths_array->AppendNew<CPDF_Number>(static_cast<int>(it->first));
+ widths_array->AppendNew<CPDF_Number>(static_cast<int>(it->second));
continue;
}
// Otherwise we can have a group of the form c [w1 w2 ...]: c has width
// w1, c+1 has width w2, etc.
- widths_array->AppendNew<CPDF_Number>(ch);
+ // A group may contain only a single item, e.g. c[w]
+ widths_array->AppendNew<CPDF_Number>(static_cast<int>(it->first));
auto current_width_array = pdfium::MakeRetain<CPDF_Array>();
- current_width_array->AppendNew<CPDF_Number>(w);
- current_width_array->AppendNew<CPDF_Number>(next_w);
- while (true) {
- auto next_it = std::next(it);
- if (next_it == widths.end() || next_it->first != it->first + 1) {
- break;
- }
- ++it;
+ current_width_array->AppendNew<CPDF_Number>(static_cast<int>(it->second));
+
+ while (next_it != widths.end() && next_it->first == it->first + 1) {
+ it = next_it;
+ next_it = std::next(it);
current_width_array->AppendNew<CPDF_Number>(static_cast<int>(it->second));
}
widths_array->Append(std::move(current_width_array));
diff --git a/testing/resources/fonts/bug_377948405.ttf b/testing/resources/fonts/bug_377948405.ttf
new file mode 100644
index 0000000..04def0b
--- /dev/null
+++ b/testing/resources/fonts/bug_377948405.ttf
Binary files differ