hexapdf 0.11.9 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/LICENSE +1 -1
  4. data/examples/001-hello_world.rb +1 -1
  5. data/examples/002-graphics.rb +1 -1
  6. data/examples/003-arcs.rb +1 -1
  7. data/examples/004-optimizing.rb +1 -1
  8. data/examples/005-merging.rb +1 -1
  9. data/examples/006-standard_pdf_fonts.rb +1 -1
  10. data/examples/007-truetype.rb +1 -1
  11. data/examples/008-show_char_bboxes.rb +1 -1
  12. data/examples/009-text_layouter_alignment.rb +1 -1
  13. data/examples/010-text_layouter_inline_boxes.rb +1 -1
  14. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  15. data/examples/012-text_layouter_styling.rb +1 -1
  16. data/examples/013-text_layouter_shapes.rb +1 -1
  17. data/examples/014-text_in_polygon.rb +1 -1
  18. data/examples/015-boxes.rb +1 -1
  19. data/examples/016-frame_automatic_box_placement.rb +1 -1
  20. data/examples/017-frame_text_flow.rb +1 -1
  21. data/examples/018-composer.rb +1 -1
  22. data/examples/019-acro_form.rb +51 -0
  23. data/lib/hexapdf.rb +1 -1
  24. data/lib/hexapdf/cli.rb +3 -1
  25. data/lib/hexapdf/cli/batch.rb +1 -1
  26. data/lib/hexapdf/cli/command.rb +18 -9
  27. data/lib/hexapdf/cli/files.rb +1 -1
  28. data/lib/hexapdf/cli/form.rb +240 -0
  29. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  30. data/lib/hexapdf/cli/images.rb +1 -1
  31. data/lib/hexapdf/cli/info.rb +1 -1
  32. data/lib/hexapdf/cli/inspect.rb +1 -1
  33. data/lib/hexapdf/cli/merge.rb +1 -1
  34. data/lib/hexapdf/cli/modify.rb +1 -1
  35. data/lib/hexapdf/cli/optimize.rb +1 -1
  36. data/lib/hexapdf/cli/split.rb +1 -1
  37. data/lib/hexapdf/cli/watermark.rb +1 -1
  38. data/lib/hexapdf/composer.rb +2 -2
  39. data/lib/hexapdf/configuration.rb +66 -11
  40. data/lib/hexapdf/content.rb +3 -1
  41. data/lib/hexapdf/content/canvas.rb +5 -18
  42. data/lib/hexapdf/content/color_space.rb +111 -32
  43. data/lib/hexapdf/content/graphic_object.rb +1 -1
  44. data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
  45. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  46. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  47. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  48. data/lib/hexapdf/content/graphics_state.rb +1 -1
  49. data/lib/hexapdf/content/operator.rb +9 -9
  50. data/lib/hexapdf/content/parser.rb +18 -5
  51. data/lib/hexapdf/content/processor.rb +1 -1
  52. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  53. data/lib/hexapdf/data_dir.rb +1 -1
  54. data/lib/hexapdf/dictionary.rb +1 -1
  55. data/lib/hexapdf/dictionary_fields.rb +1 -1
  56. data/lib/hexapdf/document.rb +14 -5
  57. data/lib/hexapdf/document/files.rb +1 -1
  58. data/lib/hexapdf/document/fonts.rb +1 -1
  59. data/lib/hexapdf/document/images.rb +1 -1
  60. data/lib/hexapdf/document/pages.rb +3 -14
  61. data/lib/hexapdf/encryption.rb +1 -1
  62. data/lib/hexapdf/encryption/aes.rb +1 -1
  63. data/lib/hexapdf/encryption/arc4.rb +1 -1
  64. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  65. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  66. data/lib/hexapdf/encryption/identity.rb +1 -1
  67. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  68. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  69. data/lib/hexapdf/encryption/security_handler.rb +1 -1
  70. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
  71. data/lib/hexapdf/error.rb +1 -1
  72. data/lib/hexapdf/filter.rb +3 -3
  73. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  74. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  75. data/lib/hexapdf/filter/encryption.rb +1 -1
  76. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  77. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  78. data/lib/hexapdf/filter/{jpx_decode.rb → pass_through.rb} +5 -5
  79. data/lib/hexapdf/filter/predictor.rb +1 -1
  80. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  81. data/lib/hexapdf/font/cmap.rb +1 -1
  82. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  83. data/lib/hexapdf/font/cmap/writer.rb +1 -1
  84. data/lib/hexapdf/font/encoding.rb +1 -1
  85. data/lib/hexapdf/font/encoding/base.rb +1 -1
  86. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  87. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  88. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  89. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  90. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  91. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  92. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  93. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  94. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  95. data/lib/hexapdf/font/true_type.rb +1 -1
  96. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  97. data/lib/hexapdf/font/true_type/font.rb +1 -1
  98. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  99. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  100. data/lib/hexapdf/font/true_type/table.rb +1 -1
  101. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  102. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  103. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  104. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  105. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  106. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  107. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  108. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  109. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  114. data/lib/hexapdf/font/true_type_wrapper.rb +54 -51
  115. data/lib/hexapdf/font/type1.rb +1 -1
  116. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  117. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  118. data/lib/hexapdf/font/type1/font.rb +1 -1
  119. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  120. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  121. data/lib/hexapdf/font/type1_wrapper.rb +67 -51
  122. data/lib/hexapdf/font_loader.rb +1 -1
  123. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  124. data/lib/hexapdf/font_loader/from_file.rb +1 -1
  125. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  126. data/lib/hexapdf/image_loader.rb +1 -1
  127. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  128. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  129. data/lib/hexapdf/image_loader/png.rb +1 -1
  130. data/lib/hexapdf/importer.rb +2 -4
  131. data/lib/hexapdf/layout.rb +1 -1
  132. data/lib/hexapdf/layout/box.rb +1 -1
  133. data/lib/hexapdf/layout/frame.rb +1 -1
  134. data/lib/hexapdf/layout/image_box.rb +1 -1
  135. data/lib/hexapdf/layout/inline_box.rb +1 -1
  136. data/lib/hexapdf/layout/line.rb +1 -1
  137. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  138. data/lib/hexapdf/layout/style.rb +1 -1
  139. data/lib/hexapdf/layout/text_box.rb +1 -1
  140. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  141. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  142. data/lib/hexapdf/layout/text_shaper.rb +1 -1
  143. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  144. data/lib/hexapdf/name_tree_node.rb +1 -1
  145. data/lib/hexapdf/number_tree_node.rb +1 -1
  146. data/lib/hexapdf/object.rb +2 -2
  147. data/lib/hexapdf/parser.rb +4 -3
  148. data/lib/hexapdf/pdf_array.rb +1 -1
  149. data/lib/hexapdf/rectangle.rb +31 -1
  150. data/lib/hexapdf/reference.rb +1 -1
  151. data/lib/hexapdf/revision.rb +2 -1
  152. data/lib/hexapdf/revisions.rb +1 -1
  153. data/lib/hexapdf/serializer.rb +1 -1
  154. data/lib/hexapdf/stream.rb +1 -1
  155. data/lib/hexapdf/task.rb +1 -1
  156. data/lib/hexapdf/task/dereference.rb +1 -1
  157. data/lib/hexapdf/task/optimize.rb +1 -1
  158. data/lib/hexapdf/tokenizer.rb +1 -1
  159. data/lib/hexapdf/type.rb +1 -1
  160. data/lib/hexapdf/type/acro_form.rb +7 -1
  161. data/lib/hexapdf/type/acro_form/appearance_generator.rb +401 -0
  162. data/lib/hexapdf/type/acro_form/button_field.rb +300 -0
  163. data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
  164. data/lib/hexapdf/type/acro_form/field.rb +220 -17
  165. data/lib/hexapdf/type/acro_form/form.rb +157 -7
  166. data/lib/hexapdf/type/acro_form/text_field.rb +186 -0
  167. data/lib/hexapdf/type/acro_form/variable_text_field.rb +122 -0
  168. data/lib/hexapdf/type/action.rb +1 -1
  169. data/lib/hexapdf/type/actions.rb +1 -1
  170. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  171. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  172. data/lib/hexapdf/type/actions/launch.rb +1 -1
  173. data/lib/hexapdf/type/actions/uri.rb +1 -1
  174. data/lib/hexapdf/type/annotation.rb +73 -3
  175. data/lib/hexapdf/type/annotations.rb +1 -1
  176. data/lib/hexapdf/type/annotations/link.rb +2 -2
  177. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  178. data/lib/hexapdf/type/annotations/text.rb +1 -1
  179. data/lib/hexapdf/type/annotations/widget.rb +239 -2
  180. data/lib/hexapdf/type/catalog.rb +21 -1
  181. data/lib/hexapdf/type/cid_font.rb +1 -1
  182. data/lib/hexapdf/type/embedded_file.rb +1 -1
  183. data/lib/hexapdf/type/file_specification.rb +1 -1
  184. data/lib/hexapdf/type/font.rb +18 -1
  185. data/lib/hexapdf/type/font_descriptor.rb +2 -2
  186. data/lib/hexapdf/type/font_simple.rb +1 -1
  187. data/lib/hexapdf/type/font_true_type.rb +1 -1
  188. data/lib/hexapdf/type/font_type0.rb +1 -1
  189. data/lib/hexapdf/type/font_type1.rb +16 -1
  190. data/lib/hexapdf/type/font_type3.rb +1 -1
  191. data/lib/hexapdf/type/form.rb +1 -1
  192. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  193. data/lib/hexapdf/type/icon_fit.rb +1 -1
  194. data/lib/hexapdf/type/image.rb +3 -1
  195. data/lib/hexapdf/type/info.rb +1 -1
  196. data/lib/hexapdf/type/names.rb +1 -1
  197. data/lib/hexapdf/type/object_stream.rb +1 -1
  198. data/lib/hexapdf/type/page.rb +1 -1
  199. data/lib/hexapdf/type/page_tree_node.rb +8 -11
  200. data/lib/hexapdf/type/resources.rb +16 -3
  201. data/lib/hexapdf/type/trailer.rb +2 -3
  202. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  203. data/lib/hexapdf/type/xref_stream.rb +1 -1
  204. data/lib/hexapdf/utils/bit_field.rb +38 -24
  205. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  206. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  207. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  208. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  209. data/lib/hexapdf/utils/object_hash.rb +1 -1
  210. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  211. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  212. data/lib/hexapdf/version.rb +2 -2
  213. data/lib/hexapdf/writer.rb +1 -1
  214. data/lib/hexapdf/xref_section.rb +1 -1
  215. data/test/hexapdf/content/common.rb +2 -2
  216. data/test/hexapdf/content/test_color_space.rb +71 -8
  217. data/test/hexapdf/content/test_operator.rb +22 -22
  218. data/test/hexapdf/content/test_parser.rb +14 -0
  219. data/test/hexapdf/document/test_fonts.rb +1 -1
  220. data/test/hexapdf/document/test_pages.rb +6 -6
  221. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  222. data/test/hexapdf/font/test_type1_wrapper.rb +32 -8
  223. data/test/hexapdf/test_document.rb +12 -0
  224. data/test/hexapdf/test_parser.rb +10 -0
  225. data/test/hexapdf/test_rectangle.rb +14 -0
  226. data/test/hexapdf/test_revision.rb +3 -0
  227. data/test/hexapdf/test_writer.rb +2 -2
  228. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +515 -0
  229. data/test/hexapdf/type/acro_form/test_button_field.rb +276 -0
  230. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  231. data/test/hexapdf/type/acro_form/test_field.rb +124 -6
  232. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  233. data/test/hexapdf/type/acro_form/test_text_field.rb +119 -0
  234. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  235. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  236. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  237. data/test/hexapdf/type/test_annotation.rb +45 -0
  238. data/test/hexapdf/type/test_catalog.rb +18 -0
  239. data/test/hexapdf/type/test_font.rb +5 -0
  240. data/test/hexapdf/type/test_font_type1.rb +8 -0
  241. data/test/hexapdf/type/test_image.rb +7 -0
  242. data/test/hexapdf/type/test_page_tree_node.rb +20 -12
  243. data/test/hexapdf/type/test_resources.rb +20 -0
  244. data/test/hexapdf/type/test_trailer.rb +4 -0
  245. data/test/hexapdf/utils/test_bit_field.rb +13 -1
  246. data/test/test_helper.rb +1 -1
  247. metadata +37 -18
  248. data/lib/hexapdf/filter/dct_decode.rb +0 -60
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -115,15 +115,19 @@ module HexaPDF
115
115
  # Returns the wrapped TrueType font object.
