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
@@ -73,58 +73,75 @@ describe HexaPDF::Font::TrueTypeWrapper do
73
73
  end
74
74
  end
75
75
 
76
- it "creates the necessary PDF dictionaries" do
77
- @font_wrapper.encode(@font_wrapper.glyph(3))
78
- glyph = @font_wrapper.decode_utf8('H').first
79
- @font_wrapper.encode(glyph)
80
- @doc.dispatch_message(:complete_objects)
81
-
82
- dict = @font_wrapper.dict
83
-
84
- # Checking Type 0 font dictionary
85
- assert_equal(:Font, dict[:Type])
86
- assert_equal(:Type0, dict[:Subtype])
87
- assert_equal(:'Identity-H', dict[:Encoding])
88
- assert_equal(1, dict[:DescendantFonts].length)
89
- assert_equal(dict[:BaseFont], dict[:DescendantFonts][0][:BaseFont])
90
- assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[3, ' '.ord], [glyph.id, 'H'.ord]]),
91
- dict[:ToUnicode].stream)
92
- assert_match(/\A[A-Z]{6}\+Ubuntu-Title\z/, dict[:BaseFont])
93
-
94
- # Checking CIDFont dictionary
95
- cidfont = dict[:DescendantFonts][0]
96
- assert_equal(:Font, cidfont[:Type])
97
- assert_equal(:CIDFontType2, cidfont[:Subtype])
98
- assert_equal({Registry: "Adobe", Ordering: "Identity", Supplement: 0}, cidfont[:CIDSystemInfo])
99
- assert_equal(:Identity, cidfont[:CIDToGIDMap])
100
- assert_equal(@font_wrapper.glyph(3).width, cidfont[:DW])
101
- assert_equal([glyph.id, [glyph.width]], cidfont[:W])
102
-
103
- # Checking font descriptor
104
- fd = cidfont[:FontDescriptor]
105
- assert_equal(dict[:BaseFont], fd[:FontName])
106
- assert(fd.flagged?(:symbolic))
107
- assert(fd.key?(:FontFile2))
108
- assert(fd.validate)
109
-
110
- # Two special cases for determining cap height and x-height
111
- @cmap.stub(:[], nil) do
112
- @font[:'OS/2'].typo_ascender = 1000
76
+ describe "creates the necessary PDF dictionaries" do
77
+ it "with fonts that are subset" do
78
+ @font_wrapper.encode(@font_wrapper.glyph(3))
79
+ glyph = @font_wrapper.decode_utf8('H').first
80
+ @font_wrapper.encode(glyph)
81
+ @doc.dispatch_message(:complete_objects)
82
+
83
+ dict = @font_wrapper.dict
84
+
85
+ # Checking Type 0 font dictionary
86
+ assert_equal(:Font, dict[:Type])
87
+ assert_equal(:Type0, dict[:Subtype])
88
+ assert_equal(:'Identity-H', dict[:Encoding])
89
+ assert_equal(1, dict[:DescendantFonts].length)
90
+ assert_equal(dict[:BaseFont], dict[:DescendantFonts][0][:BaseFont])
91
+ assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[1, ' '.ord], [2, 'H'.ord]]),
92
+ dict[:ToUnicode].stream)
93
+ assert_match(/\A[A-Z]{6}\+Ubuntu-Title\z/, dict[:BaseFont])
94
+
95
+ # Checking CIDFont dictionary
96
+ cidfont = dict[:DescendantFonts][0]
97
+ assert_equal(:Font, cidfont[:Type])
98
+ assert_equal(:CIDFontType2, cidfont[:Subtype])
99
+ assert_equal({Registry: "Adobe", Ordering: "Identity", Supplement: 0}, cidfont[:CIDSystemInfo])
100
+ assert_equal(:Identity, cidfont[:CIDToGIDMap])
101
+ assert_equal(@font_wrapper.glyph(3).width, cidfont[:DW])
102
+ assert_equal([2, [glyph.width]], cidfont[:W])
103
+ assert(cidfont.validate)
104
+
105
+ # Checking font descriptor
106
+ fd = cidfont[:FontDescriptor]
107
+ assert_equal(dict[:BaseFont], fd[:FontName])
108
+ assert(fd.flagged?(:symbolic))
109
+ assert(fd.key?(:FontFile2))
110
+ assert(fd.validate)
111
+
112
+ # Two special cases for determining cap height and x-height
113
+ @cmap.stub(:[], nil) do
114
+ @font[:'OS/2'].typo_ascender = 1000
115
+ font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font)
116
+ font_wrapper.encode(glyph)
117
+ fd = font_wrapper.dict[:DescendantFonts][0][:FontDescriptor]
118
+ assert_equal(800, fd[:CapHeight])
119
+ assert_equal(500, fd[:XHeight])
120
+ end
121
+
122
+ @font[:'OS/2'].version = 2
123
+ @font[:'OS/2'].x_height = 500 * @font[:head].units_per_em / 1000
124
+ @font[:'OS/2'].cap_height = 1000 * @font[:head].units_per_em / 1000
113
125
  font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font)
