hexapdf 0.1.0 → 0.2.0

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +3 -3
  5. data/VERSION +1 -1
  6. data/examples/arc.rb +1 -1
  7. data/examples/graphics.rb +1 -1
  8. data/examples/hello_world.rb +1 -1
  9. data/examples/merging.rb +1 -1
  10. data/examples/show_char_bboxes.rb +1 -1
  11. data/examples/standard_pdf_fonts.rb +1 -1
  12. data/examples/truetype.rb +1 -1
  13. data/lib/hexapdf/cli.rb +14 -7
  14. data/lib/hexapdf/cli/extract.rb +1 -1
  15. data/lib/hexapdf/cli/info.rb +2 -2
  16. data/lib/hexapdf/cli/inspect.rb +4 -4
  17. data/lib/hexapdf/cli/modify.rb +151 -51
  18. data/lib/hexapdf/configuration.rb +1 -1
  19. data/lib/hexapdf/content/canvas.rb +1 -1
  20. data/lib/hexapdf/content/processor.rb +1 -1
  21. data/lib/hexapdf/dictionary.rb +6 -19
  22. data/lib/hexapdf/dictionary_fields.rb +1 -1
  23. data/lib/hexapdf/document.rb +23 -16
  24. data/lib/hexapdf/document/files.rb +130 -0
  25. data/lib/hexapdf/{font_utils.rb → document/fonts.rb} +40 -38
  26. data/lib/hexapdf/document/images.rb +117 -0
  27. data/lib/hexapdf/document/pages.rb +125 -0
  28. data/lib/hexapdf/encryption/aes.rb +1 -1
  29. data/lib/hexapdf/encryption/ruby_aes.rb +10 -10
  30. data/lib/hexapdf/encryption/standard_security_handler.rb +11 -8
  31. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  32. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -6
  33. data/lib/hexapdf/font/cmap/writer.rb +5 -7
  34. data/lib/hexapdf/font/true_type.rb +4 -1
  35. data/lib/hexapdf/font/true_type/font.rb +8 -16
  36. data/lib/hexapdf/font/true_type/table.rb +5 -16
  37. data/lib/hexapdf/font/true_type/table/cmap.rb +2 -7
  38. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +2 -6
  39. data/lib/hexapdf/font/true_type/table/directory.rb +0 -5
  40. data/lib/hexapdf/font/true_type/table/glyf.rb +3 -11
  41. data/lib/hexapdf/font/true_type/table/head.rb +0 -12
  42. data/lib/hexapdf/font/true_type/table/hhea.rb +0 -7
  43. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -5
  44. data/lib/hexapdf/font/true_type/table/loca.rb +0 -4
  45. data/lib/hexapdf/font/true_type/table/maxp.rb +0 -8
  46. data/lib/hexapdf/font/true_type/table/name.rb +3 -17
  47. data/lib/hexapdf/font/true_type/table/os2.rb +0 -14
  48. data/lib/hexapdf/font/true_type/table/post.rb +0 -8
  49. data/lib/hexapdf/font/true_type_wrapper.rb +1 -1
  50. data/lib/hexapdf/font/type1.rb +2 -2
  51. data/lib/hexapdf/font/type1/font.rb +2 -1
  52. data/lib/hexapdf/font/type1/font_metrics.rb +10 -1
  53. data/lib/hexapdf/font/type1_wrapper.rb +2 -1
  54. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  55. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  56. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  57. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  58. data/lib/hexapdf/image_loader/png.rb +2 -2
  59. data/lib/hexapdf/object.rb +18 -5
  60. data/lib/hexapdf/rectangle.rb +8 -1
  61. data/lib/hexapdf/revisions.rb +4 -2
  62. data/lib/hexapdf/serializer.rb +3 -3
  63. data/lib/hexapdf/stream.rb +3 -2
  64. data/lib/hexapdf/task/dereference.rb +4 -5
  65. data/lib/hexapdf/task/optimize.rb +6 -3
  66. data/lib/hexapdf/tokenizer.rb +3 -3
  67. data/lib/hexapdf/type/file_specification.rb +2 -2
  68. data/lib/hexapdf/type/form.rb +19 -0
  69. data/lib/hexapdf/type/page.rb +21 -6
  70. data/lib/hexapdf/type/page_tree_node.rb +27 -34
  71. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  72. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  73. data/lib/hexapdf/version.rb +1 -1
  74. data/man/man1/hexapdf.1 +259 -187
  75. data/test/hexapdf/content/graphic_object/test_arc.rb +1 -1
  76. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +1 -1
  77. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +1 -1
  78. data/test/hexapdf/content/test_canvas.rb +1 -1
  79. data/test/hexapdf/document/test_files.rb +71 -0
  80. data/test/hexapdf/{test_font_utils.rb → document/test_fonts.rb} +1 -2
  81. data/test/hexapdf/document/test_images.rb +78 -0
  82. data/test/hexapdf/document/test_pages.rb +114 -0
  83. data/test/hexapdf/encryption/test_standard_security_handler.rb +26 -5
  84. data/test/hexapdf/font/test_true_type_wrapper.rb +1 -1
  85. data/test/hexapdf/font/true_type/common.rb +0 -4
  86. data/test/hexapdf/font/true_type/table/test_cmap.rb +0 -6
  87. data/test/hexapdf/font/true_type/table/test_directory.rb +0 -5
  88. data/test/hexapdf/font/true_type/table/test_glyf.rb +5 -8
  89. data/test/hexapdf/font/true_type/table/test_head.rb +0 -20
  90. data/test/hexapdf/font/true_type/table/test_hhea.rb +0 -7
  91. data/test/hexapdf/font/true_type/table/test_hmtx.rb +2 -7
  92. data/test/hexapdf/font/true_type/table/test_loca.rb +4 -8
  93. data/test/hexapdf/font/true_type/table/test_maxp.rb +0 -7
  94. data/test/hexapdf/font/true_type/table/test_name.rb +0 -19
  95. data/test/hexapdf/font/true_type/table/test_os2.rb +0 -8
  96. data/test/hexapdf/font/true_type/table/test_post.rb +0 -13
  97. data/test/hexapdf/font/true_type/test_font.rb +14 -38
  98. data/test/hexapdf/font/true_type/test_table.rb +0 -9
  99. data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
  100. data/test/hexapdf/task/test_dereference.rb +5 -1
  101. data/test/hexapdf/task/test_optimize.rb +1 -1
  102. data/test/hexapdf/test_dictionary.rb +4 -0
  103. data/test/hexapdf/test_document.rb +0 -7
  104. data/test/hexapdf/test_importer.rb +4 -4
  105. data/test/hexapdf/test_object.rb +31 -9
  106. data/test/hexapdf/test_rectangle.rb +18 -0
  107. data/test/hexapdf/test_revisions.rb +7 -0
  108. data/test/hexapdf/test_serializer.rb +6 -0
  109. data/test/hexapdf/test_writer.rb +2 -2
  110. data/test/hexapdf/type/test_form.rb +12 -0
  111. data/test/hexapdf/type/test_page.rb +39 -20
  112. data/test/hexapdf/type/test_page_tree_node.rb +28 -21
  113. metadata +21 -9
  114. data/lib/hexapdf/document_utils.rb +0 -209
  115. data/test/hexapdf/test_document_utils.rb +0 -144