116
116
  attr_reader :wrapped_font
117
117
 
118
- # Returns the PDF font dictionary representing the wrapped font.
119
- attr_reader :dict
118
+ # Returns the PDF object associated with the wrapper.
119
+ attr_reader :pdf_object
120
120
 
121
121
  # Creates a new object wrapping the TrueType font for the PDF document.
122
122
  #
123
+ # The optional argument +pdf_object+ can be used to set the PDF font object that this wrapper
124
+ # should be associated with. If no object is set, a suitable one is automatically created.
125
+ #
123
126
  # If +subset+ is true, the font is subset.
124
- def initialize(document, font, subset: true)
125
- @document = document
127
+ def initialize(document, font, pdf_object: nil, subset: true)
126
128
  @wrapped_font = font
129
+ @missing_glyph_callable = document.config['font.on_missing_glyph']
130
+
127
131
  @subsetter = (subset ? HexaPDF::Font::TrueType::Subsetter.new(font) : nil)
128
132
 
129
133
  @cmap = font[:cmap].preferred_table
@@ -131,8 +135,7 @@ module HexaPDF
131
135
  raise HexaPDF::Error, "No mapping table for Unicode characters found for TTF " \
132
136
  "font #{font.full_name}"
133
137
  end
134
- @dict = build_font_dict
135
- @document.register_listener(:complete_objects, &method(:complete_font_dict))
138
+ @pdf_object = pdf_object || create_pdf_object(document)
136
139
 
