hexapdf 0.5.0 → 0.6.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -2
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -1
  6. data/examples/boxes.rb +68 -0
  7. data/examples/graphics.rb +12 -12
  8. data/examples/{text_box_alignment.rb → text_layouter_alignment.rb} +14 -14
  9. data/examples/text_layouter_inline_boxes.rb +66 -0
  10. data/examples/{text_box_line_wrapping.rb → text_layouter_line_wrapping.rb} +9 -10
  11. data/examples/{text_box_shapes.rb → text_layouter_shapes.rb} +58 -54
  12. data/examples/text_layouter_styling.rb +125 -0
  13. data/examples/truetype.rb +5 -7
  14. data/lib/hexapdf/cli/command.rb +1 -0
  15. data/lib/hexapdf/configuration.rb +170 -106
  16. data/lib/hexapdf/content/canvas.rb +41 -36
  17. data/lib/hexapdf/content/graphics_state.rb +15 -0
  18. data/lib/hexapdf/content/operator.rb +1 -1
  19. data/lib/hexapdf/dictionary.rb +20 -8
  20. data/lib/hexapdf/dictionary_fields.rb +8 -6
  21. data/lib/hexapdf/document.rb +25 -26
  22. data/lib/hexapdf/document/fonts.rb +4 -4
  23. data/lib/hexapdf/document/images.rb +2 -2
  24. data/lib/hexapdf/document/pages.rb +16 -16
  25. data/lib/hexapdf/encryption/security_handler.rb +41 -9
  26. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  27. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  28. data/lib/hexapdf/filter/predictor.rb +7 -1
  29. data/lib/hexapdf/font/true_type/font.rb +20 -0
  30. data/lib/hexapdf/font/type1/font.rb +23 -0
  31. data/lib/hexapdf/font_loader.rb +1 -0
  32. data/lib/hexapdf/font_loader/from_configuration.rb +2 -3
  33. data/lib/hexapdf/font_loader/from_file.rb +65 -0
  34. data/lib/hexapdf/image_loader/png.rb +2 -2
  35. data/lib/hexapdf/layout.rb +3 -2
  36. data/lib/hexapdf/layout/box.rb +146 -0
  37. data/lib/hexapdf/layout/inline_box.rb +40 -31
  38. data/lib/hexapdf/layout/{line_fragment.rb → line.rb} +12 -13
  39. data/lib/hexapdf/layout/style.rb +630 -41
  40. data/lib/hexapdf/layout/text_fragment.rb +80 -12
  41. data/lib/hexapdf/layout/{text_box.rb → text_layouter.rb} +164 -109
  42. data/lib/hexapdf/number_tree_node.rb +1 -1
  43. data/lib/hexapdf/parser.rb +4 -1
  44. data/lib/hexapdf/revisions.rb +11 -4
  45. data/lib/hexapdf/stream.rb +8 -9
  46. data/lib/hexapdf/tokenizer.rb +5 -3
  47. data/lib/hexapdf/type.rb +3 -0
  48. data/lib/hexapdf/type/action.rb +56 -0
  49. data/lib/hexapdf/type/actions.rb +52 -0
  50. data/lib/hexapdf/type/actions/go_to.rb +52 -0
  51. data/lib/hexapdf/type/actions/go_to_r.rb +54 -0
  52. data/lib/hexapdf/type/actions/launch.rb +73 -0
  53. data/lib/hexapdf/type/actions/uri.rb +65 -0
  54. data/lib/hexapdf/type/annotation.rb +85 -0
  55. data/lib/hexapdf/type/annotations.rb +51 -0
  56. data/lib/hexapdf/type/annotations/link.rb +70 -0
  57. data/lib/hexapdf/type/annotations/markup_annotation.rb +70 -0
  58. data/lib/hexapdf/type/annotations/text.rb +81 -0
  59. data/lib/hexapdf/type/catalog.rb +3 -1
  60. data/lib/hexapdf/type/embedded_file.rb +6 -11
  61. data/lib/hexapdf/type/file_specification.rb +4 -6
  62. data/lib/hexapdf/type/font.rb +3 -1
  63. data/lib/hexapdf/type/font_descriptor.rb +18 -16
  64. data/lib/hexapdf/type/form.rb +3 -1
  65. data/lib/hexapdf/type/graphics_state_parameter.rb +3 -1
  66. data/lib/hexapdf/type/image.rb +4 -2
  67. data/lib/hexapdf/type/info.rb +2 -5
  68. data/lib/hexapdf/type/names.rb +2 -5
  69. data/lib/hexapdf/type/object_stream.rb +2 -1
  70. data/lib/hexapdf/type/page.rb +14 -1
  71. data/lib/hexapdf/type/page_tree_node.rb +9 -6
  72. data/lib/hexapdf/type/resources.rb +2 -5
  73. data/lib/hexapdf/type/trailer.rb +2 -5
  74. data/lib/hexapdf/type/viewer_preferences.rb +2 -5
  75. data/lib/hexapdf/type/xref_stream.rb +3 -1
  76. data/lib/hexapdf/version.rb +1 -1
  77. data/test/hexapdf/common_tokenizer_tests.rb +3 -1
  78. data/test/hexapdf/content/test_canvas.rb +29 -3
  79. data/test/hexapdf/content/test_graphics_state.rb +11 -0
  80. data/test/hexapdf/content/test_operator.rb +3 -2
  81. data/test/hexapdf/document/test_fonts.rb +8 -8
  82. data/test/hexapdf/document/test_images.rb +4 -12
  83. data/test/hexapdf/document/test_pages.rb +7 -7
  84. data/test/hexapdf/encryption/test_security_handler.rb +1 -5
  85. data/test/hexapdf/filter/test_predictor.rb +40 -12
  86. data/test/hexapdf/font/true_type/test_font.rb +16 -0
  87. data/test/hexapdf/font/type1/test_font.rb +30 -0
  88. data/test/hexapdf/font_loader/test_from_file.rb +29 -0
  89. data/test/hexapdf/font_loader/test_standard14.rb +4 -3
  90. data/test/hexapdf/layout/test_box.rb +104 -0
  91. data/test/hexapdf/layout/test_inline_box.rb +24 -10
  92. data/test/hexapdf/layout/{test_line_fragment.rb → test_line.rb} +9 -9
  93. data/test/hexapdf/layout/test_style.rb +519 -31
  94. data/test/hexapdf/layout/test_text_fragment.rb +136 -15
  95. data/test/hexapdf/layout/{test_text_box.rb → test_text_layouter.rb} +224 -144
  96. data/test/hexapdf/layout/test_text_shaper.rb +1 -1
  97. data/test/hexapdf/test_configuration.rb +12 -6
  98. data/test/hexapdf/test_dictionary.rb +27 -2
  99. data/test/hexapdf/test_dictionary_fields.rb +10 -1
  100. data/test/hexapdf/test_document.rb +14 -13
  101. data/test/hexapdf/test_parser.rb +12 -0
  102. data/test/hexapdf/test_revisions.rb +34 -0
  103. data/test/hexapdf/test_stream.rb +1 -1
  104. data/test/hexapdf/test_type.rb +18 -0
  105. data/test/hexapdf/test_writer.rb +2 -2
  106. data/test/hexapdf/type/actions/test_launch.rb +24 -0
  107. data/test/hexapdf/type/actions/test_uri.rb +23 -0
  108. data/test/hexapdf/type/annotations/test_link.rb +19 -0
  109. data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
  110. data/test/hexapdf/type/annotations/test_text.rb +38 -0
  111. data/test/hexapdf/type/test_annotation.rb +38 -0
  112. data/test/hexapdf/type/test_file_specification.rb +0 -7
  113. data/test/hexapdf/type/test_info.rb +0 -5
  114. data/test/hexapdf/type/test_page.rb +14 -0
  115. data/test/hexapdf/type/test_page_tree_node.rb +4 -1
  116. data/test/hexapdf/type/test_trailer.rb +0 -4
  117. data/test/test_helper.rb +6 -3
  118. metadata +36 -15
  119. data/examples/text_box_inline_boxes.rb +0 -56
  120. data/examples/text_box_styling.rb +0 -72
  121. data/test/hexapdf/type/test_embedded_file.rb +0 -16
  122. data/test/hexapdf/type/test_names.rb +0 -9
