pdfrb 0.2.1 → 0.6.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 (406) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +24 -0
  4. data/Gemfile +1 -0
  5. data/PROPOSAL.canvas-clip.md +92 -0
  6. data/PROPOSAL.canvas-draw-image-matrix.md +87 -0
  7. data/PROPOSAL.canvas-rich-text.md +103 -0
  8. data/PROPOSAL.canvas-text-lines.md +77 -0
  9. data/PROPOSAL.canvas-transparency.md +106 -0
  10. data/PROPOSAL.external-font-metrics.md +129 -0
  11. data/PROPOSAL.font-embedding-fontfile2.md +88 -0
  12. data/PROPOSAL.font-resolver-ttc.md +105 -0
  13. data/PROPOSAL.font-resolver.md +96 -0
  14. data/PROPOSAL.font-subsetting.md +98 -0
  15. data/PROPOSAL.gradient-shadings.md +110 -0
  16. data/PROPOSAL.page-mediabox-setter.md +66 -0
  17. data/PROPOSAL.per-glyph-width.md +91 -0
  18. data/PROPOSAL.stringio-require.md +53 -0
  19. data/PROPOSAL.struct-tree-root.md +102 -0
  20. data/PROPOSAL.text-measurement.md +93 -0
  21. data/PROPOSAL.ttf-table-parsing-fix.md +112 -0
  22. data/RELEASE.md +48 -0
  23. data/lib/pdfrb/action/base.rb +21 -0
  24. data/lib/pdfrb/action/types.rb +178 -0
  25. data/lib/pdfrb/action.rb +55 -0
  26. data/lib/pdfrb/annotation/base.rb +79 -0
  27. data/lib/pdfrb/annotation/types.rb +222 -0
  28. data/lib/pdfrb/annotation.rb +78 -0
  29. data/lib/pdfrb/appearance/generator.rb +216 -0
  30. data/lib/pdfrb/appearance.rb +11 -0
  31. data/lib/pdfrb/arlington/object_definition.rb +2 -2
  32. data/lib/pdfrb/arlington/predicate/evaluator.rb +1 -1
  33. data/lib/pdfrb/arlington/predicate/functions.rb +25 -9
  34. data/lib/pdfrb/arlington/predicate.rb +1 -5
  35. data/lib/pdfrb/cli/batch.rb +18 -0
  36. data/lib/pdfrb/cli/debug_info.rb +18 -0
  37. data/lib/pdfrb/cli/files.rb +16 -0
  38. data/lib/pdfrb/cli/fonts.rb +16 -0
  39. data/lib/pdfrb/cli/form.rb +18 -0
  40. data/lib/pdfrb/cli/image2pdf.rb +18 -0
  41. data/lib/pdfrb/cli/images.rb +34 -0
  42. data/lib/pdfrb/cli/info.rb +20 -0
  43. data/lib/pdfrb/cli/inspect.rb +18 -0
  44. data/lib/pdfrb/cli/merge.rb +18 -0
  45. data/lib/pdfrb/cli/modify.rb +18 -0
  46. data/lib/pdfrb/cli/optimize.rb +18 -0
  47. data/lib/pdfrb/cli/split.rb +21 -0
  48. data/lib/pdfrb/cli/watermark.rb +18 -0
  49. data/lib/pdfrb/cli.rb +312 -7
  50. data/lib/pdfrb/color/color_space.rb +259 -0
  51. data/lib/pdfrb/color/icc_profile.rb +90 -0
  52. data/lib/pdfrb/color.rb +12 -0
  53. data/lib/pdfrb/compare/comparator.rb +244 -0
  54. data/lib/pdfrb/compare/report.rb +78 -0
  55. data/lib/pdfrb/compare.rb +42 -0
  56. data/lib/pdfrb/composer.rb +161 -0
  57. data/lib/pdfrb/conformance/pdf_a.rb +317 -56
  58. data/lib/pdfrb/conformance/pdf_ua.rb +186 -29
  59. data/lib/pdfrb/conformance/pdf_x.rb +247 -0
  60. data/lib/pdfrb/conformance/rule.rb +78 -0
  61. data/lib/pdfrb/conformance/structure_elements.rb +174 -0
  62. data/lib/pdfrb/conformance/verapdf_bridge.rb +92 -0
  63. data/lib/pdfrb/conformance.rb +7 -3
  64. data/lib/pdfrb/content/canvas.rb +217 -72
  65. data/lib/pdfrb/content/color_space.rb +157 -0
  66. data/lib/pdfrb/content/hidden_text_detector.rb +167 -0
  67. data/lib/pdfrb/content/operator.rb +21 -1
  68. data/lib/pdfrb/content/operators/clipping.rb +36 -0
  69. data/lib/pdfrb/content/operators/graphics_state_params.rb +15 -0
  70. data/lib/pdfrb/content/operators.rb +21 -0
  71. data/lib/pdfrb/content/shading/base.rb +139 -0
  72. data/lib/pdfrb/content/shading.rb +17 -0
  73. data/lib/pdfrb/content/smart_text_extractor.rb +85 -0
  74. data/lib/pdfrb/content/tiling_pattern.rb +78 -0
  75. data/lib/pdfrb/content/transformation_matrix.rb +61 -0
  76. data/lib/pdfrb/content.rb +7 -26
  77. data/lib/pdfrb/destination/fit.rb +185 -0
  78. data/lib/pdfrb/destination.rb +13 -0
  79. data/lib/pdfrb/digital_signature/cms_handler.rb +22 -0
  80. data/lib/pdfrb/digital_signature/signing.rb +107 -11
  81. data/lib/pdfrb/digital_signature/timestamp_handler.rb +45 -0
  82. data/lib/pdfrb/digital_signature/verification.rb +88 -9
  83. data/lib/pdfrb/digital_signature/verification_result.rb +14 -0
  84. data/lib/pdfrb/digital_signature.rb +26 -2
  85. data/lib/pdfrb/document/annotations.rb +120 -3
  86. data/lib/pdfrb/document/associated_files.rb +104 -0
  87. data/lib/pdfrb/document/colors.rb +95 -0
  88. data/lib/pdfrb/document/destinations.rb +127 -5
  89. data/lib/pdfrb/document/display.rb +84 -0
  90. data/lib/pdfrb/document/fonts.rb +417 -45
  91. data/lib/pdfrb/document/form.rb +338 -0
  92. data/lib/pdfrb/document/form_xobject.rb +68 -0
  93. data/lib/pdfrb/document/graphics_state.rb +91 -0
  94. data/lib/pdfrb/document/images.rb +4 -4
  95. data/lib/pdfrb/document/info.rb +102 -0
  96. data/lib/pdfrb/document/layers.rb +62 -0
  97. data/lib/pdfrb/document/metadata.rb +12 -5
  98. data/lib/pdfrb/document/output_intents.rb +91 -0
  99. data/lib/pdfrb/document/page_labels.rb +80 -0
  100. data/lib/pdfrb/document/pages.rb +68 -19
  101. data/lib/pdfrb/document/portfolio.rb +119 -0
  102. data/lib/pdfrb/document/shadings.rb +159 -0
  103. data/lib/pdfrb/document/stamps.rb +68 -0
  104. data/lib/pdfrb/document/structure.rb +242 -0
  105. data/lib/pdfrb/document.rb +73 -4
  106. data/lib/pdfrb/encryption/identity.rb +13 -0
  107. data/lib/pdfrb/encryption/password_verification.rb +53 -8
  108. data/lib/pdfrb/encryption/security_handler.rb +8 -5
  109. data/lib/pdfrb/encryption/standard_security_handler.rb +154 -81
  110. data/lib/pdfrb/encryption.rb +17 -0
  111. data/lib/pdfrb/error.rb +2 -2
  112. data/lib/pdfrb/filter/brotli_decode.rb +32 -0
  113. data/lib/pdfrb/filter/ccitt_fax_decode.rb +12 -0
  114. data/lib/pdfrb/filter/crypt.rb +2 -2
  115. data/lib/pdfrb/filter/dct_decode.rb +18 -0
  116. data/lib/pdfrb/filter/flate_decode.rb +4 -18
  117. data/lib/pdfrb/filter/jbig2_decode.rb +12 -0
  118. data/lib/pdfrb/filter/jpx_decode.rb +18 -0
  119. data/lib/pdfrb/filter/lzw_decode.rb +3 -2
  120. data/lib/pdfrb/filter/png_predictor.rb +94 -0
  121. data/lib/pdfrb/filter.rb +26 -13
  122. data/lib/pdfrb/font/afm_parser.rb +50 -26
  123. data/lib/pdfrb/font/cmap/writer.rb +9 -0
  124. data/lib/pdfrb/font/encoding/win_ansi_encoding.rb +17 -0
  125. data/lib/pdfrb/font/encoding.rb +32 -3
  126. data/lib/pdfrb/font/metrics_helper.rb +121 -0
  127. data/lib/pdfrb/font/true_type/file.rb +10 -1
  128. data/lib/pdfrb/font/true_type/glyf.rb +82 -0
  129. data/lib/pdfrb/font/true_type/kern.rb +58 -0
  130. data/lib/pdfrb/font/true_type/loca.rb +42 -0
  131. data/lib/pdfrb/font/true_type/maxp.rb +44 -0
  132. data/lib/pdfrb/font/true_type/name.rb +53 -0
  133. data/lib/pdfrb/font/true_type/post.rb +93 -0
  134. data/lib/pdfrb/font/true_type/subsetter.rb +3 -5
  135. data/lib/pdfrb/font/true_type/wrapper.rb +102 -0
  136. data/lib/pdfrb/font/true_type.rb +7 -0
  137. data/lib/pdfrb/font.rb +2 -0
  138. data/lib/pdfrb/font_loader/from_configuration.rb +19 -0
  139. data/lib/pdfrb/font_loader/from_file.rb +20 -0
  140. data/lib/pdfrb/font_loader/standard14.rb +20 -0
  141. data/lib/pdfrb/font_loader/variant_from_name.rb +36 -0
  142. data/lib/pdfrb/font_loader.rb +4 -0
  143. data/lib/pdfrb/font_resolver.rb +138 -0
  144. data/lib/pdfrb/image/audit.rb +94 -0
  145. data/lib/pdfrb/image/downsampler.rb +124 -0
  146. data/lib/pdfrb/image.rb +13 -0
  147. data/lib/pdfrb/image_loader/jpeg.rb +39 -58
  148. data/lib/pdfrb/image_loader/png.rb +31 -257
  149. data/lib/pdfrb/layout/bidi.rb +198 -0
  150. data/lib/pdfrb/layout/box.rb +49 -0
  151. data/lib/pdfrb/layout/box_fitter.rb +54 -0
  152. data/lib/pdfrb/layout/column_box.rb +71 -0
  153. data/lib/pdfrb/layout/container_box.rb +45 -0
  154. data/lib/pdfrb/layout/frame.rb +67 -0
  155. data/lib/pdfrb/layout/image_box.rb +44 -0
  156. data/lib/pdfrb/layout/inline_box.rb +25 -0
  157. data/lib/pdfrb/layout/line.rb +55 -0
  158. data/lib/pdfrb/layout/list_box.rb +87 -0
  159. data/lib/pdfrb/layout/numeric_refinements.rb +43 -0
  160. data/lib/pdfrb/layout/page_style.rb +65 -0
  161. data/lib/pdfrb/layout/style.rb +62 -0
  162. data/lib/pdfrb/layout/table_box.rb +66 -0
  163. data/lib/pdfrb/layout/text_box.rb +38 -0
  164. data/lib/pdfrb/layout/text_fragment.rb +45 -0
  165. data/lib/pdfrb/layout/text_layouter.rb +123 -0
  166. data/lib/pdfrb/layout.rb +28 -0
  167. data/lib/pdfrb/linearization/hint_stream.rb +93 -0
  168. data/lib/pdfrb/linearization/writer.rb +259 -0
  169. data/lib/pdfrb/linearization.rb +21 -0
  170. data/lib/pdfrb/model/cos/dictionary.rb +12 -0
  171. data/lib/pdfrb/model/pdf_array.rb +2 -2
  172. data/lib/pdfrb/model/type/action.rb +42 -0
  173. data/lib/pdfrb/model/type/action_go_to_3d_view.rb +14 -0
  174. data/lib/pdfrb/model/type/action_go_to_dp.rb +16 -0
  175. data/lib/pdfrb/model/type/action_go_to_r.rb +26 -0
  176. data/lib/pdfrb/model/type/action_goto.rb +34 -0
  177. data/lib/pdfrb/model/type/action_hide.rb +26 -0
  178. data/lib/pdfrb/model/type/action_import_data.rb +18 -0
  179. data/lib/pdfrb/model/type/action_java_script.rb +18 -0
  180. data/lib/pdfrb/model/type/action_launch.rb +22 -0
  181. data/lib/pdfrb/model/type/action_movie.rb +22 -0
  182. data/lib/pdfrb/model/type/action_named.rb +23 -0
  183. data/lib/pdfrb/model/type/action_nop.rb +12 -0
  184. data/lib/pdfrb/model/type/action_rendition.rb +28 -0
  185. data/lib/pdfrb/model/type/action_reset_form.rb +22 -0
  186. data/lib/pdfrb/model/type/action_rich_media_execute.rb +17 -0
  187. data/lib/pdfrb/model/type/action_set_ocg_state.rb +32 -0
  188. data/lib/pdfrb/model/type/action_set_state.rb +14 -0
  189. data/lib/pdfrb/model/type/action_sound_action.rb +23 -0
  190. data/lib/pdfrb/model/type/action_submit_form.rb +64 -0
  191. data/lib/pdfrb/model/type/action_thread.rb +23 -0
  192. data/lib/pdfrb/model/type/action_trans.rb +30 -0
  193. data/lib/pdfrb/model/type/action_uri.rb +30 -0
  194. data/lib/pdfrb/model/type/af_embedded_file.rb +11 -0
  195. data/lib/pdfrb/model/type/af_file_specification.rb +34 -0
  196. data/lib/pdfrb/model/type/annotation.rb +49 -10
  197. data/lib/pdfrb/model/type/appearance.rb +41 -0
  198. data/lib/pdfrb/model/type/appearance_characteristics.rb +36 -0
  199. data/lib/pdfrb/model/type/appearance_generator.rb +22 -0
  200. data/lib/pdfrb/model/type/appearance_trap_net.rb +84 -0
  201. data/lib/pdfrb/model/type/border_effect.rb +25 -0
  202. data/lib/pdfrb/model/type/border_styling.rb +38 -0
  203. data/lib/pdfrb/model/type/box_color_info.rb +21 -0
  204. data/lib/pdfrb/model/type/box_style.rb +19 -0
  205. data/lib/pdfrb/model/type/button.rb +17 -0
  206. data/lib/pdfrb/model/type/cal_gray.rb +17 -0
  207. data/lib/pdfrb/model/type/cal_rgb.rb +22 -0
  208. data/lib/pdfrb/model/type/caret_annotation.rb +21 -0
  209. data/lib/pdfrb/model/type/catalog.rb +99 -16
  210. data/lib/pdfrb/model/type/choice.rb +21 -0
  211. data/lib/pdfrb/model/type/cid_font.rb +52 -1
  212. data/lib/pdfrb/model/type/cid_font_descriptor_metrics.rb +28 -0
  213. data/lib/pdfrb/model/type/cid_system_info.rb +28 -0
  214. data/lib/pdfrb/model/type/circle_annotation.rb +20 -0
  215. data/lib/pdfrb/model/type/collection.rb +27 -0
  216. data/lib/pdfrb/model/type/collection_field.rb +24 -0
  217. data/lib/pdfrb/model/type/collection_item.rb +19 -0
  218. data/lib/pdfrb/model/type/collection_schema.rb +25 -0
  219. data/lib/pdfrb/model/type/collection_sort.rb +13 -0
  220. data/lib/pdfrb/model/type/crypt_filter.rb +58 -0
  221. data/lib/pdfrb/model/type/device_n.rb +37 -0
  222. data/lib/pdfrb/model/type/device_n_mixing_hints.rb +17 -0
  223. data/lib/pdfrb/model/type/device_n_process.rb +13 -0
  224. data/lib/pdfrb/model/type/doc_mdp_transform_parameters.rb +21 -0
  225. data/lib/pdfrb/model/type/document_security_store.rb +55 -0
  226. data/lib/pdfrb/model/type/embedded_file.rb +19 -0
  227. data/lib/pdfrb/model/type/embedded_file_parameter.rb +48 -0
  228. data/lib/pdfrb/model/type/encryption_public_key.rb +38 -0
  229. data/lib/pdfrb/model/type/encryption_standard.rb +68 -0
  230. data/lib/pdfrb/model/type/field_mdp_transform_parameters.rb +26 -0
  231. data/lib/pdfrb/model/type/file_attachment_annotation.rb +29 -0
  232. data/lib/pdfrb/model/type/file_spec_ef.rb +22 -0
  233. data/lib/pdfrb/model/type/file_spec_rf.rb +21 -0
  234. data/lib/pdfrb/model/type/file_specification.rb +43 -0
  235. data/lib/pdfrb/model/type/file_trailer.rb +54 -0
  236. data/lib/pdfrb/model/type/font.rb +73 -2
  237. data/lib/pdfrb/model/type/font_cid_type0.rb +17 -0
  238. data/lib/pdfrb/model/type/font_cid_type2.rb +17 -0
  239. data/lib/pdfrb/model/type/font_descriptor.rb +47 -0
  240. data/lib/pdfrb/model/type/font_descriptor_cid_type0.rb +15 -0
  241. data/lib/pdfrb/model/type/font_descriptor_cid_type2.rb +15 -0
  242. data/lib/pdfrb/model/type/font_encoding.rb +47 -0
  243. data/lib/pdfrb/model/type/font_file.rb +28 -0
  244. data/lib/pdfrb/model/type/font_file2.rb +32 -0
  245. data/lib/pdfrb/model/type/font_file3.rb +23 -0
  246. data/lib/pdfrb/model/type/font_multiple_master.rb +60 -0
  247. data/lib/pdfrb/model/type/font_true_type.rb +1 -1
  248. data/lib/pdfrb/model/type/font_type0.rb +1 -1
  249. data/lib/pdfrb/model/type/font_type1.rb +1 -1
  250. data/lib/pdfrb/model/type/font_type3.rb +1 -1
  251. data/lib/pdfrb/model/type/form_field.rb +73 -0
  252. data/lib/pdfrb/model/type/free_text_annotation.rb +37 -0
  253. data/lib/pdfrb/model/type/function.rb +42 -0
  254. data/lib/pdfrb/model/type/function_exponential.rb +31 -0
  255. data/lib/pdfrb/model/type/function_postscript.rb +25 -0
  256. data/lib/pdfrb/model/type/function_sampled.rb +55 -0
  257. data/lib/pdfrb/model/type/function_stitching.rb +33 -0
  258. data/lib/pdfrb/model/type/generic_appearance.rb +40 -0
  259. data/lib/pdfrb/model/type/graphics_state_parameter.rb +69 -0
  260. data/lib/pdfrb/model/type/halftone.rb +21 -0
  261. data/lib/pdfrb/model/type/halftone_type1.rb +17 -0
  262. data/lib/pdfrb/model/type/halftone_type10.rb +16 -0
  263. data/lib/pdfrb/model/type/halftone_type16.rb +11 -0
  264. data/lib/pdfrb/model/type/halftone_type5.rb +24 -0
  265. data/lib/pdfrb/model/type/halftone_type6.rb +16 -0
  266. data/lib/pdfrb/model/type/highlight_annotation.rb +11 -0
  267. data/lib/pdfrb/model/type/icc_based_color_space.rb +24 -0
  268. data/lib/pdfrb/model/type/icon_fit.rb +31 -0
  269. data/lib/pdfrb/model/type/indexed.rb +32 -0
  270. data/lib/pdfrb/model/type/info.rb +44 -0
  271. data/lib/pdfrb/model/type/ink_annotation.rb +29 -0
  272. data/lib/pdfrb/model/type/interactive_form.rb +36 -0
  273. data/lib/pdfrb/model/type/interior_color.rb +12 -0
  274. data/lib/pdfrb/model/type/jbig2_globals.rb +15 -0
  275. data/lib/pdfrb/model/type/lab.rb +27 -0
  276. data/lib/pdfrb/model/type/line_annotation.rb +44 -0
  277. data/lib/pdfrb/model/type/line_ending_styling.rb +39 -0
  278. data/lib/pdfrb/model/type/link_annotation.rb +41 -0
  279. data/lib/pdfrb/model/type/link_appearance.rb +9 -0
  280. data/lib/pdfrb/model/type/mark_information.rb +23 -0
  281. data/lib/pdfrb/model/type/marked_content_reference.rb +24 -0
  282. data/lib/pdfrb/model/type/markup_annotation.rb +36 -0
  283. data/lib/pdfrb/model/type/mdp_dict.rb +28 -0
  284. data/lib/pdfrb/model/type/measure.rb +39 -0
  285. data/lib/pdfrb/model/type/media_clip.rb +28 -0
  286. data/lib/pdfrb/model/type/media_offset.rb +106 -0
  287. data/lib/pdfrb/model/type/metadata.rb +27 -3
  288. data/lib/pdfrb/model/type/misc_helpers.rb +75 -0
  289. data/lib/pdfrb/model/type/movie.rb +28 -0
  290. data/lib/pdfrb/model/type/names.rb +51 -0
  291. data/lib/pdfrb/model/type/namespace.rb +27 -0
  292. data/lib/pdfrb/model/type/object_reference.rb +31 -0
  293. data/lib/pdfrb/model/type/object_stream.rb +38 -0
  294. data/lib/pdfrb/model/type/opt_content_ext.rb +94 -0
  295. data/lib/pdfrb/model/type/opt_content_page_element.rb +18 -0
  296. data/lib/pdfrb/model/type/optional_content_config.rb +56 -0
  297. data/lib/pdfrb/model/type/optional_content_group.rb +35 -10
  298. data/lib/pdfrb/model/type/optional_content_membership.rb +36 -2
  299. data/lib/pdfrb/model/type/optional_content_properties.rb +16 -1
  300. data/lib/pdfrb/model/type/outline.rb +40 -6
  301. data/lib/pdfrb/model/type/outline_item.rb +45 -0
  302. data/lib/pdfrb/model/type/output_intent.rb +30 -0
  303. data/lib/pdfrb/model/type/page.rb +127 -0
  304. data/lib/pdfrb/model/type/page_label.rb +68 -0
  305. data/lib/pdfrb/model/type/page_tree_node.rb +51 -16
  306. data/lib/pdfrb/model/type/page_tree_node_root.rb +15 -0
  307. data/lib/pdfrb/model/type/pattern.rb +16 -0
  308. data/lib/pdfrb/model/type/pattern_shading.rb +25 -0
  309. data/lib/pdfrb/model/type/pattern_tiling.rb +25 -0
  310. data/lib/pdfrb/model/type/polygon_annotation.rb +29 -0
  311. data/lib/pdfrb/model/type/polygon_polyline.rb +14 -0
  312. data/lib/pdfrb/model/type/polyline_annotation.rb +29 -0
  313. data/lib/pdfrb/model/type/popup_annotation.rb +20 -0
  314. data/lib/pdfrb/model/type/printer_mark_annotation.rb +13 -0
  315. data/lib/pdfrb/model/type/redact_annotation.rb +28 -0
  316. data/lib/pdfrb/model/type/rendition.rb +31 -0
  317. data/lib/pdfrb/model/type/resources.rb +59 -14
  318. data/lib/pdfrb/model/type/rich_media_activation.rb +24 -0
  319. data/lib/pdfrb/model/type/rich_media_animation.rb +25 -0
  320. data/lib/pdfrb/model/type/rich_media_annotation.rb +18 -0
  321. data/lib/pdfrb/model/type/rich_media_command.rb +19 -0
  322. data/lib/pdfrb/model/type/rich_media_configuration.rb +32 -0
  323. data/lib/pdfrb/model/type/rich_media_content.rb +28 -0
  324. data/lib/pdfrb/model/type/rich_media_cue_point.rb +18 -0
  325. data/lib/pdfrb/model/type/rich_media_deactivation.rb +17 -0
  326. data/lib/pdfrb/model/type/rich_media_instance.rb +33 -0
  327. data/lib/pdfrb/model/type/rich_media_presentation.rb +29 -0
  328. data/lib/pdfrb/model/type/rich_media_settings.rb +33 -0
  329. data/lib/pdfrb/model/type/rich_media_window.rb +20 -0
  330. data/lib/pdfrb/model/type/screen_annotation.rb +19 -0
  331. data/lib/pdfrb/model/type/separation.rb +25 -0
  332. data/lib/pdfrb/model/type/sig_field_lock.rb +25 -0
  333. data/lib/pdfrb/model/type/sig_field_seed_value.rb +25 -0
  334. data/lib/pdfrb/model/type/signature.rb +65 -0
  335. data/lib/pdfrb/model/type/signature_build.rb +86 -0
  336. data/lib/pdfrb/model/type/signature_field.rb +17 -0
  337. data/lib/pdfrb/model/type/software_identifier.rb +19 -0
  338. data/lib/pdfrb/model/type/sound.rb +38 -0
  339. data/lib/pdfrb/model/type/sound_annotation.rb +21 -0
  340. data/lib/pdfrb/model/type/square_annotation.rb +24 -0
  341. data/lib/pdfrb/model/type/square_circle.rb +13 -0
  342. data/lib/pdfrb/model/type/squiggly_annotation.rb +11 -0
  343. data/lib/pdfrb/model/type/stamp_annotation.rb +19 -0
  344. data/lib/pdfrb/model/type/strikeout_annotation.rb +11 -0
  345. data/lib/pdfrb/model/type/struct_elem.rb +75 -0
  346. data/lib/pdfrb/model/type/struct_tree_root.rb +34 -0
  347. data/lib/pdfrb/model/type/structure_attributes.rb +44 -0
  348. data/lib/pdfrb/model/type/structure_element_kid.rb +33 -0
  349. data/lib/pdfrb/model/type/text_annotation.rb +25 -0
  350. data/lib/pdfrb/model/type/text_appearance.rb +9 -0
  351. data/lib/pdfrb/model/type/text_field.rb +21 -0
  352. data/lib/pdfrb/model/type/text_markup_annotation.rb +30 -0
  353. data/lib/pdfrb/model/type/three_d_activation.rb +32 -0
  354. data/lib/pdfrb/model/type/three_d_animation_style.rb +24 -0
  355. data/lib/pdfrb/model/type/three_d_background.rb +28 -0
  356. data/lib/pdfrb/model/type/three_d_cross_section.rb +25 -0
  357. data/lib/pdfrb/model/type/three_d_lighting_scheme.rb +26 -0
  358. data/lib/pdfrb/model/type/three_d_measure.rb +20 -0
  359. data/lib/pdfrb/model/type/three_d_measure_3dc.rb +23 -0
  360. data/lib/pdfrb/model/type/three_d_measure_ad3.rb +14 -0
  361. data/lib/pdfrb/model/type/three_d_measure_ld3.rb +14 -0
  362. data/lib/pdfrb/model/type/three_d_measure_pd3.rb +20 -0
  363. data/lib/pdfrb/model/type/three_d_measure_rd3.rb +14 -0
  364. data/lib/pdfrb/model/type/three_d_node.rb +37 -0
  365. data/lib/pdfrb/model/type/three_d_reference.rb +21 -0
  366. data/lib/pdfrb/model/type/three_d_render_mode.rb +34 -0
  367. data/lib/pdfrb/model/type/three_d_stream.rb +48 -0
  368. data/lib/pdfrb/model/type/three_d_units.rb +18 -0
  369. data/lib/pdfrb/model/type/three_d_view.rb +40 -0
  370. data/lib/pdfrb/model/type/to_unicode_cmap_stream.rb +23 -0
  371. data/lib/pdfrb/model/type/transition.rb +68 -0
  372. data/lib/pdfrb/model/type/underline_annotation.rb +11 -0
  373. data/lib/pdfrb/model/type/ur_transform_parameters.rb +33 -0
  374. data/lib/pdfrb/model/type/uri_dict.rb +18 -0
  375. data/lib/pdfrb/model/type/url_alias.rb +19 -0
  376. data/lib/pdfrb/model/type/variable_text_field.rb +47 -0
  377. data/lib/pdfrb/model/type/viewer_preferences.rb +42 -0
  378. data/lib/pdfrb/model/type/watermark_annotation.rb +13 -0
  379. data/lib/pdfrb/model/type/widget_annotation.rb +48 -0
  380. data/lib/pdfrb/model/type/widget_appearance.rb +9 -0
  381. data/lib/pdfrb/model/type/xobject_form.rb +41 -0
  382. data/lib/pdfrb/model/type/xobject_image.rb +56 -0
  383. data/lib/pdfrb/model/type/xref_stream.rb +37 -0
  384. data/lib/pdfrb/model/type.rb +286 -6
  385. data/lib/pdfrb/serializer.rb +2 -2
  386. data/lib/pdfrb/source/tokenizer.rb +0 -2
  387. data/lib/pdfrb/task/benchmark.rb +83 -0
  388. data/lib/pdfrb/task/extract_text.rb +1 -1
  389. data/lib/pdfrb/task/generate_corpus.rb +130 -0
  390. data/lib/pdfrb/task/memory_profile.rb +84 -0
  391. data/lib/pdfrb/task/optimize.rb +97 -7
  392. data/lib/pdfrb/task.rb +3 -3
  393. data/lib/pdfrb/test_utils.rb +112 -0
  394. data/lib/pdfrb/validator.rb +82 -0
  395. data/lib/pdfrb/version.rb +1 -1
  396. data/lib/pdfrb/writer.rb +117 -27
  397. data/lib/pdfrb/xmp/packet.rb +122 -0
  398. data/lib/pdfrb/xmp/schemas.rb +96 -0
  399. data/lib/pdfrb/xmp.rb +44 -0
  400. data/lib/pdfrb/xref_section.rb +2 -2
  401. data/lib/pdfrb.rb +25 -53
  402. data/pdfrb.gemspec +45 -0
  403. data/script/apply_fixes.sh +843 -0
  404. data/script/make_jpeg_fixture.rb +53 -0
  405. data/script/make_png_fixture.rb +25 -0
  406. metadata +312 -2
