idml 0.10.11 → 0.10.13

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: ea3cefcb949b7fd6399d850354df7fe37e6f822e766994522b9df4ceaa13cfd7
4
- data.tar.gz: 531db1a6a0d5ccb32cd7f3fddd751b99d140a5504a3ab7645913167e34fdeb7e
3
+ metadata.gz: 2f2a7b73c56645a87269780fc0cab9f998f5a6bf0d167b0ce9433dba9a8019cd
4
+ data.tar.gz: e4bb1522273937a4c2cf5631de1927f4583f63d16f2b351f893921407d89dc88
5
5
  SHA512:
6
- metadata.gz: 6659beee3fa4e1d325bed4d0ed86a4a1db4c2944010d8a3deffbe3bf029df137187cb3b34fa064b5feecd000abad671fd712fb083caae837796339157bde9e25
7
- data.tar.gz: d225c68cea43b34da224e37809cbfa5c0be3abc8775c7f10baee3d1756c69c325a002562070a54a5994ecc7da3883f58bbd407b337b66d9994a99b3532436049
6
+ metadata.gz: 83756750d549a02ff7080686509ac45acce77405db6c9d3ba0ee6dae3643622b8eb89198d28fead77047dac50bdcf9491bdab05b7e03d02e18560f1021fa5360
7
+ data.tar.gz: 8a9fc59030029b4342019b205f9fe7da46ae6296729d641c49c199391cd5765ffea195dfca6cde46ff0df6518f496dcf9a1b66dc5dad0b808503230be8057107
@@ -0,0 +1,41 @@
1
+ # TODO PDF 150: Architecture review round three — seam repairs
2
+
3
+ ## Status: COMPLETE — implemented 2026-08-30
4
+
5
+ ## Problem
6
+
7
+ Round-three review findings (after TODOs 148-149):
8
+
9
+ 1. **Page-dimensions seam leak (bug)**: `ImageCollector` and
10
+ `HyperlinkEmitter` received a hardcoded `DEFAULT_HEIGHT = 792`
11
+ while pages rendered at per-page dimensions — both use the
12
+ height for the IDML→PDF y-flip, so images and link rectangles
13
+ misplaced on any non-letter page.
14
+ 2. **Dead interface kept alive by its tests**:
15
+ `TextWrapResolver#overlap_width` (+ `line_overlap`) had zero
16
+ lib callers since TODO 138 — only its own spec — and encoded
17
+ sum-semantics for multiple contours where the real interface
18
+ (`wrap_adjustment`) uses max. The spec suite asserted behavior
19
+ the renderer no longer had.
20
+ 3. **Thirteen copies of the synthetic-package triplet**: every
21
+ engine-path spec repeated `Dir.mktmpdir → Package.write →
22
+ Package.new`, with drifting names and inconsistent part sets.
23
+
24
+ ## Solution
25
+
26
+ - Pipeline threads per-page `dims[:height]` into both consumers;
27
+ the constants stay only as `page_dimensions_for` fallbacks.
28
+ - `overlap_width` / `line_overlap` deleted; all 15 spec call
29
+ sites migrated to `wrap_adjustment` (the "sums contours" spec
30
+ now documents max-combination honestly). The polygon variant
31
+ `WrapContour.overlap_width` stays — it is load-bearing.
32
+ - `build_package(parts, name)` helper in spec_helper; thirteen
33
+ triplet copies replaced; three local helpers that shadowed the
34
+ global renamed (`anchored_fixture`, `decorated_fixture`,
35
+ `footnote_fixture`, `endnote_fixture`, `footnote_package`).
36
+
37
+ ## Files
38
+
39
+ - `lib/idml/render/pipeline.rb`
40
+ - `lib/idml/render/text_wrap_resolver.rb`
41
+ - `spec/spec_helper.rb` + 13 spec files
@@ -0,0 +1,48 @@
1
+ # TODO PDF 151: Schema conformance for the Elements layer
2
+
3
+ ## Status: COMPLETE — implemented 2026-08-31
4
+
5
+ ## Problem
6
+
7
+ Architecture review round four: 147 element classes held hundreds
8
+ of wire attributes transcribed from the RNC, but only 11 classes
9
+ had attribute-set specs — the project's own drift defense
10
+ (CLAUDE.md convention 5) covered a tenth of the surface. Any
11
+ hand-edit that added, dropped, or misspelled a `map_attribute`
12
+ passed every test.
13
+
14
+ ## Solution
15
+
16
+ - **`Idml::Schema::Rnc`** (`schema/rnc.rb`): the RNC parsing
17
+ extracted from the generator script into a lib adapter
18
+ (`element_attribute_map`, `element_definition`). Two consumers
19
+ over one seam — `scripts/rnc_to_lutaml.rb` (now presentation
20
+ only) and the conformance spec.
21
+ - **Schema-conformance spec** (`elements_schema_conformance_spec.rb`):
22
+ for every Elements class — no wire attribute outside the RNC
23
+ universe (catches typos and inventions), and every mapped
24
+ destination is a declared attribute (mapping/declaration
25
+ consistency). Legacy non-schema classes (TableCell) carry an
26
+ explicit allow-list. Full equality is documented as
27
+ unenforceable (deliberate subsets; typedef-composed attributes).
28
+
29
+ **Bug found by the probe and fixed**: `TextFramePreference`
30
+ carried four invented attributes (`InsetTop/Left/Bottom/Right`)
31
+ that appear in no schema, no spec, and no real file — while the
32
+ schema-faithful inset carrier (`Properties > InsetSpacing`, spec
33
+ examples 31/32) was unmodeled, so real documents' text frame
34
+ insets were silently ignored. New `Elements::InsetSpacing`
35
+ (single unit → all sides; list → [top, right, bottom, left]),
36
+ wired into `layout_frame`; the invented attributes removed.
37
+
38
+ ## Files
39
+
40
+ - `lib/idml/schema.rb`, `lib/idml/schema/rnc.rb` (new)
41
+ - `scripts/rnc_to_lutaml.rb`
42
+ - `lib/idml/elements/inset_spacing.rb` (new)
43
+ - `lib/idml/elements/text_frame_preference.rb`,
44
+ `lib/idml/elements/properties.rb`, `lib/idml/elements.rb`
45
+ - `lib/idml/render/renderers/text_frame_renderer.rb`,
46
+ `lib/idml.rb`
47
+ - `spec/idml/elements_schema_conformance_spec.rb` (new)
48
+ - `spec/idml/elements/inset_spacing_spec.rb` (new)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idml
4
+ module Elements
5
+ # `<InsetSpacing>` inside TextFramePreference's Properties —
6
+ # the schema-faithful carrier of text frame insets (the
7
+ # spec's examples 31/32): a single `type="unit"` value applies
8
+ # to all sides; a `type="list"` of ListItems carries
9
+ # [top, right, bottom, left].
10
+ class InsetSpacing < Lutaml::Model::Serializable
11
+ attribute :type, :string
12
+ attribute :value, :string
13
+ attribute :list_item, TypedValue, collection: true
14
+
15
+ xml do
16
+ root "InsetSpacing"
17
+ map_attribute "type", to: :type
18
+ map_content to: :value
19
+ map_element "ListItem", to: :list_item
20
+ end
21
+
22
+ # [top, right, bottom, left] insets; nil components when the
23
+ # document doesn't declare them.
24
+ def sides
25
+ return Array.new(4, value&.to_f) if list_item.empty?
26
+
27
+ (0...4).map { |i| list_item[i]&.value&.to_f }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -18,6 +18,7 @@ module Idml
18
18
  attribute :paragraph_border_color, Idml::Elements::TypedValue