114
126
  font_wrapper.encode(glyph)
115
127
  fd = font_wrapper.dict[:DescendantFonts][0][:FontDescriptor]
116
- assert_equal(800, fd[:CapHeight])
128
+ assert_equal(1000, fd[:CapHeight])
117
129
  assert_equal(500, fd[:XHeight])
118
130
  end
119
131
 
120
- @font[:'OS/2'].version = 2
121
- @font[:'OS/2'].x_height = 500 * @font[:head].units_per_em / 1000
122
- @font[:'OS/2'].cap_height = 1000 * @font[:head].units_per_em / 1000
123
- font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font)
124
- font_wrapper.encode(glyph)
125
- fd = font_wrapper.dict[:DescendantFonts][0][:FontDescriptor]
126
- assert_equal(1000, fd[:CapHeight])
127
- assert_equal(500, fd[:XHeight])
132
+ it "with fonts that are not subset (only differences to other case)" do
133
+ @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
134
+ @font_wrapper.encode(@font_wrapper.glyph(3))
135
+ glyph = @font_wrapper.decode_utf8('H').first
136
+ @font_wrapper.encode(glyph)
137
+ @doc.dispatch_message(:complete_objects)
138
+
139
+ dict = @font_wrapper.dict
140
+
141
+ assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[3, ' '.ord], [glyph.id, 'H'.ord]]),
142
+ dict[:ToUnicode].stream)
143
+ assert_equal([glyph.id, [glyph.width]], dict[:DescendantFonts][0][:W])
144
+ end
128
145
  end
129
146
 
130
147
  describe "font file embedding" do
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'stringio'
4
+ require 'test_helper'
5
+ require 'hexapdf/font/true_type'
6
+ require 'hexapdf/font/true_type/builder'
7
+
8
+ describe HexaPDF::Font::TrueType::Builder do
9
+ before do
10
+ font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
11
+ @font = HexaPDF::Font::TrueType::Font.new(File.open(font_file))
12
+ @subsetter = HexaPDF::Font::TrueType::Subsetter.new(@font)
13
+ end
14
+
15
+ after do
16
+ @font.io.close
17
+ end
18
+
19
+ it "builds the font file" do
20
+ tables = {
21
+ "head" => @font[:head].raw_data,
22
+ "glyf" => @font[:glyf].raw_data,
23
+ "loca" => @font[:loca].raw_data,
24
+ "maxp" => @font[:maxp].raw_data,
25
+ }
26
+ font_data = HexaPDF::Font::TrueType::Builder.build(tables)
27
+ built_font = HexaPDF::Font::TrueType::Font.new(StringIO.new(font_data))
28
+
29
+ assert(built_font[:head].checksum_valid?)
30
+ assert_equal(@font[:glyf].raw_data, built_font[:glyf].raw_data)
31
+ assert(built_font[:glyf].checksum_valid?)
32
+ assert_equal(@font[:loca].raw_data, built_font[:loca].raw_data)
33
+ assert(built_font[:loca].checksum_valid?)
34
+ assert_equal(@font[:maxp].raw_data, built_font[:maxp].raw_data)
35
+ assert(built_font[:maxp].checksum_valid?)
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'stringio'
4
+ require 'test_helper'
5
+ require 'hexapdf/font/true_type'
6
+
7
+ describe HexaPDF::Font::TrueType::Optimizer do
8
+ before do
9
+ font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
10
+ @font = HexaPDF::Font::TrueType::Font.new(File.open(font_file))
11
+ @subsetter = HexaPDF::Font::TrueType::Subsetter.new(@font)
12
+ end
13
+
14
+ after do
15
+ @font.io.close
16
+ end
17
+
18
+ describe "build_for_pdf" do
19
+ it "builds a font file that is optimized for use with PDFs" do
20
+ font_data = HexaPDF::Font::TrueType::Optimizer.build_for_pdf(@font)
21
+ built_font = HexaPDF::Font::TrueType::Font.new(StringIO.new(font_data))
22
+ %I(FFTM GDEF GPOS GSUB name post).each do |table|
23
+ refute(built_font[table])
24
+ end
25
+ end
26
+ end
27
+ end
@@ -21,24 +21,17 @@ describe HexaPDF::Font::TrueType::Subsetter do
21
21
  assert_equal(1, @subsetter.use_glyph(5))