@@ -195,6 +195,7 @@ module HexaPDF
195
195
  @graphics_state = GraphicsState.new
196
196
  @graphics_object = :none
197
197
  @font = nil
198
+ @font_stack = []
198
199
  @serializer = HexaPDF::Serializer.new
199
200
  @current_point = [0, 0]
200
201
  @start_point = [0, 0]
@@ -241,6 +242,7 @@ module HexaPDF
241
242
  def save_graphics_state
242
243
  raise_unless_at_page_description_level
243
244
  invoke0(:q)
245
+ @font_stack.push(@font)
244
246
  if block_given?
245
247
  yield
246
248
  restore_graphics_state
@@ -259,6 +261,7 @@ module HexaPDF
259
261
  def restore_graphics_state
260
262
  raise_unless_at_page_description_level
261
263
  invoke0(:Q)
264
+ @font = @font_stack.pop
262
265
  self
263
266
  end
264
267
 
@@ -607,16 +610,8 @@ module HexaPDF
607
610
  #
608
611
  # See: PDF1.7 s8.4.3.5, LineDashPattern
609
612
  def line_dash_pattern(value = nil, phase = 0, &block)
610
- case value
611
- when nil, LineDashPattern
612
- when Array
613
- value = LineDashPattern.new(value, phase)
614
- when 0
615
- value = LineDashPattern.new([], 0)
616
- else
617
- value = LineDashPattern.new([value], phase)
618
- end
619
- gs_getter_setter(:line_dash_pattern, :d, value, &block)
613
+ gs_getter_setter(:line_dash_pattern, :d, value && LineDashPattern.normalize(value, phase),
614
+ &block)
620
615
  end
