idml 0.2.9 → 0.3.1

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.
@@ -13,7 +13,7 @@ module Idml
13
13
  compliance: nil, tagged: false, subset_fonts: true)
14
14
  @package = package
15
15
  @output_path = output_path
16
- @font_resolver = build_font_resolver(font_search_paths)
16
+ @font_search_paths = font_search_paths
17
17
  @compliance = compliance
18
18
  @tagged = tagged
19
19
  @subset_fonts = subset_fonts
@@ -21,18 +21,26 @@ module Idml
21
21
 
22
22
  def call
23
23
  writer = PdfrbWriter.new
24
- writer.set_info(combined_metadata)
24
+ metadata = combined_metadata
25
+ writer.set_info(metadata)
25
26
  writer.enable_tagged if @tagged
27
+ PdfaPacket.attach(writer.document, metadata) if pdfa_requested?
28
+ structure = StructureTracker.new(enabled: @tagged)
26
29
  layer_filter = LayerFilter.from_designmap(@package.designmap)
27
30
  font_ref_resolver = FontReferenceResolver.build(@package)
28
31
  base_dir = File.dirname(@package.path)
29
- font_name = register_font(writer)
32
+ font_resource = register_font(writer)
33
+ font_metrics = build_font_metrics(writer, font_resource)
34
+ page_index = -1
30
35
 
31
36
  @package.spreads.each do |spread|
32
- render_spread_pages(writer, spread, base_dir, layer_filter,
33
- font_ref_resolver, font_name)
37
+ page_index = render_spread_pages(writer, spread, base_dir,
38
+ layer_filter, font_ref_resolver,
39
+ font_resource, font_metrics,
40
+ structure, page_index)
34
41
  end
35
42
 
43
+ structure.flush(writer)
36
44
  writer.build_structure if @tagged
37
45
  writer.subset_fonts! if @subset_fonts
38
46
  writer.write(@output_path)
@@ -41,126 +49,58 @@ module Idml
41
49
 
42
50
  private
43
51
 
52
+ def pdfa_requested?
53
+ @compliance&.to_s&.start_with?("pdfa")
54
+ end
55
+
44
56
  def render_spread_pages(writer, spread, base_dir, layer_filter,
45
- font_ref_resolver, font_name)
57
+ font_ref_resolver, font_resource, font_metrics,
58
+ structure, page_offset)
46
59
  pages = spread.spread.flat_map(&:page)
47
- image_refs = collect_images(writer, spread, base_dir, layer_filter)
48
- renderer = build_renderer(layer_filter, font_ref_resolver, font_name)
60
+ image_refs = ImageCollector.new(writer: writer, base_dir: base_dir,
61
+ page_height: DEFAULT_HEIGHT).collect(spread)
62
+ renderer = build_renderer(layer_filter, font_ref_resolver,
63
+ font_resource, font_metrics, structure: structure)
64
+ current = page_offset
49
65
 
50
66
  pages.each do |page|
67
+ current += 1
51
68
  dims = page_dimensions_for(page)
52
69
  canvas = writer.add_page(width: dims[:width], height: dims[:height])
53
70
  renderer.render(canvas, spread, page_width: dims[:width],
54
71
  page_height: dims[:height],
55
- image_refs: image_refs)
72
+ image_refs: image_refs,
73
+ page_index: current)
56
74
  end
75
+ current
57
76
  end
58
77
 
59
- def build_renderer(layer_filter, font_ref_resolver, font_name)
78
+ def build_renderer(layer_filter, font_ref_resolver, font_resource,
79
+ font_metrics, structure:)
60
80
  SpreadRenderer.new(
61
- font_resolver: @font_resolver,
62
- font_ps_name: font_name,
81
+ font_metrics: font_metrics,
82
+ font_ps_name: font_resource,
63
83
  package: @package,
64
84
  layer_filter: layer_filter,
65
85
  font_ref_resolver: font_ref_resolver,
86
+ structure: structure,
66
87
  )
67
88
  end
68
89
 
