hexapdf 0.11.9 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -0
  3. data/LICENSE +1 -1
  4. data/examples/001-hello_world.rb +1 -1
  5. data/examples/002-graphics.rb +1 -1
  6. data/examples/003-arcs.rb +1 -1
  7. data/examples/004-optimizing.rb +1 -1
  8. data/examples/005-merging.rb +1 -1
  9. data/examples/006-standard_pdf_fonts.rb +1 -1
  10. data/examples/007-truetype.rb +1 -1
  11. data/examples/008-show_char_bboxes.rb +1 -1
  12. data/examples/009-text_layouter_alignment.rb +1 -1
  13. data/examples/010-text_layouter_inline_boxes.rb +1 -1
  14. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  15. data/examples/012-text_layouter_styling.rb +1 -1
  16. data/examples/013-text_layouter_shapes.rb +1 -1
  17. data/examples/014-text_in_polygon.rb +1 -1
  18. data/examples/015-boxes.rb +1 -1
  19. data/examples/016-frame_automatic_box_placement.rb +1 -1
  20. data/examples/017-frame_text_flow.rb +1 -1
  21. data/examples/018-composer.rb +1 -1
  22. data/examples/019-acro_form.rb +51 -0
  23. data/lib/hexapdf.rb +1 -1
  24. data/lib/hexapdf/cli.rb +3 -1
  25. data/lib/hexapdf/cli/batch.rb +1 -1
  26. data/lib/hexapdf/cli/command.rb +22 -11
  27. data/lib/hexapdf/cli/files.rb +1 -1
  28. data/lib/hexapdf/cli/form.rb +240 -0
  29. data/lib/hexapdf/cli/image2pdf.rb +3 -2
  30. data/lib/hexapdf/cli/images.rb +1 -1
  31. data/lib/hexapdf/cli/info.rb +52 -3
  32. data/lib/hexapdf/cli/inspect.rb +31 -9
  33. data/lib/hexapdf/cli/merge.rb +2 -2
  34. data/lib/hexapdf/cli/modify.rb +1 -1
  35. data/lib/hexapdf/cli/optimize.rb +1 -1
  36. data/lib/hexapdf/cli/split.rb +1 -1
  37. data/lib/hexapdf/cli/watermark.rb +1 -1
  38. data/lib/hexapdf/composer.rb +2 -2
  39. data/lib/hexapdf/configuration.rb +81 -11
  40. data/lib/hexapdf/content.rb +3 -1
  41. data/lib/hexapdf/content/canvas.rb +5 -18
  42. data/lib/hexapdf/content/color_space.rb +111 -32
  43. data/lib/hexapdf/content/graphic_object.rb +1 -1
  44. data/lib/hexapdf/content/graphic_object/arc.rb +4 -4
  45. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  46. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  47. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  48. data/lib/hexapdf/content/graphics_state.rb +1 -1
  49. data/lib/hexapdf/content/operator.rb +9 -9
  50. data/lib/hexapdf/content/parser.rb +18 -5
  51. data/lib/hexapdf/content/processor.rb +1 -1
  52. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  53. data/lib/hexapdf/data_dir.rb +1 -1
  54. data/lib/hexapdf/dictionary.rb +5 -5
  55. data/lib/hexapdf/dictionary_fields.rb +2 -10
  56. data/lib/hexapdf/document.rb +45 -17
  57. data/lib/hexapdf/document/files.rb +1 -2
  58. data/lib/hexapdf/document/fonts.rb +1 -1
  59. data/lib/hexapdf/document/images.rb +1 -1
  60. data/lib/hexapdf/document/pages.rb +3 -14
  61. data/lib/hexapdf/encryption.rb +1 -1
  62. data/lib/hexapdf/encryption/aes.rb +1 -1
  63. data/lib/hexapdf/encryption/arc4.rb +1 -1
  64. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  65. data/lib/hexapdf/encryption/fast_arc4.rb +2 -2
  66. data/lib/hexapdf/encryption/identity.rb +1 -1
  67. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  68. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  69. data/lib/hexapdf/encryption/security_handler.rb +2 -1
  70. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -1
  71. data/lib/hexapdf/error.rb +1 -1
  72. data/lib/hexapdf/filter.rb +3 -3
  73. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  74. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  75. data/lib/hexapdf/filter/encryption.rb +1 -1
  76. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  77. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  78. data/lib/hexapdf/filter/{jpx_decode.rb → pass_through.rb} +5 -5
  79. data/lib/hexapdf/filter/predictor.rb +1 -1
  80. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  81. data/lib/hexapdf/font/cmap.rb +2 -5
  82. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  83. data/lib/hexapdf/font/cmap/writer.rb +1 -1
  84. data/lib/hexapdf/font/encoding.rb +1 -1
  85. data/lib/hexapdf/font/encoding/base.rb +9 -1
  86. data/lib/hexapdf/font/encoding/difference_encoding.rb +7 -1
  87. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  88. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  89. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  90. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  91. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  92. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  93. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  94. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  95. data/lib/hexapdf/font/true_type.rb +1 -1
  96. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  97. data/lib/hexapdf/font/true_type/font.rb +1 -1
  98. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  99. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  100. data/lib/hexapdf/font/true_type/table.rb +1 -1
  101. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  102. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  103. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  104. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  105. data/lib/hexapdf/font/true_type/table/head.rb +2 -1
  106. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  107. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  108. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  109. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/os2.rb +3 -1
  113. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  114. data/lib/hexapdf/font/true_type_wrapper.rb +54 -51
  115. data/lib/hexapdf/font/type1.rb +1 -1
  116. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  117. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  118. data/lib/hexapdf/font/type1/font.rb +1 -1
  119. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  120. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  121. data/lib/hexapdf/font/type1_wrapper.rb +68 -52
  122. data/lib/hexapdf/font_loader.rb +1 -1
  123. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  124. data/lib/hexapdf/font_loader/from_file.rb +1 -1
  125. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  126. data/lib/hexapdf/image_loader.rb +1 -1
  127. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  128. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  129. data/lib/hexapdf/image_loader/png.rb +4 -3
  130. data/lib/hexapdf/importer.rb +2 -4
  131. data/lib/hexapdf/layout.rb +1 -1
  132. data/lib/hexapdf/layout/box.rb +1 -1
  133. data/lib/hexapdf/layout/frame.rb +1 -1
  134. data/lib/hexapdf/layout/image_box.rb +1 -1
  135. data/lib/hexapdf/layout/inline_box.rb +1 -1
  136. data/lib/hexapdf/layout/line.rb +2 -2
  137. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  138. data/lib/hexapdf/layout/style.rb +24 -24
  139. data/lib/hexapdf/layout/text_box.rb +1 -1
  140. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  141. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  142. data/lib/hexapdf/layout/text_shaper.rb +4 -3
  143. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  144. data/lib/hexapdf/name_tree_node.rb +1 -1
  145. data/lib/hexapdf/number_tree_node.rb +1 -1
  146. data/lib/hexapdf/object.rb +32 -27
  147. data/lib/hexapdf/parser.rb +69 -6
  148. data/lib/hexapdf/pdf_array.rb +10 -3
  149. data/lib/hexapdf/rectangle.rb +31 -1
  150. data/lib/hexapdf/reference.rb +1 -1
  151. data/lib/hexapdf/revision.rb +2 -1
  152. data/lib/hexapdf/revisions.rb +30 -22
  153. data/lib/hexapdf/serializer.rb +2 -2
  154. data/lib/hexapdf/stream.rb +1 -1
  155. data/lib/hexapdf/task.rb +1 -1
  156. data/lib/hexapdf/task/dereference.rb +1 -1
  157. data/lib/hexapdf/task/optimize.rb +7 -5
  158. data/lib/hexapdf/tokenizer.rb +5 -4
  159. data/lib/hexapdf/type.rb +1 -1
  160. data/lib/hexapdf/type/acro_form.rb +7 -1
  161. data/lib/hexapdf/type/acro_form/appearance_generator.rb +405 -0
  162. data/lib/hexapdf/type/acro_form/button_field.rb +305 -0
  163. data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
  164. data/lib/hexapdf/type/acro_form/field.rb +250 -17
  165. data/lib/hexapdf/type/acro_form/form.rb +159 -7
  166. data/lib/hexapdf/type/acro_form/text_field.rb +187 -0
  167. data/lib/hexapdf/type/acro_form/variable_text_field.rb +122 -0
  168. data/lib/hexapdf/type/action.rb +1 -1
  169. data/lib/hexapdf/type/actions.rb +1 -1
  170. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  171. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  172. data/lib/hexapdf/type/actions/launch.rb +1 -1
  173. data/lib/hexapdf/type/actions/uri.rb +4 -3
  174. data/lib/hexapdf/type/annotation.rb +73 -3
  175. data/lib/hexapdf/type/annotations.rb +1 -1
  176. data/lib/hexapdf/type/annotations/link.rb +2 -2
  177. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  178. data/lib/hexapdf/type/annotations/text.rb +1 -1
  179. data/lib/hexapdf/type/annotations/widget.rb +238 -2
  180. data/lib/hexapdf/type/catalog.rb +23 -3
  181. data/lib/hexapdf/type/cid_font.rb +1 -1
  182. data/lib/hexapdf/type/embedded_file.rb +1 -1
  183. data/lib/hexapdf/type/file_specification.rb +2 -2
  184. data/lib/hexapdf/type/font.rb +18 -1
  185. data/lib/hexapdf/type/font_descriptor.rb +2 -2
  186. data/lib/hexapdf/type/font_simple.rb +4 -2
  187. data/lib/hexapdf/type/font_true_type.rb +7 -3
  188. data/lib/hexapdf/type/font_type0.rb +2 -2
  189. data/lib/hexapdf/type/font_type1.rb +16 -1
  190. data/lib/hexapdf/type/font_type3.rb +1 -1
  191. data/lib/hexapdf/type/form.rb +12 -2
  192. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  193. data/lib/hexapdf/type/icon_fit.rb +1 -1
  194. data/lib/hexapdf/type/image.rb +5 -3
  195. data/lib/hexapdf/type/info.rb +1 -1
  196. data/lib/hexapdf/type/names.rb +1 -1
  197. data/lib/hexapdf/type/object_stream.rb +1 -1
  198. data/lib/hexapdf/type/page.rb +36 -12
  199. data/lib/hexapdf/type/page_tree_node.rb +37 -16
  200. data/lib/hexapdf/type/resources.rb +17 -3
  201. data/lib/hexapdf/type/trailer.rb +4 -6
  202. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  203. data/lib/hexapdf/type/xref_stream.rb +1 -1
  204. data/lib/hexapdf/utils/bit_field.rb +38 -24
  205. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  206. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  207. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  208. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  209. data/lib/hexapdf/utils/object_hash.rb +1 -1
  210. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  211. data/lib/hexapdf/utils/sorted_tree_node.rb +19 -16
  212. data/lib/hexapdf/version.rb +2 -2
  213. data/lib/hexapdf/writer.rb +1 -1
  214. data/lib/hexapdf/xref_section.rb +1 -1
  215. data/test/hexapdf/common_tokenizer_tests.rb +6 -1
  216. data/test/hexapdf/content/common.rb +2 -2
  217. data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
  218. data/test/hexapdf/content/test_canvas.rb +3 -3
  219. data/test/hexapdf/content/test_color_space.rb +71 -8
  220. data/test/hexapdf/content/test_operator.rb +22 -22
  221. data/test/hexapdf/content/test_parser.rb +14 -0
  222. data/test/hexapdf/document/test_fonts.rb +1 -1
  223. data/test/hexapdf/document/test_pages.rb +6 -6
  224. data/test/hexapdf/encryption/test_aes.rb +4 -4
  225. data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
  226. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  227. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  228. data/test/hexapdf/font/encoding/test_base.rb +10 -0
  229. data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
  230. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  231. data/test/hexapdf/font/test_type1_wrapper.rb +33 -8
  232. data/test/hexapdf/layout/test_style.rb +1 -1
  233. data/test/hexapdf/layout/test_text_layouter.rb +3 -4
  234. data/test/hexapdf/test_configuration.rb +2 -2
  235. data/test/hexapdf/test_dictionary.rb +3 -1
  236. data/test/hexapdf/test_dictionary_fields.rb +2 -2
  237. data/test/hexapdf/test_document.rb +16 -4
  238. data/test/hexapdf/test_object.rb +44 -26
  239. data/test/hexapdf/test_parser.rb +125 -55
  240. data/test/hexapdf/test_pdf_array.rb +7 -0
  241. data/test/hexapdf/test_rectangle.rb +14 -0
  242. data/test/hexapdf/test_revision.rb +3 -0
  243. data/test/hexapdf/test_revisions.rb +35 -0
  244. data/test/hexapdf/test_writer.rb +2 -2
  245. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +521 -0
  246. data/test/hexapdf/type/acro_form/test_button_field.rb +281 -0
  247. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  248. data/test/hexapdf/type/acro_form/test_field.rb +163 -6
  249. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  250. data/test/hexapdf/type/acro_form/test_text_field.rb +121 -0
  251. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  252. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  253. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  254. data/test/hexapdf/type/test_annotation.rb +45 -0
  255. data/test/hexapdf/type/test_catalog.rb +18 -0
  256. data/test/hexapdf/type/test_font.rb +5 -0
  257. data/test/hexapdf/type/test_font_simple.rb +2 -1
  258. data/test/hexapdf/type/test_font_true_type.rb +6 -0
  259. data/test/hexapdf/type/test_font_type1.rb +8 -0
  260. data/test/hexapdf/type/test_form.rb +19 -1
  261. data/test/hexapdf/type/test_image.rb +7 -0
  262. data/test/hexapdf/type/test_page.rb +45 -7
  263. data/test/hexapdf/type/test_page_tree_node.rb +62 -12
  264. data/test/hexapdf/type/test_resources.rb +20 -0
  265. data/test/hexapdf/type/test_trailer.rb +4 -0
  266. data/test/hexapdf/utils/test_bit_field.rb +15 -1
  267. data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
  268. data/test/test_helper.rb +1 -1
  269. metadata +34 -21
  270. data/lib/hexapdf/filter/dct_decode.rb +0 -60
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -48,8 +48,12 @@ module HexaPDF
48
48
  private