22
22
  end
23
23
 
24
+ it "returns the subset glyph ID for already subset glyphs" do
25
+ assert_nil(@subsetter.subset_glyph_id(5))
26
+ value = @subsetter.use_glyph(5)
27
+ assert_equal(value, @subsetter.subset_glyph_id(5))
28
+ end
29
+
24
30
  it "creates the subset font file" do
25
31
  gid = @font[:cmap].preferred_table[0x41]
26
32
  @subsetter.use_glyph(gid)
27
33
  subset = HexaPDF::Font::TrueType::Font.new(StringIO.new(@subsetter.build_font))
28
34
 
29
- assert(subset[:head])
30
- assert(subset[:head].checksum_valid?)
31
- assert(subset[:hhea])
32
- assert(subset[:hhea].checksum_valid?)
33
- assert(subset[:glyf])
34
- assert(subset[:glyf].checksum_valid?)
35
- assert(subset[:loca])
36
- assert(subset[:loca].checksum_valid?)
37
- assert(subset[:maxp])
38
- assert(subset[:maxp].checksum_valid?)
39
- assert(subset[:hmtx])
40
- assert(subset[:hmtx].checksum_valid?)
41
-
42
35
  assert(Time.now - subset[:head].modified < 10)
43
36
  assert_equal(2, subset[:maxp].num_glyphs)
44
37
  assert_equal(2, subset[:hhea].num_of_long_hor_metrics)
@@ -60,19 +60,24 @@ describe HexaPDF::Configuration do
60
60
  assert_equal(HexaPDF, @config.constantize('test', 1))
61
61
  end
62
62
 
63
- it "returns nil for an unknown option" do
64
- assert_nil(@config.constantize('unknown'))
63
+ def assert_constantize_error # :nodoc:
64
+ exp = assert_raises(HexaPDF::Error) { yield }
65
+ assert_match(/Error getting constant for configuration option/, exp.message)
65
66
  end
66
67
 
67
- it "returns nil for an unknown constant" do
68
+ it "raises an error for an unknown option" do
69
+ assert_constantize_error { @config.constantize('unknown') }
70
+ end
71
+
72
+ it "raises an error for an unknown constant" do
68
73
  @config['test'] = 'SomeUnknownConstant'
69
- assert_nil(@config.constantize('test'))
74
+ assert_constantize_error { @config.constantize('test') }
70
75
  end
71
76
 
72
- it "returns nil for an unknown constant using a nested option" do
77
+ it "raises an error for an unknown constant using a nested option" do
73
78
  @config['test'] = {}
74
- assert_nil(@config.constantize('test', 'test'))
75
- assert_nil(@config.constantize('test', nil))
79
+ assert_constantize_error { @config.constantize('test', 'test') }
80
+ assert_constantize_error { @config.constantize('test', nil) }
76
81
  end
77
82
 
78
83
  it "returns the result of the given block when no constant is found" do
@@ -564,4 +564,28 @@ EOF
564
564
  assert_equal([[:callable, [:arg]], [:block, [:arg]]], args)
565
565
  end
566
566
  end
