pdfrb 0.8.3 → 0.8.5

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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pdfrb/annotation/base.rb +2 -2
  3. data/lib/pdfrb/appearance/generator.rb +1 -1
  4. data/lib/pdfrb/cli/images.rb +1 -1
  5. data/lib/pdfrb/cli.rb +3 -3
  6. data/lib/pdfrb/color/default_profile.rb +1 -1
  7. data/lib/pdfrb/conformance/ltv.rb +1 -1
  8. data/lib/pdfrb/conformance/pades.rb +4 -4
  9. data/lib/pdfrb/conformance/pdf_2_af.rb +2 -2
  10. data/lib/pdfrb/conformance/pdf_a.rb +1 -1
  11. data/lib/pdfrb/conformance/pdf_x.rb +3 -3
  12. data/lib/pdfrb/conformance/tagged_pdf.rb +1 -1
  13. data/lib/pdfrb/content/canvas.rb +1 -1
  14. data/lib/pdfrb/content/hidden_text_detector.rb +1 -1
  15. data/lib/pdfrb/content/shading/base.rb +1 -1
  16. data/lib/pdfrb/content/tiling_pattern.rb +1 -1
  17. data/lib/pdfrb/digital_signature/signing.rb +2 -2
  18. data/lib/pdfrb/document/annotations.rb +10 -10
  19. data/lib/pdfrb/document/associated_files.rb +2 -2
  20. data/lib/pdfrb/document/colors.rb +1 -1
  21. data/lib/pdfrb/document/destinations.rb +4 -4
  22. data/lib/pdfrb/document/encryption.rb +1 -1
  23. data/lib/pdfrb/document/files.rb +5 -5
  24. data/lib/pdfrb/document/fonts/subsetting.rb +87 -0
  25. data/lib/pdfrb/document/fonts.rb +10 -80
  26. data/lib/pdfrb/document/form.rb +9 -9
  27. data/lib/pdfrb/document/form_xobject.rb +1 -1
  28. data/lib/pdfrb/document/graphics_state.rb +1 -1
  29. data/lib/pdfrb/document/images.rb +1 -6
  30. data/lib/pdfrb/document/info.rb +1 -1
  31. data/lib/pdfrb/document/layers.rb +1 -1
  32. data/lib/pdfrb/document/metadata.rb +1 -1
  33. data/lib/pdfrb/document/outline.rb +8 -8
  34. data/lib/pdfrb/document/output_intents.rb +3 -3
  35. data/lib/pdfrb/document/pages.rb +19 -8
  36. data/lib/pdfrb/document/pdf_a.rb +1 -1
  37. data/lib/pdfrb/document/portfolio.rb +3 -3
  38. data/lib/pdfrb/document/shadings.rb +2 -9
  39. data/lib/pdfrb/document/stamps.rb +2 -2
  40. data/lib/pdfrb/document/structure.rb +9 -9
  41. data/lib/pdfrb/document.rb +10 -3
  42. data/lib/pdfrb/encryption/security_handler.rb +1 -2
  43. data/lib/pdfrb/font/cff/subsetter.rb +0 -0
  44. data/lib/pdfrb/font/sfnt.rb +73 -0
  45. data/lib/pdfrb/font/true_type/subsetter.rb +1 -57
  46. data/lib/pdfrb/font/true_type/wrapper.rb +3 -3
  47. data/lib/pdfrb/font.rb +1 -0
  48. data/lib/pdfrb/font_loader/true_type.rb +2 -2
  49. data/lib/pdfrb/font_loader/type3.rb +1 -1
  50. data/lib/pdfrb/importer.rb +3 -3
  51. data/lib/pdfrb/linearization/writer.rb +1 -1
  52. data/lib/pdfrb/model/object.rb +7 -0
  53. data/lib/pdfrb/model/type/action_hide.rb +1 -1
  54. data/lib/pdfrb/model/type/action_sound.rb +2 -2
  55. data/lib/pdfrb/model/type/add_action_catalog.rb +1 -1
  56. data/lib/pdfrb/model/type/add_action_form_field.rb +1 -1
  57. data/lib/pdfrb/model/type/add_action_page_object.rb +1 -1
  58. data/lib/pdfrb/model/type/add_action_screen_annotation.rb +1 -1
  59. data/lib/pdfrb/model/type/add_action_widget_annotation.rb +1 -1
  60. data/lib/pdfrb/model/type/alternate_image.rb +2 -2
  61. data/lib/pdfrb/model/type/bead.rb +4 -4
  62. data/lib/pdfrb/model/type/catalog.rb +1 -1
  63. data/lib/pdfrb/model/type/color_space.rb +5 -31
  64. data/lib/pdfrb/model/type/crypt_filter.rb +6 -6
  65. data/lib/pdfrb/model/type/d_part.rb +4 -4
  66. data/lib/pdfrb/model/type/destination.rb +2 -18
  67. data/lib/pdfrb/model/type/document_security_store.rb +1 -1
  68. data/lib/pdfrb/model/type/dss.rb +1 -1
  69. data/lib/pdfrb/model/type/font_multiple_master.rb +3 -12
  70. data/lib/pdfrb/model/type/function_stitching.rb +1 -1
  71. data/lib/pdfrb/model/type/generic_appearance.rb +1 -1
  72. data/lib/pdfrb/model/type/misc_extras.rb +2 -7
  73. data/lib/pdfrb/model/type/name_map.rb +33 -0
  74. data/lib/pdfrb/model/type/opt_content_ext.rb +2 -2
  75. data/lib/pdfrb/model/type/optional_content_membership.rb +2 -2
  76. data/lib/pdfrb/model/type/page.rb +5 -7
  77. data/lib/pdfrb/model/type/page_tree_node.rb +2 -2
  78. data/lib/pdfrb/model/type/pattern.rb +2 -18
  79. data/lib/pdfrb/model/type/rich_media_configuration.rb +1 -1
  80. data/lib/pdfrb/model/type/shading.rb +2 -18
  81. data/lib/pdfrb/model/type/signature_extras.rb +6 -21
  82. data/lib/pdfrb/model/type/soft_mask.rb +1 -13
  83. data/lib/pdfrb/model/type/struct_elem.rb +2 -2
  84. data/lib/pdfrb/model/type/struct_tree_root.rb +2 -2
  85. data/lib/pdfrb/model/type/structure_attributes.rb +5 -18
  86. data/lib/pdfrb/model/type/thread.rb +3 -3
  87. data/lib/pdfrb/model/type/three_d_stream.rb +1 -1
  88. data/lib/pdfrb/model/type/viewport.rb +1 -1
  89. data/lib/pdfrb/model/type/vri.rb +1 -1
  90. data/lib/pdfrb/model/type/web_capture_command.rb +1 -1
  91. data/lib/pdfrb/model/type/web_capture_image_set.rb +1 -1
  92. data/lib/pdfrb/model/type/web_capture_page_set.rb +2 -2
  93. data/lib/pdfrb/model/type/xobject_form.rb +1 -13
  94. data/lib/pdfrb/model/type.rb +2 -0
  95. data/lib/pdfrb/task/extract_images.rb +1 -2
  96. data/lib/pdfrb/task/extract_text.rb +1 -1
  97. data/lib/pdfrb/task/merge.rb +3 -3
  98. data/lib/pdfrb/task/thumbnail.rb +1 -1
  99. data/lib/pdfrb/version.rb +1 -1
  100. data/lib/pdfrb/writer.rb +4 -5
  101. data/lib/pdfrb/xmp.rb +2 -2
  102. metadata +5 -2
