hexapdf 0.32.2 → 0.34.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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -1
  3. data/README.md +9 -0
  4. data/examples/002-graphics.rb +15 -17
  5. data/examples/003-arcs.rb +9 -9
  6. data/examples/009-text_layouter_alignment.rb +1 -1
  7. data/examples/010-text_layouter_inline_boxes.rb +2 -2
  8. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  9. data/examples/012-text_layouter_styling.rb +7 -7
  10. data/examples/013-text_layouter_shapes.rb +1 -1
  11. data/examples/014-text_in_polygon.rb +1 -1
  12. data/examples/015-boxes.rb +8 -7
  13. data/examples/016-frame_automatic_box_placement.rb +2 -2
  14. data/examples/017-frame_text_flow.rb +2 -1
  15. data/examples/018-composer.rb +1 -1
  16. data/examples/020-column_box.rb +2 -1
  17. data/examples/025-table_box.rb +46 -0
  18. data/examples/026-optional_content.rb +55 -0
  19. data/examples/027-composer_optional_content.rb +83 -0
  20. data/lib/hexapdf/cli/command.rb +12 -3
  21. data/lib/hexapdf/cli/fonts.rb +1 -1
  22. data/lib/hexapdf/cli/form.rb +5 -5
  23. data/lib/hexapdf/cli/inspect.rb +5 -7
  24. data/lib/hexapdf/composer.rb +106 -53
  25. data/lib/hexapdf/configuration.rb +65 -40
  26. data/lib/hexapdf/content/canvas.rb +445 -267
  27. data/lib/hexapdf/content/color_space.rb +72 -25
  28. data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
  31. data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
  32. data/lib/hexapdf/content/graphic_object.rb +6 -7
  33. data/lib/hexapdf/content/graphics_state.rb +54 -45
  34. data/lib/hexapdf/content/operator.rb +54 -54
  35. data/lib/hexapdf/content/parser.rb +2 -2
  36. data/lib/hexapdf/content/processor.rb +15 -15
  37. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  38. data/lib/hexapdf/content.rb +5 -0
  39. data/lib/hexapdf/dictionary.rb +7 -5
  40. data/lib/hexapdf/dictionary_fields.rb +43 -16
  41. data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
  42. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  43. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
  44. data/lib/hexapdf/digital_signature/signature.rb +6 -6
  45. data/lib/hexapdf/digital_signature/signatures.rb +13 -12
  46. data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
  47. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
  48. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
  49. data/lib/hexapdf/digital_signature/signing.rb +4 -0
  50. data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
  51. data/lib/hexapdf/digital_signature.rb +7 -2
  52. data/lib/hexapdf/document/destinations.rb +12 -11
  53. data/lib/hexapdf/document/files.rb +1 -1
  54. data/lib/hexapdf/document/fonts.rb +1 -1
  55. data/lib/hexapdf/document/layout.rb +170 -39
  56. data/lib/hexapdf/document/pages.rb +4 -3
  57. data/lib/hexapdf/document.rb +96 -55
  58. data/lib/hexapdf/encryption/aes.rb +5 -5
  59. data/lib/hexapdf/encryption/arc4.rb +1 -1
  60. data/lib/hexapdf/encryption/fast_aes.rb +2 -2
  61. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  62. data/lib/hexapdf/encryption/identity.rb +1 -1
  63. data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
  64. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  65. data/lib/hexapdf/encryption/security_handler.rb +31 -24
  66. data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
  67. data/lib/hexapdf/encryption.rb +7 -2
  68. data/lib/hexapdf/error.rb +18 -0
  69. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  70. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  71. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  72. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  73. data/lib/hexapdf/filter/pass_through.rb +1 -1
  74. data/lib/hexapdf/filter/predictor.rb +1 -1
  75. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  76. data/lib/hexapdf/filter.rb +55 -6
  77. data/lib/hexapdf/font/cmap/parser.rb +2 -2
  78. data/lib/hexapdf/font/cmap.rb +1 -1
  79. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  80. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  81. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
  82. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  83. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  84. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
  85. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  86. data/lib/hexapdf/font/invalid_glyph.rb +3 -0
  87. data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
  88. data/lib/hexapdf/font/type1_wrapper.rb +19 -4
  89. data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
  90. data/lib/hexapdf/font_loader/from_file.rb +5 -5
  91. data/lib/hexapdf/font_loader/standard14.rb +3 -3
  92. data/lib/hexapdf/font_loader.rb +3 -0
  93. data/lib/hexapdf/image_loader/jpeg.rb +2 -2
  94. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  95. data/lib/hexapdf/image_loader/png.rb +2 -2
  96. data/lib/hexapdf/image_loader.rb +1 -1
  97. data/lib/hexapdf/importer.rb +13 -0
  98. data/lib/hexapdf/layout/box.rb +32 -5
  99. data/lib/hexapdf/layout/box_fitter.rb +2 -2
  100. data/lib/hexapdf/layout/column_box.rb +20 -5
  101. data/lib/hexapdf/layout/frame.rb +53 -18
  102. data/lib/hexapdf/layout/image_box.rb +5 -0
  103. data/lib/hexapdf/layout/inline_box.rb +21 -9
  104. data/lib/hexapdf/layout/list_box.rb +50 -20
  105. data/lib/hexapdf/layout/page_style.rb +6 -5
  106. data/lib/hexapdf/layout/style.rb +64 -9
  107. data/lib/hexapdf/layout/table_box.rb +684 -0
  108. data/lib/hexapdf/layout/text_box.rb +12 -3
  109. data/lib/hexapdf/layout/text_fragment.rb +29 -3
  110. data/lib/hexapdf/layout/text_layouter.rb +32 -8
  111. data/lib/hexapdf/layout.rb +1 -0
  112. data/lib/hexapdf/name_tree_node.rb +1 -1
  113. data/lib/hexapdf/number_tree_node.rb +1 -1
  114. data/lib/hexapdf/object.rb +18 -7
  115. data/lib/hexapdf/parser.rb +7 -7
  116. data/lib/hexapdf/pdf_array.rb +1 -1
  117. data/lib/hexapdf/rectangle.rb +1 -1
  118. data/lib/hexapdf/reference.rb +1 -1
  119. data/lib/hexapdf/revision.rb +1 -1
  120. data/lib/hexapdf/revisions.rb +3 -3
  121. data/lib/hexapdf/serializer.rb +15 -15
  122. data/lib/hexapdf/stream.rb +5 -4
  123. data/lib/hexapdf/tokenizer.rb +14 -14
  124. data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
  125. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  126. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  127. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  128. data/lib/hexapdf/type/acro_form/form.rb +1 -1
  129. data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
  130. data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
  131. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  132. data/lib/hexapdf/type/acro_form.rb +1 -1
  133. data/lib/hexapdf/type/action.rb +1 -1
  134. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  135. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  136. data/lib/hexapdf/type/actions/launch.rb +1 -1
  137. data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
  138. data/lib/hexapdf/type/actions/uri.rb +1 -1
  139. data/lib/hexapdf/type/actions.rb +2 -1
  140. data/lib/hexapdf/type/annotation.rb +3 -3
  141. data/lib/hexapdf/type/annotations/link.rb +1 -1
  142. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  143. data/lib/hexapdf/type/annotations/text.rb +2 -3
  144. data/lib/hexapdf/type/annotations/widget.rb +2 -2
  145. data/lib/hexapdf/type/annotations.rb +1 -1
  146. data/lib/hexapdf/type/catalog.rb +11 -2
  147. data/lib/hexapdf/type/cid_font.rb +18 -4
  148. data/lib/hexapdf/type/embedded_file.rb +1 -1
  149. data/lib/hexapdf/type/file_specification.rb +2 -2
  150. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  151. data/lib/hexapdf/type/font_simple.rb +2 -2
  152. data/lib/hexapdf/type/font_type0.rb +3 -3
  153. data/lib/hexapdf/type/font_type3.rb +1 -1
  154. data/lib/hexapdf/type/form.rb +76 -6
  155. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  156. data/lib/hexapdf/type/icon_fit.rb +1 -1
  157. data/lib/hexapdf/type/image.rb +1 -1
  158. data/lib/hexapdf/type/info.rb +1 -1
  159. data/lib/hexapdf/type/mark_information.rb +1 -1
  160. data/lib/hexapdf/type/names.rb +2 -2
  161. data/lib/hexapdf/type/object_stream.rb +2 -1
  162. data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
  163. data/lib/hexapdf/type/optional_content_group.rb +370 -0
  164. data/lib/hexapdf/type/optional_content_membership.rb +63 -0
  165. data/lib/hexapdf/type/optional_content_properties.rb +158 -0
  166. data/lib/hexapdf/type/outline.rb +1 -1
  167. data/lib/hexapdf/type/outline_item.rb +1 -1
  168. data/lib/hexapdf/type/page.rb +46 -21
  169. data/lib/hexapdf/type/page_label.rb +5 -9
  170. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  171. data/lib/hexapdf/type/resources.rb +1 -1
  172. data/lib/hexapdf/type/trailer.rb +2 -2
  173. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  174. data/lib/hexapdf/type/xref_stream.rb +2 -2
  175. data/lib/hexapdf/type.rb +4 -0
  176. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
  177. data/lib/hexapdf/version.rb +1 -1
  178. data/lib/hexapdf/writer.rb +4 -4
  179. data/lib/hexapdf/xref_section.rb +2 -2
  180. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
  181. data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
  182. data/test/hexapdf/content/test_canvas.rb +49 -1
  183. data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
  184. data/test/hexapdf/document/test_files.rb +2 -2
  185. data/test/hexapdf/document/test_layout.rb +105 -2
  186. data/test/hexapdf/document/test_pages.rb +6 -6
  187. data/test/hexapdf/encryption/test_security_handler.rb +12 -11
  188. data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
  189. data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
  190. data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
  191. data/test/hexapdf/layout/test_box.rb +14 -5
  192. data/test/hexapdf/layout/test_column_box.rb +65 -21
  193. data/test/hexapdf/layout/test_frame.rb +27 -15
  194. data/test/hexapdf/layout/test_image_box.rb +4 -0
  195. data/test/hexapdf/layout/test_inline_box.rb +17 -3
  196. data/test/hexapdf/layout/test_list_box.rb +84 -33
  197. data/test/hexapdf/layout/test_page_style.rb +3 -2
  198. data/test/hexapdf/layout/test_style.rb +60 -0
  199. data/test/hexapdf/layout/test_table_box.rb +728 -0
  200. data/test/hexapdf/layout/test_text_box.rb +26 -0
  201. data/test/hexapdf/layout/test_text_fragment.rb +33 -0
  202. data/test/hexapdf/layout/test_text_layouter.rb +36 -5
  203. data/test/hexapdf/test_composer.rb +10 -0
  204. data/test/hexapdf/test_dictionary.rb +10 -0
  205. data/test/hexapdf/test_dictionary_fields.rb +4 -1
  206. data/test/hexapdf/test_document.rb +5 -0
  207. data/test/hexapdf/test_filter.rb +8 -0
  208. data/test/hexapdf/test_importer.rb +9 -0
  209. data/test/hexapdf/test_object.rb +16 -5
  210. data/test/hexapdf/test_stream.rb +7 -0
  211. data/test/hexapdf/test_writer.rb +3 -3
  212. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
  213. data/test/hexapdf/type/acro_form/test_form.rb +4 -3
  214. data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
  215. data/test/hexapdf/type/test_catalog.rb +11 -0
  216. data/test/hexapdf/type/test_form.rb +119 -0
  217. data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
  218. data/test/hexapdf/type/test_optional_content_group.rb +158 -0
  219. data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
  220. data/test/hexapdf/type/test_page.rb +20 -6
  221. metadata +28 -8