@@ -56,13 +56,13 @@ module HexaPDF
56
56
  #
57
57
  # A preferred table is always a table mapping Unicode characters.
58
58
  def preferred_table
59
- tables.select(&:unicode?).sort {|a, b| a.format <=> b.format}.last
59
+ tables.select(&:unicode?).sort_by(&:format).last
60
60
  end
61
61
 
62
62
  private
63
63
 
64
64
  def parse_table #:nodoc:
65
- @version, num_tables = read_formatted(4, 'n2')
65
+ @version, num_tables = read_formatted(4, 'n2')
66
66
  @tables = []
67
67
  handle_unknown = font.config['font.true_type.cmap.unknown_format']
68
68
 
@@ -90,11 +90,6 @@ module HexaPDF
90
90
  end.compact!
91
91
  end
92
92
 
93
- def load_default #:nodoc:
94
- @version = 0
95
- @tables = []
96
- end
97
-
98
93
  end
99
94
 
100
95
  end
@@ -276,11 +276,7 @@ module HexaPDF
276
276
 
277
277
  code_map = Hash.new do |h, code|
278
278
  i = end_codes.bsearch_index {|c| c >= code}
279
- if i && start_codes[i] <= code
280
- glyph_id = compute_glyph_id.call(i, code)
281
- else
282
- glyph_id = 0
283
- end
279
+ glyph_id = (i && start_codes[i] <= code ? compute_glyph_id.call(i, code) : 0)
284
280
  h[code] = glyph_id unless glyph_id == 0
