Introduce CPDF_FontSubsetter to subset new fonts on PDF save [pdfium : main]

0 views
Skip to first unread message

Andy Phan (Gerrit)

unread,
Feb 13, 2026, 4:41:22 PM (9 days ago) Feb 13
to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
Attention needed from Lei Zhang

Andy Phan added 12 comments

File core/fpdfapi/edit/cpdf_creator.cpp
Line 208, Patchset 8: new_obj_num_array_.begin(), new_obj_num_array_.end()));
Lei Zhang . resolved

Does `new_obj_num_array_` need to be put into a set? Does it contain duplicate values?

Andy Phan

No, my goal was for performance when finding values in large collections, but since the input is guaranteed sorted, binary search works. Replaced with more spans.

Line 218, Patchset 8:
const CPDF_Object* obj_to_write = obj.Get();

auto it = font_obj_overrides.find(objnum);
if (it != font_obj_overrides.end()) {
obj_to_write = it->second.Get();
}
Lei Zhang . resolved

Use a ternary operator here:

```
auto it = font_obj_overrides.find(objnum);
const CPDF_Object* obj_to_write = it != font_obj_overrides.end()
? it->second.Get(): obj.Get();
```
Andy Phan

Done

File core/fpdfapi/edit/cpdf_fontsubsetter.h
Line 73, Patchset 8: // Mapping from font file streams to subset candidates.
Lei Zhang . resolved

Is the key a PDF object number?

Andy Phan

Yes. This was still WIP, but clarified in comment.

Line 63, Patchset 8: void AddUsedText(SubsetCandidate& candidate,
Lei Zhang . resolved

May be slightly more intuitive to order the parameters to follow the ordering in the comment.

Andy Phan

Done

Line 23, Patchset 8:class CPDF_Page;
Lei Zhang . resolved

sort

Andy Phan

Done

File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
Line 32, Patchset 8:#include "hb-subset.h"
Lei Zhang . unresolved

Is this the best way to include HarfBuzz includes?

Andy Phan

Not sure why, but that's how they're included in Chromium and Skia.

Line 104, Patchset 8: new_obj_nums_ = new_obj_nums;
Lei Zhang . resolved

As-is: Use a non-const/non-ref parameter and std::move() here. Or pass in the vector (span?) from the caller and uniquify internally. Either way will avoid a copy.

Andy Phan

Chose span.

Line 177, Patchset 8: if (font->IsCIDFont()) {
Lei Zhang . unresolved

If this is true, then `font` can be casted to CPDF_CIDFont, right? In which case, does that mean `descendants` will never be null, since CPDF_CIDFont::Load() presumably succeeded?

More generally, can CPDF_Font and subclasses help simplify the code here?

Andy Phan

Yes, although I don't see any methods that particularly help here. Able to convert these to CHECKs, though?

File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
Line 25, Patchset 15 (Latest):CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT document) {
Andy Phan . resolved

Ehh... Found two other code spots that do this.

Line 136, Patchset 15 (Latest): std::vector<uint32_t> kTestObjNums{5, 6, 7, 8, 9, 10, 11, 12, 13};
Andy Phan . unresolved

Is there a better way to do these new object numbers?

Line 149, Patchset 15 (Latest):}
Andy Phan . unresolved

Do we want to test rendering? If so, should we test the rendering here or in fpdf_save_embeddertest.cpp?

In future CLs, these tests will check the root font, CID font, and font descriptor objects, so I'm not sure if that would make sense to be moved fpdf_save_embeddertest.cpp.

Line 240, Patchset 15 (Latest): it = overrides.find(16);
Andy Phan . unresolved

Hardcoded to find a specific object number. Not sure how to avoid. This will get worse when the root font, CID font, and font descriptor objects are added to the overrides list.

Open in Gerrit

Related details

Attention is currently required from:
  • Lei Zhang
Submit Requirements:
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: pdfium
Gerrit-Branch: main
Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
Gerrit-Change-Number: 141854
Gerrit-PatchSet: 15
Gerrit-Owner: Andy Phan <andy...@chromium.org>
Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
Gerrit-CC: Lei Zhang <the...@chromium.org>
Gerrit-Attention: Lei Zhang <the...@chromium.org>
Gerrit-Comment-Date: Fri, 13 Feb 2026 21:41:18 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Lei Zhang <the...@chromium.org>
unsatisfied_requirement
open
diffy

Andy Phan (Gerrit)

unread,
Feb 13, 2026, 4:45:06 PM (9 days ago) Feb 13
to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
Attention needed from Lei Zhang

Andy Phan added 1 comment

File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
Andy Phan . unresolved

Do we want to test rendering? If so, should we test the rendering here or in fpdf_save_embeddertest.cpp?

In future CLs, these tests will check the root font, CID font, and font descriptor objects, so I'm not sure if that would make sense to be moved fpdf_save_embeddertest.cpp.

Andy Phan

We already test a little bit of rendering in fpdf_save_embeddertest.cpp, but not to this extent.

Open in Gerrit

Related details

Attention is currently required from:
  • Lei Zhang
Submit Requirements:
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: pdfium
Gerrit-Branch: main
Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
Gerrit-Change-Number: 141854
Gerrit-PatchSet: 15
Gerrit-Owner: Andy Phan <andy...@chromium.org>
Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
Gerrit-CC: Lei Zhang <the...@chromium.org>
Gerrit-Attention: Lei Zhang <the...@chromium.org>
Gerrit-Comment-Date: Fri, 13 Feb 2026 21:45:02 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Andy Phan <andy...@chromium.org>
unsatisfied_requirement
open
diffy

Lei Zhang (Gerrit)

unread,
Feb 13, 2026, 6:05:48 PM (9 days ago) Feb 13
to Andy Phan, Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
Attention needed from Andy Phan

Lei Zhang added 3 comments

File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
Line 32, Patchset 8:#include "hb-subset.h"
Lei Zhang . resolved

Is this the best way to include HarfBuzz includes?

Andy Phan

Not sure why, but that's how they're included in Chromium and Skia.

Lei Zhang

Looks like the latest patchset changed it.

File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
Line 25, Patchset 15 (Latest):CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT document) {
Andy Phan . resolved

Ehh... Found two other code spots that do this.

Lei Zhang

PDFium has all 3 ways of testing:

1) Only fpdfsdk/ embedder tests, but they cheat and cast to CPDF objects.
2) Separate fpdfsdk/ embedder tests and core/ embedder tests.
3) Separate fpdfsdk/ embedder tests and core/ unit tests.

