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