19
19
  attribute :text_wrap_offset, Idml::Elements::TypedValue
20
20
  attribute :paragraph_border_gap_color, Idml::Elements::TypedValue
21
+ attribute :inset_spacing, Idml::Elements::InsetSpacing
21
22
 
22
23
  xml do
23
24
  root "Properties"
@@ -29,6 +30,7 @@ module Idml
29
30
  map_element "ParagraphBorderColor", to: :paragraph_border_color
30
31
  map_element "TextWrapOffset", to: :text_wrap_offset
31
32
  map_element "ParagraphBorderGapColor", to: :paragraph_border_gap_color
33
+ map_element "InsetSpacing", to: :inset_spacing
32
34
  end
33
35
 
34
36
  def first_geometry
@@ -5,12 +5,10 @@ module Idml
5
5
  # Typed model of `<TextFramePreference>`. Every
6
6
  # attribute from `TextFramePreference_Object` in
7
7
  # `reference-docs/schemas/package/Resources/Styles.rnc` is declared (33 attributes),
8
- # generated by `scripts/rnc_to_lutaml.rb`.
8
+ # generated by `scripts/rnc_to_lutaml.rb`. Insets live in the
9
+ # Properties > InsetSpacing child (spec examples 31/32).
9
10
  class TextFramePreference < Lutaml::Model::Serializable
