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
data/lib/pdfrb/writer.rb CHANGED
@@ -23,9 +23,42 @@ module Pdfrb
23
23
  @io = io
24
24
  @serializer = Serializer.new(
25
25
  compress_streams: document.config["writer.compress_streams"],
26
- compress_min_size: document.config["writer.compress_min_size"]
26
+ compress_min_size: document.config["writer.compress_min_size"],
27
+ **serializer_encrypter_opts
27
28
  )
28
- @xref_offsets = {} # oid -> byte offset
29
+ @xref_offsets = {}
30
+ end
31
+
32
+ # Build the encrypter option for the Serializer from the document's
33
+ # /Encrypt dict. If the document isn't encrypted, returns empty hash.
34
+ # The SecurityHandler's encrypt_data method becomes the Serializer's
35
+ # encrypter, which encrypts string/stream payloads per-object.
36
+ def serializer_encrypter_opts
37
+ # Use a pre-configured handler if the document provides one
38
+ # (e.g., set by the encryption CLI).
39
+ handler = document.config["encryption.handler"]
40
+ return { encrypter: handler } if handler
41
+
42
+ trailer = document.trailer
43
+ return {} unless trailer
44
+
45
+ encrypt_ref = trailer[:Encrypt]
46
+ return {} unless encrypt_ref
47
+
48
+ encrypt_dict = encrypt_ref.is_a?(Pdfrb::Model::Reference) ?
49
+ document.object(encrypt_ref) : encrypt_ref
50
+ return {} unless encrypt_dict
51
+
52
+ handler = Pdfrb::Encryption::StandardSecurityHandler.new(
53
+ Encrypt: encrypt_dict.value,
54
+ ID: trailer[:ID]
55
+ )
56
+ password = document.config["encryption.password"] || ""
57
+ handler.verify_user_password(password)
58
+
59
+ { encrypter: handler }
60
+ rescue StandardError
61
+ {}
29
62
  end
30
63
 
31
64
  def self.write(document, io)
@@ -38,7 +71,7 @@ module Pdfrb
38
71
 
39
72
  def write
40
73
  @io.binmode
41
- @io.truncate(0) if @io.respond_to?(:truncate)
74
+ @io.truncate(0) if @io.is_a?(StringIO)
42
75
  write_header
43
76
  dispatch_before_write
44
77
 
@@ -81,7 +114,7 @@ module Pdfrb
81
114
  @io << document.io.read
82
115
  dispatch_before_write
83
116
  # Emit only the modified objects.
84
- document.instance_variable_get(:@objects).each_value do |obj|
117
+ document.each_indirect_object do |obj|
85
118
  next unless obj.indirect?
86
119
 
87
120
  @xref_offsets[obj.oid] = @io.pos
@@ -106,13 +139,16 @@ module Pdfrb
106
139
  max_oid = oids.max || 0
107
140
  @io << "xref\n"
108
141
  @io << "0 #{max_oid + 1}\n"
109
- @io << sprintf("0000000000 65535 f \r\n")
142
+ # Each entry must be EXACTLY 20 bytes per s7.5.4: 10-digit offset,
143
+ # 1 space, 5-digit gen, 1 space, 1-char type, 2-byte EOL (CR+LF).
144
+ # No trailing space before the EOL — that was the bug.
145
+ @io << format("%010d %05d f\r\n", 0, 65_535)
110
146
  (1..max_oid).each do |oid|
111
147
  offset = @xref_offsets[oid]
112
148
  if offset
113
- @io << sprintf("%010d %05d n \r\n", offset, 0)
149
+ @io << format("%010d %05d n\r\n", offset, 0)
114
150
  else
115
- @io << sprintf("0000000000 00000 f \r\n")
151
+ @io << format("%010d %05d f\r\n", 0, 0)
116
152
  end
117
153
  end
118
154
  pos
@@ -132,25 +168,11 @@ module Pdfrb
132
168
  w_type = 1
133
169
  w_field2 = 3
134
170
  w_field3 = 1
