pdfrb 0.3.0 → 0.7.1

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 (422) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +51 -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 +61 -0
  23. data/data/pdfrb/afm/Courier-Bold.afm +342 -342
  24. data/data/pdfrb/afm/Courier-BoldOblique.afm +342 -342
  25. data/data/pdfrb/afm/Courier-Oblique.afm +342 -342
  26. data/data/pdfrb/afm/Courier.afm +342 -342
  27. data/data/pdfrb/afm/Helvetica-Bold.afm +2827 -2827
  28. data/data/pdfrb/afm/Helvetica-BoldOblique.afm +2827 -2827
  29. data/data/pdfrb/afm/Helvetica-Oblique.afm +3051 -3051
  30. data/data/pdfrb/afm/Helvetica.afm +3051 -3051
  31. data/data/pdfrb/afm/Symbol.afm +213 -213
  32. data/data/pdfrb/afm/Times-Bold.afm +2588 -2588
  33. data/data/pdfrb/afm/Times-BoldItalic.afm +2384 -2384
  34. data/data/pdfrb/afm/Times-Italic.afm +2667 -2667
  35. data/data/pdfrb/afm/Times-Roman.afm +2419 -2419
  36. data/data/pdfrb/afm/ZapfDingbats.afm +225 -225
  37. data/lib/pdfrb/action/base.rb +21 -0
  38. data/lib/pdfrb/action/types.rb +178 -0
  39. data/lib/pdfrb/action.rb +55 -0
  40. data/lib/pdfrb/annotation/base.rb +79 -0
  41. data/lib/pdfrb/annotation/types.rb +222 -0
  42. data/lib/pdfrb/annotation.rb +78 -0
  43. data/lib/pdfrb/appearance/generator.rb +216 -0
  44. data/lib/pdfrb/appearance.rb +11 -0
  45. data/lib/pdfrb/arlington/object_definition.rb +2 -2
  46. data/lib/pdfrb/arlington/predicate/evaluator.rb +1 -1
  47. data/lib/pdfrb/arlington/predicate/functions.rb +25 -9
  48. data/lib/pdfrb/arlington/predicate.rb +1 -5
  49. data/lib/pdfrb/cli/batch.rb +18 -0
  50. data/lib/pdfrb/cli/debug_info.rb +18 -0
  51. data/lib/pdfrb/cli/files.rb +16 -0
  52. data/lib/pdfrb/cli/fonts.rb +16 -0
  53. data/lib/pdfrb/cli/form.rb +18 -0
  54. data/lib/pdfrb/cli/image2pdf.rb +18 -0
  55. data/lib/pdfrb/cli/images.rb +34 -0
  56. data/lib/pdfrb/cli/info.rb +20 -0
  57. data/lib/pdfrb/cli/inspect.rb +18 -0
  58. data/lib/pdfrb/cli/merge.rb +18 -0
  59. data/lib/pdfrb/cli/modify.rb +18 -0
  60. data/lib/pdfrb/cli/optimize.rb +18 -0
  61. data/lib/pdfrb/cli/split.rb +21 -0
  62. data/lib/pdfrb/cli/watermark.rb +18 -0
  63. data/lib/pdfrb/cli.rb +291 -7
  64. data/lib/pdfrb/color/color_space.rb +259 -0
  65. data/lib/pdfrb/color/icc_profile.rb +90 -0
  66. data/lib/pdfrb/color.rb +12 -0
  67. data/lib/pdfrb/compare/comparator.rb +244 -0
  68. data/lib/pdfrb/compare/report.rb +78 -0
  69. data/lib/pdfrb/compare.rb +42 -0
  70. data/lib/pdfrb/composer.rb +161 -0
  71. data/lib/pdfrb/conformance/pdf_a.rb +317 -56
  72. data/lib/pdfrb/conformance/pdf_ua.rb +186 -29
  73. data/lib/pdfrb/conformance/pdf_x.rb +247 -0
  74. data/lib/pdfrb/conformance/rule.rb +78 -0
  75. data/lib/pdfrb/conformance/structure_elements.rb +174 -0
  76. data/lib/pdfrb/conformance/verapdf_bridge.rb +92 -0
  77. data/lib/pdfrb/conformance.rb +7 -3
  78. data/lib/pdfrb/content/canvas.rb +354 -72
  79. data/lib/pdfrb/content/color_space.rb +157 -0
  80. data/lib/pdfrb/content/hidden_text_detector.rb +167 -0
  81. data/lib/pdfrb/content/operator.rb +21 -1
  82. data/lib/pdfrb/content/operators/clipping.rb +36 -0
  83. data/lib/pdfrb/content/operators/graphics_state_params.rb +15 -0
  84. data/lib/pdfrb/content/operators/inline_image.rb +53 -0
  85. data/lib/pdfrb/content/operators.rb +22 -0
  86. data/lib/pdfrb/content/shading/base.rb +139 -0
  87. data/lib/pdfrb/content/shading.rb +17 -0
  88. data/lib/pdfrb/content/smart_text_extractor.rb +85 -0
  89. data/lib/pdfrb/content/tiling_pattern.rb +78 -0
  90. data/lib/pdfrb/content/transformation_matrix.rb +61 -0
  91. data/lib/pdfrb/content.rb +7 -26
  92. data/lib/pdfrb/destination/fit.rb +185 -0
  93. data/lib/pdfrb/destination.rb +13 -0
  94. data/lib/pdfrb/digital_signature/cms_handler.rb +22 -0
  95. data/lib/pdfrb/digital_signature/signing.rb +107 -11
  96. data/lib/pdfrb/digital_signature/timestamp_handler.rb +45 -0
  97. data/lib/pdfrb/digital_signature/verification.rb +88 -9
  98. data/lib/pdfrb/digital_signature/verification_result.rb +14 -0
  99. data/lib/pdfrb/digital_signature.rb +26 -2
  100. data/lib/pdfrb/document/annotations.rb +120 -3
  101. data/lib/pdfrb/document/associated_files.rb +104 -0
  102. data/lib/pdfrb/document/colors.rb +95 -0
  103. data/lib/pdfrb/document/destinations.rb +127 -5
  104. data/lib/pdfrb/document/display.rb +84 -0
  105. data/lib/pdfrb/document/fonts.rb +417 -45
  106. data/lib/pdfrb/document/form.rb +338 -0
  107. data/lib/pdfrb/document/graphics_state.rb +91 -0
  108. data/lib/pdfrb/document/images.rb +4 -4
  109. data/lib/pdfrb/document/info.rb +102 -0
  110. data/lib/pdfrb/document/layers.rb +62 -0
  111. data/lib/pdfrb/document/metadata.rb +12 -5
  112. data/lib/pdfrb/document/output_intents.rb +91 -0
  113. data/lib/pdfrb/document/page_labels.rb +80 -0
  114. data/lib/pdfrb/document/pages.rb +68 -19
  115. data/lib/pdfrb/document/portfolio.rb +119 -0
  116. data/lib/pdfrb/document/shadings.rb +159 -0
  117. data/lib/pdfrb/document/stamps.rb +68 -0
  118. data/lib/pdfrb/document/structure.rb +242 -0
  119. data/lib/pdfrb/document.rb +73 -4
  120. data/lib/pdfrb/encryption/identity.rb +13 -0
  121. data/lib/pdfrb/encryption/password_verification.rb +53 -8
  122. data/lib/pdfrb/encryption/security_handler.rb +8 -5
  123. data/lib/pdfrb/encryption/standard_security_handler.rb +154 -81
  124. data/lib/pdfrb/encryption.rb +17 -0
  125. data/lib/pdfrb/error.rb +2 -2
  126. data/lib/pdfrb/filter/brotli_decode.rb +32 -0
  127. data/lib/pdfrb/filter/ccitt_fax_decode.rb +12 -0
  128. data/lib/pdfrb/filter/crypt.rb +2 -2
  129. data/lib/pdfrb/filter/dct_decode.rb +18 -0
  130. data/lib/pdfrb/filter/flate_decode.rb +4 -18
  131. data/lib/pdfrb/filter/jbig2_decode.rb +12 -0
  132. data/lib/pdfrb/filter/jpx_decode.rb +18 -0
  133. data/lib/pdfrb/filter/lzw_decode.rb +3 -2
  134. data/lib/pdfrb/filter/png_predictor.rb +94 -0
  135. data/lib/pdfrb/filter.rb +26 -13
  136. data/lib/pdfrb/font/afm_parser.rb +50 -26
  137. data/lib/pdfrb/font/cmap/writer.rb +9 -0
  138. data/lib/pdfrb/font/encoding/win_ansi_encoding.rb +17 -0
  139. data/lib/pdfrb/font/encoding.rb +32 -3
  140. data/lib/pdfrb/font/metrics_helper.rb +121 -0
  141. data/lib/pdfrb/font/true_type/file.rb +10 -1
  142. data/lib/pdfrb/font/true_type/glyf.rb +82 -0
  143. data/lib/pdfrb/font/true_type/kern.rb +58 -0
  144. data/lib/pdfrb/font/true_type/loca.rb +42 -0
  145. data/lib/pdfrb/font/true_type/maxp.rb +44 -0
  146. data/lib/pdfrb/font/true_type/name.rb +53 -0
  147. data/lib/pdfrb/font/true_type/post.rb +93 -0
  148. data/lib/pdfrb/font/true_type/subsetter.rb +3 -5
  149. data/lib/pdfrb/font/true_type/wrapper.rb +102 -0
  150. data/lib/pdfrb/font/true_type.rb +7 -0
  151. data/lib/pdfrb/font.rb +2 -0
  152. data/lib/pdfrb/font_loader/from_configuration.rb +19 -0
  153. data/lib/pdfrb/font_loader/from_file.rb +20 -0
  154. data/lib/pdfrb/font_loader/standard14.rb +20 -0
  155. data/lib/pdfrb/font_loader/variant_from_name.rb +36 -0
  156. data/lib/pdfrb/font_loader.rb +4 -0
  157. data/lib/pdfrb/font_resolver.rb +138 -0
  158. data/lib/pdfrb/image/audit.rb +94 -0
  159. data/lib/pdfrb/image/downsampler.rb +124 -0
  160. data/lib/pdfrb/image.rb +13 -0
  161. data/lib/pdfrb/image_loader/jpeg.rb +39 -58
  162. data/lib/pdfrb/image_loader/png.rb +82 -251
  163. data/lib/pdfrb/layout/bidi.rb +198 -0
  164. data/lib/pdfrb/layout/box.rb +49 -0
  165. data/lib/pdfrb/layout/box_fitter.rb +54 -0
  166. data/lib/pdfrb/layout/column_box.rb +71 -0
  167. data/lib/pdfrb/layout/container_box.rb +45 -0
  168. data/lib/pdfrb/layout/frame.rb +67 -0
  169. data/lib/pdfrb/layout/image_box.rb +44 -0
  170. data/lib/pdfrb/layout/inline_box.rb +25 -0
  171. data/lib/pdfrb/layout/line.rb +55 -0
  172. data/lib/pdfrb/layout/list_box.rb +87 -0
  173. data/lib/pdfrb/layout/numeric_refinements.rb +43 -0
  174. data/lib/pdfrb/layout/page_style.rb +65 -0
  175. data/lib/pdfrb/layout/style.rb +62 -0
  176. data/lib/pdfrb/layout/table_box.rb +66 -0
  177. data/lib/pdfrb/layout/text_box.rb +38 -0
  178. data/lib/pdfrb/layout/text_fragment.rb +45 -0
  179. data/lib/pdfrb/layout/text_layouter.rb +123 -0
  180. data/lib/pdfrb/layout.rb +28 -0
  181. data/lib/pdfrb/linearization/hint_stream.rb +93 -0
  182. data/lib/pdfrb/linearization/writer.rb +259 -0
  183. data/lib/pdfrb/linearization.rb +21 -0
  184. data/lib/pdfrb/model/cos/dictionary.rb +12 -0
  185. data/lib/pdfrb/model/pdf_array.rb +2 -2
  186. data/lib/pdfrb/model/type/action.rb +42 -0
  187. data/lib/pdfrb/model/type/action_go_to_3d_view.rb +14 -0
  188. data/lib/pdfrb/model/type/action_go_to_dp.rb +16 -0
  189. data/lib/pdfrb/model/type/action_go_to_r.rb +26 -0
  190. data/lib/pdfrb/model/type/action_goto.rb +34 -0
  191. data/lib/pdfrb/model/type/action_hide.rb +26 -0
  192. data/lib/pdfrb/model/type/action_import_data.rb +18 -0
  193. data/lib/pdfrb/model/type/action_java_script.rb +18 -0
  194. data/lib/pdfrb/model/type/action_launch.rb +22 -0
  195. data/lib/pdfrb/model/type/action_movie.rb +22 -0
  196. data/lib/pdfrb/model/type/action_named.rb +23 -0
  197. data/lib/pdfrb/model/type/action_nop.rb +12 -0
  198. data/lib/pdfrb/model/type/action_rendition.rb +28 -0
  199. data/lib/pdfrb/model/type/action_reset_form.rb +22 -0
  200. data/lib/pdfrb/model/type/action_rich_media_execute.rb +17 -0
  201. data/lib/pdfrb/model/type/action_set_ocg_state.rb +32 -0
  202. data/lib/pdfrb/model/type/action_set_state.rb +14 -0
  203. data/lib/pdfrb/model/type/action_sound_action.rb +23 -0
  204. data/lib/pdfrb/model/type/action_submit_form.rb +64 -0
  205. data/lib/pdfrb/model/type/action_thread.rb +23 -0
  206. data/lib/pdfrb/model/type/action_trans.rb +30 -0
  207. data/lib/pdfrb/model/type/action_uri.rb +30 -0
  208. data/lib/pdfrb/model/type/af_embedded_file.rb +11 -0
  209. data/lib/pdfrb/model/type/af_file_specification.rb +34 -0
  210. data/lib/pdfrb/model/type/annotation.rb +49 -10
  211. data/lib/pdfrb/model/type/appearance.rb +41 -0
  212. data/lib/pdfrb/model/type/appearance_characteristics.rb +36 -0
  213. data/lib/pdfrb/model/type/appearance_generator.rb +22 -0
  214. data/lib/pdfrb/model/type/appearance_trap_net.rb +84 -0
  215. data/lib/pdfrb/model/type/border_effect.rb +25 -0
  216. data/lib/pdfrb/model/type/border_styling.rb +38 -0
  217. data/lib/pdfrb/model/type/box_color_info.rb +21 -0
  218. data/lib/pdfrb/model/type/box_style.rb +19 -0
  219. data/lib/pdfrb/model/type/button.rb +17 -0
  220. data/lib/pdfrb/model/type/cal_gray.rb +17 -0
  221. data/lib/pdfrb/model/type/cal_rgb.rb +22 -0
  222. data/lib/pdfrb/model/type/caret_annotation.rb +21 -0
  223. data/lib/pdfrb/model/type/catalog.rb +99 -16
  224. data/lib/pdfrb/model/type/choice.rb +21 -0
  225. data/lib/pdfrb/model/type/cid_font.rb +52 -1
  226. data/lib/pdfrb/model/type/cid_font_descriptor_metrics.rb +28 -0
  227. data/lib/pdfrb/model/type/cid_system_info.rb +28 -0
  228. data/lib/pdfrb/model/type/circle_annotation.rb +20 -0
  229. data/lib/pdfrb/model/type/collection.rb +27 -0
  230. data/lib/pdfrb/model/type/collection_field.rb +24 -0
  231. data/lib/pdfrb/model/type/collection_item.rb +19 -0
  232. data/lib/pdfrb/model/type/collection_schema.rb +25 -0
  233. data/lib/pdfrb/model/type/collection_sort.rb +13 -0
  234. data/lib/pdfrb/model/type/crypt_filter.rb +58 -0
  235. data/lib/pdfrb/model/type/device_n.rb +37 -0
  236. data/lib/pdfrb/model/type/device_n_mixing_hints.rb +17 -0
  237. data/lib/pdfrb/model/type/device_n_process.rb +13 -0
  238. data/lib/pdfrb/model/type/doc_mdp_transform_parameters.rb +21 -0
  239. data/lib/pdfrb/model/type/document_security_store.rb +55 -0
  240. data/lib/pdfrb/model/type/embedded_file.rb +19 -0
  241. data/lib/pdfrb/model/type/embedded_file_parameter.rb +48 -0
  242. data/lib/pdfrb/model/type/encryption_public_key.rb +38 -0
  243. data/lib/pdfrb/model/type/encryption_standard.rb +68 -0
  244. data/lib/pdfrb/model/type/field_mdp_transform_parameters.rb +26 -0
  245. data/lib/pdfrb/model/type/file_attachment_annotation.rb +29 -0
  246. data/lib/pdfrb/model/type/file_spec_ef.rb +22 -0
  247. data/lib/pdfrb/model/type/file_spec_rf.rb +21 -0
  248. data/lib/pdfrb/model/type/file_specification.rb +43 -0
  249. data/lib/pdfrb/model/type/file_trailer.rb +54 -0
  250. data/lib/pdfrb/model/type/font.rb +73 -2
  251. data/lib/pdfrb/model/type/font_cid_type0.rb +17 -0
  252. data/lib/pdfrb/model/type/font_cid_type2.rb +17 -0
  253. data/lib/pdfrb/model/type/font_descriptor.rb +47 -0
  254. data/lib/pdfrb/model/type/font_descriptor_cid_type0.rb +15 -0
  255. data/lib/pdfrb/model/type/font_descriptor_cid_type2.rb +15 -0
  256. data/lib/pdfrb/model/type/font_encoding.rb +47 -0
  257. data/lib/pdfrb/model/type/font_file.rb +28 -0
  258. data/lib/pdfrb/model/type/font_file2.rb +32 -0
  259. data/lib/pdfrb/model/type/font_file3.rb +23 -0
  260. data/lib/pdfrb/model/type/font_multiple_master.rb +60 -0
  261. data/lib/pdfrb/model/type/font_true_type.rb +1 -1
  262. data/lib/pdfrb/model/type/font_type0.rb +1 -1
  263. data/lib/pdfrb/model/type/font_type1.rb +1 -1
  264. data/lib/pdfrb/model/type/font_type3.rb +1 -1
  265. data/lib/pdfrb/model/type/form_field.rb +73 -0
  266. data/lib/pdfrb/model/type/free_text_annotation.rb +37 -0
  267. data/lib/pdfrb/model/type/function.rb +42 -0
  268. data/lib/pdfrb/model/type/function_exponential.rb +31 -0
  269. data/lib/pdfrb/model/type/function_postscript.rb +25 -0
  270. data/lib/pdfrb/model/type/function_sampled.rb +55 -0
  271. data/lib/pdfrb/model/type/function_stitching.rb +33 -0
  272. data/lib/pdfrb/model/type/generic_appearance.rb +40 -0
  273. data/lib/pdfrb/model/type/graphics_state_parameter.rb +69 -0
  274. data/lib/pdfrb/model/type/halftone.rb +21 -0
  275. data/lib/pdfrb/model/type/halftone_type1.rb +17 -0
  276. data/lib/pdfrb/model/type/halftone_type10.rb +16 -0
  277. data/lib/pdfrb/model/type/halftone_type16.rb +11 -0
  278. data/lib/pdfrb/model/type/halftone_type5.rb +24 -0
  279. data/lib/pdfrb/model/type/halftone_type6.rb +16 -0
  280. data/lib/pdfrb/model/type/highlight_annotation.rb +11 -0
  281. data/lib/pdfrb/model/type/icc_based_color_space.rb +24 -0
  282. data/lib/pdfrb/model/type/icon_fit.rb +31 -0
  283. data/lib/pdfrb/model/type/indexed.rb +32 -0
  284. data/lib/pdfrb/model/type/info.rb +44 -0
  285. data/lib/pdfrb/model/type/ink_annotation.rb +29 -0
  286. data/lib/pdfrb/model/type/interactive_form.rb +36 -0
  287. data/lib/pdfrb/model/type/interior_color.rb +12 -0
  288. data/lib/pdfrb/model/type/jbig2_globals.rb +15 -0
  289. data/lib/pdfrb/model/type/lab.rb +27 -0
  290. data/lib/pdfrb/model/type/line_annotation.rb +44 -0
  291. data/lib/pdfrb/model/type/line_ending_styling.rb +39 -0
  292. data/lib/pdfrb/model/type/link_annotation.rb +41 -0
  293. data/lib/pdfrb/model/type/link_appearance.rb +9 -0
  294. data/lib/pdfrb/model/type/mark_information.rb +23 -0
  295. data/lib/pdfrb/model/type/marked_content_reference.rb +24 -0
  296. data/lib/pdfrb/model/type/markup_annotation.rb +36 -0
  297. data/lib/pdfrb/model/type/mdp_dict.rb +28 -0
  298. data/lib/pdfrb/model/type/measure.rb +39 -0
  299. data/lib/pdfrb/model/type/media_clip.rb +28 -0
  300. data/lib/pdfrb/model/type/media_offset.rb +106 -0
  301. data/lib/pdfrb/model/type/metadata.rb +27 -3
  302. data/lib/pdfrb/model/type/misc_helpers.rb +75 -0
  303. data/lib/pdfrb/model/type/movie.rb +28 -0
  304. data/lib/pdfrb/model/type/names.rb +51 -0
  305. data/lib/pdfrb/model/type/namespace.rb +27 -0
  306. data/lib/pdfrb/model/type/object_reference.rb +31 -0
  307. data/lib/pdfrb/model/type/object_stream.rb +38 -0
  308. data/lib/pdfrb/model/type/opt_content_ext.rb +94 -0
  309. data/lib/pdfrb/model/type/opt_content_page_element.rb +18 -0
  310. data/lib/pdfrb/model/type/optional_content_config.rb +56 -0
  311. data/lib/pdfrb/model/type/optional_content_group.rb +35 -10
  312. data/lib/pdfrb/model/type/optional_content_membership.rb +36 -2
  313. data/lib/pdfrb/model/type/optional_content_properties.rb +16 -1
  314. data/lib/pdfrb/model/type/outline.rb +40 -6
  315. data/lib/pdfrb/model/type/outline_item.rb +45 -0
  316. data/lib/pdfrb/model/type/output_intent.rb +30 -0
  317. data/lib/pdfrb/model/type/page.rb +127 -0
  318. data/lib/pdfrb/model/type/page_label.rb +68 -0
  319. data/lib/pdfrb/model/type/page_piece_info.rb +58 -0
  320. data/lib/pdfrb/model/type/page_tree_node.rb +51 -16
  321. data/lib/pdfrb/model/type/page_tree_node_root.rb +15 -0
  322. data/lib/pdfrb/model/type/pattern.rb +16 -0
  323. data/lib/pdfrb/model/type/pattern_shading.rb +25 -0
  324. data/lib/pdfrb/model/type/pattern_tiling.rb +25 -0
  325. data/lib/pdfrb/model/type/polygon_annotation.rb +29 -0
  326. data/lib/pdfrb/model/type/polygon_polyline.rb +14 -0
  327. data/lib/pdfrb/model/type/polyline_annotation.rb +29 -0
  328. data/lib/pdfrb/model/type/popup_annotation.rb +20 -0
  329. data/lib/pdfrb/model/type/printer_mark_annotation.rb +13 -0
  330. data/lib/pdfrb/model/type/redact_annotation.rb +28 -0
  331. data/lib/pdfrb/model/type/rendition.rb +31 -0
  332. data/lib/pdfrb/model/type/resources.rb +59 -14
  333. data/lib/pdfrb/model/type/rich_media_activation.rb +24 -0
  334. data/lib/pdfrb/model/type/rich_media_animation.rb +25 -0
  335. data/lib/pdfrb/model/type/rich_media_annotation.rb +18 -0
  336. data/lib/pdfrb/model/type/rich_media_command.rb +19 -0
  337. data/lib/pdfrb/model/type/rich_media_configuration.rb +32 -0
  338. data/lib/pdfrb/model/type/rich_media_content.rb +28 -0
  339. data/lib/pdfrb/model/type/rich_media_cue_point.rb +18 -0
  340. data/lib/pdfrb/model/type/rich_media_deactivation.rb +17 -0
  341. data/lib/pdfrb/model/type/rich_media_instance.rb +33 -0
  342. data/lib/pdfrb/model/type/rich_media_presentation.rb +29 -0
  343. data/lib/pdfrb/model/type/rich_media_settings.rb +33 -0
  344. data/lib/pdfrb/model/type/rich_media_window.rb +20 -0
  345. data/lib/pdfrb/model/type/screen_annotation.rb +19 -0
  346. data/lib/pdfrb/model/type/separation.rb +25 -0
  347. data/lib/pdfrb/model/type/sig_field_lock.rb +25 -0
  348. data/lib/pdfrb/model/type/sig_field_seed_value.rb +25 -0
  349. data/lib/pdfrb/model/type/signature.rb +65 -0
  350. data/lib/pdfrb/model/type/signature_build.rb +86 -0
  351. data/lib/pdfrb/model/type/signature_field.rb +17 -0
  352. data/lib/pdfrb/model/type/software_identifier.rb +19 -0
  353. data/lib/pdfrb/model/type/sound.rb +38 -0
  354. data/lib/pdfrb/model/type/sound_annotation.rb +21 -0
  355. data/lib/pdfrb/model/type/square_annotation.rb +24 -0
  356. data/lib/pdfrb/model/type/square_circle.rb +13 -0
  357. data/lib/pdfrb/model/type/squiggly_annotation.rb +11 -0
  358. data/lib/pdfrb/model/type/stamp_annotation.rb +19 -0
  359. data/lib/pdfrb/model/type/strikeout_annotation.rb +11 -0
  360. data/lib/pdfrb/model/type/struct_elem.rb +75 -0
  361. data/lib/pdfrb/model/type/struct_tree_root.rb +34 -0
  362. data/lib/pdfrb/model/type/structure_attributes.rb +44 -0
  363. data/lib/pdfrb/model/type/structure_element_kid.rb +33 -0
  364. data/lib/pdfrb/model/type/text_annotation.rb +25 -0
  365. data/lib/pdfrb/model/type/text_appearance.rb +9 -0
  366. data/lib/pdfrb/model/type/text_field.rb +21 -0
  367. data/lib/pdfrb/model/type/text_markup_annotation.rb +30 -0
  368. data/lib/pdfrb/model/type/three_d_activation.rb +32 -0
  369. data/lib/pdfrb/model/type/three_d_animation_style.rb +24 -0
  370. data/lib/pdfrb/model/type/three_d_background.rb +28 -0
  371. data/lib/pdfrb/model/type/three_d_cross_section.rb +25 -0
  372. data/lib/pdfrb/model/type/three_d_lighting_scheme.rb +26 -0
  373. data/lib/pdfrb/model/type/three_d_measure.rb +20 -0
  374. data/lib/pdfrb/model/type/three_d_measure_3dc.rb +23 -0
  375. data/lib/pdfrb/model/type/three_d_measure_ad3.rb +14 -0
  376. data/lib/pdfrb/model/type/three_d_measure_ld3.rb +14 -0
  377. data/lib/pdfrb/model/type/three_d_measure_pd3.rb +20 -0
  378. data/lib/pdfrb/model/type/three_d_measure_rd3.rb +14 -0
  379. data/lib/pdfrb/model/type/three_d_node.rb +37 -0
  380. data/lib/pdfrb/model/type/three_d_reference.rb +21 -0
  381. data/lib/pdfrb/model/type/three_d_render_mode.rb +34 -0
  382. data/lib/pdfrb/model/type/three_d_stream.rb +48 -0
  383. data/lib/pdfrb/model/type/three_d_units.rb +18 -0
  384. data/lib/pdfrb/model/type/three_d_view.rb +40 -0
  385. data/lib/pdfrb/model/type/to_unicode_cmap_stream.rb +23 -0
  386. data/lib/pdfrb/model/type/transition.rb +68 -0
  387. data/lib/pdfrb/model/type/underline_annotation.rb +11 -0
  388. data/lib/pdfrb/model/type/ur_transform_parameters.rb +33 -0
  389. data/lib/pdfrb/model/type/uri_dict.rb +18 -0
  390. data/lib/pdfrb/model/type/url_alias.rb +19 -0
  391. data/lib/pdfrb/model/type/variable_text_field.rb +47 -0
  392. data/lib/pdfrb/model/type/viewer_preferences.rb +42 -0
  393. data/lib/pdfrb/model/type/watermark_annotation.rb +13 -0
  394. data/lib/pdfrb/model/type/widget_annotation.rb +48 -0
  395. data/lib/pdfrb/model/type/widget_appearance.rb +9 -0
  396. data/lib/pdfrb/model/type/xobject_form.rb +41 -0
  397. data/lib/pdfrb/model/type/xobject_image.rb +56 -0
  398. data/lib/pdfrb/model/type/xref_stream.rb +37 -0
  399. data/lib/pdfrb/model/type.rb +289 -6
  400. data/lib/pdfrb/serializer.rb +2 -2
  401. data/lib/pdfrb/source/tokenizer.rb +0 -2
  402. data/lib/pdfrb/task/benchmark.rb +83 -0
  403. data/lib/pdfrb/task/extract_text.rb +1 -1
  404. data/lib/pdfrb/task/generate_corpus.rb +130 -0
  405. data/lib/pdfrb/task/memory_profile.rb +84 -0
  406. data/lib/pdfrb/task/optimize.rb +97 -7
  407. data/lib/pdfrb/task/regenerate_appearances.rb +98 -0
  408. data/lib/pdfrb/task.rb +4 -3
  409. data/lib/pdfrb/test_utils.rb +112 -0
  410. data/lib/pdfrb/validator.rb +82 -0
  411. data/lib/pdfrb/version.rb +1 -1
  412. data/lib/pdfrb/writer.rb +117 -27
  413. data/lib/pdfrb/xmp/packet.rb +122 -0
  414. data/lib/pdfrb/xmp/schemas.rb +96 -0
  415. data/lib/pdfrb/xmp.rb +44 -0
  416. data/lib/pdfrb/xref_section.rb +2 -2
  417. data/lib/pdfrb.rb +24 -54
  418. data/pdfrb.gemspec +45 -0
  419. data/script/apply_fixes.sh +843 -0
  420. data/script/make_jpeg_fixture.rb +53 -0
  421. data/script/make_png_fixture.rb +25 -0
  422. metadata +319 -4
