hexapdf 0.7.0 → 0.8.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 +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
|