@@ -42,7 +42,7 @@ module HexaPDF
42
42
 
43
43
  # The MacExpertEncoding for Latin texts.
44
44
  #
45
- # See: PDF1.7 sD.4
45
+ # See: PDF2.0 sD.4
46
46
  class MacExpertEncoding < Base
47
47
 
48
48
  def initialize #:nodoc:
@@ -42,7 +42,7 @@ module HexaPDF
42
42
 
43
43
  # The Mac Roman standard encoding for Latin texts.
44
44
  #
45
- # See: PDF1.7 sD.1, sD.2
45
+ # See: PDF2.0 sD.1, sD.2
46
46
  class MacRomanEncoding < Base
47
47
 
48
48
  def initialize #:nodoc:
@@ -256,7 +256,7 @@ module HexaPDF
256
256
  0264 => :yen,
257
257
  0172 => :z,
258
258
  0060 => :zero,
259
- # additions due to PDF1.7 sD.2 footnote 6
259
+ # additions due to PDF2.0 sD.2 footnote 6
260
260
  0312 => :space,
261
261
  }
262
262
  end
@@ -42,7 +42,7 @@ module HexaPDF
42
42
 
43
43
  # The Adobe standard encoding for Latin texts.
44
44
  #
45
- # See: PDF1.7 sD.1, sD.2
45
+ # See: PDF2.0 sD.1, sD.2
46
46
  class StandardEncoding < Base
