idml 0.9.0 → 0.9.2
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/.github/workflows/release.yml +5 -3
- data/TODO.pdf/104-table-schema-completion.md +16 -6
- data/TODO.pdf/116-justification-last-line.md +31 -0
- data/TODO.pdf/117-endnote-support.md +45 -0
- data/TODO.pdf/13-cjk-text-layout.md +20 -4
- data/lib/idml/elements/endnote.rb +22 -0
- data/lib/idml/elements/endnote_range.rb +21 -0
- data/lib/idml/elements/story_inner.rb +5 -0
- data/lib/idml/elements/table.rb +369 -1
- data/lib/idml/elements.rb +2 -0
- data/lib/idml/render/footnote.rb +10 -2
- data/lib/idml/render/renderers/text_frame_renderer.rb +64 -11
- data/lib/idml/render/style_resolver.rb +8 -0
- data/lib/idml/text_engine/justifier.rb +16 -5
- data/lib/idml/text_engine/line_breaker.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: eb1bde51604723b865996a34842c6a5216b3c0ce4f55947902aa9dd92358ed25
|
|
4
|
+
data.tar.gz: a1e6ed580c3d0fee769287d3619c33fa6e18ed79ee332d1aa0a7976b1352b760
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 989af27c2ad974dac8d370e335e923b70727a0c32991fe67011c2231cd5b526fdf59e73d571df8423ae69e9e27a5fa03dd69cf94a732ec76138cb34188af0792
|
|
7
|
+
data.tar.gz: d3daaccd0d51e4a78b825f5ea2f7baf6a3c23c11df1c33eb793b649c23b18bac2255de8e3fc3e3e7d62addac871b77f20879931dac73d6f14fec6fe54c10caea
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
1
|
+
# Managed by Cimas; trusted-publishing change is intentional (2026-08-18):
|
|
2
|
+
# publish via RubyGems OIDC Trusted Publishing instead of the API key
|
|
3
|
+
# (the API-key path is blocked by rubygems.org MFA OTP demands).
|
|
4
|
+
# See https://github.com/metanorma/ci — the reusable workflow uses its
|
|
5
|
+
# OIDC path whenever the rubygems-api-key secret is not passed.
|
|
3
6
|
name: release
|
|
4
7
|
|
|
5
8
|
on:
|
|
@@ -20,6 +23,5 @@ jobs:
|
|
|
20
23
|
with:
|
|
21
24
|
next_version: ${{ github.event.inputs.next_version }}
|
|
22
25
|
secrets:
|
|
23
|
-
rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }}
|
|
24
26
|
pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
|
|
25
27
|
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# TODO PDF 104: Schema-faithful Table model completion
|
|
2
2
|
|
|
3
|
-
## Status:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-18
|
|
4
|
+
|
|
5
|
+
All 171 Table_Object attributes from
|
|
6
|
+
`reference-docs/schemas/package/Stories/Story.rnc` are declared on
|
|
7
|
+
`Elements::Table` (the prior rendering-relevant subset plus the 155
|
|
8
|
+
remaining: border tints/gaps/overprints, default row/column strokes,
|
|
9
|
+
alternating fills, cell insets, diagonal lines, caption settings,
|
|
10
|
+
…), generated with `scripts/rnc_to_lutaml.rb` in schema order.
|
|
11
|
+
|
|
12
|
+
Drift defense: `spec/idml/elements/table_spec.rb` reads the RNC at
|
|
13
|
+
spec time and asserts a Table built with every schema attribute
|
|
14
|
+
round-trips every attribute — any attribute dropped from the model
|
|
15
|
+
fails the spec.
|
|
16
|
+
|
|
17
|
+
The renderer still consults only the rendering-relevant subset; the
|
|
18
|
+
rest are carried for round-trip and downstream-tool fidelity.
|
|
9
19
|
|
|
10
20
|
## Problem
|
|
11
21
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# TODO PDF 116: Justified-text last-line handling
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE — implemented 2026-08-18
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`TextEngine::Justifier` spread slack across inter-word spaces for
|
|
8
|
+
`:justified` paragraphs on EVERY line, including each paragraph's
|
|
9
|
+
final line — InDesign (and standard typography) leaves the last line
|
|
10
|
+
of a justified paragraph ragged. Every fully-justified paragraph
|
|
11
|
+
rendered with its final line stretched across the full column width.
|
|
12
|
+
|
|
13
|
+
## What was done
|
|
14
|
+
|
|
15
|
+
- `Justifier.justify` takes `last_line:`; when true, a `:justified`
|
|
16
|
+
line falls back to left alignment (no space stretching). All other
|
|
17
|
+
alignments unchanged.
|
|
18
|
+
- `TextFrameRenderer.layout_run` passes `last_line:` true for the
|
|
19
|
+
final line of the paragraph's FINAL run (`paragraph_last:` flag
|
|
20
|
+
threaded from `render_runs_for_paragraph`, which knows the run
|
|
21
|
+
list). Mid-paragraph run breaks still justify — only the true
|
|
22
|
+
paragraph-final line goes ragged.
|
|
23
|
+
- Footnote layout (`Render::Footnote.layout_paragraph`) applies the
|
|
24
|
+
same rule for the final line of the final run.
|
|
25
|
+
|
|
26
|
+
## Known limitations
|
|
27
|
+
|
|
28
|
+
- Justification adjusts inter-word spaces only; letter spacing and
|
|
29
|
+
glyph scaling (Minimum/Maximum/DesiredGlyphScaling, tracking) are
|
|
30
|
+
not applied. ToBinding / RTL binding alignment remains mapped to
|
|
31
|
+
left.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# TODO PDF 117: Endnote support
|
|
2
|
+
|
|
3
|
+
## Status: OPEN (modeling COMPLETE 2026-08-18; rendering OPEN pending
|
|
4
|
+
a real fixture)
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
IDML endnotes use a reference architecture unlike footnotes: the
|
|
9
|
+
main story carries `<Endnote Self="..." EndnoteTextRange="..."/>`
|
|
10
|
+
plus `<EndnoteRange SourceEndnote="..."/>` markers (both at Story
|
|
11
|
+
level, per Story.rnc), while the endnote TEXT lives in a separate
|
|
12
|
+
story flagged `IsEndnoteStory="true"`. Documents with endnotes
|
|
13
|
+
(scholarly publishing) currently drop endnote text entirely.
|
|
14
|
+
|
|
15
|
+
## What is done (2026-08-18)
|
|
16
|
+
|
|
17
|
+
- `Elements::Endnote` (Self, EndnoteTextRange) and
|
|
18
|
+
`Elements::EndnoteRange` (Self, SourceEndnote) — schema-faithful
|
|
19
|
+
models, wired into `StoryInner` (`endnote`, `endnote_range`
|
|
20
|
+
collections). Round-trip + parse specs.
|
|
21
|
+
- `StoryInner#is_endnote_story` already parsed, so endnote stories
|
|
22
|
+
are identifiable.
|
|
23
|
+
|
|
24
|
+
## Design for rendering (needs a real InDesign fixture to validate)
|
|
25
|
+
|
|
26
|
+
1. Marker: story-level `Endnote`/`EndnoteRange` pairs delimit the
|
|
27
|
+
anchor range; marker position requires story text-offset
|
|
28
|
+
bookkeeping the extraction layer doesn't currently track (unlike
|
|
29
|
+
footnotes, which sit inside a CSR). A range-aware extraction is
|
|
30
|
+
the prerequisite.
|
|
31
|
+
2. Endnote text: resolve `EndnoteTextRange` → the endnote story
|
|
32
|
+
(IsEndnoteStory) → its paragraphs.
|
|
33
|
+
3. Placement: per EndnoteOption Scope — end of story / end of
|
|
34
|
+
section / end of document; end-of-story is the tractable default
|
|
35
|
+
(append after the referencing story's last paragraph).
|
|
36
|
+
4. Numbering: endnotes number separately from footnotes
|
|
37
|
+
(FootnoteOption vs EndnoteOption StartAt/Prefix/Suffix — the
|
|
38
|
+
Endnote preference object is not yet modeled in Preferences).
|
|
39
|
+
|
|
40
|
+
## Why rendering is deferred
|
|
41
|
+
|
|
42
|
+
The marker-position machinery (text-range resolution) cannot be
|
|
43
|
+
validated without a real fixture; blind implementation risks wrong
|
|
44
|
+
anchor placement on production files. Schema modeling + round-trip
|
|
45
|
+
is complete and verified.
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
# TODO PDF 13: CJK text layout
|
|
2
2
|
|
|
3
|
-
## Status: PARTIAL —
|
|
4
|
-
measurement and
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
## Status: PARTIAL — horizontal-mode CJK complete (2026-08-17):
|
|
4
|
+
CJK glyph measurement, kinsoku shori, and ruby annotations render.
|
|
5
|
+
Vertical writing mode, tate-chu-yoko, and mojikumi remain OPEN
|
|
6
|
+
stretch goals — they need a vertical (rotated) layout engine and
|
|
7
|
+
real vertical-mode fixtures to build against. See
|
|
8
|
+
`lib/idml/text_engine/cjk_layout.rb`.
|
|
9
|
+
|
|
10
|
+
## Progress 2026-08-17
|
|
11
|
+
|
|
12
|
+
- Kinsoku shori is now LIVE in the pipeline: LineBreaker.break
|
|
13
|
+
post-processes any run containing CJK glyphs through
|
|
14
|
+
CjkLayout.apply_kinsoku, so no rendered line starts with
|
|
15
|
+
、。,」etc. or ends with 「 etc. (previously the logic existed
|
|
16
|
+
but was never called).
|
|
17
|
+
- Ruby (phonetic annotations) render: CSR RubyString /
|
|
18
|
+
RubyFontSize / RubyPosition extract onto the run; the renderer
|
|
19
|
+
emits the annotation centered above the run's first line (half
|
|
20
|
+
the base size by default; Below* positions place it under).
|
|
21
|
+
Approximation: IDML attaches ruby to a character range; we
|
|
22
|
+
annotate the whole first line.
|
|
7
23
|
|
|
8
24
|
## Goal
|
|
9
25
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Idml
|
|
4
|
+
module Elements
|
|
5
|
+
# `<Endnote>` — a story-level endnote reference. The endnote's
|
|
6
|
+
# TEXT lives in a separate story flagged `IsEndnoteStory="true"`;
|
|
7
|
+
# this element points at it via `EndnoteTextRange` (schema:
|
|
8
|
+
# Endnote_Object in Stories/Story.rnc — Self plus the range
|
|
9
|
+
# reference; no inline content). Rendering design in
|
|
10
|
+
# TODO.pdf/117-endnote-support.md.
|
|
11
|
+
class Endnote < Lutaml::Model::Serializable
|
|
12
|
+
attribute :self_attr, :string
|
|
13
|
+
attribute :endnote_text_range, :string
|
|
14
|
+
|
|
15
|
+
xml do
|
|
16
|
+
root "Endnote"
|
|
17
|
+
map_attribute "Self", to: :self_attr
|
|
18
|
+
map_attribute "EndnoteTextRange", to: :endnote_text_range
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Idml
|
|
4
|
+
module Elements
|
|
5
|
+
# `<EndnoteRange>` — marks the text range in the main story that
|
|
6
|
+
# an endnote references, linking back via `SourceEndnote`
|
|
7
|
+
# (schema: EndnoteRange_Object in Stories/Story.rnc). Pairs with
|
|
8
|
+
# Elements::Endnote; rendering design in
|
|
9
|
+
# TODO.pdf/117-endnote-support.md.
|
|
10
|
+
class EndnoteRange < Lutaml::Model::Serializable
|
|
11
|
+
attribute :self_attr, :string
|
|
12
|
+
attribute :source_endnote, :string
|
|
13
|
+
|
|
14
|
+
xml do
|
|
15
|
+
root "EndnoteRange"
|
|
16
|
+
map_attribute "Self", to: :self_attr
|
|
17
|
+
map_attribute "SourceEndnote", to: :source_endnote
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -313,6 +313,9 @@ module Idml
|
|
|
313
313
|
attribute :content, Idml::Elements::Content, collection: true
|
|
314
314
|
attribute :xml_element, Idml::Elements::XmlElement, collection: true
|
|
315
315
|
attribute :footnote, Idml::Elements::Footnote, collection: true
|
|
316
|
+
attribute :endnote, Idml::Elements::Endnote, collection: true
|
|
317
|
+
attribute :endnote_range, Idml::Elements::EndnoteRange,
|
|
318
|
+
collection: true
|
|
316
319
|
|
|
317
320
|
xml do
|
|
318
321
|
root "Story"
|
|
@@ -678,6 +681,8 @@ module Idml
|
|
|
678
681
|
map_element "Content", to: :content
|
|
679
682
|
map_element "XMLElement", to: :xml_element
|
|
680
683
|
map_element "Footnote", to: :footnote
|
|
684
|
+
map_element "Endnote", to: :endnote
|
|
685
|
+
map_element "EndnoteRange", to: :endnote_range
|
|
681
686
|
end
|
|
682
687
|
|
|
683
688
|
def text_content
|
data/lib/idml/elements/table.rb
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module Idml
|
|
4
4
|
module Elements
|
|
5
5
|
# `<Table>` — a table page item. Carries geometry, body/header
|
|
6
|
-
# row counts, column layout, and border styling.
|
|
6
|
+
# row counts, column layout, and border styling. Declares every
|
|
7
|
+
# attribute of Table_Object in Stories/Story.rnc (171 schema
|
|
8
|
+
# attributes). Two child
|
|
7
9
|
# collections hold the table's content:
|
|
8
10
|
#
|
|
9
11
|
# - `row` — schema-faithful `<Row>` siblings (real IDML).
|
|
@@ -43,6 +45,167 @@ module Idml
|
|
|
43
45
|
attribute :right_border_stroke_weight, :float
|
|
44
46
|
attribute :right_border_stroke_color, :string
|
|
45
47
|
|
|
48
|
+
# The remaining Table_Object attributes (border tints/gaps/
|
|
49
|
+
# overprints, default row/column strokes, alternating fills,
|
|
50
|
+
# cell insets, diagonal lines, caption settings, …) in schema
|
|
51
|
+
# order — generated by `scripts/rnc_to_lutaml.rb` from
|
|
52
|
+
# reference-docs/schemas/package/Stories/Story.rnc.
|
|
53
|
+
attribute :top_border_stroke_type, :string
|
|
54
|
+
attribute :top_border_stroke_tint, :float
|
|
55
|
+
attribute :top_border_stroke_overprint, :boolean
|
|
56
|
+
attribute :top_border_stroke_gap_color, :string
|
|
57
|
+
attribute :top_border_stroke_gap_tint, :float
|
|
58
|
+
attribute :top_border_stroke_gap_overprint, :boolean
|
|
59
|
+
attribute :left_border_stroke_type, :string
|
|
60
|
+
attribute :left_border_stroke_tint, :float
|
|
61
|
+
attribute :left_border_stroke_overprint, :boolean
|
|
62
|
+
attribute :left_border_stroke_gap_color, :string
|
|
63
|
+
attribute :left_border_stroke_gap_tint, :float
|
|
64
|
+
attribute :left_border_stroke_gap_overprint, :boolean
|
|
65
|
+
attribute :bottom_border_stroke_type, :string
|
|
66
|
+
attribute :bottom_border_stroke_tint, :float
|
|
67
|
+
attribute :bottom_border_stroke_overprint, :boolean
|
|
68
|
+
attribute :bottom_border_stroke_gap_color, :string
|
|
69
|
+
attribute :bottom_border_stroke_gap_tint, :float
|
|
70
|
+
attribute :bottom_border_stroke_gap_overprint, :boolean
|
|
71
|
+
attribute :right_border_stroke_type, :string
|
|
72
|
+
attribute :right_border_stroke_tint, :float
|
|
73
|
+
attribute :right_border_stroke_overprint, :boolean
|
|
74
|
+
attribute :right_border_stroke_gap_color, :string
|
|
75
|
+
attribute :right_border_stroke_gap_tint, :float
|
|
76
|
+
attribute :right_border_stroke_gap_overprint, :boolean
|
|
77
|
+
attribute :space_before, :float
|
|
78
|
+
attribute :space_after, :float
|
|
79
|
+
attribute :skip_first_alternating_stroke_rows, :integer
|
|
80
|
+
attribute :skip_last_alternating_stroke_rows, :integer
|
|
81
|
+
attribute :start_row_stroke_count, :integer
|
|
82
|
+
attribute :start_row_stroke_color, :string
|
|
83
|
+
attribute :start_row_stroke_weight, :float
|
|
84
|
+
attribute :start_row_stroke_type, :string
|
|
85
|
+
attribute :start_row_stroke_tint, :float
|
|
86
|
+
attribute :start_row_stroke_gap_overprint, :boolean
|
|
87
|
+
attribute :start_row_stroke_gap_color, :string
|
|
88
|
+
attribute :start_row_stroke_gap_tint, :float
|
|
89
|
+
attribute :start_row_stroke_overprint, :boolean
|
|
90
|
+
attribute :end_row_stroke_count, :integer
|
|
91
|
+
attribute :end_row_stroke_color, :string
|
|
92
|
+
attribute :end_row_stroke_weight, :float
|
|
93
|
+
attribute :end_row_stroke_type, :string
|
|
94
|
+
attribute :end_row_stroke_tint, :float
|
|
95
|
+
attribute :end_row_stroke_overprint, :boolean
|
|
96
|
+
attribute :end_row_stroke_gap_color, :string
|
|
97
|
+
attribute :end_row_stroke_gap_tint, :float
|
|
98
|
+
attribute :end_row_stroke_gap_overprint, :boolean
|
|
99
|
+
attribute :skip_first_alternating_stroke_columns, :integer
|
|
100
|
+
attribute :skip_last_alternating_stroke_columns, :integer
|
|
101
|
+
attribute :start_column_stroke_count, :integer
|
|
102
|
+
attribute :start_column_stroke_color, :string
|
|
103
|
+
attribute :start_column_stroke_weight, :float
|
|
104
|
+
attribute :start_column_stroke_type, :string
|
|
105
|
+
attribute :start_column_stroke_tint, :float
|
|
106
|
+
attribute :start_column_stroke_overprint, :boolean
|
|
107
|
+
attribute :start_column_stroke_gap_color, :string
|
|
108
|
+
attribute :start_column_stroke_gap_tint, :float
|
|
109
|
+
attribute :start_column_stroke_gap_overprint, :boolean
|
|
110
|
+
attribute :end_column_stroke_count, :integer
|
|
111
|
+
attribute :end_column_stroke_color, :string
|
|
112
|
+
attribute :end_column_stroke_weight, :float
|
|
113
|
+
attribute :end_column_line_style, :string
|
|
114
|
+
attribute :end_column_stroke_tint, :float
|
|
115
|
+
attribute :end_column_stroke_overprint, :boolean
|
|
116
|
+
attribute :end_column_stroke_gap_color, :string
|
|
117
|
+
attribute :end_column_stroke_gap_tint, :float
|
|
118
|
+
attribute :end_column_stroke_gap_overprint, :boolean
|
|
119
|
+
attribute :column_fills_priority, :boolean
|
|
120
|
+
attribute :skip_first_alternating_fill_rows, :integer
|
|
121
|
+
attribute :skip_last_alternating_fill_rows, :integer
|
|
122
|
+
attribute :start_row_fill_color, :string
|
|
123
|
+
attribute :start_row_fill_count, :integer
|
|
124
|
+
attribute :start_row_fill_tint, :float
|
|
125
|
+
attribute :start_row_fill_overprint, :boolean
|
|
126
|
+
attribute :end_row_fill_count, :integer
|
|
127
|
+
attribute :end_row_fill_color, :string
|
|
128
|
+
attribute :end_row_fill_tint, :float
|
|
129
|
+
attribute :end_row_fill_overprint, :boolean
|
|
130
|
+
attribute :skip_first_alternating_fill_columns, :integer
|
|
131
|
+
attribute :skip_last_alternating_fill_columns, :integer
|
|
132
|
+
attribute :start_column_fill_count, :integer
|
|
133
|
+
attribute :start_column_fill_color, :string
|
|
134
|
+
attribute :start_column_fill_tint, :float
|
|
135
|
+
attribute :start_column_fill_overprint, :boolean
|
|
136
|
+
attribute :end_column_fill_count, :integer
|
|
137
|
+
attribute :end_column_fill_color, :string
|
|
138
|
+
attribute :end_column_fill_tint, :float
|
|
139
|
+
attribute :end_column_fill_overprint, :boolean
|
|
140
|
+
attribute :break_headers, :string
|
|
141
|
+
attribute :break_footers, :string
|
|
142
|
+
attribute :skip_first_header, :boolean
|
|
143
|
+
attribute :skip_last_footer, :boolean
|
|
144
|
+
attribute :stroke_order, :string
|
|
145
|
+
attribute :default_row_stroke_weight, :float
|
|
146
|
+
attribute :default_row_stroke_type, :string
|
|
147
|
+
attribute :default_row_stroke_color, :string
|
|
148
|
+
attribute :default_row_stroke_tint, :float
|
|
149
|
+
attribute :default_row_stroke_overprint, :boolean
|
|
150
|
+
attribute :default_row_stroke_gap_color, :string
|
|
151
|
+
attribute :default_row_stroke_gap_tint, :float
|
|
152
|
+
attribute :default_row_stroke_gap_overprint, :boolean
|
|
153
|
+
attribute :default_column_stroke_weight, :float
|
|
154
|
+
attribute :default_column_stroke_type, :string
|
|
155
|
+
attribute :default_column_stroke_color, :string
|
|
156
|
+
attribute :default_column_stroke_tint, :float
|
|
157
|
+
attribute :default_column_stroke_overprint, :boolean
|
|
158
|
+
attribute :default_column_stroke_gap_color, :string
|
|
159
|
+
attribute :default_column_stroke_gap_tint, :float
|
|
160
|
+
attribute :default_column_stroke_gap_overprint, :boolean
|
|
161
|
+
attribute :top_inset, :float
|
|
162
|
+
attribute :left_inset, :float
|
|
163
|
+
attribute :bottom_inset, :float
|
|
164
|
+
attribute :right_inset, :float
|
|
165
|
+
attribute :fill_tint, :float
|
|
166
|
+
attribute :overprint_fill, :boolean
|
|
167
|
+
attribute :top_left_diagonal_line, :boolean
|
|
168
|
+
attribute :top_right_diagonal_line, :boolean
|
|
169
|
+
attribute :diagonal_line_in_front, :boolean
|
|
170
|
+
attribute :diagonal_line_stroke_weight, :float
|
|
171
|
+
attribute :diagonal_line_stroke_type, :string
|
|
172
|
+
attribute :diagonal_line_stroke_color, :string
|
|
173
|
+
attribute :diagonal_line_stroke_tint, :float
|
|
174
|
+
attribute :diagonal_line_stroke_overprint, :boolean
|
|
175
|
+
attribute :diagonal_line_stroke_gap_color, :string
|
|
176
|
+
attribute :diagonal_line_stroke_gap_tint, :float
|
|
177
|
+
attribute :diagonal_line_stroke_gap_overprint, :boolean
|
|
178
|
+
attribute :clip_content_to_cell, :boolean
|
|
179
|
+
attribute :first_baseline_offset, :string
|
|
180
|
+
attribute :vertical_justification, :string
|
|
181
|
+
attribute :paragraph_spacing_limit, :float
|
|
182
|
+
attribute :minimum_first_baseline_offset, :float
|
|
183
|
+
attribute :rotation_angle, :float
|
|
184
|
+
attribute :writing_direction, :boolean
|
|
185
|
+
attribute :minimum_height, :float
|
|
186
|
+
attribute :maximum_height, :float
|
|
187
|
+
attribute :keep_with_next_row, :boolean
|
|
188
|
+
attribute :start_row, :string
|
|
189
|
+
attribute :auto_grow, :boolean
|
|
190
|
+
attribute :num_header_columns, :integer
|
|
191
|
+
attribute :header_columns_position, :string
|
|
192
|
+
attribute :caption_paragraph_count, :integer
|
|
193
|
+
attribute :caption_position, :string
|
|
194
|
+
attribute :graphic_left_inset, :float
|
|
195
|
+
attribute :graphic_top_inset, :float
|
|
196
|
+
attribute :graphic_right_inset, :float
|
|
197
|
+
attribute :graphic_bottom_inset, :float
|
|
198
|
+
attribute :clip_content_to_graphic_cell, :boolean
|
|
199
|
+
attribute :text_top_inset, :float
|
|
200
|
+
attribute :text_left_inset, :float
|
|
201
|
+
attribute :text_bottom_inset, :float
|
|
202
|
+
attribute :text_right_inset, :float
|
|
203
|
+
attribute :clip_content_to_text_cell, :boolean
|
|
204
|
+
attribute :single_row_height, :float
|
|
205
|
+
attribute :applied_table_style, :string
|
|
206
|
+
attribute :display_collapsed, :boolean
|
|
207
|
+
attribute :display_order, :string
|
|
208
|
+
|
|
46
209
|
attribute :table_row, Idml::Elements::TableRow, collection: true
|
|
47
210
|
attribute :cell, Idml::Elements::Cell, collection: true
|
|
48
211
|
attribute :row, Idml::Elements::Row, collection: true
|
|
@@ -75,6 +238,211 @@ module Idml
|
|
|
75
238
|
to: :bottom_border_stroke_color
|
|
76
239
|
map_attribute "RightBorderStrokeWeight", to: :right_border_stroke_weight
|
|
77
240
|
map_attribute "RightBorderStrokeColor", to: :right_border_stroke_color
|
|
241
|
+
map_attribute "TopBorderStrokeType", to: :top_border_stroke_type
|
|
242
|
+
map_attribute "TopBorderStrokeTint", to: :top_border_stroke_tint
|
|
243
|
+
map_attribute "TopBorderStrokeOverprint",
|
|
244
|
+
to: :top_border_stroke_overprint
|
|
245
|
+
map_attribute "TopBorderStrokeGapColor",
|
|
246
|
+
to: :top_border_stroke_gap_color
|
|
247
|
+
map_attribute "TopBorderStrokeGapTint", to: :top_border_stroke_gap_tint
|
|
248
|
+
map_attribute "TopBorderStrokeGapOverprint",
|
|
249
|
+
to: :top_border_stroke_gap_overprint
|
|
250
|
+
map_attribute "LeftBorderStrokeType", to: :left_border_stroke_type
|
|
251
|
+
map_attribute "LeftBorderStrokeTint", to: :left_border_stroke_tint
|
|
252
|
+
map_attribute "LeftBorderStrokeOverprint",
|
|
253
|
+
to: :left_border_stroke_overprint
|
|
254
|
+
map_attribute "LeftBorderStrokeGapColor",
|
|
255
|
+
to: :left_border_stroke_gap_color
|
|
256
|
+
map_attribute "LeftBorderStrokeGapTint",
|
|
257
|
+
to: :left_border_stroke_gap_tint
|
|
258
|
+
map_attribute "LeftBorderStrokeGapOverprint",
|
|
259
|
+
to: :left_border_stroke_gap_overprint
|
|
260
|
+
map_attribute "BottomBorderStrokeType", to: :bottom_border_stroke_type
|
|
261
|
+
map_attribute "BottomBorderStrokeTint", to: :bottom_border_stroke_tint
|
|
262
|
+
map_attribute "BottomBorderStrokeOverprint",
|
|
263
|
+
to: :bottom_border_stroke_overprint
|
|
264
|
+
map_attribute "BottomBorderStrokeGapColor",
|
|
265
|
+
to: :bottom_border_stroke_gap_color
|
|
266
|
+
map_attribute "BottomBorderStrokeGapTint",
|
|
267
|
+
to: :bottom_border_stroke_gap_tint
|
|
268
|
+
map_attribute "BottomBorderStrokeGapOverprint",
|
|
269
|
+
to: :bottom_border_stroke_gap_overprint
|
|
270
|
+
map_attribute "RightBorderStrokeType", to: :right_border_stroke_type
|
|
271
|
+
map_attribute "RightBorderStrokeTint", to: :right_border_stroke_tint
|
|
272
|
+
map_attribute "RightBorderStrokeOverprint",
|
|
273
|
+
to: :right_border_stroke_overprint
|
|
274
|
+
map_attribute "RightBorderStrokeGapColor",
|
|
275
|
+
to: :right_border_stroke_gap_color
|
|
276
|
+
map_attribute "RightBorderStrokeGapTint",
|
|
277
|
+
to: :right_border_stroke_gap_tint
|
|
278
|
+
map_attribute "RightBorderStrokeGapOverprint",
|
|
279
|
+
to: :right_border_stroke_gap_overprint
|
|
280
|
+
map_attribute "SpaceBefore", to: :space_before
|
|
281
|
+
map_attribute "SpaceAfter", to: :space_after
|
|
282
|
+
map_attribute "SkipFirstAlternatingStrokeRows",
|
|
283
|
+
to: :skip_first_alternating_stroke_rows
|
|
284
|
+
map_attribute "SkipLastAlternatingStrokeRows",
|
|
285
|
+
to: :skip_last_alternating_stroke_rows
|
|
286
|
+
map_attribute "StartRowStrokeCount", to: :start_row_stroke_count
|
|
287
|
+
map_attribute "StartRowStrokeColor", to: :start_row_stroke_color
|
|
288
|
+
map_attribute "StartRowStrokeWeight", to: :start_row_stroke_weight
|
|
289
|
+
map_attribute "StartRowStrokeType", to: :start_row_stroke_type
|
|
290
|
+
map_attribute "StartRowStrokeTint", to: :start_row_stroke_tint
|
|
291
|
+
map_attribute "StartRowStrokeGapOverprint",
|
|
292
|
+
to: :start_row_stroke_gap_overprint
|
|
293
|
+
map_attribute "StartRowStrokeGapColor", to: :start_row_stroke_gap_color
|
|
294
|
+
map_attribute "StartRowStrokeGapTint", to: :start_row_stroke_gap_tint
|
|
295
|
+
map_attribute "StartRowStrokeOverprint", to: :start_row_stroke_overprint
|
|
296
|
+
map_attribute "EndRowStrokeCount", to: :end_row_stroke_count
|
|
297
|
+
map_attribute "EndRowStrokeColor", to: :end_row_stroke_color
|
|
298
|
+
map_attribute "EndRowStrokeWeight", to: :end_row_stroke_weight
|
|
299
|
+
map_attribute "EndRowStrokeType", to: :end_row_stroke_type
|
|
300
|
+
map_attribute "EndRowStrokeTint", to: :end_row_stroke_tint
|
|
301
|
+
map_attribute "EndRowStrokeOverprint", to: :end_row_stroke_overprint
|
|
302
|
+
map_attribute "EndRowStrokeGapColor", to: :end_row_stroke_gap_color
|
|
303
|
+
map_attribute "EndRowStrokeGapTint", to: :end_row_stroke_gap_tint
|
|
304
|
+
map_attribute "EndRowStrokeGapOverprint",
|
|
305
|
+
to: :end_row_stroke_gap_overprint
|
|
306
|
+
map_attribute "SkipFirstAlternatingStrokeColumns",
|
|
307
|
+
to: :skip_first_alternating_stroke_columns
|
|
308
|
+
map_attribute "SkipLastAlternatingStrokeColumns",
|
|
309
|
+
to: :skip_last_alternating_stroke_columns
|
|
310
|
+
map_attribute "StartColumnStrokeCount", to: :start_column_stroke_count
|
|
311
|
+
map_attribute "StartColumnStrokeColor", to: :start_column_stroke_color
|
|
312
|
+
map_attribute "StartColumnStrokeWeight", to: :start_column_stroke_weight
|
|
313
|
+
map_attribute "StartColumnStrokeType", to: :start_column_stroke_type
|
|
314
|
+
map_attribute "StartColumnStrokeTint", to: :start_column_stroke_tint
|
|
315
|
+
map_attribute "StartColumnStrokeOverprint",
|
|
316
|
+
to: :start_column_stroke_overprint
|
|
317
|
+
map_attribute "StartColumnStrokeGapColor",
|
|
318
|
+
to: :start_column_stroke_gap_color
|
|
319
|
+
map_attribute "StartColumnStrokeGapTint",
|
|
320
|
+
to: :start_column_stroke_gap_tint
|
|
321
|
+
map_attribute "StartColumnStrokeGapOverprint",
|
|
322
|
+
to: :start_column_stroke_gap_overprint
|
|
323
|
+
map_attribute "EndColumnStrokeCount", to: :end_column_stroke_count
|
|
324
|
+
map_attribute "EndColumnStrokeColor", to: :end_column_stroke_color
|
|
325
|
+
map_attribute "EndColumnStrokeWeight", to: :end_column_stroke_weight
|
|
326
|
+
map_attribute "EndColumnLineStyle", to: :end_column_line_style
|
|
327
|
+
map_attribute "EndColumnStrokeTint", to: :end_column_stroke_tint
|
|
328
|
+
map_attribute "EndColumnStrokeOverprint",
|
|
329
|
+
to: :end_column_stroke_overprint
|
|
330
|
+
map_attribute "EndColumnStrokeGapColor",
|
|
331
|
+
to: :end_column_stroke_gap_color
|
|
332
|
+
map_attribute "EndColumnStrokeGapTint", to: :end_column_stroke_gap_tint
|
|
333
|
+
map_attribute "EndColumnStrokeGapOverprint",
|
|
334
|
+
to: :end_column_stroke_gap_overprint
|
|
335
|
+
map_attribute "ColumnFillsPriority", to: :column_fills_priority
|
|
336
|
+
map_attribute "SkipFirstAlternatingFillRows",
|
|
337
|
+
to: :skip_first_alternating_fill_rows
|
|
338
|
+
map_attribute "SkipLastAlternatingFillRows",
|
|
339
|
+
to: :skip_last_alternating_fill_rows
|
|
340
|
+
map_attribute "StartRowFillColor", to: :start_row_fill_color
|
|
341
|
+
map_attribute "StartRowFillCount", to: :start_row_fill_count
|
|
342
|
+
map_attribute "StartRowFillTint", to: :start_row_fill_tint
|
|
343
|
+
map_attribute "StartRowFillOverprint", to: :start_row_fill_overprint
|
|
344
|
+
map_attribute "EndRowFillCount", to: :end_row_fill_count
|
|
345
|
+
map_attribute "EndRowFillColor", to: :end_row_fill_color
|
|
346
|
+
map_attribute "EndRowFillTint", to: :end_row_fill_tint
|
|
347
|
+
map_attribute "EndRowFillOverprint", to: :end_row_fill_overprint
|
|
348
|
+
map_attribute "SkipFirstAlternatingFillColumns",
|
|
349
|
+
to: :skip_first_alternating_fill_columns
|
|
350
|
+
map_attribute "SkipLastAlternatingFillColumns",
|
|
351
|
+
to: :skip_last_alternating_fill_columns
|
|
352
|
+
map_attribute "StartColumnFillCount", to: :start_column_fill_count
|
|
353
|
+
map_attribute "StartColumnFillColor", to: :start_column_fill_color
|
|
354
|
+
map_attribute "StartColumnFillTint", to: :start_column_fill_tint
|
|
355
|
+
map_attribute "StartColumnFillOverprint",
|
|
356
|
+
to: :start_column_fill_overprint
|
|
357
|
+
map_attribute "EndColumnFillCount", to: :end_column_fill_count
|
|
358
|
+
map_attribute "EndColumnFillColor", to: :end_column_fill_color
|
|
359
|
+
map_attribute "EndColumnFillTint", to: :end_column_fill_tint
|
|
360
|
+
map_attribute "EndColumnFillOverprint", to: :end_column_fill_overprint
|
|
361
|
+
map_attribute "BreakHeaders", to: :break_headers
|
|
362
|
+
map_attribute "BreakFooters", to: :break_footers
|
|
363
|
+
map_attribute "SkipFirstHeader", to: :skip_first_header
|
|
364
|
+
map_attribute "SkipLastFooter", to: :skip_last_footer
|
|
365
|
+
map_attribute "StrokeOrder", to: :stroke_order
|
|
366
|
+
map_attribute "DefaultRowStrokeWeight", to: :default_row_stroke_weight
|
|
367
|
+
map_attribute "DefaultRowStrokeType", to: :default_row_stroke_type
|
|
368
|
+
map_attribute "DefaultRowStrokeColor", to: :default_row_stroke_color
|
|
369
|
+
map_attribute "DefaultRowStrokeTint", to: :default_row_stroke_tint
|
|
370
|
+
map_attribute "DefaultRowStrokeOverprint",
|
|
371
|
+
to: :default_row_stroke_overprint
|
|
372
|
+
map_attribute "DefaultRowStrokeGapColor",
|
|
373
|
+
to: :default_row_stroke_gap_color
|
|
374
|
+
map_attribute "DefaultRowStrokeGapTint",
|
|
375
|
+
to: :default_row_stroke_gap_tint
|
|
376
|
+
map_attribute "DefaultRowStrokeGapOverprint",
|
|
377
|
+
to: :default_row_stroke_gap_overprint
|
|
378
|
+
map_attribute "DefaultColumnStrokeWeight",
|
|
379
|
+
to: :default_column_stroke_weight
|
|
380
|
+
map_attribute "DefaultColumnStrokeType", to: :default_column_stroke_type
|
|
381
|
+
map_attribute "DefaultColumnStrokeColor",
|
|
382
|
+
to: :default_column_stroke_color
|
|
383
|
+
map_attribute "DefaultColumnStrokeTint", to: :default_column_stroke_tint
|
|
384
|
+
map_attribute "DefaultColumnStrokeOverprint",
|
|
385
|
+
to: :default_column_stroke_overprint
|
|
386
|
+
map_attribute "DefaultColumnStrokeGapColor",
|
|
387
|
+
to: :default_column_stroke_gap_color
|
|
388
|
+
map_attribute "DefaultColumnStrokeGapTint",
|
|
389
|
+
to: :default_column_stroke_gap_tint
|
|
390
|
+
map_attribute "DefaultColumnStrokeGapOverprint",
|
|
391
|
+
to: :default_column_stroke_gap_overprint
|
|
392
|
+
map_attribute "TopInset", to: :top_inset
|
|
393
|
+
map_attribute "LeftInset", to: :left_inset
|
|
394
|
+
map_attribute "BottomInset", to: :bottom_inset
|
|
395
|
+
map_attribute "RightInset", to: :right_inset
|
|
396
|
+
map_attribute "FillTint", to: :fill_tint
|
|
397
|
+
map_attribute "OverprintFill", to: :overprint_fill
|
|
398
|
+
map_attribute "TopLeftDiagonalLine", to: :top_left_diagonal_line
|
|
399
|
+
map_attribute "TopRightDiagonalLine", to: :top_right_diagonal_line
|
|
400
|
+
map_attribute "DiagonalLineInFront", to: :diagonal_line_in_front
|
|
401
|
+
map_attribute "DiagonalLineStrokeWeight",
|
|
402
|
+
to: :diagonal_line_stroke_weight
|
|
403
|
+
map_attribute "DiagonalLineStrokeType", to: :diagonal_line_stroke_type
|
|
404
|
+
map_attribute "DiagonalLineStrokeColor", to: :diagonal_line_stroke_color
|
|
405
|
+
map_attribute "DiagonalLineStrokeTint", to: :diagonal_line_stroke_tint
|
|
406
|
+
map_attribute "DiagonalLineStrokeOverprint",
|
|
407
|
+
to: :diagonal_line_stroke_overprint
|
|
408
|
+
map_attribute "DiagonalLineStrokeGapColor",
|
|
409
|
+
to: :diagonal_line_stroke_gap_color
|
|
410
|
+
map_attribute "DiagonalLineStrokeGapTint",
|
|
411
|
+
to: :diagonal_line_stroke_gap_tint
|
|
412
|
+
map_attribute "DiagonalLineStrokeGapOverprint",
|
|
413
|
+
to: :diagonal_line_stroke_gap_overprint
|
|
414
|
+
map_attribute "ClipContentToCell", to: :clip_content_to_cell
|
|
415
|
+
map_attribute "FirstBaselineOffset", to: :first_baseline_offset
|
|
416
|
+
map_attribute "VerticalJustification", to: :vertical_justification
|
|
417
|
+
map_attribute "ParagraphSpacingLimit", to: :paragraph_spacing_limit
|
|
418
|
+
map_attribute "MinimumFirstBaselineOffset",
|
|
419
|
+
to: :minimum_first_baseline_offset
|
|
420
|
+
map_attribute "RotationAngle", to: :rotation_angle
|
|
421
|
+
map_attribute "WritingDirection", to: :writing_direction
|
|
422
|
+
map_attribute "MinimumHeight", to: :minimum_height
|
|
423
|
+
map_attribute "MaximumHeight", to: :maximum_height
|
|
424
|
+
map_attribute "KeepWithNextRow", to: :keep_with_next_row
|
|
425
|
+
map_attribute "StartRow", to: :start_row
|
|
426
|
+
map_attribute "AutoGrow", to: :auto_grow
|
|
427
|
+
map_attribute "NumHeaderColumns", to: :num_header_columns
|
|
428
|
+
map_attribute "HeaderColumnsPosition", to: :header_columns_position
|
|
429
|
+
map_attribute "CaptionParagraphCount", to: :caption_paragraph_count
|
|
430
|
+
map_attribute "CaptionPosition", to: :caption_position
|
|
431
|
+
map_attribute "GraphicLeftInset", to: :graphic_left_inset
|
|
432
|
+
map_attribute "GraphicTopInset", to: :graphic_top_inset
|
|
433
|
+
map_attribute "GraphicRightInset", to: :graphic_right_inset
|
|
434
|
+
map_attribute "GraphicBottomInset", to: :graphic_bottom_inset
|
|
435
|
+
map_attribute "ClipContentToGraphicCell",
|
|
436
|
+
to: :clip_content_to_graphic_cell
|
|
437
|
+
map_attribute "TextTopInset", to: :text_top_inset
|
|
438
|
+
map_attribute "TextLeftInset", to: :text_left_inset
|
|
439
|
+
map_attribute "TextBottomInset", to: :text_bottom_inset
|
|
440
|
+
map_attribute "TextRightInset", to: :text_right_inset
|
|
441
|
+
map_attribute "ClipContentToTextCell", to: :clip_content_to_text_cell
|
|
442
|
+
map_attribute "SingleRowHeight", to: :single_row_height
|
|
443
|
+
map_attribute "AppliedTableStyle", to: :applied_table_style
|
|
444
|
+
map_attribute "DisplayCollapsed", to: :display_collapsed
|
|
445
|
+
map_attribute "DisplayOrder", to: :display_order
|
|
78
446
|
map_element "TableRow", to: :table_row
|
|
79
447
|
map_element "Cell", to: :cell
|
|
80
448
|
map_element "Row", to: :row
|
data/lib/idml/elements.rb
CHANGED
|
@@ -43,6 +43,8 @@ module Idml
|
|
|
43
43
|
autoload :DocumentPreference, "idml/elements/pref_document_preference"
|
|
44
44
|
autoload :DottedStrokeStyle, "idml/elements/dotted_stroke_style"
|
|
45
45
|
autoload :DropShadowSetting, "idml/elements/drop_shadow_setting"
|
|
46
|
+
autoload :Endnote, "idml/elements/endnote"
|
|
47
|
+
autoload :EndnoteRange, "idml/elements/endnote_range"
|
|
46
48
|
autoload :EPubExportPreference, "idml/elements/pref_e_pub_export_preference"
|
|
47
49
|
autoload :EPubFixedLayoutExportPreference,
|
|
48
50
|
"idml/elements/pref_e_pub_fixed_layout_export_preference"
|
data/lib/idml/render/footnote.rb
CHANGED
|
@@ -128,10 +128,11 @@ module Idml
|
|
|
128
128
|
size: size)
|
|
129
129
|
lines = TextEngine::LineBreaker.break(glyphs: glyphs,
|
|
130
130
|
frame_width: wrap_width)
|
|
131
|
-
lines.
|
|
131
|
+
lines.each_with_index do |line, index|
|
|
132
132
|
TextEngine::Justifier.justify(
|
|
133
133
|
line: line, frame_width: wrap_width,
|
|
134
|
-
alignment: paragraph.alignment || :left
|
|
134
|
+
alignment: paragraph.alignment || :left,
|
|
135
|
+
last_line: last_line?(paragraph, run, index, lines)
|
|
135
136
|
)
|
|
136
137
|
end
|
|
137
138
|
leading = TextEngine::VerticalLayout.leading_for(
|
|
@@ -150,6 +151,13 @@ module Idml
|
|
|
150
151
|
end
|
|
151
152
|
private_class_method :layout_paragraph
|
|
152
153
|
|
|
154
|
+
# True for the final line of the footnote paragraph's final
|
|
155
|
+
# run — kept ragged under full justification.
|
|
156
|
+
def self.last_line?(paragraph, run, index, lines)
|
|
157
|
+
run.equal?(paragraph.runs.last) && index == lines.length - 1
|
|
158
|
+
end
|
|
159
|
+
private_class_method :last_line?
|
|
160
|
+
|
|
153
161
|
# Height to reserve at the frame bottom for the entries:
|
|
154
162
|
# separator gap + the measured paragraph stack.
|
|
155
163
|
def self.reserved_height(entries, font, frame, option = nil)
|
|
@@ -25,6 +25,7 @@ module Idml
|
|
|
25
25
|
# FontMetrics is available (no shaper → no wrap → rough output).
|
|
26
26
|
class TextFrameRenderer
|
|
27
27
|
DEFAULT_SIZE = 12.0
|
|
28
|
+
RUBY_SIZE_FACTOR = 0.5
|
|
28
29
|
|
|
29
30
|
def self.render(canvas, context)
|
|
30
31
|
frame = context.item
|
|
@@ -510,7 +511,8 @@ module Idml
|
|
|
510
511
|
canvas, run, paragraph, context, layout_frame, font,
|
|
511
512
|
run_wrap, cursor_y, space_before,
|
|
512
513
|
para_attrs[:first_line_indent], para_attrs[:left_indent],
|
|
513
|
-
char_cursor, bottom_limit
|
|
514
|
+
char_cursor, bottom_limit,
|
|
515
|
+
paragraph_last: run.equal?(runs.last)
|
|
514
516
|
)
|
|
515
517
|
char_cursor += positioned.length
|
|
516
518
|
cursor_y = next_y
|
|
@@ -711,7 +713,7 @@ module Idml
|
|
|
711
713
|
def self.layout_run(canvas, run, paragraph, context, layout_frame,
|
|
712
714
|
font, wrap_width, cursor_y, space_before,
|
|
713
715
|
first_line_indent, left_indent,
|
|
714
|
-
char_cursor, bottom_limit)
|
|
716
|
+
char_cursor, bottom_limit, paragraph_last: false)
|
|
715
717
|
size = run.point_size || DEFAULT_SIZE
|
|
716
718
|
glyphs = TextEngine::Shaper.shape(
|
|
717
719
|
text: run.text, font: font, size: size,
|
|
@@ -719,12 +721,7 @@ module Idml
|
|
|
719
721
|
lines = TextEngine::LineBreaker.break(
|
|
720
722
|
glyphs: glyphs, frame_width: wrap_width,
|
|
721
723
|
)
|
|
722
|
-
lines
|
|
723
|
-
TextEngine::Justifier.justify(
|
|
724
|
-
line: line, frame_width: wrap_width,
|
|
725
|
-
alignment: paragraph.alignment || :left
|
|
726
|
-
)
|
|
727
|
-
end
|
|
724
|
+
justify_lines(lines, paragraph, wrap_width, paragraph_last)
|
|
728
725
|
leading = leading_for(paragraph, size)
|
|
729
726
|
|
|
730
727
|
positioned, next_y = TextEngine::VerticalLayout.layout_block(
|
|
@@ -738,15 +735,41 @@ module Idml
|
|
|
738
735
|
left_indent: left_indent,
|
|
739
736
|
)
|
|
740
737
|
|
|
741
|
-
positioned
|
|
738
|
+
emit_run_lines(canvas, positioned, run, context, size, font,
|
|
739
|
+
bottom_limit, char_cursor)
|
|
740
|
+
[positioned, next_y]
|
|
741
|
+
end
|
|
742
|
+
private_class_method :layout_run
|
|
743
|
+
|
|
744
|
+
# Justifies the run's lines; only the final line of the
|
|
745
|
+
# paragraph's final run stays ragged under full
|
|
746
|
+
# justification.
|
|
747
|
+
def self.justify_lines(lines, paragraph, wrap_width,
|
|
748
|
+
paragraph_last)
|
|
749
|
+
lines.each_with_index do |line, index|
|
|
750
|
+
TextEngine::Justifier.justify(
|
|
751
|
+
line: line, frame_width: wrap_width,
|
|
752
|
+
alignment: paragraph.alignment || :left,
|
|
753
|
+
last_line: paragraph_last && index == lines.length - 1
|
|
754
|
+
)
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
private_class_method :justify_lines
|
|
758
|
+
|
|
759
|
+
# Emits the run's positioned lines (clipped at the bottom
|
|
760
|
+
# limit), recording positions and annotating the first line
|
|
761
|
+
# with the run's ruby when present.
|
|
762
|
+
def self.emit_run_lines(canvas, positioned, run, context, size,
|
|
763
|
+
font, bottom_limit, char_cursor)
|
|
764
|
+
positioned.each_with_index do |line, index|
|
|
742
765
|
break if line.y < bottom_limit
|
|
743
766
|
|
|
744
767
|
emit_line(canvas, line, run, context, size)
|
|
745
768
|
record_position(context, line, size, char_cursor)
|
|
769
|
+
emit_ruby(canvas, run, line, size, font, context) if index.zero?
|
|
746
770
|
end
|
|
747
|
-
[positioned, next_y]
|
|
748
771
|
end
|
|
749
|
-
private_class_method :
|
|
772
|
+
private_class_method :emit_run_lines
|
|
750
773
|
|
|
751
774
|
# Emits one positioned line: applies character-level styling
|
|
752
775
|
# (fill color + tint, capitalization, position, tracking,
|
|
@@ -779,6 +802,36 @@ module Idml
|
|
|
779
802
|
end
|
|
780
803
|
private_class_method :emit_line
|
|
781
804
|
|
|
805
|
+
# Emits the run's ruby (phonetic annotation) above the first
|
|
806
|
+
# line of the annotated run, centered over the line's width.
|
|
807
|
+
# RubyFontSize defaults to half the base size; RubyPosition
|
|
808
|
+
# values containing "Below" place it under the base text.
|
|
809
|
+
# Approximation: IDML attaches ruby to a character range;
|
|
810
|
+
# we annotate the whole first line.
|
|
811
|
+
def self.emit_ruby(canvas, run, line, base_size, font, context)
|
|
812
|
+
ruby_text = run.ruby_string
|
|
813
|
+
return if ruby_text.nil? || ruby_text.empty?
|
|
814
|
+
|
|
815
|
+
ruby_size = run.ruby_font_size || (base_size * RUBY_SIZE_FACTOR)
|
|
816
|
+
glyphs = TextEngine::Shaper.shape(text: ruby_text, font: font,
|
|
817
|
+
size: ruby_size)
|
|
818
|
+
ruby_width = glyphs.sum(&:width)
|
|
819
|
+
x = line.x + ((line.width - ruby_width) / 2)
|
|
820
|
+
y = ruby_y(run, line, base_size, ruby_size)
|
|
821
|
+
canvas.text(ruby_text, at: [x, y],
|
|
822
|
+
font: font_for_run(run, context), size: ruby_size)
|
|
823
|
+
end
|
|
824
|
+
private_class_method :emit_ruby
|
|
825
|
+
|
|
826
|
+
def self.ruby_y(run, line, base_size, ruby_size)
|
|
827
|
+
if run.ruby_position.to_s.include?("Below")
|
|
828
|
+
line.y - (base_size * 0.25) - ruby_size
|
|
829
|
+
else
|
|
830
|
+
line.y + (base_size * 0.45) + ruby_size
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
private_class_method :ruby_y
|
|
834
|
+
|
|
782
835
|
def self.leading_for(paragraph, size)
|
|
783
836
|
TextEngine::VerticalLayout.leading_for(paragraph.auto_leading, size)
|
|
784
837
|
end
|
|
@@ -43,6 +43,11 @@ module Idml
|
|
|
43
43
|
:baseline_shift,
|
|
44
44
|
:footnote_number,
|
|
45
45
|
:footnote_paragraphs,
|
|
46
|
+
# Ruby (phonetic annotation) — from the CSR's Ruby*
|
|
47
|
+
# attributes. Present only on runs with a RubyString.
|
|
48
|
+
:ruby_string,
|
|
49
|
+
:ruby_font_size,
|
|
50
|
+
:ruby_position,
|
|
46
51
|
keyword_init: true,
|
|
47
52
|
)
|
|
48
53
|
|
|
@@ -348,6 +353,9 @@ module Idml
|
|
|
348
353
|
:horizontal_scale),
|
|
349
354
|
vertical_scale: resolve_csr(style_lookup, csr, :vertical_scale),
|
|
350
355
|
baseline_shift: resolve_csr(style_lookup, csr, :baseline_shift),
|
|
356
|
+
ruby_string: resolve_csr(style_lookup, csr, :ruby_string),
|
|
357
|
+
ruby_font_size: resolve_csr(style_lookup, csr, :ruby_font_size),
|
|
358
|
+
ruby_position: resolve_csr(style_lookup, csr, :ruby_position),
|
|
351
359
|
)
|
|
352
360
|
end
|
|
353
361
|
private_class_method :text_run
|
|
@@ -4,19 +4,22 @@ module Idml
|
|
|
4
4
|
module TextEngine
|
|
5
5
|
# Distributes slack space within a line per the paragraph's
|
|
6
6
|
# alignment. Adjusts x_offset and optionally scales inter-word
|
|
7
|
-
# spaces for justified text.
|
|
7
|
+
# spaces for justified text. `last_line:` (the paragraph's final
|
|
8
|
+
# line) keeps ragged right under :justified, as InDesign does.
|
|
8
9
|
class Justifier
|
|
9
|
-
def self.justify(line:, frame_width:, alignment: :left
|
|
10
|
-
|
|
10
|
+
def self.justify(line:, frame_width:, alignment: :left,
|
|
11
|
+
last_line: false)
|
|
12
|
+
new(frame_width, alignment, last_line).justify(line)
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
def initialize(frame_width, alignment)
|
|
15
|
+
def initialize(frame_width, alignment, last_line)
|
|
14
16
|
@frame_width = frame_width
|
|
15
17
|
@alignment = alignment
|
|
18
|
+
@last_line = last_line
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def justify(line)
|
|
19
|
-
case
|
|
22
|
+
case effective_alignment
|
|
20
23
|
when :left, :start then line.x_offset = 0
|
|
21
24
|
when :center, :middle
|
|
22
25
|
line.x_offset = (@frame_width - line.width) / 2
|
|
@@ -29,6 +32,14 @@ module Idml
|
|
|
29
32
|
|
|
30
33
|
private
|
|
31
34
|
|
|
35
|
+
# The paragraph's last line stays ragged under full
|
|
36
|
+
# justification.
|
|
37
|
+
def effective_alignment
|
|
38
|
+
return :left if @alignment == :justified && @last_line
|
|
39
|
+
|
|
40
|
+
@alignment
|
|
41
|
+
end
|
|
42
|
+
|
|
32
43
|
def justify_line(line)
|
|
33
44
|
spaces = line.glyphs.count(&:is_space)
|
|
34
45
|
slack = @frame_width - line.width
|
|
@@ -7,10 +7,20 @@ module Idml
|
|
|
7
7
|
|
|
8
8
|
# Greedy word-wrap line breaker. Accumulates glyphs until
|
|
9
9
|
# exceeding the frame width, then breaks at the last space.
|
|
10
|
+
# CJK runs get a kinsoku shori post-pass (no line starts or
|
|
11
|
+
# ends with a forbidden character).
|
|
10
12
|
class LineBreaker
|
|
11
13
|
def self.break(glyphs:, frame_width:)
|
|
12
|
-
new(frame_width).break(glyphs)
|
|
14
|
+
lines = new(frame_width).break(glyphs)
|
|
15
|
+
return lines unless cjk_run?(glyphs)
|
|
16
|
+
|
|
17
|
+
CjkLayout.apply_kinsoku(lines)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.cjk_run?(glyphs)
|
|
21
|
+
glyphs.any? { |glyph| CjkLayout.cjk?(glyph.codepoint) }
|
|
13
22
|
end
|
|
23
|
+
private_class_method :cjk_run?
|
|
14
24
|
|
|
15
25
|
def initialize(frame_width)
|
|
16
26
|
@frame_width = frame_width
|
data/lib/idml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: idml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bigdecimal
|
|
@@ -173,6 +173,8 @@ files:
|
|
|
173
173
|
- TODO.pdf/114-text-wrap-around-shapes.md
|
|
174
174
|
- TODO.pdf/115-bullet-numbered-lists.md
|
|
175
175
|
- TODO.pdf/116-applied-style-resolution.md
|
|
176
|
+
- TODO.pdf/116-justification-last-line.md
|
|
177
|
+
- TODO.pdf/117-endnote-support.md
|
|
176
178
|
- TODO.pdf/12-idml-to-pdf-pipeline.md
|
|
177
179
|
- TODO.pdf/13-cjk-text-layout.md
|
|
178
180
|
- TODO.pdf/14-page-item-element-models.md
|
|
@@ -291,6 +293,8 @@ files:
|
|
|
291
293
|
- lib/idml/elements/document_object.rb
|
|
292
294
|
- lib/idml/elements/dotted_stroke_style.rb
|
|
293
295
|
- lib/idml/elements/drop_shadow_setting.rb
|
|
296
|
+
- lib/idml/elements/endnote.rb
|
|
297
|
+
- lib/idml/elements/endnote_range.rb
|
|
294
298
|
- lib/idml/elements/feather_setting.rb
|
|
295
299
|
- lib/idml/elements/fill_transparency_setting.rb
|
|
296
300
|
- lib/idml/elements/flex_layout_attribute_option.rb
|