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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Pdfrb
4
4
  class Document
5
- # Annotations facade (stub). Full implementation in TODO 131.
5
+ # Annotations facade. Adds and enumerates annotations on pages.
6
6
  class Annotations
7
7
  attr_reader :document
8
8
 
@@ -27,8 +27,88 @@ module Pdfrb
27
27
  annot
28
28
  end
29
29
 
30
- def each(page)
31
- return enum_for(:each, page) unless block_given?
30
+ # Sticky-note / comment text annotation.
31
+ def add_text_note(page, rect:, contents:, open: false)
32
+ annot = add(page, subtype: :Text, rect: rect, contents: contents)
33
+ annot.value[:Open] = open unless open == false
34
+ annot
35
+ end
36
+
37
+ # Hyperlink annotation pointing at a destination.
38
+ def add_link(page, rect:, dest: nil, uri: nil, highlight: :Invert)
39
+ annot = add(page, subtype: :Link, rect: rect)
40
+ annot.value[:H] = highlight
41
+ if dest
42
+ annot.value[:Dest] = dest
43
+ elsif uri
44
+ action = document.add(
45
+ { Type: :Action, S: :URI, URI: uri },
46
+ type: Pdfrb::Model::Type::Action
47
+ )
48
+ annot.value[:A] = Pdfrb::Model::Reference.new(action.oid, action.gen)
49
+ end
50
+ annot
51
+ end
52
+
53
+ # Free-text annotation showing inline text on the page.
54
+ def add_free_text(page, rect:, contents:, font: nil, font_size: nil, color: nil)
55
+ annot = document.add(
56
+ {
57
+ Type: :Annot, Subtype: :FreeText, Rect: rect,
58
+ P: Pdfrb::Model::Reference.new(page.oid, page.gen),
59
+ Contents: contents
60
+ },
61
+ type: Pdfrb::Model::Type::FreeTextAnnotation
62
+ )
63
+ da_parts = []
64
+ da_parts << "/#{font} #{font_size} Tf" if font && font_size
65
+ da_parts << "#{color.join(' ')} rg" if color
66
+ annot.value[:DA] = da_parts.join(" ") unless da_parts.empty?
67
+ annots = (page.value[:Annots] ||= [])
68
+ annots << Pdfrb::Model::Reference.new(annot.oid, annot.gen)
69
+ annot
70
+ end
71
+
72
+ # Highlight text-markup annotation over the given quad points.
73
+ def add_highlight(page, quad_points:, contents: nil)
74
+ annot = document.add(
75
+ {
76
+ Type: :Annot, Subtype: :Highlight,
77
+ QuadPoints: quad_points,
78
+ P: Pdfrb::Model::Reference.new(page.oid, page.gen),
79
+ Contents: contents
80
+ }.compact,
81
+ type: Pdfrb::Model::Type::HighlightAnnotation
82
+ )
83
+ annots = (page.value[:Annots] ||= [])
84
+ annots << Pdfrb::Model::Reference.new(annot.oid, annot.gen)
85
+ annot
86
+ end
87
+
88
+ # Stamp annotation (e.g. DRAFT, APPROVED, CONFIDENTIAL).
89
+ def add_stamp(page, rect:, name: :Draft, contents: nil)
90
+ annot = document.add(
91
+ {
92
+ Type: :Annot, Subtype: :Stamp, Rect: rect, Name: name,
93
+ P: Pdfrb::Model::Reference.new(page.oid, page.gen),
94
+ Contents: contents
95
+ }.compact,
96
+ type: Pdfrb::Model::Type::StampAnnotation
97
+ )
98
+ annots = (page.value[:Annots] ||= [])
99
+ annots << Pdfrb::Model::Reference.new(annot.oid, annot.gen)
100
+ annot
101
+ end
102
+
103
+ def count(page)
104
+ annots = page.value[:Annots]
105
+ return 0 unless annots
106
+
107
+ annots.is_a?(::Array) ? annots.length : 1
108
+ end
109
+
110
+ def each(page, &block)
111
+ return enum_for(:each, page) unless block
32
112
 
33
113
  annots = page.value[:Annots]
34
114
  return self unless annots
@@ -39,6 +119,43 @@ module Pdfrb
39
119
  end
40
120
  self
41
121
  end