@@ -5,6 +5,9 @@ require "stringio"
5
5
  module Pdfrb
6
6
  class Document
7
7
  class Fonts
8
+ autoload :Subsetting, "pdfrb/document/fonts/subsetting"
9
+ include Subsetting
10
+
8
11
  STANDARDS = %w[
9
12
  Helvetica Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique
10
13
  Times-Roman Times-Bold Times-Italic Times-BoldItalic
@@ -152,21 +155,6 @@ module Pdfrb
152
155
  desc&.value&.key?(:FontFile2)
153
156
  end
154
157
 
155
- def subset_fonts!
156
- @font_streams.each do |resource, data|
157
- next unless valid_font_data?(data)
158
-
159
- codepoints = @used_codepoints[resource]
160
- next if codepoints.empty?
161
-
162
- begin
163
- subset_font(resource, data, codepoints)
164
- rescue StandardError
165
- next
166
- end
167
- end
168
- end
169
-
170
158
  class << self
171
159
  def loaders; @loaders ||= []; end
172
160
  def register_loader(loader); loaders.unshift(loader); end
@@ -178,13 +166,13 @@ module Pdfrb
178
166
 
179
167
  widths = Array.new(256, DEFAULT_WIDTH)
180
168
  tu_stream = build_tounicode(doc)
181
- tu_ref = Pdfrb::Model::Reference.new(tu_stream.oid, tu_stream.gen)
169
+ tu_ref = tu_stream.ref
182
170
  fd = doc.add({
183
171
  Type: :FontDescriptor, FontName: name.to_sym, Flags: 32,
184
172
  FontBBox: [0, 0, 1000, 1000], ItalicAngle: 0,
185
173
  Ascent: 800, Descent: -200, CapHeight: 700, StemV: 80
186
174
  }, type: Pdfrb::Model::Cos::Dictionary)
187
- fd_ref = Pdfrb::Model::Reference.new(fd.oid, fd.gen)
175
+ fd_ref = fd.ref
188
176
  font_hash = {
189
177
  Type: :Font, Subtype: :Type1, BaseFont: name.to_sym,
190
178
  FirstChar: 0, LastChar: 255, Widths: widths,
@@ -399,18 +387,6 @@ module Pdfrb
399
387
  name.to_s.gsub(/[\s()\[\]<>{}\/%]+/, "-")
400
388
  end
401
389
 
402
- # Generate a deterministic 6-letter subset tag from the font bytes
403
- # so re-embedding the same font produces the same prefix. Format
404
- # is six uppercase ASCII letters derived from the SHA-1 of the
405
- # first 1 KB of the font file (per PDF spec s9.6.4).
406
- def subset_tag_for(font_bytes)
407
- require "digest"
408
- digest = Digest::SHA1.digest(font_bytes.byteslice(0, 1024) || "")
409
- digest.byteslice(0, 6).bytes.each_with_object(+"") do |b, s|
410
- s << ((b % 26) + 65).chr
411
- end
412
- end
413
-
414
390
  def true_type_subtype(data)
415
391
  magic = data&.byteslice(0, 4)
416
392
  # Per PDF spec s9.6.2 + s9.9: magic distinguishes the two
@@ -444,14 +420,14 @@ module Pdfrb
444
420
 
445
421
  def default_font(name)
446
422
  tu_stream = self.class.build_tounicode(document)
447
- tu_ref = Pdfrb::Model::Reference.new(tu_stream.oid, tu_stream.gen)
423
+ tu_ref = tu_stream.ref
448
424
 
449
425
  fd = document.add({
450
426
  Type: :FontDescriptor, FontName: name.to_sym, Flags: 32,
451
427
  FontBBox: [0, 0, 1000, 1000], ItalicAngle: 0,
452
428
  Ascent: 800, Descent: -200, CapHeight: 700, StemV: 80
453
429
  }, type: Pdfrb::Model::Cos::Dictionary)
454
- fd_ref = Pdfrb::Model::Reference.new(fd.oid, fd.gen)
430
+ fd_ref = fd.ref
455
431
 
456
432
  if @pending_io_data && valid_font_data?(@pending_io_data)
457
433
  font_file = document.add(
@@ -466,11 +442,11 @@ module Pdfrb
466
442
  font_file.value[:Subtype] = :OpenType
467
443
  font_file.value[:Length1] = @pending_io_data.bytesize
468
444
  fd.value[:FontFile3] =
469
- Pdfrb::Model::Reference.new(font_file.oid, font_file.gen)
445
+ font_file.ref
470
446
  else
471
447
  font_file.value[:Length1] = @pending_io_data.bytesize
472
448
  fd.value[:FontFile2] =
473
- Pdfrb::Model::Reference.new(font_file.oid, font_file.gen)
449
+ font_file.ref
474
450
  end
475
451
  end
476
452
 
@@ -483,58 +459,12 @@ module Pdfrb
483
459
  }, type: subtype == :TrueType ? Pdfrb::Model::Type::FontTrueType : Pdfrb::Model::Type::FontType1)
484
460
  end
485
461
 
486
- # Build the subsetted font bytes for +resource+ and replace the
487
- # descriptor's font-file stream (FontFile2 for TrueType
488
- # outlines, FontFile3/OpenType for CFF).
489
- def subset_font(resource, data, codepoints)
490
- subset, font_file_key =
491
- if data.byteslice(0, 4) == "OTTO".b
492
- # CFF outlines: map codepoints to glyph IDs via the OTF
493
- # cmap (it addresses CFF glyphs too), subset the 'CFF '
494
- # table, and rebuild the OTF container.
495
- cmap = Pdfrb::Font::TrueType::File.new(data).cmap
496
- gids = codepoints.filter_map { |cp| cmap.glyph_id_for(cp) }.uniq
497
- [Pdfrb::Font::CFF::Subsetter.subset_otf(data, gids), :FontFile3]
498
- else
499
- ttf = Pdfrb::Font::TrueType::File.new(data)
500
- subsetter = Pdfrb::Font::TrueType::Subsetter.new(ttf, codepoints.to_a)
501
- [subsetter.subset, :FontFile2]
502
- end
503
- dict = @font_dicts[resource]
504
- return unless dict
505
-
506
- desc_ref = dict.value[:FontDescriptor]
507
- return unless desc_ref
508
-
509
- desc = desc_ref.is_a?(Pdfrb::Model::Reference) ? document.object(desc_ref) : desc_ref
510
- return unless desc
511
-
512
- # Reuse the add-time stream object in place rather than
513
- # allocating a replacement — a second stream would leave the
514
- # full original orphaned in the file (doubling output size).
515
- fd_stream = desc.value[font_file_key]
516
- fd_stream = document.object(fd_stream) if fd_stream.is_a?(Pdfrb::Model::Reference)
517
- if fd_stream.is_a?(Pdfrb::Model::Cos::Stream)
518
- fd_stream.stream = subset
519
- fd_stream.value[:Length] = subset.bytesize
520
- fd_stream.value.delete(:Filter)
521
- if font_file_key == :FontFile3
522
- fd_stream.value[:Subtype] = :OpenType
523
- fd_stream.value[:Length1] = subset.bytesize
524
- end
525
- end
526
- end
527
-
528
462
  # Fonts attach to the page-tree ROOT's /Resources so every page
529
463
  # inherits them (s7.7.3.2 resource inheritance). Attaching to
530
464
  # the Catalog instead — as this used to — produced a stray key
531
465
  # no viewer honours, leaving written pages without fonts.
532
466
  def attach_to_resources(resource, font_dict)
533
- ref = Pdfrb::Model::Reference.new(font_dict.oid, font_dict.gen)
534
- root = document.pages.pages_root
535
- root.value[:Resources] ||= {}
536
- root.value[:Resources][:Font] ||= {}
537
- root.value[:Resources][:Font][resource] = ref
467
+ document.pages.attach_resource(:Font, resource, font_dict.ref)
538
468
  end
539
469
  end
540
470
  end
@@ -98,7 +98,7 @@ module Pdfrb
98
98
 
99
99
  fields = acroform[:Fields] || []
100
100
  fields.each do |ref|
101
- obj = ref.is_a?(Pdfrb::Model::Reference) ? document.object(ref) : ref
101
+ obj = document.resolve(ref)
102
102
  yield obj if obj
103
103
  end
104
104
  end
@@ -165,13 +165,13 @@ module Pdfrb
165
165
  fields = acroform[:Fields]
166
166
  return nil unless fields
167
167
 
168
- field_ref = Pdfrb::Model::Reference.new(field.oid, field.gen)
168
+ field_ref = field.ref
169
169
  fields.delete_if { |r| r == field_ref || (r.is_a?(Pdfrb::Model::Reference) && r.oid == field.oid) }
170
170
 
171
171
  # Remove widget annotation from page /Annots
172
172
  page_ref = field.value[:P]
173
173
  if page_ref
174
- page = page_ref.is_a?(Pdfrb::Model::Reference) ? document.object(page_ref) : page_ref
174
+ page = document.resolve(page_ref)
175
175
  annots = page&.value&.[](:Annots)
176
176
  annots&.delete_if { |r| r == field_ref || (r.is_a?(Pdfrb::Model::Reference) && r.oid == field.oid) }
177
177
  end
@@ -219,7 +219,7 @@ module Pdfrb
219
219
  page_ref = field.value[:P]
220
220
  return unless page_ref
221
221
 
222
- page = page_ref.is_a?(Pdfrb::Model::Reference) ? document.object(page_ref) : page_ref
222
+ page = document.resolve(page_ref)
223
223
  return unless page
224
224
 
225
225
  name = register_form_on_page(page, form_stream)
@@ -248,7 +248,7 @@ module Pdfrb
248
248
  end
249
249
  return nil unless target
250
250
 
251
- target.is_a?(Pdfrb::Model::Reference) ? document.object(target) : target
251
+ document.resolve(target)
252
252
  end
253
253
 
254
254
  def dictionary?(value)
@@ -278,7 +278,7 @@ module Pdfrb
278
278
  resources = page.value[:Resources] = Pdfrb::Model::Cos::Dictionary.new({}) unless resources.is_a?(Pdfrb::Model::Cos::Dictionary)
279
279
  xobjects = resources.value[:XObject] || {}
280
280
  name = unique_xobject_name(xobjects)
281
- xobjects[name] = Pdfrb::Model::Reference.new(form_stream.oid, form_stream.gen)
281
+ xobjects[name] = form_stream.ref
282
282
  resources.value[:XObject] = xobjects
283
283
  name
284
284
  end
@@ -306,7 +306,7 @@ module Pdfrb
306
306
  Subtype: :Widget,
307
307
  T: name,
308
308
  Rect: rect,
309
- P: Pdfrb::Model::Reference.new(page.oid, page.gen),
309
+ P: page.ref,
310
310
  FT: :Tx,
311
311
  },