49
49
 
50
50
  def perform_validation
51
- super
52
- yield("Required field FontDescriptor is not set", false) if self[:FontDescriptor].nil?
51
+ std_font = FontType1::StandardFonts.standard_font?(self[:BaseFont])
52
+ super(ignore_missing_font_fields: std_font)
53
+
54
+ if self[:FontDescriptor].nil? && !std_font
55
+ yield("Required field FontDescriptor is not set", false)
56
+ end
53
57
  end
54
58
 
55
59
  end
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -59,7 +59,7 @@ module HexaPDF
59
59
  # Returns the CID font of this type 0 font.
60
60
  def descendant_font
61
61
  document.cache(@data, :descendant_font) do
62
- document.wrap(document.deref(self[:DescendantFonts][0]))
62
+ document.wrap(self[:DescendantFonts][0])
63
63
  end
64
64
  end
65
65
 
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -37,6 +37,7 @@
37
37
  require 'hexapdf/data_dir'
38
38
  require 'hexapdf/type/font_simple'
39
39
  require 'hexapdf/font/type1'
40
+ require 'hexapdf/font/type1_wrapper'
40
41
 
41
42
  module HexaPDF
42
43
  module Type
@@ -105,6 +106,20 @@ module HexaPDF
105
106
  define_field :Subtype, type: Symbol, required: true, default: :Type1