285
281
  end
286
282
 
@@ -355,7 +351,7 @@ module HexaPDF
355
351
  #
356
352
  # It is assumed that the first twelve bytes of the subtable have already been consumed.
357
353
  def self.parse(io, _length)
358
- mapper(io.read(4).unpack('N').first.times.map { io.read(12).unpack('N3') })
354
+ mapper(Array.new(io.read(4).unpack('N').first) { io.read(12).unpack('N3') })
359
355
  end
360
356
 
361
357
  # The parameter +groups+ is an array containing [start_code, end_code, start_glyph_id]
@@ -79,11 +79,6 @@ module HexaPDF
79
79
  end
80
80
  end
81
81
 
82
- def load_default #:nodoc:
83
- @tag = 'true'.b
84
- @tables = {}
85
- end
86
-
87
82
  end
88
83
 
89
84
  end
@@ -127,14 +127,10 @@ module HexaPDF
127
127
  # The mapping from glyph ID to Glyph object or +nil+ (if the glyph has no outline).
128
128
  attr_accessor :glyphs
129
129
 
130
- # Returns the Glyph object for the given glyph ID, or +nil+ if it has no outline (e.g. the
131
- # space character).
130
+ # Returns the Glyph object for the given glyph ID. If the glyph has no outline (e.g. the
131
+ # space character), an empty Glyph object is returned.
132
132
  def [](glyph_id)
133
- if @glyphs.key?(glyph_id)
134
- return @glyphs[glyph_id]
135
- elsif !directory_entry
136
- return nil
137
- end
133
+ return @glyphs[glyph_id] if @glyphs.key?(glyph_id)
138
134
 
139
135
  offset = font[:loca].offset(glyph_id)
140
136
  length = font[:loca].length(glyph_id)
@@ -154,10 +150,6 @@ module HexaPDF
154
150
  @glyphs = {}
155
151
  end
156
152
 
157
- def load_default #:nodoc:
158
- @glyphs = {}
159
- end
160
-
161
153
  end
162
154
 
163
155
  end
@@ -98,8 +98,6 @@ module HexaPDF
98
98
  #
99
99
  # See: Table#checksum_valid?
100
100
  def checksum_valid?
101
- super unless directory_entry
102
-
103
101
  data = with_io_pos(directory_entry.offset) { io.read(directory_entry.length) }
104
102
  data[8, 4] = 0.chr * 4
105
103
  directory_entry.checksum == self.class.calculate_checksum(data)
@@ -125,16 +123,6 @@ module HexaPDF
125
123
  end
126
124
  end
127
125
 
