prawn 2.0.2 → 2.3.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/GPLv2 +20 -21
- data/Gemfile +3 -9
- data/Rakefile +20 -23
- data/lib/prawn.rb +37 -49
- data/lib/prawn/document.rb +181 -133
- data/lib/prawn/document/bounding_box.rb +41 -29
- data/lib/prawn/document/column_box.rb +7 -7
- data/lib/prawn/document/internals.rb +18 -8
- data/lib/prawn/document/span.rb +21 -16
- data/lib/prawn/encoding.rb +69 -68
- data/lib/prawn/errors.rb +12 -7
- data/lib/prawn/font.rb +115 -69
- data/lib/prawn/font_metric_cache.rb +14 -8
- data/lib/prawn/{font → fonts}/afm.rb +102 -68
- data/lib/prawn/{font → fonts}/dfont.rb +5 -11
- data/lib/prawn/fonts/otf.rb +11 -0
- data/lib/prawn/fonts/ttc.rb +36 -0
- data/lib/prawn/{font → fonts}/ttf.rb +87 -68
- data/lib/prawn/graphics.rb +120 -80
- data/lib/prawn/graphics/blend_mode.rb +65 -0
- data/lib/prawn/graphics/cap_style.rb +3 -3
- data/lib/prawn/graphics/color.rb +27 -25
- data/lib/prawn/graphics/dash.rb +23 -11
- data/lib/prawn/graphics/join_style.rb +9 -3
- data/lib/prawn/graphics/patterns.rb +197 -67
- data/lib/prawn/graphics/transformation.rb +17 -8
- data/lib/prawn/graphics/transparency.rb +17 -13
- data/lib/prawn/grid.rb +48 -47
- data/lib/prawn/image_handler.rb +5 -5
- data/lib/prawn/images.rb +39 -30
- data/lib/prawn/images/image.rb +2 -1
- data/lib/prawn/images/jpg.rb +28 -22
- data/lib/prawn/images/png.rb +107 -66
- data/lib/prawn/measurement_extensions.rb +10 -9
- data/lib/prawn/measurements.rb +19 -15
- data/lib/prawn/outline.rb +97 -77
- data/lib/prawn/repeater.rb +14 -10
- data/lib/prawn/security.rb +81 -61
- data/lib/prawn/security/arcfour.rb +2 -2
- data/lib/prawn/soft_mask.rb +26 -26
- data/lib/prawn/stamp.rb +20 -13
- data/lib/prawn/text.rb +68 -52
- data/lib/prawn/text/box.rb +11 -8
- data/lib/prawn/text/formatted.rb +5 -5
- data/lib/prawn/text/formatted/arranger.rb +78 -49
- data/lib/prawn/text/formatted/box.rb +134 -100
- data/lib/prawn/text/formatted/fragment.rb +11 -14
- data/lib/prawn/text/formatted/line_wrap.rb +121 -63
- data/lib/prawn/text/formatted/parser.rb +139 -117
- data/lib/prawn/text/formatted/wrap.rb +43 -31
- data/lib/prawn/transformation_stack.rb +44 -0
- data/lib/prawn/utilities.rb +7 -22
- data/lib/prawn/version.rb +2 -2
- data/lib/prawn/view.rb +17 -7
- data/manual/basic_concepts/adding_pages.rb +6 -7
- data/manual/basic_concepts/basic_concepts.rb +31 -22
- data/manual/basic_concepts/creation.rb +10 -11
- data/manual/basic_concepts/cursor.rb +4 -5
- data/manual/basic_concepts/measurement.rb +6 -7
- data/manual/basic_concepts/origin.rb +5 -6
- data/manual/basic_concepts/other_cursor_helpers.rb +11 -12
- data/manual/basic_concepts/view.rb +22 -16
- data/manual/bounding_box/bounding_box.rb +29 -24
- data/manual/bounding_box/bounds.rb +11 -12
- data/manual/bounding_box/canvas.rb +4 -5
- data/manual/bounding_box/creation.rb +6 -7
- data/manual/bounding_box/indentation.rb +14 -15
- data/manual/bounding_box/nesting.rb +24 -17
- data/manual/bounding_box/russian_boxes.rb +14 -13
- data/manual/bounding_box/stretchy.rb +12 -13
- data/manual/contents.rb +28 -22
- data/manual/cover.rb +33 -28
- data/manual/document_and_page_options/background.rb +11 -13
- data/manual/document_and_page_options/document_and_page_options.rb +25 -20
- data/manual/document_and_page_options/metadata.rb +18 -16
- data/manual/document_and_page_options/page_margins.rb +18 -20
- data/manual/document_and_page_options/page_size.rb +13 -12
- data/manual/document_and_page_options/print_scaling.rb +17 -15
- data/manual/example_helper.rb +5 -4
- data/manual/graphics/blend_mode.rb +52 -0
- data/manual/graphics/circle_and_ellipse.rb +4 -5
- data/manual/graphics/color.rb +7 -9
- data/manual/graphics/common_lines.rb +7 -8
- data/manual/graphics/fill_and_stroke.rb +4 -5
- data/manual/graphics/fill_rules.rb +9 -10
- data/manual/graphics/gradients.rb +27 -21
- data/manual/graphics/graphics.rb +48 -39
- data/manual/graphics/helper.rb +12 -9
- data/manual/graphics/line_width.rb +8 -7
- data/manual/graphics/lines_and_curves.rb +7 -8
- data/manual/graphics/polygon.rb +6 -8
- data/manual/graphics/rectangle.rb +4 -5
- data/manual/graphics/rotate.rb +6 -7
- data/manual/graphics/scale.rb +14 -15
- data/manual/graphics/soft_masks.rb +4 -5
- data/manual/graphics/stroke_cap.rb +6 -7
- data/manual/graphics/stroke_dash.rb +11 -12
- data/manual/graphics/stroke_join.rb +5 -6
- data/manual/graphics/translate.rb +9 -10
- data/manual/graphics/transparency.rb +7 -8
- data/manual/how_to_read_this_manual.rb +6 -6
- data/manual/images/absolute_position.rb +6 -7
- data/manual/images/fit.rb +7 -8
- data/manual/images/horizontal.rb +9 -10
- data/manual/images/images.rb +28 -24
- data/manual/images/plain_image.rb +5 -6
- data/manual/images/scale.rb +9 -10
- data/manual/images/vertical.rb +13 -14
- data/manual/images/width_and_height.rb +10 -11
- data/manual/layout/boxes.rb +5 -6
- data/manual/layout/content.rb +7 -8
- data/manual/layout/layout.rb +18 -16
- data/manual/layout/simple_grid.rb +6 -7
- data/manual/outline/add_subsection_to.rb +20 -21
- data/manual/outline/insert_section_after.rb +15 -16
- data/manual/outline/outline.rb +21 -17
- data/manual/outline/sections_and_pages.rb +17 -18
- data/manual/repeatable_content/alternate_page_numbering.rb +21 -17
- data/manual/repeatable_content/page_numbering.rb +17 -16
- data/manual/repeatable_content/repeatable_content.rb +25 -19
- data/manual/repeatable_content/repeater.rb +14 -15
- data/manual/repeatable_content/stamp.rb +14 -15
- data/manual/security/encryption.rb +9 -10
- data/manual/security/permissions.rb +19 -14
- data/manual/security/security.rb +19 -16
- data/manual/table.rb +3 -3
- data/manual/text/alignment.rb +16 -17
- data/manual/text/color.rb +12 -11
- data/manual/text/column_box.rb +9 -10
- data/manual/text/fallback_fonts.rb +25 -21
- data/manual/text/font.rb +11 -12
- data/manual/text/font_size.rb +13 -14
- data/manual/text/font_style.rb +7 -8
- data/manual/text/formatted_callbacks.rb +25 -21
- data/manual/text/formatted_text.rb +33 -25
- data/manual/text/free_flowing_text.rb +20 -21
- data/manual/text/inline.rb +18 -19
- data/manual/text/kerning_and_character_spacing.rb +14 -15
- data/manual/text/leading.rb +7 -8
- data/manual/text/line_wrapping.rb +37 -18
- data/manual/text/paragraph_indentation.rb +13 -14
- data/manual/text/positioned_text.rb +15 -16
- data/manual/text/registering_families.rb +20 -21
- data/manual/text/rendering_and_color.rb +9 -10
- data/manual/text/right_to_left_text.rb +26 -19
- data/manual/text/rotation.rb +28 -23
- data/manual/text/single_usage.rb +8 -9
- data/manual/text/text.rb +57 -52
- data/manual/text/text_box_excess.rb +20 -17
- data/manual/text/text_box_extensions.rb +18 -15
- data/manual/text/text_box_overflow.rb +18 -19
- data/manual/text/utf8.rb +11 -12
- data/manual/text/win_ansi_charset.rb +21 -19
- data/prawn.gemspec +45 -33
- data/spec/extensions/encoding_helpers.rb +3 -3
- data/spec/prawn/document/bounding_box_spec.rb +546 -0
- data/spec/prawn/document/column_box_spec.rb +75 -0
- data/spec/prawn/document/security_spec.rb +176 -0
- data/spec/prawn/document_annotations_spec.rb +76 -0
- data/spec/prawn/document_destinations_spec.rb +15 -0
- data/spec/prawn/document_grid_spec.rb +99 -0
- data/spec/prawn/document_reference_spec.rb +27 -0
- data/spec/prawn/document_span_spec.rb +36 -0
- data/spec/prawn/document_spec.rb +802 -0
- data/spec/prawn/font_metric_cache_spec.rb +54 -0
- data/spec/prawn/font_spec.rb +542 -0
- data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
- data/spec/prawn/graphics/transparency_spec.rb +81 -0
- data/spec/prawn/graphics_spec.rb +837 -0
- data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
- data/spec/prawn/image_handler_spec.rb +53 -0
- data/spec/prawn/images/jpg_spec.rb +20 -0
- data/spec/prawn/images/png_spec.rb +283 -0
- data/spec/prawn/images_spec.rb +224 -0
- data/spec/prawn/measurements_extensions_spec.rb +24 -0
- data/spec/prawn/outline_spec.rb +412 -0
- data/spec/prawn/repeater_spec.rb +165 -0
- data/spec/prawn/soft_mask_spec.rb +74 -0
- data/spec/prawn/stamp_spec.rb +172 -0
- data/spec/prawn/text/box_spec.rb +1112 -0
- data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
- data/spec/prawn/text/formatted/box_spec.rb +846 -0
- data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
- data/spec/prawn/text/formatted/line_wrap_spec.rb +494 -0
- data/spec/prawn/text/formatted/parser_spec.rb +697 -0
- data/spec/prawn/text_draw_text_spec.rb +149 -0
- data/spec/prawn/text_rendering_mode_spec.rb +48 -0
- data/spec/prawn/text_spacing_spec.rb +95 -0
- data/spec/prawn/text_spec.rb +603 -0
- data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
- data/spec/prawn/transformation_stack_spec.rb +66 -0
- data/spec/prawn/view_spec.rb +63 -0
- data/spec/prawn_manual_spec.rb +35 -0
- data/spec/spec_helper.rb +19 -23
- metadata +145 -185
- metadata.gz.sig +4 -0
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.color +0 -0
- data/data/images/16bit.png +0 -0
- data/data/images/arrow.png +0 -0
- data/data/images/arrow2.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.color +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/dice_interlaced.png +0 -0
- data/data/images/fractal.jpg +0 -0
- data/data/images/indexed_color.dat +0 -0
- data/data/images/indexed_color.png +0 -0
- data/data/images/letterhead.jpg +0 -0
- data/data/images/license.md +0 -8
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.color +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pal_bk.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/prawn.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/tru256.bmp +0 -0
- data/data/images/web-links.dat +0 -1
- data/data/images/web-links.png +0 -0
- data/data/pdfs/complex_template.pdf +0 -0
- data/data/pdfs/contains_ttf_font.pdf +0 -0
- data/data/pdfs/encrypted.pdf +0 -0
- data/data/pdfs/form.pdf +1 -819
- data/data/pdfs/hexagon.pdf +0 -61
- data/data/pdfs/indirect_reference.pdf +0 -86
- data/data/pdfs/multipage_template.pdf +0 -127
- data/data/pdfs/nested_pages.pdf +0 -118
- data/data/pdfs/page_without_mediabox.pdf +0 -193
- data/data/pdfs/resources_as_indirect_object.pdf +0 -83
- data/data/pdfs/two_hexagons.pdf +0 -90
- data/data/pdfs/version_1_6.pdf +0 -61
- data/data/shift_jis_text.txt +0 -1
- data/spec/acceptance/png.rb +0 -24
- data/spec/annotations_spec.rb +0 -67
- data/spec/bounding_box_spec.rb +0 -501
- data/spec/column_box_spec.rb +0 -59
- data/spec/destinations_spec.rb +0 -13
- data/spec/document_spec.rb +0 -742
- data/spec/extensions/mocha.rb +0 -45
- data/spec/font_metric_cache_spec.rb +0 -52
- data/spec/font_spec.rb +0 -475
- data/spec/formatted_text_arranger_spec.rb +0 -423
- data/spec/formatted_text_box_spec.rb +0 -716
- data/spec/formatted_text_fragment_spec.rb +0 -299
- data/spec/graphics_spec.rb +0 -666
- data/spec/grid_spec.rb +0 -95
- data/spec/image_handler_spec.rb +0 -53
- data/spec/images_spec.rb +0 -167
- data/spec/inline_formatted_text_parser_spec.rb +0 -568
- data/spec/jpg_spec.rb +0 -23
- data/spec/line_wrap_spec.rb +0 -366
- data/spec/measurement_units_spec.rb +0 -22
- data/spec/outline_spec.rb +0 -409
- data/spec/png_spec.rb +0 -235
- data/spec/reference_spec.rb +0 -25
- data/spec/repeater_spec.rb +0 -154
- data/spec/security_spec.rb +0 -151
- data/spec/soft_mask_spec.rb +0 -78
- data/spec/span_spec.rb +0 -43
- data/spec/stamp_spec.rb +0 -179
- data/spec/stroke_styles_spec.rb +0 -208
- data/spec/text_at_spec.rb +0 -142
- data/spec/text_box_spec.rb +0 -1038
- data/spec/text_rendering_mode_spec.rb +0 -45
- data/spec/text_spacing_spec.rb +0 -93
- data/spec/text_spec.rb +0 -549
- data/spec/text_with_inline_formatting_spec.rb +0 -35
- data/spec/transparency_spec.rb +0 -91
- data/spec/view_spec.rb +0 -42
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Prawn::Graphics::BlendMode do
|
6
|
+
def make_blend_mode(blend_mode)
|
7
|
+
pdf.blend_mode(blend_mode) do
|
8
|
+
yield if block_given?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:pdf) { create_pdf }
|
13
|
+
|
14
|
+
it 'the PDF version should be at least 1.4' do
|
15
|
+
make_blend_mode(:Multiply)
|
16
|
+
str = pdf.render
|
17
|
+
expect(str[0, 8]).to eq('%PDF-1.4')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'a new extended graphics state should be created for ' \
|
21
|
+
'each unique blend mode setting' do
|
22
|
+
make_blend_mode(:Multiply) do
|
23
|
+
make_blend_mode(:Screen)
|
24
|
+
end
|
25
|
+
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
|
26
|
+
expect(extgstates.length).to eq(2)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'a new extended graphics state should not be created for ' \
|
30
|
+
'each duplicate blend mode setting' do
|
31
|
+
make_blend_mode(:Multiply) do
|
32
|
+
make_blend_mode(:Multiply)
|
33
|
+
end
|
34
|
+
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
|
35
|
+
expect(extgstates.length).to eq(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'setting the blend mode with only one parameter sets a single '\
|
39
|
+
'blend mode value' do
|
40
|
+
make_blend_mode(:Multiply)
|
41
|
+
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
|
42
|
+
expect(extgstate[:blend_mode]).to eq(:Multiply)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'setting the blend mode with multiple parameters sets an array of '\
|
46
|
+
'blend modes' do
|
47
|
+
make_blend_mode(%i[Multiply Screen Overlay])
|
48
|
+
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
|
49
|
+
expect(extgstate[:blend_mode]).to eq(%i[Multiply Screen Overlay])
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'with more than one page' do
|
53
|
+
it 'the extended graphic state resource should be added to both pages' do
|
54
|
+
make_blend_mode(:Multiply)
|
55
|
+
pdf.start_new_page
|
56
|
+
make_blend_mode(:Multiply)
|
57
|
+
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
|
58
|
+
extgstate = extgstates[0]
|
59
|
+
expect(extgstates.length).to eq(2)
|
60
|
+
expect(extgstate[:blend_mode]).to eq(:Multiply)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Prawn::Graphics::Transparency do
|
6
|
+
def make_transparent(opacity, stroke_opacity = opacity)
|
7
|
+
pdf.transparent(opacity, stroke_opacity) do
|
8
|
+
yield if block_given?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:pdf) { create_pdf }
|
13
|
+
|
14
|
+
it 'the PDF version should be at least 1.4' do
|
15
|
+
make_transparent(0.5)
|
16
|
+
str = pdf.render
|
17
|
+
expect(str[0, 8]).to eq('%PDF-1.4')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'a new extended graphics state should be created for ' \
|
21
|
+
'each unique transparency setting' do
|
22
|
+
make_transparent(0.5, 0.2) do
|
23
|
+
make_transparent(0.5, 0.75)
|
24
|
+
end
|
25
|
+
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
|
26
|
+
expect(extgstates.length).to eq(2)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'a new extended graphics state should not be created for ' \
|
30
|
+
'each duplicate transparency setting' do
|
31
|
+
make_transparent(0.5, 0.75) do
|
32
|
+
make_transparent(0.5, 0.75)
|
33
|
+
end
|
34
|
+
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
|
35
|
+
expect(extgstates.length).to eq(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'setting the transparency with only one parameter sets the ' \
|
39
|
+
'transparency for both the fill and the stroke' do
|
40
|
+
make_transparent(0.5)
|
41
|
+
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
|
42
|
+
expect(extgstate[:opacity]).to eq(0.5)
|
43
|
+
expect(extgstate[:stroke_opacity]).to eq(0.5)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'setting the transparency with a numerical parameter and ' \
|
47
|
+
'a :stroke should set the fill transparency to the numerical parameter ' \
|
48
|
+
'and the stroke transparency to the option' do
|
49
|
+
make_transparent(0.5, 0.2)
|
50
|
+
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
|
51
|
+
expect(extgstate[:opacity]).to eq(0.5)
|
52
|
+
expect(extgstate[:stroke_opacity]).to eq(0.2)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does not allow negative values' do
|
56
|
+
make_transparent(-0.5, -0.2)
|
57
|
+
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
|
58
|
+
expect(extgstate[:opacity]).to eq(0.0)
|
59
|
+
expect(extgstate[:stroke_opacity]).to eq(0.0)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'does not allow too big values' do
|
63
|
+
make_transparent(2.0, 3.0)
|
64
|
+
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
|
65
|
+
expect(extgstate[:opacity]).to eq(1.0)
|
66
|
+
expect(extgstate[:stroke_opacity]).to eq(1.0)
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'with more than one page' do
|
70
|
+
it 'the extended graphic state resource should be added to both pages' do
|
71
|
+
make_transparent(0.5, 0.2)
|
72
|
+
pdf.start_new_page
|
73
|
+
make_transparent(0.5, 0.2)
|
74
|
+
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
|
75
|
+
extgstate = extgstates[0]
|
76
|
+
expect(extgstates.length).to eq(2)
|
77
|
+
expect(extgstate[:opacity]).to eq(0.5)
|
78
|
+
expect(extgstate[:stroke_opacity]).to eq(0.2)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,837 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Prawn::Graphics do
|
6
|
+
let(:pdf) { create_pdf }
|
7
|
+
|
8
|
+
describe 'When drawing a line' do
|
9
|
+
it 'draws a line from (100,600) to (100,500)' do
|
10
|
+
pdf.line([100, 600], [100, 500])
|
11
|
+
|
12
|
+
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
13
|
+
|
14
|
+
expect(line_drawing.points).to eq([[100, 600], [100, 500]])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'draws two lines at (100,600) to (100,500) and (75,100) to (50,125)' do
|
18
|
+
pdf.line(100, 600, 100, 500)
|
19
|
+
pdf.line(75, 100, 50, 125)
|
20
|
+
|
21
|
+
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
22
|
+
|
23
|
+
expect(line_drawing.points).to eq(
|
24
|
+
[[100.0, 600.0], [100.0, 500.0], [75.0, 100.0], [50.0, 125.0]]
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'properlies set line width via line_width=' do
|
29
|
+
pdf.line_width = 10
|
30
|
+
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
31
|
+
expect(line.widths.first).to eq(10)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'properlies set line width via line_width(width)' do
|
35
|
+
pdf.line_width(10)
|
36
|
+
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
37
|
+
expect(line.widths.first).to eq(10)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'carries the current line width settings over to new pages' do
|
41
|
+
pdf.line_width(10)
|
42
|
+
pdf.start_new_page
|
43
|
+
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
44
|
+
expect(line.widths.length).to eq(2)
|
45
|
+
expect(line.widths[1]).to eq(10)
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '(Horizontally)' do
|
49
|
+
it 'draws from [x1,pdf.y],[x2,pdf.y]' do
|
50
|
+
pdf.horizontal_line(100, 150)
|
51
|
+
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
52
|
+
expect(line.points).to eq([
|
53
|
+
[100.0 + pdf.bounds.absolute_left, pdf.y],
|
54
|
+
[150.0 + pdf.bounds.absolute_left, pdf.y]
|
55
|
+
])
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'draws a line from (200, 250) to (300, 250)' do
|
59
|
+
pdf.horizontal_line(200, 300, at: 250)
|
60
|
+
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
61
|
+
expect(line_drawing.points).to eq([[200, 250], [300, 250]])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '(Vertically)' do
|
66
|
+
it 'draws a line from (350, 300) to (350, 400)' do
|
67
|
+
pdf.vertical_line(300, 400, at: 350)
|
68
|
+
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
69
|
+
expect(line_drawing.points).to eq([[350, 300], [350, 400]])
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'requires a y coordinate' do
|
73
|
+
expect { pdf.vertical_line(400, 500) }
|
74
|
+
.to raise_error(ArgumentError)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'When drawing a polygon' do
|
80
|
+
it 'draws each line passed to polygon()' do
|
81
|
+
pdf.polygon([100, 500], [100, 400], [200, 400])
|
82
|
+
|
83
|
+
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
|
84
|
+
expect(line_drawing.points)
|
85
|
+
.to eq([[100, 500], [100, 400], [200, 400], [100, 500]])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'When drawing a rectangle' do
|
90
|
+
it 'uses a point, width, and height for coords' do
|
91
|
+
pdf.rectangle [200, 200], 50, 100
|
92
|
+
|
93
|
+
rectangles = PDF::Inspector::Graphics::Rectangle.analyze(pdf.render)
|
94
|
+
.rectangles
|
95
|
+
# PDF uses bottom left corner
|
96
|
+
expect(rectangles[0][:point]).to eq([200, 100])
|
97
|
+
expect(rectangles[0][:width]).to eq(50)
|
98
|
+
expect(rectangles[0][:height]).to eq(100)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'When drawing a curve' do
|
103
|
+
it 'draws a bezier curve from 50,50 to 100,100' do
|
104
|
+
pdf.move_to [50, 50]
|
105
|
+
pdf.curve_to [100, 100], bounds: [[20, 90], [90, 70]]
|
106
|
+
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
|
107
|
+
expect(curve.coords)
|
108
|
+
.to eq([50.0, 50.0, 20.0, 90.0, 90.0, 70.0, 100.0, 100.0])
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'draws a bezier curve from 100,100 to 50,50' do
|
112
|
+
pdf.curve [100, 100], [50, 50], bounds: [[20, 90], [90, 75]]
|
113
|
+
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
|
114
|
+
expect(curve.coords)
|
115
|
+
.to eq([100.0, 100.0, 20.0, 90.0, 90.0, 75.0, 50.0, 50.0])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'When drawing a rounded rectangle' do
|
120
|
+
before { pdf.rounded_rectangle([50, 550], 50, 100, 10) }
|
121
|
+
|
122
|
+
let(:original_point) do
|
123
|
+
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
|
124
|
+
curve_points = curve.coords.each_slice(2).to_a
|
125
|
+
curve_points.shift
|
126
|
+
end
|
127
|
+
let(:all_coords) do
|
128
|
+
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
|
129
|
+
curve_points = curve.coords.each_slice(2).to_a
|
130
|
+
curve_points.shift
|
131
|
+
curves = curve_points.each_slice(3).to_a
|
132
|
+
line_points = PDF::Inspector::Graphics::Line.analyze(pdf.render).points
|
133
|
+
line_points.shift
|
134
|
+
line_points.zip(curves).flatten.each_slice(2).to_a.unshift original_point
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'draws a rectangle by connecting lines with rounded bezier curves' do
|
138
|
+
expect(all_coords).to eq(
|
139
|
+
[
|
140
|
+
[60.0, 550.0], [90.0, 550.0], [95.5228, 550.0], [100.0, 545.5228],
|
141
|
+
[100.0, 540.0], [100.0, 460.0], [100.0, 454.4772], [95.5228, 450.0],
|
142
|
+
[90.0, 450.0], [60.0, 450.0], [54.4772, 450.0], [50.0, 454.4772],
|
143
|
+
[50.0, 460.0], [50.0, 540.0], [50.0, 545.5228], [54.4772, 550.0],
|
144
|
+
[60.0, 550.0]
|
145
|
+
]
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'starts and end with the same point' do
|
150
|
+
expect(original_point).to eq(all_coords.last)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe 'When drawing an ellipse' do
|
155
|
+
let(:curve) do
|
156
|
+
pdf.ellipse [100, 100], 25, 50
|
157
|
+
PDF::Inspector::Graphics::Curve.analyze(pdf.render)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'uses a Bézier approximation' do
|
161
|
+
expect(curve.coords).to eq([
|
162
|
+
125.0, 100.0,
|
163
|
+
|
164
|
+
125.0, 127.6142,
|
165
|
+
113.8071, 150,
|
166
|
+
100.0, 150.0,
|
167
|
+
|
168
|
+
86.1929, 150.0,
|
169
|
+
75.0, 127.6142,
|
170
|
+
75.0, 100.0,
|
171
|
+
|
172
|
+
75.0, 72.3858,
|
173
|
+
86.1929, 50.0,
|
174
|
+
100.0, 50.0,
|
175
|
+
|
176
|
+
113.8071, 50.0,
|
177
|
+
125.0, 72.3858,
|
178
|
+
125.0, 100.0,
|
179
|
+
|
180
|
+
100.0, 100.0
|
181
|
+
])
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'moves the pointer to the center of the ellipse after drawing' do
|
185
|
+
expect(curve.coords[-2..-1]).to eq([100, 100])
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'When drawing a circle' do
|
190
|
+
let(:curve) do
|
191
|
+
pdf.circle [100, 100], 25
|
192
|
+
pdf.ellipse [100, 100], 25, 25
|
193
|
+
PDF::Inspector::Graphics::Curve.analyze(pdf.render)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'strokes the same path as the equivalent ellipse' do
|
197
|
+
middle = curve.coords.length / 2
|
198
|
+
expect(curve.coords[0...middle]).to eq(curve.coords[middle..-1])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe 'When filling' do
|
203
|
+
it 'defaults to the f operator (nonzero winding number rule)' do
|
204
|
+
allow(pdf.renderer).to receive(:add_content).with('f')
|
205
|
+
pdf.fill
|
206
|
+
expect(pdf.renderer).to have_received(:add_content).with('f')
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'uses f* for :fill_rule => :even_odd' do
|
210
|
+
allow(pdf.renderer).to receive(:add_content).with('f*')
|
211
|
+
pdf.fill(fill_rule: :even_odd)
|
212
|
+
expect(pdf.renderer).to have_received(:add_content).with('f*')
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'uses b by default for fill_and_stroke (nonzero winding number)' do
|
216
|
+
allow(pdf.renderer).to receive(:add_content).with('b')
|
217
|
+
pdf.fill_and_stroke
|
218
|
+
expect(pdf.renderer).to have_received(:add_content).with('b')
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'uses b* for fill_and_stroke(:fill_rule => :even_odd)' do
|
222
|
+
allow(pdf.renderer).to receive(:add_content).with('b*')
|
223
|
+
pdf.fill_and_stroke(fill_rule: :even_odd)
|
224
|
+
expect(pdf.renderer).to have_received(:add_content).with('b*')
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'When setting colors' do
|
229
|
+
it 'sets stroke colors' do
|
230
|
+
pdf.stroke_color 'ffcccc'
|
231
|
+
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
|
232
|
+
# 100% red, 80% green, 80% blue
|
233
|
+
expect(colors.stroke_color).to eq([1.0, 0.8, 0.8])
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'sets fill colors' do
|
237
|
+
pdf.fill_color 'ccff00'
|
238
|
+
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
|
239
|
+
# 80% red, 100% green, 0% blue
|
240
|
+
expect(colors.fill_color).to eq([0.8, 1.0, 0])
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'raises an error for a color with a leading #' do
|
244
|
+
expect { pdf.fill_color '#ccff00' }.to raise_error(ArgumentError)
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'raises an error for a color string that is not a hex' do
|
248
|
+
expect { pdf.fill_color 'zcff00' }.to raise_error(ArgumentError)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'raises an error for a color string with invalid characters' do
|
252
|
+
expect { pdf.fill_color 'f0f0f?' }.to raise_error(ArgumentError)
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'resets the colors on each new page if they have been defined' do
|
256
|
+
pdf.fill_color 'ccff00'
|
257
|
+
|
258
|
+
pdf.start_new_page
|
259
|
+
pdf.stroke_color 'ff00cc'
|
260
|
+
|
261
|
+
pdf.start_new_page
|
262
|
+
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
|
263
|
+
expect(colors.fill_color_count).to eq(3)
|
264
|
+
expect(colors.stroke_color_count).to eq(2)
|
265
|
+
|
266
|
+
expect(colors.fill_color).to eq([0.8, 1.0, 0.0])
|
267
|
+
expect(colors.stroke_color).to eq([1.0, 0.0, 0.8])
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'sets the color space when setting colors on new pages to please fussy '\
|
271
|
+
'readers' do
|
272
|
+
pdf.stroke_color '000000'
|
273
|
+
pdf.stroke { pdf.rectangle([10, 10], 10, 10) }
|
274
|
+
pdf.start_new_page
|
275
|
+
pdf.stroke_color '000000'
|
276
|
+
pdf.stroke { pdf.rectangle([10, 10], 10, 10) }
|
277
|
+
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
|
278
|
+
expect(colors.stroke_color_space_count[:DeviceRGB]).to eq(2)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe 'Patterns' do
|
283
|
+
describe 'linear gradients' do
|
284
|
+
it 'creates a /Pattern resource' do
|
285
|
+
pdf.fill_gradient(
|
286
|
+
[0, pdf.bounds.height],
|
287
|
+
[pdf.bounds.width, pdf.bounds.height],
|
288
|
+
'FF0000', '0000FF'
|
289
|
+
)
|
290
|
+
|
291
|
+
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
|
292
|
+
pattern = grad.patterns.values.first
|
293
|
+
|
294
|
+
expect(pattern).to_not be_nil
|
295
|
+
expect(pattern[:Shading][:ShadingType]).to eq(2)
|
296
|
+
expect(pattern[:Shading][:Coords]).to eq([0, 0, pdf.bounds.width, 0])
|
297
|
+
expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? do |x1, x2|
|
298
|
+
(x1 - x2).abs < 0.01
|
299
|
+
end).to eq true
|
300
|
+
expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? do |x1, x2|
|
301
|
+
(x1 - x2).abs < 0.01
|
302
|
+
end).to eq true
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'creates a unique ID for each pattern resource' do
|
306
|
+
pdf.fill_gradient(
|
307
|
+
[256, 512],
|
308
|
+
[356, 512],
|
309
|
+
'ffffff', 'fe00ff'
|
310
|
+
)
|
311
|
+
pdf.fill_gradient(
|
312
|
+
[256, 256],
|
313
|
+
[356, 256],
|
314
|
+
'ffffff', '0000ff'
|
315
|
+
)
|
316
|
+
|
317
|
+
str = pdf.render
|
318
|
+
pattern_ids = str.scan(/SP\h{40}\s+scn/)
|
319
|
+
expect(pattern_ids.uniq.length).to eq 2
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'fill_gradient should set fill color to the pattern' do
|
323
|
+
pdf.fill_gradient(
|
324
|
+
[0, pdf.bounds.height],
|
325
|
+
[pdf.bounds.width, pdf.bounds.height],
|
326
|
+
'FF0000', '0000FF'
|
327
|
+
)
|
328
|
+
|
329
|
+
str = pdf.render
|
330
|
+
expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'stroke_gradient should set stroke color to the pattern' do
|
334
|
+
pdf.stroke_gradient(
|
335
|
+
[0, pdf.bounds.height],
|
336
|
+
[pdf.bounds.width, pdf.bounds.height],
|
337
|
+
'FF0000', '0000FF'
|
338
|
+
)
|
339
|
+
|
340
|
+
str = pdf.render
|
341
|
+
expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'uses a stitching function to render a gradient with multiple stops' do
|
345
|
+
pdf.fill_gradient(
|
346
|
+
from: [0, pdf.bounds.height],
|
347
|
+
to: [pdf.bounds.width, pdf.bounds.height],
|
348
|
+
stops: { 0 => 'FF0000', 0.8 => '00FF00', 1 => '0000FF' }
|
349
|
+
)
|
350
|
+
|
351
|
+
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
|
352
|
+
pattern = grad.patterns.values.first
|
353
|
+
|
354
|
+
expect(pattern).to_not be_nil
|
355
|
+
|
356
|
+
stitching = pattern[:Shading][:Function]
|
357
|
+
expect(stitching[:FunctionType]).to eq(3)
|
358
|
+
expect(stitching[:Functions]).to be_an(Array)
|
359
|
+
expect(stitching[:Functions].map { |f| f[:C0] })
|
360
|
+
.to eq([[1, 0, 0], [0, 1, 0]])
|
361
|
+
expect(stitching[:Functions].map { |f| f[:C1] })
|
362
|
+
.to eq([[0, 1, 0], [0, 0, 1]])
|
363
|
+
expect(stitching[:Bounds]).to eq([0.8])
|
364
|
+
expect(stitching[:Encode]).to eq([0, 1, 0, 1])
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'uses a stitching function to render a gradient with equally spaced '\
|
368
|
+
'stops' do
|
369
|
+
pdf.fill_gradient(
|
370
|
+
from: [0, pdf.bounds.height],
|
371
|
+
to: [pdf.bounds.width, pdf.bounds.height],
|
372
|
+
stops: %w[FF0000 00FF00 0000FF]
|
373
|
+
)
|
374
|
+
|
375
|
+
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
|
376
|
+
pattern = grad.patterns.values.first
|
377
|
+
|
378
|
+
expect(pattern).to_not be_nil
|
379
|
+
|
380
|
+
stitching = pattern[:Shading][:Function]
|
381
|
+
expect(stitching[:FunctionType]).to eq(3)
|
382
|
+
expect(stitching[:Functions]).to be_an(Array)
|
383
|
+
expect(stitching[:Functions].map { |f| f[:C0] })
|
384
|
+
.to eq([[1, 0, 0], [0, 1, 0]])
|
385
|
+
expect(stitching[:Functions].map { |f| f[:C1] })
|
386
|
+
.to eq([[0, 1, 0], [0, 0, 1]])
|
387
|
+
expect(stitching[:Bounds]).to eq([0.5])
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe 'radial gradients' do
|
392
|
+
it 'creates a /Pattern resource' do
|
393
|
+
pdf.fill_gradient(
|
394
|
+
[0, pdf.bounds.height], 10,
|
395
|
+
[pdf.bounds.width, pdf.bounds.height], 20,
|
396
|
+
'FF0000', '0000FF'
|
397
|
+
)
|
398
|
+
|
399
|
+
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
|
400
|
+
pattern = grad.patterns.values.first
|
401
|
+
|
402
|
+
expect(pattern).to_not be_nil
|
403
|
+
expect(pattern[:Shading][:ShadingType]).to eq(3)
|
404
|
+
expect(pattern[:Shading][:Coords])
|
405
|
+
.to eq([0, 0, 10, pdf.bounds.width, 0, 20])
|
406
|
+
expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? do |x1, x2|
|
407
|
+
(x1 - x2).abs < 0.01
|
408
|
+
end).to eq true
|
409
|
+
expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? do |x1, x2|
|
410
|
+
(x1 - x2).abs < 0.01
|
411
|
+
end).to eq true
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'fill_gradient should set fill color to the pattern' do
|
415
|
+
pdf.fill_gradient(
|
416
|
+
[0, pdf.bounds.height], 10,
|
417
|
+
[pdf.bounds.width, pdf.bounds.height], 20,
|
418
|
+
'FF0000', '0000FF'
|
419
|
+
)
|
420
|
+
|
421
|
+
str = pdf.render
|
422
|
+
expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'stroke_gradient should set stroke color to the pattern' do
|
426
|
+
pdf.stroke_gradient(
|
427
|
+
[0, pdf.bounds.height], 10,
|
428
|
+
[pdf.bounds.width, pdf.bounds.height], 20,
|
429
|
+
'FF0000', '0000FF'
|
430
|
+
)
|
431
|
+
|
432
|
+
str = pdf.render
|
433
|
+
expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe 'gradient transformations' do
|
438
|
+
subject(:transformations) do
|
439
|
+
pdf.scale 2 do
|
440
|
+
pdf.translate 40, 40 do
|
441
|
+
pdf.fill_gradient [0, 10], [15, 15], 'FF0000', '0000FF', opts
|
442
|
+
pdf.fill_gradient [0, 10], 15, [15, 15], 25, 'FF0000', '0000FF',
|
443
|
+
opts
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
|
448
|
+
grad.patterns.values.map { |pattern| pattern[:Matrix] }.uniq
|
449
|
+
end
|
450
|
+
|
451
|
+
context 'when :apply_transformations is true' do
|
452
|
+
let(:opts) { { apply_transformations: true } }
|
453
|
+
|
454
|
+
it 'uses the transformation stack to translate user co-ordinates to '\
|
455
|
+
'document co-ordinates required by /Pattern' do
|
456
|
+
expect(transformations).to eq([[2, 0, 0, 2, 80, 100]])
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
context 'when :apply_transformations is false' do
|
461
|
+
let(:opts) { { apply_transformations: false } }
|
462
|
+
|
463
|
+
it "doesn't transform the gradient" do
|
464
|
+
expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
context 'when :apply_transformations is unset' do
|
469
|
+
let(:opts) { {} }
|
470
|
+
|
471
|
+
it "doesn't transform the gradient and displays a warning" do
|
472
|
+
allow(pdf).to receive(:warn).twice
|
473
|
+
expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
|
474
|
+
expect(pdf).to have_received(:warn).twice
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
describe 'When using painting shortcuts' do
|
481
|
+
it 'converts stroke_some_method(args) into some_method(args); stroke' do
|
482
|
+
allow(pdf).to receive(:line_to).with([100, 100])
|
483
|
+
allow(pdf).to receive(:stroke)
|
484
|
+
|
485
|
+
pdf.stroke_line_to [100, 100]
|
486
|
+
|
487
|
+
expect(pdf).to have_received(:line_to).with([100, 100])
|
488
|
+
expect(pdf).to have_received(:stroke)
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'converts fill_some_method(args) into some_method(args); fill' do
|
492
|
+
allow(pdf).to receive(:line_to).with([100, 100])
|
493
|
+
allow(pdf).to receive(:fill)
|
494
|
+
|
495
|
+
pdf.fill_line_to [100, 100]
|
496
|
+
|
497
|
+
expect(pdf).to have_received(:line_to).with([100, 100])
|
498
|
+
expect(pdf).to have_received(:fill)
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'does not break method_missing' do
|
502
|
+
expect { pdf.i_have_a_pretty_girlfriend_named_jia }
|
503
|
+
.to raise_error(NoMethodError)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
describe 'When using graphics states' do
|
508
|
+
it 'adds the right content on save_graphics_state' do
|
509
|
+
allow(pdf.renderer).to receive(:add_content).with('q')
|
510
|
+
|
511
|
+
pdf.save_graphics_state
|
512
|
+
|
513
|
+
expect(pdf.renderer).to have_received(:add_content).with('q')
|
514
|
+
end
|
515
|
+
|
516
|
+
it 'adds the right content on restore_graphics_state' do
|
517
|
+
allow(pdf.renderer).to receive(:add_content).with('Q')
|
518
|
+
|
519
|
+
pdf.restore_graphics_state
|
520
|
+
|
521
|
+
expect(pdf.renderer).to have_received(:add_content).with('Q')
|
522
|
+
end
|
523
|
+
|
524
|
+
it 'saves and restore when save_graphics_state is used with a block' do
|
525
|
+
allow(pdf.renderer).to receive(:add_content).with('q')
|
526
|
+
allow(pdf).to receive(:foo)
|
527
|
+
allow(pdf.renderer).to receive(:add_content).with('Q')
|
528
|
+
|
529
|
+
pdf.save_graphics_state do
|
530
|
+
pdf.foo
|
531
|
+
end
|
532
|
+
|
533
|
+
expect(pdf.renderer).to have_received(:add_content).with('q').ordered
|
534
|
+
expect(pdf).to have_received(:foo).ordered
|
535
|
+
expect(pdf.renderer).to have_received(:add_content).with('Q').ordered
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'adds the previous color space when restoring to a graphic state with '\
|
539
|
+
'different color space' do
|
540
|
+
pdf.stroke_color '000000'
|
541
|
+
pdf.save_graphics_state
|
542
|
+
pdf.stroke_color 0, 0, 0, 0
|
543
|
+
pdf.restore_graphics_state
|
544
|
+
pdf.stroke_color 0, 0, 100, 0
|
545
|
+
expect(pdf.graphic_state.color_space).to eq(stroke: :DeviceCMYK)
|
546
|
+
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
|
547
|
+
expect(colors.color_space).to eq(:DeviceCMYK)
|
548
|
+
expect(colors.stroke_color_space_count[:DeviceCMYK]).to eq(2)
|
549
|
+
end
|
550
|
+
|
551
|
+
it 'uses the correct dash setting after restoring and starting new page' do
|
552
|
+
pdf.dash 5
|
553
|
+
pdf.save_graphics_state
|
554
|
+
pdf.dash 10
|
555
|
+
expect(pdf.graphic_state.dash[:dash]).to eq(10)
|
556
|
+
pdf.restore_graphics_state
|
557
|
+
pdf.start_new_page
|
558
|
+
expect(pdf.graphic_state.dash[:dash]).to eq(5)
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'rounds dash values to four decimal places' do
|
562
|
+
pdf.dash 5.12345
|
563
|
+
expect(pdf.graphic_state.dash_setting).to eq('[5.1235 5.1235] 0.0 d')
|
564
|
+
end
|
565
|
+
|
566
|
+
it 'raises an error when dash is called w. a zero length or space' do
|
567
|
+
expect { pdf.dash(0) }.to raise_error(ArgumentError)
|
568
|
+
expect { pdf.dash([0]) }.to raise_error(ArgumentError)
|
569
|
+
expect { pdf.dash([0, 0]) }.to raise_error(ArgumentError)
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'raises an error when dash is called w. negative lengths' do
|
573
|
+
expect { pdf.dash(-1) }.to raise_error(ArgumentError)
|
574
|
+
expect { pdf.dash([1, -3]) }.to raise_error(ArgumentError)
|
575
|
+
end
|
576
|
+
|
577
|
+
it 'the current graphic state keeps track of previous unchanged settings' do
|
578
|
+
pdf.stroke_color '000000'
|
579
|
+
pdf.save_graphics_state
|
580
|
+
pdf.dash 5
|
581
|
+
pdf.save_graphics_state
|
582
|
+
pdf.cap_style :round
|
583
|
+
pdf.save_graphics_state
|
584
|
+
pdf.fill_color 0, 0, 100, 0
|
585
|
+
pdf.save_graphics_state
|
586
|
+
|
587
|
+
expect(pdf.graphic_state.stroke_color).to eq('000000')
|
588
|
+
expect(pdf.graphic_state.join_style).to eq(:miter)
|
589
|
+
expect(pdf.graphic_state.fill_color).to eq([0, 0, 100, 0])
|
590
|
+
expect(pdf.graphic_state.cap_style).to eq(:round)
|
591
|
+
expect(pdf.graphic_state.color_space)
|
592
|
+
.to eq(fill: :DeviceCMYK, stroke: :DeviceRGB)
|
593
|
+
expect(pdf.graphic_state.dash).to eq(space: 5, phase: 0, dash: 5)
|
594
|
+
expect(pdf.graphic_state.line_width).to eq(1)
|
595
|
+
end
|
596
|
+
|
597
|
+
it "doesn't add extra graphic space closings when rendering multiple " \
|
598
|
+
'times' do
|
599
|
+
pdf.render
|
600
|
+
state = PDF::Inspector::Graphics::State.analyze(pdf.render)
|
601
|
+
expect(state.save_graphics_state_count).to eq(1)
|
602
|
+
expect(state.restore_graphics_state_count).to eq(1)
|
603
|
+
end
|
604
|
+
|
605
|
+
it 'adds extra graphic state enclosings when content is added on multiple '\
|
606
|
+
'renderings' do
|
607
|
+
pdf.render
|
608
|
+
pdf.text 'Adding a bit more content'
|
609
|
+
state = PDF::Inspector::Graphics::State.analyze(pdf.render)
|
610
|
+
expect(state.save_graphics_state_count).to eq(2)
|
611
|
+
expect(state.restore_graphics_state_count).to eq(2)
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'adds extra graphic state enclosings when new settings are applied on '\
|
615
|
+
'multiple renderings' do
|
616
|
+
pdf.render
|
617
|
+
pdf.stroke_color 0, 0, 0, 0
|
618
|
+
state = PDF::Inspector::Graphics::State.analyze(pdf.render)
|
619
|
+
expect(state.save_graphics_state_count).to eq(2)
|
620
|
+
expect(state.restore_graphics_state_count).to eq(2)
|
621
|
+
end
|
622
|
+
|
623
|
+
it 'raise_errors error if closing an empty graphic stack' do
|
624
|
+
expect do
|
625
|
+
pdf.render
|
626
|
+
pdf.restore_graphics_state
|
627
|
+
end.to raise_error(PDF::Core::Errors::EmptyGraphicStateStack)
|
628
|
+
end
|
629
|
+
|
630
|
+
it 'copies mutable attributes when passing a previous_state to '\
|
631
|
+
'the initializer' do
|
632
|
+
new_state = PDF::Core::GraphicState.new(pdf.graphic_state)
|
633
|
+
|
634
|
+
%i[color_space dash fill_color stroke_color].each do |attr|
|
635
|
+
expect(new_state.send(attr)).to eq(pdf.graphic_state.send(attr))
|
636
|
+
expect(new_state.send(attr)).to_not equal(pdf.graphic_state.send(attr))
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
it 'copies mutable attributes when duping' do
|
641
|
+
new_state = pdf.graphic_state.dup
|
642
|
+
|
643
|
+
%i[color_space dash fill_color stroke_color].each do |attr|
|
644
|
+
expect(new_state.send(attr)).to eq(pdf.graphic_state.send(attr))
|
645
|
+
expect(new_state.send(attr)).to_not equal(pdf.graphic_state.send(attr))
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
describe 'When using transformation matrix' do
|
651
|
+
# Note: The (approximate) number of significant decimal digits of precision
|
652
|
+
# in fractional part is 5 (PDF Reference, Third Edition, p. 706)
|
653
|
+
|
654
|
+
it 'sends the right content on transformation_matrix' do
|
655
|
+
allow(pdf.renderer).to receive(:add_content)
|
656
|
+
.with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
|
657
|
+
pdf.transformation_matrix 1, 0, 0.123456789, -1.0, 5.5, 20
|
658
|
+
expect(pdf.renderer).to have_received(:add_content)
|
659
|
+
.with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
|
660
|
+
end
|
661
|
+
|
662
|
+
it 'uses fixed digits with very small number' do
|
663
|
+
values = Array.new(6, 0.000000000001)
|
664
|
+
string = Array.new(6, '0.0').join ' '
|
665
|
+
allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
|
666
|
+
pdf.transformation_matrix(*values)
|
667
|
+
expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
|
668
|
+
end
|
669
|
+
|
670
|
+
it 'is received by the inspector' do
|
671
|
+
pdf.transformation_matrix 1, 0, 0, -1, 5.5, 20
|
672
|
+
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
|
673
|
+
expect(matrices.matrices).to eq([[1, 0, 0, -1, 5.5, 20]])
|
674
|
+
end
|
675
|
+
|
676
|
+
it 'saves the graphics state inside the given block' do
|
677
|
+
values = Array.new(6, 0.000000000001)
|
678
|
+
string = Array.new(6, '0.0').join ' '
|
679
|
+
|
680
|
+
allow(pdf).to receive(:save_graphics_state).with(no_args)
|
681
|
+
allow(pdf.renderer).to receive(:add_content).with(any_args).twice
|
682
|
+
allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
|
683
|
+
allow(pdf).to receive(:do_something)
|
684
|
+
allow(pdf).to receive(:restore_graphics_state).with(no_args)
|
685
|
+
|
686
|
+
pdf.transformation_matrix(*values) do
|
687
|
+
pdf.do_something
|
688
|
+
end
|
689
|
+
|
690
|
+
expect(pdf).to have_received(:save_graphics_state).with(no_args).ordered
|
691
|
+
expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
|
692
|
+
.ordered
|
693
|
+
expect(pdf).to have_received(:do_something).ordered
|
694
|
+
expect(pdf).to have_received(:restore_graphics_state).with(no_args)
|
695
|
+
.ordered
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
describe 'When using transformations shortcuts' do
|
700
|
+
let(:x) { 12 }
|
701
|
+
let(:y) { 54.32 }
|
702
|
+
let(:angle) { 12.32 }
|
703
|
+
let(:cos) { Math.cos(angle * Math::PI / 180) }
|
704
|
+
let(:sin) { Math.sin(angle * Math::PI / 180) }
|
705
|
+
let(:factor) { 0.12 }
|
706
|
+
|
707
|
+
describe '#rotate' do
|
708
|
+
it 'rotates' do
|
709
|
+
allow(pdf).to receive(:transformation_matrix)
|
710
|
+
.with(cos, sin, -sin, cos, 0, 0)
|
711
|
+
pdf.rotate(angle)
|
712
|
+
expect(pdf).to have_received(:transformation_matrix)
|
713
|
+
.with(cos, sin, -sin, cos, 0, 0)
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
describe '#rotate with :origin option' do
|
718
|
+
it 'rotates around the origin' do
|
719
|
+
x_prime = x * cos - y * sin
|
720
|
+
y_prime = x * sin + y * cos
|
721
|
+
|
722
|
+
pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
|
723
|
+
|
724
|
+
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
|
725
|
+
expect(matrices.matrices[0]).to eq([
|
726
|
+
1, 0, 0, 1,
|
727
|
+
reduce_precision(x - x_prime),
|
728
|
+
reduce_precision(y - y_prime)
|
729
|
+
])
|
730
|
+
expect(matrices.matrices[1]).to eq([
|
731
|
+
reduce_precision(cos),
|
732
|
+
reduce_precision(sin),
|
733
|
+
reduce_precision(-sin),
|
734
|
+
reduce_precision(cos),
|
735
|
+
0, 0
|
736
|
+
])
|
737
|
+
end
|
738
|
+
|
739
|
+
it 'rotates around the origin in a document with a margin' do
|
740
|
+
pdf = Prawn::Document.new
|
741
|
+
|
742
|
+
pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
|
743
|
+
|
744
|
+
x_ = x + pdf.bounds.absolute_left
|
745
|
+
y_ = y + pdf.bounds.absolute_bottom
|
746
|
+
x_prime = x_ * cos - y_ * sin
|
747
|
+
y_prime = x_ * sin + y_ * cos
|
748
|
+
|
749
|
+
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
|
750
|
+
expect(matrices.matrices[0]).to eq([
|
751
|
+
1, 0, 0, 1,
|
752
|
+
reduce_precision(x_ - x_prime),
|
753
|
+
reduce_precision(y_ - y_prime)
|
754
|
+
])
|
755
|
+
expect(matrices.matrices[1]).to eq([
|
756
|
+
reduce_precision(cos),
|
757
|
+
reduce_precision(sin),
|
758
|
+
reduce_precision(-sin),
|
759
|
+
reduce_precision(cos),
|
760
|
+
0, 0
|
761
|
+
])
|
762
|
+
end
|
763
|
+
|
764
|
+
it 'raise_errors BlockRequired if no block is given' do
|
765
|
+
expect do
|
766
|
+
pdf.rotate(angle, origin: [x, y])
|
767
|
+
end.to raise_error(Prawn::Errors::BlockRequired)
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
describe '#translate' do
|
772
|
+
it 'translates' do
|
773
|
+
x = 12
|
774
|
+
y = 54.32
|
775
|
+
allow(pdf).to receive(:transformation_matrix).with(1, 0, 0, 1, x, y)
|
776
|
+
pdf.translate(x, y)
|
777
|
+
expect(pdf).to have_received(:transformation_matrix)
|
778
|
+
.with(1, 0, 0, 1, x, y)
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
describe '#scale' do
|
783
|
+
it 'scales' do
|
784
|
+
allow(pdf).to receive(:transformation_matrix)
|
785
|
+
.with(factor, 0, 0, factor, 0, 0)
|
786
|
+
pdf.scale(factor)
|
787
|
+
expect(pdf).to have_received(:transformation_matrix)
|
788
|
+
.with(factor, 0, 0, factor, 0, 0)
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
describe '#scale with :origin option' do
|
793
|
+
it 'scales from the origin' do
|
794
|
+
x_prime = factor * x
|
795
|
+
y_prime = factor * y
|
796
|
+
|
797
|
+
pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
|
798
|
+
|
799
|
+
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
|
800
|
+
expect(matrices.matrices[0]).to eq([
|
801
|
+
1, 0, 0, 1,
|
802
|
+
reduce_precision(x - x_prime),
|
803
|
+
reduce_precision(y - y_prime)
|
804
|
+
])
|
805
|
+
expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
|
806
|
+
end
|
807
|
+
|
808
|
+
it 'scales from the origin in a document with a margin' do
|
809
|
+
pdf = Prawn::Document.new
|
810
|
+
x_ = x + pdf.bounds.absolute_left
|
811
|
+
y_ = y + pdf.bounds.absolute_bottom
|
812
|
+
x_prime = factor * x_
|
813
|
+
y_prime = factor * y_
|
814
|
+
|
815
|
+
pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
|
816
|
+
|
817
|
+
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
|
818
|
+
expect(matrices.matrices[0]).to eq([
|
819
|
+
1, 0, 0, 1,
|
820
|
+
reduce_precision(x_ - x_prime),
|
821
|
+
reduce_precision(y_ - y_prime)
|
822
|
+
])
|
823
|
+
expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
|
824
|
+
end
|
825
|
+
|
826
|
+
it 'raise_errors BlockRequired if no block is given' do
|
827
|
+
expect do
|
828
|
+
pdf.scale(factor, origin: [x, y])
|
829
|
+
end.to raise_error(Prawn::Errors::BlockRequired)
|
830
|
+
end
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
def reduce_precision(float)
|
835
|
+
float.round(5)
|
836
|
+
end
|
837
|
+
end
|