106
107
  define_field :BaseFont, type: Symbol, required: true
107
108
 
109
+ # Overrides the default to provide a font wrapper in case none is set and the font is one of
110
+ # the standard fonts.
111
+ #
112
+ # See: Font#font_wrapper
113
+ def font_wrapper
114
+ if (tmp = super)
115
+ tmp
116
+ elsif StandardFonts.standard_font?(self[:BaseFont])
117
+ self.font_wrapper = HexaPDF::Font::Type1Wrapper.new(document,
118
+ StandardFonts.font(self[:BaseFont]),
119
+ pdf_object: self)
120
+ end
121
+ end
122
+
108
123
  # Returns the unscaled width of the given code point in glyph units, or 0 if the width for the
109
124
  # code point is missing.
110
125
  def width(code)
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -101,7 +101,8 @@ module HexaPDF
101
101
 
102
102
  # Returns the resource dictionary which is automatically created if it doesn't exist.
103
103
  def resources
104
- self[:Resources] ||= document.wrap({}, type: :XXResources)
104
+ self[:Resources] ||= document.wrap({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]},
105
+ type: :XXResources)
105
106
  end
106
107
 
107
108
  # Processes the content stream of the form XObject with the given processor object.
@@ -126,13 +127,22 @@ module HexaPDF
126
127
  # The canvas object is cached once it is created so that its graphics state is correctly