137
140
  @id_to_glyph = {}
138
141
  @codepoint_to_glyph = {}
@@ -166,7 +169,7 @@ module HexaPDF
166
169
  if id >= 0 && id < @wrapped_font[:maxp].num_glyphs
167
170
  Glyph.new(@wrapped_font, id, str || (+'' << (@cmap.gid_to_code(id) || 0xFFFD)))
168
171
  else
169
- @document.config['font.on_missing_glyph'].call("\u{FFFD}", font_type, @wrapped_font)
172
+ @missing_glyph_callable.call("\u{FFFD}", font_type, @wrapped_font)
170
173
  end
171
174
  end
172
175
  end
@@ -179,7 +182,7 @@ module HexaPDF
179
182
  if (gid = @cmap[c])
180
183
  glyph(gid, +'' << c)
181
184
  else
182
- @document.config['font.on_missing_glyph'].call(+'' << c, font_type, @wrapped_font)
185
+ @missing_glyph_callable.call(+'' << c, font_type, @wrapped_font)
183
186
  end
184
187
  end
185
188
  end
@@ -202,22 +205,21 @@ module HexaPDF
202
205
 
203
206
  private
204
207
 
205
- # Builds a Type0 font object representing the TrueType font.
206
- #
207
- # The returned font object contains only information available at build time, so no
208
- # information about glyph specific attributes like width.
208
+ # Creates a Type0 font object representing the TrueType font.
209
209
  #