69
- def collect_images(writer, spread, base_dir, layer_filter)
70
- refs = []
71
- spread.each_page_item do |item|
72
- next unless layer_filter.visible?(item)
73
-
74
- images_for_item(item).each do |img|
75
- ref = register_image(writer, img, item, base_dir)
76
- refs << ref if ref
77
- end
78
- end
79
- refs
80
- end
81
-
82
- def images_for_item(item)
83
- case item
84
- when Idml::Elements::Rectangle, Idml::Elements::Polygon
85
- item.image
86
- else
87
- []
88
- end
89
- end
90
-
91
- def register_image(writer, image, parent, base_dir)
92
- uri = image.resource_uri
93
- return nil unless uri
94
-
95
- existing = writer.image_name_for(uri)
96
- return reuse_image(writer, existing, image, parent) if existing
97
-
98
- load_new_image(writer, image, parent, base_dir, uri)
99
- end
100
-
101
- def reuse_image(_writer, name, image, parent)
102
- { name: name, placement: compute_placement(image, parent),
103
- clip_box: parent_clip_box(parent) }
104
- end
105
-
106
- def load_new_image(writer, image, parent, base_dir, uri)
107
- path = Image.resolve_path(uri, base_dir: base_dir)
108
- return nil unless File.exist?(path)
109
-
110
- data = File.binread(path)
111
- dims = image_dimensions(data)
112
- return nil unless dims
113
-
114
- name = writer.add_image(data: data)
115
- writer.register_image_name(uri, name)
116
- { name: name, placement: compute_placement(image, parent, dims[1]),
117
- clip_box: parent_clip_box(parent) }
118
- end
119
-
120
- def parent_clip_box(parent)
121
- return nil unless parent.geometric_bounds
122
-
123
- Geometry.placement_rect(parent.geometric_bounds,
124
- parent.item_transform, DEFAULT_HEIGHT)
125
- end
126
-
127
- def image_dimensions(data)
128
- format = Image.detect_format(data)
129
- return nil unless format
130
-
131
- if format == :png
132
- Image.png_dimensions(data)
133
- else
134
- Image.jpeg_dimensions(data)
135
- end
136
- end
137
-
138
- def compute_placement(image, parent, pixel_height = 100)
139
- Image.compute_placement(
140
- image_transform: parse_transform_safe(image.item_transform),
141
- parent_transform: parse_transform_safe(parent.item_transform),
142
- pixel_height: pixel_height,
143
- page_height: DEFAULT_HEIGHT,
144
- )
145
- end
146
-
147
- def parse_transform_safe(str)
148
- Image.parse_transform(str) || Image.identity
149
- end
150
-
151
90
  def page_dimensions_for(page)
152
91
  { width: page.width || DEFAULT_WIDTH,
153
92
  height: page.height || DEFAULT_HEIGHT }
154
93
  end
155
94
 
156
- def build_font_resolver(paths)
157
- search = paths || TextEngine::FontResolver::DEFAULT_SEARCH_PATHS
158
- TextEngine::FontResolver.new(search_paths: search)
95
+ def build_font_metrics(writer, font_resource)
96
+ return nil if font_resource == Render::DEFAULT_FONT
97
+
98
+ TextEngine::PdfrbFontMetrics.new(writer.document.fonts, font_resource)
99
+ rescue StandardError
100
+ nil
159
101
  end
160
102
 
161
103
  def register_font(writer)
162
- return Render::DEFAULT_FONT unless @font_resolver
163
-
164
104
  path = resolve_document_font_path
165
105
  return Render::DEFAULT_FONT unless path
166
106
 
@@ -169,6 +109,12 @@ module Idml
169
109
  Render::DEFAULT_FONT
170
110
  end
171
111
 
112
+ def font_resolver
113
+ @font_resolver ||= Pdfrb::FontResolver.new(
114
+ search_paths: @font_search_paths || Pdfrb::FontResolver::DEFAULT_SEARCH_PATHS,
115
+ )
116
+ end
117
+
172
118
  def resolve_document_font_path
173
119
  return nil unless @package&.fonts
174
120
 
@@ -184,8 +130,8 @@ module Idml
184
130
  next unless font.post_script_name
185
131
  next if font.status == "Missing"
186
132
 
187
- metrics = @font_resolver.resolve_by_ps_name(font.post_script_name)
188
- return metrics.path if metrics
133
+ path = font_resolver.find_by_ps_name(font.post_script_name)
134
+ return path if path
189
135
  end
190
136
  nil
191
137
  end