47
47
 
48
48
  def initialize #:nodoc:
@@ -42,7 +42,7 @@ module HexaPDF
42
42
 
43
43
  # The built-in encoding of the Symbol font.
44
44
  #
45
- # See: PDF1.7 sD.5
45
+ # See: PDF2.0 sD.5
46
46
  class SymbolEncoding < Base
47
47
 
48
48
  def initialize #:nodoc:
@@ -42,7 +42,7 @@ module HexaPDF
42
42
 
43
43
  # The Windows Code Page 1252, the standard Windows encoding for Latin texts.
44
44
  #
45
- # See: PDF1.7 sD.1, sD.2
45
+ # See: PDF2.0 sD.1, sD.2
46
46
  class WinAnsiEncoding < Base
47
47
 
48
48
  def initialize #:nodoc:
@@ -265,11 +265,11 @@ module HexaPDF
265
265
  0172 => :z,
266
266
  0236 => :zcaron,
267
267
  0060 => :zero,
268
- # additions due to PDF1.7 sD.2 footnote 5,6
268
+ # additions due to PDF2.0 sD.2 footnote 5,6
269
269
  0240 => :space,
270
270
  0255 => :hyphen,
271
271
  }
272
- # additions due to PDF1.7 sD.2 footnote 3
272
+ # additions due to PDF2.0 sD.2 footnote 3
273
273
  041.upto(255) do |i|