127
128
  # retained without the need for parsing its contents.
128
129
  #
130
+ # If the bounding box of the form XObject doesn't have its origin at (0, 0), the canvas origin
131
+ # is translated into the bottom left corner so that this detail doesn't matter when using the
132
+ # canvas. This means that the canvas' origin is always at the bottom left corner of the
133
+ # bounding box.
134
+ #
129
135
  # *Note* that a canvas can only be retrieved for initially empty form XObjects!
130
136
  def canvas
131
137
  document.cache(@data, :canvas) do
132
138
  unless stream.empty?
133
139
  raise HexaPDF::Error, "Cannot create a canvas for a form XObjects with contents"
134
140
  end
141
+
135
142
  canvas = Content::Canvas.new(self)
143
+ if box.left != 0 || box.bottom != 0
144
+ canvas.save_graphics_state.translate(box.left, box.bottom)
145
+ end
136
146
  self.stream = canvas.stream_data
137
147
  set_filter(:FlateDecode)
138
148
  canvas
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -150,7 +150,7 @@ module HexaPDF
150
150
  color_space, = *self[:ColorSpace]
151
151
  if color_space == :Indexed
152
152
  result.indexed = true
153
- color_space, = *document.deref(self[:ColorSpace][1])
153
+ color_space, = *self[:ColorSpace][1]
154
154
  end
