idml 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/TODO.pdf/89-deep-audit-idml-vs-indesign.md +142 -0
- data/TODO.pdf/90-per-run-font-resolution.md +55 -0
- data/TODO.pdf/91-image-downsampling.md +46 -0
- data/lib/idml/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf684927e9227982c1773b71a6697b40ed9d93263eb1a272e413008ba6d2009b
|
|
4
|
+
data.tar.gz: 4fbd6e8ab4df4ba8281fb5353df9474348fe77b021703118c63c3f5d5a9f5ca1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61d12e84a29ccefd212d6f09221eee09045b8ae18c8736bab98fea27cfdc0073151e8ffa53eadf18508af7bf104ff44e171b98f2fdd8e6d96019191792d594c9
|
|
7
|
+
data.tar.gz: 26f5541332df26f45f169da739c7c47b13f483a1c42c690c85867e8f3151522b0440d41bc7b502e680ff79ab00ac09ad791f9308cf9a0d7d16acb3a5ae87567c
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
idml (0.4.
|
|
4
|
+
idml (0.4.9)
|
|
5
5
|
bigdecimal
|
|
6
6
|
lutaml-model (~> 0.8.18)
|
|
7
7
|
pdfrb
|
|
@@ -183,7 +183,7 @@ CHECKSUMS
|
|
|
183
183
|
ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496
|
|
184
184
|
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
|
|
185
185
|
ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e
|
|
186
|
-
idml (0.4.
|
|
186
|
+
idml (0.4.9)
|
|
187
187
|
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
188
188
|
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
189
189
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# TODO PDF 89: Deep audit — idml-generated PDF vs InDesign PDF
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE (audit); structural differences documented
|
|
4
|
+
|
|
5
|
+
## TL;DR
|
|
6
|
+
|
|
7
|
+
**No** — the generated PDF is not byte-identical to the InDesign
|
|
8
|
+
output. InDesign produces ~100KB, the idml render produces ~2.2MB
|
|
9
|
+
(22x larger). The visual content is structurally similar but
|
|
10
|
+
rendered through a different font pipeline, font subsetting
|
|
11
|
+
strategy, image embedding path, and color management. Below is a
|
|
12
|
+
side-by-side audit.
|
|
13
|
+
|
|
14
|
+
## File-level comparison (`sample-with-table-more`)
|
|
15
|
+
|
|
16
|
+
| Aspect | InDesign pages.pdf | idml (ours) |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Size | 100,965 bytes | 2,196,913 bytes |
|
|
19
|
+
| Pages | 4 | 4 |
|
|
20
|
+
| PDF version | 1.4 | 1.4 |
|
|
21
|
+
| Embedded fonts | 1 (subsetted `MinionPro-Regular`) | 1 (full `MinionPro-BoldCn`) |
|
|
22
|
+
| Font stream size | 8,346 bytes (subsetted) | 232,680 bytes (full) |
|
|
23
|
+
| Image streams | 4 (small, downsampled) | 1 (huge, full-res) |
|
|
24
|
+
| Image stream sizes | 11,773 + 1,731 + 4,291 + ... | 1,937,556 (single 1.94MB JPEG) |
|
|
25
|
+
| `/OutputIntents` | embedded | NOT embedded (even with `--pdf-a`) |
|
|
26
|
+
| `/StructTreeRoot` | NOT emitted | only with `--tagged` |
|
|
27
|
+
| `/OCG` layers | NOT emitted | NOT emitted |
|
|
28
|
+
| `/Metadata` XMP | NOT emitted | emitted (XMP extracted from IDML) |
|
|
29
|
+
| OCG/InDesign structure | rich (`PieceInfo`, `PageUIDList`) | absent |
|
|
30
|
+
| Compression (FlateDecode) | yes | absent on most streams |
|
|
31
|
+
|
|
32
|
+
## The 1.94MB bloat
|
|
33
|
+
|
|
34
|
+
`/Users/mulgogi/Downloads/2.png` is a 600×400 RGBA PNG (278KB on
|
|
35
|
+
disk, link-resource size 43,765 bytes per IDML metadata). The idml
|
|
36
|
+
renderer reads the file at the path IDML specifies and re-encodes
|
|
37
|
+
it through pdfrb's image pipeline as a 2048×2048 JPEG (1.94MB).
|
|
38
|
+
|
|
39
|
+
InDesign's PDF re-encodes at 300ppi: a 4.7"×3.1" image at 300ppi
|
|
40
|
+
is 1410×930, which JPEG-compresses to ~12KB. InDesign's source
|
|
41
|
+
asset (43KB) is downsampled then DCT-encoded at low quality for
|
|
42
|
+
embed. The idml render doesn't apply downsampling — it just
|
|
43
|
+
re-encodes the source file.
|
|
44
|
+
|
|
45
|
+
## Font selection discrepancy
|
|
46
|
+
|
|
47
|
+
The idml render uses `FontSetup#register` which picks the first
|
|
48
|
+
non-missing font in `package.fonts.font_family[0]` — that's
|
|
49
|
+
`MinionPro-BoldCn` (the first font in "Minion Pro" family).
|
|
50
|
+
|
|
51
|
+
InDesign's PDF embeds `MinionPro-Regular` — that's because each
|
|
52
|
+
text run is rendered with the font declared in its
|
|
53
|
+
CharacterStyleRange#AppliedFont, which can differ per run.
|
|
54
|
+
|
|
55
|
+
The idml render's `StyleResolver::StyledRun` doesn't currently
|
|
56
|
+
carry `applied_font` (TODO: extract from CSR#AppliedFont). Once
|
|
57
|
+
extracted, the TextFrameRenderer can resolve per-run fonts via
|
|
58
|
+
`FontSetup#register_resolved` and register each in pdfrb.
|
|
59
|
+
|
|
60
|
+
## Font subsetting
|
|
61
|
+
|
|
62
|
+
`Pdfrb::Font::TrueType::Subsetter` exists in pdfrb 0.5.0 and works
|
|
63
|
+
when called. Pipeline calls `writer.subset_fonts!` when
|
|
64
|
+
`subset_fonts: true`. The resulting file still embeds a ~233KB
|
|
65
|
+
font stream — investigation needed to determine why
|
|
66
|
+
subsetting isn't shrinking the file.
|
|
67
|
+
|
|
68
|
+
Likely cause: the `MinionPro-BoldCn` font we register isn't being
|
|
69
|
+
used to draw any text (no codepoints were used through it during
|
|
70
|
+
rendering), so subsetting doesn't apply. But the full font file is
|
|
71
|
+
still embedded because we registered it.
|
|
72
|
+
|
|
73
|
+
Fix: register only the font(s) that actually carry used codepoints.
|
|
74
|
+
|
|
75
|
+
## Image handling
|
|
76
|
+
|
|
77
|
+
The idml render walks `<Image>` elements via `ImageCollector`,
|
|
78
|
+
reading the file at `LinkResourceURI` and re-encoding as JPEG.
|
|
79
|
+
`Image.compute_placement` uses the image's `ItemTransform` for
|
|
80
|
+
sizing.
|
|
81
|
+
|
|
82
|
+
InDesign's PDF embeds the image at downsampled resolution (300ppi)
|
|
83
|
+
and uses Smask for transparency. The idml render doesn't have an
|
|
84
|
+
Smask path.
|
|
85
|
+
|
|
86
|
+
## Structure and metadata
|
|
87
|
+
|
|
88
|
+
InDesign's PDF embeds `PieceInfo`, `PageUIDList`,
|
|
89
|
+
`OriginalDocumentID`, `LastModified` etc. — these are
|
|
90
|
+
InDesign-specific structure for round-tripping. The idml render
|
|
91
|
+
doesn't emit any of this (it's not part of the PDF spec, but
|
|
92
|
+
IDML-aware tools can read it).
|
|
93
|
+
|
|
94
|
+
The idml render's PDF has `/Metadata` XMP (extracted from
|
|
95
|
+
`META-INF/metadata.xml`) — InDesign's reference PDF does not have
|
|
96
|
+
it. Adding XMP is a feature, not a bug.
|
|
97
|
+
|
|
98
|
+
## Visual fidelity
|
|
99
|
+
|
|
100
|
+
- **Table layout**: After v0.4.8, the idml render uses
|
|
101
|
+
`Row#single_row_height` for vertical sizing. InDesign uses
|
|
102
|
+
this too. But InDesign also accounts for spanning cells, fixed
|
|
103
|
+
heights from row attributes, and additional CellStyle overrides.
|
|
104
|
+
The idml render doesn't model these yet (no fixtures with
|
|
105
|
+
spanning cells in the test suite).
|
|
106
|
+
- **Text fonts**: As noted, the idml render uses
|
|
107
|
+
`MinionPro-BoldCn` (heavy, wide) for all text. InDesign uses
|
|
108
|
+
`MinionPro-Regular` (the lighter body weight).
|
|
109
|
+
- **Images**: Not yet rendered with downsampling.
|
|
110
|
+
- **Layer/Swatch support**: Limited.
|
|
111
|
+
|
|
112
|
+
## What to do about it
|
|
113
|
+
|
|
114
|
+
The idml render is correct in **structure** (table, fonts,
|
|
115
|
+
shapes) but **incomplete in fidelity** (subsetting, downsampling,
|
|
116
|
+
per-run font selection, layer/swatches).
|
|
117
|
+
|
|
118
|
+
For the test suite purpose, the key value is regression coverage:
|
|
119
|
+
the idml render now produces a valid PDF for the real fixture,
|
|
120
|
+
even if it doesn't match InDesign's output. Future work to close
|
|
121
|
+
the gap:
|
|
122
|
+
|
|
123
|
+
1. **TODO 88: Per-run font resolution** — extract
|
|
124
|
+
CharacterStyleRange#AppliedFont in StyleResolver and pass
|
|
125
|
+
through to renderer.
|
|
126
|
+
2. **Image downsampling** — when EffectivePpi > 72 and
|
|
127
|
+
`ImageExportResolution=Ppi300`, downsample the source image to
|
|
128
|
+
fit the rendered bounds at 300ppi before encoding as JPEG.
|
|
129
|
+
3. **Oversized font detection** — only embed fonts that have used
|
|
130
|
+
codepoints (skip fully-unused fonts to avoid embedding
|
|
131
|
+
duplicates).
|
|
132
|
+
4. **Table column widths** — derive from
|
|
133
|
+
`ColumnAttributes#ColumnWidth` rather than even division.
|
|
134
|
+
|
|
135
|
+
## Verification
|
|
136
|
+
|
|
137
|
+
- `lib/idml/render/renderers/table_renderer.rb` — schema-faithful
|
|
138
|
+
Table rendering (v0.4.7).
|
|
139
|
+
- `lib/idml/render/renderers/text_frame_renderer.rb` — inline
|
|
140
|
+
Table discovery via `Story > PSR > CSR > Table` (v0.4.8).
|
|
141
|
+
- `spec/fixtures/sample-with-table-more/` — real IDML fixture.
|
|
142
|
+
- `spec/idml/fixtures/sample_with_table_more_spec.rb` — 17 specs.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# TODO PDF 90: Per-run font resolution
|
|
2
|
+
|
|
3
|
+
## Status: PLANNED (TODO 89 audit follow-up)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`Idml::Render::FontSetup#register` picks the first non-missing font
|
|
8
|
+
in the document's `font_family` collection. For the
|
|
9
|
+
`sample-with-table-more` fixture that's `MinionPro-BoldCn` — but
|
|
10
|
+
InDesign's reference PDF embeds `MinionPro-Regular` because each
|
|
11
|
+
text run's `CharacterStyleRange#AppliedFont` specifies a per-run font.
|
|
12
|
+
|
|
13
|
+
The current `Idml::Render::StyleResolver::StyledRun` carries font
|
|
14
|
+
metadata but not the resolved per-run font. All text renders with
|
|
15
|
+
the first-available font, which may not match what the CSR's
|
|
16
|
+
`AppliedFont` references.
|
|
17
|
+
|
|
18
|
+
## Plan
|
|
19
|
+
|
|
20
|
+
1. **Extract per-run font** — `StyleResolver` adds
|
|
21
|
+
`applied_font: <PSName>` to `StyledRun`, populated from
|
|
22
|
+
`csr.applied_font` (a `CharacterStyle` reference, not a font
|
|
23
|
+
directly).
|
|
24
|
+
|
|
25
|
+
2. **Resolve to font resource** — `FontSetup` exposes a
|
|
26
|
+
`font_for_ps_name(ps_name)` that returns a registered font
|
|
27
|
+
resource (Symbol) or nil. Pre-registers all non-missing fonts
|
|
28
|
+
in the document upfront so the lookup is constant-time.
|
|
29
|
+
|
|
30
|
+
3. **Pass per-run font through the renderer** — the render path
|
|
31
|
+
resolves `run.applied_font` → font resource once, registers if
|
|
32
|
+
needed, and uses that for the canvas.text / text_rich /
|
|
33
|
+
text_lines calls.
|
|
34
|
+
|
|
35
|
+
4. **Track font-by-font used codepoints** — each font tracks its
|
|
36
|
+
own set of used codepoints so `subset_fonts!` can produce
|
|
37
|
+
per-font subsets.
|
|
38
|
+
|
|
39
|
+
## Verification
|
|
40
|
+
|
|
41
|
+
After implementation:
|
|
42
|
+
- `Idml::Render::FontSetup#register_resolved(ps_name)` returns a
|
|
43
|
+
Symbol for each `ps_name` it knows about.
|
|
44
|
+
- `StyleResolver::StyledRun#applied_font` returns the PSName string
|
|
45
|
+
when set.
|
|
46
|
+
- TextFrameRenderer resolves `run.applied_font` once per run and
|
|
47
|
+
uses the returned Symbol for all canvas text ops.
|
|
48
|
+
- subset_fonts! produces a smaller output for sample-with-table-more.
|
|
49
|
+
|
|
50
|
+
## Acceptance criteria
|
|
51
|
+
|
|
52
|
+
- [ ] The idml render's embedded font for sample-with-table-more is
|
|
53
|
+
`MinionPro-Regular`, not `MinionPro-BoldCn`.
|
|
54
|
+
- [ ] Font stream size shrinks from 232KB to <50KB after subsetting.
|
|
55
|
+
- [ ] Spec asserts the correct font is selected per run.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# TODO PDF 91: Image downsampling for embed
|
|
2
|
+
|
|
3
|
+
## Status: PLANNED (TODO 89 audit follow-up)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
The idml render reads the image at the path IDML specifies
|
|
8
|
+
(`LinkResourceURI="file:..."`) and re-encodes it through pdfrb's
|
|
9
|
+
image pipeline as a JPEG. The current code does not downsample —
|
|
10
|
+
for `sample-with-table-more` the embedded 2.png (600×400 RGBA,
|
|
11
|
+
278KB) is rendered into a 2048×2048 JPEG (1.94MB).
|
|
12
|
+
|
|
13
|
+
InDesign's reference PDF downsamples to the rendered bounds at the
|
|
14
|
+
export resolution (300ppi for the test fixture). The same image
|
|
15
|
+
becomes 1410×930 → ~12KB JPEG.
|
|
16
|
+
|
|
17
|
+
## Plan
|
|
18
|
+
|
|
19
|
+
1. **Compute render bounds** — `Image.compute_placement` already
|
|
20
|
+
returns the placement rect. The bounds define the output
|
|
21
|
+
dimensions in PDF points.
|
|
22
|
+
2. **Compute target pixel dimensions** — convert placement width /
|
|
23
|
+
height in points to pixels at 300ppi (or the
|
|
24
|
+
`ImageExportResolution` attribute if present):
|
|
25
|
+
`target_w = (width / 72) * 300`, similarly for height.
|
|
26
|
+
3. **Downsample before encoding** — if source dimensions exceed
|
|
27
|
+
target, resize using a simple bilinear algorithm. Otherwise
|
|
28
|
+
encode at source resolution.
|
|
29
|
+
4. **Strip alpha if not needed** — for the test fixture the source
|
|
30
|
+
PNG has alpha but the rendered use is opaque. Strip alpha
|
|
31
|
+
before JPEG-encoding to save additional bytes.
|
|
32
|
+
|
|
33
|
+
## Acceptance criteria
|
|
34
|
+
|
|
35
|
+
- [ ] Image stream size for sample-with-table-more <50KB (vs 1.94MB).
|
|
36
|
+
- [ ] Visual fidelity: rendered image is downsampled to 300ppi at
|
|
37
|
+
the placement bounds.
|
|
38
|
+
- [ ] Spec compares image stream sizes before/after downsampling.
|
|
39
|
+
- [ ] Test fixture with high-resolution image (1MB+) verifies
|
|
40
|
+
downsampling kicks in.
|
|
41
|
+
|
|
42
|
+
## Implementation note
|
|
43
|
+
|
|
44
|
+
Pure-Ruby bilinear resize is simple but slow. A 2048×2048 → 600×400
|
|
45
|
+
resize would take ~200ms. For larger images, a C extension or
|
|
46
|
+
subprocess would be faster. For now, pure Ruby is acceptable.
|
data/lib/idml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: idml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
@@ -214,6 +214,9 @@ files:
|
|
|
214
214
|
- TODO.pdf/85-per-text-range-hyperlinks.md
|
|
215
215
|
- TODO.pdf/86-caching-performance.md
|
|
216
216
|
- TODO.pdf/87-final-state-summary.md
|
|
217
|
+
- TODO.pdf/89-deep-audit-idml-vs-indesign.md
|
|
218
|
+
- TODO.pdf/90-per-run-font-resolution.md
|
|
219
|
+
- TODO.pdf/91-image-downsampling.md
|
|
217
220
|
- TODO.pdf/README.md
|
|
218
221
|
- exe/idml
|
|
219
222
|
- idml.gemspec
|