hexapdf 0.3.0 → 0.4.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/CONTRIBUTERS +1 -1
  4. data/README.md +35 -4
  5. data/Rakefile +1 -0
  6. data/VERSION +1 -1
  7. data/data/hexapdf/cmap/83pv-RKSJ-H +314 -0
  8. data/data/hexapdf/cmap/90ms-RKSJ-H +259 -0
  9. data/data/hexapdf/cmap/90ms-RKSJ-V +156 -0
  10. data/data/hexapdf/cmap/90msp-RKSJ-H +257 -0
  11. data/data/hexapdf/cmap/90msp-RKSJ-V +155 -0
  12. data/data/hexapdf/cmap/90pv-RKSJ-H +355 -0
  13. data/data/hexapdf/cmap/Add-RKSJ-H +738 -0
  14. data/data/hexapdf/cmap/Add-RKSJ-V +135 -0
  15. data/data/hexapdf/cmap/Adobe-CNS1-UCS2 +18209 -0
  16. data/data/hexapdf/cmap/Adobe-GB1-UCS2 +14267 -0
  17. data/data/hexapdf/cmap/Adobe-Japan1-UCS2 +19159 -0
  18. data/data/hexapdf/cmap/Adobe-Korea1-UCS2 +9267 -0
  19. data/data/hexapdf/cmap/B5pc-H +337 -0
  20. data/data/hexapdf/cmap/B5pc-V +90 -0
  21. data/data/hexapdf/cmap/CNS-EUC-H +490 -0
  22. data/data/hexapdf/cmap/CNS-EUC-V +538 -0
  23. data/data/hexapdf/cmap/ETen-B5-H +343 -0
  24. data/data/hexapdf/cmap/ETen-B5-V +91 -0
  25. data/data/hexapdf/cmap/ETenms-B5-H +79 -0
  26. data/data/hexapdf/cmap/ETenms-B5-V +99 -0
  27. data/data/hexapdf/cmap/EUC-H +207 -0
  28. data/data/hexapdf/cmap/EUC-V +105 -0
  29. data/data/hexapdf/cmap/Ext-RKSJ-H +768 -0
  30. data/data/hexapdf/cmap/Ext-RKSJ-V +117 -0
  31. data/data/hexapdf/cmap/GB-EUC-H +173 -0
  32. data/data/hexapdf/cmap/GB-EUC-V +98 -0
  33. data/data/hexapdf/cmap/GBK-EUC-H +4273 -0
  34. data/data/hexapdf/cmap/GBK-EUC-V +97 -0
  35. data/data/hexapdf/cmap/GBK2K-H +5325 -0
  36. data/data/hexapdf/cmap/GBK2K-V +118 -0
  37. data/data/hexapdf/cmap/GBKp-EUC-H +4272 -0
  38. data/data/hexapdf/cmap/GBKp-EUC-V +97 -0
  39. data/data/hexapdf/cmap/GBpc-EUC-H +175 -0
  40. data/data/hexapdf/cmap/GBpc-EUC-V +98 -0
  41. data/data/hexapdf/cmap/H +200 -0
  42. data/data/hexapdf/cmap/HKscs-B5-H +1331 -0
  43. data/data/hexapdf/cmap/HKscs-B5-V +90 -0
  44. data/data/hexapdf/cmap/Identity-H +339 -0
  45. data/data/hexapdf/cmap/Identity-V +73 -0
  46. data/data/hexapdf/cmap/KSC-EUC-H +562 -0
  47. data/data/hexapdf/cmap/KSC-EUC-V +94 -0
  48. data/data/hexapdf/cmap/KSCms-UHC-H +776 -0
  49. data/data/hexapdf/cmap/KSCms-UHC-HW-H +775 -0
  50. data/data/hexapdf/cmap/KSCms-UHC-HW-V +93 -0
  51. data/data/hexapdf/cmap/KSCms-UHC-V +94 -0
  52. data/data/hexapdf/cmap/KSCpc-EUC-H +608 -0
  53. data/data/hexapdf/cmap/LICENSE.txt +26 -0
  54. data/data/hexapdf/cmap/README.txt +9 -0
  55. data/data/hexapdf/cmap/UniCNS-UCS2-H +16992 -0
  56. data/data/hexapdf/cmap/UniCNS-UCS2-V +90 -0
  57. data/data/hexapdf/cmap/UniCNS-UTF16-H +19117 -0
  58. data/data/hexapdf/cmap/UniCNS-UTF16-V +94 -0
  59. data/data/hexapdf/cmap/UniGB-UCS2-H +14321 -0
  60. data/data/hexapdf/cmap/UniGB-UCS2-V +101 -0
  61. data/data/hexapdf/cmap/UniGB-UTF16-H +14381 -0
  62. data/data/hexapdf/cmap/UniGB-UTF16-V +104 -0
  63. data/data/hexapdf/cmap/UniJIS-UCS2-H +8870 -0
  64. data/data/hexapdf/cmap/UniJIS-UCS2-HW-H +81 -0
  65. data/data/hexapdf/cmap/UniJIS-UCS2-HW-V +279 -0
  66. data/data/hexapdf/cmap/UniJIS-UCS2-V +275 -0
  67. data/data/hexapdf/cmap/UniJIS-UTF16-H +14450 -0
  68. data/data/hexapdf/cmap/UniJIS-UTF16-V +299 -0
  69. data/data/hexapdf/cmap/UniKS-UCS2-H +8725 -0
  70. data/data/hexapdf/cmap/UniKS-UCS2-V +95 -0
  71. data/data/hexapdf/cmap/UniKS-UTF16-H +8895 -0
  72. data/data/hexapdf/cmap/UniKS-UTF16-V +99 -0
  73. data/data/hexapdf/cmap/V +105 -0
  74. data/examples/arc.rb +3 -3
  75. data/examples/merging.rb +4 -1
  76. data/examples/optimizing.rb +3 -0
  77. data/examples/show_char_bboxes.rb +2 -2
  78. data/examples/truetype.rb +2 -2
  79. data/lib/hexapdf/cli.rb +40 -1
  80. data/lib/hexapdf/cli/batch.rb +72 -0
  81. data/lib/hexapdf/cli/command.rb +112 -15
  82. data/lib/hexapdf/cli/files.rb +2 -2
  83. data/lib/hexapdf/cli/images.rb +14 -6
  84. data/lib/hexapdf/cli/info.rb +6 -8
  85. data/lib/hexapdf/cli/inspect.rb +5 -8
  86. data/lib/hexapdf/cli/merge.rb +13 -20
  87. data/lib/hexapdf/cli/modify.rb +4 -7
  88. data/lib/hexapdf/cli/optimize.rb +2 -5
  89. data/lib/hexapdf/configuration.rb +32 -3
  90. data/lib/hexapdf/content/canvas.rb +130 -37
  91. data/lib/hexapdf/content/parser.rb +40 -6
  92. data/lib/hexapdf/content/processor.rb +4 -4
  93. data/lib/hexapdf/document.rb +40 -10
  94. data/lib/hexapdf/document/fonts.rb +1 -0
  95. data/lib/hexapdf/encryption/security_handler.rb +8 -12
  96. data/lib/hexapdf/filter/flate_decode.rb +25 -2
  97. data/lib/hexapdf/font/cmap.rb +124 -8
  98. data/lib/hexapdf/font/cmap/parser.rb +65 -15
  99. data/lib/hexapdf/font/encoding/base.rb +2 -2
  100. data/lib/hexapdf/font/encoding/glyph_list.rb +2 -4
  101. data/lib/hexapdf/font/true_type.rb +1 -0
  102. data/lib/hexapdf/font/true_type/builder.rb +75 -0
  103. data/lib/hexapdf/font/true_type/optimizer.rb +65 -0
  104. data/lib/hexapdf/font/true_type/subsetter.rb +9 -22
  105. data/lib/hexapdf/font/true_type_wrapper.rb +9 -21
  106. data/lib/hexapdf/font_loader.rb +1 -1
  107. data/lib/hexapdf/importer.rb +1 -1
  108. data/lib/hexapdf/serializer.rb +5 -3
  109. data/lib/hexapdf/type.rb +2 -0
  110. data/lib/hexapdf/type/cid_font.rb +120 -0
  111. data/lib/hexapdf/type/font.rb +32 -12
  112. data/lib/hexapdf/type/font_simple.rb +34 -42
  113. data/lib/hexapdf/type/font_type0.rb +148 -0
  114. data/lib/hexapdf/type/form.rb +4 -4
  115. data/lib/hexapdf/type/page.rb +12 -11
  116. data/lib/hexapdf/type/resources.rb +14 -0
  117. data/lib/hexapdf/utils/graphics_helpers.rb +77 -0
  118. data/lib/hexapdf/version.rb +1 -1
  119. data/man/man1/hexapdf.1 +43 -1
  120. data/test/hexapdf/content/test_canvas.rb +76 -0
  121. data/test/hexapdf/content/test_parser.rb +20 -1
  122. data/test/hexapdf/content/test_processor.rb +11 -7
  123. data/test/hexapdf/document/test_fonts.rb +3 -1
  124. data/test/hexapdf/font/cmap/test_parser.rb +42 -7
  125. data/test/hexapdf/font/encoding/test_base.rb +1 -1
  126. data/test/hexapdf/font/encoding/test_glyph_list.rb +3 -3
  127. data/test/hexapdf/font/test_cmap.rb +104 -0
  128. data/test/hexapdf/font/test_true_type_wrapper.rb +63 -46
  129. data/test/hexapdf/font/true_type/test_builder.rb +37 -0
  130. data/test/hexapdf/font/true_type/test_optimizer.rb +27 -0
  131. data/test/hexapdf/font/true_type/test_subsetter.rb +6 -13
  132. data/test/hexapdf/test_configuration.rb +12 -7
  133. data/test/hexapdf/test_document.rb +24 -0
  134. data/test/hexapdf/test_importer.rb +9 -1
  135. data/test/hexapdf/test_writer.rb +2 -2
  136. data/test/hexapdf/type/test_cid_font.rb +61 -0
  137. data/test/hexapdf/type/test_font.rb +31 -4
  138. data/test/hexapdf/type/test_font_simple.rb +6 -21
  139. data/test/hexapdf/type/test_font_type0.rb +114 -0
  140. data/test/hexapdf/type/test_resources.rb +17 -1
  141. data/test/hexapdf/utils/test_graphics_helpers.rb +29 -0
  142. metadata +82 -3
