hexapdf 0.5.0 → 0.6.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 +76 -2
- data/CONTRIBUTERS +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/boxes.rb +68 -0
- data/examples/graphics.rb +12 -12
- data/examples/{text_box_alignment.rb → text_layouter_alignment.rb} +14 -14
- data/examples/text_layouter_inline_boxes.rb +66 -0
- data/examples/{text_box_line_wrapping.rb → text_layouter_line_wrapping.rb} +9 -10
- data/examples/{text_box_shapes.rb → text_layouter_shapes.rb} +58 -54
- data/examples/text_layouter_styling.rb +125 -0
- data/examples/truetype.rb +5 -7
- data/lib/hexapdf/cli/command.rb +1 -0
- data/lib/hexapdf/configuration.rb +170 -106
- data/lib/hexapdf/content/canvas.rb +41 -36
- data/lib/hexapdf/content/graphics_state.rb +15 -0
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/dictionary.rb +20 -8
- data/lib/hexapdf/dictionary_fields.rb +8 -6
- data/lib/hexapdf/document.rb +25 -26
- data/lib/hexapdf/document/fonts.rb +4 -4
- data/lib/hexapdf/document/images.rb +2 -2
- data/lib/hexapdf/document/pages.rb +16 -16
- data/lib/hexapdf/encryption/security_handler.rb +41 -9
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +7 -1
- data/lib/hexapdf/font/true_type/font.rb +20 -0
- data/lib/hexapdf/font/type1/font.rb +23 -0
- data/lib/hexapdf/font_loader.rb +1 -0
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -3
- data/lib/hexapdf/font_loader/from_file.rb +65 -0
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/layout.rb +3 -2
- data/lib/hexapdf/layout/box.rb +146 -0
- data/lib/hexapdf/layout/inline_box.rb +40 -31
- data/lib/hexapdf/layout/{line_fragment.rb → line.rb} +12 -13
- data/lib/hexapdf/layout/style.rb +630 -41
- data/lib/hexapdf/layout/text_fragment.rb +80 -12
- data/lib/hexapdf/layout/{text_box.rb → text_layouter.rb} +164 -109
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/parser.rb +4 -1
- data/lib/hexapdf/revisions.rb +11 -4
- data/lib/hexapdf/stream.rb +8 -9
- data/lib/hexapdf/tokenizer.rb +5 -3
- data/lib/hexapdf/type.rb +3 -0
- data/lib/hexapdf/type/action.rb +56 -0
- data/lib/hexapdf/type/actions.rb +52 -0
- data/lib/hexapdf/type/actions/go_to.rb +52 -0
- data/lib/hexapdf/type/actions/go_to_r.rb +54 -0
- data/lib/hexapdf/type/actions/launch.rb +73 -0
- data/lib/hexapdf/type/actions/uri.rb +65 -0
- data/lib/hexapdf/type/annotation.rb +85 -0
- data/lib/hexapdf/type/annotations.rb +51 -0
- data/lib/hexapdf/type/annotations/link.rb +70 -0
- data/lib/hexapdf/type/annotations/markup_annotation.rb +70 -0
- data/lib/hexapdf/type/annotations/text.rb +81 -0
- data/lib/hexapdf/type/catalog.rb +3 -1
- data/lib/hexapdf/type/embedded_file.rb +6 -11
- data/lib/hexapdf/type/file_specification.rb +4 -6
- data/lib/hexapdf/type/font.rb +3 -1
- data/lib/hexapdf/type/font_descriptor.rb +18 -16
- data/lib/hexapdf/type/form.rb +3 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +3 -1
- data/lib/hexapdf/type/image.rb +4 -2
- data/lib/hexapdf/type/info.rb +2 -5
- data/lib/hexapdf/type/names.rb +2 -5
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/page.rb +14 -1
- data/lib/hexapdf/type/page_tree_node.rb +9 -6
- data/lib/hexapdf/type/resources.rb +2 -5
- data/lib/hexapdf/type/trailer.rb +2 -5
- data/lib/hexapdf/type/viewer_preferences.rb +2 -5
- data/lib/hexapdf/type/xref_stream.rb +3 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +3 -1
- data/test/hexapdf/content/test_canvas.rb +29 -3
- data/test/hexapdf/content/test_graphics_state.rb +11 -0
- data/test/hexapdf/content/test_operator.rb +3 -2
- data/test/hexapdf/document/test_fonts.rb +8 -8
- data/test/hexapdf/document/test_images.rb +4 -12
- data/test/hexapdf/document/test_pages.rb +7 -7
- data/test/hexapdf/encryption/test_security_handler.rb +1 -5
- data/test/hexapdf/filter/test_predictor.rb +40 -12
- data/test/hexapdf/font/true_type/test_font.rb +16 -0
- data/test/hexapdf/font/type1/test_font.rb +30 -0
- data/test/hexapdf/font_loader/test_from_file.rb +29 -0
- data/test/hexapdf/font_loader/test_standard14.rb +4 -3
- data/test/hexapdf/layout/test_box.rb +104 -0
- data/test/hexapdf/layout/test_inline_box.rb +24 -10
- data/test/hexapdf/layout/{test_line_fragment.rb → test_line.rb} +9 -9
- data/test/hexapdf/layout/test_style.rb +519 -31
- data/test/hexapdf/layout/test_text_fragment.rb +136 -15
- data/test/hexapdf/layout/{test_text_box.rb → test_text_layouter.rb} +224 -144
- data/test/hexapdf/layout/test_text_shaper.rb +1 -1
- data/test/hexapdf/test_configuration.rb +12 -6
- data/test/hexapdf/test_dictionary.rb +27 -2
- data/test/hexapdf/test_dictionary_fields.rb +10 -1
- data/test/hexapdf/test_document.rb +14 -13
- data/test/hexapdf/test_parser.rb +12 -0
- data/test/hexapdf/test_revisions.rb +34 -0
- data/test/hexapdf/test_stream.rb +1 -1
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_link.rb +19 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +38 -0
- data/test/hexapdf/type/test_annotation.rb +38 -0
- data/test/hexapdf/type/test_file_specification.rb +0 -7
- data/test/hexapdf/type/test_info.rb +0 -5
- data/test/hexapdf/type/test_page.rb +14 -0
- data/test/hexapdf/type/test_page_tree_node.rb +4 -1
- data/test/hexapdf/type/test_trailer.rb +0 -4
- data/test/test_helper.rb +6 -3
- metadata +36 -15
- data/examples/text_box_inline_boxes.rb +0 -56
- data/examples/text_box_styling.rb +0 -72
- data/test/hexapdf/type/test_embedded_file.rb +0 -16
- data/test/hexapdf/type/test_names.rb +0 -9
|
@@ -18,11 +18,7 @@ describe HexaPDF::Document::Images do
|
|
|
18
18
|
s = HexaPDF::StreamData.new(s) if s.kind_of?(IO)
|
|
19
19
|
doc.add({Subtype: :Image}, stream: s)
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
after do
|
|
25
|
-
HexaPDF::GlobalConfiguration['image_loader'].delete(@loader)
|
|
21
|
+
@doc.config['image_loader'].unshift(@loader)
|
|
26
22
|
end
|
|
27
23
|
|
|
28
24
|
it "adds an image using a filename" do
|
|
@@ -49,13 +45,9 @@ describe HexaPDF::Document::Images do
|
|
|
49
45
|
end
|
|
50
46
|
|
|
51
47
|
it "fails if the needed image loader can't be resolved" do
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
assert_match(/image loader from configuration/, exp.message)
|
|
56
|
-
ensure
|
|
57
|
-
HexaPDF::GlobalConfiguration['image_loader'].shift
|
|
58
|
-
end
|
|
48
|
+
@doc.config['image_loader'].unshift('SomeUnknownConstantHere')
|
|
49
|
+
exp = assert_raises(HexaPDF::Error) { @doc.images.add(StringIO.new('test')) }
|
|
50
|
+
assert_match(/image loader from configuration/, exp.message)
|
|
59
51
|
end
|
|
60
52
|
|
|
61
53
|
it "fails if no image loader is found" do
|
|
@@ -27,21 +27,21 @@ describe HexaPDF::Document::Pages do
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it "adds a new empty page with the given page format" do
|
|
30
|
-
page = @doc.pages.add(:A4)
|
|
30
|
+
page = @doc.pages.add(:A4, orientation: :landscape)
|
|
31
31
|
assert_same(page, @doc.pages[0])
|
|
32
|
-
assert_equal([0, 0,
|
|
32
|
+
assert_equal([0, 0, 842, 595], @doc.pages[0].box(:media).value)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
it "
|
|
36
|
-
assert_raises(HexaPDF::Error) { @doc.pages.add(:A953) }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it "adds a given page to the end" do
|
|
35
|
+
it "adds the given page to the end" do
|
|
40
36
|
page = @doc.pages.add
|
|
41
37
|
new_page = @doc.add(Type: :Page)
|
|
42
38
|
assert_same(new_page, @doc.pages.add(new_page))
|
|
43
39
|
assert_equal([page, new_page], @doc.pages.root[:Kids])
|
|
44
40
|
end
|
|
41
|
+
|
|
42
|
+
it "fails if an unknown page format is given" do
|
|
43
|
+
assert_raises(HexaPDF::Error) { @doc.pages.add(:A953) }
|
|
44
|
+
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
describe "<<" do
|
|
@@ -67,11 +67,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
|
67
67
|
|
|
68
68
|
describe "class methods" do
|
|
69
69
|
before do
|
|
70
|
-
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
after do
|
|
74
|
-
HexaPDF::GlobalConfiguration['encryption.filter_map'].delete(:Test)
|
|
70
|
+
@document.config['encryption.filter_map'][:Test] = TestHandler
|
|
75
71
|
end
|
|
76
72
|
|
|
77
73
|
describe "class set_up_encryption" do
|
|
@@ -88,12 +88,26 @@ describe HexaPDF::Filter::Predictor do
|
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
it "
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
it "handles a short last row if 'filter.predictor.strict' is false" do
|
|
92
|
+
data = @testcases['up']
|
|
93
|
+
encoder = @obj.png_execute(:encoder, feeder(data[:source][0..-3], 1), data[:Predictor],
|
|
94
|
+
data[:Colors], data[:BitsPerComponent], data[:Columns])
|
|
95
|
+
result = collector(encoder)
|
|
96
|
+
assert_equal(1 + 5 + 1 + 3, result.length)
|
|
97
|
+
assert_equal(data[:result][0..-3], result)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "fails if the last row is missing data and 'filter.predictor.strict' is true " do
|
|
101
|
+
begin
|
|
102
|
+
HexaPDF::GlobalConfiguration['filter.predictor.strict'] = true
|
|
103
|
+
assert_raises(HexaPDF::FilterError) do
|
|
104
|
+
data = @testcases['up']
|
|
105
|
+
encoder = @obj.png_execute(:encoder, feeder(data[:source][0..-2], 1), data[:Predictor],
|
|
106
|
+
data[:Colors], data[:BitsPerComponent], data[:Columns])
|
|
107
|
+
collector(encoder)
|
|
108
|
+
end
|
|
109
|
+
ensure
|
|
110
|
+
HexaPDF::GlobalConfiguration['filter.predictor.strict'] = false
|
|
97
111
|
end
|
|
98
112
|
end
|
|
99
113
|
end
|
|
@@ -107,12 +121,26 @@ describe HexaPDF::Filter::Predictor do
|
|
|
107
121
|
end
|
|
108
122
|
end
|
|
109
123
|
|
|
110
|
-
it "
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
it "handles a short last row if 'filter.predictor.strict' is false" do
|
|
125
|
+
data = @testcases['up']
|
|
126
|
+
encoder = @obj.png_execute(:decoder, feeder(data[:result][0..-3], 1), data[:Predictor],
|
|
127
|
+
data[:Colors], data[:BitsPerComponent], data[:Columns])
|
|
128
|
+
result = collector(encoder)
|
|
129
|
+
assert_equal(5 + 3, result.length)
|
|
130
|
+
assert_equal(data[:source][0..-3], result)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "fails if the last row is missing data and 'filter.predictor.strict' is true " do
|
|
134
|
+
begin
|
|
135
|
+
HexaPDF::GlobalConfiguration['filter.predictor.strict'] = true
|
|
136
|
+
assert_raises(HexaPDF::FilterError) do
|
|
137
|
+
data = @testcases['up']
|
|
138
|
+
encoder = @obj.png_execute(:decoder, feeder(data[:result][0..-2], 1), data[:Predictor],
|
|
139
|
+
data[:Colors], data[:BitsPerComponent], data[:Columns])
|
|
140
|
+
collector(encoder)
|
|
141
|
+
end
|
|
142
|
+
ensure
|
|
143
|
+
HexaPDF::GlobalConfiguration['filter.predictor.strict'] = false
|
|
116
144
|
end
|
|
117
145
|
end
|
|
118
146
|
end
|
|
@@ -88,6 +88,22 @@ describe HexaPDF::Font::TrueType::Font do
|
|
|
88
88
|
it "returns the font's dominant vertical stem width" do
|
|
89
89
|
assert_equal(80, @font.dominant_vertical_stem_width)
|
|
90
90
|
end
|
|
91
|
+
|
|
92
|
+
it "returns the underline position" do
|
|
93
|
+
assert_equal(-125, @font.underline_position)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "returns the underline thickness" do
|
|
97
|
+
assert_equal(50, @font.underline_thickness)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "returns the strikeout position" do
|
|
101
|
+
assert_equal(256, @font.strikeout_position)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "returns the strikeout thickness" do
|
|
105
|
+
assert_equal(48, @font.strikeout_thickness)
|
|
106
|
+
end
|
|
91
107
|
end
|
|
92
108
|
|
|
93
109
|
it "is able to return the ID of the missing glyph" do
|
|
@@ -71,4 +71,34 @@ describe HexaPDF::Font::Type1::Font do
|
|
|
71
71
|
assert_equal([:kern, :liga].to_set, FONT_TIMES.features)
|
|
72
72
|
assert(FONT_SYMBOL.features.empty?)
|
|
73
73
|
end
|
|
74
|
+
|
|
75
|
+
describe "underline properties" do
|
|
76
|
+
before do
|
|
77
|
+
@font.metrics.underline_position = -100
|
|
78
|
+
@font.metrics.underline_thickness = 50
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "returns the underline position" do
|
|
82
|
+
assert_equal(-75, @font.underline_position)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "returns the underline thickness" do
|
|
86
|
+
assert_equal(50, @font.underline_thickness)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe "strikeout properties" do
|
|
91
|
+
it "returns the strikeout position" do
|
|
92
|
+
assert_equal(225, @font.strikeout_position)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "returns the strikeout thickness" do
|
|
96
|
+
assert_equal(50, @font.strikeout_thickness)
|
|
97
|
+
|
|
98
|
+
emdash = HexaPDF::Font::Type1::CharacterMetrics.new
|
|
99
|
+
emdash.bbox = [0, 200, 1000, 240]
|
|
100
|
+
@font.metrics.character_metrics[:emdash] = emdash
|
|
101
|
+
assert_equal(40, @font.strikeout_thickness)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
74
104
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/font_loader'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::FontLoader::FromFile do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
|
|
11
|
+
@klass = HexaPDF::FontLoader::FromFile
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "loads the specified font file" do
|
|
15
|
+
wrapper = @klass.call(@doc, @font_file)
|
|
16
|
+
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "passes the subset value to the wrapper" do
|
|
20
|
+
wrapper = @klass.call(@doc, @font_file)
|
|
21
|
+
assert(wrapper.subset?)
|
|
22
|
+
wrapper = @klass.call(@doc, @font_file, subset: false)
|
|
23
|
+
refute(wrapper.subset?)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "returns nil if the given name doesn't represent a file" do
|
|
27
|
+
assert_nil(@klass.call(@doc, "Unknown"))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -7,16 +7,17 @@ require 'hexapdf/document'
|
|
|
7
7
|
describe HexaPDF::FontLoader::Standard14 do
|
|
8
8
|
before do
|
|
9
9
|
@doc = HexaPDF::Document.new
|
|
10
|
+
@obj = HexaPDF::FontLoader::Standard14
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
it "loads the font if it is a standard PDF built-in font" do
|
|
13
|
-
wrapper = @
|
|
14
|
+
wrapper = @obj.call(@doc, "Times")
|
|
14
15
|
assert_equal("Times-Roman", wrapper.wrapped_font.font_name)
|
|
15
|
-
wrapper = @
|
|
16
|
+
wrapper = @obj.call(@doc, "Helvetica", variant: :bold)
|
|
16
17
|
assert_equal("Helvetica-Bold", wrapper.wrapped_font.font_name)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
it "returns nil for unknown fonts" do
|
|
20
|
-
assert_nil(
|
|
21
|
+
assert_nil(@obj.call(@doc, "Unknown"))
|
|
21
22
|
end
|
|
22
23
|
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require_relative '../content/common'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/layout/box'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Layout::Box do
|
|
9
|
+
def create_box(*args, &block)
|
|
10
|
+
HexaPDF::Layout::Box.new(*args, &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "initialize" do
|
|
14
|
+
it "takes content width and height" do
|
|
15
|
+
box = create_box(content_width: 100, content_height: 200)
|
|
16
|
+
assert_equal(100, box.content_width)
|
|
17
|
+
assert_equal(200, box.content_height)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "takes box width and height" do
|
|
21
|
+
box = create_box(width: 100, height: 200)
|
|
22
|
+
assert_equal(100, box.content_width)
|
|
23
|
+
assert_equal(200, box.content_height)
|
|
24
|
+
|
|
25
|
+
box = create_box(width: 100, height: 200, style: {padding: [20, 10], border: {width: [10, 5]}})
|
|
26
|
+
assert_equal(70, box.content_width)
|
|
27
|
+
assert_equal(140, box.content_height)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "allows passing a Style object or a hash" do
|
|
31
|
+
box = create_box(style: {padding: 20})
|
|
32
|
+
assert_equal(20, box.style.padding.top)
|
|
33
|
+
|
|
34
|
+
box = create_box(style: HexaPDF::Layout::Style.new(padding: 20))
|
|
35
|
+
assert_equal(20, box.style.padding.top)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "returns the full width and height of the box" do
|
|
40
|
+
box = create_box(content_width: 100, content_height: 200,
|
|
41
|
+
style: {padding: [20, 10], border: {width: [10, 5]}})
|
|
42
|
+
assert_equal(130, box.width)
|
|
43
|
+
assert_equal(260, box.height)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe "draw" do
|
|
47
|
+
it "draws the box onto the canvas" do
|
|
48
|
+
box = create_box(content_width: 100, content_height: 100) do |canvas, _|
|
|
49
|
+
canvas.line_width(15)
|
|
50
|
+
end
|
|
51
|
+
box.style.background_color = 0.5
|
|
52
|
+
box.style.border(width: 5)
|
|
53
|
+
box.style.padding([10, 20])
|
|
54
|
+
box.style.underlays.add {|canvas, _| canvas.line_width(10) }
|
|
55
|
+
box.style.overlays.add {|canvas, _| canvas.line_width(20) }
|
|
56
|
+
|
|
57
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
58
|
+
box.draw(@canvas, 5, 5)
|
|
59
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
60
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
61
|
+
[:append_rectangle, [5, 5, 150, 130]],
|
|
62
|
+
[:fill_path_non_zero],
|
|
63
|
+
[:restore_graphics_state],
|
|
64
|
+
[:save_graphics_state],
|
|
65
|
+
[:concatenate_matrix, [1, 0, 0, 1, 5, 5]],
|
|
66
|
+
[:save_graphics_state],
|
|
67
|
+
[:set_line_width, [10]],
|
|
68
|
+
[:restore_graphics_state],
|
|
69
|
+
[:restore_graphics_state],
|
|
70
|
+
[:save_graphics_state],
|
|
71
|
+
[:set_line_width, [5]],
|
|
72
|
+
[:append_rectangle, [7.5, 7.5, 145, 125]],
|
|
73
|
+
[:stroke_path],
|
|
74
|
+
[:restore_graphics_state],
|
|
75
|
+
[:save_graphics_state],
|
|
76
|
+
[:concatenate_matrix, [1, 0, 0, 1, 30, 20]],
|
|
77
|
+
[:set_line_width, [15]],
|
|
78
|
+
[:restore_graphics_state],
|
|
79
|
+
[:save_graphics_state],
|
|
80
|
+
[:concatenate_matrix, [1, 0, 0, 1, 5, 5]],
|
|
81
|
+
[:save_graphics_state],
|
|
82
|
+
[:set_line_width, [20]],
|
|
83
|
+
[:restore_graphics_state],
|
|
84
|
+
[:restore_graphics_state]])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "draws nothing onto the canvas if the box is empty" do
|
|
88
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
89
|
+
create_box.draw(@canvas, 5, 5)
|
|
90
|
+
assert_operators(@canvas.contents, [])
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "empty?" do
|
|
95
|
+
it "is only empty when no drawing operation is specified" do
|
|
96
|
+
assert(create_box.empty?)
|
|
97
|
+
refute(create_box {}.empty?)
|
|
98
|
+
refute(create_box(style: {background_color: [5]}).empty?)
|
|
99
|
+
refute(create_box(style: {border: {width: 1}}).empty?)
|
|
100
|
+
refute(create_box(style: {underlays: [proc {}]}).empty?)
|
|
101
|
+
refute(create_box(style: {overlays: [proc {}]}).empty?)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -5,36 +5,50 @@ require 'hexapdf/layout/inline_box'
|
|
|
5
5
|
|
|
6
6
|
describe HexaPDF::Layout::InlineBox do
|
|
7
7
|
before do
|
|
8
|
-
@box = HexaPDF::Layout::InlineBox.
|
|
8
|
+
@box = HexaPDF::Layout::InlineBox.create(width: 10, height: 15, style: {margin: [15, 10]})
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
it "draws the wrapped box at the correct position" do
|
|
12
|
+
canvas = Object.new
|
|
13
|
+
block = ->(c, x, y) do
|
|
14
|
+
assert_equal(canvas, c)
|
|
15
|
+
assert_equal(10, x)
|
|
16
|
+
assert_equal(15, y)
|
|
15
17
|
end
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
assert_equal([1, 2, [@box, @canvas]], @box.draw(@canvas, 1, 2))
|
|
18
|
+
@box.box.stub(:draw, block) do
|
|
19
|
+
@box.draw(canvas, 0, 0)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
it "returns true if the inline box is empty with no drawing operations" do
|
|
24
|
+
assert(@box.empty?)
|
|
25
|
+
refute(HexaPDF::Layout::InlineBox.create(width: 10, height: 15) {}.empty?)
|
|
26
|
+
end
|
|
27
|
+
|
|
22
28
|
describe "valign" do
|
|
23
29
|
it "has a default value of :baseline" do
|
|
24
30
|
assert_equal(:baseline, @box.valign)
|
|
25
31
|
end
|
|
26
32
|
|
|
27
33
|
it "can be changed on creation" do
|
|
28
|
-
box = HexaPDF::Layout::InlineBox.
|
|
34
|
+
box = HexaPDF::Layout::InlineBox.create(width: 10, height: 15, valign: :test)
|
|
29
35
|
assert_equal(:test, box.valign)
|
|
30
36
|
end
|
|
31
37
|
end
|
|
32
38
|
|
|
39
|
+
it "returns the width including margins" do
|
|
40
|
+
assert_equal(30, @box.width)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "returns the height including margins" do
|
|
44
|
+
assert_equal(45, @box.height)
|
|
45
|
+
end
|
|
46
|
+
|
|
33
47
|
it "returns 0 for x_min" do
|
|
34
48
|
assert_equal(0, @box.x_min)
|
|
35
49
|
end
|
|
36
50
|
|
|
37
51
|
it "returns width for x_max" do
|
|
38
|
-
assert_equal(
|
|
52
|
+
assert_equal(@box.width, @box.x_max)
|
|
39
53
|
end
|
|
40
54
|
end
|
|
@@ -3,25 +3,25 @@
|
|
|
3
3
|
require 'test_helper'
|
|
4
4
|
require 'hexapdf/document'
|
|
5
5
|
|
|
6
|
-
describe HexaPDF::Layout::
|
|
6
|
+
describe HexaPDF::Layout::Line::HeightCalculator do
|
|
7
7
|
before do
|
|
8
|
-
@calc = HexaPDF::Layout::
|
|
8
|
+
@calc = HexaPDF::Layout::Line::HeightCalculator.new
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it "simulate the height as if an item was added" do
|
|
12
|
-
@calc << HexaPDF::Layout::InlineBox.
|
|
12
|
+
@calc << HexaPDF::Layout::InlineBox.create(width: 10, height: 20, valign: :baseline) {}
|
|
13
13
|
assert_equal([0, 20, 0, 0], @calc.result)
|
|
14
|
-
new_item = HexaPDF::Layout::InlineBox.
|
|
14
|
+
new_item = HexaPDF::Layout::InlineBox.create(width: 10, height: 30, valign: :top) {}
|
|
15
15
|
assert_equal(30, @calc.simulate_height(new_item))
|
|
16
16
|
assert_equal([0, 20, 0, 0], @calc.result)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
describe HexaPDF::Layout::
|
|
20
|
+
describe HexaPDF::Layout::Line do
|
|
21
21
|
before do
|
|
22
22
|
@doc = HexaPDF::Document.new
|
|
23
|
-
@font = @doc.fonts.
|
|
24
|
-
@line = HexaPDF::Layout::
|
|
23
|
+
@font = @doc.fonts.add("Times", custom_encoding: true)
|
|
24
|
+
@line = HexaPDF::Layout::Line.new
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def setup_fragment(text)
|
|
@@ -29,14 +29,14 @@ describe HexaPDF::Layout::LineFragment do
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def setup_box(width, height, valign = :baseline)
|
|
32
|
-
HexaPDF::Layout::InlineBox.
|
|
32
|
+
HexaPDF::Layout::InlineBox.create(width: width, height: height, valign: valign) {}
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
describe "initialize" do
|
|
36
36
|
it "allows setting the items of the line fragment" do
|
|
37
37
|
frag1 = setup_fragment("Hello")
|
|
38
38
|
frag2 = HexaPDF::Layout::TextFragment.new(items: frag1.items.slice!(3, 2), style: frag1.style)
|
|
39
|
-
line = HexaPDF::Layout::
|
|
39
|
+
line = HexaPDF::Layout::Line.new([frag1, frag2])
|
|
40
40
|
assert_equal(1, line.items.count)
|
|
41
41
|
assert_equal(5, line.items[0].items.count)
|
|
42
42
|
end
|