hexapdf 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/CONTRIBUTERS +1 -1
- data/README.md +5 -5
- data/VERSION +1 -1
- data/examples/emoji-smile.png +0 -0
- data/examples/emoji-wink.png +0 -0
- data/examples/graphics.rb +9 -8
- data/examples/standard_pdf_fonts.rb +2 -1
- data/examples/text_box_alignment.rb +47 -0
- data/examples/text_box_inline_boxes.rb +56 -0
- data/examples/text_box_line_wrapping.rb +57 -0
- data/examples/text_box_shapes.rb +166 -0
- data/examples/text_box_styling.rb +72 -0
- data/examples/truetype.rb +3 -4
- data/lib/hexapdf/cli/optimize.rb +2 -2
- data/lib/hexapdf/configuration.rb +8 -6
- data/lib/hexapdf/content/canvas.rb +8 -5
- data/lib/hexapdf/content/parser.rb +3 -2
- data/lib/hexapdf/content/processor.rb +14 -3
- data/lib/hexapdf/document.rb +1 -0
- data/lib/hexapdf/document/fonts.rb +2 -1
- data/lib/hexapdf/document/pages.rb +23 -0
- data/lib/hexapdf/font/invalid_glyph.rb +78 -0
- data/lib/hexapdf/font/true_type/font.rb +14 -3
- data/lib/hexapdf/font/true_type/table.rb +1 -0
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -0
- data/lib/hexapdf/font/true_type/table/glyf.rb +4 -0
- data/lib/hexapdf/font/true_type/table/kern.rb +170 -0
- data/lib/hexapdf/font/true_type/table/post.rb +5 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +71 -24
- data/lib/hexapdf/font/type1/afm_parser.rb +3 -2
- data/lib/hexapdf/font/type1/character_metrics.rb +0 -9
- data/lib/hexapdf/font/type1/font.rb +11 -0
- data/lib/hexapdf/font/type1/font_metrics.rb +6 -1
- data/lib/hexapdf/font/type1_wrapper.rb +51 -7
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/layout.rb +51 -0
- data/lib/hexapdf/layout/inline_box.rb +95 -0
- data/lib/hexapdf/layout/line_fragment.rb +333 -0
- data/lib/hexapdf/layout/numeric_refinements.rb +56 -0
- data/lib/hexapdf/layout/style.rb +365 -0
- data/lib/hexapdf/layout/text_box.rb +727 -0
- data/lib/hexapdf/layout/text_fragment.rb +206 -0
- data/lib/hexapdf/layout/text_shaper.rb +155 -0
- data/lib/hexapdf/task.rb +0 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/font_descriptor.rb +2 -1
- data/lib/hexapdf/type/font_type0.rb +3 -1
- data/lib/hexapdf/type/form.rb +12 -4
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +7 -0
- data/test/hexapdf/content/common.rb +8 -0
- data/test/hexapdf/content/test_canvas.rb +10 -22
- data/test/hexapdf/content/test_processor.rb +4 -1
- data/test/hexapdf/document/test_pages.rb +16 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +25 -11
- data/test/hexapdf/font/test_type1_wrapper.rb +26 -10
- data/test/hexapdf/font/true_type/table/common.rb +27 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +14 -20
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +7 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +8 -6
- data/test/hexapdf/font/true_type/table/test_head.rb +9 -13
- data/test/hexapdf/font/true_type/table/test_hhea.rb +16 -23
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +4 -7
- data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +7 -13
- data/test/hexapdf/font/true_type/table/test_maxp.rb +4 -9
- data/test/hexapdf/font/true_type/table/test_name.rb +14 -17
- data/test/hexapdf/font/true_type/table/test_os2.rb +3 -5
- data/test/hexapdf/font/true_type/table/test_post.rb +21 -19
- data/test/hexapdf/font/true_type/test_font.rb +4 -0
- data/test/hexapdf/font/type1/common.rb +6 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +9 -0
- data/test/hexapdf/font/type1/test_font.rb +6 -0
- data/test/hexapdf/layout/test_inline_box.rb +40 -0
- data/test/hexapdf/layout/test_line_fragment.rb +206 -0
- data/test/hexapdf/layout/test_style.rb +143 -0
- data/test/hexapdf/layout/test_text_box.rb +640 -0
- data/test/hexapdf/layout/test_text_fragment.rb +208 -0
- data/test/hexapdf/layout/test_text_shaper.rb +64 -0
- data/test/hexapdf/task/test_dereference.rb +1 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_font_descriptor.rb +4 -2
- data/test/hexapdf/type/test_font_type0.rb +7 -0
- data/test/hexapdf/type/test_form.rb +12 -0
- metadata +29 -2
|
@@ -115,6 +115,13 @@ module CommonTokenizerTests
|
|
|
115
115
|
assert_raises(HexaPDF::MalformedPDFError) { @tokenizer.next_token }
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
+
it "next_token: returns a PDF keyword for a solitary plus sign" do
|
|
119
|
+
create_tokenizer("+")
|
|
120
|
+
token = @tokenizer.next_token
|
|
121
|
+
assert_equal("+", token)
|
|
122
|
+
assert(token.kind_of?(HexaPDF::Tokenizer::Token))
|
|
123
|
+
end
|
|
124
|
+
|
|
118
125
|
it "next_object: works for all PDF object types, including array and dictionary" do
|
|
119
126
|
create_tokenizer(<<-EOF.chomp.gsub(/^ {8}/, ''))
|
|
120
127
|
true false null 123 34.5 (string) <4E6F76> /Name
|
|
@@ -28,4 +28,12 @@ module TestHelper
|
|
|
28
28
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# Asserts that the content string contains the operators.
|
|
32
|
+
def assert_operators(content, operators, only_names: false)
|
|
33
|
+
processor = TestHelper::OperatorRecorder.new
|
|
34
|
+
HexaPDF::Content::Parser.new.parse(content, processor)
|
|
35
|
+
result = processor.recorded_ops
|
|
36
|
+
result.map!(&:first) if only_names
|
|
37
|
+
assert_equal(operators, result)
|
|
38
|
+
end
|
|
31
39
|
end
|
|
@@ -9,22 +9,12 @@ require 'hexapdf/content/parser'
|
|
|
9
9
|
|
|
10
10
|
describe HexaPDF::Content::Canvas do
|
|
11
11
|
before do
|
|
12
|
-
@processor = TestHelper::OperatorRecorder.new
|
|
13
|
-
@parser = HexaPDF::Content::Parser.new
|
|
14
|
-
|
|
15
12
|
@doc = HexaPDF::Document.new
|
|
16
13
|
@doc.config['graphic_object.arc.max_curves'] = 4
|
|
17
14
|
@page = @doc.pages.add
|
|
18
15
|
@canvas = @page.canvas
|
|
19
16
|
end
|
|
20
17
|
|
|
21
|
-
# Asserts that the content string contains the operators.
|
|
22
|
-
def assert_operators(content, operators)
|
|
23
|
-
@processor.recorded_ops.clear
|
|
24
|
-
@parser.parse(content, @processor)
|
|
25
|
-
assert_equal(operators, @processor.recorded_ops)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
18
|
# Asserts that a specific operator is invoked when the block is executed.
|
|
29
19
|
def assert_operator_invoked(op, *args)
|
|
30
20
|
mock = Minitest::Mock.new
|
|
@@ -638,10 +628,9 @@ describe HexaPDF::Content::Canvas do
|
|
|
638
628
|
|
|
639
629
|
it "serializes correctly" do
|
|
640
630
|
@canvas.circle(0, 0, 1)
|
|
641
|
-
@
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
@processor.recorded_ops.map(&:first))
|
|
631
|
+
assert_operators(@canvas.contents,
|
|
632
|
+
[:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath],
|
|
633
|
+
only_names: true)
|
|
645
634
|
end
|
|
646
635
|
|
|
647
636
|
it "returns the canvas object" do
|
|
@@ -658,10 +647,9 @@ describe HexaPDF::Content::Canvas do
|
|
|
658
647
|
|
|
659
648
|
it "serializes correctly" do
|
|
660
649
|
@canvas.ellipse(0, 0, a: 10, b: 5, inclination: 10)
|
|
661
|
-
@
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
@processor.recorded_ops.map(&:first))
|
|
650
|
+
assert_operators(@canvas.contents,
|
|
651
|
+
[:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath],
|
|
652
|
+
only_names: true)
|
|
665
653
|
end
|
|
666
654
|
|
|
667
655
|
it "returns the canvas object" do
|
|
@@ -916,8 +904,7 @@ describe HexaPDF::Content::Canvas do
|
|
|
916
904
|
@canvas.begin_text
|
|
917
905
|
@canvas.begin_text
|
|
918
906
|
@canvas.begin_text(force_new: true)
|
|
919
|
-
|
|
920
|
-
assert_equal([:begin_text, :end_text, :begin_text], @processor.recorded_ops.map(&:first))
|
|
907
|
+
assert_operators(@canvas.contents, [:begin_text, :end_text, :begin_text], only_names: true)
|
|
921
908
|
end
|
|
922
909
|
|
|
923
910
|
it "returns the canvas object" do
|
|
@@ -947,8 +934,7 @@ describe HexaPDF::Content::Canvas do
|
|
|
947
934
|
@canvas.begin_text
|
|
948
935
|
@canvas.end_text
|
|
949
936
|
@canvas.end_text
|
|
950
|
-
|
|
951
|
-
assert_equal([:begin_text, :end_text], @processor.recorded_ops.map(&:first))
|
|
937
|
+
assert_operators(@page.contents, [:begin_text, :end_text], only_names: true)
|
|
952
938
|
end
|
|
953
939
|
|
|
954
940
|
it "returns the canvas object" do
|
|
@@ -1014,6 +1000,8 @@ describe HexaPDF::Content::Canvas do
|
|
|
1014
1000
|
assert_nil(@canvas.font)
|
|
1015
1001
|
@canvas.font("Times", size: 10)
|
|
1016
1002
|
assert_same(@doc.fonts.load("Times"), @canvas.font)
|
|
1003
|
+
@canvas.font(@canvas.font)
|
|
1004
|
+
assert_same(@doc.fonts.load("Times"), @canvas.font)
|
|
1017
1005
|
@canvas.font("Helvetica", size: 10)
|
|
1018
1006
|
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 10]],
|
|
1019
1007
|
[:set_leading, [12.0]],
|
|
@@ -99,7 +99,10 @@ describe HexaPDF::Content::Processor do
|
|
|
99
99
|
@processor.resources = resources = Object.new
|
|
100
100
|
@processor.resources.define_singleton_method(:xobject) do |_name|
|
|
101
101
|
obj = {Matrix: [2, 0, 0, 2, 10, 10], Subtype: :Form}
|
|
102
|
-
obj.define_singleton_method(:process_contents)
|
|
102
|
+
obj.define_singleton_method(:process_contents) do |processor, original_resources:|
|
|
103
|
+
test_case.assert_same(resources, original_resources)
|
|
104
|
+
processor.process(:w, [10])
|
|
105
|
+
end
|
|
103
106
|
obj
|
|
104
107
|
end
|
|
105
108
|
|
|
@@ -20,6 +20,22 @@ describe HexaPDF::Document::Pages do
|
|
|
20
20
|
assert_equal([page], @doc.pages.root[:Kids])
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
it "adds a new empty page with the given dimensions" do
|
|
24
|
+
page = @doc.pages.add([0, 0, 20, 20])
|
|
25
|
+
assert_same(page, @doc.pages[0])
|
|
26
|
+
assert_equal([0, 0, 20, 20], @doc.pages[0].box(:media).value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "adds a new empty page with the given page format" do
|
|
30
|
+
page = @doc.pages.add(:A4)
|
|
31
|
+
assert_same(page, @doc.pages[0])
|
|
32
|
+
assert_equal([0, 0, 595, 842], @doc.pages[0].box(:media).value)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "fails if an unknown page format is given" do
|
|
36
|
+
assert_raises(HexaPDF::Error) { @doc.pages.add(:A953) }
|
|
37
|
+
end
|
|
38
|
+
|
|
23
39
|
it "adds a given page to the end" do
|
|
24
40
|
page = @doc.pages.add
|
|
25
41
|
new_page = @doc.add(Type: :Page)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/font/invalid_glyph'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Font::InvalidGlyph do
|
|
7
|
+
before do
|
|
8
|
+
font = Object.new
|
|
9
|
+
font.define_singleton_method(:missing_glyph_id) { 0 }
|
|
10
|
+
font.define_singleton_method(:full_name) { "Test Roman" }
|
|
11
|
+
@glyph = HexaPDF::Font::InvalidGlyph.new(font, "str")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "returns the missing glyph id for id/name" do
|
|
15
|
+
assert_equal(0, @glyph.id)
|
|
16
|
+
assert_equal(0, @glyph.name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "returns 0 for all glyph dimensions" do
|
|
20
|
+
assert_equal(0, @glyph.x_min)
|
|
21
|
+
assert_equal(0, @glyph.x_max)
|
|
22
|
+
assert_equal(0, @glyph.y_min)
|
|
23
|
+
assert_equal(0, @glyph.y_max)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "doesn't allow the application of word spacing" do
|
|
27
|
+
refute(@glyph.apply_word_spacing?)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "can represent itself for debug purposes" do
|
|
31
|
+
assert_equal('#<HexaPDF::Font::InvalidGlyph font="Test Roman" id=0 "str">',
|
|
32
|
+
@glyph.inspect)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -35,24 +35,34 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
35
35
|
@font_wrapper.decode_utf8("Test").map {|g| @cmap.gid_to_code(g.id)}.pack('U*'))
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
it "UTF-8 characters for which no glyph exists
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
assert_equal(128_513,
|
|
38
|
+
it "invokes font.on_missing_glyph for UTF-8 characters for which no glyph exists" do
|
|
39
|
+
glyphs = @font_wrapper.decode_utf8("😁")
|
|
40
|
+
assert_equal(1, glyphs.length)
|
|
41
|
+
assert_kind_of(HexaPDF::Font::InvalidGlyph, glyphs.first)
|
|
42
|
+
assert_equal('' << 128_513, glyphs.first.str)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
describe "glyph" do
|
|
47
47
|
it "returns the glyph object for the given id" do
|
|
48
|
-
glyph = @font_wrapper.glyph(
|
|
49
|
-
assert_equal(
|
|
50
|
-
assert_equal(
|
|
51
|
-
|
|
48
|
+
glyph = @font_wrapper.glyph(17)
|
|
49
|
+
assert_equal(17, glyph.id)
|
|
50
|
+
assert_equal("0", glyph.str)
|
|
51
|
+
assert_equal(628, glyph.width)
|
|
52
|
+
assert_equal(47, glyph.x_min)
|
|
53
|
+
assert_equal(0, glyph.y_min)
|
|
54
|
+
assert_equal(584, glyph.x_max)
|
|
55
|
+
assert_equal(696, glyph.y_max)
|
|
56
|
+
refute(glyph.apply_word_spacing?)
|
|
57
|
+
assert_equal('#<HexaPDF::Font::TrueTypeWrapper::Glyph font="Ubuntu-Title" id=17 "0">',
|
|
58
|
+
glyph.inspect)
|
|
52
59
|
end
|
|
53
60
|
|
|
54
61
|
it "invokes font.on_missing_glyph for missing glyphs" do
|
|
55
|
-
|
|
62
|
+
glyph = @font_wrapper.glyph(9999)
|
|
63
|
+
assert_kind_of(HexaPDF::Font::InvalidGlyph, glyph)
|
|
64
|
+
assert_equal(0, glyph.id)
|
|
65
|
+
assert_equal('' << 0xFFFD, glyph.str)
|
|
56
66
|
end
|
|
57
67
|
end
|
|
58
68
|
|
|
@@ -71,6 +81,10 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
71
81
|
code = @font_wrapper.encode(@font_wrapper.glyph(10))
|
|
72
82
|
assert_equal([10].pack('n'), code)
|
|
73
83
|
end
|
|
84
|
+
|
|
85
|
+
it "raises an error if an InvalidGlyph is encoded" do
|
|
86
|
+
assert_raises(HexaPDF::Error) { @font_wrapper.encode(@font_wrapper.glyph(9999)) }
|
|
87
|
+
end
|
|
74
88
|
end
|
|
75
89
|
|
|
76
90
|
describe "creates the necessary PDF dictionaries" do
|
|
@@ -89,7 +103,7 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
89
103
|
assert_equal(1, dict[:DescendantFonts].length)
|
|
90
104
|
assert_equal(dict[:BaseFont], dict[:DescendantFonts][0][:BaseFont])
|
|
91
105
|
assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[1, ' '.ord], [2, 'H'.ord]]),
|
|
92
|
-
|
|
106
|
+
dict[:ToUnicode].stream)
|
|
93
107
|
assert_match(/\A[A-Z]{6}\+Ubuntu-Title\z/, dict[:BaseFont])
|
|
94
108
|
|
|
95
109
|
# Checking CIDFont dictionary
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
require 'test_helper'
|
|
4
|
+
require_relative 'type1/common'
|
|
4
5
|
require 'hexapdf/font/type1_wrapper'
|
|
5
6
|
require 'hexapdf/document'
|
|
6
7
|
|
|
7
|
-
FONT_TIMES = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm', "Times-Roman.afm"))
|
|
8
|
-
FONT_SYMBOL = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm', "Symbol.afm"))
|
|
9
|
-
|
|
10
8
|
describe HexaPDF::Font::Type1Wrapper do
|
|
11
9
|
before do
|
|
12
10
|
@doc = HexaPDF::Document.new
|
|
@@ -14,16 +12,20 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
14
12
|
@symbol_wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_SYMBOL)
|
|
15
13
|
end
|
|
16
14
|
|
|
15
|
+
it "returns 1 for the scaling factor" do
|
|
16
|
+
assert_equal(1, @times_wrapper.scaling_factor)
|
|
17
|
+
end
|
|
18
|
+
|
|
17
19
|
describe "decode_utf8" do
|
|
18
20
|
it "returns an array of glyph objects" do
|
|
19
21
|
assert_equal([:T, :e, :s, :t], @times_wrapper.decode_utf8("Test").map(&:name))
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
it "UTF-8 characters for which no glyph name
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
assert_equal(
|
|
24
|
+
it "UTF-8 characters for which no glyph name exists, are mapped to InvalidGlyph objects" do
|
|
25
|
+
glyphs = @times_wrapper.decode_utf8("😁")
|
|
26
|
+
assert_equal(1, glyphs.length)
|
|
27
|
+
assert_kind_of(HexaPDF::Font::InvalidGlyph, glyphs.first)
|
|
28
|
+
assert_equal('' << 128_513, glyphs.first.str)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -31,12 +33,22 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
31
33
|
it "returns the glyph object for the given name" do
|
|
32
34
|
glyph = @times_wrapper.glyph(:A)
|
|
33
35
|
assert_equal(:A, glyph.name)
|
|
36
|
+
assert_equal("A", glyph.str)
|
|
34
37
|
assert_equal(722, glyph.width)
|
|
35
|
-
|
|
38
|
+
assert_equal(15, glyph.x_min)
|
|
39
|
+
assert_equal(0, glyph.y_min)
|
|
40
|
+
assert_equal(706, glyph.x_max)
|
|
41
|
+
assert_equal(674, glyph.y_max)
|
|
42
|
+
refute(glyph.apply_word_spacing?)
|
|
43
|
+
assert_equal('#<HexaPDF::Font::Type1Wrapper::Glyph font="Times Roman" id=:A "A">',
|
|
44
|
+
glyph.inspect)
|
|
36
45
|
end
|
|
37
46
|
|
|
38
47
|
it "invokes font.on_missing_glyph for missing glyphs" do
|
|
39
|
-
|
|
48
|
+
glyph = @times_wrapper.glyph(:ffi)
|
|
49
|
+
assert_kind_of(HexaPDF::Font::InvalidGlyph, glyph)
|
|
50
|
+
assert_equal(:'.notdef', glyph.name)
|
|
51
|
+
assert_equal('ffi', glyph.str)
|
|
40
52
|
end
|
|
41
53
|
end
|
|
42
54
|
|
|
@@ -49,6 +61,10 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
49
61
|
assert_equal(:WinAnsiEncoding, @times_wrapper.dict[:Encoding])
|
|
50
62
|
end
|
|
51
63
|
|
|
64
|
+
it "fails if an InvalidGlyph is encoded" do
|
|
65
|
+
assert_raises(HexaPDF::Error) { @times_wrapper.encode(@times_wrapper.glyph(:ffi)) }
|
|
66
|
+
end
|
|
67
|
+
|
|
52
68
|
it "fails if the encoding does not support the given glyph" do
|
|
53
69
|
assert_raises(HexaPDF::Error) { @times_wrapper.encode(@times_wrapper.glyph(:uring)) }
|
|
54
70
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
require 'hexapdf/font/true_type'
|
|
6
|
+
|
|
7
|
+
module TestHelper
|
|
8
|
+
|
|
9
|
+
def set_up_stub_true_type_font(initial_data = ''.b, register_vars: true)
|
|
10
|
+
font = Object.new
|
|
11
|
+
font.define_singleton_method(:io) { @io ||= StringIO.new(initial_data) }
|
|
12
|
+
font.define_singleton_method(:config) { @config ||= {} }
|
|
13
|
+
entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('mock', 0, 0, initial_data.length)
|
|
14
|
+
@font, @entry = font, entry if register_vars
|
|
15
|
+
[font, entry]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_table(name, data = nil, standalone: false)
|
|
19
|
+
font, entry = !standalone ? [@font, @entry] : set_up_stub_true_type_font(register_vars: false)
|
|
20
|
+
if data
|
|
21
|
+
font.io.string = data
|
|
22
|
+
entry.length = font.io.length
|
|
23
|
+
end
|
|
24
|
+
HexaPDF::Font::TrueType::Table.const_get(name).new(font, entry)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -1,53 +1,47 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
require 'test_helper'
|
|
4
|
-
|
|
4
|
+
require_relative 'common'
|
|
5
5
|
require 'hexapdf/font/true_type/table/cmap'
|
|
6
6
|
|
|
7
7
|
describe HexaPDF::Font::TrueType::Table::Cmap do
|
|
8
8
|
before do
|
|
9
9
|
f0 = [0, 262, 0].pack('n3') + (0..255).to_a.pack('C*')
|
|
10
|
-
data = [0, 3].pack('n2') << [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
config = @config = {}
|
|
17
|
-
@file = Object.new
|
|
18
|
-
@file.define_singleton_method(:io) { io }
|
|
19
|
-
@file.define_singleton_method(:config) { config }
|
|
20
|
-
@entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('cmap', 0, 0, io.length)
|
|
10
|
+
data = [0, 3].pack('n2') << [
|
|
11
|
+
[0, 1, 28],
|
|
12
|
+
[3, 1, 28 + f0.length],
|
|
13
|
+
[1, 0, 28],
|
|
14
|
+
].map {|a| a.pack('n2N')}.join('') << f0 << f0
|
|
15
|
+
set_up_stub_true_type_font(data)
|
|
21
16
|
end
|
|
22
17
|
|
|
23
18
|
describe "initialize" do
|
|
24
19
|
it "reads the data from the associated file" do
|
|
25
|
-
table =
|
|
20
|
+
table = create_table(:Cmap)
|
|
26
21
|
assert_equal(0, table.version)
|
|
27
22
|
assert_equal(3, table.tables.length)
|
|
28
23
|
end
|
|
29
24
|
|
|
30
25
|
it "ignores unknown subtable when the config option is set to :ignore" do
|
|
31
|
-
|
|
32
|
-
table = HexaPDF::Font::TrueType::Table::Cmap.new(@file, @entry)
|
|
26
|
+
table = create_table(:Cmap, [0, 1].pack('n2') << [3, 1, 12].pack('n2N') << "\x00\x03")
|
|
33
27
|
assert_equal(0, table.tables.length)
|
|
34
28
|
end
|
|
35
29
|
|
|
36
30
|
it "raises an error when an unsupported subtable is found and the option is set to :raise" do
|
|
37
|
-
|
|
38
|
-
@
|
|
39
|
-
assert_raises(HexaPDF::Error) {
|
|
31
|
+
data = [0, 1].pack('n2') << [3, 1, 12].pack('n2N') << "\x00\x03"
|
|
32
|
+
@font.config['font.true_type.unknown_format'] = :raise
|
|
33
|
+
assert_raises(HexaPDF::Error) { create_table(:Cmap, data) }
|
|
40
34
|
end
|
|
41
35
|
|
|
42
36
|
it "loads data from subtables with identical offsets only once" do
|
|
43
|
-
table =
|
|
37
|
+
table = create_table(:Cmap)
|
|
44
38
|
assert_same(table.tables[0].gid_map, table.tables[2].gid_map)
|
|
45
39
|
refute_same(table.tables[0].gid_map, table.tables[1].gid_map)
|
|
46
40
|
end
|
|
47
41
|
end
|
|
48
42
|
|
|
49
43
|
it "returns the preferred table" do
|
|
50
|
-
table =
|
|
44
|
+
table = create_table(:Cmap)
|
|
51
45
|
assert_equal(table.tables[1], table.preferred_table)
|
|
52
46
|
end
|
|
53
47
|
end
|
|
@@ -90,6 +90,13 @@ describe HexaPDF::Font::TrueType::Table::CmapSubtable do
|
|
|
90
90
|
assert_equal(65535, t.gid_to_code(84))
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
it "works for format 4 with invalid 0xffff entry" do
|
|
94
|
+
f4 = [2, 0, 0, 0, 65535, 0, 65535, 0, 32767].pack('n*')
|
|
95
|
+
t = table([4, f4.length + 6, 0].pack('n3') << f4)
|
|
96
|
+
assert_nil(t[65535])
|
|
97
|
+
assert_equal(65535, t.gid_to_code(0))
|
|
98
|
+
end
|
|
99
|
+
|
|
93
100
|
it "works for format 6" do
|
|
94
101
|
t = table(([6, 30, 0, 1024, 10] + (1..10).to_a).pack('n*'))
|
|
95
102
|
assert_nil(t[0])
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
require 'test_helper'
|
|
4
|
-
|
|
4
|
+
require_relative 'common'
|
|
5
5
|
require 'hexapdf/font/true_type/table/glyf'
|
|
6
6
|
|
|
7
7
|
describe HexaPDF::Font::TrueType::Table::Glyf do
|
|
8
8
|
before do
|
|
9
|
-
@file = Object.new
|
|
10
9
|
loca = Object.new
|
|
11
10
|
loca.define_singleton_method(:offsets) { @offsets ||= [] }
|
|
12
11
|
loca.define_singleton_method(:offset) {|i| @offsets[i]}
|
|
@@ -21,17 +20,20 @@ describe HexaPDF::Font::TrueType::Table::Glyf do
|
|
|
21
20
|
data << [0b10100001, 4, 20, 30, 40, 50, 60, 70].pack('n2n2n4')
|
|
22
21
|
data << [0b00000000, 1, 20, 30].pack('n2C2')
|
|
23
22
|
loca.offsets << data.size
|
|
24
|
-
|
|
25
|
-
@
|
|
26
|
-
@entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('glyf', 0, 0, @file.io.length)
|
|
23
|
+
set_up_stub_true_type_font(data)
|
|
24
|
+
@font.define_singleton_method(:[]) {|_arg| loca }
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
describe "initialize" do
|
|
30
28
|
it "reads the data from the associated file" do
|
|
31
|
-
table =
|
|
29
|
+
table = create_table(:Glyf)
|
|
32
30
|
glyph = table[0]
|
|
33
31
|
refute(glyph.compound?)
|
|
34
32
|
assert_equal(0, glyph.number_of_contours)
|
|
33
|
+
assert_equal(0, glyph.x_min)
|
|
34
|
+
assert_equal(0, glyph.y_min)
|
|
35
|
+
assert_equal(0, glyph.x_max)
|
|
36
|
+
assert_equal(0, glyph.y_max)
|
|
35
37
|
|
|
36
38
|
glyph = table[1]
|
|
37
39
|
refute(glyph.compound?)
|