621
616
  alias :line_dash_pattern= :line_dash_pattern
622
617
 
@@ -1612,7 +1607,7 @@ module HexaPDF
1612
1607
  # See: PDF1.7 s9.2.2
1613
1608
  def font(name = nil, size: nil, **options)
1614
1609
  if name
1615
- @font = (name.respond_to?(:dict) ? name : context.document.fonts.load(name, options))
1610
+ @font = (name.respond_to?(:dict) ? name : context.document.fonts.add(name, options))
1616
1611
  if size
1617
1612
  font_size(size)
1618
1613
  else
@@ -1684,6 +1679,7 @@ module HexaPDF
1684
1679
  #
1685
1680
  # See: http://www.unicode.org/reports/tr18/#Line_Boundaries
1686
1681
  def text(text, at: nil)
1682
+ raise_unless_font_set
1687
1683
  move_text_cursor(offset: at) if at
1688
1684
  lines = text.split(/\u{D A}|(?!\u{D A})[\u{A}-\u{D}\u{85}\u{2028}\u{2029}]/, -1)
1689
1685
  lines.each_with_index do |str, index|
@@ -1709,6 +1705,7 @@ module HexaPDF
1709
1705
  #
1710
1706
  # This method is usually not invoked directly but by higher level methods like #text.
1711
1707
  def show_glyphs(glyphs)
1708
+ raise_unless_font_set
1712
1709
  begin_text
1713
1710
 
1714
1711
  result = [''.b]
@@ -1744,6 +1741,7 @@ module HexaPDF
1744
1741
  # #text_cursor and other methods using the current text matrix are invalid until the next call
1745
1742
  # to #text_matrix or #end_text.
1746
1743
  def show_glyphs_only(glyphs)
1744
+ raise_unless_font_set
1747
1745
  begin_text
1748
1746
 
1749
1747
  result = [''.b]
@@ -1850,6 +1848,18 @@ module HexaPDF
1850
1848
  self
1851
1849
  end
1852
1850
 
1851
+ # Creates a color object from the given color specification. See #stroke_color for details
1852
+ # on the possible color specifications.
1853
+ def color_from_specification(spec)
1854
+ if spec.length == 1 && spec[0].kind_of?(String)
1855
+ resources.color_space(:DeviceRGB).color(*spec[0].scan(/../).map!(&:hex))
1856
+ elsif spec.length == 1 && spec[0].respond_to?(:color_space)
1857
+ spec[0]
1858
+ else
1859
+ resources.color_space(color_space_for_components(spec)).color(*spec)
1860
+ end
1861
+ end
1862
+
1853
1863
  private
1854
1864
 
1855
1865
  # Invokes the given operator with the operands and serializes it.
@@ -1895,15 +1905,15 @@ module HexaPDF
1895
1905
 