135
- w_type + w_field2 + w_field3
136
-
137
- data = +""
138
- (0..max_oid).each do |oid|
139
- if oid.zero?
140
- data << encode_xref_entry(0, 0, 0, w_type, w_field2, w_field3)
141
- elsif @xref_offsets.key?(oid)
142
- data << encode_xref_entry(1, @xref_offsets[oid], 0,
143
- w_type, w_field2, w_field3)
144
- elsif packed.key?(oid)
145
- objstm_oid, index = packed[oid]
146
- data << encode_xref_entry(2, objstm_oid, index,
147
- w_type, w_field2, w_field3)
148
- end
149
- end
150
171
 
172
+ data = build_xref_stream_data(max_oid, packed, w_type, w_field2, w_field3)
151
173
  compressed = ::Zlib::Deflate.deflate(data)
152
174
 
153
- xref_stream_oid = document.instance_variable_get(:@next_oid) || (max_oid + 1)
175
+ xref_stream_oid = document.next_oid || (max_oid + 1)
154
176
  stream_offset = @io.pos
155
177
  header = "#{xref_stream_oid} 0 obj\n"
156
178
 
@@ -172,6 +194,33 @@ module Pdfrb
172
194
  stream_offset
173
195
  end
174
196
 
197
+ # Build the binary xref stream body. Always emits max_oid + 1
198
+ # entries (one per oid 0..max_oid) so the reader's per-oid index
199
+ # stays aligned. Gaps from dedup'd oids become free entries.
200
+ def build_xref_stream_data(max_oid, packed, w_type, w_field2, w_field3)
201
+ data = +""
202
+ (0..max_oid).each do |oid|
203
+ entry = xref_entry_for(oid, packed)
204
+ data << encode_xref_entry(entry[0], entry[1], entry[2],
205
+ w_type, w_field2, w_field3)
206
+ end
207
+ data
208
+ end
209
+
210
+ # Returns [type, field2, field3] for the xref entry of +oid+.
211
+ def xref_entry_for(oid, packed)
212
+ return [0, 0, 0] if oid.zero?
213
+
214
+ if @xref_offsets.key?(oid)
215
+ [1, @xref_offsets[oid], 0]
216
+ elsif packed.key?(oid)
217
+ objstm_oid, index = packed[oid]
218
+ [2, objstm_oid, index]
219
+ else
220
+ [0, 0, 0]
221
+ end
222
+ end
223
+
175
224
  def write_xref_stream_trailer(xref_pos)
176
225
  @io << "startxref\n#{xref_pos}\n%%EOF\n"
177
226
  end
@@ -237,9 +286,9 @@ module Pdfrb
237
286
 
238
287
  def write_trailer(xref_pos, prev: nil)
239
288
  root = document.catalog
240
- root_ref = root && root.respond_to?(:indirect?) && root.indirect? ?
289
+ root_ref = root && root.is_a?(Pdfrb::Model::Object) && root.indirect? ?
241
290
  Pdfrb::Model::Reference.new(root.oid, root.gen) : nil
242
- size = [(@xref_offsets.keys.max || 0) + 1, document.instance_variable_get(:@next_oid) || 1].max
291
+ size = [(@xref_offsets.keys.max || 0) + 1, document.next_oid || 1].max
243
292
 
244
293
  existing = document.trailer || {}
245
294
  trailer_hash = {}
@@ -267,10 +316,12 @@ module Pdfrb
267
316
 
268
317
  def dispatch_before_write
269
318
  document.dispatch_message(:before_write)
319
+ compress_content_streams if document.config["writer.compress_streams"]
270
320
  end
271
321
 
272
322
  def version
273
- document.version || DEFAULT_VERSION
323
+ v = document.version || DEFAULT_VERSION
324
+ detect_version_features(v)
274
325
  end
275
326
 
276
327
  def each_indirect_object
@@ -296,8 +347,47 @@ module Pdfrb
296
347
  hash[k] = v
297
348
  end
298
349
  [(@xref_offsets.keys.max || 0) + 1,
299
- document.instance_variable_get(:@next_oid) || 1].max
350
+ document.next_oid || 1].max
300
351
  hash