10
- attribute :inset_top, :float
11
- attribute :inset_left, :float
12
- attribute :inset_bottom, :float
13
- attribute :inset_right, :float
11
+ attribute :properties, Idml::Elements::Properties, collection: true
14
12
  attribute :footnotes_enable_overrides, :boolean
15
13
  attribute :footnotes_span_across_columns, :boolean
16
14
  attribute :footnotes_minimum_spacing, :float
@@ -47,10 +45,7 @@ module Idml
47
45
 
48
46
  xml do
49
47
  root "TextFramePreference"
50
- map_attribute "InsetTop", to: :inset_top
51
- map_attribute "InsetLeft", to: :inset_left
52
- map_attribute "InsetBottom", to: :inset_bottom
53
- map_attribute "InsetRight", to: :inset_right
48
+ map_element "Properties", to: :properties
54
49
  map_attribute "FootnotesEnableOverrides", to: :footnotes_enable_overrides
55
50
  map_attribute "FootnotesSpanAcrossColumns", to: :footnotes_span_across_columns
56
51
  map_attribute "FootnotesMinimumSpacing", to: :footnotes_minimum_spacing
data/lib/idml/elements.rb CHANGED
@@ -90,6 +90,7 @@ module Idml
90
90
  autoload :MasterSpreadObject, "idml/elements/master_spread_object"
91
91
  autoload :MixedInk, "idml/elements/mixed_ink"
92
92
  autoload :MixedInkGroup, "idml/elements/mixed_ink_group"
93
+ autoload :InsetSpacing, "idml/elements/inset_spacing"
93
94
  autoload :MojikumiTable, "idml/elements/mojikumi_table"
94
95
  autoload :MojikumiTableProperties, "idml/elements/mojikumi_table"
95
96
  autoload :OverrideMojikumiAki, "idml/elements/mojikumi_table"
@@ -96,9 +96,6 @@ module Idml
96
96
  position_tracker, condition_filter, style_lookup,
97
97
  page_offset)
98
98
  pages = spread.spread.flat_map(&:page)
