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
@@ -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
@@ -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
@@ -46,6 +46,54 @@ module HexaPDF
46
46
  # See: PDF1.7 s12.5
47
47
  class Annotation < Dictionary
48
48
 
49
+ # The appearance dictionary references appearance streams for various use cases.
50
+ #
51
+ # Each appearance can either be an XObject or a dictionary mapping names to XObjects. The
52
+ # latter is used when the appearance depends on the state of the annotation, e.g. a check box
53
+ # widget that can be checked or unchecked.
54
+ #
55
+ # See: PDF1.7 s12.5.5
56
+ class AppearanceDictionary < Dictionary
57
+
58
+ define_type :XXAppearanceDictionary
59
+
60
+ define_field :N, type: [Dictionary, Stream], required: true
61
+ define_field :R, type: [Dictionary, Stream]
62
+ define_field :D, type: [Dictionary, Stream]
63
+
64
+ # The annotation's normal appearance.
65
+ def normal_appearance
66
+ self[:N]
67
+ end
68
+
69
+ # The rollover appearance which should be used when the cursor is moved into the active area
70
+ # of the annotation without pressing a button.
71
+ def rollover_appearance
72
+ self[:R] || self[:N]
73
+ end
74
+
75
+ # The down appearance which should be used when the mouse button is pressed or held down
76
+ # inside the active area of the annotation.
77
+ def down_appearance
78
+ self[:D] || self[:N]
79
+ end
80
+
81
+ end
82
+
83
+ # Border style dictionary used by various annotation types.
84
+ #
85
+ # See: PDF1.7 s12.5.4
86
+ class Border < Dictionary
87
+
88
+ define_type :Border
89
+
90
+ define_field :Type, type: Symbol, default: type
91
+ define_field :W, type: [Integer, Float], default: 1
92
+ define_field :S, type: Symbol, default: :S, allowed_values: [:S, :D, :B, :I, :U]
93
+ define_field :D, type: PDFArray, default: [3]
94
+
95
+ end
96
+
49
97
  extend Utils::BitField
50
98
 
51
99
  define_type :Annot
@@ -58,7 +106,7 @@ module HexaPDF
58
106
  define_field :NM, type: String, version: '1.4'
59
107
  define_field :M, type: PDFDate, version: '1.1'
60
108
  define_field :F, type: Integer, default: 0, version: '1.1'
61
- define_field :AP, type: Dictionary, version: '1.2'
109
+ define_field :AP, type: :XXAppearanceDictionary, version: '1.2'
62
110
  define_field :AS, type: Symbol, version: '1.2'
63
111
  define_field :Border, type: PDFArray, default: [0, 0, 1]
64
112
  define_field :C, type: PDFArray, version: '1.1'
@@ -68,7 +116,29 @@ module HexaPDF
68
116
  bit_field(:raw_flags, {invisible: 0, hidden: 1, print: 2, no_zoom: 3, no_rotate: 4,
69
117
  no_view: 5, read_only: 6, locked: 7, toggle_no_view: 8,
70
118
  locked_contents: 9},
71
- lister: "flags", getter: "flagged?", setter: "flag")
119
+ lister: "flags", getter: "flagged?", setter: "flag", unsetter: "unflag")
120
+
121
+ # Returns +true+ because annotation objects must always be indirect objects.
122
+ def must_be_indirect?
123
+ true
124
+ end
125
+
126
+ # Returns the AppearanceDictionary instance associated with the annotation or +nil+ if none is
127
+ # set.
128
+ def appearance
129
+ self[:AP]
130
+ end
131
+
132
+ # Returns +true+ if the widget's normal appearance exists.
133
+ #
134
+ # Note that this checks only if the appearance exists but not if the structure of the
135
+ # appearance dictionary conforms to the expectations of the annotation.
136
+ def appearance?
137
+ return false unless (normal_appearance = appearance&.normal_appearance)
138
+ normal_appearance.kind_of?(HexaPDF::Stream) ||
139
+ (!normal_appearance.empty? &&
140
+ normal_appearance.each.all? {|_k, v| v.kind_of?(HexaPDF::Stream) })
141
+ end
72
142
 
73
143
  private
74
144
 
@@ -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
@@ -53,7 +53,7 @@ module HexaPDF
53
53
  version: '1.2'
54
54
  define_field :PA, type: Dictionary, version: '1.3'
55
55
  define_field :QuadPoints, type: PDFArray, version: '1.6'
56
- define_field :BS, type: Dictionary, version: '1.6'
56
+ define_field :BS, type: :Border, version: '1.6'
57
57
 
58
58
  end
59
59
 
@@ -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
@@ -35,6 +35,8 @@
35
35
  #++
36
36
 
37
37
  require 'hexapdf/type/annotation'
38
+ require 'hexapdf/content'
39
+ require 'hexapdf/serializer'
38
40
 
39
41
  module HexaPDF
40
42
  module Type
@@ -80,9 +82,244 @@ module HexaPDF
80
82
  define_field :MK, type: :XXAppearanceCharacteristics
