hexapdf 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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