312
312
  type: Pdfrb::Model::Type::Annotation
@@ -317,7 +317,7 @@ module Pdfrb
317
317
 
318
318
  def add_annot_to_page(page, widget)
319
319
  annots = page.value[:Annots]
320
- ref = Pdfrb::Model::Reference.new(widget.oid, widget.gen)
320
+ ref = widget.ref
321
321
  if annots.nil?
322
322
  page.value[:Annots] = [ref]
323
323
  elsif annots.is_a?(::Array)
@@ -330,7 +330,7 @@ module Pdfrb
330
330
  def register_field(field)
331
331
  acroform = document.catalog.value[:AcroForm]
332
332
  fields = acroform[:Fields]
333
- ref = Pdfrb::Model::Reference.new(field.oid, field.gen)
333
+ ref = field.ref
334
334
  fields << ref
335
335
  end
336
336
  end
@@ -50,7 +50,7 @@ module Pdfrb
50
50
  resources = page.value[:Resources]
51
51
  resources = {} unless resources.is_a?(::Hash)
52
52
  xobjects = resources[:XObject] || {}
53
- xobjects[@name] = Pdfrb::Model::Reference.new(@stream.oid, @stream.gen)
53
+ xobjects[@name] = @stream.ref
54
54
  resources[:XObject] = xobjects
