hexapdf 0.34.1 → 0.35.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +76 -0
- data/examples/009-text_layouter_alignment.rb +7 -7
- data/examples/010-text_layouter_inline_boxes.rb +1 -1
- data/examples/011-text_layouter_line_wrapping.rb +2 -4
- data/examples/013-text_layouter_shapes.rb +9 -11
- data/examples/014-text_in_polygon.rb +2 -2
- data/examples/016-frame_automatic_box_placement.rb +6 -7
- data/examples/017-frame_text_flow.rb +2 -2
- data/examples/018-composer.rb +5 -6
- data/examples/020-column_box.rb +2 -2
- data/examples/021-list_box.rb +1 -1
- data/examples/027-composer_optional_content.rb +5 -5
- data/examples/028-frame_mask_mode.rb +23 -0
- data/examples/029-composer_fallback_fonts.rb +22 -0
- data/lib/hexapdf/cli/info.rb +1 -0
- data/lib/hexapdf/cli/inspect.rb +55 -2
- data/lib/hexapdf/composer.rb +4 -3
- data/lib/hexapdf/configuration.rb +61 -1
- data/lib/hexapdf/content/canvas.rb +63 -0
- data/lib/hexapdf/content/canvas_composer.rb +142 -0
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +2 -8
- data/lib/hexapdf/content.rb +1 -0
- data/lib/hexapdf/dictionary.rb +14 -3
- data/lib/hexapdf/document/layout.rb +35 -13
- data/lib/hexapdf/document.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +15 -0
- data/lib/hexapdf/error.rb +2 -1
- data/lib/hexapdf/font/invalid_glyph.rb +22 -6
- data/lib/hexapdf/font/true_type_wrapper.rb +48 -20
- data/lib/hexapdf/font/type1_wrapper.rb +48 -24
- data/lib/hexapdf/layout/box.rb +14 -10
- data/lib/hexapdf/layout/column_box.rb +5 -3
- data/lib/hexapdf/layout/frame.rb +78 -40
- data/lib/hexapdf/layout/image_box.rb +3 -3
- data/lib/hexapdf/layout/list_box.rb +20 -19
- data/lib/hexapdf/layout/style.rb +173 -68
- data/lib/hexapdf/layout/table_box.rb +3 -3
- data/lib/hexapdf/layout/text_box.rb +5 -5
- data/lib/hexapdf/layout/text_fragment.rb +50 -0
- data/lib/hexapdf/layout/text_layouter.rb +7 -6
- data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
- data/lib/hexapdf/object.rb +5 -2
- data/lib/hexapdf/pdf_array.rb +5 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +16 -11
- data/lib/hexapdf/type/icon_fit.rb +2 -2
- data/lib/hexapdf/type/page.rb +35 -35
- data/lib/hexapdf/utils/sorted_tree_node.rb +0 -10
- data/lib/hexapdf/utils.rb +66 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas.rb +37 -0
- data/test/hexapdf/content/test_canvas_composer.rb +112 -0
- data/test/hexapdf/document/test_layout.rb +40 -12
- data/test/hexapdf/document/test_pages.rb +5 -5
- data/test/hexapdf/encryption/test_standard_security_handler.rb +43 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +13 -1
- data/test/hexapdf/font/test_true_type_wrapper.rb +15 -2
- data/test/hexapdf/font/test_type1_wrapper.rb +21 -2
- data/test/hexapdf/layout/test_box.rb +7 -0
- data/test/hexapdf/layout/test_column_box.rb +18 -4
- data/test/hexapdf/layout/test_frame.rb +181 -95
- data/test/hexapdf/layout/test_list_box.rb +7 -7
- data/test/hexapdf/layout/test_page_style.rb +5 -5
- data/test/hexapdf/layout/test_style.rb +14 -10
- data/test/hexapdf/layout/test_table_box.rb +3 -3
- data/test/hexapdf/layout/test_text_box.rb +2 -2
- data/test/hexapdf/layout/test_text_fragment.rb +37 -0
- data/test/hexapdf/layout/test_text_layouter.rb +10 -10
- data/test/hexapdf/test_composer.rb +13 -13
- data/test/hexapdf/test_configuration.rb +49 -0
- data/test/hexapdf/test_dictionary.rb +1 -1
- data/test/hexapdf/test_object.rb +13 -12
- data/test/hexapdf/test_pdf_array.rb +9 -0
- data/test/hexapdf/test_utils.rb +20 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +41 -13
- data/test/hexapdf/type/test_page.rb +3 -3
- data/test/hexapdf/type/test_page_tree_node.rb +1 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +1 -1
- metadata +9 -3
@@ -34,6 +34,43 @@ describe HexaPDF::Layout::TextFragment do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
describe "create_with_fallback_glyphs" do
|
38
|
+
it "creates an array with a single fragment object if no block is given" do
|
39
|
+
frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("Tom", font: @font, font_size: 20)
|
40
|
+
assert_equal(1, frags.size)
|
41
|
+
assert_equal(37.78, frags[0].width)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "allows using a style object instead of directly specifying style properties" do
|
45
|
+
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20)
|
46
|
+
frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("Tom", style)
|
47
|
+
assert_equal(37.78, frags[0].width)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "replaces invalid glyphs with the result of the block" do
|
51
|
+
zapf_dingbats = @doc.fonts.add('ZapfDingbats')
|
52
|
+
i = 0
|
53
|
+
fallback = lambda do |codepoint, invalid_glyph|
|
54
|
+
case (i += 1) % 3
|
55
|
+
when 0 then []
|
56
|
+
when 1 then [zapf_dingbats.decode_codepoint(codepoint)]
|
57
|
+
when 2 then @font.decode_utf8("Tom")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20, font_features: {kern: true})
|
61
|
+
|
62
|
+
frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("✂Tom✂✂Tom✂", style, &fallback)
|
63
|
+
assert_equal(5, frags.size)
|
64
|
+
assert_equal(zapf_dingbats, frags[0].style.font)
|
65
|
+
assert_equal(:a2, frags[0].items[0].name)
|
66
|
+
assert_equal(@font, frags[2].style.font)
|
67
|
+
|
68
|
+
frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("Tom✂Tom", style, &fallback)
|
69
|
+
assert_equal(3, frags.size)
|
70
|
+
assert_equal(frags[0].width, frags[1].width)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
37
74
|
describe "initialize" do
|
38
75
|
before do
|
39
76
|
@items = @font.decode_utf8("Tom")
|
@@ -651,7 +651,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
651
651
|
end
|
652
652
|
|
653
653
|
it "aligns the contents to the left" do
|
654
|
-
@style.
|
654
|
+
@style.text_align = :left
|
655
655
|
result = @layouter.fit(@items, 100, 100)
|
656
656
|
assert_equal(0, result.lines[0].x_offset)
|
657
657
|
assert_equal(80, result.lines[0].width)
|
@@ -661,7 +661,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
661
661
|
end
|
662
662
|
|
663
663
|
it "aligns the contents to the center" do
|
664
|
-
@style.
|
664
|
+
@style.text_align = :center
|
665
665
|
result = @layouter.fit(@items, 100, 100)
|
666
666
|
assert_equal(10, result.lines[0].x_offset)
|
667
667
|
result = @layouter.fit(@items, proc { 100 }, 100)
|
@@ -669,7 +669,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
669
669
|
end
|
670
670
|
|
671
671
|
it "aligns the contents to the right" do
|
672
|
-
@style.
|
672
|
+
@style.text_align = :right
|
673
673
|
result = @layouter.fit(@items, 100, 100)
|
674
674
|
assert_equal(20, result.lines[0].x_offset)
|
675
675
|
result = @layouter.fit(@items, proc { 100 }, 100)
|
@@ -683,28 +683,28 @@ describe HexaPDF::Layout::TextLayouter do
|
|
683
683
|
end
|
684
684
|
|
685
685
|
it "aligns the contents to the top" do
|
686
|
-
@style.
|
686
|
+
@style.text_valign = :top
|
687
687
|
result = @layouter.fit(@items, 40, 100)
|
688
688
|
assert_equal(result.lines[0].y_max, result.lines[0].y_offset)
|
689
689
|
assert_equal(40, result.height)
|
690
690
|
end
|
691
691
|
|
692
692
|
it "aligns the contents to the center" do
|
693
|
-
@style.
|
693
|
+
@style.text_valign = :center
|
694
694
|
result = @layouter.fit(@items, 40, 100)
|
695
695
|
assert_equal((100 - 40) / 2 + 20, result.lines[0].y_offset)
|
696
696
|
assert_equal(70, result.height)
|
697
697
|
end
|
698
698
|
|
699
699
|
it "aligns the contents to the bottom" do
|
700
|
-
@style.
|
700
|
+
@style.text_valign = :bottom
|
701
701
|
result = @layouter.fit(@items, 40, 100)
|
702
702
|
assert_equal(100 - 20 * 2 + 20, result.lines[0].y_offset)
|
703
703
|
assert_equal(100, result.height)
|
704
704
|
end
|
705
705
|
|
706
706
|
it "doesn't vertically align when layouting in variable-width mode" do
|
707
|
-
@style.
|
707
|
+
@style.text_valign = :bottom
|
708
708
|
result = @layouter.fit(@items, proc { 40 }, 100)
|
709
709
|
assert_equal(result.lines[0].y_max, result.lines[0].y_offset)
|
710
710
|
assert_equal(40, result.height)
|
@@ -723,7 +723,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
723
723
|
# Missing width: 100 - 90 = 10
|
724
724
|
# -> Each space must be doubled!
|
725
725
|
|
726
|
-
@style.
|
726
|
+
@style.text_align = :justify
|
727
727
|
result = @layouter.fit(items, 100, 100)
|
728
728
|
assert(result.remaining_items.empty?)
|
729
729
|
assert_equal(:success, result.status)
|
@@ -770,8 +770,8 @@ describe HexaPDF::Layout::TextLayouter do
|
|
770
770
|
|
771
771
|
it "respects the x- and y-offsets" do
|
772
772
|
top = 100
|
773
|
-
@layouter.style.
|
774
|
-
@layouter.style.
|
773
|
+
@layouter.style.text_valign = :center
|
774
|
+
@layouter.style.text_align = :center
|
775
775
|
|
776
776
|
result = @layouter.fit([@frag], @width, top)
|
777
777
|
result.draw(@canvas, 5, top)
|
@@ -16,8 +16,8 @@ describe HexaPDF::Composer do
|
|
16
16
|
assert_kind_of(HexaPDF::Type::Page, @composer.page)
|
17
17
|
assert_equal(36, @composer.frame.left)
|
18
18
|
assert_equal(36, @composer.frame.bottom)
|
19
|
-
assert_equal(523, @composer.frame.width)
|
20
|
-
assert_equal(
|
19
|
+
assert_equal(523.275591, @composer.frame.width)
|
20
|
+
assert_equal(769.889764, @composer.frame.height)
|
21
21
|
assert_kind_of(HexaPDF::Layout::Style, @composer.style(:base))
|
22
22
|
end
|
23
23
|
|
@@ -28,15 +28,15 @@ describe HexaPDF::Composer do
|
|
28
28
|
|
29
29
|
it "allows the customization of the page orientation" do
|
30
30
|
composer = HexaPDF::Composer.new(page_orientation: :landscape)
|
31
|
-
assert_equal([0, 0,
|
31
|
+
assert_equal([0, 0, 841.889764, 595.275591], composer.page.box.value)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "allows the customization of the margin" do
|
35
35
|
composer = HexaPDF::Composer.new(margin: [100, 80, 60, 40])
|
36
36
|
assert_equal(40, composer.frame.left)
|
37
37
|
assert_equal(60, composer.frame.bottom)
|
38
|
-
|
39
|
-
assert_equal(
|
38
|
+
assert_in_delta(475.275591, composer.frame.width)
|
39
|
+
assert_equal(681.889764, composer.frame.height)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "allows skipping the initial page creation" do
|
@@ -107,7 +107,7 @@ describe HexaPDF::Composer do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
it "returns the current y-position" do
|
110
|
-
assert_equal(
|
110
|
+
assert_equal(805.889764, @composer.y)
|
111
111
|
end
|
112
112
|
|
113
113
|
describe "style" do
|
@@ -203,7 +203,7 @@ describe HexaPDF::Composer do
|
|
203
203
|
@composer.draw_box(create_box)
|
204
204
|
assert_operators(@composer.canvas.contents,
|
205
205
|
[[:save_graphics_state],
|
206
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
206
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 705.889764]],
|
207
207
|
[:restore_graphics_state],
|
208
208
|
[:save_graphics_state],
|
209
209
|
[:concatenate_matrix, [1, 0, 0, 1, 36, 36]],
|
@@ -232,14 +232,14 @@ describe HexaPDF::Composer do
|
|
232
232
|
@composer.draw_box(box)
|
233
233
|
assert_operators(first_page_contents,
|
234
234
|
[[:save_graphics_state],
|
235
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
235
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 405.889764]],
|
236
236
|
[:restore_graphics_state],
|
237
237
|
[:save_graphics_state],
|
238
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
238
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 5.889764]],
|
239
239
|
[:restore_graphics_state]])
|
240
240
|
assert_operators(@composer.canvas.contents,
|
241
241
|
[[:save_graphics_state],
|
242
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
242
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 705.889764]],
|
243
243
|
[:restore_graphics_state]])
|
244
244
|
end
|
245
245
|
|
@@ -252,14 +252,14 @@ describe HexaPDF::Composer do
|
|
252
252
|
@composer.draw_box(box)
|
253
253
|
assert_operators(first_page_contents,
|
254
254
|
[[:save_graphics_state],
|
255
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
255
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 405.889764]],
|
256
256
|
[:restore_graphics_state],
|
257
257
|
[:save_graphics_state],
|
258
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
258
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 305.889764]],
|
259
259
|
[:restore_graphics_state]])
|
260
260
|
assert_operators(@composer.canvas.contents,
|
261
261
|
[[:save_graphics_state],
|
262
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
262
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 405.889764]],
|
263
263
|
[:restore_graphics_state]])
|
264
264
|
end
|
265
265
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
4
|
require 'hexapdf/configuration'
|
5
|
+
require 'hexapdf/document'
|
5
6
|
|
6
7
|
describe HexaPDF::Configuration do
|
7
8
|
before do
|
@@ -91,3 +92,51 @@ describe HexaPDF::Configuration do
|
|
91
92
|
end
|
92
93
|
end
|
93
94
|
end
|
95
|
+
|
96
|
+
describe "HexaPDF.font_on_invalid_glyph" do
|
97
|
+
before do
|
98
|
+
@doc = HexaPDF::Document.new
|
99
|
+
@font = @doc.fonts.add('Helvetica')
|
100
|
+
end
|
101
|
+
|
102
|
+
def assert_glyph(font, codepoint, font_name, glyph_name)
|
103
|
+
invalid_glyph = HexaPDF::Font::InvalidGlyph.new(font, codepoint.chr("UTF-8"))
|
104
|
+
glyph = HexaPDF.font_on_invalid_glyph(codepoint, invalid_glyph)
|
105
|
+
|
106
|
+
assert_equal(1, glyph.size)
|
107
|
+
glyph = glyph.first
|
108
|
+
assert(glyph.valid?)
|
109
|
+
assert_equal(font_name, glyph.font_wrapper.wrapped_font.font_name)
|
110
|
+
assert_equal(glyph_name, glyph.name)
|
111
|
+
glyph
|
112
|
+
end
|
113
|
+
|
114
|
+
it "tries each fallback font and uses the first valid glyph" do
|
115
|
+
assert_glyph(@font, 10102, "ZapfDingbats", :a130)
|
116
|
+
assert_glyph(@font, 8855, "Symbol", :circlemultiply)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "takes the font variant into account" do
|
120
|
+
@doc.config['font.fallback'] = ['Times']
|
121
|
+
|
122
|
+
glyph = assert_glyph(@doc.fonts.add("Helvetica", variant: :bold), 65, "Times-Bold", :A)
|
123
|
+
assert(glyph.font_wrapper.bold?)
|
124
|
+
|
125
|
+
glyph = assert_glyph(@doc.fonts.add("Helvetica", variant: :italic), 65, "Times-Italic", :A)
|
126
|
+
assert(glyph.font_wrapper.italic?)
|
127
|
+
|
128
|
+
glyph = assert_glyph(@doc.fonts.add("Helvetica", variant: :bold_italic), 65, "Times-BoldItalic", :A)
|
129
|
+
assert(glyph.font_wrapper.bold?)
|
130
|
+
assert(glyph.font_wrapper.italic?)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "falls back to the :none variant of a fallback font if a more specific one doesn't exist" do
|
134
|
+
assert_glyph(@doc.fonts.add("Helvetica", variant: :bold), 9985, "ZapfDingbats", :a1)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "returns the given invalid glyph if no fallback glyph could be found" do
|
138
|
+
@doc.config['font.fallback'] = []
|
139
|
+
invalid_glyph = HexaPDF::Font::InvalidGlyph.new(@font, "A")
|
140
|
+
assert_equal([invalid_glyph], HexaPDF.font_on_invalid_glyph(65, invalid_glyph))
|
141
|
+
end
|
142
|
+
end
|
@@ -204,7 +204,7 @@ describe HexaPDF::Dictionary do
|
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
207
|
-
describe "
|
207
|
+
describe "perform_validation" do
|
208
208
|
before do
|
209
209
|
@test_class.define_field(:Inherited, type: [Array, Symbol], required: true, indirect: false)
|
210
210
|
@test_class.define_field(:AllowedValues, type: Integer, allowed_values: [1, 5])
|
data/test/hexapdf/test_object.rb
CHANGED
@@ -186,6 +186,19 @@ describe HexaPDF::Object do
|
|
186
186
|
refute(@obj.validate(auto_correct: false))
|
187
187
|
assert_equal([:before], invoked)
|
188
188
|
end
|
189
|
+
|
190
|
+
it "re-raises caught HexaPDF::Error exceptions" do
|
191
|
+
@obj.define_singleton_method(:perform_validation) { raise HexaPDF::Error, "Unknown" }
|
192
|
+
invoked = []
|
193
|
+
assert_raises(HexaPDF::Error) { @obj.validate {|*a| invoked << a } }
|
194
|
+
end
|
195
|
+
|
196
|
+
it "catches errors raised in perform_validation and produces an appropriate message" do
|
197
|
+
@obj.define_singleton_method(:perform_validation) { raise "Unknown" }
|
198
|
+
invoked = []
|
199
|
+
refute(@obj.validate {|*a| invoked << a })
|
200
|
+
assert_equal([["Error: Unexpected value encountered", false, @obj]], invoked)
|
201
|
+
end
|
189
202
|
end
|
190
203
|
|
191
204
|
it "can represent itself during inspection" do
|
@@ -286,17 +299,5 @@ describe HexaPDF::Object do
|
|
286
299
|
obj.validate
|
287
300
|
assert_equal(1, obj.oid)
|
288
301
|
end
|
289
|
-
|
290
|
-
it "validates collection values" do
|
291
|
-
x = HexaPDF::Object.new(:x, document: @doc)
|
292
|
-
x.must_be_indirect = true
|
293
|
-
y = HexaPDF::Object.new(:y, document: @doc)
|
294
|
-
y.must_be_indirect = true
|
295
|
-
|
296
|
-
obj = HexaPDF::Object.new([1, 2, 3, [4, x], {X: [y, 5]}])
|
297
|
-
obj.validate
|
298
|
-
assert_equal(1, x.oid)
|
299
|
-
assert_equal(1, y.oid)
|
300
|
-
end
|
301
302
|
end
|
302
303
|
end
|
@@ -166,4 +166,13 @@ describe HexaPDF::PDFArray do
|
|
166
166
|
it "can be converted to a simple array" do
|
167
167
|
assert_equal([1, :data, "deref", @array[3]], @array.to_ary)
|
168
168
|
end
|
169
|
+
|
170
|
+
describe "perform_validation" do
|
171
|
+
it "validates nested objects" do
|
172
|
+
@array.value << HexaPDF::PDFArray.new([HexaPDF::Reference.new(1, 0)], document: self)
|
173
|
+
assert(@array.validate)
|
174
|
+
assert_kind_of(HexaPDF::Object, @array.value[2])
|
175
|
+
assert_kind_of(HexaPDF::Object, @array.value.last.value[0])
|
176
|
+
end
|
177
|
+
end
|
169
178
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/utils.rb'
|
5
|
+
|
6
|
+
describe HexaPDF::Utils do
|
7
|
+
include HexaPDF::Utils
|
8
|
+
|
9
|
+
it "checks floats for equality with a certain precision" do
|
10
|
+
assert(float_equal(1.0, 1))
|
11
|
+
assert(float_equal(1.0, 1.0000003))
|
12
|
+
end
|
13
|
+
|
14
|
+
it "compares floats like the <=> operator" do
|
15
|
+
assert_equal(0, float_compare(1.0, 1))
|
16
|
+
assert_equal(0, float_compare(1.0, 1.0000003))
|
17
|
+
assert_equal(-1, float_compare(1.0, 1.03))
|
18
|
+
assert_equal(1, float_compare(1.0, 0.9997))
|
19
|
+
end
|
20
|
+
end
|
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.35.1)>>
|
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.35.1)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
|
|
214
214
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
215
215
|
endobj
|
216
216
|
5 0 obj
|
217
|
-
<</Producer(HexaPDF version 0.
|
217
|
+
<</Producer(HexaPDF version 0.35.1)>>
|
218
218
|
endobj
|
219
219
|
4 0 obj
|
220
220
|
<</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
|
@@ -453,7 +453,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
453
453
|
before do
|
454
454
|
@widget[:Rect].height = 20
|
455
455
|
@widget[:Rect].width = 100
|
456
|
-
@field
|
456
|
+
@field[:V] = ''
|
457
457
|
end
|
458
458
|
|
459
459
|
it "uses the non-zero font size" do
|
@@ -477,7 +477,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
477
477
|
end
|
478
478
|
|
479
479
|
it "shrinks the font size if necessary to fit the rectangle width" do
|
480
|
-
@field
|
480
|
+
@field[:V] = "This is some arbitrary, long text"
|
481
481
|
@generator.create_appearances
|
482
482
|
assert_operators(@widget[:AP][:N].stream,
|
483
483
|
[:set_font_and_size, [:F1, 6.909955]],
|
@@ -498,7 +498,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
498
498
|
describe "single line text fields" do
|
499
499
|
describe "quadding e.g. text alignment" do
|
500
500
|
before do
|
501
|
-
@field
|
501
|
+
@field[:V] = 'Test'
|
502
502
|
@field.set_default_appearance_string(font_size: 10)
|
503
503
|
@widget[:Rect].height = 20
|
504
504
|
end
|
@@ -551,7 +551,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
551
551
|
|
552
552
|
describe "Javascript action AFNumber_Format" do
|
553
553
|
before do
|
554
|
-
@field
|
554
|
+
@field[:V] = '1234567.898765'
|
555
555
|
@action = {S: :JavaScript, JS: ''}
|
556
556
|
@field[:AA] = {F: @action}
|
557
557
|
end
|
@@ -576,7 +576,8 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
576
576
|
end
|
577
577
|
|
578
578
|
it "respects the negative value styling" do
|
579
|
-
@field
|
579
|
+
@field[:V] = '-1234567.898'
|
580
|
+
@widget[:Rect].height = 11.25
|
580
581
|
["-E1234567,90", "E1234567,90", "(E1234567,90)", "(E1234567,90)"].each_with_index do |result, style|
|
581
582
|
assert_format("2, 3, #{style}, 0, \"E\", true",
|
582
583
|
[[:set_device_rgb_non_stroking_color, [style % 2, 0.0, 0.0]],
|
@@ -597,8 +598,11 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
597
598
|
end
|
598
599
|
|
599
600
|
it "creates the /N appearance stream according to the set string" do
|
600
|
-
@
|
601
|
+
@doc.config['font.on_invalid_glyph'] = lambda do |codepoint, _|
|
602
|
+
[@doc.fonts.add('ZapfDingbats').decode_codepoint(codepoint)]
|
603
|
+
end
|
601
604
|
@field.set_default_appearance_string(font_color: "red")
|
605
|
+
@field[:V] = 'Te ✂ xt'
|
602
606
|
@generator.create_appearances
|
603
607
|
assert_operators(@widget[:AP][:N].stream,
|
604
608
|
[[:begin_marked_content, [:Tx]],
|
@@ -606,11 +610,17 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
606
610
|
[:append_rectangle, [1, 1, 98, 9.25]],
|
607
611
|
[:clip_path_non_zero],
|
608
612
|
[:end_path],
|
609
|
-
[:set_font_and_size, [:F1,
|
613
|
+
[:set_font_and_size, [:F1, 10]],
|
610
614
|
[:set_device_rgb_non_stroking_color, [1.0, 0.0, 0.0]],
|
611
615
|
[:begin_text],
|
612
|
-
[:set_text_matrix, [1, 0, 0, 1, 2,
|
613
|
-
[:show_text, ["
|
616
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 2.035]],
|
617
|
+
[:show_text, ["Te "]],
|
618
|
+
[:set_font_and_size, [:F2, 10]],
|
619
|
+
[:move_text, [14.45, 0]],
|
620
|
+
[:show_text, ["\""]],
|
621
|
+
[:set_font_and_size, [:F1, 10]],
|
622
|
+
[:move_text, [9.61, 0]],
|
623
|
+
[:show_text, [" xt"]],
|
614
624
|
[:end_text],
|
615
625
|
[:restore_graphics_state],
|
616
626
|
[:end_marked_content]])
|
@@ -656,7 +666,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
656
666
|
end
|
657
667
|
|
658
668
|
it "creates the /N appearance stream according to the set string" do
|
659
|
-
@field
|
669
|
+
@field[:V] = "Test\nValue"
|
660
670
|
@field.set_default_appearance_string(font_size: 10, font_color: "red")
|
661
671
|
@generator.create_appearances
|
662
672
|
assert_operators(@widget[:AP][:N].stream,
|
@@ -679,7 +689,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
679
689
|
[:restore_graphics_state],
|
680
690
|
[:end_marked_content]])
|
681
691
|
|
682
|
-
@field
|
692
|
+
@field[:V] = "Test\nTest\nTest"
|
683
693
|
@field.set_default_appearance_string(font_size: 0)
|
684
694
|
@generator.create_appearances
|
685
695
|
assert_operators(@widget[:AP][:N].stream,
|
@@ -744,7 +754,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
744
754
|
end
|
745
755
|
|
746
756
|
it "handles centering like Adobe, e.g. shift left, when text cannot be completely centered" do
|
747
|
-
@field
|
757
|
+
@field[:V] = 'Hello'
|
748
758
|
@field.text_alignment(:center)
|
749
759
|
@generator.create_appearances
|
750
760
|
assert_operators(@widget[:AP][:N].stream,
|
@@ -754,7 +764,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
754
764
|
end
|
755
765
|
|
756
766
|
it "creates the /N appearance stream according to the set string" do
|
757
|
-
@field
|
767
|
+
@field[:V] = 'Text'
|
758
768
|
@field.set_default_appearance_string(font_size: 10, font_color: "red")
|
759
769
|
@generator.create_appearances
|
760
770
|
assert_operators(@widget[:AP][:N].stream,
|
@@ -773,11 +783,29 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
773
783
|
[:end_marked_content]])
|
774
784
|
end
|
775
785
|
|
786
|
+
it "works for empty strings" do
|
787
|
+
@field[:V] = ''
|
788
|
+
@generator.create_appearances
|
789
|
+
assert_operators(@widget[:AP][:N].stream,
|
790
|
+
[[:begin_text],
|
791
|
+
[:set_text_matrix, [1, 0, 0, 1, 2, 6.41]],
|
792
|
+
[:end_text]], range: 6..8)
|
793
|
+
end
|
794
|
+
|
776
795
|
it "fails if the /MaxLen key is not set" do
|
777
796
|
@field.delete(:MaxLen)
|
778
797
|
@field[:V] = 't'
|
779
798
|
assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
780
799
|
end
|
800
|
+
|
801
|
+
it "fails if fallback glyphs are needed for rendering" do
|
802
|
+
@doc.config['font.on_invalid_glyph'] = lambda do |codepoint, _|
|
803
|
+
[@doc.fonts.add('ZapfDingbats').decode_codepoint(codepoint)]
|
804
|
+
end
|
805
|
+
@field[:V] = 'Test ✂'
|
806
|
+
ex = assert_raises(HexaPDF::Error) { @generator.create_appearances }
|
807
|
+
assert_match(/Fallback glyphs/, ex.message)
|
808
|
+
end
|
781
809
|
end
|
782
810
|
|
783
811
|
describe "choice fields" do
|
@@ -12,11 +12,11 @@ describe HexaPDF::Type::Page do
|
|
12
12
|
|
13
13
|
describe "::media_box" do
|
14
14
|
it "returns the media box for a given paper size" do
|
15
|
-
assert_equal([0, 0, 595,
|
15
|
+
assert_equal([0, 0, 595.275591, 841.889764], HexaPDF::Type::Page.media_box(:A4))
|
16
16
|
end
|
17
17
|
|
18
18
|
it "respects the orientation key" do
|
19
|
-
assert_equal([0, 0,
|
19
|
+
assert_equal([0, 0, 841.889764, 595.275591], HexaPDF::Type::Page.media_box(:A4, orientation: :landscape))
|
20
20
|
end
|
21
21
|
|
22
22
|
it "works with a paper size array" do
|
@@ -116,7 +116,7 @@ describe HexaPDF::Type::Page do
|
|
116
116
|
page1[:MediaBox] = [0, 0, 1, 1]
|
117
117
|
refute(page2.validate(auto_correct: false))
|
118
118
|
assert(page2.validate)
|
119
|
-
assert_equal([0, 0, 595,
|
119
|
+
assert_equal([0, 0, 595.275591, 841.889764], page2[:MediaBox])
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
@@ -89,7 +89,7 @@ describe HexaPDF::Type::PageTreeNode do
|
|
89
89
|
assert_equal(:Page, page[:Type])
|
90
90
|
assert_equal(@root, page[:Parent])
|
91
91
|
assert_kind_of(HexaPDF::Rectangle, page[:MediaBox])
|
92
|
-
assert_equal([0, 0,
|
92
|
+
assert_equal([0, 0, 841.889764, 595.275591], page[:MediaBox].value)
|
93
93
|
assert_equal({}, page[:Resources].value)
|
94
94
|
refute(@root.value.key?(:Parent))
|
95
95
|
end
|
@@ -209,7 +209,7 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
209
209
|
@root[:Kids][0] = ref
|
210
210
|
@kid1.oid = 0
|
211
211
|
assert(@root.validate do |message, c|
|
212
|
-
assert_match(/must be indirect
|
212
|
+
assert_match(/must be an indirect object/, message)
|
213
213
|
assert(c)
|
214
214
|
end)
|
215
215
|
assert(@kid1.indirect?)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.35.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -301,6 +301,8 @@ files:
|
|
301
301
|
- examples/025-table_box.rb
|
302
302
|
- examples/026-optional_content.rb
|
303
303
|
- examples/027-composer_optional_content.rb
|
304
|
+
- examples/028-frame_mask_mode.rb
|
305
|
+
- examples/029-composer_fallback_fonts.rb
|
304
306
|
- examples/emoji-smile.png
|
305
307
|
- examples/emoji-wink.png
|
306
308
|
- examples/machupicchu.jpg
|
@@ -324,6 +326,7 @@ files:
|
|
324
326
|
- lib/hexapdf/configuration.rb
|
325
327
|
- lib/hexapdf/content.rb
|
326
328
|
- lib/hexapdf/content/canvas.rb
|
329
|
+
- lib/hexapdf/content/canvas_composer.rb
|
327
330
|
- lib/hexapdf/content/color_space.rb
|
328
331
|
- lib/hexapdf/content/graphic_object.rb
|
329
332
|
- lib/hexapdf/content/graphic_object/arc.rb
|
@@ -516,6 +519,7 @@ files:
|
|
516
519
|
- lib/hexapdf/type/trailer.rb
|
517
520
|
- lib/hexapdf/type/viewer_preferences.rb
|
518
521
|
- lib/hexapdf/type/xref_stream.rb
|
522
|
+
- lib/hexapdf/utils.rb
|
519
523
|
- lib/hexapdf/utils/bit_field.rb
|
520
524
|
- lib/hexapdf/utils/bit_stream.rb
|
521
525
|
- lib/hexapdf/utils/graphics_helpers.rb
|
@@ -603,6 +607,7 @@ files:
|
|
603
607
|
- test/hexapdf/content/graphic_object/test_geom2d.rb
|
604
608
|
- test/hexapdf/content/graphic_object/test_solid_arc.rb
|
605
609
|
- test/hexapdf/content/test_canvas.rb
|
610
|
+
- test/hexapdf/content/test_canvas_composer.rb
|
606
611
|
- test/hexapdf/content/test_color_space.rb
|
607
612
|
- test/hexapdf/content/test_graphics_state.rb
|
608
613
|
- test/hexapdf/content/test_operator.rb
|
@@ -724,6 +729,7 @@ files:
|
|
724
729
|
- test/hexapdf/test_stream.rb
|
725
730
|
- test/hexapdf/test_tokenizer.rb
|
726
731
|
- test/hexapdf/test_type.rb
|
732
|
+
- test/hexapdf/test_utils.rb
|
727
733
|
- test/hexapdf/test_writer.rb
|
728
734
|
- test/hexapdf/test_xref_section.rb
|
729
735
|
- test/hexapdf/type/acro_form/test_appearance_generator.rb
|
@@ -795,7 +801,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
795
801
|
- !ruby/object:Gem::Version
|
796
802
|
version: '0'
|
797
803
|
requirements: []
|
798
|
-
rubygems_version: 3.
|
804
|
+
rubygems_version: 3.5.3
|
799
805
|
signing_key:
|
800
806
|
specification_version: 4
|
801
807
|
summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|