@@ -9,13 +9,15 @@ module Idml
9
9
  RenderContext = Struct.new(
10
10
  :item,
11
11
  :package,
12
- :font_resolver,
12
+ :font_metrics,
13
13
  :font_ref_resolver,
14
14
  :color_resolver,
15
15
  :font_ps_name,
16
16
  :page_width,
17
17
  :page_height,
18
18
  :layer_filter,
19
+ :structure,
20
+ :page_index,
19
21
  keyword_init: true,
20
22
  )
21
23
  end
@@ -14,13 +14,15 @@ module Idml
14
14
  child_context = Render::RenderContext.new(
15
15
  item: child,
16
16
  package: context.package,
17
- font_resolver: context.font_resolver,
17
+ font_metrics: context.font_metrics,
18
18
  font_ref_resolver: context.font_ref_resolver,
19
19
  color_resolver: context.color_resolver,
20
20
  font_ps_name: context.font_ps_name,
21
21
  page_width: context.page_width,
22
22
  page_height: context.page_height,
23
23
  layer_filter: context.layer_filter,
24
+ structure: context.structure,
25
+ page_index: context.page_index,
24
26
  )
25
27
  PageItemRenderer.render(canvas, child_context)
26
28
  end
@@ -4,10 +4,10 @@ module Idml
4
4
  module Render
5
5
  module Renderers
6
6
  # Renders an IDML TextFrame on a Pdfrb::Content::Canvas. Extracts
7
- # styled runs via StyleResolver. When a FontMetrics is available,
8
- # runs the full text engine pipeline (Shaper → LineBreaker →
9
- # VerticalLayout) for proper word-wrap; otherwise falls back to
10
- # simple one-text-per-run positioning.
7
+ # styled runs via StyleResolver. When a FontMetrics is available
8
+ # (pdfrb-backed), runs the full text engine pipeline (Shaper →
9
+ # LineBreaker) for proper word-wrap; otherwise falls back to a
10
+ # single text_rich call per frame.
11
11
  class TextFrameRenderer
12
12
  DEFAULT_SIZE = 12.0
13
13
  LEADING_FACTOR = 1.2
@@ -28,7 +28,7 @@ module Idml
28
28
  end
29
29
 
30
30
  def self.render_text(canvas, runs, context, box)
31
- font = resolve_font_metrics(context)
31
+ font = context.font_metrics
32
32
  if font
33
33
  engine_render(canvas, runs, context, box, font)
34
34
  else
@@ -47,26 +47,6 @@ module Idml
47
47
  end
48
48
  private_class_method :frame_box
49
49
 
50
- def self.resolve_font_metrics(context)
51
- return nil unless context.font_resolver
52
-
53
- context.font_resolver.resolve(
54
- family_name: Render::DEFAULT_FONT, style_name: "Regular",
55
- )
56
- end
57
- private_class_method :resolve_font_metrics
58
-
59
- def self.simple_render(canvas, runs, context, box)
60
- runs.each_with_index do |run, index|
61
- y = box[:y] + box[:height] -
62
- ((index + 1) * run.point_size * LEADING_FACTOR)
63
- canvas.text(run.text, at: [box[:x], y],
64
- font: context.font_ps_name,
65
- size: run.point_size)
66
- end
67
- end
68
- private_class_method :simple_render
69
-
70
50
  def self.engine_render(canvas, runs, context, box, font)
71
51
  baseline_y = box[:y] + box[:height] - runs.first.point_size
72
52
 
@@ -78,6 +58,9 @@ module Idml
78
58
  end
79
59
  private_class_method :engine_render
80
60
 
61
+ # Emits one `canvas.text_lines` call per run, after shaping
62
+ # and line-breaking with real font metrics. Lines that fall
63
+ # below the frame's bottom edge are clipped.
81
64
  def self.render_run_lines(canvas, run, context, box, font, baseline_y)
82
65
  size = run.point_size
