hexapdf 0.5.0 → 0.6.0

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