155
155
  case color_space
156
156
  when :DeviceRGB, :CalRGB
@@ -169,6 +169,8 @@ module HexaPDF
169
169
  result.writable = false if result.type == :png
170
170
  end
171
171
 
172
+ result.writable = false if self[:SMask]
173
+
172
174
  result
173
175
  end
174
176
 
@@ -238,7 +240,7 @@ module HexaPDF
238
240
  end
239
241
 
240
242
  if color_type == ImageLoader::PNG::INDEXED
241
- palette_data = document.deref(self[:ColorSpace][3])
243
+ palette_data = self[:ColorSpace][3]
242
244
  palette_data = palette_data.stream unless palette_data.kind_of?(String)
243
245
  palette = ''.b
244
246
  if info.color_space == :rgb
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -304,7 +304,7 @@ module HexaPDF
304
304
  def contents
305
305
  Array(self[:Contents]).each_with_object("".b) do |content_stream, content|
306
306
  content << " " unless content.empty?
307
- content << document.deref(content_stream).stream
307
+ content << content_stream.stream
308
308
  end
309
309
  end
310
310
 
@@ -323,10 +323,11 @@ module HexaPDF
323
323
  end
324
324
  end
325
325
 
326
- # Returns the possibly inherited resource dictionary which is automatically created if it
326
+ # Returns the, possibly inherited, resource dictionary which is automatically created if it
327
327
  # doesn't exist.
328
328
  def resources
329
- self[:Resources] ||= document.wrap({}, type: :XXResources)
329
+ self[:Resources] ||= document.wrap({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]},
330
+ type: :XXResources)
330
331
  end
331
332
 
332
333
  # Processes the content streams associated with the page with the given processor object.
@@ -344,8 +345,7 @@ module HexaPDF
344
345
  node = self
345
346
  while (parent_node = node[:Parent])
346
347
  parent_node[:Kids].each do |kid|
347
- kid = document.deref(kid)
348
- break if kid.data == node.data
348
+ break if kid == node
349
349
  idx += (kid.type == :Page ? 1 : kid[:Count])
350
350
  end
351
351
  node = parent_node
@@ -353,11 +353,26 @@ module HexaPDF
353
353
  idx
354
354
  end
355
355
 
356
+ # Returns all parent nodes of the page up to the root of the page tree.
357
+ #
358
+ # The direct parent is the first node in the array and the root node the last.
359
+ def ancestor_nodes
360
+ parent = self[:Parent]
361
+ result = [parent]
362
+ result << parent while (parent = parent[:Parent])
363
+ result
364
+ end
365
+
356
366
  # Returns the requested type of canvas for the page.
357
367
  #
358
368
  # The canvas object is cached once it is created so that its graphics state is correctly
359
369
  # retained without the need for parsing its contents.
360
370
  #