99
- image_refs = ImageCollector.new(writer: writer,
100
- base_dir: File.dirname(@package.path),
101
- page_height: DEFAULT_HEIGHT).collect(spread)
102
99
  renderer = build_renderer(layer_filter, font_ref_resolver,
103
100
  font_resource, font_metrics, font_map,
104
101
  structure, position_tracker, condition_filter,
@@ -108,21 +105,26 @@ module Idml
108
105
  pages.each do |page|
109
106
  current += 1
110
107
  dims = page_dimensions_for(page)
108
+ image_refs = ImageCollector.new(
109
+ writer: writer,
110
+ base_dir: File.dirname(@package.path),
111
+ page_height: dims[:height],
112
+ ).collect(spread)
111
113
  canvas = writer.add_page(width: dims[:width], height: dims[:height])
112
114
  renderer.render(canvas, spread, page_width: dims[:width],
113
115
  page_height: dims[:height],
114
116
  image_refs: image_refs,
115
117
  page_index: current)
116
- emit_hyperlinks(writer, spread, current, layer_filter,
117
- position_tracker)
118
+ emit_hyperlinks(writer, spread, current, dims[:height],
119
+ layer_filter, position_tracker)
118
120
  end
119
121
  current
120
122
  end
121
123
 
122
- def emit_hyperlinks(writer, spread, page_index, layer_filter,
123
- position_tracker)
124
+ def emit_hyperlinks(writer, spread, page_index, page_height,
125
+ layer_filter, position_tracker)
124
126
  HyperlinkEmitter.new(writer: writer, package: @package,
125
- page_height: DEFAULT_HEIGHT,
127
+ page_height: page_height,
126
128
  layer_filter: layer_filter,
127
129
  position_tracker: position_tracker)
128
130
  .emit_for(spread, page_index)
@@ -244,20 +244,40 @@ module Idml
244
244
  # back to 0 when the preference is absent or doesn't declare
245
245
  # them.
246
246
  def self.layout_frame(frame_item, box)
247
- pref = frame_item.text_frame_preference&.first
247
+ top, right, bottom, left = frame_insets(
248
+ frame_item.text_frame_preference&.first,
249
+ )
248
250
  TextEngine::Frame.new(
249
251
  x: box[:x],
250
252
  y: box[:y],
251
253
  width: box[:width],
252
254
  height: box[:height],
253
- inset_top: pref&.inset_top,
254
- inset_bottom: pref&.inset_bottom,
255
- inset_left: pref&.inset_left,
256
- inset_right: pref&.inset_right,
255
+ inset_top: top,
256
+ inset_bottom: bottom,
257
+ inset_left: left,
258
+ inset_right: right,
257
259
  )
258
260
  end
259
261
  private_class_method :layout_frame
260
262
 
263
+ # Insets from Properties > InsetSpacing (spec examples
264
+ # 31/32): one unit value applies to all sides; a list
265
+ # carries [top, right, bottom, left]. Missing sides are 0.
266
+ def self.frame_insets(pref)
267
+ props = pref&.properties
268
+ spacing = props.first&.inset_spacing if props
269
+ return Array.new(4, 0.0) unless spacing
270
+
271
+ side_or_zero(spacing)
272
+ end
273
+
274
+ def self.side_or_zero(spacing)
275
+ [0, 1, 2, 3].map do |index|
276
+ spacing.sides[index] || 0.0
277
+ end
278
+ end
279
+ private_class_method :frame_insets
280
+
261
281
  # Walks the chain state's paragraphs/runs and emits one
262
282
  # `canvas.text` per line via the Shaper → LineBreaker →
263
283
  # Justifier → VerticalLayout pipeline. Cursor y descends
@@ -159,28 +159,6 @@ module Idml
159
159
  end
160
160
  private :interval_side_adjustment
161
161
 
162
- # Returns the total horizontal overlap of all contours with a
163
- # text line at the given y range within the given x range.
164
- # Sum, not max — multiple overlapping contours compound.
165
- # Returns 0.0 when there's no overlap. Inverse contours
166
- # reduce by the frame width minus their own width (text may
167
- # only flow inside).
168
- def overlap_width(line_y, line_height, frame_x, frame_right)
169
- @contours.sum do |contour|
170
- width = if contour.is_a?(WrapContour::Shape)
171
- WrapContour.overlap_width(contour, line_y,
172
- line_height, frame_x,
173
- frame_right)
174
- else
175
- line_overlap(contour, line_y, line_height,
176
- frame_x, frame_right)[:width]
177
- end
178
- next width unless contour.inverse
179
-
180
- (frame_right - frame_x) - width
181
- end
182
- end
183
-
184
162
  # The wrap shape for an item: a bounding-box rectangle
185
163
  # (BoundingBoxTextWrap — the legacy "BoundingBox" spelling is
186
164
  # also accepted from early synthetic fixtures) or a flattened
@@ -250,13 +228,6 @@ module Idml
250
228
  end
251
229
  private_class_method :contour_mode?
252
230
 
253
- def line_overlap(contour, line_y, line_height, frame_x, frame_right)
254
- return { width: 0.0 } unless y_overlap?(contour, line_y, line_height)
255
- return { width: 0.0 } unless x_overlap?(contour, frame_x, frame_right)
256
-
257
- { width: x_overlap_width(contour, frame_x, frame_right) }
258
- end
259
-
260
231
  def y_overlap?(contour, line_y, line_height)
261
232
  line_top = line_y + line_height
262
233
  line_bottom = line_y
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idml
4
+ module Schema
5
+ # Parses RelaxNG Compact element definitions into data:
6
+ # element name → wire attribute names. Handles the Adobe
7
+ # package schemas' shape (`NAME_Object = element Name { ... }`
8
+ # with balanced braces; attributes as `attribute X { type }`).
9
+ # Two consumers over this seam: the class generator script and
10
+ # the schema-conformance spec.
11
+ module Rnc
12
+ ELEMENT_DEF = /(\w+)\s*+=\s*+element\s*+([\w:]+)\s*+\{/
13
+ ATTRIBUTE_DEF = /attribute\s++(\w+)\s*+\{([^}]*)\}/
14
+
15
+ module_function
16
+
17
+ # { "Story" => [["Self", ...]], ... } for every element
18
+ # definition across the given RNC files. An element defined
19
+ # in several files keeps each definition as an alternative
20
+ # (array of attribute-name arrays) — a class conforms when
21
+ # its wire attributes equal ANY one alternative.
22
+ def element_attribute_map(paths)
23
+ definitions = Hash.new { |h, k| h[k] = [] }
24
+ each_element(paths) do |element_name, body|
25
+ definitions[element_name] << body_attributes(body)
26
+ end
27
+ definitions.to_h
28
+ end
29
+
30
+ # [xml_element_name, attrs_with_types] for one named
31
+ # definition (`Story_Object`) in one file; nil when absent.
32
+ # attrs_with_types: [["Self", "xsd:string"], ...].
33
+ def element_definition(path, definition_name)
34
+ src = File.read(path)
35
+ prefix = /#{Regexp.escape(definition_name)}\s*=\s*element\s+/
36
+ pattern = /#{prefix}([\w:]+)\s*\{/
37
+ match = src.match(pattern)
38
+ return nil unless match
39
+
40
+ body = balanced_body(src, match.end(0))
41
+ return nil unless body
42
+
43
+ attrs_with_types = body.scan(ATTRIBUTE_DEF)
44
+ .map { |name, type| [name, type.strip] }
45
+ [match[1], attrs_with_types]
46
+ end
47
+
48
+ def body_attributes(body)
49
+ body.scan(ATTRIBUTE_DEF).map(&:first).uniq
50
+ end
51
+
52
+ def each_element(paths, &block)
53
+ paths.each do |path|
54
+ scan_file(path, &block)
55
+ end
56
+ end
57
+
58
+ def scan_file(path)
59
+ src = File.read(path)
60
+ src.scan(ELEMENT_DEF) do
61
+ match = Regexp.last_match
62
+ body = balanced_body(src, match.end(0))
63
+ next unless body
64
+
65
+ element_name = match[2].sub(/^idPkg:/, "")
66
+ yield(element_name, body)
67
+ end
68
+ end
69
+
70
+ # The balanced-brace body starting just after `from` (which
71
+ # points past the opening brace); nil when unterminated.
72
+ def balanced_body(src, from)
73
+ depth = 1
74
+ i = from
75
+ while i < src.length && depth.positive?
76
+ case src[i]
77
+ when "{" then depth += 1
78
+ when "}" then depth -= 1
79
+ end
80
+ i += 1
81
+ end
82
+ return nil if depth.positive?
83
+
84
+ src[from...(i - 1)]
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idml
4
+ # Namespace for adapters over the committed RelaxNG Compact
5
+ # schemas (`reference-docs/schemas/`). The RNC files are the
6
+ # authoritative interface of the Elements layer — this namespace
7
+ # turns them into data for the generator script and the
8
+ # conformance spec (two consumers, one seam).
9
+ module Schema
10
+ autoload :Rnc, "#{__dir__}/schema/rnc"
11
+ end
12
+ end
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.10.11"
4
+ VERSION = "0.10.13"
5
5
  end
data/lib/idml.rb CHANGED
@@ -18,6 +18,7 @@ module Idml
18
18
  autoload :Validation, "#{__dir__}/idml/validation"
19
19
  autoload :Composition, "#{__dir__}/idml/composition"
20
20
  autoload :Geometry, "#{__dir__}/idml/geometry"
21
+ autoload :Schema, "#{__dir__}/idml/schema"
21
22
  autoload :TextEngine, "#{__dir__}/idml/text_engine"
22
23
  autoload :Render, "#{__dir__}/idml/render"
23
24
  autoload :CLI, "#{__dir__}/idml/cli"
@@ -10,21 +10,11 @@
10
10
  # Example:
11
11
  # scripts/rnc_to_lutaml.rb reference-docs/schemas/package/Stories/Story.rnc Story_Object
12
12
  #
13
- # The script reads the named element definition (e.g.,
14
- # `Story_Object = element Story { ... }`), extracts every `attribute
15
- # Name { Type }?` line, and prints Ruby code suitable for pasting
16
- # into a Lutaml::Model::Serializable subclass.
17
- #
18
- # Generated output:
19
- # attribute :self_attr, :string
20
- # attribute :first_line_indent, :float
21
- # # ... etc.
22
- # xml do
23
- # map_attribute "Self", to: :self_attr
24
- # map_attribute "FirstLineIndent", to: :first_line_indent
25
- # end
13
+ # RNC parsing lives in Idml::Schema::Rnc (shared with the
14
+ # schema-conformance spec); this script owns presentation only.
26
15
 
27
- require "strscan"
16
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
17
+ require "idml"
28
18
 
29
19
  RESERVED_RUBY_NAMES = %w[self class integer float string end do begin ensure
30
20
  rescue retry next return break yield nil true false].freeze
@@ -56,59 +46,25 @@ def ruby_attr_name(xml_name)
56
46
  base
57
47
  end
58
48
 
59
- def extract_element(rnc_path, element_name)
60
- src = File.read(rnc_path)
61
- pattern = /#{Regexp.escape(element_name)}\s*=\s*element\s+([\w:]+)\s*\{/
62
- match = src.match(pattern)
63
- abort "#{element_name} not found in #{rnc_path}" unless match
64
-
65
- xml_element_name = match[1]
66
- start_pos = match.end(0)
67
-
68
- # Walk the brace structure to find the matching close.
69
- depth = 1
70
- i = start_pos
71
- while i < src.length && depth.positive?
72
- case src[i]
73
- when "{" then depth += 1
74
- when "}" then depth -= 1
75
- end
76
- i += 1
77
- end
78
- body = src[start_pos...(i - 1)]
79
-
80
- [xml_element_name, body]
81
- end
82
-
83
- def extract_attributes(body)
84
- attrs = []
85
- body.scan(/^\s*attribute\s+(\w+)\s*\{\s*([^}]+)\}/) do |name, type|
86
- attrs << [name, type.strip]
87
- end
88
- attrs
89
- end
90
-
91
49
  rnc_path, element_name = ARGV
