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
@@ -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)