55
55
  page.value[:Resources] = resources
56
56
  end
@@ -24,7 +24,7 @@ module Pdfrb
24
24
  def register(page, **params)
25
25
  gs = document.add({ Type: :ExtGState, **params },
26
26
  type: Pdfrb::Model::Type::GraphicsStateParameter)
27
- ref = Pdfrb::Model::Reference.new(gs.oid, gs.gen)
27
+ ref = gs.ref
28
28
 
29
29
  resources = ensure_resources(page)
30
30
  ext_gstate = resources.value[:ExtGState] || {}
@@ -46,12 +46,7 @@ module Pdfrb
46
46
  end
47
47
 
48
48
  def attach_to_resources(name, image)
49
- ref = Pdfrb::Model::Reference.new(image.oid, image.gen)
50
- # Page-tree root so every page inherits it (s7.7.3.2).
51
- root = document.pages.pages_root
52
- root.value[:Resources] ||= {}
53
- root.value[:Resources][:XObject] ||= {}
54
- root.value[:Resources][:XObject][name] = ref
49
+ document.pages.attach_resource(:XObject, name, image.ref)
55
50
  end
56
51
  end
57
52
  end
@@ -63,7 +63,7 @@ module Pdfrb
63
63
 
64
64
  info = document.add({}, type: Pdfrb::Model::Cos::Dictionary)