122
+
123
+ # Iterate annotations of a specific subtype across the whole doc.
124
+ def each_of_subtype(subtype, &block)
125
+ return enum_for(:each_of_subtype, subtype) unless block
126
+
127
+ document.pages.each do |page|
128
+ each(page) do |annot|
129
+ next unless annot[:Subtype]&.to_sym == subtype
130
+
131
+ yield annot
132
+ end
133
+ end
134
+ self
135
+ end
136
+
137
+ # Iterate every link annotation in the document.
138
+ def each_link(&block)
139
+ return enum_for(:each_link) unless block
140
+
141
+ each_of_subtype(:Link, &block)
142
+ end
143
+
144
+ # Iterate every widget (form) annotation in the document.
145
+ def each_widget(&block)
146
+ return enum_for(:each_widget) unless block
147
+
148
+ each_of_subtype(:Widget, &block)
149
+ end
150
+
151
+ # Iterate every text-markup annotation in the document.
152
+ def each_text_markup(&block)
153
+ return enum_for(:each_text_markup) unless block
154
+
155
+ %i[Highlight Underline Squiggly StrikeOut].each do |sub|
156
+ each_of_subtype(sub, &block)
157
+ end
158
+ end
42
159
  end
43
160
  end
44
161
  end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ class Document
5
+ # Associated Files (AF) facade. PDF 2.0 introduced /AF arrays on
6
+ # Catalog, Page, and StructElem to declare the relationship between
7
+ # embedded files and the context they appear in.
8
+ #
9
+ # Relationship types (ISO 32000-2 §7.11.3):
10
+ # :Source — source document for the content
11
+ # :Data — data file referenced by the content
12
+ # :Alternative — alternative representation
13
+ # :Supplement — supplemental information
14
+ # :Unspecified — relationship not specified
15
+ #
16
+ # App Note 002: https://github.com/pdf-association/appnote-pdf20-002-af
17
+ class AssociatedFiles
18
+ RELATIONSHIP_TYPES = %i[
19
+ Source Data Alternative Supplement Unspecified
20
+ Encrypted Payload FormData Schema Template
21
+ ].freeze
22
+
23
+ attr_reader :document
24
+
25
+ def initialize(document)
26
+ @document = document
27
+ end
28
+
29
+ # Associate a filespec with the Catalog-level /AF array.
30
+ # @param filespec_ref [Pdfrb::Model::Reference] the /Filespec ref.
31
+ # @param relationship [Symbol] one of RELATIONSHIP_TYPES.
32
+ def add_to_catalog(filespec_ref, relationship: :Unspecified)
33
+ ensure_relationship(filespec_ref, relationship)
34
+ append_to_af_array(document.catalog, filespec_ref)
35
+ end
36
+
37
+ # Associate a filespec with a specific page's /AF array.
38
+ def add_to_page(page, filespec_ref, relationship: :Unspecified)
39
+ ensure_relationship(filespec_ref, relationship)
40
+ append_to_af_array(page, filespec_ref)
41
+ end
42
+
43
+ # Associate a filespec with a structure element's /AF array.
44
+ def add_to_element(struct_elem, filespec_ref, relationship: :Unspecified)
45
+ ensure_relationship(filespec_ref, relationship)
46
+ append_to_af_array(struct_elem, filespec_ref)
47
+ end
48
+
49
+ # Convenience: create a filespec + embedded file from raw bytes,
50
+ # then associate it at the catalog level.
51
+ # @return [Pdfrb::Model::Reference] the filespec reference.
52
+ def embed(filename:, data:, relationship: :Unspecified,
53
+ description: nil, mime_type: nil)
54
+ filespec = document.files.add(data, name: filename,
55
+ description: description,
56
+ mime_type: mime_type)
57
+ ref = Pdfrb::Model::Reference.new(filespec.oid, filespec.gen)
58
+ filespec.value[:AFRelationship] = relationship
59
+ add_to_catalog(ref, relationship: relationship)
60
+ ref
61
+ end
62
+
63
+ # List all catalog-level AF filespecs.
64
+ def catalog_files
65
+ read_af_array(document.catalog)
66
+ end
67
+
68
+ private
69
+
70
+ def ensure_relationship(filespec_ref, relationship)
71
+ unless RELATIONSHIP_TYPES.include?(relationship)
72
+ raise ArgumentError, "invalid AF relationship: #{relationship}"
73
+ end
74
+
75
+ filespec = document.object(filespec_ref)
76
+ return unless filespec.is_a?(Pdfrb::Model::Cos::Dictionary)
77
+
78
+ filespec.value[:AFRelationship] = relationship
79
+ end
80
+
81
+ def append_to_af_array(dict_obj, ref)
82
+ af = dict_obj.value[:AF]
83
+ if af.nil?
84
+ dict_obj.value[:AF] = [ref]
85
+ elsif af.is_a?(::Array)
86
+ af << ref
87
+ else
88
+ dict_obj.value[:AF] = [af, ref]
89
+ end
90
+ end
91
+
92
+ def read_af_array(dict_obj)
93
+ af = dict_obj.value[:AF]
94
+ return [] unless af
95
+
96
+ refs = af.is_a?(::Array) ? af : [af]
97
+ refs.filter_map do |ref|
98
+ obj = ref.is_a?(Pdfrb::Model::Reference) ? document.object(ref) : ref
99
+ obj if obj.is_a?(Pdfrb::Model::Cos::Dictionary)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ class Document
5
+ # Facade for color space management. Embeds ICC profiles, registers
6
+ # named color spaces in page Resources, and provides lookup.
7
+ class Colors
8
+ attr_reader :document
9
+
10
+ def initialize(document)
11
+ @document = document
12
+ @next_cs_number = 1
13
+ end
14
+
15
+ # Embed an ICC profile as a stream and return the color space
16
+ # array [/ICCBased <ref>] for use in Resources.
17
+ #
18
+ # @param icc_bytes [String] raw ICC profile bytes.
19
+ # @param alternate [Symbol, nil] fallback color space
20
+ # (:DeviceRGB, :DeviceCMYK, :DeviceGray, :Lab). Derived from
21
+ # the profile header if nil.
22
+ # @return [Pdfrb::Model::PdfArray] the [/ICCBased <ref>] array.
23
+ def embed_icc_profile(icc_bytes, alternate: nil)
24
+ profile = Pdfrb::Color::ICCProfile.new(icc_bytes, alternate: alternate)
25
+ stream = document.add(
26
+ { **profile.stream_dictionary_fields,
27
+ Length: profile.raw_data.bytesize },
28
+ type: Pdfrb::Model::Cos::Stream
29
+ )
30
+ stream.stream = profile.raw_data
31
+ Pdfrb::Model::PdfArray.new([
32
+ :ICCBased,
33
+ Pdfrb::Model::Reference.new(stream.oid, stream.gen),
34
+ ])
35
+ end
36
+
37
+ # Register a color space in a page's Resources /ColorSpace dict
38
+ # and return the name (e.g. :CS1) for use in content streams.
39
+ #
40
+ # @param page [Pdfrb::Model::Cos::Dictionary] the target page.
41
+ # @param color_space the color space value (name, array, or ref).
42
+ # @return [Symbol] the registered name.
43
+ def register(page, color_space)
44
+ resources = ensure_resources(page)
45
+ cs_dict = resources.value[:ColorSpace]
46
+ cs_dict = ensure_color_space_dict(resources, cs_dict)
47
+
48
+ name = cs_name
49
+ cs_dict[name] = color_space
50
+ name
51
+ end
52
+
53
+ private
54
+
55
+ def ensure_resources(page)
56
+ resources = page.value[:Resources]
57
+ return resources if resources.is_a?(Pdfrb::Model::Cos::Dictionary)
58
+
59
+ resources_hash = if resources.is_a?(Pdfrb::Model::Reference)
60
+ document.object(resources)&.value
61
+ else
62
+ resources
63
+ end
64
+ if resources_hash.nil?
65
+ resources_hash = {}
66
+ page.value[:Resources] = resources_hash
67
+ end
68
+ wrap_resources(page, resources_hash)
69
+ end
70
+
71
+ def wrap_resources(page, hash)
72
+ existing = page.value[:Resources]
73
+ return existing if existing.is_a?(Pdfrb::Model::Cos::Dictionary)
74
+
75
+ wrapped = Pdfrb::Model::Cos::Dictionary.new(hash)
76
+ page.value[:Resources] = wrapped
77
+ wrapped
78
+ end
79
+
80
+ def ensure_color_space_dict(resources, cs_dict)
81
+ return cs_dict if cs_dict.is_a?(::Hash) || cs_dict.is_a?(Pdfrb::Model::Cos::Dictionary)
82
+
83
+ cs_hash = {}
84
+ resources.value[:ColorSpace] = cs_hash
85
+ cs_hash
86
+ end
87
+
88
+ def cs_name
89
+ name = :"CS#{@next_cs_number}"
90
+ @next_cs_number += 1
91
+ name
92
+ end
93
+ end
94
+ end
95
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  module Pdfrb
4
4
  class Document