128
- def load_default #:nodoc:
129
- @version = 1.to_r
130
- @font_revision = 1.to_r
131
- @checksum_adjustment = @flags = @mac_style = @smallest_readable_size =
132
- @font_direction_hint = @index_to_loc_format = 0
133
- @units_per_em = 64
134
- @created = @modified = Time.now
135
- @bbox = [0, 0, 0, 0]
136
- end
137
-
138
126
  end
139
127
 
140
128
  end
@@ -94,13 +94,6 @@ module HexaPDF
94
94
  @caret_offset, @num_of_long_hor_metrics = read_formatted(32, 's>3ns>6x10n')
95
95
  end
96
96
 
97
- def load_default #:nodoc:
98
- @version = 1.to_r
99
- @ascent = @descent = @line_gap = @advance_width_max = @min_left_side_bearing =
100
- @min_right_side_bearing = @x_max_extent = @caret_slope_rise = @caret_slope_run =
101
- @caret_offset = @num_of_long_hor_metrics = 0
102
- end
103
-
104
97
  end
105
98
 
106
99
  end
@@ -60,17 +60,13 @@ module HexaPDF
60
60
 
61
61
  def parse_table #:nodoc:
62
62
  nr_entries = font[:hhea].num_of_long_hor_metrics
63
- @horizontal_metrics = nr_entries.times.map { Metric.new(*read_formatted(4, 'ns>')) }
63
+ @horizontal_metrics = Array.new(nr_entries) { Metric.new(*read_formatted(4, 'ns>')) }
64
64
  last_advance_width = @horizontal_metrics[-1].advance_width
65
65
  read_formatted(directory_entry.length - 4 * nr_entries, 's>*').map do |lsb|
66
66
  @horizontal_metrics << Metric.new(last_advance_width, lsb)
67
67
  end
68
68
  end
69
69
 
70
- def load_default #:nodoc:
71
- @horizontal_metrics = []
72
- end
73
-
74
70
  end
75
71
 
76
72
  end
@@ -67,10 +67,6 @@ module HexaPDF
67
67
  @offsets.map! {|offset| offset * 2} if entry_size == 0
68
68
  end
69
69
 
70
- def load_default #:nodoc:
71
- @offsets = []
72
- end
73
-
74
70
  end
75
71
 
76
72
  end
@@ -96,14 +96,6 @@ module HexaPDF
96
96
  @max_component_depth = read_formatted(directory_entry.length - 4, 'n14')
97
97
  end
98
98
 
99
- def load_default #:nodoc:
100
- @version = 1.to_r
101
- @num_glyphs = @max_points = @max_contours = @max_component_points =
102
- @max_component_contours = @max_twilight_points = @max_storage = @max_function_defs =
103
- @max_instruction_defs = @max_stack_elements = @max_size_of_instructions =
104
- @max_component_elements = @max_component_depth = 0
105
- end
106
-
107
99
  end
108
100
 
109
101
  end
@@ -68,7 +68,7 @@ module HexaPDF
68
68
  postscript_cid_name: 20,
69
69
  wws_family: 21,
70
70
  wws_subfamily: 22,
71
- }
71
+ }.freeze
72
72
 
73
73
  # Contains the information for a Name Record.
74
74
  #
@@ -167,14 +167,6 @@ module HexaPDF
167
167
  @records[name_or_id.kind_of?(Symbol) ? NAME_MAP[name_or_id] : name_or_id]
168
168
  end
169
169
 
170
- # Adds a new record for the given name identifier (either a symbol or an ID).
171
- #
172
- # The optional platform, encoding and language IDs are preset to represent the text as
173
- # English in Mac Roman encoding.
174
- def add(name_or_id, text, platform_id: 1, encoding_id: 0, language_id: 0)
175
- self[name_or_id] << Record.new(text, platform_id, encoding_id, language_id)
176
- end
177
-
178
170
  private
179
171
 
180
172
  def parse_table #:nodoc:
@@ -184,10 +176,10 @@ module HexaPDF
184
176
  @records = Hash.new {|h, k| h[k] = Records.new}