@@ -7,10 +7,13 @@ module Pdfrb
7
7
  class AFMParser
8
8
  attr_reader :font_name, :full_name, :family_name,
9
9
  :encoding_scheme, :bbox, :cap_height, :x_height,
10
- :ascender, :descender, :char_metrics
10
+ :ascender, :descender, :italic_angle,
11
+ :average_width, :max_width, :char_metrics
11
12
 
12
13
  def initialize
13
14
  @char_metrics = {}
15
+ @average_width = nil
16
+ @max_width = nil
14
17
  end
15
18
 
16
19
  def self.parse(text)
@@ -25,35 +28,48 @@ module Pdfrb
25
28
  in_char_metrics = false
26
29
  text.each_line do |line|
27
30
  line = line.strip
28
- if line.start_with?("FontName ")
29
- @font_name = line.split(" ", 2)[1]
30
- elsif line.start_with?("FullName ")
31
- @full_name = line.split(" ", 2)[1]
32
- elsif line.start_with?("FamilyName ")
33
- @family_name = line.split(" ", 2)[1]
34
- elsif line.start_with?("EncodingScheme ")
35
- @encoding_scheme = line.split(" ", 2)[1]
36
- elsif line.start_with?("FontBBox ")
37
- @bbox = line.split.drop(1).map(&:to_f)
38
- elsif line.start_with?("CapHeight ")
39
- @cap_height = line.split[1].to_f
40
- elsif line.start_with?("XHeight ")
41
- @x_height = line.split[1].to_f
42
- elsif line.start_with?("Ascender ")
43
- @ascender = line.split[1].to_f
44
- elsif line.start_with?("Descender ")
45
- @descender = line.split[1].to_f
46
- elsif line == "StartCharMetrics" || line.start_with?("StartCharMetrics ")
47
- in_char_metrics = true
48
- elsif line == "EndCharMetrics" || line.start_with?("EndCharMetrics")
49
- in_char_metrics = false
50
- elsif in_char_metrics && line.start_with?("C ")
51
- parse_char_metric(line)
52
- end
31
+ in_char_metrics = parse_line(line, in_char_metrics)
53
32
  end