5
- # Named-destinations facade (stub). Full implementation in TODO 129.
5
+ # Named-destinations facade. Manages both name-tree destinations
6
+ # (Catalog /Names /Dests) and the explicit /Dests dictionary.
6
7
  class Destinations
7
8
  attr_reader :document
8
9
 
@@ -10,12 +11,133 @@ module Pdfrb
10
11
  @document = document
11
12
  end
12
13
 
14
+ # Look up a named destination by name. Returns the destination
15
+ # array or nil.
13
16
  def [](name)
14
- names = document.catalog.value[:Names]
15
- return nil unless names.is_a?(::Hash) || names.is_a?(Pdfrb::Model::Cos::Dictionary)
17
+ dests_dict = resolved_explicit_dict
18
+ return dests_dict[name] if dests_dict && dests_dict[name]
16
19
 
17
- dests = names.is_a?(Pdfrb::Model::Cos::Dictionary) ? names.value[:Dests] : names[:Dests]
18
- dests ? dests[name] : nil
20
+ name_tree = resolved_name_tree
21
+ lookup_in_name_tree(name_tree, name)
22
+ end
23
+
24
+ # Add an explicit destination under +name+. Creates the /Dests
25
+ # dictionary if absent. +destination+ is typically an Array
26
+ # [page_ref, fit_keyword, *args].
27
+ def add(name, destination)
28
+ ensure_dictionaries
29
+ catalog = document.catalog
30
+ catalog.value[:Dests] ||= {}
31
+ catalog.value[:Dests][name.to_sym] = destination
32
+ name.to_sym
33
+ end
34
+
35
+ # Enumerate all destination names (from both /Dests and /Names/Dests).
36
+ def each_name(&)
37
+ return enum_for(:each_name) unless block_given?
38
+
39
+ seen = Set.new
40
+ explicit = resolved_explicit_dict
41
+ explicit&.each_key do |k|
42
+ next if seen.include?(k)
43
+
44
+ seen << k
45
+ yield k
46
+ end
47
+
48
+ name_tree = resolved_name_tree
49
+ each_name_tree_key(name_tree) do |k|
50
+ next if seen.include?(k)
51
+
52
+ seen << k
53
+ yield k
54
+ end
55
+ end
56
+
57
+ def names
58
+ each_name.to_a
59
+ end
60
+
61
+ def empty?
62
+ names.empty?
63
+ end
64
+
65
+ def count
66
+ names.size
67
+ end
68
+
69
+ private
70
+
71
+ def ensure_dictionaries
72
+ catalog = document.catalog
73
+ catalog.value[:Names] ||= {}
74
+ catalog.value[:Names][:Dests] ||= {}
75
+ end
76
+
77
+ def resolved_explicit_dict
78
+ catalog = document.catalog
79
+ d = catalog.value[:Dests]
80
+ return nil unless d
81
+
82
+ d.is_a?(Pdfrb::Model::Reference) ? document.object(d) : d
83
+ end
84
+
85
+ def resolved_name_tree
86
+ catalog = document.catalog
87
+ names = catalog.value[:Names]
88
+ return nil unless names
89
+
90
+ names = document.object(names) if names.is_a?(Pdfrb::Model::Reference)
91
+ return nil unless names
92
+
93
+ d = names.is_a?(Pdfrb::Model::Cos::Dictionary) ? names.value[:Dests] : names[:Dests]
94
+ return nil unless d
95
+
96
+ d.is_a?(Pdfrb::Model::Reference) ? document.object(d) : d
97
+ end
98
+
99
+ def lookup_in_name_tree(node, name)
100
+ return nil unless node
101
+
102
+ if node.is_a?(Pdfrb::Model::Cos::Dictionary) && node.value[:Names]
103
+ arr = node.value[:Names]
104
+ arr = arr.to_a if arr.is_a?(Pdfrb::Model::PdfArray)
105
+ arr.each_slice(2) { |k, v| return v if k == name }
106
+ elsif node.is_a?(Pdfrb::Model::Cos::Dictionary) && node.value[:Kids]
107
+ arr = node.value[:Kids]
108
+ arr = arr.to_a if arr.is_a?(Pdfrb::Model::PdfArray)
109
+ arr.each do |kid_ref|
110
+ kid = kid_ref.is_a?(Pdfrb::Model::Reference) ? document.object(kid_ref) : kid_ref
111
+ result = lookup_in_name_tree(kid, name)
112
+ return result if result
113
+ end
114
+ elsif node.is_a?(Hash) && node[:Names]
115
+ arr = node[:Names]
116
+ arr = arr.to_a if arr.is_a?(Pdfrb::Model::PdfArray)
117
+ arr.each_slice(2) { |k, v| return v if k == name }
118
+ end
119
+ nil
120
+ end
121
+
122
+ def each_name_tree_key(node, &block)
123
+ return unless node
124
+
125
+ if node.is_a?(Pdfrb::Model::Cos::Dictionary) && node.value[:Names]
126
+ arr = node.value[:Names]
127
+ arr = arr.to_a if arr.is_a?(Pdfrb::Model::PdfArray)
128
+ arr.each_slice(2) { |k, _v| yield k }
129
+ elsif node.is_a?(Pdfrb::Model::Cos::Dictionary) && node.value[:Kids]
130
+ arr = node.value[:Kids]
131
+ arr = arr.to_a if arr.is_a?(Pdfrb::Model::PdfArray)
132
+ arr.each do |kid_ref|
133
+ kid = kid_ref.is_a?(Pdfrb::Model::Reference) ? document.object(kid_ref) : kid_ref
134
+ each_name_tree_key(kid, &block)
135
+ end
136
+ elsif node.is_a?(Hash) && node[:Names]
137
+ arr = node[:Names]
138
+ arr = arr.to_a if arr.is_a?(Pdfrb::Model::PdfArray)
139
+ arr.each_slice(2) { |k, _v| yield k }
140
+ end
19
141
  end