210
- # See: #complete_font_dict
211
- def build_font_dict
212
- fd = @document.add({Type: :FontDescriptor,
213
- FontName: @wrapped_font.font_name.intern,
214
- FontWeight: @wrapped_font.weight,
215
- Flags: 0,
216
- FontBBox: @wrapped_font.bounding_box.map {|m| m * scaling_factor },
217
- ItalicAngle: @wrapped_font.italic_angle || 0,
218
- Ascent: @wrapped_font.ascender * scaling_factor,
219
- Descent: @wrapped_font.descender * scaling_factor,
220
- StemV: @wrapped_font.dominant_vertical_stem_width})
210
+ # The returned font object contains only information available at creation time, so no
211
+ # information about glyph specific attributes like width. The missing information is added
212
+ # before the PDF document gets written.
213
+ def create_pdf_object(document)
214
+ fd = document.add({Type: :FontDescriptor,
215
+ FontName: @wrapped_font.font_name.intern,
216
+ FontWeight: @wrapped_font.weight,
217
+ Flags: 0,
218
+ FontBBox: @wrapped_font.bounding_box.map {|m| m * scaling_factor },
219
+ ItalicAngle: @wrapped_font.italic_angle || 0,
220
+ Ascent: @wrapped_font.ascender * scaling_factor,
221
+ Descent: @wrapped_font.descender * scaling_factor,
222
+ StemV: @wrapped_font.dominant_vertical_stem_width})
221
223
  if @wrapped_font[:'OS/2'].version >= 2