54
33
  self
55
34
  end
56
35
 
36
+ def parse_line(line, in_char_metrics)
37
+ return handle_state_change(line, in_char_metrics) if parse_global_metric(line) == :handled
38
+
39
+ handle_state_change(line, in_char_metrics)
40
+ end
41
+
42
+ def handle_state_change(line, in_char_metrics)
43
+ if line.start_with?("StartCharMetrics")
44
+ true
45
+ elsif line.start_with?("EndCharMetrics")
46
+ compute_average_and_max_widths
47
+ false
48
+ elsif in_char_metrics && line.start_with?("C ")
49
+ parse_char_metric(line)
50
+ in_char_metrics
51
+ else
52
+ in_char_metrics
53
+ end
54
+ end
55
+
56
+ def parse_global_metric(line)
57
+ case line
58
+ when /\AFontName / then @font_name = line.split(" ", 2)[1]
59
+ when /\AFullName / then @full_name = line.split(" ", 2)[1]
60
+ when /\AFamilyName / then @family_name = line.split(" ", 2)[1]
61
+ when /\AEncodingScheme / then @encoding_scheme = line.split(" ", 2)[1]
62
+ when /\AFontBBox / then @bbox = line.split.drop(1).map(&:to_f)
63
+ when /\ACapHeight / then @cap_height = line.split[1].to_f
64
+ when /\AXHeight / then @x_height = line.split[1].to_f
65
+ when /\AAscender / then @ascender = line.split[1].to_f
66
+ when /\ADescender / then @descender = line.split[1].to_f
67
+ when /\AItalicAngle / then @italic_angle = line.split[1].to_f
68
+ else return nil
69
+ end
70
+ :handled
71
+ end
72
+
57
73
  def width_for(glyph_name_or_code)