@@ -61,8 +61,8 @@ module HexaPDF
61
61
  @code_to_name.fetch(code, :'.notdef')
62
62
  end
63
63
 
64
- # Returns the Unicode value in UTF-8 for the given code, or an empty string if the code
65
- # cannot be mapped.
64
+ # Returns the Unicode value in UTF-8 for the given code, or +nil+ if the code cannot be
65
+ # mapped.
66
66
  #
67
67
  # Note that this method caches the result of the Unicode mapping and therefore should only
68
68
  # be called after all codes have been defined.
@@ -71,8 +71,8 @@ module HexaPDF
71
71
  load
72
72
  end
73
73
 
74
- # Maps the given name to a string by following the Adobe Glyph Specification. An empty
75
- # string is returned if the name has no correct mapping.
74
+ # Maps the given name to a string by following the Adobe Glyph Specification. Returns +nil+
75
+ # if the name has no correct mapping.
76
76
  #
77
77
  # If this method is invoked when dealing with the ZapfDingbats font, the +zapf_dingbats+
78
78
  # option needs to be set to +true+.
@@ -88,8 +88,6 @@ module HexaPDF
88
88
  name = name.to_s
89
89
  if name =~ /\Auni([0-9A-F]{4})\Z/ || name =~ /\Au([0-9A-f]{4,6})\Z/
