idml 0.2.5 → 0.2.6
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/40-font-ps-name-resolution.md +40 -28
- data/TODO.pdf/52-font-subsetting.md +34 -4
- data/TODO.pdf/55-dead-code-deprecation.md +15 -21
- data/TODO.pdf/63-replace-fontmetrics-with-pdfrb.md +60 -18
- data/TODO.pdf/65-pdfrb-019-integration.md +16 -16
- data/TODO.pdf/67-text-rich-multi-run.md +51 -0
- data/TODO.pdf/68-radial-gradient-shadings.md +36 -0
- data/TODO.pdf/69-transparency-blend-modes.md +84 -0
- data/TODO.pdf/70-stroke-styling.md +60 -0
- data/lib/idml/elements/graphic_line.rb +2 -0
- data/lib/idml/elements/polygon.rb +2 -0
- data/lib/idml/elements/rectangle.rb +2 -0
- data/lib/idml/elements/text_frame.rb +2 -0
- data/lib/idml/elements/transparency_setting.rb +25 -0
- data/lib/idml/render/blending.rb +60 -0
- data/lib/idml/render/renderers/graphic_line_renderer.rb +1 -1
- data/lib/idml/render/renderers/polygon_renderer.rb +1 -1
- data/lib/idml/render/renderers/rectangle_renderer.rb +35 -16
- data/lib/idml/render.rb +1 -0
- data/lib/idml/version.rb +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02ab670c36492471dd8dd986380fd180826e9ce250d5e22dde6fb5fda18b9bd3
|
|
4
|
+
data.tar.gz: 73d2c392dee732f12b1551a445f5e1a866a551885742adf3552d81abe0c3e9d4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b5c44e670ba295c964c6b58f6cecab0dcde43b579d4e4eb282e2d5b66cf567df34f943377a84510df7edd84d831892b3f3d6e13d7a4b13eb5311058377b94494
|
|
7
|
+
data.tar.gz: d1692d7d0ed411358d2ed877497abe2838fe25d2a1aa6a9fc534561b116743fd0a4e279819ea0206b8dfb5ecaf29c8f806e4341cca941267a2c2dc71193dbd9e
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
idml (0.2.
|
|
4
|
+
idml (0.2.6)
|
|
5
5
|
bigdecimal
|
|
6
6
|
fontisan
|
|
7
7
|
lutaml-model (~> 0.8.18)
|
|
@@ -198,7 +198,7 @@ CHECKSUMS
|
|
|
198
198
|
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
|
|
199
199
|
ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e
|
|
200
200
|
fontisan (0.4.45) sha256=2337ca0205f806647b24b8cac57aa34f3769473026ae6f52dc36bde0feed6c62
|
|
201
|
-
idml (0.2.
|
|
201
|
+
idml (0.2.6)
|
|
202
202
|
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
203
203
|
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
204
204
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
# TODO PDF 40: Font PS name resolution from Fonts.xml
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
3
|
+
## Status: DONE
|
|
4
|
+
|
|
5
|
+
## What was implemented
|
|
6
|
+
|
|
7
|
+
The pipeline now resolves document fonts from `Resources/Fonts.xml` via
|
|
8
|
+
PostScriptName lookup, falling back to the default font when the file
|
|
9
|
+
isn't found on disk:
|
|
10
|
+
|
|
11
|
+
1. `Idml::TextEngine::FontResolver#resolve_by_ps_name(ps_name)`
|
|
12
|
+
walks system font directories and matches on each candidate font's
|
|
13
|
+
internal PostScript name (read by Fontisan).
|
|
14
|
+
2. `Idml::Render::Pipeline#resolve_document_font_path` iterates
|
|
15
|
+
`Parts::Fonts#font_family` → `FontFamily#font`, skips entries with
|
|
16
|
+
`Status="Missing"`, and returns the first match.
|
|
17
|
+
3. `Pipeline#register_font` rescues any resolver failure and returns
|
|
18
|
+
`Render::DEFAULT_FONT` (`"Helvetica"`) so rendering never crashes
|
|
19
|
+
when a font file is absent.
|
|
20
|
+
4. The chosen font name is threaded through `RenderContext` to every
|
|
21
|
+
renderer via `font_ps_name`.
|
|
22
|
+
|
|
23
|
+
## Verification
|
|
24
|
+
|
|
25
|
+
- `lib/idml/render/pipeline.rb:170` — `resolve_document_font_path`
|
|
26
|
+
- `lib/idml/render/pipeline.rb:185` — `resolve_by_ps_name` call
|
|
27
|
+
- `lib/idml/text_engine/font_resolver.rb:38` — PS-name search
|
|
28
|
+
|
|
29
|
+
## Acceptance criteria
|
|
30
|
+
|
|
31
|
+
- [x] Parts::Fonts typed model parses FontFamily → Font entries.
|
|
32
|
+
- [x] FontFamily and Font element models expose PostScriptName.
|
|
33
|
+
- [x] PostScriptName-based search added to FontResolver.
|
|
34
|
+
- [x] Pipeline passes document font references to the resolver.
|
|
35
|
+
- [x] Falls back to default font when document font can't be found.
|
|
36
|
+
|
|
37
|
+
## Out of scope
|
|
38
|
+
|
|
39
|
+
Per-run font selection (each `CharacterStyleRange#applied_font`
|
|
40
|
+
mapped to its own registered font) requires TODO 67's multi-font
|
|
41
|
+
`text_rich` integration and is tracked there. This TODO covers the
|
|
42
|
+
document-default font, which is what every renderer uses today.
|
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
# TODO PDF 52: Font subsetting via pdfrb
|
|
2
2
|
|
|
3
|
+
## Status: BLOCKED (pdfrb has no subsetting API)
|
|
4
|
+
|
|
3
5
|
## Goal
|
|
4
6
|
|
|
5
7
|
Subset embedded TrueType fonts to include only the glyphs actually used
|
|
6
8
|
in the document. This dramatically reduces PDF file size (from full font
|
|
7
9
|
~500KB to subset ~5-20KB per font).
|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## Blocker
|
|
12
|
+
|
|
13
|
+
pdfrb 0.4.0 exposes no subsetting API:
|
|
14
|
+
|
|
15
|
+
- `Pdfrb::Document::Fonts#add` accepts `**opts` but ignores `subset:`.
|
|
16
|
+
- No `used_codepoints` collector.
|
|
17
|
+
- `glyph_width` returns stub 500 for TrueType — the per-glyph data
|
|
18
|
+
needed to build a subset is unavailable.
|
|
19
|
+
|
|
20
|
+
Without per-glyph metrics, subsetting cannot be implemented in the
|
|
21
|
+
caller either. This is also the same blocker that blocks TODO 63
|
|
22
|
+
(replace FontMetrics with pdfrb).
|
|
23
|
+
|
|
24
|
+
## Path forward
|
|
25
|
+
|
|
26
|
+
Either:
|
|
27
|
+
|
|
28
|
+
1. pdfrb adds a subsetting API that consumes a `used_codepoints:` set
|
|
29
|
+
and emits a subsetted FontFile2 at write time, OR
|
|
30
|
+
2. pdfrb grows real TTF table parsing (head/hmtx/cmap) so the caller
|
|
31
|
+
can collect used codepoints, build the subset, and pass the bytes
|
|
32
|
+
back to `Fonts#add` as pre-subsetted data.
|
|
33
|
+
|
|
34
|
+
Once pdfrb unblocks, the idml side needs to:
|
|
35
|
+
|
|
36
|
+
1. Pipeline collects all Unicode codepoints across all stories.
|
|
37
|
+
2. Pipeline passes `used_codepoints: set` to `Fonts#add(subset: true, ...)`.
|
|
38
|
+
3. Spec renders text with known characters and verifies the embedded
|
|
39
|
+
font only contains those glyphs.
|
|
40
|
+
|
|
41
|
+
## Acceptance criteria (after pdfrb unblocks)
|
|
10
42
|
|
|
11
43
|
- [ ] Pipeline collects all Unicode codepoints used across all stories.
|
|
12
44
|
- [ ] For each embedded font, only the used glyphs are included.
|
|
@@ -17,6 +49,4 @@ in the document. This dramatically reduces PDF file size (from full font
|
|
|
17
49
|
|
|
18
50
|
## Dependencies
|
|
19
51
|
|
|
20
|
-
- pdfrb
|
|
21
|
-
FontFile2 embedding may require pdfrb enhancement).
|
|
22
|
-
- TODO 27 (text engine for glyph collection).
|
|
52
|
+
- pdfrb subsetting API (NOT YET AVAILABLE in 0.4.0).
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
# TODO PDF 55: Dead code deprecation markers
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Status: DONE (modules already removed)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
superseded by pdfrb but cannot be deleted per the project's
|
|
7
|
-
"never delete source files" policy. Add deprecation comments and
|
|
8
|
-
update autoloads to lazy-load only when explicitly referenced.
|
|
5
|
+
## What was done
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
The old hand-rolled modules have already been deleted from the codebase
|
|
8
|
+
during the pdfrb migration (TODO 51 + TODO 62). No `Idml::Render::PdfWriter`,
|
|
9
|
+
`Idml::Render::FontEmbedder`, `Idml::Render::Color`, `Idml::Render::Path`,
|
|
10
|
+
or `Idml::Render::Text` references remain in `lib/` or `spec/`.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- `Idml::Render::Path` — replaced by Canvas drawing methods
|
|
16
|
-
- `Idml::Render::Text` — replaced by Canvas text method
|
|
12
|
+
The original goal — deprecation comments on dead modules — is moot:
|
|
13
|
+
there are no dead modules to deprecate. This TODO is closed without
|
|
14
|
+
further work.
|
|
17
15
|
|
|
18
|
-
##
|
|
16
|
+
## Verification
|
|
19
17
|
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
- [ ] No active code path (Pipeline, SpreadRenderer, renderers) references
|
|
23
|
-
deprecated modules.
|
|
24
|
-
- [ ] Tests for deprecated modules are tagged with `:deprecated` or
|
|
25
|
-
moved to a separate spec file.
|
|
26
|
-
- [ ] `render.rb` autoloads keep the old modules for backward compatibility.
|
|
18
|
+
`grep -rn "PdfWriter|FontEmbedder|Render::Color\b|Render::Path\b|Render::Text\b" lib/ spec/`
|
|
19
|
+
returns zero matches.
|
|
27
20
|
|
|
28
|
-
##
|
|
21
|
+
## Acceptance criteria
|
|
29
22
|
|
|
30
|
-
-
|
|
23
|
+
- [x] No live code references the old hand-rolled modules.
|
|
24
|
+
- [x] `bundle exec rake` is green (2314 examples, 0 failures).
|
|
@@ -1,31 +1,73 @@
|
|
|
1
1
|
# TODO PDF 63: Replace FontMetrics with pdfrb measurement API
|
|
2
2
|
|
|
3
|
+
## Status: BLOCKED (pdfrb measurement APIs are stubs)
|
|
4
|
+
|
|
3
5
|
## Goal
|
|
4
6
|
|
|
5
7
|
Replace `Idml::TextEngine::FontMetrics` (200+ lines of TTF binary parsing
|
|
6
|
-
via Fontisan) with pdfrb
|
|
7
|
-
|
|
8
|
+
via Fontisan) with pdfrb's native `Fonts#measure_text`, `#glyph_width`,
|
|
9
|
+
and `#metrics_for`. Eliminates the Fontisan dependency for text layout.
|
|
10
|
+
|
|
11
|
+
## Blocker
|
|
12
|
+
|
|
13
|
+
pdfrb 0.4.0 ships the API surface but the implementations are stubs
|
|
14
|
+
that return constant values, not real per-glyph widths:
|
|
15
|
+
|
|
16
|
+
`/Users/mulgogi/src/claricle/pdfrb/lib/pdfrb/document/fonts.rb`:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
def measure_text(text, font:, size:)
|
|
20
|
+
return 0 unless text
|
|
21
|
+
# TODO: use font metrics for per-glyph width lookup
|
|
22
|
+
text.to_s.length * (size || 0).to_f * 0.5
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def glyph_width(_char, _resource)
|
|
26
|
+
500
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def metrics_for(_resource)
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The Shaper and LineBreaker depend on accurate per-glyph widths for
|
|
35
|
+
correct word-wrap. With stub data, line breaks land at the wrong
|
|
36
|
+
positions and run advance (needed for TODO 67's `text_rich`) is wrong.
|
|
37
|
+
|
|
38
|
+
Fontisan correctly parses head/hhea/hmtx/cmap tables for any TTF/OTF
|
|
39
|
+
and exposes per-glyph advance widths. Until pdfrb either grows real
|
|
40
|
+
TTF parsing or accepts externally-provided metrics (see
|
|
41
|
+
`/Users/mulgogi/src/claricle/pdfrb/PROPOSAL.external-font-metrics.md`),
|
|
42
|
+
Fontisan stays.
|
|
43
|
+
|
|
44
|
+
## Path forward
|
|
8
45
|
|
|
9
|
-
|
|
46
|
+
Proposal at `~/src/claricle/pdfrb/PROPOSAL.external-font-metrics.md`
|
|
47
|
+
suggests letting `Fonts#add` accept `widths:`/`metrics:`/`encoding:`
|
|
48
|
+
hashes from the caller, so pdfrb does not need to parse TTF tables
|
|
49
|
+
itself. The idml gem would then build those hashes from FontMetrics
|
|
50
|
+
and pass them in — keeping Fontisan as the parser while pdfrb handles
|
|
51
|
+
only PDF assembly. This is the Unix-philosophy split.
|
|
10
52
|
|
|
11
|
-
|
|
12
|
-
- `document.fonts.measure_text(text, font:, size:)` → width in points
|
|
13
|
-
- `document.fonts.metrics_for(font)` → { ascent:, descent:, cap_height:, ... }
|
|
53
|
+
Once that proposal lands:
|
|
14
54
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
55
|
+
1. `PdfrbWriter#register_font_with_metrics(path, widths:, metrics:, ...)`.
|
|
56
|
+
2. Pipeline resolves fonts via FontResolver (Fontisan) and passes the
|
|
57
|
+
resulting metrics to pdfrb.
|
|
58
|
+
3. Internal `FontMetrics` may still exist as the Fontisan adapter, but
|
|
59
|
+
Shaper/LineBreaker no longer need to read it — they go through
|
|
60
|
+
`pdfrb.measure_text`.
|
|
18
61
|
|
|
19
|
-
##
|
|
62
|
+
## Acceptance criteria (after pdfrb unblocks)
|
|
20
63
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
3. Remove `fontisan` from gemspec dependencies.
|
|
26
|
-
4. Remove `text_engine/font_metrics.rb` (200+ lines).
|
|
64
|
+
- [ ] Pipeline registers fonts with externally-provided metrics.
|
|
65
|
+
- [ ] Shaper/LineBreaker call pdfrb's measurement API, not FontMetrics.
|
|
66
|
+
- [ ] `fontisan` remains a dependency for parsing, but no longer feeds
|
|
67
|
+
Shaper/LineBreaker directly.
|
|
27
68
|
|
|
28
69
|
## Dependencies
|
|
29
70
|
|
|
30
|
-
- pdfrb 0.
|
|
31
|
-
|
|
71
|
+
- pdfrb 0.4.0's `Fonts#measure_text`/`#glyph_width`/`#metrics_for`
|
|
72
|
+
with real implementations OR
|
|
73
|
+
- pdfrb accepts the external-metrics proposal linked above.
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
# TODO PDF 65: pdfrb 0.19.0 feature integration
|
|
2
2
|
|
|
3
|
-
## Status: PARTIALLY DONE
|
|
3
|
+
## Status: PARTIALLY DONE (remainder blocked)
|
|
4
4
|
|
|
5
5
|
## What was implemented
|
|
6
6
|
|
|
7
|
-
1. **Canvas#text_lines**: TextFrameRenderer uses `canvas.text_lines`
|
|
7
|
+
1. **Canvas#text_lines**: `TextFrameRenderer` uses `canvas.text_lines`
|
|
8
8
|
instead of N separate `canvas.text` calls per line. Single batch
|
|
9
9
|
call for all lines within a run.
|
|
10
10
|
|
|
11
|
-
## What remains
|
|
11
|
+
## What remains
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
replace FontMetrics/Fontisan until this is fixed.
|
|
13
|
+
Blocked by pdfrb 0.4.0 stubs in `Fonts#measure_text` (returns
|
|
14
|
+
`length * 0.5 * size`) and `#glyph_width` (returns 500). Until real
|
|
15
|
+
per-glyph widths land:
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
4. **Font subsetting**: `Fonts#add` accepts `**opts`
|
|
22
|
-
|
|
17
|
+
2. **`text_rich` for multi-run text**: tracked in TODO 67. Cannot
|
|
18
|
+
advance between runs without real measurement.
|
|
19
|
+
3. **Replace FontMetrics with pdfrb measurement**: tracked in TODO 63.
|
|
20
|
+
4. **Font subsetting**: tracked in TODO 52. `Fonts#add` accepts `**opts`
|
|
21
|
+
but `subset:` has no effect.
|
|
23
22
|
|
|
24
23
|
## Acceptance criteria
|
|
25
24
|
|
|
26
|
-
- [x] TextFrameRenderer uses canvas.text_lines
|
|
27
|
-
- [ ] FontMetrics replaced with pdfrb glyph_width (blocked
|
|
28
|
-
- [ ] FontResolver replaced with Pdfrb::FontResolver (blocked
|
|
29
|
-
- [ ] Font subsetting verified
|
|
25
|
+
- [x] TextFrameRenderer uses `canvas.text_lines`
|
|
26
|
+
- [ ] FontMetrics replaced with pdfrb `glyph_width` (blocked — TODO 63)
|
|
27
|
+
- [ ] FontResolver replaced with `Pdfrb::FontResolver` (blocked — TODO 63)
|
|
28
|
+
- [ ] Font subsetting verified (blocked — TODO 52)
|
|
29
|
+
- [ ] `text_rich` used for multi-run frames (blocked — TODO 67)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# TODO PDF 67: Multi-run text batching via Canvas#text_rich
|
|
2
|
+
|
|
3
|
+
## Status: BLOCKED (pdfrb measure_text is a stub)
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Replace the per-run `Canvas#text` calls in `TextFrameRenderer#simple_render`
|
|
8
|
+
with a single `Canvas#text_rich` call per text frame. `text_rich` emits
|
|
9
|
+
all runs inside one BT/ET block, advancing the text matrix between runs.
|
|
10
|
+
|
|
11
|
+
## Motivation
|
|
12
|
+
|
|
13
|
+
`text_rich` is the right primitive for multi-run text — one begin/end
|
|
14
|
+
text block per frame instead of N. But its run-advance relies on
|
|
15
|
+
`Pdfrb::Document::Fonts#measure_text`, which in pdfrb 0.4.0 returns a
|
|
16
|
+
stub `length * 0.5 * size`. Without accurate advance, runs overwrite
|
|
17
|
+
each other on the line.
|
|
18
|
+
|
|
19
|
+
## Blocker
|
|
20
|
+
|
|
21
|
+
`/Users/mulgogi/src/claricle/pdfrb/lib/pdfrb/document/fonts.rb:65`:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
def measure_text(text, font:, size:)
|
|
25
|
+
return 0 unless text
|
|
26
|
+
# TODO: use font metrics for per-glyph width lookup
|
|
27
|
+
text.to_s.length * (size || 0).to_f * 0.5
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`glyph_width` (line 79) is also a stub returning 500, and `metrics_for`
|
|
32
|
+
(line 87) returns nil. These need real Fontisan-backed implementations
|
|
33
|
+
before text_rich produces correct output.
|
|
34
|
+
|
|
35
|
+
## Plan (after pdfrb unblocks)
|
|
36
|
+
|
|
37
|
+
1. Build `runs` array of `{ text:, font:, size:, color: }` per line.
|
|
38
|
+
2. Replace `simple_render` body with `canvas.text_lines(..., rich: true)`
|
|
39
|
+
or `canvas.text_rich(runs, at: [x, y])`.
|
|
40
|
+
3. Verify rendered PDF: runs no longer overwrite, multi-color lines work.
|
|
41
|
+
|
|
42
|
+
## Acceptance criteria
|
|
43
|
+
|
|
44
|
+
- [ ] `TextFrameRenderer#simple_render` uses `canvas.text_rich`.
|
|
45
|
+
- [ ] Render spec verifies a multi-run line has correct horizontal advance.
|
|
46
|
+
- [ ] Single BT/ET block per frame (assertion on PDF content stream).
|
|
47
|
+
|
|
48
|
+
## Dependencies
|
|
49
|
+
|
|
50
|
+
- pdfrb 0.4.0 `Fonts#measure_text` returns real per-glyph widths.
|
|
51
|
+
- pdfrb 0.4.0 `Fonts#glyph_width` not returning stub 500.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# TODO PDF 68: Radial gradient shadings via pdfrb 0.4.0
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
RectangleRenderer routes gradient fills to either `Shadings#add_axial`
|
|
6
|
+
(linear) or `Shadings#add_radial` (radial) based on the IDML
|
|
7
|
+
`Gradient.type` attribute (`"Linear"` vs `"Radial"`).
|
|
8
|
+
|
|
9
|
+
## Background
|
|
10
|
+
|
|
11
|
+
IDML `<Gradient Type="...">` supports two gradient shapes:
|
|
12
|
+
- `"Linear"` (default) — colour stops along a straight axis
|
|
13
|
+
- `"Radial"` — colour stops along a radius from a centre point
|
|
14
|
+
|
|
15
|
+
pdfrb 0.4.0 exposes both:
|
|
16
|
+
- `document.shadings.add_axial(from:, to:, stops:, extend:)`
|
|
17
|
+
- `document.shadings.add_radial(from:, to:, stops:, extend:)`
|
|
18
|
+
|
|
19
|
+
Before this TODO, every gradient was rendered as axial regardless of the
|
|
20
|
+
declared type, producing incorrect output for radial swatches.
|
|
21
|
+
|
|
22
|
+
## Implementation
|
|
23
|
+
|
|
24
|
+
1. Read `Gradient#type` (already modelled on `Elements::Gradient`).
|
|
25
|
+
2. In `RectangleRenderer.render_gradient_fill`, dispatch:
|
|
26
|
+
- `type == "Radial"` → `add_radial` with centre-to-edge coords.
|
|
27
|
+
- otherwise → `add_axial` (existing behaviour).
|
|
28
|
+
3. Radial coordinates use the box centre as both `from` and the
|
|
29
|
+
near corner of `to` so the shading fills the rectangle.
|
|
30
|
+
|
|
31
|
+
## Acceptance criteria
|
|
32
|
+
|
|
33
|
+
- [x] `Gradient.type == "Radial"` produces a pdfrb radial shading.
|
|
34
|
+
- [x] `Gradient.type == "Linear"` (or nil) keeps producing an axial shading.
|
|
35
|
+
- [x] Spec covers both branches.
|
|
36
|
+
- [x] All existing specs still pass.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# TODO PDF 69: Transparency and blend modes via pdfrb 0.4.0
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Honor IDML `<BlendingSetting BlendMode="..." Opacity="..."/>` on page
|
|
6
|
+
items by routing through pdfrb's `Canvas#with_transparency` and
|
|
7
|
+
`Canvas#blend_mode=`. Applies to Rectangle, Polygon, GraphicLine, and
|
|
8
|
+
TextFrame fill+stroke paths.
|
|
9
|
+
|
|
10
|
+
## Background
|
|
11
|
+
|
|
12
|
+
IDML models transparency as nested elements under each page item:
|
|
13
|
+
|
|
14
|
+
```xml
|
|
15
|
+
<Rectangle ...>
|
|
16
|
+
<TransparencySetting>
|
|
17
|
+
<BlendingSetting BlendMode="Multiply" Opacity="0.5"
|
|
18
|
+
KnockoutGroup="true" IsolateBlending="false"/>
|
|
19
|
+
<DropShadowSetting .../> <!-- not handled here -->
|
|
20
|
+
</TransparencySetting>
|
|
21
|
+
</Rectangle>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`TransparencySetting` lives as a child of the page item (sibling of
|
|
25
|
+
`Properties`). Its first `BlendingSetting` child carries the blend mode
|
|
26
|
+
and opacity that apply to the item's entire drawing.
|
|
27
|
+
|
|
28
|
+
pdfrb 0.4.0 exposes the matching primitives:
|
|
29
|
+
|
|
30
|
+
- `Canvas#opacity=` — sets alpha for both fill and stroke (ExtGState `ca`/`CA`).
|
|
31
|
+
- `Canvas#blend_mode=` — sets BM name.
|
|
32
|
+
- `Canvas#with_transparency(opacity:, blend_mode:) { ... }` — saves graphics state, applies both, restores on exit.
|
|
33
|
+
|
|
34
|
+
## IDML → PDF blend-mode mapping
|
|
35
|
+
|
|
36
|
+
IDML's `BlendMode` enumeration maps to PDF's `BM` names where they line
|
|
37
|
+
up; PDF does not have equivalents for every IDML mode. Initial mapping:
|
|
38
|
+
|
|
39
|
+
| IDML | PDF |
|
|
40
|
+
|--------------------|---------------|
|
|
41
|
+
| `Normal` | `Normal` |
|
|
42
|
+
| `Multiply` | `Multiply` |
|
|
43
|
+
| `Screen` | `Screen` |
|
|
44
|
+
| `Overlay` | `Overlay` |
|
|
45
|
+
| `SoftLight` | `SoftLight` |
|
|
46
|
+
| `HardLight` | `HardLight` |
|
|
47
|
+
| `Darken` | `Darken` |
|
|
48
|
+
| `Lighten` | `Lighten` |
|
|
49
|
+
| `ColorDodge` | `ColorDodge` |
|
|
50
|
+
| `ColorBurn` | `ColorBurn` |
|
|
51
|
+
| `Difference` | `Difference` |
|
|
52
|
+
| `Exclusion` | `Exclusion` |
|
|
53
|
+
| `Hue` | `Hue` |
|
|
54
|
+
| `Saturation` | `Saturation` |
|
|
55
|
+
| `Color` | `Color` |
|
|
56
|
+
| `Luminosity` | `Luminosity` |
|
|
57
|
+
|
|
58
|
+
Unknown modes fall back to `Normal`.
|
|
59
|
+
|
|
60
|
+
## Implementation
|
|
61
|
+
|
|
62
|
+
1. Add `transparency_setting` attribute to `Rectangle`, `Polygon`,
|
|
63
|
+
`GraphicLine`, `TextFrame`, `Group` (RNC lists it on every page item).
|
|
64
|
+
2. Model `TransparencySetting` with a `blending_setting` child.
|
|
65
|
+
3. New helper `Render::Blending` that maps an `Elements::BlendingSetting`
|
|
66
|
+
to a `{ opacity:, blend_mode: }` hash.
|
|
67
|
+
4. In each renderer, wrap the existing fill/stroke block in
|
|
68
|
+
`canvas.with_transparency(**blending_args) { ... }` when a setting is
|
|
69
|
+
present.
|
|
70
|
+
5. Reuse the existing `save_graphics_state` block — `with_transparency`
|
|
71
|
+
already saves/restores, so renderers call it in place of the plain
|
|
72
|
+
save block when blending applies.
|
|
73
|
+
|
|
74
|
+
## Acceptance criteria
|
|
75
|
+
|
|
76
|
+
- [ ] IDML `<BlendingSetting Opacity="0.5"/>` halves the item's alpha in the output PDF.
|
|
77
|
+
- [ ] IDML `<BlendingSetting BlendMode="Multiply"/>` sets `/BM /Multiply` in ExtGState.
|
|
78
|
+
- [ ] Items without BlendingSetting render exactly as before (no ExtGState emitted).
|
|
79
|
+
- [ ] Spec covers opacity, blend_mode, and pass-through cases.
|
|
80
|
+
|
|
81
|
+
## Dependencies
|
|
82
|
+
|
|
83
|
+
- pdfrb 0.4.0 `Canvas#with_transparency` (DONE — confirmed in pdfrb 0.4.0).
|
|
84
|
+
- pdfrb 0.4.0 `Canvas#blend_mode=` (DONE).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# TODO PDF 70: Stroke styling — line cap, join, dash patterns
|
|
2
|
+
|
|
3
|
+
## Status: PLANNED (not started)
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Honor IDML stroke styling attributes by routing through pdfrb 0.4.0's
|
|
8
|
+
stroke-style Canvas setters:
|
|
9
|
+
|
|
10
|
+
- `line_width=` (already used — TODO 31)
|
|
11
|
+
- `line_cap=` — RoundButt/RoundJoin cap shape
|
|
12
|
+
- `line_join=` — miter/round/bevel corner joining
|
|
13
|
+
- `miter_limit=` — clamps spikes on sharp angles
|
|
14
|
+
- `dash_pattern=` — dashed/dotted stroke styles
|
|
15
|
+
|
|
16
|
+
## Background
|
|
17
|
+
|
|
18
|
+
pdfrb 0.4.0 exposes the full PDF graphics-state stroke suite. IDML
|
|
19
|
+
exposes the matching attributes through `<DashedStrokeStyle>` and
|
|
20
|
+
`<DottedStrokeStyle>` elements in `Resources/Graphic.xml`, plus
|
|
21
|
+
per-item `StrokeColor`/`StrokeWeight` (already handled).
|
|
22
|
+
|
|
23
|
+
IDML → PDF stroke mapping:
|
|
24
|
+
|
|
25
|
+
| IDML | PDF |
|
|
26
|
+
|-------------------------------------|---------------------------|
|
|
27
|
+
| `EndCap="RoundCap"` | `line_cap = 1` (Round) |
|
|
28
|
+
| `EndCap="ButtCap"` | `line_cap = 0` (Butt) |
|
|
29
|
+
| `EndCap="ProjectingCap"` | `line_cap = 2` (Square) |
|
|
30
|
+
| `Join="RoundJoin"` | `line_join = 1` |
|
|
31
|
+
| `Join="MiterJoin"` | `line_join = 0` |
|
|
32
|
+
| `Join="BevelJoin"` | `line_join = 2` |
|
|
33
|
+
| `<DashedStrokeStyle DashArray="...">` | `dash_pattern = [arr, 0]` |
|
|
34
|
+
| `<DottedStrokeStyle DotArray="...">` | `dash_pattern = [arr, 0]` (dots are short dashes) |
|
|
35
|
+
|
|
36
|
+
## Plan
|
|
37
|
+
|
|
38
|
+
1. `Parts::Graphic` already exposes `dashed_stroke_style` and
|
|
39
|
+
`dotted_stroke_style` collections — add accessors on the renderer
|
|
40
|
+
context.
|
|
41
|
+
2. New `Render::StrokeStyle` helper that resolves an
|
|
42
|
+
`Elements::DashedStrokeStyle` / `DottedStrokeStyle` (or stroke-style
|
|
43
|
+
self-id reference on the item) to a `{ line_cap:, line_join:,
|
|
44
|
+
miter_limit:, dash_pattern: }` hash.
|
|
45
|
+
3. Each stroke renderer (Rectangle, Polygon, GraphicLine) calls
|
|
46
|
+
`StrokeStyle.apply(canvas, item, context)` before `canvas.stroke`.
|
|
47
|
+
|
|
48
|
+
## Acceptance criteria
|
|
49
|
+
|
|
50
|
+
- [ ] Dashed strokes render with correct dash array.
|
|
51
|
+
- [ ] Dotted strokes render as zero-length dashes with round caps.
|
|
52
|
+
- [ ] Round/miter/bevel joins produce matching PDF output.
|
|
53
|
+
- [ ] Plain strokes (no style) render exactly as before.
|
|
54
|
+
- [ ] Spec covers dashed, dotted, round-join, and pass-through cases.
|
|
55
|
+
|
|
56
|
+
## Dependencies
|
|
57
|
+
|
|
58
|
+
- pdfrb 0.4.0 `line_cap=`, `line_join=`, `miter_limit=`, `dash_pattern=`
|
|
59
|
+
(DONE — all confirmed in pdfrb 0.4.0).
|
|
60
|
+
- IDML element models for stroke styles (DONE — already generated from RNC).
|
|
@@ -13,6 +13,7 @@ module Idml
|
|
|
13
13
|
attribute :visible, :boolean
|
|
14
14
|
attribute :item_layer, :string
|
|
15
15
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
16
|
+
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
16
17
|
|
|
17
18
|
xml do
|
|
18
19
|
root "GraphicLine"
|
|
@@ -24,6 +25,7 @@ module Idml
|
|
|
24
25
|
map_attribute "Visible", to: :visible
|
|
25
26
|
map_attribute "ItemLayer", to: :item_layer
|
|
26
27
|
map_element "Properties", to: :properties
|
|
28
|
+
map_element "TransparencySetting", to: :transparency_setting
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def geometric_bounds
|
|
@@ -15,6 +15,7 @@ module Idml
|
|
|
15
15
|
attribute :visible, :boolean
|
|
16
16
|
attribute :item_layer, :string
|
|
17
17
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
18
|
+
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
18
19
|
attribute :image, Idml::Elements::Image, collection: true
|
|
19
20
|
|
|
20
21
|
xml do
|
|
@@ -29,6 +30,7 @@ module Idml
|
|
|
29
30
|
map_attribute "Visible", to: :visible
|
|
30
31
|
map_attribute "ItemLayer", to: :item_layer
|
|
31
32
|
map_element "Properties", to: :properties
|
|
33
|
+
map_element "TransparencySetting", to: :transparency_setting
|
|
32
34
|
map_element "Image", to: :image
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -19,6 +19,7 @@ module Idml
|
|
|
19
19
|
attribute :name, :string
|
|
20
20
|
attribute :item_layer, :string
|
|
21
21
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
22
|
+
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
22
23
|
attribute :image, Idml::Elements::Image, collection: true
|
|
23
24
|
|
|
24
25
|
xml do
|
|
@@ -36,6 +37,7 @@ module Idml
|
|
|
36
37
|
map_attribute "Name", to: :name
|
|
37
38
|
map_attribute "ItemLayer", to: :item_layer
|
|
38
39
|
map_element "Properties", to: :properties
|
|
40
|
+
map_element "TransparencySetting", to: :transparency_setting
|
|
39
41
|
map_element "Image", to: :image
|
|
40
42
|
end
|
|
41
43
|
|
|
@@ -20,6 +20,7 @@ module Idml
|
|
|
20
20
|
attribute :name, :string
|
|
21
21
|
attribute :item_layer, :string
|
|
22
22
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
23
|
+
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
23
24
|
attribute :text_frame_preference, Idml::Elements::TextFramePreference,
|
|
24
25
|
collection: true
|
|
25
26
|
|
|
@@ -39,6 +40,7 @@ module Idml
|
|
|
39
40
|
map_attribute "Name", to: :name
|
|
40
41
|
map_attribute "ItemLayer", to: :item_layer
|
|
41
42
|
map_element "Properties", to: :properties
|
|
43
|
+
map_element "TransparencySetting", to: :transparency_setting
|
|
42
44
|
map_element "TextFramePreference", to: :text_frame_preference
|
|
43
45
|
end
|
|
44
46
|
|
|
@@ -7,8 +7,33 @@ module Idml
|
|
|
7
7
|
# `reference-docs/schemas/package/Resources/Styles.rnc` is declared (0 attributes),
|
|
8
8
|
# generated by `scripts/rnc_to_lutaml.rb`.
|
|
9
9
|
class TransparencySetting < Lutaml::Model::Serializable
|
|
10
|
+
attribute :blending_setting, Idml::Elements::BlendingSetting
|
|
11
|
+
attribute :drop_shadow_setting, Idml::Elements::DropShadowSetting
|
|
12
|
+
attribute :feather_setting, Idml::Elements::FeatherSetting
|
|
13
|
+
attribute :inner_shadow_setting, Idml::Elements::InnerShadowSetting
|
|
14
|
+
attribute :outer_glow_setting, Idml::Elements::OuterGlowSetting
|
|
15
|
+
attribute :inner_glow_setting, Idml::Elements::InnerGlowSetting
|
|
16
|
+
attribute :bevel_and_emboss_setting, Idml::Elements::BevelAndEmbossSetting
|
|
17
|
+
attribute :satin_setting, Idml::Elements::SatinSetting
|
|
18
|
+
attribute :directional_feather_setting,
|
|
19
|
+
Idml::Elements::DirectionalFeatherSetting
|
|
20
|
+
attribute :gradient_feather_setting,
|
|
21
|
+
Idml::Elements::GradientFeatherSetting
|
|
22
|
+
|
|
10
23
|
xml do
|
|
11
24
|
root "TransparencySetting"
|
|
25
|
+
map_element "BlendingSetting", to: :blending_setting
|
|
26
|
+
map_element "DropShadowSetting", to: :drop_shadow_setting
|
|
27
|
+
map_element "FeatherSetting", to: :feather_setting
|
|
28
|
+
map_element "InnerShadowSetting", to: :inner_shadow_setting
|
|
29
|
+
map_element "OuterGlowSetting", to: :outer_glow_setting
|
|
30
|
+
map_element "InnerGlowSetting", to: :inner_glow_setting
|
|
31
|
+
map_element "BevelAndEmbossSetting", to: :bevel_and_emboss_setting
|
|
32
|
+
map_element "SatinSetting", to: :satin_setting
|
|
33
|
+
map_element "DirectionalFeatherSetting",
|
|
34
|
+
to: :directional_feather_setting
|
|
35
|
+
map_element "GradientFeatherSetting",
|
|
36
|
+
to: :gradient_feather_setting
|
|
12
37
|
end
|
|
13
38
|
end
|
|
14
39
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Idml
|
|
4
|
+
module Render
|
|
5
|
+
# Maps an `Elements::BlendingSetting` to the keyword arguments that
|
|
6
|
+
# `Pdfrb::Content::Canvas#with_transparency` expects. IDML stores
|
|
7
|
+
# opacity as 0–100 (percentage); pdfrb wants 0.0–1.0 alpha. Blend
|
|
8
|
+
# mode names line up directly with PDF `BM` names; unknown modes
|
|
9
|
+
# fall back to `Normal`.
|
|
10
|
+
module Blending
|
|
11
|
+
PDF_BLEND_MODES = %w[
|
|
12
|
+
Normal Multiply Screen Overlay SoftLight HardLight Darken Lighten
|
|
13
|
+
ColorDodge ColorBurn Difference Exclusion Hue Saturation Color
|
|
14
|
+
Luminosity
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
def self.args_for(setting)
|
|
18
|
+
return nil unless setting&.blending_setting
|
|
19
|
+
|
|
20
|
+
build_args(setting.blending_setting)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.build_args(blending)
|
|
24
|
+
opacity = normalize_opacity(blending.opacity)
|
|
25
|
+
mode = normalize_mode(blending.blend_mode)
|
|
26
|
+
return nil unless opacity || mode
|
|
27
|
+
|
|
28
|
+
{ opacity: opacity || 1.0, blend_mode: mode || "Normal" }
|
|
29
|
+
end
|
|
30
|
+
private_class_method :build_args
|
|
31
|
+
|
|
32
|
+
# Wraps the given block in `Canvas#with_transparency` when the
|
|
33
|
+
# page item has a BlendingSetting, otherwise yields directly.
|
|
34
|
+
def self.wrap(canvas, setting, &)
|
|
35
|
+
args = args_for(setting)
|
|
36
|
+
if args
|
|
37
|
+
canvas.with_transparency(**args, &)
|
|
38
|
+
else
|
|
39
|
+
yield
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.normalize_opacity(raw)
|
|
44
|
+
return nil if raw.nil?
|
|
45
|
+
|
|
46
|
+
(raw.to_f / 100.0).clamp(0.0, 1.0)
|
|
47
|
+
end
|
|
48
|
+
private_class_method :normalize_opacity
|
|
49
|
+
|
|
50
|
+
def self.normalize_mode(raw)
|
|
51
|
+
return nil if raw.nil? || raw == "Normal"
|
|
52
|
+
|
|
53
|
+
camel = raw.to_s.dup
|
|
54
|
+
camel[0] = camel[0].upcase
|
|
55
|
+
PDF_BLEND_MODES.include?(camel) ? camel : nil
|
|
56
|
+
end
|
|
57
|
+
private_class_method :normalize_mode
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -14,7 +14,7 @@ module Idml
|
|
|
14
14
|
box = RectangleRenderer.placement_box(line, context.page_height)
|
|
15
15
|
return unless box
|
|
16
16
|
|
|
17
|
-
canvas.
|
|
17
|
+
Blending.wrap(canvas, line.transparency_setting) do
|
|
18
18
|
canvas.stroke_color(ColorHelper.to_canvas(color))
|
|
19
19
|
canvas.line_width = line.stroke_weight
|
|
20
20
|
canvas.move_to(box[:x], box[:y] + box[:height])
|
|
@@ -11,7 +11,7 @@ module Idml
|
|
|
11
11
|
box = RectangleRenderer.placement_box(poly, context.page_height)
|
|
12
12
|
return unless box
|
|
13
13
|
|
|
14
|
-
canvas.
|
|
14
|
+
Blending.wrap(canvas, poly.transparency_setting) do
|
|
15
15
|
render_fill(canvas, poly, context, box)
|
|
16
16
|
render_stroke(canvas, poly, context, box)
|
|
17
17
|
end
|
|
@@ -12,7 +12,7 @@ module Idml
|
|
|
12
12
|
box = placement_box(rect, context.page_height)
|
|
13
13
|
return unless box
|
|
14
14
|
|
|
15
|
-
canvas.
|
|
15
|
+
Blending.wrap(canvas, rect.transparency_setting) do
|
|
16
16
|
render_fill(canvas, rect, context, box)
|
|
17
17
|
render_stroke(canvas, rect, context, box)
|
|
18
18
|
end
|
|
@@ -44,32 +44,51 @@ module Idml
|
|
|
44
44
|
private_class_method :render_fill
|
|
45
45
|
|
|
46
46
|
def self.render_gradient_fill(canvas, rect, context, box)
|
|
47
|
-
|
|
48
|
-
return unless
|
|
47
|
+
gradient = gradient_for(rect, context)
|
|
48
|
+
return unless gradient
|
|
49
|
+
return unless gradient.gradient_stop.length >= 2
|
|
50
|
+
|
|
51
|
+
stops = gradient_stops(gradient, context)
|
|
52
|
+
return unless stops.length >= 2
|
|
53
|
+
|
|
54
|
+
shading = if gradient.type == "Radial"
|
|
55
|
+
radial_shading(canvas, box, stops)
|
|
56
|
+
else
|
|
57
|
+
axial_shading(canvas, box, stops)
|
|
58
|
+
end
|
|
59
|
+
canvas.rectangle(box[:x], box[:y], box[:width], box[:height])
|
|
60
|
+
canvas.fill_shading(shading)
|
|
61
|
+
end
|
|
62
|
+
private_class_method :render_gradient_fill
|
|
49
63
|
|
|
50
|
-
|
|
64
|
+
def self.axial_shading(canvas, box, stops)
|
|
65
|
+
canvas.document.shadings.add_axial(
|
|
51
66
|
from: [box[:x], box[:y] + box[:height]],
|
|
52
67
|
to: [box[:x] + box[:width], box[:y]],
|
|
53
68
|
stops: stops,
|
|
54
69
|
)
|
|
55
|
-
canvas.rectangle(box[:x], box[:y], box[:width], box[:height])
|
|
56
|
-
canvas.fill_shading(shading)
|
|
57
70
|
end
|
|
58
|
-
private_class_method :
|
|
71
|
+
private_class_method :axial_shading
|
|
72
|
+
|
|
73
|
+
def self.radial_shading(canvas, box, stops)
|
|
74
|
+
cx = box[:x] + (box[:width] / 2.0)
|
|
75
|
+
cy = box[:y] + (box[:height] / 2.0)
|
|
76
|
+
radius = [box[:width], box[:height]].max / 2.0
|
|
77
|
+
canvas.document.shadings.add_radial(
|
|
78
|
+
from: [cx, cy, 0.0],
|
|
79
|
+
to: [cx, cy, radius],
|
|
80
|
+
stops: stops,
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
private_class_method :radial_shading
|
|
59
84
|
|
|
60
|
-
def self.
|
|
85
|
+
def self.gradient_for(rect, context)
|
|
61
86
|
graphic = context.package&.graphic
|
|
62
87
|
return nil unless graphic
|
|
63
88
|
|
|
64
|
-
|
|
65
|
-
g.self_attr == rect.fill_color
|
|
66
|
-
end
|
|
67
|
-
return nil unless gradient
|
|
68
|
-
return nil unless gradient.gradient_stop.length >= 2
|
|
69
|
-
|
|
70
|
-
gradient_stops(gradient, context)
|
|
89
|
+
graphic.gradient.find { |g| g.self_attr == rect.fill_color }
|
|
71
90
|
end
|
|
72
|
-
private_class_method :
|
|
91
|
+
private_class_method :gradient_for
|
|
73
92
|
|
|
74
93
|
def self.gradient_stops(gradient, context)
|
|
75
94
|
gradient.gradient_stop.filter_map do |stop|
|
data/lib/idml/render.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Idml
|
|
|
11
11
|
autoload :FontReferenceResolver,
|
|
12
12
|
"#{__dir__}/render/font_reference_resolver"
|
|
13
13
|
autoload :GradientResolver, "#{__dir__}/render/gradient_resolver"
|
|
14
|
+
autoload :Blending, "#{__dir__}/render/blending"
|
|
14
15
|
autoload :LayerFilter, "#{__dir__}/render/layer_filter"
|
|
15
16
|
autoload :StoryThreader, "#{__dir__}/render/story_threader"
|
|
16
17
|
autoload :PdfrbWriter, "#{__dir__}/render/pdfrb_writer"
|
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.2.
|
|
4
|
+
version: 0.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
@@ -207,6 +207,10 @@ files:
|
|
|
207
207
|
- TODO.pdf/64-pipeline-cli-optimizations.md
|
|
208
208
|
- TODO.pdf/65-pdfrb-019-integration.md
|
|
209
209
|
- TODO.pdf/66-gradient-shadings-pdfrb.md
|
|
210
|
+
- TODO.pdf/67-text-rich-multi-run.md
|
|
211
|
+
- TODO.pdf/68-radial-gradient-shadings.md
|
|
212
|
+
- TODO.pdf/69-transparency-blend-modes.md
|
|
213
|
+
- TODO.pdf/70-stroke-styling.md
|
|
210
214
|
- TODO.pdf/README.md
|
|
211
215
|
- exe/idml
|
|
212
216
|
- idml.gemspec
|
|
@@ -371,6 +375,7 @@ files:
|
|
|
371
375
|
- lib/idml/parts/style_mapping.rb
|
|
372
376
|
- lib/idml/parts/tags.rb
|
|
373
377
|
- lib/idml/render.rb
|
|
378
|
+
- lib/idml/render/blending.rb
|
|
374
379
|
- lib/idml/render/color_helper.rb
|
|
375
380
|
- lib/idml/render/color_resolver.rb
|
|
376
381
|
- lib/idml/render/font_reference_resolver.rb
|