371
+ # If the media box of the page doesn't have its origin at (0, 0), the canvas origin is
372
+ # translated into the bottom left corner so that this detail doesn't matter when using the
373
+ # canvas. This means that the canvas' origin is always at the bottom left corner of the media
374
+ # box.
375
+ #
361
376
  # type::
362
377
  # Can either be
363
378
  # * :page for getting the canvas for the page itself (only valid for initially empty pages)
@@ -374,16 +389,25 @@ module HexaPDF
374
389
  raise HexaPDF::Error, "Cannot get the canvas for a page with contents"
375
390
  end
376
391
 
392
+ create_canvas = lambda do
393
+ Content::Canvas.new(self).tap do |canvas|
394
+ media_box = box(:media)
395
+ if media_box.left != 0 || media_box.bottom != 0
396
+ canvas.translate(media_box.left, media_box.bottom)
397
+ end
398
+ end
399
+ end
400
+
377
401
  contents = self[:Contents]
378
402
  if contents.nil?
379
- page_canvas = document.cache(@data, :page_canvas, Content::Canvas.new(self))
403
+ page_canvas = document.cache(@data, :page_canvas, create_canvas.call)
380
404
  self[:Contents] = document.add({Filter: :FlateDecode},
381
405
  stream: page_canvas.stream_data)
382
406
  end
383
407
 
384
408
  if type == :overlay || type == :underlay
385
- underlay_canvas = document.cache(@data, :underlay_canvas, Content::Canvas.new(self))
386
- overlay_canvas = document.cache(@data, :overlay_canvas, Content::Canvas.new(self))
409
+ underlay_canvas = document.cache(@data, :underlay_canvas, create_canvas.call)
410
+ overlay_canvas = document.cache(@data, :overlay_canvas, create_canvas.call)
387
411
 
388
412
  stream = HexaPDF::StreamData.new do
389
413
  Fiber.yield(" q ")
@@ -396,11 +420,12 @@ module HexaPDF
396
420
  underlay = document.add({Filter: :FlateDecode}, stream: stream)
397
421
 
398
422
  stream = HexaPDF::StreamData.new do
399
- Fiber.yield(" Q ")
423
+ Fiber.yield(" Q q ")
400
424
  fiber = overlay_canvas.stream_data.fiber
401
425
  while fiber.alive? && (data = fiber.resume)
402
426
  Fiber.yield(data)
403
427
  end
428
+ " Q "
404
429
  end
405
430
  overlay = document.add({Filter: :FlateDecode}, stream: stream)
406
431
 
@@ -448,8 +473,7 @@ module HexaPDF
448
473
  REQUIRED_INHERITABLE_FIELDS.each do |name|
449
474
  next if self[name]
450
475
  yield("Inheritable page field #{name} not set", name == :Resources)
451
- self[:Resources] = {}
452
- self[:Resources].validate(&block)
476
+ resources.validate(&block) if name == :Ressources
453
477
  end
454
478
  end
455
479
 
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -35,6 +35,7 @@
35
35
  #++
36
36
 
37
37
  require 'hexapdf/dictionary'
38
+ require 'hexapdf/error'
38
39
 
39
40
  module HexaPDF
40
41
  module Type
@@ -159,38 +160,58 @@ module HexaPDF
159
160
  end
160
161
 
161
162
  # :call-seq:
162
- # pages.delete_page(page) -> page or nil
163
- # pages.delete_page(index) -> page or nil
163
+ # pages.delete_page(page)
164
+ # pages.delete_page(index)
164
165
  #
165
166
  # Deletes the given page or the page at the position specified by the zero-based index from
166
- # the page tree and returns the deleted page object. If the page was not deleted, +nil+ is
167
- # returned.
168
- #
169
- # Note that the page is *not* deleted from the document itself, only from the page tree! This
170
- # also means that the /Parent entry of the page is set to +nil+ if deleted.
167
+ # the page tree and the document.
171
168
  #
172
169
  # Negative indices count backwards from the end, i.e. -1 is the last page.
173
170
  def delete_page(page)
174
171
  page = self.page(page) if page.kind_of?(Integer)
175
- return nil unless page && page[:Parent]
172
+ return unless page && !page.null? && page[:Parent]
176
173
 
177
174
  parent = page[:Parent]
178
- index = parent[:Kids].index {|kid| kid.data == page.data }
175
+ index = parent[:Kids].index(page)
179
176
 
180
177
  if index
