Skip to content

fix: 打包版 pdf.js 的 CMap 和标准字体路径不再依赖 cwd (#85) - #92

Merged
haywolf merged 3 commits into
masterfrom
fix/85-pdfjs-asset-paths
Nov 5, 2026
Merged

haywolf merged 3 commits into
masterfrom
fix/85-pdfjs-asset-paths

Conversation

@haywolf

@haywolf haywolf commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #85(部分,见“未解决”)

复现

已在 Linux 上复现,包括真实打包应用:从无关目录启动 master 打包版,拆页时 pdf.js 报 Unable to load binary CMap,使用预定义 CMap(UniGB-UCS2-H、GBK-EUC-H)和 Identity-H/Adobe-GB1 的嵌入字体中文页,整行文字都没画出来(暗像素数为 0,开发模式为 12800)。

改动

  • 新增 PDFPageRenderer.ts,作为访问 pdf-to-png-converter 的唯一入口:从转换库解析 pdfjs-dist 的实际位置,覆盖按 process.cwd() 拼出的 CMap 和标准字体路径;数据文件缺失时直接报错,不再静默丢字;打开 pdf.js 警告
  • PDFSplitter 改走 PDFPageRenderer,去掉 verbosityLevel: 0
  • pdf-to-png-converter 钉死为 3.11.0,因为修复依赖它的非公开文件
  • verify:package 新增 CJK PDF 渲染检查(样例在 scripts/fixtures/),并且所有检查前先切到空目录
  • electron.vite.config.ts 增加 pdf-page-renderer 入口,供打包检查调用

测试

  • 新增 PDFPageRenderer.test.ts(6 个)和 PDFPageRenderer.integration.test.ts(在没有 node_modules 的 cwd 下跑完整拆页)
  • npm run test:unit:51 个文件,1033 个用例全部通过;npm run test:renderer:283 个用例通过
  • npm run typecheck、npm run lint:通过
  • npm run verify:package(Linux x64):通过,修复后打包版拆页 PNG 和开发模式逐字节一致
  • 撤回修复后,集成测试和 verify:package 都会失败

未解决

  • 未嵌入字体的中文 PDF(issue 复现步骤用的就是这一类),在 Linux 上修复后画出来的是豆腐块(□),开发模式也一样。原因是 @napi-rs/canvas 不会按字符回退到系统 CJK 字体,和本次的路径问题无关。建议先在 macOS 和 Windows 上确认实际表现,再单独开 issue

未验证项

  • macOS、Windows 打包版未验证(本机只有 Linux),release.yml 在各平台跑 verify:package 时会覆盖
  • 只测了 --dir 解包产物,没有测 AppImage 或安装器;npx 模式只模拟了目录布局

pdf-to-png-converter builds the pdf.js asset paths from process.cwd() when
it loads, with no option to change them. Outside `npm run dev` pdf.js could
not load predefined CMaps (UniGB-UCS2-H, GBK-EUC-H, Adobe-GB1-UCS2, ...) and
silently drew no text for those fonts; standard fonts fell back to the canvas.

- Add PDFPageRenderer as the only way into the converter. It points pdf.js
  at the installed pdfjs-dist, and fails loudly if the files are missing.
- Turn pdf.js warnings on, so a font it can't load shows up in the logs.
- Pin pdf-to-png-converter: the override relies on a non-public file, and
  npx installs without our lock file.
- verify:package renders a CJK sample through the packaged renderer, from an
  empty cwd. From the repo root the check would pass with the fix reverted.

Fixes #85
@haywolf
haywolf merged commit 14f4a19 into master Nov 5, 2026
2 checks passed
@haywolf
haywolf deleted the fix/85-pdfjs-asset-paths branch November 5, 2026 02:00
haywolf added a commit that referenced this pull request Nov 5, 2026
Removing sharp also removed the re-encoding it did on the way out: uploads
reached the model provider byte for byte, with their GPS position and device
details, and much larger (a 4000x3000 JPEG: 8.48 MB, was 3.29 MB).

- ImageSplitter re-encodes uploads through @napi-rs/canvas at sharp's default
  quality, and fails rather than fall back to the original. The Exif
  orientation is applied to the pixels, so photos stay upright.
- ModelService re-encodes any page file that may still hold metadata. Retrying
  a task reuses its page files, and those of older versions are plain copies.
- Reject truncated images. Unlike sharp, the decoder draws what's there and
  leaves the rest blank, so a page would silently lose its text.
- Pin @napi-rs/canvas to 1.0.11. Up to 1.0.3 it segfaults on damaged images,
  which would crash the app on each start, and 1.0.12 and 1.0.13 segfault in
  Electron as soon as they load. The load check now covers both.
- Mount all of scripts/ in the distro containers: the load check requires
  fixtures/count-dark-pixels.cjs, which the PDF rendering check of #92 can
  share instead of its use of sharp.
haywolf added a commit that referenced this pull request Nov 5, 2026
…-engines

Brings in #91 and #92, both of which still relied on sharp.

- pdf-render-check.cjs and the PDF rendering test count pixels through
  count-dark-pixels.cjs and @napi-rs/canvas.
- package-load-check.cjs: no checkSharp, and both checkPdfRendering and the
  Prisma engine it reports.
- ModelService: no sharp, keeping providerSuffix from #91 and the re-encoding.
- ProviderEndpoint.test: drop its mock of sharp.
haywolf added a commit that referenced this pull request Nov 5, 2026
* fix(db): apply migrations atomically and report init failures in the UI

A failed database initialization was swallowed: the app kept running with
every database feature broken and nothing telling the user why (#86).

- initDatabase returns its outcome and runs once, and the renderer shows a
  persistent alert with the error through app:getDatabaseStatus.
- Services that need the database don't start when it's unavailable.
- runMigrations throws on the first failure instead of logging it and going
  on to apply later migrations on top of an incomplete schema.
- Each migration runs in one transaction. Its statements could otherwise land
  on different pooled connections, whose stale schema cache made a RENAME after
  a DROP TABLE fail at random with "there is already another table", leaving
  the database half migrated: 19 of 640 replays failed before, 0 of 640 after.
- The window opens after the migrations, so that no query sees them half done.

* fix(linux): ship the Prisma engine of every distro and drop sharp

Linux packages only held the engine of the Ubuntu build machine, while Prisma
picks one from the distro family and libssl of the user's machine. On Fedora,
RHEL, openSUSE and any OpenSSL 1.1 distro it found none (#86).

- Generate debian/rhel x OpenSSL 1.1/3.0 engines on x64 and both OpenSSL arm64
  engines on arm64. binaryTargets comes from PRISMA_BINARY_TARGETS, set by
  scripts/prisma-cli.mjs, so a package never holds engines of the other arch.
- verify:package requires each of them, and verify:package:distros checks in
  containers that Fedora, Rocky 8 and Ubuntu 20.04 load the engine meant for
  them. Both CI and the release workflow run it.
- Leave the unused engine copies of the Prisma CLI out of the package.
- Remove sharp, which segfaults in Electron on Fedora 44: its libvips bundles
  glib while Electron links the system's. Conversion only used it to read the
  page image, so it now reads the file. EXIF is no longer stripped from
  uploaded images as a side effect.

* fix(split): re-encode uploaded images to drop their metadata

Removing sharp also removed the re-encoding it did on the way out: uploads
reached the model provider byte for byte, with their GPS position and device
details, and much larger (a 4000x3000 JPEG: 8.48 MB, was 3.29 MB).

- ImageSplitter re-encodes uploads through @napi-rs/canvas at sharp's default
  quality, and fails rather than fall back to the original. The Exif
  orientation is applied to the pixels, so photos stay upright.
- ModelService re-encodes any page file that may still hold metadata. Retrying
  a task reuses its page files, and those of older versions are plain copies.
- Reject truncated images. Unlike sharp, the decoder draws what's there and
  leaves the rest blank, so a page would silently lose its text.
- Pin @napi-rs/canvas to 1.0.11. Up to 1.0.3 it segfaults on damaged images,
  which would crash the app on each start, and 1.0.12 and 1.0.13 segfault in
  Electron as soon as they load. The load check now covers both.
- Mount all of scripts/ in the distro containers: the load check requires
  fixtures/count-dark-pixels.cjs, which the PDF rendering check of #92 can
  share instead of its use of sharp.

* fix(db): explain how to rebuild a database that can't be migrated

A database left half migrated by an older version, or one that lost the record
of a migration, fails on every start, while the alert only said to restart.

initDatabase now also returns the database path when the file is what's failing,
and the alert gives it with the steps to back it up and rebuild it. A missing
engine keeps the old text, since a new database would fail as well.

* docs: replace sharp and bare prisma commands
haywolf added a commit that referenced this pull request Nov 5, 2026
Conflicts in PDFSplitter.ts and its unit test. Batched rendering now goes
through renderPdfPages (#92), which also decides the pdf.js log level, so the
splitter's verbosityLevel: 0 is gone. PDFPageRenderer needed no change:
pagesToProcess already passes through.

strictPagesToProcess, which master still passed, is left out: PdfToPngOptions
of the pinned converter has no such option and nothing reads it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

打包版按 cwd 解析 pdf.js 的 CMap 和标准字体路径,中文 PDF 拆页时文字可能不绘制

1 participant