65
65
  if document.trailer
66
- document.trailer[:Info] = Pdfrb::Model::Reference.new(info.oid, info.gen)
66
+ document.trailer[:Info] = info.ref
67
67
  end
68
68
  info
69
69
  end
@@ -26,7 +26,7 @@ module Pdfrb
26
26
  { Type: :OCG, Name: name },
27
27
  type: Pdfrb::Model::Cos::Dictionary
28
28
  )
29
- ref = Pdfrb::Model::Reference.new(ocg.oid, ocg.gen)
29
+ ref = ocg.ref
30
30
  @groups << { ref: ref, ocg: ocg, default_on: default_on }
31
31
  ocg
32
32
  end
@@ -62,7 +62,7 @@ module Pdfrb
62
62
 
63
63
  if obj.nil? && create
64
64
  obj = document.add({}, type: Pdfrb::Model::Type::Info)
65
- document.trailer[:Info] = Pdfrb::Model::Reference.new(obj.oid, obj.gen)
65
+ document.trailer[:Info] = obj.ref
66
66
  end
67
67
  @cached_info = obj
68
68
  end
@@ -29,16 +29,16 @@ module Pdfrb
29
29
  { Type: :Outlines },
30
30
  type: Pdfrb::Model::Cos::Dictionary
31
31
  )
32
- root_ref = Pdfrb::Model::Reference.new(root.oid, root.gen)
32
+ root_ref = root.ref
33
33
 
34
34
  prev_dict = nil
35
35
  first_ref = nil
36
36
  @entries.each do |entry|
37
37
  dict = entry.build!(@document)
38
- ref = Pdfrb::Model::Reference.new(dict.oid, dict.gen)
38
+ ref = dict.ref
39
39
 
40
40
  dict.value[:Parent] = root_ref
41
- dict.value[:Prev] = Pdfrb::Model::Reference.new(prev_dict.oid, dict.gen) if prev_dict
41
+ dict.value[:Prev] = prev_dict.ref if prev_dict
42
42
  dict.value[:Next] = nil
43
43
  prev_dict&.value&.[]=(:Next, ref)
44
44
 
@@ -47,7 +47,7 @@ module Pdfrb
47
47
  end
48
48
 
49
49
  root.value[:First] = first_ref
50
- root.value[:Last] = Pdfrb::Model::Reference.new(prev_dict.oid, prev_dict.gen) if prev_dict
50
+ root.value[:Last] = prev_dict.ref if prev_dict
51
51
  root.value[:Count] = @entries.length
52
52
 
53
53
  @document.catalog.value[:Outlines] = root_ref
@@ -78,11 +78,11 @@ module Pdfrb
78
78
  first_ref = nil
79
79
  @children.each do |child|
80
80
  child_dict = child.build!(document)
81
- child_ref = Pdfrb::Model::Reference.new(child_dict.oid, child_dict.gen)
82
- parent_ref = Pdfrb::Model::Reference.new(dict.oid, dict.gen)
81
+ child_ref = child_dict.ref
82
+ parent_ref = dict.ref
83
83
 
84
84
  child_dict.value[:Parent] = parent_ref