274
274
  next if @code_to_name.key?(i)
275
275
  @code_to_name[i] = :bullet
@@ -42,7 +42,7 @@ module HexaPDF
42
42
 
43
43
  # The built-in encoding of the ZapfDingbats font.
44
44
  #
45
- # See: PDF1.7 sD.6
45
+ # See: PDF2.0 sD.6
46
46
  class ZapfDingbatsEncoding < Base
47
47
 
48
48
  def initialize #:nodoc:
@@ -41,6 +41,9 @@ module HexaPDF
41
41
  # font.
42
42
  class InvalidGlyph
43
43
 
44
+ # The associated font object.
45
+ attr_reader :font
46
+
44
47
  # The string that could not be represented as a glyph.
45
48
  attr_reader :str
46
49
 
@@ -51,7 +51,7 @@ module HexaPDF
51
51
  #
52
52
  # * By using a composite font more than 256 characters can be encoded with one font object.
53
53
  # * Fonts for vertical writing can potentially be used.
54
- # * The PDF specification recommends using a composite font (see PDF1.7 s9.9 at the end).
54
+ # * The PDF specification recommends using a composite font (see PDF2.0 s9.9.1 at the end).
55
55
  #
56
56
  # Additionally, TrueType fonts are *always* embedded.
57
57
  class TrueTypeWrapper
@@ -59,6 +59,9 @@ module HexaPDF
59
59
  # Represents a single glyph of the wrapped font.
60
60
  class Glyph
61
61
 
62
+ # The associated font object.
63
+ attr_reader :font
64
+
62
65
  # The glyph ID.
63
66
  attr_reader :id
64
67
 
@@ -171,6 +174,18 @@ module HexaPDF
171
174
  end
172
175
  end
173
176
 
177
+ # Returns a custom Glyph object which represents the given +string+ via the given glyph +id+.
178
+ #
179
+ # This functionality can be used to associate a single glyph id with multiple, different
180
+ # strings for replacement glyph purposes. When used in such a way, the used glyph id is often
181
+ # 0 which represents the missing glyph.
182
+ def custom_glyph(id, string)
183
+ if id < 0 || id >= @wrapped_font[:maxp].num_glyphs
184
+ raise HexaPDF::Error, "Glyph ID #{id} is invalid for font '#{@wrapped_font.full_name}'"
185
+ end
186
+ Glyph.new(@wrapped_font, id, string)
187
+ end
188
+
174
189
  # Returns an array of glyph objects representing the characters in the UTF-8 encoded string.
175
190
  def decode_utf8(str)
176
191
  str.codepoints.map! do |c|
@@ -187,9 +202,7 @@ module HexaPDF
187
202
  def encode(glyph)
188
203
  (@encoded_glyphs[glyph.id] ||=
189
204
  begin
190
- if glyph.kind_of?(InvalidGlyph)
191
- raise HexaPDF::Error, "Glyph for #{glyph.str.inspect} missing"
192
- end
205
+ raise HexaPDF::MissingGlyphError.new(glyph) if glyph.kind_of?(InvalidGlyph)
193
206
  if @subsetter
194
207
  [[@subsetter.use_glyph(glyph.id)].pack('n'), glyph]
195
208
  else
@@ -48,6 +48,9 @@ module HexaPDF
48
48
  # Represents a single glyph of the wrapped font.
49
49
  class Glyph
50
50
 
51
+ # The associated font object.
52
+ attr_reader :font
53
+
51
54
  # The name of the glyph.
52
55
  attr_reader :name
53
56
  alias id name
@@ -164,6 +167,20 @@ module HexaPDF
164
167
  end
165
168
  end
166
169
 
170
+ # Returns a custom Glyph object which represents the given +string+ via the given glyph
171
+ # +name+.
172
+ #
173
+ # This functionality can be used to associate a single glyph name with multiple, different
174
+ # strings for replacement glyph purposes. When used in such a way, the used glyph name is
175
+ # often :question.
176
+ def custom_glyph(name, string)
177
+ unless @wrapped_font.metrics.character_metrics.key?(name)
178
+ raise HexaPDF::Error, "Glyph named #{name.inspect} not found in " \
179
+ "font '#{@wrapped_font.full_name}'"
180
+ end
181
+ Glyph.new(@wrapped_font, name, string)
182
+ end
183
+
167
184
  # Returns an array of glyph objects representing the characters in the UTF-8 encoded string.
168
185
  #
169
186
  # If a Unicode codepoint is not available as glyph object, it is tried to map the codepoint
@@ -188,9 +205,7 @@ module HexaPDF
188
205
  def encode(glyph)
189
206
  @encoded_glyphs[glyph.name] ||=
190
207
  begin
191
- if glyph.name == @wrapped_font.missing_glyph_id
192
- raise HexaPDF::Error, "Glyph for #{glyph.str.inspect} missing"
193
- end
208
+ raise HexaPDF::MissingGlyphError.new(glyph) if glyph.kind_of?(InvalidGlyph)
194
209
  code = @encoding.code(glyph.name)
195
210
  if code
196
211
  code.chr.freeze
@@ -199,7 +214,7 @@ module HexaPDF
199
214
  @encoding.code_to_name[@max_code] = glyph.name
200
215
  @max_code.chr.freeze
201
216
  else
202
- raise HexaPDF::Error, "Type1 encoding has no codepoint for #{glyph.name}"
217
+ raise HexaPDF::Error, "Used Type1 encoding has no codepoint for #{glyph.name.inspect}"
203
218
  end
204
219
  end
205
220
  end
@@ -43,13 +43,14 @@ module HexaPDF
43
43
  # This module uses the configuration option 'font.map' for loading a font.
44
44
  module FromConfiguration
45
45
 
46
- # Loads the given font by looking up the needed file in the 'font.map' configuration option.
46
+ # Returns a TrueType font wrapper for the given font by looking up the needed file in the
47
+ # 'font.map' configuration option.
47
48
  #
48
49
  # The file object representing the font file is *not* closed and if needed must be closed by
49
50
  # the caller once the font is not needed anymore.
50
51
  #
51
52
  # +document+::
52
- # The PDF document to associate the font object with.
53
+ # The PDF document to associate the font wrapper with.
53
54
  #
54
55
  # +name+::
55
56
  # The name of the font.
@@ -59,6 +60,8 @@ module HexaPDF
59
60
  #
60
61
  # +subset+::
61
62
  # Specifies whether the font should be subset if possible.
63
+ #
64
+ # This method uses the FromFile font loader behind the scenes.
62
65
  def self.call(document, name, variant: :none, subset: true)
63
66
  file = document.config['font.map'].dig(name, variant)
64
67
  return nil if file.nil?
@@ -39,15 +39,15 @@ require 'hexapdf/font/true_type_wrapper'
39
39
  module HexaPDF
40
40
  module FontLoader
41
41
 
42
- # This module interprets the font name either as file name and tries to load it, or as font
43
- # object to be wrapped directly.
42
+ # This module interprets the font name either as file name and tries to load it, or as TrueType
43
+ # font object to be wrapped directly.
44
44
  module FromFile
45
45
 
46
46
  # :call-seq:
47
47
  # FromFile.call(document, file_name, subset: true, **) -> wrapped_font
48
48
  # FromFile.call(document, font_object, subset: true, **) -> wrapped_font
49
49
  #
50
- # Returns an appropriate font wrapper for the given file name or font object.
50
+ # Returns an appropriate font wrapper for the given file name or TrueType font object.
51
51
  #
52
52
  # If a file name is given, the file object representing the font file is *not* closed and if
53
53
  # needed must be closed by the caller once the font is not needed anymore.
@@ -57,10 +57,10 @@ module HexaPDF
57
57
  # font file.
58
58
  #
59
59
  # +document+::
60
- # The PDF document to associate the font object with.
60
+ # The PDF document to associate the font wrapper with.
61
61
  #
62
62
  # +file_name+/+font_object+::
63
- # The file name or TrueType font object.
63
+ # The file name or a HexaPDF::Font::TrueType::Font object.
64
64
  #
65
65
  # +subset+::
66
66
  # Specifies whether the font should be subset if possible.
@@ -71,10 +71,10 @@ module HexaPDF
71
71
  },
72
72
  }.freeze
73
73
 
74
- # Creates a new font object backed by the AFM font metrics read from the file or IO stream.
74
+ # Returns a font wrapper for the named Standard PDF font.
75
75
  #
76
76
  # +document+::
77
- # The PDF document to associate the font object with.
77
+ # The PDF document to associate the font wrapper with.
78
78
  #
79
79
  # +name+::
80
80
  # The name of the built-in font. One of Times, Helvetica, Courier, Symbol or ZapfDingbats.
@@ -85,7 +85,7 @@ module HexaPDF
85
85
  #
86
86
  # +custom_encoding+::
87
87
  # For Times, Helvetica and Courier the standard encoding WinAnsiEncoding is used. If this
88
- # option is not wanted because access to other glyphs is needed, set this to +true+
88
+ # is not wanted because access to other glyphs is needed, set this to +true+
89
89
  def self.call(document, name, variant: :none, custom_encoding: false, **)
90
90
  name = MAPPING[name] && MAPPING[name][variant]
91
91
  return nil if name.nil?
@@ -63,6 +63,7 @@ module HexaPDF
63
63
  # Optionally, a font loader can provide a method +available_fonts(document)+ that returns a hash
64
64
  # where the keys are the font names and the values are the variants of all the provided fonts.
65
65
  #
66
+ #
66
67
  # == Font Wrappers
67
68
  #
68
69
  # A font wrapper needs to provide the following generic interface so that it can be used correctly
@@ -80,6 +81,8 @@ module HexaPDF
80
81
  # and returns an encoded string that can be decoded with the font dictionary returned by
81
82
  # \#dict.
82
83
  #
84
+ # HexaPDF contains a font wrapper implementation for the Standard 14 PDF fonts (see
85
+ # HexaPDF::Font::Type1Wrapper) and one for TrueType fonts (see HexaPDF::Font::TrueTypeWrapper).
83
86
  module FontLoader
84
87
 
85
88
  autoload(:Standard14, 'hexapdf/font_loader/standard14')
@@ -41,7 +41,7 @@ module HexaPDF
41
41
 
42
42
  # This module is used for loading images in the JPEG format from files or IO streams.
43
43
  #
44
- # See: PDF1.7 s7.4.8, ITU T.81 Annex B, ITU T.872
44
+ # See: PDF2.0 s7.4.8, ITU T.81 Annex B, ITU T.872
45
45
  module JPEG
46
46
 
47
47
  # The magic marker that tells us if the file/IO contains an image in JPEG format.
@@ -139,7 +139,7 @@ module HexaPDF
139
139
  break if components != 4 || invert_colors
140
140
  end
141
141
 
142
- # PDF1.7 s8.9.5.1
142
+ # PDF2.0 s8.9.5.1
143
143
  if bits != 8
144
144
  raise HexaPDF::Error, "Unsupported number of bits per component: #{bits}"
145
145
  end
@@ -46,7 +46,7 @@ module HexaPDF
46
46
  # image/xobject drawing methods of HexaPDF::Content::Canvas know how to handle them correctly so
47
47
  # that this doesn't matter from a user's point of view.
48
48
  #
49
- # See: PDF1.7 s8.10
49
+ # See: PDF2.0 s8.10
50
50
  module PDF
51
51
 
52
52
  # The magic marker that tells us if the file/IO contains an PDF file.
@@ -52,7 +52,7 @@ module HexaPDF
52
52
  #
53
53
  # All PNG specification section references are in reference to http://www.w3.org/TR/PNG/.
54
54
  #
55
- # See: PDF1.7 s7.4.4., s8.9
55
+ # See: PDF2.0 s7.4.4., s8.9
56
56
  class PNG
57
57
 
58
58
  # The magic marker that tells us if the file/IO contains an image in PNG format.
@@ -261,7 +261,7 @@ module HexaPDF
261
261
  # Returns a hash for a CalRGB color space definition using the x,y chromaticity coordinates
262
262
  # of the white point and the red, green and blue primaries.
263
263
  #
264
- # See: PDF1.7 s8.6.5.3
264
+ # See: PDF2.0 s8.6.5.3
265
265
  def calrgb_definition_from_chrm(xw, yw, xr, yr, xg, yg, xb, yb)
266
266
  z = yw * ((xg - xb) * yr - (xr - xb) * yg + (xr - xg) * yb)
267
267
 
@@ -59,7 +59,7 @@ module HexaPDF
59
59
  # The image XObject may use any implemented filter. For example, an image loader for JPEG files
60
60
  # would typically use the DCTDecode filter instead of decoding the image itself.
61
61
  #
62
- # See: PDF1.7 s8.9
62
+ # See: PDF2.0 s8.9
63
63
  module ImageLoader
64
64
 
65
65
  autoload(:JPEG, 'hexapdf/image_loader/jpeg')
@@ -68,6 +68,19 @@ module HexaPDF
68
68
  @map[destination.hash] ||= new(destination)
69
69
  end
70
70
 
71
+ # Imports the given +object+ (belonging to the +source+ document) by completely copying it and
72
+ # all referenced objects into the +destination+ object.
73
+ #
74
+ # Specifying +source+ is optionial if it can be determined through +object+.
75
+ #
76
+ # After the operation is finished, all state is discarded. This means that another call to this
77
+ # method for the same object will yield a new - and different - object. This is in contrast to
78
+ # using ::for together with #import which remembers and returns already imported objects (which
79
+ # is generally what one wants).
80
+ def self.copy(destination, object, source: nil)
81
+ new(NullableWeakRef.new(destination)).import(object, source: source)
82
+ end
83
+
71
84
  private_class_method :new
72
85
 
73
86
  attr_reader :destination #:nodoc:
@@ -60,18 +60,25 @@ module HexaPDF
60
60
  # instantiated from the common convenience method HexaPDF::Document::Layout#box. To use this
61
61
  # facility subclasses need to be registered with the configuration option 'layout.boxes.map'.
62
62
  #
63
- # The methods #fit, #split or #split_content, and #draw or #draw_content need to be customized
64
- # according to the subclass's use case.
63
+ # The methods #fit, #supports_position_flow?, #split or #split_content, #empty?, and #draw or
64
+ # #draw_content need to be customized according to the subclass's use case.
65
65
  #
66
66
  # #fit:: This method should return +true+ if fitting was successful. Additionally, the
67
67
  # @fit_successful instance variable needs to be set to the fit result as it is used in
68
68
  # #split.
69
69
  #
70
+ # #supports_position_flow?::
71
+ # If the subclass supports the value :flow of the 'position' style property, this method
72
+ # needs to be overridden to return +true+.
73
+ #
70
74
  # #split:: This method splits the content so that the available space is used as good as
71
75
  # possible. The default implementation should be fine for most use-cases, so only
72
76
  # #split_content needs to be implemented. The method #create_split_box should be used
73
77
  # for getting a basic cloned box.
74
78
  #
79
+ # #empty?:: This method should return +true+ if the subclass won't draw anything when #draw is
80
+ # called.
81
+ #
75
82
  # #draw:: This method draws the content and the default implementation already handles things
76
83
  # like drawing the border and background. Therefore it's best to implement #draw_content
77
84
  # which should just draw the content.
@@ -120,10 +127,24 @@ module HexaPDF
120
127
  #
121
128
  # This can be used to store arbitrary information on boxes for later use. For example, a
122
129
  # generic style layer could use one or more custom properties for its work.
130
+ #
131
+ # The Box class itself uses the following properties:
132
+ #
133
+ # optional_content::
134
+ #
135
+ # If this property is set, it needs to be an optional content group dictionary, a String
136
+ # defining an (optionally existing) optional content group dictionary, or an optional
137
+ # content membership dictionary.
138
+ #
139
+ # The whole content of the box, i.e. including padding, border, background..., is
140
+ # wrapped with the appropriate commands so that the optional content group or membership
141
+ # dictionary specifies whether the content is shown or not.
142
+ #
143
+ # See: HexaPDF::Type::OptionalContentProperties
123
144
  attr_reader :properties
124
145
 
125
146
  # :call-seq:
126
- # Box.new(width: 0, height: 0, style: nil, properties: {}) {|canv, box| block} -> box
147
+ # Box.new(width: 0, height: 0, style: nil, properties: nil) {|canv, box| block} -> box
127
148
  #
128
149
  # Creates a new Box object with the given width and height that uses the provided block when
129
150
  # it is asked to draw itself on a canvas (see #draw).
@@ -131,11 +152,11 @@ module HexaPDF
131
152
  # Since the final location of the box is not known beforehand, the drawing operations inside
132
153
  # the block should draw inside the rectangle (0, 0, content_width, content_height) - note that
133
154
  # the width and height of the box may not be known beforehand.
134
- def initialize(width: 0, height: 0, style: nil, properties: {}, &block)
155
+ def initialize(width: 0, height: 0, style: nil, properties: nil, &block)
135
156
  @width = @initial_width = width
136
157
  @height = @initial_height = height
137
158
  @style = Style.create(style)
138
- @properties = properties
159
+ @properties = properties || {}
139
160
  @draw_block = block
140
161
  @fit_successful = false
141
162
  @split_box = false