222
224
  fd[:CapHeight] = @wrapped_font.cap_height * scaling_factor
223
225
  fd[:XHeight] = @wrapped_font.x_height * scaling_factor
@@ -242,28 +244,29 @@ module HexaPDF
242
244
  @wrapped_font[:'OS/2'].selection_include?(:oblique)
243
245
  fd.flag(:symbolic)
244
246
 
245
- cid_font = @document.add({Type: :Font, Subtype: :CIDFontType2,
246
- BaseFont: fd[:FontName], FontDescriptor: fd,
247
- CIDSystemInfo: {Registry: "Adobe", Ordering: "Identity",
248
- Supplement: 0},
249
- CIDToGIDMap: :Identity})
250
- @document.add({Type: :Font, Subtype: :Type0, BaseFont: cid_font[:BaseFont],
251
- Encoding: :"Identity-H", DescendantFonts: [cid_font]})
252
- end
247
+ cid_font = document.add({Type: :Font, Subtype: :CIDFontType2,
248
+ BaseFont: fd[:FontName], FontDescriptor: fd,
249
+ CIDSystemInfo: {Registry: "Adobe", Ordering: "Identity",
250
+ Supplement: 0},
251
+ CIDToGIDMap: :Identity})
252
+ dict = document.add({Type: :Font, Subtype: :Type0, BaseFont: cid_font[:BaseFont],
253
+ Encoding: :"Identity-H", DescendantFonts: [cid_font]})
254
+ dict.font_wrapper = self
255
+
256
+ document.register_listener(:complete_objects) do
257
+ update_font_name(dict)
258
+ embed_font(dict, document)
259
+ complete_width_information(dict)
260
+ create_to_unicode_cmap(dict, document)
261
+ end
253
262
 
254
- # Makes sure that the Type0 font object as well as the CIDFont object contain all the needed
255
- # information.
256
- def complete_font_dict
257
- update_font_name
258
- embed_font
259
- complete_width_information
260
- create_to_unicode_cmap
263
+ dict
261
264
  end
262
265
 
263
266
  UPPERCASE_LETTERS = ('A'..'Z').to_a.freeze #:nodoc:
264
267
 
265
268
  # Updates the font name with a unique tag if the font is subset.