20
142
  end
21
143
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ class Document
5
+ # Catalog display controls. Sets viewer behavior: hide toolbar,
6
+ # page layout, page mode, and open-action destination.
7
+ #
8
+ # /ViewerPreferences — fine-grained viewer controls
9
+ # /PageLayout — how pages are laid out in the viewer
10
+ # /PageMode — which panels are visible on open
11
+ # /OpenAction — initial destination or action
12
+ class Display
13
+ PAGE_LAYOUTS = %i[
14
+ SinglePage OneColumn TwoColumnLeft TwoColumnRight
15
+ TwoPageLeft TwoPageRight
16
+ ].freeze
17
+
18
+ PAGE_MODES = %i[
19
+ UseNone UseOutlines UseThumbs FullScreen UseOC UseAttachments
20
+ ].freeze
21
+
22
+ NON_FULLSCREEN_PAGE_MODES = %i[UseNone UseOutlines UseThumbs UseOC].freeze
23
+
24
+ DIRECTIONS = %i[L2R R2L].freeze
25
+
26
+ attr_reader :document
27
+
28
+ def initialize(document)
29
+ @document = document
30
+ end
31
+
32
+ # @param layout [Symbol] one of PAGE_LAYOUTS.
33
+ def page_layout=(layout)
34
+ raise ArgumentError, "invalid page layout: #{layout}" unless PAGE_LAYOUTS.include?(layout)
35
+
36
+ document.catalog.value[:PageLayout] = layout
37
+ end
38
+
39
+ # @param mode [Symbol] one of PAGE_MODES.
40
+ def page_mode=(mode)
41
+ raise ArgumentError, "invalid page mode: #{mode}" unless PAGE_MODES.include?(mode)
42
+
43
+ document.catalog.value[:PageMode] = mode
44
+ end
45
+
46
+ # Set the /OpenAction destination or action.
47
+ # @param dest_or_action [Array, Hash, Pdfrb::Destination::Fit,
48
+ # Pdfrb::Model::PdfArray] the destination array or action dict.
49
+ def open_action=(dest_or_action)
50
+ document.catalog.value[:OpenAction] = dest_or_action
51
+ end
52
+
53
+ # Get or set /ViewerPreferences as a keyword-hash.
54
+ def viewer_preferences(**opts)
55
+ return read_viewer_preferences if opts.empty?
56
+
57
+ vp = document.catalog.value[:ViewerPreferences]
58
+ vp ||= {}
59
+ opts.each do |key, value|
60
+ vp[key] = value
61
+ end
62
+ document.catalog.value[:ViewerPreferences] = vp
63
+ end
64
+
65
+ # Convenience: hide toolbar and menubar, fit window.
66
+ def presentation_mode!
67
+ viewer_preferences(
68
+ HideToolbar: true,
69
+ HideMenubar: true,
70
+ FitWindow: true,
71
+ CenterWindow: true,
72
+ DisplayDocTitle: true
73
+ )
74
+ self.page_mode = :FullScreen
75
+ end
76
+
77
+ private
78
+
79
+ def read_viewer_preferences
80
+ document.catalog.value[:ViewerPreferences] || {}
81
+ end
82
+ end
83
+ end
84
+ end