Skip to content

[Due for payment 2026-07-07] [PDF Viewer] Black text disappears from certain PDF receipts after they finish rendering (web) #94484

Closed

Description

@melvin-bot

Problem

Certain PDF receipts viewed in New Expensify (web) render incorrectly: while the
PDF is loading, the text is briefly visible, but once it has fully loaded the
black text on the receipt disappears, leaving a blank/near-blank page. The same
PDFs display correctly in Expensify Classic.

This only affects some PDFs — it appears to be font-specific. PDFs using a
different embedded font render fine. The blank/blink behaviour does not reproduce
in macOS Preview, so it looks specific to how we render the affected fonts.

Observed workarounds / clues reported

  • Hovering over the receipt thumbnail shows the content correctly.
  • Opening the receipt shows the text briefly during load (while the green progress
    bar is shown), then it vanishes when rendering completes.
  • Navigating to the next receipt and back makes the text reappear.

Expected

PDF text renders and stays visible after the document finishes loading, matching
Classic and other PDF viewers.

Actual

Text flashes in during load, then disappears once the PDF fully renders. Re-rendering
(navigate away + back) restores it.

Platforms

  • Web / Desktop (New Expensify)
  • Other platforms not yet confirmed.

Technical notes

New Expensify renders PDFs on web via react-pdf + pdfjs-dist (^5.5.207).
The symptoms (text visible mid-load then cleared on completion, font-specific,
fixed by a re-render) point to a text-layer / embedded-font rendering issue in
the pdf.js pipeline rather than a data problem. A good starting point would be
src/components/PDFView / src/components/PDFThumbnail and the pdf.js text-layer

  • cmaps/font handling.

Repro file

A sample PDF that reproduces the issue is available but cannot be shared
publicly
(client document). It can be shared privately with the engineer who
picks this up.

Issue OwnerCurrent Issue Owner: @mallenexpensify

Activity

  1. mountiny commented on Jun 24, 2026

    @mountiny
    Contributor

    PDF to test with is here https://expensify.slack.com/archives/C05LX9D6E07/p1782316539163899?thread_ts=1782209979.435539&cid=C05LX9D6E07

    DO NOT SHARE IT IN GITHUB OR IN VIDEOS WITH IT - post the videos in Slack

  2. VickyStash commented on Jun 25, 2026

    @VickyStash
    Contributor

    Problem

    A specific PDF receipt logged:

    Warning: Error during font loading: Unable to load CMap data at: cmaps/UniGB-UCS2-H.bcmap
    

    and part of the text in the PDF did not render. The stack trace originated in PDFThumbnail (src/components/PDFThumbnail/index.tsx:46).

    Root Cause (confirmed in source)

    • UniGB-UCS2-H is the Adobe-GB1 (Simplified Chinese) CMap. When it fails to load, that CJK text can't be mapped to glyphs and doesn't render.
    • The CMap files are copied into the build at the server root (/cmaps/) via webpack CopyPlugin (config/webpack/webpack.common.ts:218), and UniGB-UCS2-H.bcmap exists in pdfjs-dist.
    • The bug is the relative cMapUrl: 'cmaps/'. PDF.js builds the URL as `${cMapUrl}${filename}` and fetch()s it against document.baseURI (node_modules/pdfjs-dist/legacy/build/pdf.mjs:7566).
    • With publicPath: '/' and path-based browser routing, on a deep route like /r/123/... the relative URL resolves to /r/123/cmaps/UniGB-UCS2-H.bcmap → 404.

    Fix Applied

    • src/components/PDFThumbnail/index.tsx: changed cMapUrl: 'cmaps/' → '/cmaps/' (root-relative).

    Still Outstanding — The Full Viewer

    • PDFView → react-fast-pdf's PDFPreviewer has the same relative-cmaps/ bug in its hardcoded DEFAULT_DOCUMENT_OPTIONS (node_modules/react-fast-pdf/dist/constants.js:17), and exposes no override prop. So full-screen receipt view will still drop the CJK text.
    • Decision: fix this via an upstream PR to react-fast-pdf.

    Guidance for the Upstream PR

    Don't just swap the default to /cmaps/.

    • Downside: /cmaps/ breaks for consumers deploying under a sub-path (non-root publicPath), since it resolves to /cmaps/ instead of /subpath/cmaps/. It's correct for Expensify only because our publicPath is /. So a straight default swap is a potential breaking change for other consumers.
    • Recommended approach:
      1. Add an optional prop (a cMapUrl, or broader options passthrough merged into DEFAULT_DOCUMENT_OPTIONS).
      2. Keep the existing default to avoid regressions.
      3. Pass cMapUrl="/cmaps/" from our PDFView.tsx.
  3. VickyStash commented on Jun 25, 2026

    @VickyStash
    Contributor

    react-fast-pdf PR: Expensify/react-fast-pdf#54
    Draft app PR: #94568

  4. 7 remaining items

  5. added
    Awaiting PaymentAuto-added when associated PR is deployed to production
    on Jul 5, 2026
  6. melvin-bot commented on Jul 5, 2026

    @melvin-bot
    Author

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  7. FitseTLT commented on Jul 6, 2026

    @FitseTLT
    Contributor

    BugZero Checklist:

    • [Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

      Link to comment:https://github.com/Expensify/App/pull/35255/changes#r3529573983

    • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

      Link to discussion: N/A

    • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

    • [BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.

      Link to issue: https://github.com/Expensify/Expensify/issues/657657

    Regression Test Proposal

    Test:

    1. Create expense with special PDF file https://expensify.slack.com/archives/C05LX9D6E07/p1782316395764239?thread_ts=1782209979.435539&cid=C05LX9D6E07
    2. Make sure PDF file looks as expected and text doesn't disappear on both preview and full screen attachment modal.

    Do we agree 👍 or 👎

    Zapier Logs Run ID: 00040eee-7731-a441-2bbf-db5d539b75a4
  8. melvin-bot commented on Jul 6, 2026

    @melvin-bot
    Author

    Payment Summary

    Upwork Job

    • Contributor: @VickyStash does not require payment (Contractor)
    • Reviewer: @FitseTLT owed $250 via NewDot

    BugZero Checklist (@mallenexpensify)

    • I have verified the correct assignees and roles are listed above and updated the necessary manual offers
    • I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants//hired)
    • I have verified the PR was not reverted
    • I have applied any discounts due to bugs/regressions introduced by this PR
    • I have paid out the Upwork contracts or cancelled the ones that are incorrect
    • I have verified the payment summary above is correct
  9. mallenexpensify commented on Jul 8, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @FitseTLT due $250 via NewDot

    $125 cuz no tests

    Image
  10. FitseTLT commented on Jul 9, 2026

    @FitseTLT
    Contributor

    @mallenexpensify The test is confidential as it involves customer receipt that's why 😄 we communicated in slack.

  11. mallenexpensify commented on Jul 9, 2026

    @mallenexpensify
    Contributor

    @FitseTLT that's a rare one! Updated price to $250

  12. flaviadefaria commented on Jul 31, 2026

    @flaviadefaria
    Contributor

    Approved: @FitseTLT due $250 via NewDot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionDailyKSv2Task

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions