hexapdf 0.32.2 → 0.34.0

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