185
177
  @language_tags = {}
186
178
 
187
- record_rows = count.times.map { read_formatted(12, 'n6') }
179
+ record_rows = Array.new(count) { read_formatted(12, 'n6') }
188
180
  if @format == 1
189
181
  count = read_formatted(2, 'n').first
190
- language_rows = count.times.map { read_formatted(4, 'n2') }
182
+ language_rows = Array.new(count) { read_formatted(4, 'n2') }
191
183
  end
192
184
 
193
185
  record_rows.each do |pid, eid, lid, nid, length, offset|
@@ -204,12 +196,6 @@ module HexaPDF
204
196
  end
205
197
  end
206
198
 
207
- def load_default #:nodoc:
208
- @format = 0
209
- @records = Hash.new {|h, k| h[k] = Records.new}
210
- @language_tags = {}
211
- end
212
-
213
199
  end
214
200
 
215
201
  end
@@ -178,20 +178,6 @@ module HexaPDF
178
178
  read_formatted(directory_entry.length - 58, 'a4n3s>3n2Q>s>2n5')
179
179
  end
180
180
 
181
- def load_default #:nodoc:
182
- @version = 5
183
- @panose = ''.b
184
- @vendor_id = ' '.b
185
- @x_avg_char_width = @weight_class = @width_class = @type = @subscript_x_size =
186
- @subscript_y_size = @subscript_x_offset = @subscript_y_offset = @superscript_x_size =
187
- @superscript_y_size = @superscript_x_offset = @superscript_y_offset =
188
- @strikeout_size = @strikeout_position = @family_class = @unicode_range = @selection =
189
- @first_char_index = @last_char_index = @typo_ascender = @typo_descender =
190
- @typo_line_gap = @win_ascent = @win_descent = @code_page_range = @x_height =
191
- @cap_height = @default_char = @break_char = @max_context = @lower_point_size =
192
- @upper_point_size = 0
193
- end
194
-
195
181
  end
196
182
 
197
183
  end
@@ -106,14 +106,6 @@ module HexaPDF
106
106
  end
107
107
  end
108
108
 
109
- def load_default #:nodoc:
110
- @format = 1.to_r
111
- @italic_angle = 0.to_r
112
- @underline_position = @underline_thickness = @is_fixed_pitch = @min_mem_type42 =
113
- @max_mem_type42 = @min_mem_type1 = @max_mem_type1 = 0
114
- @glyph_names = {}
115
- end
116
-
117
109
 
118
110
  # 'post' table format 1
119
111
  module Format1
@@ -203,7 +203,7 @@ module HexaPDF
203
203
  cid_font[:DW] = default_width = glyph(3).width
204
204
 
205
205
  glyphs = @encoded_glyphs.keys.reject {|g| g.width == default_width}.sort_by(&:id)
206
- if glyphs.length > 0
206
+ unless glyphs.empty?
207
207
  cid_font[:W] = widths = []
208
208
  last_id = -10
209
209
  cur_widths = nil
@@ -36,8 +36,8 @@ module HexaPDF
36
36
 
37
37
  # This module provides classes for handling Type1 fonts.
38
38
  #
39
- # Note that not all parts of the file format are supported, only those needed for using the
40
- # fonts with PDF.
39
+ # Note that not all parts of the various file formats are supported, only those needed for using
40
+ # the fonts with PDF.
41
41
  module Type1
42
42
 
43
43
  autoload(:AFMParser, 'hexapdf/font/type1/afm_parser')
@@ -50,6 +50,7 @@ module HexaPDF
50
50
  # * full_name
51
51
  # * family_name
52
52
  # * weight
53
+ # * weight_class
53
54
  # * font_bbox
54
55
  # * italic_angle
55
56
  # * ascender
@@ -71,7 +72,7 @@ module HexaPDF
71
72
  attr_reader :metrics
72
73
 