If it's easier to group everything together, maybe just do (1)?

Line 136, Patchset 15 (Latest): std::vector<uint32_t> kTestObjNums{5, 6, 7, 8, 9, 10, 11, 12, 13};
Andy Phan . unresolved

Is there a better way to do these new object numbers?

Lei Zhang

std::iota()?

Open in Gerrit

Related details

Attention is currently required from:
  • Andy Phan
Submit Requirements:
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: pdfium
Gerrit-Branch: main
Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
Gerrit-Change-Number: 141854
Gerrit-PatchSet: 15
Gerrit-Owner: Andy Phan <andy...@chromium.org>
Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
Gerrit-CC: Lei Zhang <the...@chromium.org>
Gerrit-Attention: Andy Phan <andy...@chromium.org>
Gerrit-Comment-Date: Fri, 13 Feb 2026 23:05:45 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Lei Zhang <the...@chromium.org>
Comment-In-Reply-To: Andy Phan <andy...@chromium.org>
unsatisfied_requirement
open
diffy

Lei Zhang (Gerrit)

unread,
Feb 13, 2026, 6:09:22 PM (9 days ago) Feb 13
to Andy Phan, Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
Attention needed from Andy Phan

Lei Zhang added 2 comments

File core/fpdfapi/edit/cpdf_fontsubsetter.h
Line 39, Patchset 15 (Latest): pdfium::span<uint32_t> new_obj_nums);
Lei Zhang . unresolved

`const uint32_t` if CPDF_FontSubsetter is only reading the span.