1896
1906
  # Raises an error unless the current graphics object is a path.
1897
1907
  def raise_unless_in_path
1898
- if graphics_object != :path
1899
- raise HexaPDF::Error, "Operation only allowed when current graphics object is a path"
1908
+ unless graphics_object == :path
1909
+ raise HexaPDF::Error, "Operation only allowed if current graphics object is a path"
1900
1910
  end
1901
1911
  end
1902
1912
 
1903
1913
  # Raises an error unless the current graphics object is a path or a clipping path.
1904
1914
  def raise_unless_in_path_or_clipping_path
1905
- if graphics_object != :path && graphics_object != :clipping_path
1906
- raise HexaPDF::Error, "Operation only allowed when current graphics object is a " \
1915
+ unless graphics_object == :path || graphics_object == :clipping_path
1916
+ raise HexaPDF::Error, "Operation only allowed if current graphics object is a " \
1907
1917
  "path or clipping path"
1908
1918
  end
1909
1919
  end
@@ -1912,15 +1922,15 @@ module HexaPDF
1912
1922
  # level.
1913
1923
  def raise_unless_at_page_description_level
1914
1924
  end_text if graphics_object == :text
1915
- if graphics_object != :none
1916
- raise HexaPDF::Error, "Operation only allowed when there is no current graphics object"
1925
+ unless graphics_object == :none
1926
+ raise HexaPDF::Error, "Operation only allowed if there is no current graphics object"
1917
1927
  end
1918
1928
  end
1919
1929
 
1920
1930
  # Raises an error unless the current graphics object is none or a text object.
1921
1931
  def raise_unless_at_page_description_level_or_in_text
1922
- if graphics_object != :none && graphics_object != :text
1923
- raise HexaPDF::Error, "Operation only allowed when current graphics object is a " \
1932
+ unless graphics_object == :none || graphics_object == :text
1933
+ raise HexaPDF::Error, "Operation only allowed if current graphics object is a " \
1924
1934
  "text object or if there is no current object"
1925
1935
  end
1926
1936
  end
@@ -1928,20 +1938,27 @@ module HexaPDF
1928
1938
  # Raises an error unless the current graphics object is none or a path object.
1929
1939
  def raise_unless_at_page_description_level_or_in_path
1930
1940
  end_text if graphics_object == :text
1931
- if graphics_object != :none && graphics_object != :path
1932
- raise HexaPDF::Error, "Operation only allowed when current graphics object is a " \
1941
+ unless graphics_object == :none || graphics_object == :path
1942
+ raise HexaPDF::Error, "Operation only allowed if current graphics object is a " \
1933
1943
  "path object or if there is no current object"
1934
1944
  end
1935
1945
  end
1936
1946
 
1937
1947
  # Raises an error unless the current graphics object is a text object.
1938
1948
  def raise_unless_in_text
1939
- if graphics_object != :text
1940
- raise HexaPDF::Error, "Operation only allowed when current graphics object is a " \
1949
+ unless graphics_object == :text
1950
+ raise HexaPDF::Error, "Operation only allowed if current graphics object is a " \
1941
1951
  "text object"
1942
1952
  end
1943
1953
  end
1944
1954
 
1955
+ # Raises an error unless a font has been set.
1956
+ def raise_unless_font_set
1957
+ unless @font
1958
+ raise HexaPDF::Error, "Operation only allowed if a font is set"
1959
+ end
1960
+ end
1961
+
1945
1962
  # Utility method that abstracts the implementation of the stroke and fill color methods.
1946
1963
  def color_getter_setter(name, color, rg, g, k, cs, scn)
1947
1964
  color.flatten!
@@ -1950,7 +1967,7 @@ module HexaPDF
1950
1967
  color = color_from_specification(color)
1951
1968
 
1952
1969
  save_graphics_state if block_given?
1953
- if color != graphics_state.send(name)
1970
+ unless color == graphics_state.send(name)
1954
1971
  case color.color_space.family
1955
1972
  when :DeviceRGB then serialize(rg, *color.components)
1956
1973
  when :DeviceGray then serialize(g, *color.components)
@@ -1977,18 +1994,6 @@ module HexaPDF
1977
1994
  end
1978
1995
  end
1979
1996
 