81
83
  define_field :A, type: Dictionary, version: '1.1'
82
84
  define_field :AA, type: Dictionary, version: '1.2'
83
- define_field :BS, type: Dictionary, version: '1.2'
85
+ define_field :BS, type: :Border, version: '1.2'
84
86
  define_field :Parent, type: Dictionary
85
87
 
88
+ # Returs the AcroForm field object to which this widget annotation belongs.
89
+ #
90
+ # Since a widget and a field can share the same dictionary object, the returned object is
91
+ # often just the widget re-wrapped in the correct field class.
92
+ def form_field
93
+ field = if key?(:Parent) &&
94
+ (tmp = document.wrap(self[:Parent], type: :XXAcroFormField)).terminal_field?
95
+ tmp
96
+ else
97
+ document.wrap(self, type: :XXAcroFormField)
98
+ end
99
+ document.wrap(field, type: :XXAcroFormField, subtype: field[:FT])
100
+ end
101
+
102
+ # :call-seq:
103
+ # widget.background_color => background_color or nil
104
+ # widget.background_color(*color) => widget
105
+ #
106
+ # Returns the current background color as device color object, or +nil+ if no background
107
+ # color is set, when no argument is given. Otherwise sets the background color using the
108
+ # +color+ argument and returns self.
109
+ #
110
+ # See HexaPDF::Content::ColorSpace.device_color_from_specification for information on the
111
+ # allowed arguments.
112
+ def background_color(*color)
113
+ if color.empty?
114
+ components = self[:MK]&.[](:BG)
115
+ components.nil? ? nil : Content::ColorSpace.prenormalized_device_color(components)
116
+ else
117
+ color = Content::ColorSpace.device_color_from_specification(color)
118
+ (self[:MK] ||= {})[:BG] = color.components
119
+ self
120
+ end
121
+ end
122
+
123
+ # Describes the border of an annotation.
124
+ #
125
+ # The +color+ property is either +nil+ if the border is transparent or else a device color
126
+ # object - see HexaPDF::Content::ColorSpace.
127
+ #
128
+ # The +style+ property can be one of the following:
129
+ #
130
+ # :solid:: Solid line.
131
+ # :beveled:: Embossed rectangle seemingly raised above the surface of the page.
132
+ # :inset:: Engraved rectangle receeding into the page.
133
+ # :underlined:: Underlined, i.e. only the bottom border is draw.
134
+ # Array: Dash array describing how to dash the line.
135
+ BorderStyle = Struct.new(:width, :color, :style, :horizontal_corner_radius,
136
+ :vertical_corner_radius)
137
+
138
+ # :call-seq:
139
+ # widget.border_style => border_style
140
+ # widget.border_style(color: 0, width: 1, style: :solid) => widget
141
+ #
142
+ # Returns a BorderStyle instance representing the border style of the widget when no
143
+ # argument is given. Otherwise sets the border style of the widget and returns self.
144
+ #
145
+ # When setting a border style, arguments that are not provided will use the default: a
146
+ # border with a solid, black, 1pt wide line. This also means that multiple invocations will
147
+ # reset *all* prior values.
148
+ #
149
+ # +color+:: The color of the border. See
150
+ # HexaPDF::Content::ColorSpace.device_color_from_specification for information on
151
+ # the allowed arguments.
152
+ #
153
+ # If the special value +:transparent+ is used when setting the color, a
154
+ # transparent is used. A transparent border will return a +nil+ value when getting
155
+ # the border color.
156
+ #
157
+ # +width+:: The width of the border. If set to 0, no border is shown.
158
+ #
159
+ # +style+:: Defines how the border is drawn. can be one of the following:
160
+ #
161
+ # +:solid+:: Draws a solid border.
162
+ # +:beveled+:: Draws a beveled border.
163
+ # +:inset+:: Draws an inset border.
164
+ # +:underlined+:: Draws only the bottom border.
165
+ # Array:: An array specifying a line dash pattern (see
166
+ # HexaPDF::Content::LineDashPattern)
167
+ def border_style(color: nil, width: nil, style: nil)
168
+ if color || width || style
169
+ color = if color == :transparent
170
+ []
171
+ else
172
+ Content::ColorSpace.device_color_from_specification(color || 0).components
173
+ end
174
+ width ||= 1
175
+ style ||= :solid
176
+
177
+ (self[:MK] ||= {})[:BC] = color
178
+ bs = self[:BS] = {W: width}
179
+ case style
180
+ when :solid then bs[:S] = :S
181
+ when :beveled then bs[:S] = :B
182
+ when :inset then bs[:S] = :I
183
+ when :underlined then bs[:S] = :U
184
+ when Array
185
+ bs[:S] = :D
186
+ bs[:D] = style
187
+ else
188
+ raise ArgumentError, "Unknown value #{style} for style argument"
189
+ end
190
+ self
191
+ else
192
+ result = BorderStyle.new(1, nil, :solid, 0, 0)
193
+ if (ac = self[:MK]) && (bc = ac[:BC]) && !bc.empty?
194
+ result.color = Content::ColorSpace.prenormalized_device_color(bc.value)
195
+ end
196
+
197
+ if (bs = self[:BS])
198
+ result.width = bs[:W] if bs.key?(:W)
199
+ result.style = case bs[:S]
200
+ when :S then :solid
201
+ when :B then :beveled
202
+ when :I then :inset
203
+ when :U then :underlined
204
+ when :D then bs[:D].value
205
+ else :solid
206
+ end
207
+ elsif key?(:Border)
208
+ border = self[:Border]
209
+ result.horizontal_corner_radius = border[0]
210
+ result.vertical_corner_radius = border[1]
211
+ result.width = border[2]
212
+ result.style = border[3] if border[3]
213
+ end
214
+
215
+ result
216
+ end
217
+ end
218
+
219
+ # Describes the marker style of a check box or radio button widget.
220
+ class MarkerStyle
221
+
222
+ # The kind of marker that is shown inside the widget. Can either be one of the symbols
223
+ # +:check+, +:circle+, +:cross+, +:diamond+, +:square+ or +:star+, or a one character
224
+ # string. The latter is interpreted using the ZapfDingbats font.
225
+ attr_reader :style
226
+
227
+ # The size of the marker in PDF points that is shown inside the widget. The special value
228
+ # 0 means that the marker should be auto-sized based on the widget's rectangle.
229
+ attr_reader :size
230
+
231
+ # A device color object representing the color of the marker - see
232
+ # HexaPDF::Content::ColorSpace.
233
+ attr_reader :color
234
+
235
+ # Creates a new instance with the given values.
236
+ def initialize(style, size, color)
237
+ @style = style
238
+ @size = size
239
+ @color = color
240
+ end
241
+
242
+ end
243
+
244
+ # :call-seq:
245
+ # widget.marker_style => marker_style
246
+ # widget.marker_style(style: nil, size: nil, color: nil) => widget
247
+ #
248
+ # Returns a MarkerStyle instance representing the marker style of the widget when no
249
+ # argument is given. Otherwise sets the button marker style of the widget and returns self.
250
+ #
251
+ # This method returns valid information only for check boxes and radio buttons!
252
+ #
253
+ # When setting a marker style, arguments that are not provided will use the default: a black
254
+ # auto-sized checkmark (i.e. :check for for check boxes) or circle (:circle for radio
255
+ # buttons). This also means that multiple invocations will reset *all* prior values.
256
+ #
257
+ # Note: The marker is called "normal caption" in the PDF 1.7 spec and the /CA entry of the
258
+ # associated appearance characteristics dictionary. The marker size and color are set using
259
+ # the /DA key on the widget (although /DA is not defined for widget, this is how Acrobat
260
+ # does it).
261
+ #
262
+ # See: PDF1.7 s12.5.6.19 and s17.7.3.3
263
+ def marker_style(style: nil, size: nil, color: nil)
264
+ field = form_field
265
+ if style || size || color
266
+ style ||= (field.check_box? ? :check : :cicrle)
267
+ size ||= 0
268
+ color = Content::ColorSpace.device_color_from_specification(color || 0)
269
+
270
+ self[:MK] ||= {}
271
+ self[:MK][:CA] = case style
272
+ when :check then '4'
273
+ when :circle then 'l'
274
+ when :cross then '8'
275
+ when :diamond then 'u'
276
+ when :square then 'n'
277
+ when :star then 'H'
278
+ when String then style
279
+ else
280
+ raise ArgumentError, "Unknown value #{style} for argument 'style'"
281
+ end
282
+ operator = case color.color_space.family
283
+ when :DeviceRGB then :rg
284
+ when :DeviceGray then :g
285
+ when :DeviceCMYK then :k
286
+ end
287
+ serialized_color = Content::Operator::DEFAULT_OPERATORS[operator].
288
+ serialize(HexaPDF::Serializer.new, *color.components)
289
+ self[:DA] = "/ZaDb #{size} Tf #{serialized_color}".strip
290
+ else
291
+ style = case self[:MK]&.[](:CA)
292
+ when '4' then :check
293
+ when 'l' then :circle
294
+ when '8' then :cross
295
+ when 'u' then :diamond
296
+ when 'n' then :square
297
+ when 'H' then :star
298
+ when String then self[:MK][:CA]
299
+ else
300
+ if field.check_box?
301
+ :check
302
+ else
303
+ :circle
304
+ end
305
+ end
306
+ size = 0
307
+ color = [0]
308
+ if (da = self[:DA] || field[:DA])
309
+ HexaPDF::Content::Parser.parse(da) do |obj, params|
310
+ if obj == :rg || obj == :g || obj == :k
311
+ color = params.dup
312
+ elsif obj == :Tf
313
+ size = params[1]
314
+ end
315
+ end
316
+ end
317
+ color = HexaPDF::Content::ColorSpace.prenormalized_device_color(color)
318
+
319
+ MarkerStyle.new(style, size, color)
320
+ end
321
+ end
322
+
86
323
  end
87
324
 
88
325
  end