90
90
  '' << $1.hex
91
- else
92
- ''
93
91
  end
94
92
  end
95
93
  end
@@ -45,6 +45,7 @@ module HexaPDF
45
45
 
46
46
  autoload(:Font, 'hexapdf/font/true_type/font')
47
47
  autoload(:Subsetter, 'hexapdf/font/true_type/subsetter')
48
+ autoload(:Optimizer, 'hexapdf/font/true_type/optimizer')
48
49
 
49
50
  end
50
51
 
@@ -0,0 +1,75 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2017 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #++
33
+
34
+ module HexaPDF
35
+ module Font
36
+ module TrueType
37
+
38
+ # Builds a TrueType font file given a hash of TrueType tables.
39
+ module Builder
40
+
41
+ # Returns a TrueType font file representing the given TrueType tables (a hash mapping table
42
+ # names (strings) to table data).
43
+ def self.build(tables)
44
+ search_range = 2**(tables.length.bit_length - 1) * 16
45
+ entry_selector = tables.length.bit_length - 1
46
+ range_shift = tables.length * 16 - search_range
47
+
48
+ font_data = "\x0\x1\x0\x0".b + \
49
+ [tables.length, search_range, entry_selector, range_shift].pack('n4')
50
+
51
+ offset = font_data.length + tables.length * 16
52
+ checksum = Table.calculate_checksum(font_data)
53
+
54
+ # prepare head table for checksumming
55
+ tables['head'][8, 4] = "\0\0\0\0"
56
+
57
+ tables.each do |tag, data|
58
+ table_checksum = Table.calculate_checksum(data)
59
+ # tag, offset, data.length are all 32bit uint, table_checksum for header and body
60
+ checksum += tag.unpack('N').first + 2 * table_checksum + offset + data.length
61
+ font_data << [tag, table_checksum, offset, data.length].pack('a4N3')
62
+ offset += data.length
63
+ end
64
+
65
+ tables['head'][8, 4] = [0xB1B0AFBA - checksum].pack('N')
66
+ tables.each_value {|data| font_data << data}
67
+
68
+ font_data
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,65 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2017 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #++
33
+
34
+ require 'hexapdf/font/true_type/builder'
35
+
36
+ module HexaPDF
37
+ module Font
38
+ module TrueType
39
+
40
+ # Provides methods for optimizing a TrueType font file in various ways.
41
+ module Optimizer
42
+
43
+ # Returns for the given font a TrueType font file as binary string that is optimized for use
44
+ # in a PDF (i.e. only the essential tables are retained).
45
+ def self.build_for_pdf(font)
46
+ tables = {
47
+ 'head' => font[:head].raw_data,
48
+ 'hhea' => font[:hhea].raw_data,
49
+ 'maxp' => font[:maxp].raw_data,
50
+ 'glyf' => font[:glyf].raw_data,
51
+ 'loca' => font[:loca].raw_data,
52
+ 'hmtx' => font[:hmtx].raw_data,
53
+ }
54
+ tables['cmap'] = font[:cmap].raw_data if font[:cmap]
55
+ tables['cvt '] = font[:"cvt "].raw_data if font[:"cvt "]
56
+ tables['fpgm'] = font[:fpgm].raw_data if font[:fpgm]
57
+ tables['prep'] = font[:prep].raw_data if font[:prep]
58
+ Builder.build(tables)
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -31,6 +31,8 @@
31
31
  # is created or manipulated using HexaPDF.