85
- child_dict.value[:Prev] = Pdfrb::Model::Reference.new(prev_child_dict.oid, prev_child_dict.gen) if prev_child_dict
85
+ child_dict.value[:Prev] = prev_child_dict.ref if prev_child_dict
86
86
  child_dict.value[:Next] = nil
87
87
  prev_child_dict&.value&.[]=(:Next, child_ref)
88
88
 
@@ -91,7 +91,7 @@ module Pdfrb
91
91
  end
92
92
 
93
93
  dict.value[:First] = first_ref
94
- dict.value[:Last] = Pdfrb::Model::Reference.new(prev_child_dict.oid, prev_child_dict.gen) if prev_child_dict
94
+ dict.value[:Last] = prev_child_dict.ref if prev_child_dict
95
95
  dict.value[:Count] = @children.length
96
96
  end
97
97
 
@@ -42,7 +42,7 @@ module Pdfrb
42
42
  )
43
43
  catalog = document.catalog
44
44
  intents = catalog.value[:OutputIntents]
45
- ref = Pdfrb::Model::Reference.new(intent.oid, intent.gen)
45
+ ref = intent.ref
46
46
  if intents.nil?
47
47
  catalog.value[:OutputIntents] = [ref]
48
48
  elsif intents.is_a?(::Array)
@@ -63,7 +63,7 @@ module Pdfrb
63
63
  type: Pdfrb::Model::Cos::Stream
64
64
  )
65
65
  stream.stream = profile.raw_data
66
- ref = Pdfrb::Model::Reference.new(stream.oid, stream.gen)
66
+ ref = stream.ref
67
67
  add(ref, identifier: identifier, condition: condition, registry: registry, subtype: subtype)
68
68
  end
69
69
 
@@ -74,7 +74,7 @@ module Pdfrb
74
74
  return self unless intents
75
75
 
76
76
  intents.each do |ref|
77
- obj = ref.is_a?(Pdfrb::Model::Reference) ? document.object(ref) : ref
77
+ obj = document.resolve(ref)
78
78
  yield obj if obj
79
79
  end
80
80
  self
@@ -31,9 +31,9 @@ module Pdfrb
31
31
  # one lazily when it attaches XObjects.
32
32
  page_hash = {
33
33
  Type: :Page,
34
- Parent: Pdfrb::Model::Reference.new(root.oid, root.gen),
34
+ Parent: root.ref,
35
35
  MediaBox: media_box,
36
- Contents: Pdfrb::Model::Reference.new(contents.oid, 0)
36
+ Contents: contents.ref
37
37
  }
38
38
  page_hash[:BleedBox] = bleed_box if bleed_box
39
39
  page_hash[:TrimBox] = trim_box if trim_box
@@ -42,7 +42,7 @@ module Pdfrb
42
42
  page = document.add(page_hash, type: Pdfrb::Model::Type::Page)
43
43
  page.value[:Rotate] = rotate if rotate.nonzero?
44
44
  kids = (root.value[:Kids] ||= [])
45
- kids << Pdfrb::Model::Reference.new(page.oid, page.gen)
45
+ kids << page.ref
46
46
  root.value[:Count] = (root.value[:Count] || 0) + 1
47
47
  page
48
48
  end
@@ -57,7 +57,8 @@ module Pdfrb
57
57
 
58
58
  def count
59
59
  raw = pages_root[:Count]
60
- resolved = raw.is_a?(Pdfrb::Model::Reference) ? document.object(raw)&.value : raw
60
+ resolved = document.resolve(raw)
61
+ resolved = resolved.value if resolved.is_a?(Pdfrb::Model::Object)
61
62
  case resolved
62
63
  when Integer then resolved
63
64
  when Numeric then resolved.to_i
@@ -136,6 +137,17 @@ module Pdfrb
136
137
  end
137
138
  end
138
139
 
140
+ # Attach +ref+ under +category+ (e.g. :Font, :XObject,
141
+ # :Shading) in the page-tree root's /Resources, where every
142
+ # page inherits it (s7.7.3.2). The single seam for resource
143
+ # placement — placement policy changes land here.
144
+ def attach_resource(category, name, ref)
145
+ root = pages_root
146
+ root.value[:Resources] ||= {}
147
+ root.value[:Resources][category] ||= {}
148
+ root.value[:Resources][category][name] = ref
149
+ end
150
+
139
151
  # The page-tree root (Catalog /Pages), seeding an empty tree if
140
152
  # needed. Resources placed here are inherited by every page
141
153
  # (s7.7.3.2).
@@ -149,7 +161,7 @@ module Pdfrb
149
161
  # No /Pages yet — seed an empty tree.
150
162
  root = document.add({ Type: :Pages, Kids: [], Count: 0 },
151
163
  type: Pdfrb::Model::Type::PageTreeNode)
152
- catalog.value[:Pages] = Pdfrb::Model::Reference.new(root.oid, root.gen)
164
+ catalog.value[:Pages] = root.ref
153
165
  root
154
166
  end
155
167
 
