idml 0.7.3 → 0.7.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66e6127aeed8e54926e01cacb67c81445565c1e43a558015fd9740e022c12372
4
- data.tar.gz: 20b6dcdef5ee316ca394a2b062ed0dac2db9137e08e28f7649944623420e3938
3
+ metadata.gz: 2f60e795f4325dcc4006a28915a18baf7069e21767694ca670c41f84db62d98d
4
+ data.tar.gz: 4f1f4846d41050b518c8add338ce431951b45ea58891b2c56154b47e59478bc7
5
5
  SHA512:
6
- metadata.gz: 94180fd8ac2231198178f40f2051d4e1bd9c56d917cd271808fc87f7de66746739c4ecad65e23f6bbb57be539f061ff5ef9abfda56e71a2bb23ddc403ac3d9a5
7
- data.tar.gz: 353fcc512f92ed689fc1e41cabf4fe9310bcb5ce0464be24b0be6a452c1559f860201c7132318bb3fd4be9c510eed63ebb98f056b927b9e781bffcfc2123f632
6
+ metadata.gz: bbf7caac497ce2ab01a8c35af7bad78a24b7a706db6baa582b7d416cae1ce4995f18a3903379c4e994ab49ca1f28efd33e162821dead95250ae8248c041d16cd
7
+ data.tar.gz: ed5d2ae0abf40c0c6bf101d3c05ca35f59bc48aefed14366de7f5ac0f15979ff458ca7cf8493d012c4f6a78b0c1889815f987ea3dc9f24971f31ae34583f6fc4
@@ -1,6 +1,9 @@
1
1
  # TODO PDF 115: Bullet and numbered list rendering
2
2
 
3
- ## Status: OPEN gap identified in 2026-08-10 audit
3
+ ## Status: DONE for inline marker emission. Hanging-indent layout
4
+ (bullet in a left gutter with wrapped lines indented to align with
5
+ text after marker) is a follow-up — currently the marker sits inline
6
+ before the first run's text.
4
7
 
5
8
  ## Problem
6
9
 
@@ -0,0 +1,63 @@
1
+ # frozen_string: true
2
+
3
+ module Idml
4
+ module Render
5
+ # Produces a list-marker string for a paragraph (bullet glyph or
6
+ # numbered-list expression). IDML stores the resolved marker
7
+ # directly on the PSR (BulletCharacterValue as a Unicode codepoint,
8
+ # NumberingExpression as the rendered text), so the renderer
9
+ # doesn't need to maintain its own counter.
10
+ #
11
+ # Used by `StyleResolver` to prepend the marker to the paragraph's
12
+ # first run's text. The marker becomes part of the run's natural
13
+ # text flow — no special positioning needed for MVP.
14
+ #
15
+ # Hanging-indent layout (bullet aligned in a left gutter with
16
+ # wrapped lines indented to align with text after the marker) is
17
+ # a follow-up; for now the marker sits inline before the text.
18
+ module ListMarker
19
+ DEFAULT_BULLET = "•".freeze # BULLET
20
+ DEFAULT_TEXT_AFTER = "\t".freeze
21
+
22
+ # Returns the marker string for the paragraph, or nil when the
23
+ # paragraph isn't part of a list.
24
+ def self.marker_for(paragraph)
25
+ return nil unless paragraph
26
+
27
+ case paragraph.bullets_and_numbering_list_type
28
+ when "BulletList" then bullet_marker(paragraph)
29
+ when "NumberedList" then numbered_marker(paragraph)
30
+ end
31
+ end
32
+
33
+ def self.bullet_marker(paragraph)
34
+ bullet = bullet_char(paragraph)
35
+ suffix = text_after(paragraph)
36
+ "#{bullet}#{suffix}"
37
+ end
38
+ private_class_method :bullet_marker
39
+
40
+ def self.numbered_marker(paragraph)
41
+ expr = paragraph.numbering_expression
42
+ return nil if expr.nil? || expr.empty?
43
+
44
+ suffix = text_after(paragraph)
45
+ "#{expr}#{suffix}"
46
+ end
47
+ private_class_method :numbered_marker
48
+
49
+ def self.bullet_char(paragraph)
50
+ codepoint = paragraph.bullet_character_value
51
+ return DEFAULT_BULLET unless codepoint&.positive?
52
+
53
+ [codepoint].pack("U")
54
+ end
55
+ private_class_method :bullet_char
56
+
57
+ def self.text_after(paragraph)
58
+ paragraph.bullets_text_after || DEFAULT_TEXT_AFTER
59
+ end
60
+ private_class_method :text_after
61
+ end
62
+ end
63
+ end
@@ -55,6 +55,10 @@ module Idml
55
55
  :right_indent,