32
32
  #++
33
33
 
34
+ require 'hexapdf/font/true_type/builder'
35
+
34
36
  module HexaPDF
35
37
  module Font
36
38
  module TrueType
@@ -61,6 +63,12 @@ module HexaPDF
61
63
  @glyph_map[glyph_id] = @last_id
62
64
  end
63
65
 
66
+ # Returns the new subset glyph ID for the given glyph ID, or +nil+ if the glyph isn't
67
+ # subset.
68
+ def subset_glyph_id(glyph_id)
69
+ @glyph_map[glyph_id]
70
+ end
71
+
64
72
  # Builds the subset font file and returns it as a binary string.
65
73
  def build_font
66
74
  glyf, locations = build_glyf_table
@@ -82,28 +90,7 @@ module HexaPDF
82
90
  tables['fpgm'] = @font[:fpgm].raw_data if @font[:fpgm]
83
91
  tables['prep'] = @font[:prep].raw_data if @font[:prep]
84
92
 
85
- search_range = 2**(tables.length.bit_length - 1) * 16
86
- entry_selector = tables.length.bit_length - 1
87
- range_shift = tables.length * 16 - search_range
88
-
89
- font_data = "\x0\x1\x0\x0".b + \
90
- [tables.length, search_range, entry_selector, range_shift].pack('n4')
91
-
92
- offset = font_data.length + tables.length * 16
93
- checksum = Table.calculate_checksum(font_data)
94
-
95
- tables.each do |tag, data|
96
- table_checksum = Table.calculate_checksum(data)
97
- # tag, offset, data.length are all 32bit uint, table_checksum for header and body
98
- checksum += tag.unpack('N').first + 2 * table_checksum + offset + data.length
99
- font_data << [tag, table_checksum, offset, data.length].pack('a4N3')
100
- offset += data.length
101
- end
102
-
103
- head[8, 4] = [0xB1B0AFBA - checksum].pack('N')
104
- tables.each_value {|data| font_data << data}
105
-
106
- font_data
93
+ Builder.build(tables)
107
94
  end