1980
- # Creates a color object from the given color specification. See #stroke_color for details
1981
- # on the possible color specifications.
1982
- def color_from_specification(spec)
1983
- if spec.length == 1 && spec[0].kind_of?(String)
1984
- resources.color_space(:DeviceRGB).color(*spec[0].scan(/../).map!(&:hex))
1985
- elsif spec.length == 1 && spec[0].respond_to?(:color_space)
1986
- spec[0]
1987
- else
1988
- resources.color_space(color_space_for_components(spec)).color(*spec)
1989
- end
1990
- end
1991
-
1992
1997
  # Returns the name of the device color space that should be used for creating a color object
1993
1998
  # from the components array.
1994
1999
  def color_space_for_components(components)
@@ -159,6 +159,21 @@ module HexaPDF
159
159
  # See: PDF1.7 s8.4.3.6
160
160
  class LineDashPattern
161
161
 
162
+ # Returns the arguments normalized to a valid LineDashPattern instance.
163
+ #
164
+ # If +array+ is 0, the default line dash pattern representing a solid line will be used. If it
165
+ # is a single number, it will be converted into an array holding that number.
166
+ def self.normalize(array, phase = 0)
167
+ case array
168
+ when LineDashPattern then array
169
+ when Array then new(array, phase)
170
+ when 0 then new
171
+ when Numeric then new([array], phase)
172
+ else
173
+ raise ArgumentError, "Unknown line dash pattern: #{array} / #{phase}"
174
+ end
175
+ end
176
+
162
177
  # The dash array.
163
178
  attr_reader :array
164
179
 
@@ -784,7 +784,7 @@ module HexaPDF
784
784
  end
785
785
 
786
786
  def invoke(processor, rendering_mode) #:nodoc:
787
- processor.graphics_state.text_rendering_mode = rendering_mode
787
+ processor.graphics_state.text_rendering_mode = TextRenderingMode.normalize(rendering_mode)
788
788
  end
789
789
 
790
790
  end
@@ -117,6 +117,19 @@ module HexaPDF
117
117
  @fields.each(&block) if defined?(@fields)
118
118
  end
119
119
 
120
+ # Defines the static PDF type of the class in cases where this is possible, i.e. when the class
121
+ # implements one specific PDF type (e.g. the HexaPDF::Type::Catalog class).
122
+ def self.define_type(type)
123
+ @type = type
124
+ end
125
+
126
+ # Returns the statically defined PDF type of the class.
127
+ #
128
+ # See ::define_type
129
+ def self.type
130
+ defined?(@type) && @type
131
+ end
132
+
120
133
 
121
134
  # Returns the value for the given dictionary entry.
122
135
  #
@@ -166,9 +179,9 @@ module HexaPDF
166
179
  end
167
180
  end
168
181
 
169
- # Returns +true+ if the given key is present in the dictionary.
182
+ # Returns +true+ if the given key is present in the dictionary and not +nil+.
170
183
  def key?(key)
171
- value.key?(key)
184
+ !value[key].nil?
172
185
  end
173
186
 
174
187
  # Deletes the name-value pair from the dictionary and returns the value. If such a pair does
@@ -190,9 +203,10 @@ module HexaPDF
190
203
  self
191
204
  end
192
205
 
193
- # Returns the value of the /Type field or, if not set, the result of Object#type.
206
+ # Returns, in order or availability, the value of ::type, the /Type field or the result of
207
+ # Object#type.
194
208
  def type
195
- self[:Type] || super
209
+ self.class.type || self[:Type] || super
196
210
  end
197
211
 
198
212
  # Returns +true+ if the dictionary contains no entries.
@@ -201,11 +215,9 @@ module HexaPDF
201
215
  end
202
216
 
203
217
  # Returns a dup of the underlying hash.
204
- def to_hash
218
+ def to_h
205
219
  value.dup
206
220
  end
207
- alias :to_h :to_hash
208
-
209
221
 
210
222
  private
211
223
 
@@ -242,7 +254,7 @@ module HexaPDF
242
254
  def perform_validation(&block)
243
255
  super
244
256
  each_set_key_or_required_field do |name, field|
245
- obj = key?(name) && self[name] || nil
257
+ obj = key?(name) ? self[name] : nil
246
258
 
247
259
  # Validate nested objects
248
260
  validate_nested(obj, &block)