56
56
  :auto_leading,
57
57
  :drop_cap_lines,
58
+ :bullets_and_numbering_list_type,
59
+ :bullet_character_value,
60
+ :bullets_text_after,
61
+ :numbering_expression,
58
62
  :drop_cap_characters,
59
63
  # RuleAbove / RuleBelow — paragraph rules (horizontal lines
60
64
  # above/below the paragraph block). Driven by PSR attributes.
@@ -115,7 +119,7 @@ module Idml
115
119
  runs = csr_runs(psr, condition_filter: condition_filter)
116
120
  next if runs.empty?
117
121
 
118
- Paragraph.new(
122
+ paragraph = Paragraph.new(
119
123
  runs: runs,
120
124
  alignment: runs.first.alignment,
121
125
  space_before: psr.space_before,
@@ -126,6 +130,10 @@ module Idml
126
130
  auto_leading: psr.auto_leading,
127
131
  drop_cap_lines: psr.drop_cap_lines,
128
132
  drop_cap_characters: psr.drop_cap_characters,
133
+ bullets_and_numbering_list_type: psr.bullets_and_numbering_list_type,
134
+ bullet_character_value: psr.bullet_character_value,
135
+ bullets_text_after: psr.bullets_text_after,
136
+ numbering_expression: psr.numbering_expression,
129
137
  rule_above: psr.rule_above,
130
138
  rule_above_line_weight: psr.rule_above_line_weight,
131
139
  rule_above_color: psr.stroke_color,
@@ -143,6 +151,8 @@ module Idml
143
151
  rule_below_right_indent: psr.rule_below_right_indent,
144
152
  rule_below_width: psr.rule_below_width,
145
153
  )
154
+ prepend_list_marker(paragraph)
155
+ paragraph
146
156
  end
147
157
  end
148
158
 
@@ -193,6 +203,19 @@ module Idml
193
203
  end
194
204
  private_class_method :alignment_for
195
205
 
206
+ # Prepends the list marker (bullet glyph or numbered expression)
207
+ # to the paragraph's first run when the paragraph is part of
208
+ # a list. Mutates the first run's text in place.
209
+ def self.prepend_list_marker(paragraph)
210
+ marker = ListMarker.marker_for(paragraph)
211
+ return unless marker
212
+ return if paragraph.runs.empty?
213
+
214
+ first_run = paragraph.runs.first
215
+ first_run.text = "#{marker}#{first_run.text}"
216
+ end
217
+ private_class_method :prepend_list_marker
218
+
196
219
  # Concatenate runs into a single block. Used when the renderer
197
220
  # can't handle per-run styling (e.g., no font metrics available).
198
221
  def self.concatenate(runs)
data/lib/idml/render.rb CHANGED
@@ -16,6 +16,7 @@ module Idml
16
16
  autoload :CharacterStyle, "#{__dir__}/render/character_style"
17
17
  autoload :ParagraphRules, "#{__dir__}/render/paragraph_rules"
18
18
  autoload :DropCap, "#{__dir__}/render/drop_cap"
19
+ autoload :ListMarker, "#{__dir__}/render/list_marker"
19
20
  autoload :StoryChainController, "#{__dir__}/render/story_chain_controller"
20
21
  autoload :LayerFilter, "#{__dir__}/render/layer_filter"
21
22
  autoload :ConditionFilter, "#{__dir__}/render/condition_filter"
data/lib/idml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Idml
4
- VERSION = "0.7.3"
4
+ VERSION = "0.7.4"
5
5
  end
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.7.3
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
@@ -444,6 +444,7 @@ files:
444
444
  - lib/idml/render/image.rb
445
445
  - lib/idml/render/image_collector.rb
446
446
  - lib/idml/render/layer_filter.rb
447
+ - lib/idml/render/list_marker.rb
447
448
  - lib/idml/render/metadata_builder.rb
448
449
  - lib/idml/render/page_item_renderer.rb
449
450
  - lib/idml/render/paragraph_rules.rb