@@ -0,0 +1,53 @@
1
+ # PROPOSAL: Missing `require "stringio"` in Fonts
2
+
3
+ ## Summary
4
+
5
+ `Pdfrb::Document::Fonts` references `StringIO` without requiring it.
6
+ When pdfrb is loaded without another gem that happens to require
7
+ `stringio` first, `Fonts#register_font` raises `NameError`.
8
+
9
+ ## Bug description
10
+
11
+ In `fonts.rb`, line 204:
12
+
13
+ ```ruby
14
+ def register_font(resource, name, name_or_io, **opts)
15
+ font_dict = if name_or_io.is_a?(IO) || name_or_io.is_a?(StringIO)
16
+ # ^^^^^^^^ NameError if stringio not loaded
17
+ ```
18
+
19
+ Line 207 also uses `StringIO`:
20
+
21
+ ```ruby
22
+ io = StringIO.new(File.binread(name_or_io))
23
+ ```
24
+
25
+ There is no `require "stringio"` at the top of `fonts.rb`.
26
+
27
+ ## Reproduction
28
+
29
+ ```ruby
30
+ # In a clean Ruby process where stringio hasn't been loaded:
31
+ require "pdfrb"
32
+ doc = Pdfrb::Document.new
33
+ doc.fonts.add("/path/to/font.ttf")
34
+ # NameError: uninitialized constant Pdfrb::Document::Fonts::StringIO
35
+ ```
36
+
37
+ In practice, other gems often load `stringio` first, masking this bug.
38
+ But it surfaces in minimal environments.
39
+
40
+ ## Proposed fix
41
+
42
+ Add to the top of `lib/pdfrb/document/fonts.rb`:
43
+
44
+ ```ruby
45
+ require "stringio"
46
+ ```
47
+
48
+ Or add it to `lib/pdfrb.rb` (the main entry point).
49
+
50
+ ## Priority
51
+
52
+ Low — easy one-line fix, but rare in practice (other gems usually
53
+ load stringio first).
@@ -0,0 +1,102 @@
1
+ # PROPOSAL: StructTreeRoot for tagged PDF (PDF/UA)
2
+
3
+ ## Summary
4
+
5
+ Add `Document#structure` facade for building PDF structure trees
6
+ (`/StructTreeRoot`), enabling tagged PDF output for accessibility
7
+ (PDF/UA, WCAG 2.1, Section 508 compliance).
8
+
9
+ ## Motivation
10
+
11
+ pdfrb 0.3.0 already has Canvas tagged-content support (`tagged`,
12
+ `artifact`, `marked_content`), but lacks the `/StructTreeRoot` assembly
13
+ that ties tagged content sequences into a navigable structure tree.
14
+ Without StructTreeRoot, screen readers cannot navigate the document.
15
+
16
+ The `idml` gem needs tagged PDF output for:
17
+ - Government documents (Section 508 compliance)
18
+ - Academic publishing (WCAG 2.1 AA)
19
+ - Accessible PDF/A (PDF/A-1a, PDF/A-2a)
20
+
21
+ ## Proposed API
22
+
23
+ ```ruby
24
+ # Build structure tree
25
+ doc.structure.add_element(:H1, text: "Chapter 1", page: 0, mcid: 0)
26
+ doc.structure.add_element(:P, text: "Body text...", page: 0, mcid: 1)
27
+ doc.structure.add_element(:Figure, alt: "Diagram", page: 1, mcid: 2)
28
+ doc.structure.add_element(:TR, children: [
29
+ doc.structure.element(:TH, text: "Name"),
30
+ doc.structure.element(:TH, text: "Value"),
31
+ ])
32
+
33
+ # Mark content in the Canvas
34
+ page.canvas.tagged(:H1, mcid: 0) { canvas.text("Chapter 1", ...) }
35
+ page.canvas.tagged(:P, mcid: 1) { canvas.text("Body text...", ...) }
36
+
37
+ # Build and attach to Catalog
38
+ doc.structure.build!
39
+ ```
40
+
41
+ ## PDF structure
42
+
43
+ ```
44
+ /StructTreeRoot
45
+ /Type /StructTreeRoot
46
+ /K [ struct_elem_0 struct_elem_1 ... ]
47
+ /ParentTree << /Nums [ 0 [elem_ref] 1 [elem_ref] ... ] >>
48
+ /ParentTreeNextKey N
49
+
50
+ /StructElem (each)
51
+ /Type /StructElem
52
+ /S /H1 (structure type: H1, P, Figure, TR, TH, TD, ...)
53
+ /P struct_tree_root (parent)
54
+ /K mcid_or_ref (content reference)
55
+ /Pg page_ref (page containing the content)
56
+ /Alt (alt text) (for figures)
57
+ ```
58
+
59
+ ## Implementation outline
60
+
61
+ ```ruby
62
+ class Pdfrb::Document
63
+ class Structure
64
+ attr_reader :document, :elements
65
+
66
+ def initialize(document)
67
+ @document = document
68
+ @elements = []
69
+ @next_mcid = 0
70
+ end
71
+
72
+ def add_element(type, text: nil, alt: nil, page:, mcid:, **opts)
73
+ elem = StructElement.new(type, text:, alt:, page:, mcid:, **opts)
74
+ @elements << elem
75
+ elem
76
+ end
77
+
78
+ def next_mcid
79
+ id = @next_mcid
80
+ @next_mcid += 1
81
+ id
82
+ end
83
+
84
+ # Build /StructTreeRoot and attach to Catalog.
85
+ def build!
86
+ return if @elements.empty?
87
+
88
+ root_id = allocate_struct_tree_root
89
+ @elements.each { |e| e.build_object(document, root_id) }
90
+ build_parent_tree(root_id)
91
+ document.catalog[:StructTreeRoot] = Pdfrb::Model::Reference.new(root_id)
92
+ document.catalog[:MarkInfo] = { Type: :MarkInfo, Marked: true }
93
+ end
94
+ end
95
+ end
96
+ ```
97
+
98
+ ## Priority
99
+
100
+ Medium — the Canvas already supports marked content (`BMC`/`EMC`), so
101
+ this is the missing piece for full tagged PDF. Not blocking for basic
102
+ rendering but essential for accessibility compliance.
@@ -0,0 +1,93 @@
1
+ # PROPOSAL: Canvas text measurement API
2
+
3
+ ## Summary
4
+
5
+ Add `Document#fonts#measure_text(text, font:, size:)` returning the
6
+ rendered width in points. Enables callers to do line breaking, text
7
+ centering, and frame-fitting without a separate font metrics library.
8
+
9
+ ## Motivation
10
+
11
+ The `idml` gem has its own text layout engine (`Idml::TextEngine`) with
12
+ a `FontMetrics` class that reads TTF tables via Fontisan. This is
13
+ necessary because pdfrb doesn't expose text measurement. If pdfrb
14
+ provided this, the idml gem could:
15
+
16
+ 1. Remove the entire `Idml::TextEngine::FontMetrics` class (~200 lines).
17
+ 2. Remove the `Fontisan` dependency.
18
+ 3. Use pdfrb's own font data (which it already loads for `encode_text`).
19
+
20
+ ## Proposed API
21
+
22
+ ```ruby
23
+ # Measure the rendered width of a text string at a given font and size.
24
+ # @param text [String] the text to measure.
25
+ # @param font [Symbol] font resource name from Fonts#add.
26
+ # @param size [Float] font size in points.
27
+ # @return [Float] width in points.
28
+ def measure_text(text, font:, size:)
29
+ afm = afm_for_font(font)
30
+ return 0.0 unless afm
31
+
32
+ scale = size / 1000.0
33
+ text.each_char.sum { |char| (afm.widths[char] || 0) * scale }
34
+ end
35
+
36
+ # Get font metrics for a registered font.
37
+ # @param font [Symbol] font resource name.
38
+ # @return [Hash] { ascent:, descent:, cap_height:, x_height:, units_per_em: }
39
+ def metrics_for(font)
40
+ afm = afm_for_font(font)
41
+ return {} unless afm
42
+
43
+ {
44
+ ascent: afm.ascent,
45
+ descent: afm.descent,
46
+ cap_height: afm.cap_height,
47
+ x_height: afm.x_height,
48
+ units_per_em: 1000,
49
+ }
50
+ end
51
+ ```
52
+
53
+ ## Implementation notes
54
+
55
+ pdfrb already loads AFM (Adobe Font Metrics) data for the 14 standard
56
+ Type1 fonts (`AFM_DIR`). For embedded TrueType fonts, the glyph widths
57
+ are in the font's `hmtx` table, which pdfrb parses during font loading.
58
+
59
+ The `measure_text` method would:
60
+ 1. For standard fonts: use AFM width data (already loaded).
61
+ 2. For embedded TrueType: use the glyph widths from `hmtx`.
62
+ 3. For unknown fonts: return 0.0 or raise.
63
+
64
+ ## Usage example
65
+
66
+ ```ruby
67
+ font = doc.fonts.add("Helvetica")
68
+ width = doc.fonts.measure_text("Hello, World!", font: font, size: 12)
69
+ # => ~63.0 (points)
70
+
71
+ # Line breaking
72
+ frame_width = 200
73
+ words = text.split
74
+ line = +""
75
+ lines = []
76
+ words.each do |word|
77
+ test = "#{line} #{word}".strip
78
+ if doc.fonts.measure_text(test, font: font, size: 12) <= frame_width
79
+ line = test
80
+ else
81
+ lines << line
82
+ line = word
83
+ end
84
+ end
85
+ lines << line if line.any?
86
+ ```
87
+
88
+ ## Priority
89
+
90
+ Medium — the idml gem has a workaround (its own FontMetrics), but
91
+ centralizing measurement in pdfrb reduces duplication and eliminates
92
+ the Fontisan dependency for idml consumers who don't need standalone
93
+ font parsing.
@@ -0,0 +1,112 @@
1
+ # PROPOSAL: Fix TTF table parsing — table() returns raw bytes instead of parsed objects
2
+
3
+ ## Summary
4
+
5
+ `Pdfrb::Font::TrueType::File#table("cmap")` returns a raw `String` of
6
+ table bytes instead of a parsed table object. This breaks
7
+ `Fonts#glyph_width`, `Fonts#glyph_widths`, `Fonts#measure_text`, and
8
+ `Fonts#extract_ttf_widths` for all TrueType/OpenType fonts.
9
+
10
+ ## Bug description
11
+
12
+ In `extract_ttf_widths` (fonts.rb:380-400):
13
+
14
+ ```ruby
15
+ ttf = Pdfrb::Font::TrueType::File.new(data)
16
+ cmap = ttf.table("cmap") # Returns: String (raw bytes)
17
+ hmtx = ttf.table("hmtx") # Returns: String (raw bytes)
18
+
19
+ gid = cmap.glyph_id_for(cp) # ERROR: undefined method 'glyph_id_for' for String
20
+ ```
21
+
22
+ `ttf.table("cmap")` returns the raw binary data of the cmap table as
23
+ a String, not a parsed `CmapTable` object with a `glyph_id_for` method.
24
+
25
+ ## Reproduction
26
+
27
+ ```ruby
28
+ require "pdfrb"
29
+ doc = Pdfrb::Document.new
30
+ font = doc.fonts.add("/System/Library/Fonts/Supplemental/Arial.ttf")
31
+ doc.fonts.glyph_width(font, 65)
32
+ # => 0 (should be ~667 for 'A' in Arial)
33
+ # Warning: pdfrb: could not extract TTF widths: undefined method
34
+ # 'glyph_id_for' for an instance of String
35
+ ```
36
+
37
+ Standard 14 fonts (Helvetica, Times, Courier) work because they use
38
+ AFM data, not TTF parsing.
39
+
40
+ ## Impact
41
+
42
+ - `glyph_width` / `glyph_widths` return 0 for all TTF/OTF fonts.
43
+ - `measure_text` returns 0 for TTF/OTF text.
44
+ - `metrics_for` returns empty hash for TTF/OTF fonts.
45
+ - Font width arrays in FontDescriptor are empty or wrong.
46
+ - Font embedding (FontFile2) may have incorrect /Widths array.
47
+
48
+ This blocks the `idml` gem from:
49
+ - Using pdfrb's measurement API (TODO 63)
50
+ - Replacing Fontisan (the idml gem keeps fontisan as a fallback)
51
+ - Producing correct PDFs with embedded TrueType fonts
52
+
53
+ ## Proposed fix
54
+
55
+ `TrueType::File#table(name)` should return a parsed table object:
56
+
57
+ ```ruby
58
+ class Pdfrb::Font::TrueType::File
59
+ def table(name)
60
+ entry = @tables[name]
61
+ return nil unless entry
62
+
63
+ data = @data[entry.offset, entry.length]
64
+ parse_table(name, data)
65
+ end
66
+
67
+ def parse_table(name, data)
68
+ case name
69
+ when "cmap" then CmapTable.new(data)
70
+ when "hmtx" then HmtxTable.new(data, @num_hmetrics)
71
+ when "head" then HeadTable.new(data)
72
+ when "hhea" then HheaTable.new(data)
73
+ when "name" then NameTable.new(data)
74
+ else data # fallback: return raw bytes
75
+ end
76
+ end
77
+ end
78
+
79
+ class CmapTable
80
+ def initialize(data)
81
+ @data = data
82
+ @subtables = parse_subtables(data)
83
+ end
84
+
85
+ def glyph_id_for(codepoint)
86
+ @subtables.each do |sub|
87
+ gid = sub.lookup(codepoint)
88
+ return gid if gid && gid > 0
89
+ end
90
+ nil
91
+ end
92
+ end
93
+
94
+ class HmtxTable
95
+ def initialize(data, num_metrics)
96
+ @widths = []
97
+ num_metrics.times do |i|
98
+ @widths[i] = data[i * 4, 2].unpack1("n")
99
+ end
100
+ end
101
+
102
+ def width_for(glyph_id)
103
+ @widths[glyph_id] || @widths.last || 0
104
+ end
105
+ end
106
+ ```
107
+
108
+ ## Priority
109
+
110
+ **Critical** — this is the #1 bug blocking pdfrb adoption for the idml
111
+ gem. Without TTF parsing, glyph widths are wrong, text measurement
112
+ fails, and font embedding produces incorrect PDFs.
data/RELEASE.md ADDED
@@ -0,0 +1,48 @@
1
+ # Releasing pdfrb
2
+
3
+ Releases are driven by git tags. The release workflow is at
4
+ `.github/workflows/release.yml` and runs on tag push (or manual dispatch
5
+ for an existing tag).
6
+
7
+ ## Publish flow
8
+
9
+ 1. Ensure `lib/pdfrb/version.rb` has the target version.
10
+ 2. Merge the version-bump change via a PR (never commit directly to main).
11
+ 3. When the PR lands, tag the merged commit:
12
+
13
+ ```sh
14
+ git tag v$(ruby -Ilib -e 'require "pdfrb/version"; print Pdfrb::VERSION')
15
+ git push origin v<VERSION>
16
+ ```
17
+
18
+ 4. The `Release` workflow fires automatically:
19
+ - Verifies tag ↔ `lib/pdfrb/version.rb` match.
20
+ - Builds the gem (`bundle exec rake build`).
21
+ - Publishes to RubyGems via OIDC trusted publishing (no stored API key).
22
+ - Creates a GitHub release with auto-generated notes.
23
+
24
+ ## Trusted publishing (OIDC)
25
+
26
+ The workflow uses `rubygems/configure-rubygems-credentials` with OIDC.
27
+ To enable, on RubyGems:
28
+
29
+ 1. Open the `pdfrb` gem settings.
30
+ 2. Add the GitHub workflow as a trusted publisher:
31
+ - Repository: `claricle/pdfrb`
32
+ - Workflow filename: `release.yml`
33
+ - Environment: (leave blank)
34
+
35
+ Until that is configured, the `Publish to RubyGems` step will fail.
36
+
37
+ ## Manual dispatch
38
+
39
+ The workflow accepts a `tag` input. Run it from the Actions UI on a
40
+ previously-pushed tag if the auto-trigger missed (e.g., the workflow
41
+ file was added after the tag was pushed).
42
+
43
+ ## Pre-release checklist
44
+
45
+ - `bundle exec rake` is green.
46
+ - `lib/pdfrb/version.rb` matches the intended tag.
47
+ - `CHANGELOG.md` has an entry for the new version.
48
+ - No uncommitted changes on `main`.
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Action
5
+ # Base action builder. Subclasses define +action_type+ (the /S value)
6
+ # and +to_pdf+ (type-specific serialization).
7
+ class Base
8
+ class << self
9
+ # @return [Symbol] the /S value (e.g. :GoTo, :URI).
10
+ def action_type
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def register_as(name = action_type)
15
+ Action.register(name, self)
16
+ self
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Action
5
+ # Concrete action types (ISO 32000-2 §12.6). Each class defines
6
+ # its /S value and type-specific fields, then registers itself.
7
+
8
+ # GoTo — jump to a destination in the current document. §12.6.4.2
9
+ class GoTo < Base
10
+ class << self
11
+ def action_type; :GoTo; end
12
+
13
+ def to_pdf(dest:)
14
+ { S: :GoTo, D: dest }
15
+ end
16
+ end
17
+ register_as
18
+ end
19
+
20
+ # URI — open a Uniform Resource Identifier. §12.6.4.7
21
+ class URI < Base
22
+ class << self
23
+ def action_type; :URI; end
24
+
25
+ def to_pdf(uri:, is_map: false)
26
+ { S: :URI, URI: uri, IsMap: is_map }.compact
27
+ end
28
+ end
29
+ register_as
30
+ end
31
+
32
+ # Launch — launch an external application. §12.6.4.5
33
+ class Launch < Base
34
+ class << self
35
+ def action_type; :Launch; end
36
+
37
+ def to_pdf(file_spec:, new_window: nil)
38
+ payload = { S: :Launch, F: file_spec }
39
+ payload[:NewWindow] = new_window unless new_window.nil?
40
+ payload
41
+ end
42
+ end
43
+ register_as
44
+ end
45
+
46
+ # Named — execute a named action predefined by the viewer. §12.6.4.11
47
+ class Named < Base
48
+ class << self
49
+ def action_type; :Named; end
50
+
51
+ def to_pdf(name:)
52
+ { S: :Named, N: name }
53
+ end
54
+ end
55
+ register_as
56
+ end
57
+
58
+ # GoToR — go to a destination in a remote document. §12.6.4.3
59
+ class GoToR < Base
60
+ class << self
61
+ def action_type; :GoToR; end
62
+
63
+ def to_pdf(file_spec:, dest:, new_window: nil)
64
+ payload = { S: :GoToR, F: file_spec, D: dest }
65
+ payload[:NewWindow] = new_window unless new_window.nil?
66
+ payload
67
+ end
68
+ end
69
+ register_as
70
+ end
71
+
72
+ # SubmitForm — submit form data to a URL. §12.7.6.2
73
+ class SubmitForm < Base
74
+ class << self
75
+ def action_type; :SubmitForm; end
76
+
77
+ def to_pdf(url:, flags: 0)
78
+ { S: :SubmitForm, F: url, Flags: flags }
79
+ end
80
+ end
81
+ register_as
82
+ end
83
+
84
+ # ResetForm — reset form fields to defaults. §12.7.6.3
85
+ class ResetForm < Base
86
+ class << self
87
+ def action_type; :ResetForm; end
88
+
89
+ def to_pdf(fields: nil, flags: 0)
90
+ payload = { S: :ResetForm, Flags: flags }
91
+ payload[:Fields] = fields if fields
92
+ payload
93
+ end
94
+ end
95
+ register_as
96
+ end
97
+
98
+ # JavaScript — execute JavaScript. §12.6.4.16
99
+ # NOTE: PDF/A prohibits this; included for completeness.
100
+ class JavaScript < Base
101
+ class << self
102
+ def action_type; :JavaScript; end
103
+
104
+ def to_pdf(script:)
105
+ { S: :JavaScript, JS: script }
106
+ end
107
+ end
108
+ register_as
109
+ end
110
+
111
+ # Hide — show or hide an annotation. §12.6.4.9
112
+ class Hide < Base
113
+ class << self
114
+ def action_type; :Hide; end
115
+
116
+ def to_pdf(target:, hide: true)
117
+ { S: :Hide, T: target, H: hide }
118
+ end
119
+ end
120
+ register_as
121
+ end
122
+
123
+ # ImportData — import FDF/XFDF form data. §12.7.6.4
124
+ class ImportData < Base
125
+ class << self
126
+ def action_type; :ImportData; end
127
+
128
+ def to_pdf(file_spec:)
129
+ { S: :ImportData, F: file_spec }
130
+ end
131
+ end
132
+ register_as
133
+ end
134
+
135
+ # SetOCGState — toggle optional-content groups. §12.6.4.13
136
+ class SetOCGState < Base
137
+ class << self
138
+ def action_type; :SetOCGState; end
139
+
140
+ def to_pdf(state:, preserve_rb: false)
141
+ payload = { S: :SetOCGState, State: state }
142
+ payload[:PreserveRB] = preserve_rb unless preserve_rb == false
143
+ payload
144
+ end
145
+ end
146
+ register_as
147
+ end
148
+
149
+ # Trans — page transition for slide-show mode. §12.4.3
150
+ class Trans < Base
151
+ class << self
152
+ def action_type; :Trans; end
153
+
154
+ def to_pdf(style:, duration: nil, direction: nil)
155
+ trans = { S: style }
156
+ trans[:D] = duration if duration
157
+ trans[:Di] = direction if direction
158
+ { S: :Trans, Trans: trans }
159
+ end
160
+ end
161
+ register_as
162
+ end
163
+
164
+ # GoToE — go to a destination in an embedded file. §12.6.4.4 (PDF 1.6+)
165
+ class GoToE < Base
166
+ class << self
167
+ def action_type; :GoToE; end
168
+
169
+ def to_pdf(file_spec:, dest:, new_window: nil)
170
+ payload = { S: :GoToE, F: file_spec, D: dest }
171
+ payload[:NewWindow] = new_window unless new_window.nil?
172
+ payload
173
+ end
174
+ end
175
+ register_as
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ # Actions (ISO 32000-2 §12.6). An action specifies an interactive
5
+ # behavior: jump to a destination, open a URI, launch an app, etc.
6
+ #
7
+ # Each action type is a value object that serializes to a PDF action
8
+ # dictionary with /S (action type) and type-specific keys.
9
+ module Action
10
+ autoload :Base, "pdfrb/action/base"
11
+ # Each action builder class lives in types.rb but is referenced
12
+ # directly by callers. Autoload from that file.
13
+ autoload :GoTo, "pdfrb/action/types"
14
+ autoload :URI, "pdfrb/action/types"
15
+ autoload :Launch, "pdfrb/action/types"
16
+ autoload :Named, "pdfrb/action/types"
17
+ autoload :GoToR, "pdfrb/action/types"
18
+ autoload :SubmitForm, "pdfrb/action/types"
19
+ autoload :ResetForm, "pdfrb/action/types"
20
+ autoload :JavaScript, "pdfrb/action/types"
21
+ autoload :Hide, "pdfrb/action/types"
22
+ autoload :ImportData, "pdfrb/action/types"
23
+ autoload :SetOCGState, "pdfrb/action/types"
24
+ autoload :Trans, "pdfrb/action/types"
25
+ autoload :GoToE, "pdfrb/action/types"
26
+
27
+ class << self
28
+ def registry
29
+ @registry ||= {}
30
+ end
31
+
32
+ def register(name, klass)
33
+ registry[name.to_sym] = klass
34
+ end
35
+
36
+ def [](name)
37
+ key = name.to_sym
38
+ result = registry[key]
39
+ return result if result
40
+
41
+ # Lazy-load on miss: trigger autoload of GoTo which loads
42
+ # the entire types.rb file and registers all subtypes.
43
+ GoTo unless @loaded
44
+ @loaded = true
45
+ registry[key]
46
+ end
47
+
48
+ def types
49
+ GoTo unless @loaded
50
+ @loaded = true
51
+ registry.keys
52
+ end
53
+ end
54
+ end
55
+ end