92
50
  abort "usage: #{$PROGRAM_NAME} <rnc-file> <ElementName>" unless rnc_path && element_name
93
51
 
94
- xml_root, body = extract_element(rnc_path, element_name)
95
- attrs = extract_attributes(body)
52
+ xml_root, attrs = Idml::Schema::Rnc.element_definition(rnc_path, element_name)
53
+ abort "#{element_name} not found in #{rnc_path}" unless xml_root
96
54
 
97
55
  puts "# xml root: <#{xml_root}>"
98
56
  puts "# source: #{rnc_path} (#{element_name})"
99
57
  puts "# #{attrs.length} attributes"
100
58
  puts
101
- puts "attribute :_placeholder_, :string # remove me"
102
- puts
103
- puts "xml do"
104
- puts " root \"#{xml_root.sub(/^idPkg:/, '')}\""
105
- attrs.each do |name, _type| # rubocop:disable Style/HashEachMethods
106
- puts %( map_attribute "#{name}", to: :#{ruby_attr_name(name)}")
107
- end
108
- puts "end"
109
- puts
110
59
  puts "# attribute declarations:"
111
60
  attrs.each do |name, type|
112
61
  lutaml = lutaml_type(type.split(/\s/).first)
113
62
  puts "attribute :#{ruby_attr_name(name)}, :#{lutaml}"
114
63
  end
64
+ puts
65
+ puts "xml do"
66
+ puts %( root "#{xml_root.sub(/^idPkg:/, '')}")
67
+ attrs.each do |name, _type| # rubocop:disable Style/HashEachMethods
68
+ puts %( map_attribute "#{name}", to: :#{ruby_attr_name(name)})
69
+ end
70
+ puts "end"
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.10.11
4
+ version: 0.10.13
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-29 00:00:00.000000000 Z
11
+ date: 2026-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -214,6 +214,8 @@ files:
214
214
  - TODO.pdf/148-architecture-improvements.md
215
215
  - TODO.pdf/149-architecture-round-two.md
216
216
  - TODO.pdf/15-wire-spread-children.md
217
+ - TODO.pdf/150-architecture-round-three.md
218
+ - TODO.pdf/151-schema-conformance.md
217
219
  - TODO.pdf/16-typed-model-pipeline.md
218
220
  - TODO.pdf/17-shape-rendering.md
219
221
  - TODO.pdf/18-text-rendering.md
@@ -351,6 +353,7 @@ files:
351
353
  - lib/idml/elements/ink.rb
352
354
  - lib/idml/elements/inner_glow_setting.rb
353
355
  - lib/idml/elements/inner_shadow_setting.rb
356
+ - lib/idml/elements/inset_spacing.rb
354
357
  - lib/idml/elements/layer.rb
355
358
  - lib/idml/elements/link.rb
356
359
  - lib/idml/elements/master_spread_object.rb
@@ -526,6 +529,8 @@ files:
526
529
  - lib/idml/render/style_resolver.rb
527
530
  - lib/idml/render/text_wrap_resolver.rb
528
531
  - lib/idml/render/wrap_contour.rb
532
+ - lib/idml/schema.rb
533
+ - lib/idml/schema/rnc.rb
529
534
  - lib/idml/text_engine.rb
530
535
  - lib/idml/text_engine/cjk_layout.rb
531
536
  - lib/idml/text_engine/justifier.rb