DEV Community

Cover image for Why My Install Zip Won't Upload: 89MB, and Half Is a PDF Library's Fonts
HideyukiMORI
HideyukiMORI

Posted on • Edited on

Why My Install Zip Won't Upload: 89MB, and Half Is a PDF Library's Fonts

My install package is 89MB. A lot of Japanese shared hosts cap uploads somewhere between 2 and 50MB. So the simplest distribution path I offer — download a zip, upload it through a control panel — just doesn't work for a chunk of the people I built it for.

Docker never told me this. A container doesn't care about a 90MB layer. A shared host's upload form does, and that's a constraint my whole dev setup was structurally blind to.

I blamed the wrong fonts

My first instinct was the heading fonts. The app bundles IPAex for headings — a few megabytes of TTF — and that was the obvious suspect. I even had a build report that agreed with me and pointed the finger there.

Then I actually unzipped the release and measured.

IPAex wasn't the problem. The heading fonts were about 9MB compressed — real, but not the story. The story was the PDF library's bundled CJK fonts. Two files alone:

  • Sun-ExtA.ttf — about 22MB on disk
  • Sun-ExtB.ttf — about 17MB on disk

Those two, plus the rest of that library's font directory, were the single largest region of the package by a wide margin — roughly half the compressed zip. I'd been ready to optimize the 9MB I could see instead of the 40MB I couldn't, because I trusted a summary instead of the artifact.

Lesson one, before anything else: when something is too big, measure where the bytes actually are. Not where a report says. Where unzip -v says.

Why the fonts can't just go

The tempting move is "delete the giant fonts." I can't.

The app generates PDF invoices, and it has to render Japanese — including the rare Extension-B kanji that show up in real personal and place names (the kind where someone's surname uses a character most fonts don't have). Drop those fonts and the invoice renders tofu — little empty boxes — for exactly the customers whose names are unusual. For a compliance document, that's not a cosmetic bug. It's wrong output.

So the fonts are load-bearing. The size isn't waste; it's the cost of rendering a whole writing system correctly.

The design (which I have not shipped)

Here's where I have to be careful, because it would be easy to write this as a finished story. It isn't one.

What I've designed — and written up as an architecture decision, not yet built — is a deferred font pack:

  • Ship only the minimal fonts the PDF engine needs to boot, plus the small heading set. That gets the base package under typical upload limits.
  • Publish the big CJK fonts as a separate, versioned, signed artifact. Verify it on arrival with a SHA-256 and a bundled public key; refuse to use it if the signature doesn't match.
  • Fetch it two ways: server-side over HTTPS by default, with a manual upload fallback for hosts that block outbound connections.
  • Fail closed. Before generating a PDF, check that the required fonts are present. If they're missing and can't be fetched, don't emit a tofu invoice — stop with a clear error.

That's the plan. The architecture decision is written down and currently marked proposed; the implementation issue is open; there is no slimmed build yet.

Which means I'm not going to tell you it dropped to 20MB or 40MB or any number, because I haven't built it and measured it. I've seen enough of my own writing lately claim outcomes that hadn't happened. The honest version is: here's an 89MB problem, here's where the bytes are, and here's the design I'm going to try. If the slimmed number turns out interesting, that's a follow-up post with a real measurement in it.

The takeaway that generalizes

Two things I'll actually carry forward:

Distribution size is a first-class constraint, and Docker hides it. If you ship anything people install by hand — a plugin, a zip, an appliance — the size limit lives in someone's upload form, and your container-based workflow will never surface it. Put it in CI or a checklist, because you won't feel it otherwise.

Optimize the measured bytes, not the visible ones. The fonts I could name were not the fonts that mattered. I nearly spent effort on the 9MB in front of me because I hadn't opened the box.

What's unexpectedly blown up an artifact or image size for you — and when you finally measured, was it the thing you first blamed?

── Hideyuki Mori (Ayane International) 🔗 hideyuki-mori.com

Top comments (3)

Collapse
 
sebas_flores profile image
Sebas Flores

Hi! :)
The deferred font package sounds like the right direction, especially with the manual-upload fallback.

One additional check I would add is an artifact size budget in CI. For example, fail or warn when the installation ZIP exceeds a defined limit, and print the largest files or directories in the build output. That would make this type of regression visible before release.

I would also verify whether the PDF library supports font subsetting when generating the final PDF. It would not reduce the installer size because the complete font is still needed at runtime, but it could keep each generated invoice much smaller.

For the external font package, using a versioned manifest containing the SHA-256, expected file sizes and compatible application versions could also help detect partial or incorrect installations.

Collapse
 
hideyukimori profile image
HideyukiMORI

Hi Sebas — thanks, all three of these land.

The size budget is the one that stings, because the regression was visible in the artifact for weeks and nothing was positioned to look. A CI step that fails over a threshold and prints the largest entries would have caught it at the first oversized build instead of at upload time. Adopted — it's now a dated task on the release pipeline's list, not a "someday". And the "print the largest files" half matters as much as the gate: my diagnosis was exactly that command, run manually, after the fact.

On subsetting, I went and checked rather than answer from memory: the PDF library (mPDF) subsets embedded TTFs by default, and we don't override it — so each generated invoice already embeds only the glyphs it uses. Your prediction about the installer is exactly right though: the full fonts (~14 MB of Japanese typefaces alone) have to ship regardless, because they're the generation-time input. So half of your suggestion turned out to be already true, which I'd rather discover than assume.

And the versioned manifest closes the gap the manual-upload fallback opens: a folder that exists proves something was created, not that it's complete or correct. SHA-256 plus expected sizes plus compatible app versions turns "hopefully installed" into "verifiably installed". That's the shape the deferred package should take.

Collapse
 
hideyukimori profile image
HideyukiMORI

A correction you didn't ask for: when I said the size budget was "a dated task, not a someday", that was true when I wrote it and it isn't now. The date came off yesterday, when I re-paced that whole lane into a weekly quality slot — so the thing I told you to reassure you has quietly turned into a claim I can't support. The work itself hasn't happened either. The issue has been open since July 31st, and there is still no step in the release pipeline that looks at artifact size.

I'm deliberately not handing you a new date, or a different mechanism to trust in place of the one that just lapsed — naming a second mechanism immediately after the first one failed is the same move twice, and you'd have no more reason to believe it. What you'll get instead is the actual report when it lands: the threshold, whether it fired on a real build, and specifically whether the "print the largest entries" half made it in, since we both said that half carries as much weight as the gate does.