58
74
  metric = @char_metrics[glyph_name_or_code] ||
59
75
  @char_metrics[glyph_name_or_code.to_i]
@@ -62,6 +78,14 @@ module Pdfrb
62
78
 
63
79
  private
64
80
 
81
+ def compute_average_and_max_widths
82
+ widths = @char_metrics.values.filter_map { |m| m[:width] }
83
+ return if widths.empty?
84
+
85
+ @average_width = widths.sum / widths.length
86
+ @max_width = widths.max
87
+ end
88
+
65
89
  def parse_char_metric(line)
66
90
  parts = line.split(";").map(&:strip)
67
91
  code = nil
@@ -12,6 +12,15 @@ module Pdfrb
12
12
  # chunking of bfchar sections (max 100 entries per section per
13
13
  # the PDF spec).
14
14
  class Writer
15
+ def self.write(mapping, name: "Adobe-Identity-UCS")
16
+ str_mapping = mapping.transform_values { |cp| [cp].pack("U") }
17
+ new(
18
+ cmap_name: name,
19
+ cid_system_info: { registry: "Adobe", ordering: "UCS", supplement: 0 },
20
+ mapping: str_mapping,
21
+ code_size: 1,
22
+ ).to_s
23
+ end
15
24
  MAX_BFCHAR_ENTRIES = 100