File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
Line 170, Patchset 15 (Latest): if (!std::binary_search(new_obj_nums.begin(), new_obj_nums.end(),
Lei Zhang . unresolved

std::ranges::binary_search()

Open in Gerrit

Related details

Attention is currently required from:
  • Andy Phan
Submit Requirements:
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: pdfium
Gerrit-Branch: main
Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
Gerrit-Change-Number: 141854
Gerrit-PatchSet: 15
Gerrit-Owner: Andy Phan <andy...@chromium.org>
Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
Gerrit-CC: Lei Zhang <the...@chromium.org>
Gerrit-Attention: Andy Phan <andy...@chromium.org>
Gerrit-Comment-Date: Fri, 13 Feb 2026 23:09:19 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
unsatisfied_requirement
open
diffy

Andy Phan (Gerrit)

unread,
Feb 18, 2026, 6:49:16 PM (4 days ago) Feb 18
to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
Attention needed from Lei Zhang

Andy Phan added 6 comments

File core/fpdfapi/edit/cpdf_fontsubsetter.h
Line 39, Patchset 15: pdfium::span<uint32_t> new_obj_nums);
Lei Zhang . resolved

`const uint32_t` if CPDF_FontSubsetter is only reading the span.

Andy Phan

Done

File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
Line 170, Patchset 15: if (!std::binary_search(new_obj_nums.begin(), new_obj_nums.end(),
Lei Zhang . resolved

std::ranges::binary_search()

Andy Phan

Done

Line 177, Patchset 8: if (font->IsCIDFont()) {
Lei Zhang . resolved

If this is true, then `font` can be casted to CPDF_CIDFont, right? In which case, does that mean `descendants` will never be null, since CPDF_CIDFont::Load() presumably succeeded?

More generally, can CPDF_Font and subclasses help simplify the code here?

Andy Phan

Yes, although I don't see any methods that particularly help here. Able to convert these to CHECKs, though?

Andy Phan

Done

File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
Line 136, Patchset 15: std::vector<uint32_t> kTestObjNums{5, 6, 7, 8, 9, 10, 11, 12, 13};
Andy Phan . resolved

Is there a better way to do these new object numbers?

Lei Zhang

std::iota()?

Andy Phan

Done

Line 149, Patchset 15:}
Andy Phan . resolved

Do we want to test rendering? If so, should we test the rendering here or in fpdf_save_embeddertest.cpp?

In future CLs, these tests will check the root font, CID font, and font descriptor objects, so I'm not sure if that would make sense to be moved fpdf_save_embeddertest.cpp.

Andy Phan

We already test a little bit of rendering in fpdf_save_embeddertest.cpp, but not to this extent.

Andy Phan

Decided to keep the tests as-is and have the rendering/text extraction tests occur in fpdf_save_embeddertest.cpp. CPDFFontSubsetterTest can test dictionary values and stream lengths.

Line 240, Patchset 15: it = overrides.find(16);
Andy Phan . resolved

Hardcoded to find a specific object number. Not sure how to avoid. This will get worse when the root font, CID font, and font descriptor objects are added to the overrides list.

Andy Phan

For input: using a large range of new obj nums.
For output: using gMock matchers.

Open in Gerrit

Related details

Attention is currently required from:
  • Lei Zhang
Submit Requirements:
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: pdfium
    Gerrit-Branch: main
    Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
    Gerrit-Change-Number: 141854
    Gerrit-PatchSet: 23
    Gerrit-Owner: Andy Phan <andy...@chromium.org>
    Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
    Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
    Gerrit-Attention: Lei Zhang <the...@chromium.org>
    Gerrit-Comment-Date: Wed, 18 Feb 2026 23:49:13 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Andy Phan (Gerrit)

    unread,
    Feb 18, 2026, 7:24:49 PM (4 days ago) Feb 18
    to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
    Attention needed from Lei Zhang

    Andy Phan added 1 comment

    File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
    Line 141, Patchset 23 (Latest): ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
    Andy Phan . resolved

    BTW, I think this still has to be an embedder test for FPDFText_LoadFont.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Lei Zhang
    Submit Requirements:
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: pdfium
    Gerrit-Branch: main
    Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
    Gerrit-Change-Number: 141854
    Gerrit-PatchSet: 23
    Gerrit-Owner: Andy Phan <andy...@chromium.org>
    Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
    Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
    Gerrit-Attention: Lei Zhang <the...@chromium.org>
    Gerrit-Comment-Date: Thu, 19 Feb 2026 00:24:45 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Lei Zhang (Gerrit)

    unread,
    Feb 19, 2026, 5:07:49 PM (3 days ago) Feb 19
    to Andy Phan, Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
    Attention needed from Andy Phan

    Lei Zhang added 11 comments

    File core/fpdfapi/edit/cpdf_fontsubsetter.h
    Line 73, Patchset 23 (Latest): std::map<uint32_t, CPDF_FontSubsetter::SubsetCandidate> candidates_;
    Lei Zhang . unresolved

    Can be shortened, like line 68.

    Line 67, Patchset 23 (Latest): CPDF_Font* font,
    Lei Zhang . unresolved

    Isn't `font` always `text->GetFont()`? Omit?

    Line 37, Patchset 23 (Latest): std::map<uint32_t, RetainPtr<const CPDF_Object>> GenerateObjectOverrides(
    Lei Zhang . unresolved

    Can the caller call this method multiple times without issues? Should `candidates_` be cleared at the start of the method, or should it be created on the stack and passed around?

    File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
    Line 46, Patchset 23 (Latest):std::vector<uint8_t> GenerateFontSubset(CPDF_Document* doc,
    Lei Zhang . unresolved

    Return DataVector<uint8_t> instead. Then use the CPDF_Stream ctor that takes a DataVector<uint8_t> (+ dictionary with /Length1 set) to avoid a copy.

    Line 79, Patchset 23 (Latest): unsigned int out_len = hb_blob_get_length(subset_blob.get());
    Lei Zhang . unresolved

    Instead of separately getting the length this way, use the second parameter of hb_blob_get_data().

    Line 105, Patchset 23 (Latest): auto old_stream_acc =
    Lei Zhang . unresolved

    Maybe "old" and "new" can be "original" and "subsetted", respectively.

    Line 149, Patchset 23 (Latest): if (!page_obj->IsText()) {
    Lei Zhang . unresolved

    I think this can call AsText() like line 154 here, and then skip the entry if `text` is null.

    Line 156, Patchset 23 (Latest): if (!font) {
    Lei Zhang . unresolved

    Based on other GetFont() callers, this shouldn't ever happen.

    Line 161, Patchset 23 (Latest): if (!root_font) {
    Lei Zhang . unresolved

    Same. Never happens except during FPDF_DestroyLibrary().

    File fpdfsdk/fpdf_save_embeddertest.cpp
    Line 327, Patchset 23 (Latest): void InsertNewTextObject(const FPDF_PAGE& page,
    Lei Zhang . unresolved

    Some of the test refactoring can happen separately.

    Line 330, Patchset 23 (Latest): float x,
    Lei Zhang . unresolved

    Use a CFX_PointF for x and y coordinates.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Andy Phan
    Submit Requirements:
      • requirement is not satisfiedCode-Owners
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: pdfium
      Gerrit-Branch: main
      Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
      Gerrit-Change-Number: 141854
      Gerrit-PatchSet: 23
      Gerrit-Owner: Andy Phan <andy...@chromium.org>
      Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
      Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
      Gerrit-Attention: Andy Phan <andy...@chromium.org>
      Gerrit-Comment-Date: Thu, 19 Feb 2026 22:07:47 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      unsatisfied_requirement
      open
      diffy

      Andy Phan (Gerrit)

      unread,
      Feb 19, 2026, 6:13:25 PM (3 days ago) Feb 19
      to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
      Attention needed from Lei Zhang

      Andy Phan added 11 comments

      File core/fpdfapi/edit/cpdf_fontsubsetter.h
      Line 73, Patchset 23: std::map<uint32_t, CPDF_FontSubsetter::SubsetCandidate> candidates_;
      Lei Zhang . resolved

      Can be shortened, like line 68.

      Andy Phan

      Done

      Line 67, Patchset 23: CPDF_Font* font,
      Lei Zhang . resolved

      Isn't `font` always `text->GetFont()`? Omit?

      Andy Phan

      Done

      Line 37, Patchset 23: std::map<uint32_t, RetainPtr<const CPDF_Object>> GenerateObjectOverrides(
      Lei Zhang . resolved

      Can the caller call this method multiple times without issues? Should `candidates_` be cleared at the start of the method, or should it be created on the stack and passed around?

      Andy Phan

      At the moment, it won't cause any issues, but it will in future CLs. So clearing at start of method.

      File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
      Line 46, Patchset 23:std::vector<uint8_t> GenerateFontSubset(CPDF_Document* doc,
      Lei Zhang . resolved

      Return DataVector<uint8_t> instead. Then use the CPDF_Stream ctor that takes a DataVector<uint8_t> (+ dictionary with /Length1 set) to avoid a copy.

      Andy Phan

      Done

      Line 79, Patchset 23: unsigned int out_len = hb_blob_get_length(subset_blob.get());
      Lei Zhang . resolved

      Instead of separately getting the length this way, use the second parameter of hb_blob_get_data().

      Andy Phan

      Done

      Line 105, Patchset 23: auto old_stream_acc =
      Lei Zhang . resolved

      Maybe "old" and "new" can be "original" and "subsetted", respectively.

      Andy Phan

      Done

      Line 149, Patchset 23: if (!page_obj->IsText()) {
      Lei Zhang . resolved

      I think this can call AsText() like line 154 here, and then skip the entry if `text` is null.

      Andy Phan

      Done

      Line 156, Patchset 23: if (!font) {
      Lei Zhang . resolved

      Based on other GetFont() callers, this shouldn't ever happen.

      Andy Phan

      Done

      Line 161, Patchset 23: if (!root_font) {
      Lei Zhang . resolved

      Same. Never happens except during FPDF_DestroyLibrary().

      Andy Phan

      Done

      File fpdfsdk/fpdf_save_embeddertest.cpp
      Line 327, Patchset 23: void InsertNewTextObject(const FPDF_PAGE& page,
      Lei Zhang . resolved

      Some of the test refactoring can happen separately.

      Use a CFX_PointF for x and y coordinates.

      Attention is currently required from:
      • Lei Zhang
      Submit Requirements:
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: pdfium
        Gerrit-Branch: main
        Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
        Gerrit-Change-Number: 141854
        Gerrit-PatchSet: 24
        Gerrit-Owner: Andy Phan <andy...@chromium.org>
        Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
        Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
        Gerrit-Attention: Lei Zhang <the...@chromium.org>
        Gerrit-Comment-Date: Thu, 19 Feb 2026 23:13:21 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Lei Zhang <the...@chromium.org>
        unsatisfied_requirement
        open
        diffy

        Lei Zhang (Gerrit)

        unread,
        Feb 19, 2026, 8:40:36 PM (3 days ago) Feb 19
        to Andy Phan, Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com
        Attention needed from Andy Phan

        Lei Zhang voted and added 7 comments

        Votes added by Lei Zhang

        Code-Review+1

        7 comments

        File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
        Line 125, Patchset 24 (Latest): auto subsetted_stream = pdfium::MakeRetain<CPDF_Stream>(
        Lei Zhang . unresolved

        Local variable no longer needed. Just assign directly.

        File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
        Line 116, Patchset 24 (Latest): std::vector<uint32_t> new_obj_nums;
        Lei Zhang . unresolved

        Probably don't need this variable, just pass `{}` and GetTestNewObjNums() in directly.

        Line 122, Patchset 24 (Latest): // No-text object.
        Lei Zhang . unresolved

        "Not a text object." ?

        Line 138, Patchset 24 (Latest): size_t original_size = font_data.size();
        Lei Zhang . unresolved

        Make this const. More below.

        Line 139, Patchset 24 (Latest): ASSERT_GT(original_size, 0u);
        Lei Zhang . unresolved

        Can probably put in a larger number here. Also line 167. An exact number comparion is probably ok, since the test fonts rarely changes. If they do change, it might affect the tests anyway and someone may want to adjust the numbers. More below.

        Line 141, Patchset 23: ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
        Andy Phan . resolved

        BTW, I think this still has to be an embedder test for FPDFText_LoadFont.

        Lei Zhang

        OK. Copying the relevant part of FPDFText_LoadFont() itself actually isn't that bad, but it would be unreasonable to copy LoadCompositeFont().

        Line 153, Patchset 24 (Latest): EXPECT_THAT(overrides, UnorderedElementsAre(StreamSizeIsWithinRange(
        Lei Zhang . unresolved

        But there's only 1 element in the vector. More below.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Andy Phan
        Submit Requirements:
        • requirement satisfiedCode-Owners
        • requirement satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: pdfium
        Gerrit-Branch: main
        Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
        Gerrit-Change-Number: 141854
        Gerrit-PatchSet: 24
        Gerrit-Owner: Andy Phan <andy...@chromium.org>
        Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
        Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
        Gerrit-Attention: Andy Phan <andy...@chromium.org>
        Gerrit-Comment-Date: Fri, 20 Feb 2026 01:40:32 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        Comment-In-Reply-To: Andy Phan <andy...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Andy Phan (Gerrit)

        unread,
        Feb 20, 2026, 3:11:00 PM (2 days ago) Feb 20
        to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com

        Andy Phan added 6 comments

        File core/fpdfapi/edit/cpdf_fontsubsetter.cpp
        Line 125, Patchset 24: auto subsetted_stream = pdfium::MakeRetain<CPDF_Stream>(
        Lei Zhang . resolved

        Local variable no longer needed. Just assign directly.

        Andy Phan

        Done

        File core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
        Line 116, Patchset 24: std::vector<uint32_t> new_obj_nums;
        Lei Zhang . resolved

        Probably don't need this variable, just pass `{}` and GetTestNewObjNums() in directly.

        Andy Phan

        Done

        Line 122, Patchset 24: // No-text object.
        Lei Zhang . resolved

        "Not a text object." ?

        Andy Phan

        Done

        Line 138, Patchset 24: size_t original_size = font_data.size();
        Lei Zhang . resolved

        Make this const. More below.

        Andy Phan

        Done

        Line 139, Patchset 24: ASSERT_GT(original_size, 0u);
        Lei Zhang . resolved

        Can probably put in a larger number here. Also line 167. An exact number comparion is probably ok, since the test fonts rarely changes. If they do change, it might affect the tests anyway and someone may want to adjust the numbers. More below.

        Andy Phan

        Makes sense, done.

        Line 153, Patchset 24: EXPECT_THAT(overrides, UnorderedElementsAre(StreamSizeIsWithinRange(
        Lei Zhang . resolved

        But there's only 1 element in the vector. More below.

        Andy Phan

        Will use Contain() for now, but not for long when we override more objects.

        Open in Gerrit

        Related details

        Attention set is empty
        Submit Requirements:
          • requirement satisfiedCode-Owners
          • requirement satisfiedCode-Review
          • requirement satisfiedReview-Enforcement
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: pdfium
          Gerrit-Branch: main
          Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
          Gerrit-Change-Number: 141854
          Gerrit-PatchSet: 25
          Gerrit-Owner: Andy Phan <andy...@chromium.org>
          Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
          Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
          Gerrit-Comment-Date: Fri, 20 Feb 2026 20:10:57 +0000
          satisfied_requirement
          open
          diffy

          Andy Phan (Gerrit)

          unread,
          Feb 20, 2026, 4:37:59 PM (2 days ago) Feb 20
          to Lei Zhang, Pdfium LUCI CQ, pdfium-...@googlegroups.com

          Andy Phan voted Commit-Queue+2

          Commit-Queue+2
          Open in Gerrit

          Related details

          Attention set is empty
          Submit Requirements:
          • requirement satisfiedCode-Owners
          • requirement satisfiedCode-Review
          • requirement satisfiedReview-Enforcement
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: pdfium
          Gerrit-Branch: main
          Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
          Gerrit-Change-Number: 141854
          Gerrit-PatchSet: 25
          Gerrit-Owner: Andy Phan <andy...@chromium.org>
          Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
          Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
          Gerrit-Comment-Date: Fri, 20 Feb 2026 21:37:55 +0000
          Gerrit-HasComments: No
          Gerrit-Has-Labels: Yes
          satisfied_requirement
          open
          diffy

          Pdfium LUCI CQ (Gerrit)

          unread,
          Feb 20, 2026, 4:39:37 PM (2 days ago) Feb 20
          to Andy Phan, Lei Zhang, pdfium-...@googlegroups.com

          Pdfium LUCI CQ submitted the change with unreviewed changes

          Unreviewed changes

          24 is the latest approved patch-set.
          The change was submitted with unreviewed changes in the following files:

          ```
          The name of the file: core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
          Insertions: 20, Deletions: 24.

          @@ -22,6 +22,7 @@
          #include "testing/utils/file_util.h"
          #include "testing/utils/path_service.h"

          +using ::testing::Contains;
          using ::testing::IsEmpty;
          using ::testing::Matcher;
          using ::testing::UnorderedElementsAre;
          @@ -113,16 +114,16 @@

          CPDF_FontSubsetter subsetter(CPDFDocumentFromFPDFDocument(document()));

          - std::vector<uint32_t> new_obj_nums;
          - EXPECT_THAT(subsetter.GenerateObjectOverrides(new_obj_nums), IsEmpty());
          + EXPECT_THAT(subsetter.GenerateObjectOverrides({}), IsEmpty());

          - new_obj_nums = GetTestNewObjNums();
          - EXPECT_THAT(subsetter.GenerateObjectOverrides(new_obj_nums), IsEmpty());
          + EXPECT_THAT(subsetter.GenerateObjectOverrides(GetTestNewObjNums()),
          + IsEmpty());

          - // No-text object.
          + // Not a text object.
          FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
          FPDFPage_InsertObject(page.get(), rect);
          - EXPECT_THAT(subsetter.GenerateObjectOverrides(new_obj_nums), IsEmpty());
          + EXPECT_THAT(subsetter.GenerateObjectOverrides(GetTestNewObjNums()),
          + IsEmpty());
          }

          TEST_F(CPDFFontSubsetterTest, OpenType) {
          @@ -133,10 +134,9 @@
          GetTestFontFilePath("NotoSansCJKjp-Regular.otf");
          ASSERT_FALSE(font_path.empty());

          - // The file size for `font_path` is ~16.4 MB.
          std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
          - size_t original_size = font_data.size();
          - ASSERT_GT(original_size, 0u);
          + const size_t original_size = font_data.size();
          + ASSERT_EQ(16427228u, original_size);

          ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
          font_data.size(), FPDF_FONT_TRUETYPE,
          @@ -150,7 +150,7 @@
          ASSERT_EQ(1u, overrides.size());

          // Subset size is ~2.5% of the original font file, i.e. ~450 KB.
          - EXPECT_THAT(overrides, UnorderedElementsAre(StreamSizeIsWithinRange(
          + EXPECT_THAT(overrides, Contains(StreamSizeIsWithinRange(
          original_size * 0.02, original_size * 0.03)));
          }

          @@ -161,10 +161,9 @@
          const std::string font_path = GetTestFontFilePath("Arimo-Regular.ttf");
          ASSERT_FALSE(font_path.empty());

          - // The file size for `font_path` is ~436 KB.
          std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
          - size_t original_size = font_data.size();
          - ASSERT_GT(original_size, 0u);
          + const size_t original_size = font_data.size();
          + ASSERT_EQ(436180u, original_size);

          ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
          font_data.size(), FPDF_FONT_TRUETYPE,
          @@ -179,7 +178,7 @@
          ASSERT_EQ(1u, overrides.size());

          // Subset size is ~3% of the original font file, i.e. ~13 KB.
          - EXPECT_THAT(overrides, UnorderedElementsAre(StreamSizeIsWithinRange(
          + EXPECT_THAT(overrides, Contains(StreamSizeIsWithinRange(
          original_size * 0.025, original_size * 0.035)));
          }

          @@ -190,10 +189,9 @@
          const std::string font_path = GetTestFontFilePath("Arimo-Regular.ttf");
          ASSERT_FALSE(font_path.empty());

          - // The file size for `font_path` is ~436 KB.
          std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
          - size_t original_size = font_data.size();
          - ASSERT_GT(original_size, 0u);
          + const size_t original_size = font_data.size();
          + ASSERT_EQ(436180u, original_size);

          ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
          font_data.size(), FPDF_FONT_TRUETYPE,
          @@ -210,7 +208,7 @@
          ASSERT_EQ(1u, overrides.size());

          // Subset size is ~3.5% of the original font file, i.e. ~15 KB.
          - EXPECT_THAT(overrides, UnorderedElementsAre(StreamSizeIsWithinRange(
          + EXPECT_THAT(overrides, Contains(StreamSizeIsWithinRange(
          original_size * 0.03, original_size * 0.04)));
          }

          @@ -223,14 +221,12 @@
          const std::string font_path2 = GetTestFontFilePath("Arimo-Regular.ttf");
          ASSERT_FALSE(font_path2.empty());

          - // The file size for `font_path1` is ~48 KB.
          std::vector<uint8_t> font_data1 = GetFileContents(font_path1.c_str());
          - size_t original_size1 = font_data1.size();
          - ASSERT_GT(original_size1, 0u);
          - // The file size for `font_path2` is ~436 KB.
          + const size_t original_size1 = font_data1.size();
          + ASSERT_EQ(48908u, original_size1);
          std::vector<uint8_t> font_data2 = GetFileContents(font_path2.c_str());
          - size_t original_size2 = font_data2.size();
          - ASSERT_GT(original_size2, 0u);
          + const size_t original_size2 = font_data2.size();
          + ASSERT_EQ(436180u, original_size2);

          ScopedFPDFFont font1(FPDFText_LoadFont(document(), font_data1.data(),
          font_data1.size(), FPDF_FONT_TRUETYPE,
          ```
          ```
          The name of the file: core/fpdfapi/edit/cpdf_fontsubsetter.cpp
          Insertions: 1, Deletions: 2.

          @@ -122,9 +122,8 @@
          // TrueType fonts requires a Length1 entry.
          subsetted_font_dict->SetNewFor<CPDF_Number>(
          "Length1", static_cast<int>(subsetted_font_data.size()));
          - auto subsetted_stream = pdfium::MakeRetain<CPDF_Stream>(
          + overrides[obj_num] = pdfium::MakeRetain<CPDF_Stream>(
          std::move(subsetted_font_data), std::move(subsetted_font_dict));
          - overrides[obj_num] = subsetted_stream;
          }
          return overrides;
          }
          ```

          Change information

          Commit message:
          Introduce CPDF_FontSubsetter to subset new fonts on PDF save

          Add CPDF_FontSubsetter, which, when given a list of new PDF object
          numbers, will output object overrides required to subset any new fonts
          while saving the PDF. The objects do not affect the original copy and
          are purely used for writing the new PDF, avoiding any in-memory PDF
          mutation. CPDF_FontSubsetter uses HarfBuzz internally to subset fonts.

          third_party/test_fonts is already included in PDFium, so use it to test
          font subsetting. Some of the fonts are multiple megabytes large, so some
          tests may take ~2-3 seconds.

          There are still some remaining TODOs to make the subsetted font
          compliant with the PDF spec, but some PDF viewers will be able to
          properly render the subsetted font with this CL alone.
          Bug: 476127152
          Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
          Reviewed-by: Lei Zhang <the...@chromium.org>
          Commit-Queue: Andy Phan <andy...@chromium.org>
          Files:
          • M core/fpdfapi/edit/BUILD.gn
          • M core/fpdfapi/edit/cpdf_creator.cpp
          • M core/fpdfapi/edit/cpdf_creator.h
          • A core/fpdfapi/edit/cpdf_fontsubsetter.cpp
          • A core/fpdfapi/edit/cpdf_fontsubsetter.h
          • A core/fpdfapi/edit/cpdf_fontsubsetter_embeddertest.cpp
          • M fpdfsdk/fpdf_save_embeddertest.cpp
          • A testing/resources/embedder_tests/save_multiple_fonts_agg_linux.png
          • A testing/resources/embedder_tests/save_multiple_fonts_agg_mac.png
          • A testing/resources/embedder_tests/save_multiple_fonts_agg_win.png
          • A testing/resources/embedder_tests/save_multiple_fonts_skia_linux.png
          • A testing/resources/embedder_tests/save_multiple_fonts_skia_mac.png
          • A testing/resources/embedder_tests/save_multiple_fonts_skia_win.png
          Change size: L
          Delta: 13 files changed, 598 insertions(+), 7 deletions(-)
          Branch: refs/heads/main
          Submit Requirements:
          • requirement satisfiedCode-Review: +1 by Lei Zhang
          Open in Gerrit
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: merged
          Gerrit-Project: pdfium
          Gerrit-Branch: main
          Gerrit-Change-Id: I0c16be9a15c295be231aa34735f5d2e4edbc9d1b
          Gerrit-Change-Number: 141854
          Gerrit-PatchSet: 26
          Gerrit-Owner: Andy Phan <andy...@chromium.org>
          Gerrit-Reviewer: Andy Phan <andy...@chromium.org>
          Gerrit-Reviewer: Lei Zhang <the...@chromium.org>
          open
          diffy
          satisfied_requirement
          Reply all
          Reply to author
          Forward
          0 new messages