@@ -163,8 +175,7 @@ module Pdfrb
163
175
  kids = kids.value if kids.is_a?(Pdfrb::Model::Cos::Dictionary) && !kids.is_a?(::Hash)
164
176
 
165
177
  Array(kids).each do |kid_ref|
166
- kid = kid_ref.is_a?(Pdfrb::Model::Reference) ?
167
- document.object(kid_ref) : kid_ref
178
+ kid = document.resolve(kid_ref)
168
179
  next unless kid
169
180
 
170
181
  type = kid.is_a?(Pdfrb::Model::Cos::Dictionary) ? kid.value[:Type] : nil
@@ -181,7 +192,7 @@ module Pdfrb
181
192
  def ref_to(page)
182
193
  return page if page.is_a?(Pdfrb::Model::Reference)
183
194
 
184
- Pdfrb::Model::Reference.new(page.oid, page.gen)
195
+ page.ref
185
196
  end
186
197
  end
187
198
  end
@@ -48,7 +48,7 @@ module Pdfrb
48
48
  type: Pdfrb::Model::Cos::Stream)
49
49
  icc.stream = bytes
50
50
  output_intents.add(
51
- Pdfrb::Model::Reference.new(icc.oid, icc.gen),
51
+ icc.ref,
52
52
  identifier: SRGB_IDENTIFIER,
53
53
  condition: "sRGB IEC61966-2.1",
54
54
  registry: SRGB_REGISTRY,
@@ -100,9 +100,9 @@ module Pdfrb
100
100
  },
101
101
  type: Pdfrb::Model::Cos::Dictionary
102
102
  )
103
- filespec.value[:EF] = Pdfrb::Model::Reference.new(ef.oid, ef.gen)
103
+ filespec.value[:EF] = ef.ref
104
104
  filespec.value[:AFRelationship] = :Unspecified
105
- Pdfrb::Model::Reference.new(filespec.oid, filespec.gen)
105
+ filespec.ref
106
106
  end
107
107
  end
108
108
 
@@ -112,7 +112,7 @@ module Pdfrb
112
112
  type: Pdfrb::Model::Cos::Stream
113
113
  )
114
114
  embedded.stream = item.file_bytes
115
- Pdfrb::Model::Reference.new(embedded.oid, embedded.gen)
115
+ embedded.ref
116
116
  end
117
117
  end
118
118
  end
@@ -109,7 +109,7 @@ module Pdfrb
109
109
  {
110
110
  FunctionType: 3,
111
111
  Domain: Pdfrb::Model::PdfArray.new([0, 1]),
112
- Functions: functions.map { |f| Pdfrb::Model::Reference.new(f.oid, f.gen) },
112
+ Functions: functions.map(&:ref),
113
113
  Bounds: Pdfrb::Model::PdfArray.new(bounds),
114
114
  Encode: Pdfrb::Model::PdfArray.new(stops.each_cons(2).map { [0, 1] }.flatten),
115
115
  },
@@ -142,14 +142,7 @@ module Pdfrb
142
142
 
143
143
  def register(name, shading_obj)
144
144
  @registry[name] = shading_obj
145
- document.catalog
146
- # Attach to the page-tree root so every page inherits it
147
- # (s7.7.3.2); the Catalog has no /Resources key in PDF.
148
- root = document.pages.pages_root
149
- root.value[:Resources] ||= {}
150
- root.value[:Resources][:Shading] ||= {}
151
- root.value[:Resources][:Shading][name] =
152
- Pdfrb::Model::Reference.new(shading_obj.oid, shading_obj.gen)
145
+ document.pages.attach_resource(:Shading, name, shading_obj.ref)
153
146
  end
154
147
 
155
148
  def next_name
@@ -19,7 +19,7 @@ module Pdfrb
19
19
  # @return [Symbol] the resource name (e.g., :St1).
20
20
  def stamp_everywhere(form_xobject, at: [0, 0])
21
21
  name = next_stamp_name
22
- ref = Pdfrb::Model::Reference.new(form_xobject.oid, form_xobject.gen)
22
+ ref = form_xobject.ref
23
23
 
24
24
  document.pages.each do |page|
25
25
  register_xobject_in_resources(page, name, ref)
@@ -31,7 +31,7 @@ module Pdfrb
31
31
  # Stamp a Form XObject on a single page.
32
32
  def stamp_page(page, form_xobject, at: [0, 0])
33
33
  name = next_stamp_name
34
- ref = Pdfrb::Model::Reference.new(form_xobject.oid, form_xobject.gen)
34
+ ref = form_xobject.ref
35
35
  register_xobject_in_resources(page, name, ref)
36
36
  emit_do_operator(page, name, at)
37
37
  name
@@ -35,13 +35,13 @@ module Pdfrb
35
35
  )
36
36
  catalog = document.catalog
37
37
  catalog.value[:StructTreeRoot] =