266
- def update_font_name
269
+ def update_font_name(dict)
267
270
  return unless @subsetter
268
271
 
269
272
  tag = +''
@@ -275,13 +278,13 @@ module HexaPDF
275
278
  end
276
279
 
277
280
  name = (tag << "+" << @wrapped_font.font_name).intern
278
- @dict[:BaseFont] = name
279
- @dict[:DescendantFonts].first[:BaseFont] = name
280
- @dict[:DescendantFonts].first[:FontDescriptor][:FontName] = name
281
+ dict[:BaseFont] = name
282
+ dict[:DescendantFonts].first[:BaseFont] = name
283
+ dict[:DescendantFonts].first[:FontDescriptor][:FontName] = name
281
284
  end
282
285
 
283
286
  # Embeds the font.
284
- def embed_font
287
+ def embed_font(dict, document)
285
288
  if @subsetter
286
289
  data = @subsetter.build_font
287
290
  length = data.size
@@ -290,22 +293,22 @@ module HexaPDF
290
293
  length = @wrapped_font.io.size
291
294
  stream = HexaPDF::StreamData.new(@wrapped_font.io, length: length)
292
295
  end
293
- font = @document.add({Length1: length, Filter: :FlateDecode}, stream: stream)
294
- @dict[:DescendantFonts].first[:FontDescriptor][:FontFile2] = font
296
+ font = document.add({Length1: length, Filter: :FlateDecode}, stream: stream)
297
+ dict[:DescendantFonts].first[:FontDescriptor][:FontFile2] = font
295
298
  end
296
299
 
297
300
  # Adds the /DW and /W fields to the CIDFont dictionary.
298
- def complete_width_information
301
+ def complete_width_information(dict)
299
302
  default_width = glyph(3, " ").width.to_i
300
303
  widths = @encoded_glyphs.reject {|_, v| v[1].width == default_width }.map do |id, v|
301
304
  [(@subsetter ? @subsetter.subset_glyph_id(id) : id), v[1].width]
302
305
  end.sort!
303
- @dict[:DescendantFonts].first.set_widths(widths, default_width: default_width)
306
+ dict[:DescendantFonts].first.set_widths(widths, default_width: default_width)
304
307
  end
305
308
 
306
309
  # Creates the /ToUnicode CMap and updates the font dictionary so that text extraction works
307
310
  # correctly.
308
- def create_to_unicode_cmap
311
+ def create_to_unicode_cmap(dict, document)
309
312
  stream = HexaPDF::StreamData.new do
310
313
  mapping = @encoded_glyphs.keys.map! do |id|
311
314
  # Using 0xFFFD as mentioned in Adobe #5411, last line before section 1.5
@@ -313,9 +316,9 @@ module HexaPDF
313
316
  end.sort_by!(&:first)
314
317
  HexaPDF::Font::CMap.create_to_unicode_cmap(mapping)
315
318
  end
316
- stream_obj = @document.add({}, stream: stream)
319
+ stream_obj = document.add({}, stream: stream)
317
320
  stream_obj.set_filter(:FlateDecode)
318
- @dict[:ToUnicode] = stream_obj
321
+ dict[:ToUnicode] = stream_obj
319
322
  end
320
323
 
321
324
  end
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -104,23 +104,33 @@ module HexaPDF
104
104
  # Returns the wrapped Type1 font object.
105
105
  attr_reader :wrapped_font
106
106
 
107
- # The PDF font dictionary representing the wrapped font.
108
- attr_reader :dict
107
+ # Returns the PDF object associated with the wrapper.
108
+ attr_reader :pdf_object
109
109
 
110
- # Creates a new object wrapping the Type1 font for the PDF document.
110
+ # Creates a new Type1Wrapper object wrapping the Type1 font.
111
111
  #