73
74
  def_delegators :@metrics, :font_name, :full_name, :family_name
74
- def_delegators :@metrics, :weight, :bounding_box, :italic_angle
75
+ def_delegators :@metrics, :weight, :weight_class, :bounding_box, :italic_angle
75
76
  def_delegators :@metrics, :ascender, :descender, :cap_height, :x_height
76
77
  def_delegators :@metrics, :dominant_horizontal_stem_width, :dominant_vertical_stem_width
77
78
 
@@ -56,7 +56,7 @@ module HexaPDF
56
56
  # A string indicating the default encoding used for the font.
57
57
  attr_accessor :encoding_scheme
58
58
 
59
- # Weight of the font.
59
+ # A string describing the weight of the font.
60
60
  attr_accessor :weight
61
61
 
62
62
  # The font bounding box as array of four numbers, specifying the x- and y-coordinates of the
@@ -110,6 +110,15 @@ module HexaPDF
110
110
  @kerning_pairs = Hash.new {|h, k| h[k] = {}}
111
111
  end
112
112
 
113
+ WEIGHT_NAME_TO_NUMBER = {'Bold' => 700, 'Medium' => 500, 'Roman' => 400}.freeze #:nodoc:
114
+
115
+ # Returns the weight of the font as a number.
116
+ #
117
+ # The return value 0 is used if the weight class cannot be determined.
118
+ def weight_class
119
+ WEIGHT_NAME_TO_NUMBER.fetch(weight, 0)
120
+ end
121
+
113
122
  end
114
123
 
115
124
  end
@@ -150,6 +150,7 @@ module HexaPDF
150
150
  unless defined?(@fd)
151
151
  @fd = @document.wrap(Type: :FontDescriptor,
152
152
  FontName: @wrapped_font.font_name.intern,
153
+ FontWeight: @wrapped_font.weight_class,
153
154
  FontBBox: @wrapped_font.bounding_box,
154
155
  ItalicAngle: @wrapped_font.italic_angle || 0,
155
156
  Ascent: @wrapped_font.ascender || 0,
@@ -169,7 +170,7 @@ module HexaPDF
169
170
  end
170
171
 
171
172
  # Array of valid encoding names in PDF
172
- VALID_ENCODING_NAMES = [:WinAnsiEncoding, :MacRomanEncoding, :MacExpertEncoding]
173
+ VALID_ENCODING_NAMES = [:WinAnsiEncoding, :MacRomanEncoding, :MacExpertEncoding].freeze
173
174
 
174
175
  # Completes the font dictionary by filling in the values that depend on the used encoding.
175
176
  def complete_font_dict
@@ -60,7 +60,7 @@ module HexaPDF
60
60
  raise HexaPDF::Error, "The configured font file #{file} does not exist"
61
61
  end
62
62
 
63
- font = HexaPDF::Font::TrueType::Font.new(io: File.open(file))
63
+ font = HexaPDF::Font::TrueType::Font.new(File.open(file))
64
64
  HexaPDF::Font::TrueTypeWrapper.new(document, font)
65
65
  end
66
66
 
@@ -66,7 +66,7 @@ module HexaPDF
66
66
  'ZapfDingbats' => {
67
67
  none: 'ZapfDingbats',
68
68
  },
69
- }
69
+ }.freeze
70
70
 
71
71
  # Creates a new font object backed by the AFM font metrics read from the file or IO stream.
72
72
  #
@@ -49,7 +49,7 @@ module HexaPDF
49
49
  # object.
50
50
  #
51
51
  # See: ITU T.81 B1.1.3
52
- SOF_MARKERS = [0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF]
52
+ SOF_MARKERS = [0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF].freeze
53
53
 
54
54
  # Adobe uses the marker 0xEE (APPE) for its purposes. We need to use it for determinig
55
55
  # whether to invert the colors for CMYK/YCCK images or not (Adobe does this...).