16
25
 
17
26
  attr_reader :cmap_name, :cid_system_info, :mapping
@@ -19,6 +19,23 @@ module Pdfrb
19
19
  }.each { |b, cp| TABLE[b] = cp }
20
20
  (0xA0..0xFF).each { |b| TABLE[b] = b }
21
21
  TABLE.freeze
22
+
23
+ class << self
24
+ def encode(text)
25
+ Pdfrb::Font::Encoding.encode(:WinAnsiEncoding, text)
26
+ end
27
+
28
+ def decode(bytes)
29
+ Pdfrb::Font::Encoding.decode(:WinAnsiEncoding, bytes)
30
+ end
31
+
32
+ def encodable?(codepoint)
33
+ return true if codepoint < 0x80
34
+
35
+ reverse = Pdfrb::Font::Encoding.reverse_table_for(:WinAnsiEncoding)
36
+ !reverse[codepoint].nil?
37
+ end
38
+ end
22
39
  end
23
40
  end
24
41
  end
@@ -10,17 +10,28 @@ module Pdfrb
10
10
  autoload :ZapfDingbatsEncoding, "pdfrb/font/encoding/zapf_dingbats_encoding"
11
11
 
12
12
  class << self
13
- # Decode +bytes+ (one byte per glyph) using +encoding_name+
14
- # (a Symbol like :WinAnsiEncoding) to Unicode.
15
13
  def decode(encoding_name, bytes)