181
- ancestors = [parent]
182
- ancestors << parent while (parent = parent[:Parent])
178
+ ancestors = page.ancestor_nodes
183
179
  return nil unless ancestors.include?(self)
184
180
 
185
181
  page[:Parent][:Kids].delete_at(index)
186
182
  page.delete(:Parent)
183
+ document.delete(page)
187
184
  ancestors.each {|node| node[:Count] -= 1 }
188
- page
189
185
  else
190
- nil
186
+ raise HexaPDF::Error, "Given page not found in page tree"
191
187
  end
192
188
  end
193
189
 
190
+ # :call-seq:
191
+ # pages.move_page(page, to_index)
192
+ # pages.move_page(index, to_index)
193
+ #
194
+ # Moves the given page or the page at the position specified by the zero-based index to the
195
+ # +to_index+ position.
196
+ #
197
+ # If the page that should be moved, doesn't exist or is invalid, an error is raised.
198
+ #
199
+ # Negative indices count backwards from the end, i.e. -1 is the last page. When using a
200
+ # negative index, the page will be moved after that element. So using an index of -1 will
201
+ # move the page after the last page.
202
+ def move_page(page, to_index)
203
+ page = self.page(page) if page.kind_of?(Integer)
204
+ if page.nil? || page.null? || !page[:Parent] ||
205
+ !(ancestors = page.ancestor_nodes).include?(self)
206
+ raise HexaPDF::Error, "The page to be moved doesn't exist in this page tree"
207
+ end
208
+
209
+ parent = page[:Parent]
210
+ insert_page(to_index, page)
211
+ ancestors.each {|node| node[:Count] -= 1 }
212
+ parent[:Kids].delete(page)
213
+ end
214
+
194
215
  # :call-seq:
195
216
  # pages.each_page {|page| block } -> pages
196
217
  # pages.each_page -> Enumerator
@@ -225,7 +246,7 @@ module HexaPDF
225
246
  # Ensures that the /Count and /Parent fields of the whole page tree are set up correctly and
226
247
  # that there is at least one page node. This is therefore only done for the root node of the
227
248
  # page tree!
228
- def perform_validation
249
+ def perform_validation(&block)
229
250
  super
230
251
  return if key?(:Parent)
231
252
 
@@ -258,7 +279,7 @@ module HexaPDF
258
279
 
259
280
  if self[:Count] == 0
260
281
  yield("A PDF document needs at least one page", true)
261
- add_page.validate {|msg, correctable| yield(msg, correctable) }
282
+ add_page.validate(&block)
262
283
  end
263
284
  end
264
285
 
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -69,11 +69,10 @@ module HexaPDF
69
69
  when :DeviceRGB, :DeviceGray, :DeviceCMYK
70
70
  GlobalConfiguration.constantize('color_space.map', name).new
71
71
  else
72
- space_definition = self[:ColorSpace] && self[:ColorSpace][name]
72
+ space_definition = (name == :Pattern ? name : self[:ColorSpace]&.[](name))
73
73
  if space_definition.nil?
74
74
  raise HexaPDF::Error, "Color space '#{name}' not found in the resources"
75
75
  elsif space_definition.kind_of?(Array)
76
- space_definition.map! {|item| document.deref(item) }
77
76
  space_family = space_definition[0]
78
77
  else
79
78
  space_family = space_definition
@@ -168,6 +167,20 @@ module HexaPDF
168
167
  object_setter(:Properties, 'P', dict)
169
168
  end
170
169
 
170
+ # Returns the pattern dictionary stored under the given name.
171
+ #
172
+ # If the dictionary is not found, an error is raised.
173
+ def pattern(name)
174
+ object_getter(:Pattern, name)
175
+ end
176
+
177
+ # Adds the pattern dictionary to the resources and returns the name under which it is stored.
178
+ #
179
+ # If there already exists a name for the given dictionary, it is just returned.
180
+ def add_pattern(object)
181
+ object_setter(:Pattern, 'P', object)
182
+ end
183
+
171
184
  private
172
185
 
173
186
  # Helper method for returning an entry of a subdictionary.
@@ -206,6 +219,7 @@ module HexaPDF
206
219
  super
207
220
  val = self[:ProcSet]
208
221
  if !val
222
+ yield("No procedure set specified", true)
209
223
  self[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
210
224
  else
211
225
  val.reject! do |name|