hexapdf 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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