301
352
  end
353
+
354
+ def detect_version_features(current)
355
+ catalog = document.catalog
356
+ return current unless catalog
357
+
358
+ v = current
359
+ if (catalog.value[:AF] || catalog.value[:Collection]) && compare_versions(v, "2.0").negative?
360
+ v = "2.0"
361
+ end
362
+ if catalog.value[:OCProperties] && compare_versions(v, "1.5").negative?
363
+ v = "1.5"
364
+ end
365
+ document.version = v
366
+ v
367
+ end
368
+
369
+ def compare_versions(a, b)
370
+ aa = a.to_s.split(".").map(&:to_i)
371
+ bb = b.to_s.split(".").map(&:to_i)
372
+ (aa <=> bb) || 0
373
+ end
374
+
375
+ def compress_content_streams
376
+ require "zlib"
377
+ document.each_indirect_object do |obj|
378
+ next unless obj.is_a?(Pdfrb::Model::Cos::Stream)
379
+ next if obj.value[:Filter]
380
+ next unless obj.stream
381
+ next if obj.value[:Type] == :Metadata
382
+
383
+ min = document.config["writer.compress_min_size"] || 50
384
+ next if obj.stream.bytesize < min
385
+
386
+ compressed = ::Zlib::Deflate.deflate(obj.stream)
387
+ obj.stream = compressed
388
+ obj.value[:Filter] = :FlateDecode
389
+ obj.value[:Length] = compressed.bytesize
390
+ end
391
+ end
302
392
  end
303
393
  end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin; require "lutaml/model"; rescue LoadError; end