@@ -213,6 +234,10 @@ module HexaPDF
213
234
  # instance variable to +nil+ or a valid block. This is useful to avoid unnecessary set-up
214
235
  # operations when the block does nothing.
215
236
  def draw(canvas, x, y)
237
+ if (oc = properties['optional_content'])
238
+ canvas.optional_content(oc)
239
+ end
240
+
216
241
  if style.background_color? && style.background_color
217
242
  canvas.save_graphics_state do
218
243
  canvas.opacity(fill_alpha: style.background_alpha).
@@ -226,6 +251,8 @@ module HexaPDF
226
251
  draw_content(canvas, x + reserved_width_left, y + reserved_height_bottom)
227
252
 
228
253
  style.overlays.draw(canvas, x, y, self) if style.overlays?
254
+
255
+ canvas.end_optional_content if oc
229
256
  end
230
257
 
231
258
  # Returns +true+ if no drawing operations are performed.
@@ -98,7 +98,7 @@ module HexaPDF
98
98
  if result.success?
99
99
  current_frame.remove_area(result.mask)
100
100
  @content_heights[@frame_index] = [@content_heights[@frame_index],
101
- @initial_frame_y[@frame_index] - result.mask[0].y].max
101
+ @initial_frame_y[@frame_index] - result.mask.y].max
102
102
  @fit_results << result
103
103
  box = nil
104
104
  break
@@ -109,7 +109,7 @@ module HexaPDF
109
109
  if draw_box
110
110
  current_frame.remove_area(result.mask)
111
111
  @content_heights[@frame_index] = [@content_heights[@frame_index],
112
- @initial_frame_y[@frame_index] - result.mask[0].y].max
112
+ @initial_frame_y[@frame_index] - result.mask.y].max
113
113
  @fit_results << result
114
114
  elsif !current_frame.find_next_region
115
115
  @frame_index += 1
@@ -62,8 +62,9 @@ module HexaPDF
62
62
 
63
63
  # The columns definition.
64
64
  #
65
- # This is an array containing the widths of the columns. The size of the array is the number
66
- # of columns.
65
+ # If the value is an array, it needs to contain the widths of the columns. The size of the
66
+ # array determines the number of columns. Otherwise, if the value is an integer, the value
67
+ # defines the number of equally sized columns, i.e. a value of +N+ is equal to [-1]*N.
67
68
  #
68
69
  # If a negative integer is used for the width, the column is auto-sized. Such columns split
69
70
  # the remaining width (after substracting the widths of the fixed columns) proportionally
@@ -132,6 +133,11 @@ module HexaPDF
132
133
  true
133
134
  end
134
135
 
136
+ # Returns +true+ if no box was fitted into the columns.
137
+ def empty?
138
+ super && (!@box_fitter || @box_fitter.fit_results.empty?)
139
+ end
140
+
135
141
  # Fits the column box into the available space.
136
142
  #
137
143
  # If the style property 'position' is set to :flow, the columns might not be rectangles but
@@ -171,7 +177,8 @@ module HexaPDF
171
177
  [column_left, column_bottom + height])
172
178
  shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, rect, :intersection)
173
179
  end
174
- column_frame = Frame.new(column_left, column_bottom, column_width, height, shape: shape)
180
+ column_frame = Frame.new(column_left, column_bottom, column_width, height,
181
+ shape: shape, context: frame.context)
175
182
  @box_fitter << column_frame
176
183
  end
177
184
 
@@ -199,6 +206,8 @@ module HexaPDF
199
206
 
200
207
  @width = columns[-1].sum + reserved_width
201
208
  @height = @box_fitter.content_heights.max + reserved_height
209
+ @draw_pos_x = frame.x + reserved_width_left
210
+ @draw_pos_y = frame.y - @height + reserved_height_bottom
202
211
 
203
212
  @box_fitter.fit_successful?
204
213
  end
@@ -237,8 +246,14 @@ module HexaPDF
237
246
  end
238
247
 
239
248
  # Draws the child boxes onto the canvas at position [x, y].
240
- def draw_content(canvas, _x, _y)
241
- @box_fitter.fit_results.each {|result| result.draw(canvas) }
249
+ def draw_content(canvas, x, y)
250
+ if style.position != :flow && (x != @draw_pos_x || y != @draw_pos_y)
251
+ canvas.translate(x - @draw_pos_x, y - @draw_pos_y) do
252
+ @box_fitter.fit_results.each {|result| result.draw(canvas) }
253
+ end
254
+ else
255
+ @box_fitter.fit_results.each {|result| result.draw(canvas) }
256
+ end
242
257
  end
243
258
 
244
259
  end