38
- Pdfrb::Model::Reference.new(root_elem.oid, root_elem.gen)
38
+ root_elem.ref
39
39
  mark_info = catalog.value[:MarkInfo]
40
40
  if mark_info.nil?
41
41
  mark_info = document.add({ Marked: true },
42
42
  type: Pdfrb::Model::Cos::Dictionary)
43
43
  catalog.value[:MarkInfo] =
44
- Pdfrb::Model::Reference.new(mark_info.oid, mark_info.gen)
44
+ mark_info.ref
45
45
  end
46
46
  @root = root_elem
47
47
  end
@@ -59,7 +59,7 @@ module Pdfrb
59
59
  page_ref = if page.is_a?(Pdfrb::Model::Reference)
60
60
  page
61
61
  else
62
- Pdfrb::Model::Reference.new(page.oid, page.gen)
62
+ page.ref
63
63
  end
64
64
  elem.value[:Pg] = page_ref
65
65
  end
@@ -166,7 +166,7 @@ module Pdfrb
166
166
  nums << page_idx
167
167
  nums << entries.length
168
168
  entries.each do |_mcid, elem| # rubocop:disable Style/HashEachMethods
169
- nums << Pdfrb::Model::Reference.new(elem.oid, elem.gen)
169
+ nums << elem.ref
170
170
  end
171
171
  end
172
172
 
@@ -189,7 +189,7 @@ module Pdfrb
189
189
  return 0 unless page_ref
190
190
 
191
191
  document.pages.each_with_index do |page, idx|
192
- page_obj = page.is_a?(Pdfrb::Model::Reference) ? document.object(page) : page
192
+ page_obj = document.resolve(page)
193
193
  return idx if page_obj == page_ref || page == page_ref
194
194
  end
195
195
  0
@@ -215,16 +215,16 @@ module Pdfrb
215
215
  def append_child(parent, child)
216
216
  children = parent.value[:K]
217
217
  if children.nil?
218
- parent.value[:K] = [Pdfrb::Model::Reference.new(child.oid, child.gen)]
218
+ parent.value[:K] = [child.ref]
219
219
  elsif children.is_a?(::Array)
220
- children << Pdfrb::Model::Reference.new(child.oid, child.gen)
220
+ children << child.ref
221
221
  else
222
222
  parent.value[:K] = [
223
223
  children,
224
- Pdfrb::Model::Reference.new(child.oid, child.gen),
224
+ child.ref,
225
225
  ]
226
226
  end
227
- child.value[:P] = Pdfrb::Model::Reference.new(parent.oid, parent.gen)
227
+ child.value[:P] = parent.ref
228
228
  end
229
229
 
230
230
  public
@@ -88,6 +88,13 @@ module Pdfrb
88
88
  # Resolve a Reference to its Object. New/modified objects take
89
89
  # precedence over xref-loaded ones; otherwise consult the
90
90
  # ObjectReader (which caches per oid).
91
+ # Resolve a value to the object it denotes: References are
92
+ # dereferenced against this document; anything else is returned
93
+ # unchanged. The single seam for "give me the thing itself".
94
+ def resolve(value)
95
+ value.is_a?(Pdfrb::Model::Reference) ? object(value) : value
96
+ end
97
+
91
98
  def object(reference)
92
99
  return reference unless reference.is_a?(Pdfrb::Model::Reference)
93
100
 
@@ -329,7 +336,7 @@ module Pdfrb
329
336
 
330
337
  max_oid = @xref.entries.keys.max || 0
331
338
  size = @trailer_dict && @trailer_dict[:Size]
332
- size = size.to_i if size.respond_to?(:to_i)
339
+ size = size.to_i if size.is_a?(Numeric)
333
340
  @next_oid = [max_oid + 1, size.to_i, @next_oid].max
334
341
  end
335
342
 
@@ -430,9 +437,9 @@ module Pdfrb
430
437
 
431
438
  def seed_empty_structure
432
439
  pages = add({ Type: :Pages, Kids: [], Count: 0 })
433
- catalog = add({ Type: :Catalog, Pages: Pdfrb::Model::Reference.new(pages.oid, 0) })
440
+ catalog = add({ Type: :Catalog, Pages: pages.ref })
434
441
  @catalog = catalog
435
- @empty_trailer = { Size: @next_oid, Root: Pdfrb::Model::Reference.new(catalog.oid, 0) }
442
+ @empty_trailer = { Size: @next_oid, Root: catalog.ref }
436
443
  end
437
444
 
438
445
  def recover_or_raise(error)
@@ -21,8 +21,7 @@ module Pdfrb
21
21
  ref = trailer[:Encrypt]
22
22
  return nil unless ref
23
23
 
24
- encrypt = ref.is_a?(Pdfrb::Model::Reference) ?
25
- document.object(ref) : ref
24
+ encrypt = document.resolve(ref)
26
25
  return nil unless encrypt
27
26
 
28
27
  filter = encrypt[:Filter] || :Standard
Binary file