16
14
  table = table_for(encoding_name)
17
15
  return bytes unless table
18
16
 
19
17
  bytes.each_byte.with_object(+"") do |b, buf|
20
- buf << (table[b] ? [table[b]].pack("U") : "?")
18
+ cp = table.is_a?(::Array) ? table[b] : table[b.to_s.to_sym]
19
+ buf << (cp ? [cp].pack("U") : "?")
21
20
  end.encode("UTF-8")
22
21
  end
23
22
 
23
+ def encode(encoding_name, text)
24
+ table = table_for(encoding_name)
25
+ return text.to_s.b unless table
26
+
27
+ reverse = reverse_table_for(encoding_name)
28
+ text.to_s.each_char.with_object(+"") do |ch, buf|
29
+ cp = ch.ord
30
+ byte = reverse[cp] || (cp < 0x80 ? cp : nil)
31
+ buf << [byte || 0x3F].pack("C")
32
+ end.b
33
+ end
34
+
24
35
  def table_for(name)
25
36
  case name.to_sym
26
37
  when :WinAnsiEncoding then WinAnsiEncoding::TABLE
@@ -30,6 +41,24 @@ module Pdfrb
30
41
  when :ZapfDingbatsEncoding then ZapfDingbatsEncoding::TABLE
31
42
  end
32
43
  end
44
+
45
+ def reverse_table_for(name)
46
+ @reverse_tables ||= {}
47
+ @reverse_tables[name] ||= build_reverse_table(name)
48
+ end
49
+
50
+ def build_reverse_table(name)
51
+ table = table_for(name)
52
+ return {} unless table
53
+
54
+ if table.is_a?(::Array)
55
+ table.each_with_index.with_object({}) do |(cp, byte), h|
56
+ h[cp] = byte if cp
57
+ end
58
+ else
59
+ table.each_with_object({}) { |(byte, cp), h| h[cp] = byte }
60
+ end
61
+ end
33
62
  end
34
63
  end