@@ -325,24 +325,26 @@ module HexaPDF
325
325
  # converted to the corresponding file specification dictionary.
326
326
  module FileSpecificationConverter
327
327
 
328
- # This converter is only used for the :FileSpec type.
328
+ # This converter is only used for the :Filespec type.
329
329
  def self.usable_for?(type)
330
330
  type == :Filespec
331
331
  end
332
332
 
333
- # FileSpecs can also be simple hashes or strings.
333
+ # Filespecs can also be simple hashes or strings.
334
334
  def self.additional_types
335
335
  [Hash, String]
336
336
  end
337
337
 
338
338
  # Returns +true+ if the given data is a string file specification.
339
- def self.convert?(data, _type)
340
- data.kind_of?(String)
339
+ def self.convert?(data, type)
340
+ !data.kind_of?(type.first) &&
341
+ (data.kind_of?(Hash) || data.kind_of?(HexaPDF::Dictionary) || data.kind_of?(String))
341
342
  end
342
343
 
343
- # Converts the string file specification into a full file specification.
344
+ # Converts a string file specification or a hash into a full file specification.
344
345
  def self.convert(data, type, document)
345
- document.wrap({F: data}, type: type.first)
346
+ data = {F: data} if data.kind_of?(String)
347
+ document.wrap(data, type: type.first)
346
348
  end
347
349
 
348
350
  end
@@ -276,16 +276,15 @@ module HexaPDF
276
276
  # of the +type+ and +subtype+ options as well as on the 'object.type_map' and
277
277
  # 'object.subtype_map' global configuration options:
278
278
  #
279
- # * If *only* +type+ or +subtype+ is provided and a mapping is found, the resulting class is
280
- # used.
279
+ # * First +type+ is used to try to determine the class. If it is already a Class object, it is
280
+ # used, otherwise the type is looked up in 'object.type_map'.
281
281
  #
282
- # * If both +type+ and +subtype+ are provided and and a mapping for +subtype+ is found, the
283
- # resulting class is used. If no mapping is found but there is a mapping for +type+, the
284
- # mapped class is used.
282
+ # * If +subtype+ is provided or can be determined because +obj+ is a hash with a :Subtype or :S
283
+ # field, the type and subtype together are used to look up a special subtype class in
284
+ # 'object.subtype_map'.
285
285
  #
286
- # * If there is no valid class after the above steps, HexaPDF::Stream is used if a stream
287
- # is given, HexaPDF::Dictionary is used if the given objecct is a hash or else
288
- # HexaPDF::Object is used.
286
+ # * If there is no valid class after the above steps, HexaPDF::Stream is used if a stream is
287
+ # given, HexaPDF::Dictionary if the given objecct is a hash or else HexaPDF::Object is used.
289
288
  #
290
289
  # Options:
291
290
  #
@@ -316,27 +315,27 @@ module HexaPDF
316
315
 
317
316
  if type.kind_of?(Class)
318
317
  klass = type
318
+ type = (klass <= HexaPDF::Dictionary ? klass.type : nil)
319
319
  else
320
- if data.value.kind_of?(Hash)
321
- type ||= deref(data.value[:Type])
322
- subtype ||= deref(data.value[:Subtype])
323
- end
320
+ type ||= deref(data.value[:Type]) if data.value.kind_of?(Hash)
321
+ klass = GlobalConfiguration.constantize('object.type_map'.freeze, type) { nil } if type
322
+ end
324
323
 
325
- if subtype
326
- klass = GlobalConfiguration.constantize('object.subtype_map'.freeze, subtype) { nil }
327
- end
328
- if type && !klass
329
- klass = GlobalConfiguration.constantize('object.type_map'.freeze, type) { nil }
330
- end
331
- klass ||= if data.stream
332
- HexaPDF::Stream
333
- elsif data.value.kind_of?(Hash)
334
- HexaPDF::Dictionary
335
- else
336
- HexaPDF::Object
337
- end
324
+ if data.value.kind_of?(Hash)
325
+ subtype ||= deref(data.value[:Subtype]) || deref(data.value[:S])
326
+ end
327
+ if subtype
328
+ klass = GlobalConfiguration.constantize('object.subtype_map'.freeze, type, subtype) { klass }
338
329
  end
339
330
 