567
+
568
+ describe "caching interface" do
569
+ it "allows setting and retrieving values" do
570
+ assert_equal(:test, @doc.cache(:a, :b, :test))
571
+ assert_equal(:test, @doc.cache(:a, :b, :other))
572
+ assert_equal(:other, @doc.cache(:a, :c) { :other })
573
+ assert(@doc.cached?(:a, :b))
574
+ assert(@doc.cached?(:a, :c))
575
+ end
576
+
577
+ it "allows clearing cached values" do
578
+ @doc.cache(:a, :b, :c)
579
+ @doc.cache(:b, :c, :d)
580
+ @doc.clear_cache(:a)
581
+ refute(@doc.cached?(:a, :b))
582
+ assert(@doc.cached?(:b, :c))
583
+ @doc.clear_cache
584
+ refute(@doc.cached?(:a, :c))
585
+ end
586
+
587
+ it "fails if no cached value exists and neither a value nor a block is given" do
588
+ assert_raises(LocalJumpError) { @doc.cache(:a, :b) }
589
+ end
590
+ end
567
591
  end
@@ -8,7 +8,8 @@ describe HexaPDF::Importer do
8
8
  before do
9
9
  @source = HexaPDF::Document.new
10
10
  obj = @source.add("test")
11
- @obj = @source.add(hash: {key: "value"}, array: ["one", "two"],
11
+ @hash = @source.wrap(key: "value")
12
+ @obj = @source.add(hash: @hash, array: ["one", "two"],
12
13
  ref: HexaPDF::Reference.new(obj.oid, obj.gen),
13
14
  others: [:symbol, 5, 5.5, nil, true, false])
14
15
  @source.pages.add
@@ -24,6 +25,13 @@ describe HexaPDF::Importer do
24
25
  end
25
26
 
26
27
  describe "import" do
28
+ it "updates the associated document" do
29
+ obj = @importer.import(@obj)
30
+ assert_same(obj.document, @dest)
31
+ obj = @importer.import(@hash)
32
+ assert_same(obj.document, @dest)
33
+ end
34
+
27
35
  it "imports an object only once" do
28
36
  obj = @importer.import(@obj)
29
37
  assert_same(obj, @importer.import(@obj))
@@ -40,7 +40,7 @@ startxref
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.3.0)>>
43
+ <</Producer(HexaPDF version 0.4.0)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -72,7 +72,7 @@ startxref
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.3.0)>>
75
+ <</Producer(HexaPDF version 0.4.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -0,0 +1,61 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/cid_font'
6
+
7
+ describe HexaPDF::Type::CIDFont do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @font = @doc.wrap(Type: :Font, Subtype: :CIDFontType2, W: [1, 2, 3], DW: 100,
11
+ CIDSystemInfo: {Registry: 'Adobe', Ordering: 'Japan1', Supplement: 1})
12
+ end
13
+
14
+ describe "width" do
15
+ before do
16
+ @font[:W] = [1, [1], 2, [2, 3, 4], 5, 6, 10, 20, [20, 21], 30, 32, 40]
17
+ end
18
+
19
+ it "returns the glyph width for a CID defined via the /W array" do
20
+ assert_equal(1, @font.width(1))
21
+ assert_equal(2, @font.width(2))
22
+ assert_equal(3, @font.width(3))
23
+ assert_equal(4, @font.width(4))
24
+ assert_equal(10, @font.width(5))
25
+ assert_equal(10, @font.width(6))
26
+ assert_equal(20, @font.width(20))
27
+ assert_equal(21, @font.width(21))
28
+ assert_equal(40, @font.width(32))
29
+ end
30
+
31
+ it "returns the /DW value for CIDs not in the /W array" do
32
+ assert_equal(100, @font.width(100))
33
+ @font.delete(:DW)
34
+ assert_equal(1000, @font.width(100))
35
+ end
36
+ end
37
+
38
+ describe "set_widths" do
39
+ it "allows setting the widths" do
40
+ @font.set_widths([[1, 1], [2, 2], [4, 4], [5, 5], [7, 7.1]], default_width: 5.1)
41
+ assert_equal(5, @font[:DW])
42
+ assert_equal([1, [1, 2], 4, [4, 5], 7, [7]], @font[:W])
43
+ end
44
+
45
+ it "handles an empty widths array correctly" do
46
+ @font.set_widths([], default_width: 100)
47
+ refute(@font.key?(:W))
48
+ assert_equal(100, @font[:DW])
49
+
50
+ @font.set_widths([])
51
+ refute(@font.key?(:W))
52
+ end
53
+
54
+ it "handles setting /DW to the default value correctly" do
55
+ @font.set_widths([])
56
+ refute(@font.key?(:DW))
57
+ @font.set_widths([[1, 1]])
58
+ refute(@font.key?(:DW))
59
+ end
60
+ end
61
+ end
@@ -13,7 +13,8 @@ describe HexaPDF::Type::Font do
13
13
  <22> <0042>
14
14
  endbfchar
15
15
  EOF
16
- @font = @doc.add({Type: :Font, BaseFont: :TestFont, ToUnicode: cmap})
16
+ fd = @doc.add(Type: :FontDescriptor, FontBBox: [0, 1, 2, 3])
17
+ @font = @doc.add(Type: :Font, BaseFont: :TestFont, FontDescriptor: fd, ToUnicode: cmap)
17
18
  end
18
19
 
19
20
  it "must always be an indirect" do
@@ -24,12 +25,38 @@ describe HexaPDF::Type::Font do
24
25
  it "uses the /ToUnicode CMap if it is available" do
25
26
  assert_equal("A", @font.to_utf8(32))
26
27
  assert_equal("B", @font.to_utf8(34))
27
- assert_equal("", @font.to_utf8(0))
28
+ assert_raises(HexaPDF::Error) { @font.to_utf8(0) }
28
29
  end
29
30
 
30
- it "returns an empty string if no /ToUnicode CMap is available" do
31
+ it "calls the configured proc if no /ToUnicode CMap is available" do
31
32
  @font.delete(:ToUnicode)
32
- assert_equal("", @font.to_utf8(32))
33
+ assert_raises(HexaPDF::Error) { @font.to_utf8(32) }
34
+ end
35
+ end
36
+
37
+ describe "bounding_box" do
38
+ it "returns the bounding box" do
39
+ assert_equal([0, 1, 2, 3], @font.bounding_box)
40
+ end
41
+
42
+ it "returns nil if no bounding box information can be found" do
43
+ @font[:FontDescriptor].delete(:FontBBox)
44
+ assert_nil(@font.bounding_box)
45
+ end
46
+ end
47
+
48
+ describe "embedded" do
49
+ it "returns true if the font is embedded" do
50
+ refute(@font.embedded?)
51
+ @font[:FontDescriptor][:FontFile] = 5
52
+ assert(@font.embedded?)
53
+ end
54
+ end
55
+
56
+ describe "font_file" do
57
+ it "returns the stream object representing the embedded font file" do
58
+ @font[:FontDescriptor][:FontFile] = 5
59
+ assert_equal(5, @font.font_file)
33
60
  end
34
61
  end
35
62
  end
@@ -112,9 +112,9 @@ describe HexaPDF::Type::FontSimple do
112
112
  assert_equal(" ", @font.to_utf8(32))
113
113
  end
114
114
 
115
- it "returns an empty string if no correct mapping could be found" do
115
+ it "calls the configured proc if no correct mapping could be found" do
116
116
  @font.delete(:ToUnicode)
117
- assert_equal("", @font.to_utf8(0))
117
+ assert_raises(HexaPDF::Error) { @font.to_utf8(0) }
118
118
  end
119
119
  end
120
120
 
@@ -141,25 +141,6 @@ describe HexaPDF::Type::FontSimple do
141
141
  end
142
142
  end
143
143
 
144
- describe "bounding_box" do
145
- it "returns the bounding box" do
146
- assert_equal([0, 1, 2, 3], @font.bounding_box)
147
- end
148
-
149
- it "returns nil if no bounding box information can be found" do
150
- @font[:FontDescriptor].delete(:FontBBox)
151
- assert_nil(@font.bounding_box)
152
- end
153
- end
154
-
155
- describe "embedded" do
156
- it "returns true if the font is embedded" do
157
- refute(@font.embedded?)
158
- @font[:FontDescriptor][:FontFile] = 5
159
- assert(@font.embedded?)
160
- end
161
- end
162
-
163
144
  describe "symbolic?" do
164
145
  it "return true if the font is symbolic" do
165
146
  @font[:FontDescriptor].flag(clear_existing: true)
@@ -175,6 +156,10 @@ describe HexaPDF::Type::FontSimple do
175
156
  end
176
157
  end
177
158
 
159
+ it "defines word spacing as always applicable" do
160
+ assert(@font.word_spacing_applicable?)
161
+ end
162
+
178
163
  describe "validation" do
179
164
  it "validates the existence of required keys" do
180
165
  assert(@font.validate)