35
64
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Font
5
+ # Metrics value object + lookup helper. Returns ascent/descent/
6
+ # cap_height/x_height/line_gap/avg_width for any font (Standard 14
7
+ # via AFM, embedded TTF/OTF via head/hhea/OS2 tables).
8
+ Metrics = Struct.new(:font_name, :units_per_em, :ascent, :descent,
9
+ :cap_height, :x_height, :line_gap, :avg_width,
10
+ :max_width, :stem_v, :italic_angle, :bbox,
11
+ keyword_init: true) do
12
+ def line_height(font_size)
13
+ ((ascent || 800) - (descent || -200) + (line_gap || 0)) *
14
+ font_size.to_f / (units_per_em || 1000)
15
+ end
16
+
17
+ def ascender(font_size)
18
+ (ascent || 800) * font_size.to_f / (units_per_em || 1000)
19
+ end
20
+
21
+ def descender(font_size)
22
+ (descent || -200) * font_size.to_f / (units_per_em || 1000)
23
+ end
24
+ end
25
+
26
+ module MetricsHelper
27
+ module_function
28
+
29
+ # Look up metrics for a Standard 14 font name.
30
+ # @param font_name [String, Symbol] e.g., "Helvetica", "Times-Roman".
31
+ # @return [Pdfrb::Font::Metrics, nil]
32
+ def metrics_for_standard14(font_name)
33
+ afm = afm_for(font_name.to_s)
34
+ return nil unless afm
35
+
36
+ Metrics.new(
37
+ font_name: font_name.to_s,
38
+ units_per_em: 1000,
39
+ ascent: afm.ascender,
40
+ descent: afm.descender,
41
+ cap_height: afm.cap_height,
42
+ x_height: afm.x_height,
43
+ line_gap: 0,
44
+ avg_width: afm.average_width,
45
+ max_width: afm.max_width,
46
+ stem_v: 80,
47
+ italic_angle: afm.italic_angle || 0,
48
+ bbox: afm.bbox
49
+ )
50
+ rescue StandardError
51
+ nil
52
+ end
53
+
54
+ # Look up metrics from raw TTF/OTF bytes.
55
+ # @param font_data [String] raw TTF or OTF bytes.
56
+ # @return [Pdfrb::Font::Metrics, nil]
57
+ def metrics_for_ttf(font_data)
58
+ return nil unless font_data && font_data.bytesize >= 4
59
+
60
+ ttf = Pdfrb::Font::TrueType::File.new(font_data)
61
+ head = ttf.head_table_parsed
62
+ hhea = ttf.hhea_table_parsed
63
+ os2 = ttf.os2_table_parsed
64
+ return nil unless head && hhea
65
+
66
+ Metrics.new(
67
+ font_name: ttf.name_table_parsed&.ps_name || "EmbeddedFont",
68
+ units_per_em: head.units_per_em || 1000,
69
+ ascent: hhea.ascender || os2&.s_typo_ascender || 800,
70
+ descent: hhea.descender || os2&.s_typo_descender || -200,
71
+ cap_height: os2&.s_cap_height || 700,
72
+ x_height: 500,
73
+ line_gap: hhea.line_gap || 0,
74
+ avg_width: 500,
75
+ max_width: hhea.advance_width_max || 1000,
76
+ stem_v: 80,
77
+ italic_angle: (head.italic? ? -12 : 0),
78
+ bbox: head.bbox
79
+ )
80
+ rescue StandardError
81
+ nil
82
+ end
83
+
84
+ # Convenience: dispatch on font_name vs raw bytes.
85
+ def metrics_for(font_name_or_data)
86
+ case font_name_or_data
87
+ when String
88
+ if font_name_or_data.bytesize >= 4 && valid_magic?(font_name_or_data)
89
+ metrics_for_ttf(font_name_or_data)
90
+ else
91
+ metrics_for_standard14(font_name_or_data)
92
+ end
93
+ when Symbol then metrics_for_standard14(font_name_or_data)
94
+ end
95
+ end
96
+
97
+ def valid_magic?(bytes)
98
+ magic = bytes.byteslice(0, 4)
99
+ ["\x00\x01\x00\x00".b, "OTTO".b, "true".b, "ttcf".b].include?(magic)
100
+ end
101
+
102
+ def afm_for(name)
103
+ @afm_cache ||= {}
104
+ return @afm_cache[name] if @afm_cache.key?(name)
105
+
106
+ path = standard14_afm_path(name)
107
+ return @afm_cache[name] = nil unless path && File.exist?(path)
108
+
109
+ @afm_cache[name] = Pdfrb::Font::AFMParser.parse(File.read(path))
110
+ end
111
+
112
+ def standard14_afm_path(name)
113
+ # __dir__ is lib/pdfrb/font/. Three .. gets to project root
114
+ # (lib/pdfrb/font → lib/pdfrb → lib → project root). Data
115
+ # files live at <root>/data/pdfrb/afm/.
116
+ base = File.expand_path("../../../data/pdfrb/afm", __dir__)
117
+ File.join(base, "#{name}.afm")
118
+ end
119
+ end
120
+ end
121
+ end
@@ -7,10 +7,11 @@ module Pdfrb
7
7
  # sfnt header + table directory; lazy-loads individual tables
8
8
  # on access.
9
9
  class File
10
- attr_reader :tables, :num_tables
10
+ attr_reader :tables, :num_tables, :raw_data
11
11
 
12
12
  def initialize(data)
13
13
  @data = data.b
14
+ @raw_data = @data
14
15
  @tables = {}
15
16
  parse_directory
16
17
  end
@@ -40,9 +41,17 @@ module Pdfrb
40
41
  def os2_table; table("OS/2"); end
41
42
  def glyf_table; table("glyf"); end
42
43
  def loca_table; table("loca"); end
44
+ def kern_table; table("kern"); end
43
45
 
44
46
  # Parsed-table accessors. Lazy and memoised on the instance.
45
47
  def head; @head ||= Head.new(head_table); end
48
+
49
+ def maxp; @maxp ||= Maxp.new(maxp_table); end
50
+ def post; @post ||= Post.new(post_table); end
51
+ def name_table_parsed; @name_table_parsed ||= Name.new(name_table); end
52
+ def loca_parsed; @loca_parsed ||= Loca.new(loca_table, long_format: head.long_loca?, num_glyphs: maxp.num_glyphs); end
53
+ def glyf_parsed; @glyf_parsed ||= Glyf.new(glyf_table, loca_parsed); end
54
+ def kern_parsed; @kern_parsed ||= Kern.new(kern_table); end
46
55
  def hhea; @hhea ||= Hhea.new(hhea_table); end
47
56
  def os2; @os2 ||= OS2.new(os2_table); end
48
57
  def cmap; @cmap ||= Cmap.new(cmap_table); end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Font
