hexapdf 0.11.9 → 0.12.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 (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -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 +18 -9
  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 +1 -1
  30. data/lib/hexapdf/cli/images.rb +1 -1
  31. data/lib/hexapdf/cli/info.rb +1 -1
  32. data/lib/hexapdf/cli/inspect.rb +1 -1
  33. data/lib/hexapdf/cli/merge.rb +1 -1
  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 +66 -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 +1 -1
  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 +1 -1
  55. data/lib/hexapdf/dictionary_fields.rb +1 -1
  56. data/lib/hexapdf/document.rb +14 -5
  57. data/lib/hexapdf/document/files.rb +1 -1
  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 +1 -1
  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 +1 -1
  70. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -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 +1 -1
  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 +1 -1
  86. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -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 +1 -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 +1 -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 +67 -51
  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 +1 -1
  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 +1 -1
  137. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  138. data/lib/hexapdf/layout/style.rb +1 -1
  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 +1 -1
  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 +2 -2
  147. data/lib/hexapdf/parser.rb +4 -3
  148. data/lib/hexapdf/pdf_array.rb +1 -1
  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 +1 -1
  153. data/lib/hexapdf/serializer.rb +1 -1
  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 +1 -1
  158. data/lib/hexapdf/tokenizer.rb +1 -1
  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 +401 -0
  162. data/lib/hexapdf/type/acro_form/button_field.rb +300 -0
  163. data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
  164. data/lib/hexapdf/type/acro_form/field.rb +220 -17
  165. data/lib/hexapdf/type/acro_form/form.rb +157 -7
  166. data/lib/hexapdf/type/acro_form/text_field.rb +186 -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 +1 -1
  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 +239 -2
  180. data/lib/hexapdf/type/catalog.rb +21 -1
  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 +1 -1
  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 +1 -1
  187. data/lib/hexapdf/type/font_true_type.rb +1 -1
  188. data/lib/hexapdf/type/font_type0.rb +1 -1
  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 +1 -1
  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 +3 -1
  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 +1 -1
  199. data/lib/hexapdf/type/page_tree_node.rb +8 -11
  200. data/lib/hexapdf/type/resources.rb +16 -3
  201. data/lib/hexapdf/type/trailer.rb +2 -3
  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 +1 -1
  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/content/common.rb +2 -2
  216. data/test/hexapdf/content/test_color_space.rb +71 -8
  217. data/test/hexapdf/content/test_operator.rb +22 -22
  218. data/test/hexapdf/content/test_parser.rb +14 -0
  219. data/test/hexapdf/document/test_fonts.rb +1 -1
  220. data/test/hexapdf/document/test_pages.rb +6 -6
  221. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  222. data/test/hexapdf/font/test_type1_wrapper.rb +32 -8
  223. data/test/hexapdf/test_document.rb +12 -0
  224. data/test/hexapdf/test_parser.rb +10 -0
  225. data/test/hexapdf/test_rectangle.rb +14 -0
  226. data/test/hexapdf/test_revision.rb +3 -0
  227. data/test/hexapdf/test_writer.rb +2 -2
  228. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +515 -0
  229. data/test/hexapdf/type/acro_form/test_button_field.rb +276 -0
  230. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  231. data/test/hexapdf/type/acro_form/test_field.rb +124 -6
  232. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  233. data/test/hexapdf/type/acro_form/test_text_field.rb +119 -0
  234. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  235. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  236. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  237. data/test/hexapdf/type/test_annotation.rb +45 -0
  238. data/test/hexapdf/type/test_catalog.rb +18 -0
  239. data/test/hexapdf/type/test_font.rb +5 -0
  240. data/test/hexapdf/type/test_font_type1.rb +8 -0
  241. data/test/hexapdf/type/test_image.rb +7 -0
  242. data/test/hexapdf/type/test_page_tree_node.rb +20 -12
  243. data/test/hexapdf/type/test_resources.rb +20 -0
  244. data/test/hexapdf/type/test_trailer.rb +4 -0
  245. data/test/hexapdf/utils/test_bit_field.rb +13 -1
  246. data/test/test_helper.rb +1 -1
  247. metadata +37 -18
  248. data/lib/hexapdf/filter/dct_decode.rb +0 -60
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f56bc512ed28de11d43f5b2eb662729086734dc3120e6c8e96b56eab89420da4
4
- data.tar.gz: 4b9e75f7d533cc7b3c3a6e297d26ab0358dcaa4ac0f0a5346df3e95c90d9c173
3
+ metadata.gz: 34c65a5eca10ade6778049ce85a8913ce180c32fed6826254e61a9cd801482fb
4
+ data.tar.gz: 7fd2cd5870e03acd71e5d723e41199664d9cabd2ddb1fd9430f997918cc15dd3
5
5
  SHA512:
6
- metadata.gz: 3651a7ed4a7fb305c79a70b0c009441b0e3012188ce5e8baf8847107bce27793e0c48a577ae0bbc9ae3d6bece5356b86955b4deb2abd505d0b7a856053461ab3
7
- data.tar.gz: 7cfd77439479567d81133e39243715dd05b3a3b31ccc64fd923ec7391ba745834977011e4618f1f9462cdcb2a682ed1b102c20474a4b3f5d7c69be438c6abd51
6
+ metadata.gz: 843a8e201d625d0d1d4a6755aeecf0d2ed810372058d1fec0136bf781e51ed9c46346d3d52fb4d0eda50100aac0b29112f6e4d42c3555bc923deee4583dbba00
7
+ data.tar.gz: d2042cb0b6cbd82bae3d6252a87ef68ee7791e02bf87e96a233d10bf3bb37eb991dca63c7293396ad5c75d03ecc668938722dbc03ade777131c4cc190cbfbda4
@@ -1,3 +1,85 @@
1
+ ## 0.12.0 - 2020-08-12
2
+
3
+ ### Added
4
+
5
+ * Convenience methods for accessing field flags for
6
+ [HexaPDF::Type::AcroForm::Field]
7
+ * [HexaPDF::Type::AcroForm::TextField] and
8
+ [HexaPDF::Type::AcroForm::VariableTextField] for basic text field support
9
+ * [HexaPDF::Type::AcroForm::ButtonField] for push button, radio button and
10
+ check box support
11
+ * [HexaPDF::Type::AcroForm::ChoiceField] for combo box and list box support
12
+ * [HexaPDF::Type::AcroForm::AppearanceGenerator] as central class for
13
+ generating appearance streams for form fields
14
+ * Various convenience methods for [HexaPDF::Type::AcroForm::Form]
15
+ * Various convenience methods for [HexaPDF::Type::AcroForm::Field]
16
+ * Various convenience methods for [HexaPDF::Type::Annotations::Widget]
17
+ * [HexaPDF::Type::Annotation::AppearanceDictionary]
18
+ * [HexaPDF::Document#acro_form] and [HexaPDF::Type::Catalog#acro_form]
19
+ convenience methods
20
+ * CLI command `hexapdf form` for listing fields of interactive forms and filling
21
+ them out
22
+ * [HexaPDF::Rectangle] methods for setting the left, top, right, bottom, width
23
+ and height
24
+ * Method #prenormalized_color to all color space implementations
25
+ * [HexaPDF::Type::Font#font_wrapper] for accessing an associated font wrapper
26
+ instance
27
+ * [HexaPDF::Type::FontType1#font_wrapper] for providing a font wrapper for the
28
+ standard PDF fonts
29
+ * [HexaPDF::Type::Annotation::Border] class
30
+ * [HexaPDF::Content::ColorSpace::device_color_from_specification] for easily
31
+ getting a device color object
32
+ * [HexaPDF::Content::ColorSpace::prenormalized_device_color] for getting a device
33
+ color object without normalizing values
34
+ * [HexaPDF::Type::Annotation#appearance] for returning the associated appearance
35
+ dictionary
36
+ * [HexaPDF::Type::Annotation#appearance?] for checking whether an appearance for
37
+ the annotation exists
38
+ * Configuration option 'acro_form.create_appearance_streams' for automatically
39
+ creating appearance streams
40
+ * [HexaPDF::Type::Resources] methods `#pattern` and `add_pattern`
41
+
42
+ ### Changed
43
+
44
+ * Deletion of pages to delete them from the document as well
45
+ * Refactored [HexaPDF::Font::Type1Wrapper] and [HexaPDF::Font::TrueTypeWrapper]
46
+ and renamed `#dict` to `#pdf_object`
47
+ * Fall back to the Type1 font's internal encoding when decoding a string
48
+ * All [HexaPDF::Content::ColorSpace] implementations to only normalize values
49
+ when using the ::color method
50
+ * [HexaPDF::Content::Parser#parse] to also accept a block in place of a
51
+ processor object
52
+ * HexaPDF::Type::AcroForm::Field#full_name to
53
+ [HexaPDF::Type::AcroForm::Field#full_field_name]
54
+ * Moved `HexaPDF::Content::Canvas#color_space_for_components` to class method on
55
+ [HexaPDF::Content::ColorSpace]
56
+ * Added bit unsetter method to[HexaPDF::Utils::BitField]
57
+ * [HexaPDF::Type::AcroForm::Form#find_root_fields] and `#each_field` to take the
58
+ field type into account when wrapping a field dictionary
59
+ * Pages specification of CLI commands to allow counting from the end using the
60
+ new `r<N>` notation
61
+ * [HexaPDF::Font::Type1Wrapper] to use the internal encoding of a font with a
62
+ 'Special' character set instead of a custom encoding
63
+ * Configuration 'filter.map' to use the pass-through filter on all unsupported
64
+ filters
65
+
66
+ ### Fixed
67
+
68
+ * Wrong normalization of color values when invoking a color operator
69
+ * Invalid type of `/DR` field of [HexaPDF::Type::AcroForm::Form]
70
+ * Invalid ordering of types for the `/V` and `/DV` fields of
71
+ [HexaPDF::Type::AcroForm::Field]
72
+ * [HexaPDF::Type::AcroForm::Field#terminal_field?] to work according to the spec
73
+ * Handling of empty files by throwing better error messages
74
+ * [HexaPDF::Type::Image#info] to correctly identify images with a soft mask as
75
+ currently not supported for writing
76
+ * [HexaPDF::Revision#delete] to remove the connection between the object and the
77
+ document
78
+ * Missing `#definition` method of `DeviceRGB`, `DeviceCMYK` and `DeviceGray`
79
+ color spaces
80
+ * Handling of 'Pattern' color spaces when parsing content streams
81
+
82
+
1
83
  ## 0.11.9 - 2020-06-15
2
84
 
3
85
  ### Changed
data/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
2
- Copyright (C) 2014-2019 Thomas Leitner
2
+ Copyright (C) 2014-2020 Thomas Leitner
3
3
 
4
4
  HexaPDF is free software: you can redistribute it and/or modify it
5
5
  under the terms of the GNU Affero General Public License version 3 as
@@ -1,4 +1,4 @@
1
- # ## Hello World
1
+ # # Hello World
2
2
  #
3
3
  # This simple example mimics the classic "hello world" examples from
4
4
  # programming languages.
@@ -1,4 +1,4 @@
1
- # ## Graphics Primitives
1
+ # # Graphics Primitives
2
2
  #
3
3
  # This example shows many of the operations that the canvas implementation
4
4
  # allows.
@@ -1,4 +1,4 @@
1
- # ## Arcs and Solid Arcs
1
+ # # Arcs and Solid Arcs
2
2
  #
3
3
  # This example shows how to use the graphic objects `:arc` and `:solid_arc` to
4
4
  # draw simple pie charts.
@@ -1,4 +1,4 @@
1
- # ## Optimizing a PDF File
1
+ # # Optimizing a PDF File
2
2
  #
3
3
  # This example shows how to read a PDF file, optimize it and write it
4
4
  # out again.
@@ -1,4 +1,4 @@
1
- # ## Merging PDF Files
1
+ # # Merging PDF Files
2
2
  #
3
3
  # Merging of PDF files can be done in various ways of sophistication.
4
4
  #
@@ -1,4 +1,4 @@
1
- # ## Standard PDF Fonts
1
+ # # Standard PDF Fonts
2
2
  #
3
3
  # This example shows all characters that are available in the standard 14 PDF
4
4
  # fonts.
@@ -1,4 +1,4 @@
1
- # ## TrueType Fonts
1
+ # # TrueType Fonts
2
2
  #
3
3
  # This example displays all glyphs of a TrueType font and shows that using a
4
4
  # TrueType font with HexaPDF is very similar to using one of the standard PDF
@@ -1,4 +1,4 @@
1
- # ## Show Character Bounding Boxes
1
+ # # Show Character Bounding Boxes
2
2
  #
3
3
  # This examples shows how to process the contents of a page. It finds all
4
4
  # characters on a page and surrounds them with their bounding box. Additionally,
@@ -1,4 +1,4 @@
1
- # ## Text Layouter - Alignment
1
+ # # Text Layouter - Alignment
2
2
  #
3
3
  # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
4
4
  # inside a rectangular area, with various horizontal and vertical alignment
@@ -1,4 +1,4 @@
1
- # ## Text Layouter - Inline Boxes
1
+ # # Text Layouter - Inline Boxes
2
2
  #
3
3
  # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
4
4
  # mixed with inline boxes.
@@ -1,4 +1,4 @@
1
- # ## Text Layouter - Line Wrapping
1
+ # # Text Layouter - Line Wrapping
2
2
  #
3
3
  # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text,
4
4
  # automatically wrapping it appropriately.
@@ -1,4 +1,4 @@
1
- # ## Text Layouter - Styling
1
+ # # Text Layouter - Styling
2
2
  #
3
3
  # The text used as part of a [HexaPDF::Layout::TextLayouter] class can be styled
4
4
  # using [HexaPDF::Layout::Style]. To do this [HexaPDF::Layout::TextFragment]
@@ -1,4 +1,4 @@
1
- # ## Text Layouter - Shapes
1
+ # # Text Layouter - Shapes
2
2
  #
3
3
  # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text,
4
4
  # not limiting the area to a rectangle but any shape. There is only one
@@ -1,4 +1,4 @@
1
- # ## Text in Polygon
1
+ # # Text in Polygon
2
2
  #
3
3
  # While creating width specifications for the [HexaPDF::Layout::TextLayouter]
4
4
  # class by hand is possible, the [HexaPDF::Layout::WidthFromPolygon] class
@@ -1,4 +1,4 @@
1
- # ## Boxes
1
+ # # Boxes
2
2
  #
3
3
  # The [HexaPDF::Layout::Box] class is used as the basis for all document layout
4
4
  # features.
@@ -1,4 +1,4 @@
1
- # ## Frame - Automatic Box Placement
1
+ # # Frame - Automatic Box Placement
2
2
  #
3
3
  # The [HexaPDF::Layout::Frame] class is used for placing rectangular boxes.
4
4
  #
@@ -1,4 +1,4 @@
1
- # ## Frame - Text Flow
1
+ # # Frame - Text Flow
2
2
  #
3
3
  # This example shows how [HexaPDF::Layout::Frame] and [HexaPDF::Layout::TextBox]
4
4
  # can be used to flow text around objects.
@@ -1,4 +1,4 @@
1
- # ## Composer
1
+ # # Composer
2
2
  #
3
3
  # This example shows how [HexaPDF::Composer] simplifies the creation of PDF
4
4
  # documents by providing a high-level interface to the box layouting engine.
@@ -0,0 +1,51 @@
1
+ # # PDF Forms
2
+ #
3
+ # PDF files can be used for interactive forms, containing various types of form
4
+ # fields. HexaPDF supports the creation and processing of these forms.
5
+ #
6
+ # This example show-cases how to create the various form field types and their
7
+ # possible standard appearances.
8
+ #
9
+ # Usage:
10
+ # : `ruby acro_form.rb`
11
+ #
12
+
13
+ require 'hexapdf'
14
+
15
+ doc = HexaPDF::Document.new
16
+ page = doc.pages.add
17
+ canvas = page.canvas
18
+
19
+ canvas.font("Helvetica", size: 36)
20
+ canvas.text("Form Example", at: [50, 750])
21
+ form = doc.acro_form(create: true)
22
+
23
+ canvas.font_size(16)
24
+ canvas.text("Check boxes", at: [50, 650])
25
+ [:check, :circle, :cross, :diamond, :square, :star].each_with_index do |symbol, index|
26
+ cb = form.create_check_box("Checkbox #{index}")
27
+ widget = cb.create_widget(page, Rect: [200 + 50 * index, 640, 240 + 50 * index, 680])
28
+ widget.background_color(1 - 0.05 * index)
29
+ widget.marker_style(style: symbol, color: [0.166 * index, 0, 1 - 0.166 * index],
30
+ size: 7 * index)
31
+ cb.field_value = true
32
+ end
33
+
34
+ canvas.text("Radio buttons", at: [50, 550])
35
+ rb = form.create_radio_button("Radio")
36
+ [:check, :circle, :cross, :diamond, :square, :star].each_with_index do |symbol, index|
37
+ widget = rb.create_widget(page, value: :"button#{index}",
38
+ Rect: [200 + 50 * index, 540, 240 + 50 * index, 580])
39
+ widget.background_color(1 - 0.05 * index)
40
+ widget.marker_style(style: symbol, color: [0.166 * index, 0, 1 - 0.166 * index],
41
+ size: 7 * index)
42
+ end
43
+ rb.field_value = :button0
44
+
45
+ canvas.text("Text field", at: [50, 450])
46
+ tx = form.create_text_field("Single Line")
47
+ widget = tx.create_widget(page, Rect: [200, 445, 500, 465])
48
+ tx.set_default_appearance_string(font_size: 16)
49
+ tx.field_value = "A sample test string!"
50
+
51
+ doc.write('acro_form.pdf', optimize: true)
@@ -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
@@ -46,6 +46,7 @@ require 'hexapdf/cli/batch'
46
46
  require 'hexapdf/cli/split'
47
47
  require 'hexapdf/cli/watermark'
48
48
  require 'hexapdf/cli/image2pdf'
49
+ require 'hexapdf/cli/form'
49
50
  require 'hexapdf/version'
50
51
  require 'hexapdf/document'
51
52
 
@@ -99,6 +100,7 @@ module HexaPDF
99
100
  add_command(HexaPDF::CLI::Split.new)
100
101
  add_command(HexaPDF::CLI::Watermark.new)
101
102
  add_command(HexaPDF::CLI::Image2PDF.new)
103
+ add_command(HexaPDF::CLI::Form.new)
102
104
  add_command(CmdParse::HelpCommand.new)
103
105
  version_command = CmdParse::VersionCommand.new(add_switches: false)
104
106
  add_command(version_command)
@@ -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
@@ -80,7 +80,7 @@ module HexaPDF
80
80
  # Creates a HexaPDF::Document instance for the PDF file and yields it.
81
81
  #
82
82
  # If +out_file+ is given, the document is written to it after yielding.
83
- def with_document(file, password: nil, out_file: nil) #:yield: document
83
+ def with_document(file, password: nil, out_file: nil, incremental: false) #:yield: document
84
84
  if file == out_file
85
85
  doc = HexaPDF::Document.open(file, **pdf_options(password))
86
86
  else
@@ -90,7 +90,7 @@ module HexaPDF
90
90
 
91
91
  yield(doc)
92
92
 
93
- write_document(doc, out_file)
93
+ write_document(doc, out_file, incremental: incremental)
94
94
  ensure
95
95
  file_io&.close
96
96
  end
@@ -116,7 +116,7 @@ module HexaPDF
116
116
  end
117
117
 
118
118
  # Writes the document to the given file or does nothing if +out_file+ is +nil+.
119
- def write_document(doc, out_file)
119
+ def write_document(doc, out_file, incremental: false)
120
120
  if out_file
121
121
  doc.validate(auto_correct: true) do |object, msg, correctable|
122
122
  if command_parser.strict && !correctable
@@ -126,7 +126,7 @@ module HexaPDF
126
126
  "for object (#{object.oid},#{object.gen}): #{msg}"
127
127
  end
128
128
  end
129
- doc.write(out_file, validate: false)
129
+ doc.write(out_file, validate: false, incremental: incremental)
130
130
  end
131
131
  end
132
132
 
@@ -289,7 +289,16 @@ module HexaPDF
289
289
  end
290
290
  end
291
291
 
292
- PAGE_NUMBER_SPEC = "([1-9]\\d*|e)" #:nodoc:
292
+ PAGE_NUMBER_SPEC = "(r?[1-9]\\d*|e)" #:nodoc:
293
+ PAGE_MAP = lambda do |result, count|
294
+ if result == 'e'
295
+ count
296
+ elsif result.start_with?('r')
297
+ count - result[1..-1].to_i + 1
298
+ else
299
+ result.to_i
300
+ end
301
+ end
293
302
  ROTATE_MAP = {'l' => 90, 'r' => -90, 'd' => 180, 'n' => :none}.freeze #:nodoc:
294
303
 
295
304
  # Parses the pages specification string and returns an array of tuples containing a page
@@ -304,12 +313,12 @@ module HexaPDF
304
313
  range.split(',').each_with_object([]) do |str, arr|
305
314
  case str
306
315
  when /\A#{PAGE_NUMBER_SPEC}(l|r|d|n)?\z/o
307
- page_num = ($1 == 'e' ? count : str.to_i)
316
+ page_num = PAGE_MAP[$1, count]
308
317
  next if page_num > count
309
318
  arr << [page_num - 1, ROTATE_MAP[$2]]
310
319
  when /\A#{PAGE_NUMBER_SPEC}-#{PAGE_NUMBER_SPEC}(?:\/([1-9]\d*))?(l|r|d|n)?\z/o
311
- start_nr = ($1 == 'e' ? count : [$1.to_i, count].min) - 1
312
- end_nr = ($2 == 'e' ? count : [$2.to_i, count].min) - 1
320
+ start_nr = [PAGE_MAP[$1, count], count].min - 1
321
+ end_nr = [PAGE_MAP[$2, count], count].min - 1
313
322
  step = ($3 ? $3.to_i : 1) * (start_nr > end_nr ? -1 : 1)
314
323
  rotation = ROTATE_MAP[$4]
315
324
  start_nr.step(to: end_nr, by: step) {|n| arr << [n, rotation] }
@@ -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
@@ -0,0 +1,240 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/cli/command'
38
+ require 'strscan'
39
+
40
+ module HexaPDF
41
+ module CLI
42
+
43
+ # Processes a PDF that contains an interactive form (AcroForm).
44
+ class Form < Command
45
+
46
+ def initialize #:nodoc:
47
+ super('form', takes_commands: false)
48
+ short_desc("Show form fields and fill out a form")
49
+ long_desc(<<~EOF)
50
+ Use this command to process interactive PDF forms.
51
+
52
+ If the the output file name is not given, all form fields are listed in page order. Use
53
+ the global --verbose option to show additional information like field type and location.
54
+
55
+ If the output file name is given, the fields can be interactively filled out. By
56
+ additionally using the --template option, the data for the fields is read from the given
57
+ template file instead of the standard input.
58
+ EOF
59
+
60
+ options.on("--password PASSWORD", "-p", String,
61
+ "The password for decryption. Use - for reading from standard input.") do |pwd|
62
+ @password = (pwd == '-' ? read_password : pwd)
63
+ end
64
+ options.on("--template TEMPLATE_FILE", "-t TEMPLATE_FILE",
65
+ "Use the template file for the field values") do |template|
66
+ @template = template
67
+ end
68
+ options.on("--[no-]viewer-override", "Let the PDF viewer override the visual " \
69
+ "appearance. Default: use setting from input PDF") do |need_appearances|
70
+ @need_appearances = need_appearances
71
+ end
72
+ options.on("--[no-]incremental-save", "Append the changes instead of rewriting the " \
73
+ "whole file. Default: true") do |incremental|
74
+ @incremental = incremental
75
+ end
76
+
77
+ @password = nil
78
+ @template = nil
79
+ @need_appearances = nil
80
+ @incremental = true
81
+ end
82
+
83
+ def execute(in_file, out_file = nil) #:nodoc:
84
+ maybe_raise_on_existing_file(out_file) if out_file
85
+ with_document(in_file, password: @password, out_file: out_file,
86
+ incremental: @incremental) do |doc|
87
+ if !doc.acro_form
88
+ raise "This PDF doesn't contain an interactive form"
89
+ elsif out_file
90
+ doc.acro_form[:NeedAppearances] = @need_appearances unless @need_appearances.nil?
91
+ if @template
92
+ fill_form_with_template(doc)
93
+ else
94
+ fill_form(doc)
95
+ end
96
+ else
97
+ list_form_fields(doc)
98
+ end
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ # Lists all terminal form fields.
105
+ def list_form_fields(doc)
106
+ current_page_index = -1
107
+ each_field(doc) do |_page, page_index, field, widget|
108
+ if current_page_index != page_index
109
+ puts "Page #{page_index + 1}"
110
+ current_page_index = page_index
111
+ end
112
+
113
+ field_name = field.full_field_name +
114
+ (field.alternate_field_name ? " (#{field.alternate_field_name})" : '')
115
+ concrete_field_type = field.concrete_field_type
116
+ nice_field_type = concrete_field_type.to_s.split('_').map(&:capitalize).join(' ')
117
+ position = "(#{widget[:Rect].left}, #{widget[:Rect].bottom})"
118
+
119
+ puts " #{field_name}"
120
+ if command_parser.verbosity_info?
121
+ printf(" └─ %-22s | %-20s\n", nice_field_type, position)
122
+ end
123
+ puts " └─ #{field.field_value.inspect}"
124
+ if command_parser.verbosity_info?
125
+ if field.field_type == :Ch
126
+ puts " └─ Options: #{field.option_items.map(&:inspect).join(', ')}"
127
+ elsif concrete_field_type == :radio_button
128
+ puts " └─ Options: #{field.radio_button_values.map(&:inspect).join(', ')}"
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ # Fills out the form by interactively asking the user for field values.
135
+ def fill_form(doc)
136
+ current_page_index = -1
137
+ each_field(doc) do |_page, page_index, field, _widget|
138
+ if current_page_index != page_index
139
+ puts "Page #{page_index + 1}"
140
+ current_page_index = page_index
141
+ end
142
+
143
+ field_name = field.full_field_name +
144
+ (field.alternate_field_name ? " (#{field.alternate_field_name})" : '')
145
+ concrete_field_type = field.concrete_field_type
146
+
147
+ puts " #{field_name}"
148
+ puts " └─ Current value: #{field.field_value.inspect}"
149
+
150
+ if field.field_type == :Ch
151
+ puts " └─ Possible values: #{field.option_items.map(&:inspect).join(', ')}"
152
+ elsif concrete_field_type == :radio_button
153
+ puts " └─ Possible values: #{field.radio_button_values.map(&:inspect).join(', ')}"
154
+ elsif concrete_field_type == :check_box
155
+ puts " └─ Possible values: y(es), t(rue); n(o), f(alse)"
156
+ end
157
+
158
+ begin
159
+ print " └─ New value: "
160
+ value = $stdin.readline.chomp
161
+ next if value.empty?
162
+ apply_field_value(field, value)
163
+ rescue HexaPDF::Error => e
164
+ puts " ⚠ #{e.message}"
165
+ retry
166
+ end
167
+ end
168
+ end
169
+
170
+ # Fills out the form using the data from the provided template file.
171
+ def fill_form_with_template(doc)
172
+ data = parse_template
173
+ form = doc.acro_form
174
+ data.each do |name, value|
175
+ field = form.field_by_name(name)
176
+ raise "Field '#{name}' not found in input PDF" unless field
177
+ apply_field_value(field, value)
178
+ end
179
+ end
180
+
181
+ # Parses the data from the given template file.
182
+ def parse_template
183
+ data = {}
184
+ scanner = StringScanner.new(File.read(@template))
185
+ until scanner.eos?
186
+ field_name = scanner.scan(/(\\:|[^:])*?:/)
187
+ break unless field_name
188
+ field_name.gsub!(/\\:/, ':')
189
+ field_value = scanner.scan(/.*?(?=^\S|\z)/m)
190
+ data[field_name.chop] = field_value.strip.gsub(/^\s*/, '') if field_value
191
+ end
192
+ if !scanner.eos? && command_parser.verbosity_warning?
193
+ $stderr.puts "Warning: Some template could not be parsed"
194
+ end
195
+ data
196
+ end
197
+
198
+ # Applies the given value to the field.
199
+ def apply_field_value(field, value)
200
+ case field.concrete_field_type
201
+ when :single_line_text_field
202
+ field.field_value = value
203
+ when :combo_box, :list_box
204
+ field.field_value = value
205
+ when :editable_combo_box
206
+ field.field_value = value
207
+ when :check_box
208
+ unless value.match?(/y(es)?|t(rue)?|f(alse)?|n(o)/)
209
+ raise HexaPDF::Error, "Invalid input, use one of the possible values"
210
+ end
211
+ field.field_value = value.match?(/y(es)?|t(rue)?/)
212
+ when :radio_button
213
+ field.field_value = value.intern
214
+ else
215
+ raise "Field type not yet supported"
216
+ end
217
+ end
218
+
219
+ # Iterates over all non-push button fields in page order. If a field appears on multiple
220
+ # pages, it is only yielded on the first page.
221
+ def each_field(doc) # :yields: page, page_index, field
222
+ seen = {}
223
+
224
+ doc.pages.each_with_index do |page, page_index|
225
+ page[:Annots]&.each do |annotation|
226
+ next unless annotation[:Subtype] == :Widget
227
+ field = annotation.form_field
228
+ next if field.concrete_field_type == :push_button
229
+ unless seen[field]
230
+ yield(page, page_index, field, annotation)
231
+ seen[field] = true
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ end
238
+
239
+ end
240
+ end