108
95
 
109
96
  private
@@ -247,34 +247,22 @@ module HexaPDF
247
247
 
248
248
  # Adds the /DW and /W fields to the CIDFont dictionary.
249
249
  def complete_width_information
250
- cid_font = @dict[:DescendantFonts].first
251
- cid_font[:DW] = default_width = glyph(3).width
252
-
253
- glyphs = @encoded_glyphs.keys.reject {|g| g.width == default_width}.sort_by(&:id)
254
- unless glyphs.empty?
255
- cid_font[:W] = widths = []
256
- last_id = -10
257
- cur_widths = nil
258
- glyphs.each do |glyph|
259
- gid = glyph.id
260
- if last_id + 1 != gid
261
- cur_widths = []
262
- widths << gid << cur_widths
263
- end
264
- cur_widths << glyph.width
265
- last_id = gid
266
- end
267
- end
250
+ default_width = glyph(3).width.to_i
251
+ widths = @encoded_glyphs.keys.reject {|g| g.width == default_width}.map! do |glyph|
252
+ [(@subsetter ? @subsetter.subset_glyph_id(glyph.id) : glyph.id), glyph.width]
253
+ end.sort!
254
+ @dict[:DescendantFonts].first.set_widths(widths, default_width: default_width)
268
255
  end
269
256
 
270
257
  # Creates the /ToUnicode CMap and updates the font dictionary so that text extraction works
271
258
  # correctly.
272
259
  def create_to_unicode_cmap
273
260
  stream = HexaPDF::StreamData.new do
274
- mapping = @encoded_glyphs.keys.sort_by(&:id).map do |glyph|
261
+ mapping = @encoded_glyphs.keys.map do |glyph|
275
262
  # Using 0xFFFD as mentioned in Adobe #5411, last line before section 1.5
276
- [glyph.id, @cmap.gid_to_code(glyph.id) || 0xFFFD]
277
- end
263
+ [(@subsetter ? @subsetter.subset_glyph_id(glyph.id) : glyph.id),
264
+ @cmap.gid_to_code(glyph.id) || 0xFFFD]
265
+ end.sort_by(&:first)
278
266
  HexaPDF::Font::CMap.create_to_unicode_cmap(mapping)
279
267
  end
280
268
  stream_obj = @document.add({}, stream: stream)
@@ -68,7 +68,7 @@ module HexaPDF
68
68
  #
69
69
  # decode_utf8(str)::
70
70
  # This method needs to convert the given string into an array of glyph objects. The glyph
71
- # objects themselves are treated as opaque objects by HexaPDF::Content::Canvas.
71
+ # objects themselves have to respond to \#width which should return their horizontal width.
72
72
  #
73
73
  # encode(glyph)::
74
74
  # This method takes a single glyph object, that needs to be compatible with the font wrapper,
@@ -116,7 +116,7 @@ module HexaPDF
116
116
  @mapper[object.data] = nil
117
117
  else
118
118
  obj = @mapper[object.data] = object.dup
119
- obj.document = @destination
119
+ obj.document = @destination.__getobj__
120
120
  obj.instance_variable_set(:@data, obj.data.dup)
121
121
  obj.data.oid = 0
122
122
  obj.data.gen = 0
@@ -231,10 +231,12 @@ module HexaPDF
231
231
  def serialize_string(obj)
232
232
  if @encrypter && @object.kind_of?(HexaPDF::Object) && @object.indirect?
233
233
  obj = encrypter.encrypt_string(obj, @object)