5
+ module TrueType
6
+ class Glyf
7
+ attr_reader :data, :loca
8
+
9
+ def initialize(data, loca = nil)
10
+ @data = data
11
+ @loca = loca
12
+ end
13
+
14
+ def glyph_data(glyph_id)
15
+ return nil unless @data && @loca
16
+ return nil if glyph_id >= @loca.offsets.length - 1
17
+
18
+ offset = @loca.glyph_offset(glyph_id)
19
+ length = @loca.glyph_length(glyph_id)
20
+ return nil if offset.nil? || length.zero?
21
+
22
+ @data.byteslice(offset, length)
23
+ end
24
+
25
+ def glyph_header(glyph_id)
26
+ g = glyph_data(glyph_id)
27
+ return nil unless g && g.bytesize >= 10
28
+
29
+ {
30
+ number_of_contours: s16(g, 0),
31
+ x_min: s16(g, 2),
32
+ y_min: s16(g, 4),
33
+ x_max: s16(g, 6),
34
+ y_max: s16(g, 8),
35
+ }
36
+ end
37
+
38
+ def composite?(glyph_id)
39
+ h = glyph_header(glyph_id)
40
+ h && h[:number_of_contours].negative?
41
+ end
42
+
43
+ def empty?(glyph_id)
44
+ length = @loca&.glyph_length(glyph_id) || 0
45
+ length.zero?
46
+ end
47
+
48
+ def component_glyphs(glyph_id)
49
+ return [] unless composite?(glyph_id)
50
+
51
+ g = glyph_data(glyph_id)
52
+ return [] unless g
53
+
54
+ components = []
55
+ offset = 10
56
+ loop do
57
+ break if offset + 4 > g.bytesize
58
+
59
+ flags = u16(g, offset)
60
+ glyph_index = u16(g, offset + 2)
61
+ components << glyph_index
62
+ offset += 4
63
+
64
+ offset += 4 if flags & 0x0001 != 0
65
+ offset += 2 if flags & 0x0002 != 0
66
+ break if flags.nobits?(0x0020)
67
+ end
68
+ components
69
+ end
70
+
71
+ private
72
+
73
+ def u16(d, o); (d.getbyte(o) << 8) | d.getbyte(o + 1); end
74
+
75
+ def s16(d, o)
76
+ v = u16(d, o)
77
+ v >= 32768 ? v - 65536 : v
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Font
5
+ module TrueType
6
+ class Kern
7
+ attr_reader :pairs
8
+
9
+ def initialize(data)
10
+ @pairs = {}
11
+ return unless data && data.bytesize >= 4
12
+
13
+ version = u16(data, 0)
14
+ n_tables = u16(data, 2)
15
+
16
+ offset = 4
17
+ n_tables.times do
18
+ break if offset + 6 > data.bytesize
19
+
20
+ u16(data, offset)
21
+ sub_length = u16(data, offset + 2)
22
+ coverage = u16(data, offset + 4)
23
+
24
+ if version.zero? && coverage & 0x0001 != 0
25
+ n_pairs = u16(data, offset + 6)
26
+ pair_offset = offset + 14
27
+
28
+ n_pairs.times do |_p|
29
+ break if pair_offset + 6 > data.bytesize
30
+
31
+ left = u16(data, pair_offset)
32
+ right = u16(data, pair_offset + 2)
33
+ value = s16(data, pair_offset + 4)
34
+ @pairs[[left, right]] = value
35
+ pair_offset += 6
36
+ end
37
+ end
38
+
39
+ offset += sub_length
40
+ end
41
+ end
42
+
43
+ def kerning(left_glyph, right_glyph)
44
+ @pairs[[left_glyph, right_glyph]] || 0
45
+ end
46
+
47
+ private
48
+
49
+ def u16(d, o); (d.getbyte(o) << 8) | d.getbyte(o + 1); end
50
+
51
+ def s16(d, o)
52
+ v = u16(d, o)
53
+ v >= 32768 ? v - 65536 : v
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Font
5
+ module TrueType
6
+ class Loca
7
+ attr_reader :offsets
8
+
9
+ def initialize(data, long_format: false, num_glyphs: 0)
10
+ @offsets = []
11
+ return unless data
12
+
13
+ count = num_glyphs + 1
14
+ count.times do |i|
15
+ @offsets << if long_format
16
+ u32(data, i * 4)
17
+ else
18
+ (u16(data, i * 2) * 2)
19
+ end
20
+ end
21
+ end
22
+
23
+ def glyph_offset(index)
24
+ return nil if index.nil? || index >= @offsets.length
25
+
26
+ @offsets[index]
27
+ end
28
+
29
+ def glyph_length(index)
30
+ return 0 if index.nil? || index + 1 >= @offsets.length
31
+
32
+ @offsets[index + 1] - @offsets[index]
33
+ end
34
+
35
+ private
36
+
37
+ def u16(data, off); (data.getbyte(off) << 8) | data.getbyte(off + 1); end
38
+ def u32(data, off); (data.getbyte(off) << 24) | (data.getbyte(off + 1) << 16) | (data.getbyte(off + 2) << 8) | data.getbyte(off + 3); end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Font
5
+ module TrueType
6
+ class Maxp
7
+ attr_reader :version, :num_glyphs, :max_points, :max_contours,
8
+ :max_composite_points, :max_composite_contours,
9
+ :max_zones, :max_twilight_points, :max_storage,
10
+ :max_function_defs, :max_instruction_defs,
11
+ :max_stack_elements, :max_size_of_instructions,
12
+ :max_component_elements, :max_component_depth
13
+
14
+ def initialize(data)
15
+ return unless data && data.bytesize >= 6
16
+
17
+ @version = data.bytes[0, 4].pack("C*").unpack1("N")
18
+ @num_glyphs = u16(data, 4)
19
+ return unless @version == 0x00010000 && data.bytesize >= 32
20
+
21
+ @max_points = u16(data, 6)
22
+ @max_contours = u16(data, 8)
23
+ @max_composite_points = u16(data, 10)
24
+ @max_composite_contours = u16(data, 12)
25
+ @max_zones = u16(data, 14)
26
+ @max_twilight_points = u16(data, 16)
27
+ @max_storage = u16(data, 18)
28
+ @max_function_defs = u16(data, 20)
29
+ @max_instruction_defs = u16(data, 22)
30
+ @max_stack_elements = u16(data, 24)
31
+ @max_size_of_instructions = u16(data, 26)
32
+ @max_component_elements = u16(data, 28)
33
+ @max_component_depth = u16(data, 30)
34
+ end
35
+
36
+ private
37
+
38
+ def u16(data, off)
39
+ (data.getbyte(off) << 8) | data.getbyte(off + 1)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Font
5
+ module TrueType
6
+ class Name
7
+ attr_reader :format, :count, :records
8
+
9
+ def initialize(data)
10
+ @records = {}
11
+ return unless data && data.bytesize >= 6
12
+
13
+ @format = u16(data, 0)
14
+ @count = u16(data, 2)
15
+ string_offset = u16(data, 4)
16
+
17
+ @count.times do |i|
18
+ offset = 6 + (i * 12)
19
+ break if offset + 12 > data.bytesize
20
+
21
+ platform_id = u16(data, offset)
22
+ u16(data, offset + 2)
23
+ _lang_id = u16(data, offset + 4)
24
+ name_id = u16(data, offset + 6)
25
+ length = u16(data, offset + 8)
26
+ str_offset = u16(data, offset + 10)
27
+
28
+ next unless [1, 3].include?(platform_id)
29
+
30
+ raw = data.byteslice(string_offset + str_offset, length)
31
+ next unless raw
32
+
33
+ str = if platform_id == 3
34
+ raw.bytes.each_slice(2).map { |b| (b[0] << 8) | (b[1] || 0) }.pack("U*")
35
+ else
36
+ raw.force_encoding("UTF-8")
37
+ end
38
+ @records[name_id] = str
39
+ end
40
+ end
41
+
42
+ def family; @records[1] || @records[16]; end
43
+ def subfamily; @records[2] || @records[17]; end
44
+ def ps_name; @records[6]; end
45
+ def full_name; @records[4]; end
46
+
47
+ private
48
+
49
+ def u16(data, off); (data.getbyte(off) << 8) | data.getbyte(off + 1); end
50
+ end
51
+ end
52
+ end
53
+ end