112
- # The optional argument +custom_encoding+ can be set to +true+ so that a custom encoding
113
- # instead of the WinAnsiEncoding is used.
114
- def initialize(document, font, custom_encoding: false)
115
- @document = document
112
+ # The optional argument +pdf_object+ can be used to set the PDF font object that this wrapper
113
+ # should be associated with. If no object is set, a suitable one is automatically created.
114
+ #
115
+ # If +pdf_object+ is provided, the PDF object's encoding is used. Otherwise, the
116
+ # WinAnsiEncoding or, for 'Special' fonts, the font's internal encoding is used. The optional
117
+ # argument +custom_encoding+ can be set to +true+ so that a custom encoding is used (only
118
+ # respected if +pdf_object+ is not provided).
119
+ def initialize(document, font, pdf_object: nil, custom_encoding: false)
116
120
  @wrapped_font = font
121
+ @pdf_object = pdf_object || create_pdf_object(document)
122
+ @missing_glyph_callable = document.config['font.on_missing_glyph']
117
123
 
118
- @dict = build_font_dict
119
- @document.register_listener(:complete_objects, &method(:complete_font_dict))
120
- if @wrapped_font.metrics.character_set == 'Special' || custom_encoding
124
+ if pdf_object
125
+ @encoding = pdf_object.encoding
126
+ @max_code = 255 # Encoding is not modified
127
+ elsif custom_encoding
121
128
  @encoding = Encoding::Base.new
122
129
  @encoding.code_to_name[32] = :space
123
130
  @max_code = 32 # 32 = space
131
+ elsif @wrapped_font.metrics.character_set == 'Special'
132
+ @encoding = @wrapped_font.encoding
133
+ @max_code = 255 # Encoding is not modified
124
134
  else
125
135
  @encoding = Encoding.for_name(:WinAnsiEncoding)
126
136
  @max_code = 255 # Encoding is not modified
@@ -150,17 +160,25 @@ module HexaPDF
150
160
  if @wrapped_font.metrics.character_metrics.key?(name)
151
161
  Glyph.new(@wrapped_font, name, str)
152
162
  else
153
- @document.config['font.on_missing_glyph'].call(str, font_type, @wrapped_font)
163
+ @missing_glyph_callable.call(str, font_type, @wrapped_font)
154
164
  end
155
165
  end
156
166
  end
157
167
 
158
168
  # Returns an array of glyph objects representing the characters in the UTF-8 encoded string.
169
+ #
170
+ # If a Unicode codepoint is not available as glyph object, it is tried to map the codepoint
171
+ # using the font's internal encoding. This is useful, for example, for the ZapfDingbats font
172
+ # to use ASCII characters for accessing the glyphs.
159
173
  def decode_utf8(str)
160
174
  str.codepoints.map! do |c|
161
175
  @codepoint_to_glyph[c] ||=
162
176
  begin
163
177
  name = Encoding::GlyphList.unicode_to_name(+'' << c, **@zapf_dingbats_opt)
178
+ if @wrapped_font.metrics.character_set == 'Special' &&
179
+ (name == :'.notdef' || !@wrapped_font.metrics.character_metrics.key?(name))
180
+ name = @encoding.name(c)
181
+ end
164
182
  name = +"u" << c.to_s(16).rjust(6, '0') if name == :'.notdef'
165
183
  glyph(name)
166
184
  end
@@ -189,49 +207,47 @@ module HexaPDF
189
207
 
190
208
  private
191
209
 