234
- elsif obj.encoding != Encoding::BINARY && obj =~ /[^ -~\t\r\n]/
235
- obj = "\xFE\xFF".b << obj.encode(Encoding::UTF_16BE).force_encoding(Encoding::BINARY)
236
234
  elsif obj.encoding != Encoding::BINARY
237
- obj = obj.b
235
+ obj = if obj =~ /[^ -~\t\r\n]/
236
+ "\xFE\xFF".b << obj.encode(Encoding::UTF_16BE).force_encoding(Encoding::BINARY)
237
+ else
238
+ obj.b
239
+ end
238
240
  end
239
241
  "(" << obj.gsub(/[\(\)\\\r]/n) {|m| STRING_ESCAPE_MAP[m]} << ")".freeze
240
242
  end
data/lib/hexapdf/type.rb CHANGED
@@ -61,6 +61,8 @@ module HexaPDF
61
61
  autoload(:FontSimple, 'hexapdf/type/font_simple')
62
62
  autoload(:FontType1, 'hexapdf/type/font_type1')
63
63
  autoload(:FontTrueType, 'hexapdf/type/font_true_type')
64
+ autoload(:FontType0, 'hexapdf/type/font_type0')
65
+ autoload(:CIDFont, 'hexapdf/type/cid_font')
64
66
 
65
67
  end
66
68
 
@@ -0,0 +1,120 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2017 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #++
33
+
34
+ require 'hexapdf/type/font'
35
+
36
+ module HexaPDF
37
+ module Type
38
+
39
+ # Represents a generic CIDFont which can only be used as a descendant font of a composite PDF
40
+ # font.
41
+ #
42
+ # See: PDF1.7 s9.7.4
43
+ class CIDFont < Font
44
+
45
+ DEFAULT_WIDTH = 1000 # :nodoc:
46
+
47
+ define_field :CIDSystemInfo, type: [Hash, Dictionary], required: true
48
+ define_field :FontDescriptor, type: :FontDescriptor, indirect: true, required: true
49
+ define_field :DW, type: Integer, default: DEFAULT_WIDTH
50
+ define_field :W, type: Array
51
+ define_field :DW2, type: Array, default: [880, -1100]
52
+ define_field :W2, type: Array
53
+
54
+
55
+ # Returns the unscaled width of the given CID in glyph units, or 0 if the width for the CID is
56
+ # missing.
57
+ #
58
+ # Note that in contrast to other fonts, the argument must *not* be a code point but a CID!
59
+ def width(cid)
60
+ widths[cid] || value[:DW] || DEFAULT_WIDTH
61
+ end
62
+
63
+ # Sets the /W and /DW keys using the given array of [CID, width] pairs and an optional default
64
+ # width.
65
+ #
66
+ # See: PDF1.7 s9.7.4.3
67
+ def set_widths(widths, default_width: DEFAULT_WIDTH)
68
+ if widths.empty?
69
+ (default_width == DEFAULT_WIDTH ? delete(:DW) : self[:DW] = default_width)
70
+ delete(:W)
71
+ else
72
+ self[:DW] = default_width.to_i unless default_width == DEFAULT_WIDTH
73
+ self[:W] = w = []
74
+ last_cid = -10
75
+ cur_widths = nil
76
+ widths.each do |cid, width|
77
+ if last_cid + 1 != cid
78
+ cur_widths = []
79
+ w << cid << cur_widths
80
+ end
81
+ cur_widths << width.to_i
82
+ last_cid = cid
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ # Returns a hash mapping CIDs to their respective width.
90
+ #
91
+ # Note that the hash is cached internally when accessed the first time.
92
+ #
93
+ # See: PDF1.7 s9.7.4.3
94
+ def widths
95
+ document.cache(@data, :widths) do
96
+ result = {}
97
+ index = 0
98
+ array = self[:W] || []
99
+
100
+ while index < array.size
101
+ entry = array[index]
102
+ value = array[index + 1]
103
+ if value.kind_of?(Array)
104
+ value.each_with_index {|width, i| result[entry + i] = width}
105
+ index += 2
106
+ else
107
+ width = array[index + 2]
108
+ entry.upto(value) {|cid| result[cid] = width}
109
+ index += 3
110
+ end
111
+ end
112
+
113
+ result
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+ end