hexapdf 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -1
- data/CONTRIBUTERS +1 -1
- data/LICENSE +3 -0
- data/README.md +2 -1
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/examples/{hello_world.rb → 001-hello_world.rb} +0 -0
- data/examples/{graphics.rb → 002-graphics.rb} +1 -1
- data/examples/{arc.rb → 003-arcs.rb} +2 -2
- data/examples/{optimizing.rb → 004-optimizing.rb} +0 -0
- data/examples/{merging.rb → 005-merging.rb} +0 -0
- data/examples/{standard_pdf_fonts.rb → 006-standard_pdf_fonts.rb} +0 -0
- data/examples/{truetype.rb → 007-truetype.rb} +0 -0
- data/examples/{show_char_bboxes.rb → 008-show_char_bboxes.rb} +0 -0
- data/examples/{text_layouter_alignment.rb → 009-text_layouter_alignment.rb} +3 -3
- data/examples/{text_layouter_inline_boxes.rb → 010-text_layouter_inline_boxes.rb} +7 -9
- data/examples/{text_layouter_line_wrapping.rb → 011-text_layouter_line_wrapping.rb} +6 -5
- data/examples/{text_layouter_styling.rb → 012-text_layouter_styling.rb} +6 -8
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/{boxes.rb → 015-boxes.rb} +29 -21
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/lib/hexapdf/cli/command.rb +4 -3
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +0 -1
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/configuration.rb +2 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/graphic_object.rb +1 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +132 -0
- data/lib/hexapdf/dictionary.rb +7 -1
- data/lib/hexapdf/dictionary_fields.rb +35 -83
- data/lib/hexapdf/document.rb +9 -5
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +2 -2
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/layout.rb +3 -0
- data/lib/hexapdf/layout/box.rb +64 -46
- data/lib/hexapdf/layout/frame.rb +348 -0
- data/lib/hexapdf/layout/inline_box.rb +2 -2
- data/lib/hexapdf/layout/line.rb +3 -3
- data/lib/hexapdf/layout/style.rb +81 -14
- data/lib/hexapdf/layout/text_box.rb +84 -0
- data/lib/hexapdf/layout/text_fragment.rb +8 -8
- data/lib/hexapdf/layout/text_layouter.rb +278 -169
- data/lib/hexapdf/layout/width_from_polygon.rb +246 -0
- data/lib/hexapdf/rectangle.rb +9 -9
- data/lib/hexapdf/stream.rb +2 -2
- data/lib/hexapdf/type.rb +1 -0
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +2 -1
- data/lib/hexapdf/type/font.rb +0 -1
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +3 -3
- data/lib/hexapdf/type/font_true_type.rb +8 -0
- data/lib/hexapdf/type/font_type0.rb +2 -1
- data/lib/hexapdf/type/font_type1.rb +7 -1
- data/lib/hexapdf/type/font_type3.rb +61 -0
- data/lib/hexapdf/type/graphics_state_parameter.rb +8 -8
- data/lib/hexapdf/type/image.rb +10 -0
- data/lib/hexapdf/type/page.rb +83 -10
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +1 -1
- data/test/hexapdf/font/test_true_type_wrapper.rb +1 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_directory.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_head.rb +7 -3
- data/test/hexapdf/layout/test_box.rb +57 -15
- data/test/hexapdf/layout/test_frame.rb +313 -0
- data/test/hexapdf/layout/test_inline_box.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +74 -0
- data/test/hexapdf/layout/test_text_box.rb +77 -0
- data/test/hexapdf/layout/test_text_layouter.rb +220 -239
- data/test/hexapdf/layout/test_width_from_polygon.rb +108 -0
- data/test/hexapdf/test_dictionary_fields.rb +22 -26
- data/test/hexapdf/test_document.rb +3 -3
- data/test/hexapdf/test_reference.rb +1 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_font_true_type.rb +25 -0
- data/test/hexapdf/type/test_font_type1.rb +6 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_image.rb +10 -0
- data/test/hexapdf/type/test_page.rb +114 -0
- data/test/test_helper.rb +1 -1
- metadata +65 -17
- data/examples/text_layouter_shapes.rb +0 -170
@@ -0,0 +1,108 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/layout/width_from_polygon'
|
5
|
+
|
6
|
+
describe HexaPDF::Layout::WidthFromPolygon do
|
7
|
+
def create_width_spec(polygon, offset = 0)
|
8
|
+
HexaPDF::Layout::WidthFromPolygon.new(polygon, offset)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "respects the offset" do
|
12
|
+
ws = create_width_spec(Geom2D::Polygon([0, 0], [0, 10], [10, 5]), 5)
|
13
|
+
assert_equal([0, 8], ws.call(0, 1))
|
14
|
+
end
|
15
|
+
|
16
|
+
it "works in the case bottom and top line are the same" do
|
17
|
+
ws = create_width_spec(Geom2D::Polygon([0, 0], [0, 10], [10, 5]))
|
18
|
+
assert_equal([0, 0], ws.call(0, 0))
|
19
|
+
assert_equal([0, 0], ws.call(5, 0))
|
20
|
+
end
|
21
|
+
|
22
|
+
it "works when the first segment has not the minimal x-value" do
|
23
|
+
ws = create_width_spec(Geom2D::Polygon([10, 10], [10, 0], [0, 0], [5, 10]))
|
24
|
+
assert_equal([5, 5], ws.call(0, 1))
|
25
|
+
assert_equal([2.5, 7.5], ws.call(5, 1))
|
26
|
+
end
|
27
|
+
|
28
|
+
it "works when the polygon is specified in counterclockwise order" do
|
29
|
+
ws = create_width_spec(Geom2D::Polygon([10, 10], [5, 10], [0, 0], [10, 0]))
|
30
|
+
assert_equal([5, 5], ws.call(0, 1))
|
31
|
+
assert_equal([2.5, 7.5], ws.call(5, 1))
|
32
|
+
end
|
33
|
+
|
34
|
+
it "works if some segments only cross the top line" do
|
35
|
+
ws = create_width_spec(Geom2D::Polygon([0, 0], [0, 10], [2, 11], [4, 9], [6, 11], [10, 10],
|
36
|
+
[10, 0]))
|
37
|
+
assert_equal([0, 3, 2, 5], ws.call(1, 2))
|
38
|
+
end
|
39
|
+
|
40
|
+
it "works if some segments only cross the bottom line" do
|
41
|
+
ws = create_width_spec(Geom2D::Polygon([0, 0], [0, 10], [2, 4], [4, 6], [6, 4], [10, 10],
|
42
|
+
[10, 0]))
|
43
|
+
assert_equal([0, 1, 7, 2], ws.call(3, 2))
|
44
|
+
end
|
45
|
+
|
46
|
+
it "works if some non-horizontal segments don't cross the top/bottom line at all" do
|
47
|
+
ws = create_width_spec(Geom2D::Polygon([0, 0], [0, 10], [2, 4], [4, 6.5], [6, 6], [10, 10],
|
48
|
+
[10, 0]))
|
49
|
+
assert_equal([0, 1, 6, 3], ws.call(3, 2))
|
50
|
+
end
|
51
|
+
|
52
|
+
it "works if there is no available space" do
|
53
|
+
ws = create_width_spec(Geom2D::Polygon([0, 0], [0, 10], [5, 9], [10, 10], [10, 0]))
|
54
|
+
assert_equal([0, 0], ws.call(0, 2))
|
55
|
+
end
|
56
|
+
|
57
|
+
it "works if the first processed segment doesn't cross both lines" do
|
58
|
+
ws = create_width_spec(Geom2D::Polygon([0, 5], [0, 0], [10, 0], [10, 10], [5, 10], [5, 5]))
|
59
|
+
assert_equal([5, 5], ws.call(4, 2))
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "multiple polygons" do
|
63
|
+
it "rectangle in rectangle" do
|
64
|
+
ws = create_width_spec(Geom2D::PolygonSet(Geom2D::Polygon([0, 0], [0, 10], [10, 10], [10, 0]),
|
65
|
+
Geom2D::Polygon([2, 2], [2, 8], [8, 8], [8, 2])))
|
66
|
+
assert_equal([0, 2, 6, 2], ws.call(1, 8))
|
67
|
+
assert_equal([0, 10], ws.call(0, 2))
|
68
|
+
assert_equal([0, 2, 6, 2], ws.call(2, 1))
|
69
|
+
assert_equal([0, 2, 6, 2], ws.call(7, 2))
|
70
|
+
end
|
71
|
+
|
72
|
+
it "rectangle in rectangle with reverse direction" do
|
73
|
+
ws = create_width_spec(Geom2D::PolygonSet(Geom2D::Polygon([0, 0], [0, 10], [10, 10], [10, 0]),
|
74
|
+
Geom2D::Polygon([2, 8], [2, 2], [8, 2], [8, 8])))
|
75
|
+
assert_equal([0, 2, 6, 2], ws.call(7, 2))
|
76
|
+
assert_equal([0, 2, 6, 2], ws.call(1, 8))
|
77
|
+
assert_equal([0, 10], ws.call(0, 2))
|
78
|
+
assert_equal([0, 2, 6, 2], ws.call(2, 1))
|
79
|
+
end
|
80
|
+
|
81
|
+
it "first segment of inner polygon is between the lines, polygon crosses both lines" do
|
82
|
+
ws = create_width_spec(Geom2D::PolygonSet(Geom2D::Polygon([0, 0], [0, 10], [10, 10], [10, 0]),
|
83
|
+
Geom2D::Polygon([2, 4], [2, 6], [8, 8], [8, 2])))
|
84
|
+
assert_equal([0, 10], ws.call(0, 2))
|
85
|
+
assert_equal([0, 5, 3, 2], ws.call(2, 1).map {|f| f.round(5) })
|
86
|
+
assert_equal([0, 2, 6, 2], ws.call(3, 4))
|
87
|
+
end
|
88
|
+
|
89
|
+
it "first segment of inner polygon is between the lines, polygon crosses one line" do
|
90
|
+
ws = create_width_spec(Geom2D::PolygonSet(Geom2D::Polygon([0, 0], [0, 10], [10, 10], [10, 0]),
|
91
|
+
Geom2D::Polygon([2, 4], [4, 6], [8, 2])))
|
92
|
+
assert_equal([0, 2, 5, 3], ws.call(3, 4))
|
93
|
+
end
|
94
|
+
|
95
|
+
it "polygon is partly between the lines, maximum between the lines" do
|
96
|
+
ws = create_width_spec(Geom2D::PolygonSet(Geom2D::Polygon([0, 0], [0, 10], [10, 10], [10, 0]),
|
97
|
+
Geom2D::Polygon([2, 4], [2, 6], [8, 8], [9, 5],
|
98
|
+
[8, 2])))
|
99
|
+
assert_equal([0, 2, 7, 1], ws.call(3, 4))
|
100
|
+
end
|
101
|
+
|
102
|
+
it "polygon is partly between the lines, maximum is at an line crossing" do
|
103
|
+
ws = create_width_spec(Geom2D::PolygonSet(Geom2D::Polygon([0, 0], [0, 10], [10, 10], [10, 0]),
|
104
|
+
Geom2D::Polygon([2, 4], [8, 8], [5, 5], [8, 2])))
|
105
|
+
assert_equal([0, 2, 5, 3], ws.call(3, 4))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -10,7 +10,7 @@ describe HexaPDF::DictionaryFields do
|
|
10
10
|
|
11
11
|
describe "Field" do
|
12
12
|
before do
|
13
|
-
@field = self.class::Field.new([:Integer,
|
13
|
+
@field = self.class::Field.new([:Integer, self.class::PDFByteString], true, 500, false, '1.2')
|
14
14
|
HexaPDF::GlobalConfiguration['object.type_map'][:Integer] = Integer
|
15
15
|
end
|
16
16
|
|
@@ -27,22 +27,26 @@ describe HexaPDF::DictionaryFields do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it "maps string types to constants" do
|
30
|
-
assert_equal([Integer], @field.type)
|
30
|
+
assert_equal([Integer, self.class::PDFByteString, Hash, String], @field.type)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
describe "convert" do
|
34
|
+
it "returns the converted object, using the first usable converter" do
|
35
|
+
doc = Minitest::Mock.new
|
36
|
+
doc.expect(:wrap, :data, [Hash, Hash])
|
37
|
+
@field.convert({}, doc)
|
38
|
+
doc.verify
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
assert(@field.convert('str', self).encoding == Encoding::BINARY)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns nil for unconvertable objects" do
|
44
|
+
assert_nil(@field.convert(5.5, self))
|
45
|
+
end
|
42
46
|
end
|
43
47
|
|
44
48
|
it "can check for a valid object" do
|
45
|
-
refute(@field.valid_object?(
|
49
|
+
refute(@field.valid_object?(5.5))
|
46
50
|
assert(@field.valid_object?(5))
|
47
51
|
assert(@field.valid_object?(HexaPDF::Object.new(5)))
|
48
52
|
end
|
@@ -59,18 +63,20 @@ describe HexaPDF::DictionaryFields do
|
|
59
63
|
end
|
60
64
|
|
61
65
|
it "allows conversion from a hash" do
|
62
|
-
assert(@field.convert?({}))
|
63
66
|
@doc.expect(:wrap, :data, [Hash, Hash])
|
64
67
|
@field.convert({Test: :value}, @doc)
|
65
68
|
@doc.verify
|
66
69
|
end
|
67
70
|
|
68
71
|
it "allows conversion from a Dictionary" do
|
69
|
-
assert(@field.convert?(HexaPDF::Dictionary.new({})))
|
70
72
|
@doc.expect(:wrap, :data, [HexaPDF::Dictionary, Hash])
|
71
73
|
@field.convert(HexaPDF::Dictionary.new(Test: :value), @doc)
|
72
74
|
@doc.verify
|
73
75
|
end
|
76
|
+
|
77
|
+
it "doesn't allow conversion from nil" do
|
78
|
+
refute(@field.convert(nil, @doc))
|
79
|
+
end
|
74
80
|
end
|
75
81
|
|
76
82
|
describe "StringConverter" do
|
@@ -79,7 +85,7 @@ describe HexaPDF::DictionaryFields do
|
|
79
85
|
end
|
80
86
|
|
81
87
|
it "allows conversion to UTF-8 string from binary" do
|
82
|
-
|
88
|
+
refute(@field.convert("test", self))
|
83
89
|
|
84
90
|
str = @field.convert("\xfe\xff\x00t\x00e\x00s\x00t".b, self)
|
85
91
|
assert_equal('test', str)
|
@@ -100,8 +106,7 @@ describe HexaPDF::DictionaryFields do
|
|
100
106
|
end
|
101
107
|
|
102
108
|
it "allows conversion to a binary string" do
|
103
|
-
|
104
|
-
refute(@field.convert?('test'.b))
|
109
|
+
refute(@field.convert('test'.b, self))
|
105
110
|
|
106
111
|
str = @field.convert("test", self)
|
107
112
|
assert_equal('test', str)
|
@@ -119,9 +124,7 @@ describe HexaPDF::DictionaryFields do
|
|
119
124
|
end
|
120
125
|
|
121
126
|
it "allows conversion to a Time object from a binary string" do
|
122
|
-
|
123
|
-
refute(@field.convert?('test'.b))
|
124
|
-
assert(@field.convert?(date))
|
127
|
+
refute(@field.convert('test'.b, self))
|
125
128
|
|
126
129
|
[
|
127
130
|
["D:1998", [1998, 01, 01, 00, 00, 00, "-00:00"]],
|
@@ -155,8 +158,6 @@ describe HexaPDF::DictionaryFields do
|
|
155
158
|
end
|
156
159
|
|
157
160
|
it "allows conversion from a string" do
|
158
|
-
assert(@field.convert?("test"))
|
159
|
-
|
160
161
|
@doc = Minitest::Mock.new
|
161
162
|
@doc.expect(:wrap, :data, [{F: 'test'}, {type: HexaPDF::Type::FileSpecification}])
|
162
163
|
@field.convert('test', @doc)
|
@@ -164,8 +165,6 @@ describe HexaPDF::DictionaryFields do
|
|
164
165
|
end
|
165
166
|
|
166
167
|
it "allows conversion from a hash/dictionary" do
|
167
|
-
assert(@field.convert?({}))
|
168
|
-
|
169
168
|
@doc = Minitest::Mock.new
|
170
169
|
@doc.expect(:wrap, :data, [{F: 'test'}, {type: HexaPDF::Type::FileSpecification}])
|
171
170
|
@field.convert({F: 'test'}, @doc)
|
@@ -183,9 +182,6 @@ describe HexaPDF::DictionaryFields do
|
|
183
182
|
end
|
184
183
|
|
185
184
|
it "allows conversion to a Rectangle from an Array" do
|
186
|
-
assert(@field.convert?([5, 6]))
|
187
|
-
refute(@field.convert?(:name))
|
188
|
-
|
189
185
|
doc = Minitest::Mock.new
|
190
186
|
doc.expect(:wrap, :data, [[0, 1, 2, 3], type: HexaPDF::Rectangle])
|
191
187
|
@field.convert([0, 1, 2, 3], doc)
|
@@ -436,17 +436,17 @@ describe HexaPDF::Document do
|
|
436
436
|
end
|
437
437
|
|
438
438
|
it "validates indirect objects" do
|
439
|
-
@doc.add(Type: :Catalog)
|
439
|
+
obj = @doc.add(Type: :Catalog)
|
440
440
|
refute(@doc.validate(auto_correct: false))
|
441
441
|
|
442
442
|
called = false
|
443
|
-
assert(@doc.validate { called = true })
|
443
|
+
assert(@doc.validate {|o| assert_same(obj, o); called = true })
|
444
444
|
assert(called)
|
445
445
|
end
|
446
446
|
|
447
447
|
it "validates the trailer object" do
|
448
448
|
@doc.trailer[:ID] = :Symbol
|
449
|
-
refute(@doc.validate)
|
449
|
+
refute(@doc.validate {|obj| assert_same(@doc.trailer, obj) })
|
450
450
|
end
|
451
451
|
end
|
452
452
|
|
@@ -19,6 +19,7 @@ describe HexaPDF::Reference do
|
|
19
19
|
obj = Object.new
|
20
20
|
obj.define_singleton_method(:oid) { 1 }
|
21
21
|
obj.define_singleton_method(:gen) { 0 }
|
22
|
+
obj.define_singleton_method(:<=>) {|o| HexaPDF::Reference.new(oid, gen) <=> o }
|
22
23
|
assert_equal([obj, HexaPDF::Reference.new(1, 1), HexaPDF::Reference.new(5, 7)],
|
23
24
|
[HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(1, 1), obj].sort)
|
24
25
|
assert_nil(HexaPDF::Reference.new(1, 0) <=> 5)
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
40
40
|
219
|
41
41
|
%%EOF
|
42
42
|
3 0 obj
|
43
|
-
<</Producer(HexaPDF version 0.
|
43
|
+
<</Producer(HexaPDF version 0.8.0)>>
|
44
44
|
endobj
|
45
45
|
xref
|
46
46
|
3 1
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
72
72
|
141
|
73
73
|
%%EOF
|
74
74
|
6 0 obj
|
75
|
-
<</Producer(HexaPDF version 0.
|
75
|
+
<</Producer(HexaPDF version 0.8.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/font_true_type'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::FontTrueType do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
font_descriptor = @doc.add(Type: :FontDescriptor, FontName: :Something, Flags: 0b100,
|
11
|
+
FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
|
12
|
+
Descent: -100, CapHeight: 800, StemV: 20)
|
13
|
+
@font = @doc.add(Type: :Font, Subtype: :TrueType, Encoding: :WinAnsiEncoding,
|
14
|
+
FirstChar: 32, LastChar: 34, Widths: [600, 0, 700],
|
15
|
+
BaseFont: :Something, FontDescriptor: font_descriptor)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "validation" do
|
19
|
+
it "requires that the FontDescriptor key is set" do
|
20
|
+
assert(@font.validate)
|
21
|
+
@font.delete(:FontDescriptor)
|
22
|
+
refute(@font.validate)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -124,5 +124,11 @@ describe HexaPDF::Type::FontType1 do
|
|
124
124
|
it "allows empty fields for standard fonts" do
|
125
125
|
assert(@font.validate)
|
126
126
|
end
|
127
|
+
|
128
|
+
it "requires that the FontDescriptor key is set for non-standard fonts" do
|
129
|
+
assert(@embedded_font.validate)
|
130
|
+
@embedded_font.delete(:FontDescriptor)
|
131
|
+
refute(@embedded_font.validate)
|
132
|
+
end
|
127
133
|
end
|
128
134
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/document'
|
5
|
+
require 'hexapdf/type/font_type3'
|
6
|
+
|
7
|
+
describe HexaPDF::Type::FontType3 do
|
8
|
+
before do
|
9
|
+
@doc = HexaPDF::Document.new
|
10
|
+
@font = @doc.add(Type: :Font, Subtype: :Type3, Encoding: :WinAnsiEncoding,
|
11
|
+
FirstChar: 32, LastChar: 34, Widths: [600, 0, 700],
|
12
|
+
FontBBox: [0, 0, 100, 100], FontMatrix: [1, 0, 0, 1, 0, 0],
|
13
|
+
CharProcs: {})
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "validation" do
|
17
|
+
it "works for valid objects" do
|
18
|
+
assert(@font.validate)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "fails if the Encoding key is missing" do
|
22
|
+
@font.delete(:Encoding)
|
23
|
+
refute(@font.validate)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -11,6 +11,16 @@ describe HexaPDF::Type::Image do
|
|
11
11
|
@doc = HexaPDF::Document.new
|
12
12
|
end
|
13
13
|
|
14
|
+
it "returns the width of the image" do
|
15
|
+
@image = @doc.wrap(Subtype: :Image, Width: 10)
|
16
|
+
assert_equal(10, @image.width)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns the height of the image" do
|
20
|
+
@image = @doc.wrap(Subtype: :Image, Height: 10)
|
21
|
+
assert_equal(10, @image.height)
|
22
|
+
end
|
23
|
+
|
14
24
|
describe "info" do
|
15
25
|
before do
|
16
26
|
@image = @doc.wrap(Subtype: :Image, Width: 10, Height: 5, ColorSpace: :DeviceRGB,
|
@@ -118,6 +118,120 @@ describe HexaPDF::Type::Page do
|
|
118
118
|
it "fails if an unknown box type is supplied" do
|
119
119
|
assert_raises(ArgumentError) { @page.box(:undefined) }
|
120
120
|
end
|
121
|
+
|
122
|
+
it "sets the correct box" do
|
123
|
+
@page.box(:media, :media)
|
124
|
+
assert_equal(:media, @page.box(:media))
|
125
|
+
@page.box(:crop, :crop)
|
126
|
+
assert_equal(:crop, @page.box(:crop))
|
127
|
+
@page.box(:bleed, :bleed)
|
128
|
+
assert_equal(:bleed, @page.box(:bleed))
|
129
|
+
@page.box(:trim, :trim)
|
130
|
+
assert_equal(:trim, @page.box(:trim))
|
131
|
+
@page.box(:art, :art)
|
132
|
+
assert_equal(:art, @page.box(:art))
|
133
|
+
end
|
134
|
+
|
135
|
+
it "fails if an unknown box type is supplied when setting a box" do
|
136
|
+
assert_raises(ArgumentError) { @page.box(:undefined, [1, 2, 3, 4]) }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "orientation" do
|
141
|
+
before do
|
142
|
+
@page = @doc.pages.add
|
143
|
+
end
|
144
|
+
|
145
|
+
it "returns :portrait for appropriate media boxes and rotation values" do
|
146
|
+
@page.box(:media, [0, 0, 100, 300])
|
147
|
+
assert_equal(:portrait, @page.orientation)
|
148
|
+
@page[:Rotate] = 0
|
149
|
+
assert_equal(:portrait, @page.orientation)
|
150
|
+
@page[:Rotate] = 180
|
151
|
+
assert_equal(:portrait, @page.orientation)
|
152
|
+
|
153
|
+
@page.box(:media, [0, 0, 300, 100])
|
154
|
+
@page[:Rotate] = 90
|
155
|
+
assert_equal(:portrait, @page.orientation)
|
156
|
+
@page[:Rotate] = 270
|
157
|
+
assert_equal(:portrait, @page.orientation)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "returns :landscape for appropriate media boxes and rotation values" do
|
161
|
+
@page.box(:media, [0, 0, 300, 100])
|
162
|
+
assert_equal(:landscape, @page.orientation)
|
163
|
+
@page[:Rotate] = 0
|
164
|
+
assert_equal(:landscape, @page.orientation)
|
165
|
+
@page[:Rotate] = 180
|
166
|
+
assert_equal(:landscape, @page.orientation)
|
167
|
+
|
168
|
+
@page.box(:media, [0, 0, 100, 300])
|
169
|
+
@page[:Rotate] = 90
|
170
|
+
assert_equal(:landscape, @page.orientation)
|
171
|
+
@page[:Rotate] = 270
|
172
|
+
assert_equal(:landscape, @page.orientation)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "rotate" do
|
177
|
+
before do
|
178
|
+
@page = @doc.pages.add
|
179
|
+
reset_media_box
|
180
|
+
end
|
181
|
+
|
182
|
+
def reset_media_box
|
183
|
+
@page.box(:media, [50, 100, 200, 300])
|
184
|
+
end
|
185
|
+
|
186
|
+
it "works directly on the :Rotate key" do
|
187
|
+
@page.rotate(90)
|
188
|
+
assert_equal(270, @page[:Rotate])
|
189
|
+
|
190
|
+
@page.rotate(180)
|
191
|
+
assert_equal(90, @page[:Rotate])
|
192
|
+
|
193
|
+
@page.rotate(-90)
|
194
|
+
assert_equal(180, @page[:Rotate])
|
195
|
+
end
|
196
|
+
|
197
|
+
describe "flatten" do
|
198
|
+
it "adjust all page boxes" do
|
199
|
+
@page.box(:crop, @page.box)
|
200
|
+
@page.box(:bleed, @page.box)
|
201
|
+
@page.box(:trim, @page.box)
|
202
|
+
@page.box(:art, @page.box)
|
203
|
+
|
204
|
+
@page.rotate(90, flatten: true)
|
205
|
+
box = [-300, 50, -100, 200]
|
206
|
+
assert_equal(box, @page.box(:media).value)
|
207
|
+
assert_equal(box, @page.box(:crop).value)
|
208
|
+
assert_equal(box, @page.box(:bleed).value)
|
209
|
+
assert_equal(box, @page.box(:trim).value)
|
210
|
+
assert_equal(box, @page.box(:art).value)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "works correctly for 90 degrees" do
|
214
|
+
@page.rotate(90, flatten: true)
|
215
|
+
assert_equal([-300, 50, -100, 200], @page.box(:media).value)
|
216
|
+
assert_equal(" q 0 1 -1 0 0 0 cm Q ", @page.contents)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "works correctly for 180 degrees" do
|
220
|
+
@page.rotate(180, flatten: true)
|
221
|
+
assert_equal([-200, -300, -50, -100], @page.box(:media).value)
|
222
|
+
assert_equal(" q -1 0 0 -1 0 0 cm Q ", @page.contents)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "works correctly for 270 degrees" do
|
226
|
+
@page.rotate(270, flatten: true)
|
227
|
+
assert_equal([100, -200, 300, -50], @page.box(:media).value)
|
228
|
+
assert_equal(" q 0 -1 1 0 0 0 cm Q ", @page.contents)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
it "fails if the angle is not a multiple of 90" do
|
233
|
+
assert_raises(ArgumentError) { @page.rotate(27) }
|
234
|
+
end
|
121
235
|
end
|
122
236
|
|
123
237
|
describe "contents" do
|