331
+ klass ||= if data.stream
332
+ HexaPDF::Stream
333
+ elsif data.value.kind_of?(Hash)
334
+ HexaPDF::Dictionary
335
+ else
336
+ HexaPDF::Object
337
+ end
338
+
340
339
  klass.new(data, document: self)
341
340
  end
342
341
 
@@ -478,7 +477,7 @@ module HexaPDF
478
477
  #
479
478
  # See Task for more information.
480
479
  def task(name, **opts, &block)
481
- task = GlobalConfiguration.constantize('task.map'.freeze, name) do
480
+ task = config.constantize('task.map'.freeze, name) do
482
481
  raise HexaPDF::Error, "No task named '#{name}' is available"
483
482
  end
484
483
  task.call(self, **opts, &block)
@@ -48,13 +48,13 @@ module HexaPDF
48
48
  end
49
49
 
50
50
  # :call-seq:
51
- # fonts.load(name, **options) -> font
51
+ # fonts.add(name, **options) -> font
52
52
  #
53
- # Loads and returns the font (using the loaders specified with the configuration option
54
- # 'font_loaders').
53
+ # Adds the font to the document and returns if (using the loaders specified with the
54
+ # configuration option 'font_loaders').
55
55
  #
56
56
  # If a font with the same parameters has been loaded before, the cached font object is used.
57
- def load(name, **options)
57
+ def add(name, **options)
58
58
  options[:variant] ||= :none # assign default value for consistency with caching
59
59
  font = @loaded_fonts_cache[[name, options]]
60
60
  return font if font
@@ -101,8 +101,8 @@ module HexaPDF
101
101
  # Returns the image loader (see HexaPDF::ImageLoader) for the given file or IO stream or
102
102
  # raises an error if no suitable image loader is found.
103
103
  def image_loader_for(file_or_io)
104
- GlobalConfiguration['image_loader'].each_index do |index|
105
- loader = GlobalConfiguration.constantize('image_loader', index) do
104
+ @document.config['image_loader'].each_index do |index|
105
+ loader = @document.config.constantize('image_loader', index) do
106
106
  raise HexaPDF::Error, "Couldn't retrieve image loader from configuration"
107
107
  end
108
108
  return loader if loader.handles?(file_or_io)
@@ -55,27 +55,27 @@ module HexaPDF
55
55
  end
56
56
 
57
57
  # :call-seq:
58
- # pages.add -> new_page
59
- # pages.add(media_box) -> new_page
60
- # pages.add(page) -> page
58
+ # pages.add -> new_page
59
+ # pages.add(media_box, orientation: :portrait) -> new_page
60
+ # pages.add(page) -> page
61
61
  #
62
62
  # Adds the page or a new empty page at the end and returns it.
63
63
  #
64
64
  # If no argument is given, a new page with the default dimensions (see configuration option
65
- # 'page.default_media_box') is used. If the single argument is an array with four numbers
66
- # (specifying the media box) or a symbol (referencing a pre-defined media box, see
67
- # HexaPDF::Type::Page::PAPER_SIZE), the new page will have these dimensions.
68
- def add(page = nil)
69
- case page
70
- when Array
65
+ # 'page.default_media_box') is used.
66
+ #
67
+ # If the single argument is an array with four numbers (specifying the media box), the new
68
+ # page will have these dimensions.
69
+ #
70
+ # If the single argument is a symbol, it is taken as referencing a pre-defined media box in
71
+ # HexaPDF::Type::Page::PAPER_SIZE for the new page. The optional argument +orientation+ can be
72
+ # used to change the orientation to :landscape if needed.
73
+ def add(page = nil, orientation: :portrait)
74
+ if page.kind_of?(Array)
71
75
  page = @document.add(Type: :Page, MediaBox: page)
72
- when Symbol
73
- if Type::Page::PAPER_SIZE.key?(page)
74
- media_box = Type::Page::PAPER_SIZE[page].dup
75
- page = @document.add(Type: :Page, MediaBox: media_box)
76
- else
77
- raise HexaPDF::Error, "Invalid page format specified: #{page}"
78
- end
76
+ elsif page.kind_of?(Symbol)
77
+ box = Type::Page.media_box(page, orientation: orientation)
78
+ page = @document.add(Type: :Page, MediaBox: box)
79
79
  end
80
80
  @document.catalog.pages.add_page(page)
81
81
  end