4
+
5
+ module Pdfrb
6
+ module XMP
7
+ # XMP packet wrapper. An XMP packet is an XML document wrapped in
8
+ # processing instructions: <?xpacket begin?> ... <?xpacket end?>.
9
+ #
10
+ # The packet contains an RDF document with one Description element
11
+ # per schema. This class assembles the schemas into a single RDF
12
+ # Description and wraps it in the packet PI.
13
+ class Packet
14
+ XMP_BEGIN = "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"
15
+ XMP_END = "<?xpacket end=\"w\"?>\n"
16
+
17
+ attr_reader :dc, :pdf, :xmp, :rights
18
+
19
+ def initialize
20
+ @dc = Schemas::DublinCore.new
21
+ @pdf = Schemas::PDF.new
22
+ @xmp = Schemas::XMPBasic.new
23
+ @rights = Schemas::XMPRights.new
24
+ end
25
+
26
+ def title=(value); @dc.title = Array(value); end
27
+ def title; @dc.title.first; end
28
+ def creator=(value); @dc.creator = Array(value); end
29
+ def creator; @dc.creator.first; end
30
+
31
+ def author=(value); @dc.creator = Array(value); end
32
+ def author; @dc.creator.first; end
33
+ def description=(value); @dc.description = Array(value); end
34
+ def description; @dc.description.first; end
35
+ def subject=(value); @dc.subject = Array(value); end
36
+ def subject; @dc.subject; end
37
+
38
+ def keywords=(value); @pdf.keywords = value; end
39
+ def keywords; @pdf.keywords; end
40
+ def producer=(value); @pdf.producer = value; end
41
+ def producer; @pdf.producer; end
42
+
43
+ def creator_tool=(value); @xmp.creator_tool = value; end
44
+ def creator_tool; @xmp.creator_tool; end
45
+
46
+ def to_xmp
47
+ XMP_BEGIN + rdf_body + XMP_END
48
+ end
49
+
50
+ private
51
+
52
+ def rdf_body
53
+ lines = [
54
+ '<x:xmpmeta xmlns:x="adobe:ns:meta/">',
55
+ '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">',
56
+ '<rdf:Description rdf:about=""',
57
+ ' xmlns:dc="http://purl.org/dc/elements/1.1/"',
58
+ ' xmlns:pdf="http://ns.adobe.com/pdf/1.3/"',
59
+ ' xmlns:xmp="http://ns.adobe.com/xap/1.0/">',
60
+ ]
61
+ lines += schema_lines
62
+ lines << "</rdf:Description>"
63
+ lines << "</rdf:RDF>"
64
+ lines << "</x:xmpmeta>"
65
+ "#{lines.join("\n")}\n"
66
+ end
67
+
68
+ def schema_lines
69
+ parts = []
70
+ parts += dc_lines if has_dc?
71
+ parts += pdf_lines if has_pdf?
72
+ parts += xmp_lines if has_xmp?
73
+ parts
74
+ end
75
+
76
+ def has_dc?
77
+ (@dc.title && !@dc.title.empty?) ||
78
+ (@dc.creator && !@dc.creator.empty?) ||
79
+ (@dc.description && !@dc.description.empty?) ||
80
+ (@dc.subject && !@dc.subject.empty?)
81
+ end
82
+
83
+ def has_pdf?
84
+ @pdf.keywords || @pdf.producer || @pdf.trapped
85
+ end
86
+
87
+ def has_xmp?
88
+ @xmp.creator_tool || @xmp.create_date || @xmp.modify_date
89
+ end
90
+
91
+ def dc_lines
92
+ lines = (@dc.title || []).map { |t| " <dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">#{escape(t)}</rdf:li></rdf:Alt></dc:title>" }
93
+ (@dc.creator || []).each { |c| lines << " <dc:creator><rdf:Seq><rdf:li>#{escape(c)}</rdf:li></rdf:Seq></dc:creator>" }
94
+ (@dc.description || []).each { |d| lines << " <dc:description><rdf:Alt><rdf:li xml:lang=\"x-default\">#{escape(d)}</rdf:li></rdf:Alt></dc:description>" }
95
+ (@dc.subject || []).each { |s| lines << " <dc:subject><rdf:Bag><rdf:li>#{escape(s)}</rdf:li></rdf:Bag></dc:subject>" }
96
+ lines
97
+ end
98
+
99
+ def pdf_lines
100
+ lines = []
101
+ lines << " <pdf:Keywords>#{escape(@pdf.keywords)}</pdf:Keywords>" if @pdf.keywords
102
+ lines << " <pdf:Producer>#{escape(@pdf.producer)}</pdf:Producer>" if @pdf.producer
103
+ lines
104
+ end
105
+
106
+ def xmp_lines
107
+ lines = []
108
+ lines << " <xmp:CreatorTool>#{escape(@xmp.creator_tool)}</xmp:CreatorTool>" if @xmp.creator_tool
109
+ lines << " <xmp:CreateDate>#{@xmp.create_date}</xmp:CreateDate>" if @xmp.create_date
110
+ lines << " <xmp:ModifyDate>#{@xmp.modify_date}</xmp:ModifyDate>" if @xmp.modify_date
111
+ lines
112
+ end
113
+
114
+ def escape(str)
115
+ str.to_s
116
+ .gsub("&", "&amp;")
117
+ .gsub("<", "&lt;")
118
+ .gsub(">", "&gt;")
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin; require "lutaml/model"; rescue LoadError; end
4
+
5
+ module Pdfrb
6
+ module XMP
7
+ module Schemas
8
+ # Namespace definitions for XMP schemas.
9
+ class DublinCoreNS < Lutaml::Xml::Namespace
10
+ uri "http://purl.org/dc/elements/1.1/"
11
+ prefix_default "dc"
12
+ end
13
+
14
+ class PdfNS < Lutaml::Xml::Namespace
15
+ uri "http://ns.adobe.com/pdf/1.3/"
16
+ prefix_default "pdf"
17
+ end
18
+
19
+ class XmpNS < Lutaml::Xml::Namespace
20
+ uri "http://ns.adobe.com/xap/1.0/"
21
+ prefix_default "xmp"
22
+ end
23
+
24
+ class XmpRightsNS < Lutaml::Xml::Namespace
25
+ uri "http://ns.adobe.com/xap/1.0/rights/"
26
+ prefix_default "xmpRights"
27
+ end
28
+
29
+ # Dublin Core (dc:) — basic descriptive metadata.
30
+ class DublinCore < Lutaml::Model::Serializable
31
+ attribute :title, :string, collection: true
32
+ attribute :creator, :string, collection: true
33
+ attribute :description, :string, collection: true
34
+ attribute :subject, :string, collection: true
35
+ attribute :identifier, :string, collection: true
36
+ attribute :language, :string, collection: true
37
+
38
+ xml do
39
+ root "dc"
40
+ namespace DublinCoreNS
41
+ map_element "title", to: :title
42
+ map_element "creator", to: :creator
43
+ map_element "description", to: :description
44
+ map_element "subject", to: :subject
45
+ map_element "identifier", to: :identifier
46
+ map_element "language", to: :language
47
+ end
48
+ end
49
+
50
+ # PDF (pdf:) — PDF-specific metadata.
51
+ class PDF < Lutaml::Model::Serializable
52
+ attribute :keywords, :string
53
+ attribute :producer, :string
54
+ attribute :trapped, :string
55
+
56
+ xml do
57
+ root "pdf"
58
+ namespace PdfNS
59
+ map_element "Keywords", to: :keywords
60
+ map_element "Producer", to: :producer
61
+ map_element "Trapped", to: :trapped
62
+ end
63
+ end
64
+
65
+ # XMP basic (xmp:) — document lifecycle metadata.
66
+ class XMPBasic < Lutaml::Model::Serializable
67
+ attribute :creator_tool, :string
68
+ attribute :create_date, :string
69
+ attribute :modify_date, :string
70
+ attribute :metadata_date, :string
71
+
72
+ xml do
73
+ root "xmp"
74
+ namespace XmpNS
75
+ map_element "CreatorTool", to: :creator_tool
76
+ map_element "CreateDate", to: :create_date
77
+ map_element "ModifyDate", to: :modify_date
78
+ map_element "MetadataDate", to: :metadata_date
79
+ end
80
+ end
81
+
82
+ # XMP Rights (xmpRights:) — rights management metadata.
83
+ class XMPRights < Lutaml::Model::Serializable
84
+ attribute :marked, :boolean
85
+ attribute :web_statement, :string
86
+
87
+ xml do
88
+ root "xmpRights"
89
+ namespace XmpRightsNS
90
+ map_element "Marked", to: :marked
91
+ map_element "WebStatement", to: :web_statement
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
data/lib/pdfrb/xmp.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module XMP
5
+ autoload :Packet, "pdfrb/xmp/packet"
6
+ autoload :Schemas, "pdfrb/xmp/schemas"
7
+ end
8
+ end
9
+
10
+ Pdfrb::Document.prepend(Module.new do
11
+ def write(path = nil, io: nil)
12
+ sync_xmp_metadata! unless self.io
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def sync_xmp_metadata!
19
+ info = trailer&.[](:Info)
20
+ return unless info
21
+
22
+ info_obj = object(info)
23
+ return unless info_obj
24
+
25
+ title = info_obj.value[:Title]
26
+ return unless title
27
+
28
+ packet = begin
29
+ xmp
30
+ rescue StandardError
31
+ nil
32
+ end
33
+ return unless packet
34
+
35
+ xmp_data = packet.to_xmp
36
+ stream = add(
37
+ { Type: :Metadata, Subtype: :XML, Length: xmp_data.bytesize },
38
+ type: Pdfrb::Model::Cos::Stream
39
+ )
40
+ stream.stream = xmp_data
41
+ catalog.value[:Metadata] =
42
+ Pdfrb::Model::Reference.new(stream.oid, stream.gen)
43
+ end
44
+ end)
@@ -38,8 +38,8 @@ module Pdfrb
38
38
  @entries[oid]
39
39
  end
40
40
 
41
- def each_entry(&block)
42
- @entries.each(&block)
41
+ def each_entry(&)
42
+ @entries.each(&)
43
43
  end
44
44
 
45
45
  def merge!(other)
data/lib/pdfrb.rb CHANGED
@@ -1,35 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Pdfrb — pure-Ruby PDF library.
4
- #
5
- # Two-direction contract:
6
- # * "PDF file <=> Model" — parse PDF bytes into a typed Model, serialize
7
- # a Model back to PDF bytes. Round-trip is the correctness backbone.
8
- # * "API Builder Input => Model" — Document / Canvas build a typed
9
- # Model from user intent, which is then serialized.
10
- #
11
- # Pdfrb is deliberately scoped to the PDF byte format. Layout concerns
12
- # (page templates, flows, line breaking, tables, etc.) live in the
13
- # sibling `arroolio` gem.
14
- #
15
- # The PDF object model is sourced directly from the vendored Arlington
16
- # PDF Model TSVs (data/pdfrb/arlington/*.tsv). Field metadata, version
17
- # predicates, and validators stay aligned with ISO 32000-2:2020 by data,
18
- # not by hand-coded mimicry.
19
- #
20
- # Layering (mirrors the sibling postscript gem):
21
- # Source — bytes → tokens → COS graph (read direction)
22
- # Model — typed domain model (COS values + Type::* semantics)
23
- # Arlington — TSV loader + predicate evaluator (drives Model field defs)
24
- # Filter — stream filter pipeline (Flate, ASCII-85, LZW, ...)
25
- # Content — content-stream operators + Canvas drawing API
26
- # Serializer — Model → bytes
27
- # Writer — Document → file (xref + trailer assembly)
28
- # Document — top-level facade
29
3
  module Pdfrb
30
4
  autoload :VERSION, "pdfrb/version"
31
5
 
32
- # Errors — every failure mode in the gem subclasses Pdfrb::Error.
33
6
  autoload :Error, "pdfrb/error"
34
7
  autoload :ParseError, "pdfrb/error"
35
8
  autoload :LexError, "pdfrb/error"
@@ -42,13 +15,10 @@ module Pdfrb
42
15
  autoload :ValidationError, "pdfrb/error"
43
16
  autoload :ObjectReferenceError, "pdfrb/error"
44
17
 
45
- # Cross-cutting helpers.
46
18
  autoload :Configuration, "pdfrb/configuration"
47
19
  autoload :DataDir, "pdfrb/data_dir"
48
20
  autoload :PdfConstants, "pdfrb/pdf_constants"
49
21
 
50
- # Sub-namespaces. Each lives in its own file which holds the autoloads
51
- # for that namespace's children (the "immediate parent" rule).
52
22
  autoload :Source, "pdfrb/source"
53
23
  autoload :Model, "pdfrb/model"
54
24
  autoload :Arlington, "pdfrb/arlington"
@@ -57,32 +27,22 @@ module Pdfrb
57
27
  autoload :Encryption, "pdfrb/encryption"
58
28
  autoload :DigitalSignature, "pdfrb/digital_signature"
59
29
 
60
- # Eager-load Type::* subclasses so they self-register in the
61
- # /Type-symbol type_map before any wrap call happens. Without
62
- # this, the first read of a Catalog wouldn't upgrade to
63
- # Type::Catalog (because the constant hadn't been autoloaded yet
64
- # and therefore hadn't called register_type).
65
- require "pdfrb/model/type/file_trailer"
66
- require "pdfrb/model/type/catalog"
67
- require "pdfrb/model/type/info"
68
- require "pdfrb/model/type/page_tree_node"
69
- require "pdfrb/model/type/page"
70
- require "pdfrb/model/type/resources"
71
- require "pdfrb/model/type/metadata"
72
- require "pdfrb/model/type/object_stream"
73
- require "pdfrb/model/type/xref_stream"
74
- require "pdfrb/model/type/graphics_state_parameter"
75
- require "pdfrb/model/type/optional_content_group"
76
- require "pdfrb/model/type/struct_tree_root"
77
- require "pdfrb/model/type/font_type1"
78
- require "pdfrb/model/type/annotation"
79
- require "pdfrb/model/type/action"
80
30
  autoload :Font, "pdfrb/font"
81
31
  autoload :FontLoader, "pdfrb/font_loader"
32
+ autoload :FontResolver, "pdfrb/font_resolver"
82
33
  autoload :ImageLoader, "pdfrb/image_loader"
34
+ autoload :Image, "pdfrb/image"
83
35
  autoload :Task, "pdfrb/task"
36
+ autoload :Color, "pdfrb/color"
37
+ autoload :Annotation, "pdfrb/annotation"
38
+ autoload :Appearance, "pdfrb/appearance"
39
+ autoload :Conformance, "pdfrb/conformance"
40
+ autoload :Validator, "pdfrb/validator"
41
+ autoload :XMP, "pdfrb/xmp"
42
+ autoload :Destination, "pdfrb/destination"
43
+ autoload :Linearization, "pdfrb/linearization"
44
+ autoload :Action, "pdfrb/action"
84
45
 
85
- # Top-level orchestrators.
86
46
  autoload :Serializer, "pdfrb/serializer"
87
47
  autoload :Writer, "pdfrb/writer"
88
48
  autoload :Importer, "pdfrb/importer"
@@ -91,7 +51,19 @@ module Pdfrb
91
51
  autoload :XrefSection, "pdfrb/xref_section"
92
52
  autoload :Document, "pdfrb/document"
93
53
 
94
- # CLI (only loaded when invoked via the +pdfrb+ executable; avoids
95
- # pulling in thor for library-only users).
54
+ autoload :Layout, "pdfrb/layout"
55
+ autoload :Composer, "pdfrb/composer"
56
+ autoload :Compare, "pdfrb/compare"
57
+ autoload :TestUtils, "pdfrb/test_utils"
96
58
  autoload :CLI, "pdfrb/cli"
59
+
60
+ require "logger"
61
+
62
+ def self.logger
63
+ @logger ||= Logger.new($stderr, level: Logger::WARN)
64
+ end
65
+
66
+ def self.logger=(log)
67
+ @logger = log
68
+ end
97
69
  end
data/pdfrb.gemspec ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/pdfrb/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "pdfrb"
7
+ spec.version = Pdfrb::VERSION
8
+ spec.authors = ["Ribose Inc."]
9
+ spec.email = ["open.source@ribose.com"]
10
+
11
+ spec.summary = "Pure-Ruby PDF parser, Arlington-model-driven domain model, and serializer"
12
+ spec.description = <<~HEREDOC
13
+ Pdfrb is a pure-Ruby PDF library: a byte-level reader, an
14
+ Arlington-model-driven typed domain model, and a serializer. The
15
+ PDF object model is sourced directly from the vendored Arlington
16
+ PDF Model TSVs (machine-readable ISO 32000-2:2020), so field
17
+ metadata, version predicates, and validators stay aligned with
18
+ the spec by data, not by hand-coded mimicry.
19
+
20
+ Two-direction contract: "PDF file <=> Model" and "API Builder
21
+ Input => Model". Mirrors the layered design of the sibling
22
+ postscript gem.
23
+ HEREDOC
24
+
25
+ spec.homepage = "https://github.com/claricle/pdfrb"
26
+ spec.license = "BSD-2-Clause"
27
+ spec.required_ruby_version = ">= 3.2.0"
28
+
29
+ spec.metadata["homepage_uri"] = spec.homepage
30
+ spec.metadata["source_code_uri"] = "https://github.com/claricle/pdfrb"
31
+ spec.metadata["changelog_uri"] = "https://github.com/claricle/pdfrb/blob/main/CHANGELOG.md"
32
+ spec.metadata["bug_tracker_uri"] = "https://github.com/claricle/pdfrb/issues"
33
+ spec.metadata["rubygems_mfa_required"] = "true"
34
+
35
+ spec.files = Dir.chdir(__dir__) do
36
+ `git ls-files -z`.split("\x0").reject do |f|
37
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
38
+ end
39
+ end
40
+ spec.bindir = "exe"
41
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
42
+ spec.require_paths = ["lib"]
43
+
44
+ spec.add_dependency "thor"
45
+ end