192
- # Builds a generic Type1 font dictionary for the wrapped font.
193
- #
194
- # Generic in the sense that no information regarding the encoding or widths is included.
195
- def build_font_dict
196
- unless defined?(@fd)
197
- @fd = @document.wrap({Type: :FontDescriptor,
198
- FontName: @wrapped_font.font_name.intern,
199
- FontWeight: @wrapped_font.weight_class,
200
- FontBBox: @wrapped_font.bounding_box,
201
- ItalicAngle: @wrapped_font.italic_angle || 0,
202
- Ascent: @wrapped_font.ascender || 0,
203
- Descent: @wrapped_font.descender || 0,
204
- CapHeight: @wrapped_font.cap_height,
205
- XHeight: @wrapped_font.x_height,
206
- StemH: @wrapped_font.dominant_horizontal_stem_width,
207
- StemV: @wrapped_font.dominant_vertical_stem_width || 0})
208
- @fd.flag(:fixed_pitch) if @wrapped_font.metrics.is_fixed_pitch
209
- @fd.flag(@wrapped_font.metrics.character_set == 'Special' ? :symbolic : :nonsymbolic)
210
- @fd.must_be_indirect = true
211
- end
212
-
213
- @document.wrap({Type: :Font, Subtype: :Type1,
214
- BaseFont: @wrapped_font.font_name.intern, Encoding: :WinAnsiEncoding,
215
- FontDescriptor: @fd})
216
- end
217
-
218
210
  # Array of valid encoding names in PDF
219
211
  VALID_ENCODING_NAMES = [:WinAnsiEncoding, :MacRomanEncoding, :MacExpertEncoding].freeze
220
212
 
221
- # Completes the font dictionary by filling in the values that depend on the used encoding.
222
- def complete_font_dict
223
- min, max = @encoding.code_to_name.keys.minmax
224
- @dict[:FirstChar] = min
225
- @dict[:LastChar] = max
226
- @dict[:Widths] = (min..max).map {|code| glyph(@encoding.name(code)).width }
227
-
228
- if VALID_ENCODING_NAMES.include?(@encoding.encoding_name)
229
- @dict[:Encoding] = @encoding.encoding_name
230
- else
231
- differences = [min]
232
- (min..max).each {|code| differences << @encoding.name(code) }
233
- @dict[:Encoding] = {Differences: differences}
213
+ # Creates a PDF object representing the wrapped font for the given PDF document.
214
+ def create_pdf_object(document)
215
+ fd = document.wrap({Type: :FontDescriptor,
216
+ FontName: @wrapped_font.font_name.intern,
217
+ FontWeight: @wrapped_font.weight_class,
218
+ FontBBox: @wrapped_font.bounding_box,
219
+ ItalicAngle: @wrapped_font.italic_angle || 0,
220
+ Ascent: @wrapped_font.ascender || 0,
221
+ Descent: @wrapped_font.descender || 0,
222
+ CapHeight: @wrapped_font.cap_height,
223
+ XHeight: @wrapped_font.x_height,
224
+ StemH: @wrapped_font.dominant_horizontal_stem_width,
225
+ StemV: @wrapped_font.dominant_vertical_stem_width || 0})
226
+ fd.flag(:fixed_pitch) if @wrapped_font.metrics.is_fixed_pitch
227
+ fd.flag(@wrapped_font.metrics.character_set == 'Special' ? :symbolic : :nonsymbolic)
228
+ fd.must_be_indirect = true
229
+
230
+ dict = document.wrap({Type: :Font, Subtype: :Type1,
231
+ BaseFont: @wrapped_font.font_name.intern,
232
+ FontDescriptor: fd})
233
+ dict.font_wrapper = self
234
+
235
+ document.register_listener(:complete_objects) do
236
+ min, max = @encoding.code_to_name.keys.minmax
237
+ dict[:FirstChar] = min
238
+ dict[:LastChar] = max
239
+ dict[:Widths] = (min..max).map {|code| glyph(@encoding.name(code)).width }
240
+
241
+ if VALID_ENCODING_NAMES.include?(@encoding.encoding_name)
242
+ dict[:Encoding] = @encoding.encoding_name
243
+ elsif @encoding != @wrapped_font.encoding
244
+ differences = [min]
245
+ (min..max).each {|code| differences << @encoding.name(code) }
246
+ dict[:Encoding] = {Differences: differences}
247
+ end
234
248
  end
249
+
250
+ dict
235
251
  end
236
252
 
237
253
  end
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as