@@ -79,7 +79,7 @@ module HexaPDF
79
79
  else
80
80
  HexaPDF::Document.new(io: file_or_io)
81
81
  end
82
- form = idoc.pages.page(0).to_form_xobject
82
+ form = idoc.pages[0].to_form_xobject
83
83
  document.add(document.import(form))
84
84
  end
85
85
 
@@ -78,10 +78,10 @@ module HexaPDF
78
78
  1 => Content::RenderingIntent::RELATIVE_COLORIMETRIC,
79
79
  2 => Content::RenderingIntent::SATURATION,
80
80
  3 => Content::RenderingIntent::ABSOLUTE_COLORIMETRIC,
81
- }
81
+ }.freeze
82
82
 
83
83
  # The primary chromaticities and white point used by the sRGB specification.
84
- SRGB_CHRM = [0.3127, 0.329, 0.64, 0.33, 0.3, 0.6, 0.15, 0.06]
84
+ SRGB_CHRM = [0.3127, 0.329, 0.64, 0.33, 0.3, 0.6, 0.15, 0.06].freeze
85
85
 
86
86
  # :call-seq:
87
87
  # PNG.handles?(filename) -> true or false
@@ -121,7 +121,7 @@ module HexaPDF
121
121
  include Comparable
122
122
 
123
123
  # A list of classes whose objects cannot be duplicated.
124
- NOT_DUPLICATABLE_CLASSES = [NilClass, FalseClass, TrueClass, Symbol, Integer, Float]
124
+ NOT_DUPLICATABLE_CLASSES = [NilClass, FalseClass, TrueClass, Symbol, Integer, Float].freeze
125
125
 
126
126
  # :call-seq:
127
127
  # HexaPDF::Object.deep_copy(object) -> copy
@@ -134,7 +134,7 @@ module HexaPDF
134
134
  when Array
135
135
  object.map {|o| deep_copy(o)}
136
136
  when HexaPDF::Object
137
- (object.indirect? ? object : deep_copy(object.value))
137
+ (object.indirect? || object.must_be_indirect? ? object : deep_copy(object.value))
138
138
  when HexaPDF::Reference
139
139
  object
140
140
  when *NOT_DUPLICATABLE_CLASSES
@@ -265,10 +265,10 @@ module HexaPDF
265
265
  # *Note*: Even if the return value is +true+ there may be problems since HexaPDF doesn't
266
266
  # currently implement the full PDF spec. However, if the return value is +false+, there is
267
267
  # certainly a problem!
268
- def validate(auto_correct: true, &block)
268
+ def validate(auto_correct: true)
269
269
  catch do |catch_tag|
270
270
  perform_validation do |msg, correctable|
271
- block.call(msg, correctable) if block
271
+ yield(msg, correctable) if block_given?
272
272
  throw(catch_tag, false) unless auto_correct && correctable
273
273
  end
274
274
  true
@@ -350,12 +350,25 @@ module HexaPDF
350
350
  # self.value = {}
351
351
  # end
352
352
  # end
353
- def perform_validation
353
+ def perform_validation(&block)
354
354
  # Validate that the object is indirect if #must_be_indirect? is +true+.
355
355
  if must_be_indirect? && !indirect?
356
356
  yield("Object must be an indirect object", true)
357
357
  document.add(self)
358
358
  end
359
+
360
+ validate_nested(value, &block)
361
+ end
362
+
363
+ # Validates all nested values of the object, i.e. values inside collection objects.
364
+ def validate_nested(obj, &block)
365
+ if obj.kind_of?(HexaPDF::Object) && !obj.indirect?
366
+ obj.validate(&block)
367
+ elsif obj.kind_of?(Hash)
368
+ obj.each_value {|val| validate_nested(val, &block)}
369
+ elsif obj.kind_of?(Array)
370
+ obj.each {|val| validate_nested(val, &block)}
371
+ end
359
372
  end
360
373
 
361
374
  end