83
66
  glyphs = TextEngine::Shaper.shape(
@@ -105,6 +88,32 @@ module Idml
105
88
  baseline_y
106
89
  end
107
90
  private_class_method :render_run_lines
91
+
92
+ # Fallback when no metrics are available: emit all runs as
93
+ # one `text_rich` block, letting pdfrb measure advance widths.
94
+ # Uses pdfrb's measurement API directly (no Fontisan).
95
+ def self.simple_render(canvas, runs, context, box)
96
+ runs_for = build_rich_runs(runs, context)
97
+ return if runs_for.empty?
98
+
99
+ first_size = runs.first.point_size
100
+ canvas.text_rich(
101
+ runs_for,
102
+ at: [box[:x], box[:y] + box[:height] - first_size],
103
+ )
104
+ end
105
+ private_class_method :simple_render
106
+
107
+ def self.build_rich_runs(runs, context)
108
+ runs.map do |run|
109
+ {
110
+ text: run.text,
111
+ font: context.font_ps_name,
112
+ size: run.point_size,
113
+ }
114
+ end
115
+ end
116
+ private_class_method :build_rich_runs
108
117
  end
109
118
  end
110
119
  end
@@ -4,26 +4,30 @@ module Idml
4
4
  module Render
5
5
  # Renders a typed `Parts::Spread` onto a Pdfrb::Content::Canvas.
6
6
  class SpreadRenderer
7
- def initialize(font_resolver: nil, font_ps_name: Render::DEFAULT_FONT,
7
+ def initialize(font_metrics: nil, font_ps_name: Render::DEFAULT_FONT,
8
8
  package: nil, layer_filter: LayerFilter::EXCLUDE_NONE,
9
- font_ref_resolver: nil)
10
- @font_resolver = font_resolver
9
+ font_ref_resolver: nil, structure: nil)
10
+ @font_metrics = font_metrics
11
11
  @font_ps_name = font_ps_name
12
12
  @package = package
13
13
  @layer_filter = layer_filter
14
14
  @font_ref_resolver = font_ref_resolver
15
+ @structure = structure
15
16
  end
16
17
 
17
- def render(canvas, spread, page_width:, page_height:, image_refs: [])
18
+ def render(canvas, spread, page_width:, page_height:, image_refs: [],
19
+ page_index: 0)
18
20
  context_base = {
19
21
  package: @package,
20
- font_resolver: @font_resolver,
22
+ font_metrics: @font_metrics,
21
23
  font_ref_resolver: @font_ref_resolver,
22
24
  color_resolver: build_color_resolver,
23
25
  font_ps_name: @font_ps_name,
24
26
  page_width: page_width,
25
27
  page_height: page_height,
26
28
  layer_filter: @layer_filter,
29
+ structure: @structure,
30
+ page_index: page_index,
27
31
  }
28
32
 
29
33
  canvas.save_graphics_state do
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idml
4
+ module Render
5
+ # Maps IDML page-item classes to PDF structure types (PDF 1.7
6
+ # §14.8.4). Each visible page item becomes one structure element
7
+ # when tagged output is enabled.
8
+ #
9
+ # Mapping rationale:
10
+ # TextFrame → P (paragraph)
11
+ # Group → Sect (section, contains children)
12
+ # Table → Table
13
+ # Rectangle/Polygon with image → Figure
14
+ # Rectangle/Polygon without image → Path (decorative shape)
15
+ # GraphicLine → Path
16
+ #
17
+ # The PDF spec reserves lowercase for standard types; pdfrb's
18
+ # `add_element(type:)` accepts a Symbol which is serialised as
19
+ # `/Type`. We use Title-case Symbols to match the spec's typical
20
+ # spelling (P, Sect, Table, Figure, Path).
21
+ module StructureMapper
22
+ def self.type_for(item)
23
+ case item
24
+ when Idml::Elements::TextFrame then :P
25
+ when Idml::Elements::Group then :Sect
26
+ when Idml::Elements::Table then :Table
27
+ when Idml::Elements::GraphicLine then :Path
28
+ when Idml::Elements::Rectangle, Idml::Elements::Polygon
29
+ shape_type(item)
30
+ end
31
+ end
32
+
33
+ def self.shape_type(item)
34
+ image?(item) ? :Figure : :Path
35
+ end
36
+ private_class_method :shape_type
37
+
38
+ # Optional `Alt` text for Figure elements. IDML doesn't have a
39
+ # dedicated alt-text attribute on `<Image>`, but the parent
40
+ # page item's `Name` (if present) is a useful fallback.
41
+ def self.alt_for(item)
42
+ case item
43
+ when Idml::Elements::Rectangle, Idml::Elements::Polygon
44
+ image?(item) ? item.name : nil
45
+ end
46
+ end
47
+
48
+ def self.image?(item)
49
+ Array(item.image).any?
50
+ end
51
+ private_class_method :image?
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idml
4
+ module Render
5
+ # Allocates MCID integers per page and buffers structure-element
6
+ # registrations until `Pipeline` is ready to flush them. Created
7
+ # once per pipeline run; `enabled?` is false when `tagged:` was
8
+ # not requested, in which case every method is a no-op.
9
+ class StructureTracker
10
+ def initialize(enabled: false)
11
+ @enabled = enabled
12
+ @entries = []
13
+ @page_mcids = Hash.new(0)
14
+ end
15
+
16
+ def enabled?
17
+ @enabled
18
+ end
19
+
20
+ # Returns the next MCID for the given page index. MCIDs are
21
+ # per-page in PDF (reset to 0 on each new page).
22
+ def next_mcid(page_index)
23
+ current = @page_mcids[page_index]
24
+ @page_mcids[page_index] = current + 1
25
+ current
26
+ end
27
+
28
+ def add(type, page_index:, mcid:, text: nil, alt: nil)
29
+ return unless @enabled
30
+
31
+ @entries << Entry.new(type, page_index, mcid, text, alt)
32
+ end
33
+
34
+ # Push buffered entries into the writer in registration order.
35
+ def flush(writer)
36
+ return unless @enabled
37
+
38
+ @entries.each do |entry|
39
+ writer.add_structure_element(entry.type, page_index: entry.page_index,
40
+ mcid: entry.mcid,
41
+ text: entry.text,
42
+ alt: entry.alt)
43
+ end
44
+ end
45
+
46
+ Entry = Struct.new(:type, :page_index, :mcid, :text, :alt, keyword_init: true) do
47
+ def initialize(type, page_index, mcid, text, alt)
48
+ super(type: type, page_index: page_index, mcid: mcid,
49
+ text: text, alt: alt)
50
+ end
51
+ end
52
+ private_constant :Entry
53
+ end
54
+ end
55
+ end
data/lib/idml/render.rb CHANGED
@@ -15,6 +15,10 @@ module Idml
15
15
  autoload :StrokeStyle, "#{__dir__}/render/stroke_style"
16
16
  autoload :LayerFilter, "#{__dir__}/render/layer_filter"
17
17
  autoload :Placement, "#{__dir__}/render/placement"
18
+ autoload :ImageCollector, "#{__dir__}/render/image_collector"
19
+ autoload :StructureTracker, "#{__dir__}/render/structure_tracker"
20
+ autoload :StructureMapper, "#{__dir__}/render/structure_mapper"
21
+ autoload :PdfaPacket, "#{__dir__}/render/pdfa_packet"
18
22
  autoload :StoryThreader, "#{__dir__}/render/story_threader"
19
23
  autoload :PdfrbWriter, "#{__dir__}/render/pdfrb_writer"
20
24
  autoload :SpreadRenderer, "#{__dir__}/render/spread_renderer"
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idml
4
+ module TextEngine
5
+ # Adapter that exposes the same measurement surface as
6
+ # `FontMetrics` (the Fontisan-based parser) but delegates to
7
+ # pdfrb's `Fonts` collection + a registered font resource Symbol.
8
+ #
9
+ # Once a font is registered with the pdfrb document via
10
+ # `document.fonts.add(path)`, pdfrb parses the TTF tables (cmap,
11
+ # hmtx, head, hhea) and exposes real per-glyph advance widths
12
+ # through `Fonts#glyph_width(resource, codepoint)` and
13
+ # `Fonts#metrics_for(resource)`. This adapter wraps that pair so
14
+ # the text engine (Shaper, LineBreaker) can use real metrics
15
+ # without any Fontisan dependency.
16
+ class PdfrbFontMetrics
17
+ attr_reader :resource, :fonts
18
+
19
+ def initialize(fonts_collection, resource)
20
+ @fonts = fonts_collection
21
+ @resource = resource
22
+ end
23
+
24
+ def units_per_em
25
+ metrics_data[:units_per_em] || 1000
26
+ end
27
+
28
+ def ascent
29
+ metrics_data[:ascent] || 800
30
+ end
31
+
32
+ def descent
33
+ metrics_data[:descent] || -200
34
+ end
35
+
36
+ def line_gap
37
+ 0
38
+ end
39
+
40
+ # Returns raw advance width in font units (not scaled by size).
41
+ def glyph_width(codepoint)
42
+ cp = codepoint.is_a?(String) ? codepoint.each_codepoint.first : codepoint
43
+ @fonts.glyph_width(@resource, cp).to_i
44
+ end
45
+
46
+ def measure_text(text, size:)
47
+ return 0.0 if text.nil? || text.empty?
48
+
49
+ text.each_codepoint.sum do |cp|
50
+ glyph_width(cp) * size.to_f / units_per_em
51
+ end
52
+ end
53
+
54
+ def kerning_pair(_left_cp, _right_cp)
55
+ 0
56
+ end
57
+
58
+ def postscript_name
59
+ @resource.to_s
60
+ end
61
+
62
+ def family_name
63
+ postscript_name
64
+ end
65
+
66
+ def style_name
67
+ "Regular"
68
+ end
69
+
70
+ def path
71
+ nil
72
+ end
73
+
74
+ private
75
+
76
+ def metrics_data
77
+ @fonts.metrics_for(@resource) || {}
78
+ end
79
+ end
80
+ end
81
+ end
@@ -5,8 +5,7 @@ module Idml
5
5
  # geometry and produces positioned glyphs using font metrics from
6
6
  # .ttf/.otf files. IDML-agnostic — works on any styled text.
7
7
  module TextEngine
8
- autoload :FontMetrics, "#{__dir__}/text_engine/font_metrics"
9
- autoload :FontResolver, "#{__dir__}/text_engine/font_resolver"
8
+ autoload :PdfrbFontMetrics, "#{__dir__}/text_engine/pdfrb_font_metrics"
10
9
  autoload :Shaper, "#{__dir__}/text_engine/shaper"
11
10
  autoload :ShapedGlyph, "#{__dir__}/text_engine/shaper"
12
11
  autoload :LineBreaker, "#{__dir__}/text_engine/line_breaker"
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.2.9"
4
+ VERSION = "0.3.1"
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.2.9
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
- - !ruby/object:Gem::Dependency
27
- name: fontisan
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: '0'
40
26
  - !ruby/object:Gem::Dependency
41
27
  name: lutaml-model
42
28
  requirement: !ruby/object:Gem::Requirement
@@ -218,6 +204,8 @@ files:
218
204
  - TODO.pdf/75-pdf-metadata-from-xmp.md
219
205
  - TODO.pdf/76-tagged-pdf-structure.md
220
206
  - TODO.pdf/77-pdfa-xmp-output-intent.md
207
+ - TODO.pdf/78-hyperlinks.md
208
+ - TODO.pdf/79-bookmarks-outline.md
221
209
  - TODO.pdf/README.md
222
210
  - exe/idml
223
211
  - idml.gemspec
@@ -389,8 +377,10 @@ files:
389
377
  - lib/idml/render/font_reference_resolver.rb
390
378
  - lib/idml/render/gradient_resolver.rb
391
379
  - lib/idml/render/image.rb
380
+ - lib/idml/render/image_collector.rb
392
381
  - lib/idml/render/layer_filter.rb
393
382
  - lib/idml/render/page_item_renderer.rb
383
+ - lib/idml/render/pdfa_packet.rb
394
384
  - lib/idml/render/pdfrb_writer.rb
395
385
  - lib/idml/render/pipeline.rb
396
386
  - lib/idml/render/placement.rb
@@ -405,13 +395,14 @@ files:
405
395
  - lib/idml/render/spread_renderer.rb
406
396
  - lib/idml/render/story_threader.rb
407
397
  - lib/idml/render/stroke_style.rb
398
+ - lib/idml/render/structure_mapper.rb
399
+ - lib/idml/render/structure_tracker.rb
408
400
  - lib/idml/render/style_resolver.rb
409
401
  - lib/idml/text_engine.rb
410
402
  - lib/idml/text_engine/cjk_layout.rb
411
- - lib/idml/text_engine/font_metrics.rb
412
- - lib/idml/text_engine/font_resolver.rb
413
403
  - lib/idml/text_engine/justifier.rb
414
404
  - lib/idml/text_engine/line_breaker.rb
405
+ - lib/idml/text_engine/pdfrb_font_metrics.rb
415
406
  - lib/idml/text_engine/shaper.rb
416
407
  - lib/idml/text_engine/vertical_layout.rb
417
408
  - lib/idml/validation.rb