idml 0.8.7 → 0.8.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/TODO.pdf/102-anchored-objects.md +40 -36
- data/TODO.pdf/106-page-item-type-coverage.md +37 -7
- data/TODO.pdf/111-footnote-support.md +45 -35
- data/lib/idml/elements/character_style_range.rb +18 -0
- data/lib/idml/elements/footnote.rb +32 -0
- data/lib/idml/elements/graphic_line.rb +3 -0
- data/lib/idml/elements/group.rb +3 -0
- data/lib/idml/elements/oval.rb +3 -0
- data/lib/idml/elements/path.rb +3 -0
- data/lib/idml/elements/polygon.rb +3 -0
- data/lib/idml/elements/rectangle.rb +3 -0
- data/lib/idml/elements/story_inner.rb +2 -0
- data/lib/idml/elements.rb +1 -0
- data/lib/idml/render/contour.rb +110 -0
- data/lib/idml/render/footnote.rb +211 -0
- data/lib/idml/render/renderers/oval_renderer.rb +5 -6
- data/lib/idml/render/renderers/path_renderer.rb +5 -6
- data/lib/idml/render/renderers/polygon_renderer.rb +6 -37
- data/lib/idml/render/renderers/rectangle_renderer.rb +5 -94
- data/lib/idml/render/renderers/text_frame_renderer.rb +117 -28
- data/lib/idml/render/shape_paint.rb +172 -0
- data/lib/idml/render/style_resolver.rb +93 -38
- data/lib/idml/render.rb +3 -0
- data/lib/idml/text_engine/vertical_layout.rb +11 -1
- data/lib/idml/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d58f00746f6afb4233e161dfa9cdeb116d714eed0c238ec543a0f039b4167f0e
|
|
4
|
+
data.tar.gz: 1a000e34996525fe34cdc441625c9eaa303f121c836ae3f3b07b73c12712abb1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15001cbf1427dc278c701cd4980b837ff4b015f6c5657eeb5c88c0c8774933934e606439c40fa3c900368ef2959fc1170cd12841a9caadc0c025a199dd18cd68
|
|
7
|
+
data.tar.gz: 41f45b1b2db245e1dd2d5ef40aa60990fc3c957930b3d16a633974d1f267149e8b2c5cf638b7b3549b4318bf8ea62f378fbb5d0f98ad32d4d50fbce0e3cb9505
|
|
@@ -1,41 +1,45 @@
|
|
|
1
1
|
# TODO PDF 102: Anchored objects (anchored object settings)
|
|
2
2
|
|
|
3
|
-
## Status:
|
|
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
|
-
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-16
|
|
4
|
+
|
|
5
|
+
## What was built
|
|
6
|
+
|
|
7
|
+
- Model wiring: `anchored_object_setting` attribute + mapping on
|
|
8
|
+
Rectangle, Oval, Polygon, GraphicLine, Group, and Path (per
|
|
9
|
+
`*_Object` in Story.rnc — every page item can carry
|
|
10
|
+
`<AnchoredObjectSetting>`). The setting parses AnchoredPosition
|
|
11
|
+
(InlinePosition / AboveLine / Anchored), AnchorPoint,
|
|
12
|
+
AnchorXoffset / AnchorYoffset, Horizontal/VerticalReferencePoint,
|
|
13
|
+
and the remaining schema attributes.
|
|
14
|
+
- `CharacterStyleRange` now models its story-embedded page-item
|
|
15
|
+
children: `rectangle`, `oval`, `polygon`, `graphic_line`,
|
|
16
|
+
`group`, `text_frame` collections. Previously these elements were
|
|
17
|
+
dropped at parse time — anchored objects were lost entirely.
|
|
18
|
+
- Rendering: `TextFrameRenderer` discovers embedded items via the
|
|
19
|
+
shared `story_csrs` walk and renders each through
|
|
20
|
+
`PageItemRenderer` dispatch (fill, stroke, gradients, images —
|
|
21
|
+
everything the spread-level path does) with a child context.
|
|
22
|
+
Items render at their own stored geometry: InDesign resolves an
|
|
23
|
+
anchored object's position when saving the file, so the stored
|
|
24
|
+
ItemTransform + PathGeometry IS the anchored position for all
|
|
25
|
+
three AnchorTypes.
|
|
26
|
+
|
|
27
|
+
## Known limitations
|
|
28
|
+
|
|
29
|
+
- AnchorType-specific text reflow is not simulated: the layout
|
|
30
|
+
engine positions body text independently of embedded items, so
|
|
31
|
+
body text may overlap inline/above-line objects in frames whose
|
|
32
|
+
stored text positions differ from our layout engine's output.
|
|
33
|
+
- HorizontalReferencePoint / VerticalReferencePoint /
|
|
34
|
+
HorizontalAlignment / VerticalAlignment on the setting are parsed
|
|
35
|
+
but not consulted (stored geometry already encodes the result).
|
|
29
36
|
|
|
30
37
|
## Acceptance criteria
|
|
31
38
|
|
|
32
|
-
- [
|
|
33
|
-
at
|
|
34
|
-
- [
|
|
35
|
-
|
|
36
|
-
- [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## Dependencies
|
|
40
|
-
|
|
41
|
-
- TODO 94 (need to know character positions in the text flow).
|
|
39
|
+
- [x] Page item with AnchoredObjectSetting AnchorType="Inline"
|
|
40
|
+
renders at its anchored position (stored geometry).
|
|
41
|
+
- [x] Page item with AnchorType="Above Line" renders above the
|
|
42
|
+
anchored line (stored geometry).
|
|
43
|
+
- [x] Page item with AnchorType="Anchored" renders at the position
|
|
44
|
+
derived from the anchor (stored geometry; AnchorX/Yoffset
|
|
45
|
+
parsing verified).
|
|
@@ -1,12 +1,42 @@
|
|
|
1
1
|
# TODO PDF 106: Page-item type coverage — Ellipse, Path, EPS, WMF, PICT, HtmlItem
|
|
2
2
|
|
|
3
|
-
## Status:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
## Status: COMPLETE (rendering scope) — implemented 2026-08-17
|
|
4
|
+
|
|
5
|
+
## What was built (2026-08-17 refinement: true contours)
|
|
6
|
+
|
|
7
|
+
- `Render::Contour` — draws an item's PathGeometry as true Bézier
|
|
8
|
+
contours: PathPointType anchors, LeftDirection / RightDirection
|
|
9
|
+
control handles, ItemTransform applied per point, y flipped into
|
|
10
|
+
PDF space. One subpath per GeometryPathType (compound paths);
|
|
11
|
+
closed unless PathOpen="true"; degenerate handles fall back to the
|
|
12
|
+
anchor. OvalRenderer, PathRenderer, and PolygonRenderer all
|
|
13
|
+
delegate here — ovals render as real ellipses (rotation preserved
|
|
14
|
+
through the transform), polygons as their actual outlines, Paths
|
|
15
|
+
as compound contours. Bounding-box rectangle fallback when an
|
|
16
|
+
item has no PathGeometry but has geometric_bounds.
|
|
17
|
+
- `Render::ShapePaint` — the single owner of the IDML → PDF paint
|
|
18
|
+
mapping (solid fill, gradient shading fill, stroke styling, and
|
|
19
|
+
the PDF paint-op sequencing including fill_stroke on one path).
|
|
20
|
+
RectangleRenderer now delegates to it; the fill/stroke/gradient
|
|
21
|
+
logic it previously duplicated across shape renderers lives in
|
|
22
|
+
one place.
|
|
23
|
+
- Oval/Path modeled + registered in RENDERER_MAP (earlier
|
|
24
|
+
milestone); Polygon renders contours instead of its bounding box.
|
|
25
|
+
|
|
26
|
+
## Out of scope (unchanged)
|
|
27
|
+
|
|
28
|
+
- EPS / WMF / PICT / HtmlItem / Movie / Sound / FormField — foreign
|
|
29
|
+
or interactive formats, not modeled. Image placement goes through
|
|
30
|
+
ImageCollector (separate, model-driven path).
|
|
31
|
+
|
|
32
|
+
## Acceptance criteria
|
|
33
|
+
|
|
34
|
+
- [x] SpreadObject has `oval` + `path` collections (Ellipse is the
|
|
35
|
+
Oval element in the schema's naming).
|
|
36
|
+
- [x] OvalRenderer / PathRenderer registered in RENDERER_MAP.
|
|
37
|
+
- [x] True Bézier rendering from PathGeometry anchors + control
|
|
38
|
+
points (TODO 106 refinement complete).
|
|
39
|
+
- [x] Compound Path objects render with multiple sub-paths.
|
|
10
40
|
|
|
11
41
|
## Problem
|
|
12
42
|
|
|
@@ -1,40 +1,50 @@
|
|
|
1
1
|
# TODO PDF 111: Footnote support
|
|
2
2
|
|
|
3
|
-
## Status:
|
|
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
|
-
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-16
|
|
4
|
+
|
|
5
|
+
## What was built
|
|
6
|
+
|
|
7
|
+
- `Elements::Footnote` — schema-faithful model of `Footnote_Object`
|
|
8
|
+
(Story.rnc): no attributes, PSR/CSR children carrying the footnote
|
|
9
|
+
text. Wired into `CharacterStyleRange` and `StoryInner` (`footnote`
|
|
10
|
+
collection). Footnote text is excluded from `text_content` so it
|
|
11
|
+
never leaks into body text.
|
|
12
|
+
- `Render::Footnote` — footnote semantics unit (MECE):
|
|
13
|
+
- `Counter`: story-scoped sequential numbering, honoring
|
|
14
|
+
FootnoteOption `StartAt`.
|
|
15
|
+
- `marker_run`: superscript marker emitted into the body text at the
|
|
16
|
+
anchoring CSR's position; inherits the CSR's character styling.
|
|
17
|
+
- `extract`: footnote paragraphs via StyleResolver with the marker
|
|
18
|
+
text (`Prefix + number + Suffix`) prefixed to the first run.
|
|
19
|
+
- `layout_entries` / `reserved_height`: one shared Shaper →
|
|
20
|
+
LineBreaker → VerticalLayout walk drives both height measurement
|
|
21
|
+
and rendering, so the reserved area always matches what's drawn.
|
|
22
|
+
- `emit_separator`: separator rule honoring FootnoteOption RuleOn /
|
|
23
|
+
RuleLineWeight / RuleWidth / RuleLeftIndent.
|
|
24
|
+
- `TextFrameRenderer`: marker runs are collected during body layout
|
|
25
|
+
(`register_footnote`), each raising the effective bottom limit so
|
|
26
|
+
body text avoids the footnote area (incremental reservation,
|
|
27
|
+
InDesign-style); after body layout, `render_footnote_entries` draws
|
|
28
|
+
the separator and the footnote paragraphs above the frame's content
|
|
29
|
+
bottom. Per-column in multi-column frames.
|
|
30
|
+
- `FootnoteOption` (Preferences.xml) honored: StartAt, Prefix, Suffix,
|
|
31
|
+
RuleOn, RuleLineWeight, RuleWidth, RuleLeftIndent, Spacer
|
|
32
|
+
(separator gap), SpaceBetween (inter-entry gap).
|
|
33
|
+
|
|
34
|
+
## Known limitations
|
|
35
|
+
|
|
36
|
+
- Numbering restarts per story; document-wide continuous numbering
|
|
37
|
+
and per-section restart are not modeled.
|
|
38
|
+
- Footnote overflow balancing (a footnote too tall for its frame
|
|
39
|
+
moving wholly to the next frame, NoSplitting) is not modeled.
|
|
40
|
+
- The simple-render fallback (no font metrics) shows markers inline
|
|
41
|
+
but does not render footnote text at the frame bottom.
|
|
42
|
+
- Footnotes nested in CSRs inside CSRs (CSR > CSR > Footnote) are
|
|
43
|
+
not extracted; only direct CSR children emit markers.
|
|
30
44
|
|
|
31
45
|
## Acceptance criteria
|
|
32
46
|
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
35
|
-
- [
|
|
36
|
-
|
|
37
|
-
## Dependencies
|
|
38
|
-
|
|
39
|
-
- TODO 108 (multi-frame story flow) — footnotes need to know which
|
|
40
|
-
page a reference appears on.
|
|
47
|
+
- [x] Footnote text rendered at bottom of containing frame.
|
|
48
|
+
- [x] Footnote marker in body text (superscript, numbered).
|
|
49
|
+
- [x] Numbering honors FootnoteOption StartAt / Prefix / Suffix
|
|
50
|
+
(per-story scope).
|
|
@@ -300,6 +300,17 @@ module Idml
|
|
|
300
300
|
collection: true
|
|
301
301
|
attribute :table, Idml::Elements::Table, collection: true
|
|
302
302
|
attribute :xml_element, Idml::Elements::XmlElement, collection: true
|
|
303
|
+
attribute :footnote, Idml::Elements::Footnote, collection: true
|
|
304
|
+
# Anchored page items embedded in the story flow. Their
|
|
305
|
+
# AnchoredObjectSetting (child of each item) declares how the
|
|
306
|
+
# item anchors to the surrounding text.
|
|
307
|
+
attribute :rectangle, Idml::Elements::Rectangle, collection: true
|
|
308
|
+
attribute :oval, Idml::Elements::Oval, collection: true
|
|
309
|
+
attribute :polygon, Idml::Elements::Polygon, collection: true
|
|
310
|
+
attribute :graphic_line, Idml::Elements::GraphicLine,
|
|
311
|
+
collection: true
|
|
312
|
+
attribute :group, Idml::Elements::Group, collection: true
|
|
313
|
+
attribute :text_frame, Idml::Elements::TextFrame, collection: true
|
|
303
314
|
|
|
304
315
|
xml do
|
|
305
316
|
root "CharacterStyleRange"
|
|
@@ -655,6 +666,13 @@ module Idml
|
|
|
655
666
|
map_element "HyperlinkTextSource", to: :hyperlink_text_source
|
|
656
667
|
map_element "Table", to: :table
|
|
657
668
|
map_element "XMLElement", to: :xml_element
|
|
669
|
+
map_element "Footnote", to: :footnote
|
|
670
|
+
map_element "Rectangle", to: :rectangle
|
|
671
|
+
map_element "Oval", to: :oval
|
|
672
|
+
map_element "Polygon", to: :polygon
|
|
673
|
+
map_element "GraphicLine", to: :graphic_line
|
|
674
|
+
map_element "Group", to: :group
|
|
675
|
+
map_element "TextFrame", to: :text_frame
|
|
658
676
|
end
|
|
659
677
|
|
|
660
678
|
def text_content
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Idml
|
|
4
|
+
module Elements
|
|
5
|
+
# `<Footnote>` — story-embedded footnote content. Schema-faithful
|
|
6
|
+
# model of `Footnote_Object` in
|
|
7
|
+
# `reference-docs/schemas/package/Stories/Story.rnc`: the element
|
|
8
|
+
# declares no attributes; its children are HiddenText /
|
|
9
|
+
# GaijiOwnedItemObject / Table / TextVariableInstance /
|
|
10
|
+
# ParagraphStyleRange / CharacterStyleRange. Of these, the
|
|
11
|
+
# ParagraphStyleRange (with its CSR children) and
|
|
12
|
+
# CharacterStyleRange children carry the footnote text; the rest
|
|
13
|
+
# are parsed by their own models where relevant.
|
|
14
|
+
#
|
|
15
|
+
# A Footnote sits inside the CharacterStyleRange that owns the
|
|
16
|
+
# anchor position: the body-text marker renders where the CSR's
|
|
17
|
+
# text ends, and the Footnote's paragraphs render at the bottom
|
|
18
|
+
# of the frame holding that marker (see Render::Footnote).
|
|
19
|
+
class Footnote < Lutaml::Model::Serializable
|
|
20
|
+
attribute :paragraph_style_range, Idml::Elements::ParagraphStyleRange,
|
|
21
|
+
collection: true
|
|
22
|
+
attribute :character_style_range, Idml::Elements::CharacterStyleRange,
|
|
23
|
+
collection: true
|
|
24
|
+
|
|
25
|
+
xml do
|
|
26
|
+
root "Footnote"
|
|
27
|
+
map_element "ParagraphStyleRange", to: :paragraph_style_range
|
|
28
|
+
map_element "CharacterStyleRange", to: :character_style_range
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -19,6 +19,8 @@ module Idml
|
|
|
19
19
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
20
20
|
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
21
21
|
attribute :text_wrap_preference, Idml::Elements::TextWrapPreference
|
|
22
|
+
attribute :anchored_object_setting,
|
|
23
|
+
Idml::Elements::AnchoredObjectSetting
|
|
22
24
|
|
|
23
25
|
xml do
|
|
24
26
|
root "GraphicLine"
|
|
@@ -36,6 +38,7 @@ module Idml
|
|
|
36
38
|
map_element "Properties", to: :properties
|
|
37
39
|
map_element "TransparencySetting", to: :transparency_setting
|
|
38
40
|
map_element "TextWrapPreference", to: :text_wrap_preference
|
|
41
|
+
map_element "AnchoredObjectSetting", to: :anchored_object_setting
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def geometric_bounds
|
data/lib/idml/elements/group.rb
CHANGED
|
@@ -10,6 +10,8 @@ module Idml
|
|
|
10
10
|
attribute :item_transform, :string
|
|
11
11
|
attribute :visible, :boolean
|
|
12
12
|
attribute :item_layer, :string
|
|
13
|
+
attribute :anchored_object_setting,
|
|
14
|
+
Idml::Elements::AnchoredObjectSetting
|
|
13
15
|
attribute :rectangle, Idml::Elements::Rectangle, collection: true
|
|
14
16
|
attribute :text_frame, Idml::Elements::TextFrame, collection: true
|
|
15
17
|
attribute :polygon, Idml::Elements::Polygon, collection: true
|
|
@@ -22,6 +24,7 @@ module Idml
|
|
|
22
24
|
map_attribute "ItemTransform", to: :item_transform
|
|
23
25
|
map_attribute "Visible", to: :visible
|
|
24
26
|
map_attribute "ItemLayer", to: :item_layer
|
|
27
|
+
map_element "AnchoredObjectSetting", to: :anchored_object_setting
|
|
25
28
|
map_element "Rectangle", to: :rectangle
|
|
26
29
|
map_element "TextFrame", to: :text_frame
|
|
27
30
|
map_element "Polygon", to: :polygon
|
data/lib/idml/elements/oval.rb
CHANGED
|
@@ -30,6 +30,8 @@ module Idml
|
|
|
30
30
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
31
31
|
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
32
32
|
attribute :text_wrap_preference, Idml::Elements::TextWrapPreference
|
|
33
|
+
attribute :anchored_object_setting,
|
|
34
|
+
Idml::Elements::AnchoredObjectSetting
|
|
33
35
|
attribute :image, Idml::Elements::Image, collection: true
|
|
34
36
|
|
|
35
37
|
xml do
|
|
@@ -52,6 +54,7 @@ module Idml
|
|
|
52
54
|
map_element "Properties", to: :properties
|
|
53
55
|
map_element "TransparencySetting", to: :transparency_setting
|
|
54
56
|
map_element "TextWrapPreference", to: :text_wrap_preference
|
|
57
|
+
map_element "AnchoredObjectSetting", to: :anchored_object_setting
|
|
55
58
|
map_element "Image", to: :image
|
|
56
59
|
end
|
|
57
60
|
|
data/lib/idml/elements/path.rb
CHANGED
|
@@ -26,6 +26,8 @@ module Idml
|
|
|
26
26
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
27
27
|
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
28
28
|
attribute :text_wrap_preference, Idml::Elements::TextWrapPreference
|
|
29
|
+
attribute :anchored_object_setting,
|
|
30
|
+
Idml::Elements::AnchoredObjectSetting
|
|
29
31
|
|
|
30
32
|
xml do
|
|
31
33
|
root "Path"
|
|
@@ -43,6 +45,7 @@ module Idml
|
|
|
43
45
|
map_element "Properties", to: :properties
|
|
44
46
|
map_element "TransparencySetting", to: :transparency_setting
|
|
45
47
|
map_element "TextWrapPreference", to: :text_wrap_preference
|
|
48
|
+
map_element "AnchoredObjectSetting", to: :anchored_object_setting
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def geometric_bounds
|
|
@@ -21,6 +21,8 @@ module Idml
|
|
|
21
21
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
22
22
|
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
23
23
|
attribute :text_wrap_preference, Idml::Elements::TextWrapPreference
|
|
24
|
+
attribute :anchored_object_setting,
|
|
25
|
+
Idml::Elements::AnchoredObjectSetting
|
|
24
26
|
attribute :image, Idml::Elements::Image, collection: true
|
|
25
27
|
|
|
26
28
|
xml do
|
|
@@ -41,6 +43,7 @@ module Idml
|
|
|
41
43
|
map_element "Properties", to: :properties
|
|
42
44
|
map_element "TransparencySetting", to: :transparency_setting
|
|
43
45
|
map_element "TextWrapPreference", to: :text_wrap_preference
|
|
46
|
+
map_element "AnchoredObjectSetting", to: :anchored_object_setting
|
|
44
47
|
map_element "Image", to: :image
|
|
45
48
|
end
|
|
46
49
|
|
|
@@ -25,6 +25,8 @@ module Idml
|
|
|
25
25
|
attribute :properties, Idml::Elements::Properties, collection: true
|
|
26
26
|
attribute :transparency_setting, Idml::Elements::TransparencySetting
|
|
27
27
|
attribute :text_wrap_preference, Idml::Elements::TextWrapPreference
|
|
28
|
+
attribute :anchored_object_setting,
|
|
29
|
+
Idml::Elements::AnchoredObjectSetting
|
|
28
30
|
attribute :image, Idml::Elements::Image, collection: true
|
|
29
31
|
|
|
30
32
|
xml do
|
|
@@ -48,6 +50,7 @@ module Idml
|
|
|
48
50
|
map_element "Properties", to: :properties
|
|
49
51
|
map_element "TransparencySetting", to: :transparency_setting
|
|
50
52
|
map_element "TextWrapPreference", to: :text_wrap_preference
|
|
53
|
+
map_element "AnchoredObjectSetting", to: :anchored_object_setting
|
|
51
54
|
map_element "Image", to: :image
|
|
52
55
|
end
|
|
53
56
|
|
|
@@ -312,6 +312,7 @@ module Idml
|
|
|
312
312
|
collection: true
|
|
313
313
|
attribute :content, Idml::Elements::Content, collection: true
|
|
314
314
|
attribute :xml_element, Idml::Elements::XmlElement, collection: true
|
|
315
|
+
attribute :footnote, Idml::Elements::Footnote, collection: true
|
|
315
316
|
|
|
316
317
|
xml do
|
|
317
318
|
root "Story"
|
|
@@ -676,6 +677,7 @@ module Idml
|
|
|
676
677
|
map_element "CharacterStyleRange", to: :character_style_range
|
|
677
678
|
map_element "Content", to: :content
|
|
678
679
|
map_element "XMLElement", to: :xml_element
|
|
680
|
+
map_element "Footnote", to: :footnote
|
|
679
681
|
end
|
|
680
682
|
|
|
681
683
|
def text_content
|
data/lib/idml/elements.rb
CHANGED
|
@@ -54,6 +54,7 @@ module Idml
|
|
|
54
54
|
"idml/elements/flex_layout_attribute_option"
|
|
55
55
|
autoload :Font, "idml/elements/font"
|
|
56
56
|
autoload :FontFamily, "idml/elements/font_family"
|
|
57
|
+
autoload :Footnote, "idml/elements/footnote"
|
|
57
58
|
autoload :FootnoteOption, "idml/elements/pref_footnote_option"
|
|
58
59
|
autoload :FrameFittingOption, "idml/elements/frame_fitting_option"
|
|
59
60
|
autoload :GraphicLine, "idml/elements/graphic_line"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Idml
|
|
4
|
+
module Render
|
|
5
|
+
# Renders a page item's PathGeometry as true Bézier contours on
|
|
6
|
+
# a pdfrb canvas. Shared by OvalRenderer, PathRenderer, and
|
|
7
|
+
# PolygonRenderer: every item whose Properties carry a
|
|
8
|
+
# PathGeometry draws its actual outline — PathPointType anchors
|
|
9
|
+
# with LeftDirection / RightDirection control handles,
|
|
10
|
+
# ItemTransform applied, y flipped into PDF space — instead of
|
|
11
|
+
# its bounding box. Paint op selection (fill / stroke /
|
|
12
|
+
# gradient) lives in ShapePaint. Items without usable geometry
|
|
13
|
+
# fall back to the bounding-box rectangle.
|
|
14
|
+
module Contour
|
|
15
|
+
# Renders the item's contours with fill/stroke from
|
|
16
|
+
# ShapePaint. Returns true when painted.
|
|
17
|
+
def self.render(canvas, context)
|
|
18
|
+
item = context.item
|
|
19
|
+
return false if item.visible == false
|
|
20
|
+
|
|
21
|
+
paths = geometry_paths(item)
|
|
22
|
+
return rectangle_fallback(canvas, item, context) if paths.empty?
|
|
23
|
+
|
|
24
|
+
ShapePaint.paint(canvas, item, context) do |c|
|
|
25
|
+
draw_path(c, paths, item.item_transform, context.page_height)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.rectangle_fallback(canvas, item, context)
|
|
30
|
+
box = Placement.box(item, context.page_height)
|
|
31
|
+
return false unless box
|
|
32
|
+
|
|
33
|
+
ShapePaint.paint(canvas, item, context) do |c|
|
|
34
|
+
c.rectangle(box[:x], box[:y], box[:width], box[:height])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
private_class_method :rectangle_fallback
|
|
38
|
+
|
|
39
|
+
# All GeometryPathType children across the item's Properties.
|
|
40
|
+
def self.geometry_paths(item)
|
|
41
|
+
item.properties.flat_map do |props|
|
|
42
|
+
props.path_geometry.flat_map(&:geometry_path_type)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Constructs the full path on the canvas: one subpath per
|
|
47
|
+
# GeometryPathType, each starting at its first anchor and
|
|
48
|
+
# curving through every subsequent anchor via the points'
|
|
49
|
+
# control handles. Closed paths (PathOpen false or absent)
|
|
50
|
+
# loop back to the first anchor and close.
|
|
51
|
+
def self.draw_path(canvas, paths, item_transform, page_height)
|
|
52
|
+
transform = Geometry.parse_transform(item_transform)
|
|
53
|
+
paths.each do |path|
|
|
54
|
+
draw_subpath(canvas, path, transform, page_height)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.draw_subpath(canvas, path, transform, page_height)
|
|
59
|
+
points = path.points
|
|
60
|
+
return if points.length < 2
|
|
61
|
+
|
|
62
|
+
open = path.path_open == true
|
|
63
|
+
anchors = points.map do |point|
|
|
64
|
+
pdf_pair(point.anchor, transform, page_height)
|
|
65
|
+
end
|
|
66
|
+
canvas.move_to(anchors.first[0], anchors.first[1])
|
|
67
|
+
draw_segments(canvas, points, anchors, transform, page_height, open)
|
|
68
|
+
canvas.close_path unless open
|
|
69
|
+
end
|
|
70
|
+
private_class_method :draw_subpath
|
|
71
|
+
|
|
72
|
+
def self.draw_segments(canvas, points, anchors, transform,
|
|
73
|
+
page_height, open)
|
|
74
|
+
segment_count = open ? points.length - 1 : points.length
|
|
75
|
+
segment_count.times do |index|
|
|
76
|
+
from = points[index]
|
|
77
|
+
to = points[(index + 1) % points.length]
|
|
78
|
+
c1 = control_point(from, :right_direction, from.anchor,
|
|
79
|
+
transform, page_height)
|
|
80
|
+
c2 = control_point(to, :left_direction, to.anchor,
|
|
81
|
+
transform, page_height)
|
|
82
|
+
to_anchor = anchors[(index + 1) % points.length]
|
|
83
|
+
canvas.curve_to(c1[0], c1[1], c2[0], c2[1],
|
|
84
|
+
to_anchor[0], to_anchor[1])
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
private_class_method :draw_segments
|
|
88
|
+
|
|
89
|
+
# Control point for one end of a segment: the point's
|
|
90
|
+
# direction handle when present, else the anchor itself
|
|
91
|
+
# (degenerate control = straight-ish curve).
|
|
92
|
+
def self.control_point(point, direction_attr, fallback, transform,
|
|
93
|
+
page_height)
|
|
94
|
+
raw = point.public_send(direction_attr)
|
|
95
|
+
raw = nil if raw && raw.strip.empty?
|
|
96
|
+
pdf_pair(raw || fallback, transform, page_height)
|
|
97
|
+
end
|
|
98
|
+
private_class_method :control_point
|
|
99
|
+
|
|
100
|
+
# Parses an IDML "x y" pair, applies the item transform, and
|
|
101
|
+
# flips y into PDF space (origin bottom-left).
|
|
102
|
+
def self.pdf_pair(raw, transform, page_height)
|
|
103
|
+
x, y = raw.to_s.split(/\s+/).map(&:to_f)
|
|
104
|
+
tx, ty = Geometry.apply_transform(transform, x, y)
|
|
105
|
+
[tx, page_height - ty]
|
|
106
|
+
end
|
|
107
|
+
private_class_method :pdf_pair
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|