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,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'set'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
describe Prawn::Images do
|
8
|
+
let(:pdf) { create_pdf }
|
9
|
+
let(:filename) { "#{Prawn::DATADIR}/images/pigs.jpg" }
|
10
|
+
|
11
|
+
it "onlies embed an image once, even if it's added multiple times" do
|
12
|
+
pdf.image filename, at: [100, 100]
|
13
|
+
pdf.image filename, at: [300, 300]
|
14
|
+
|
15
|
+
output = pdf.render
|
16
|
+
images = PDF::Inspector::XObject.analyze(output)
|
17
|
+
# there should be 2 images in the page resources
|
18
|
+
expect(images.page_xobjects.first.size).to eq(2)
|
19
|
+
# but only 1 image xobject
|
20
|
+
expect(output.scan(%r{/Type /XObject}).size).to eq(1)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns the image info object' do
|
24
|
+
info = pdf.image(filename)
|
25
|
+
|
26
|
+
expect(info).to be_a_kind_of(Prawn::Images::JPG)
|
27
|
+
|
28
|
+
expect(info.height).to eq(453)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'accepts IO objects' do
|
32
|
+
file = File.open(filename, 'rb')
|
33
|
+
info = pdf.image(file)
|
34
|
+
|
35
|
+
expect(info.height).to eq(453)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'rewinds IO objects to be able to embed them multiply' do
|
39
|
+
file = File.open(filename, 'rb')
|
40
|
+
|
41
|
+
pdf.image(file)
|
42
|
+
info = pdf.image(file)
|
43
|
+
expect(info.height).to eq(453)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'does not close passed-in IO objects' do
|
47
|
+
file = File.open(filename, 'rb')
|
48
|
+
_info = pdf.image(file)
|
49
|
+
|
50
|
+
expect(file).to_not be_closed
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'accepts Pathname objects' do
|
54
|
+
info = pdf.image(Pathname.new(filename))
|
55
|
+
expect(info.height).to eq(453)
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'closes opened files again after getting Pathnames', issue: 975 do
|
59
|
+
describe 'spec with File message spy' do
|
60
|
+
let(:not_filename) { 'non-existent filename' }
|
61
|
+
let(:pathname_double) { instance_double('Pathname', file?: true) }
|
62
|
+
|
63
|
+
before do
|
64
|
+
file_content = File.new(filename, 'rb').read
|
65
|
+
allow(Pathname).to receive(:new).with(not_filename) { pathname_double }
|
66
|
+
allow(pathname_double).to receive(:binread) { file_content }
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'uses only binread, which closes opened files' do
|
70
|
+
# this implicitly tests that a file handle is closed again
|
71
|
+
# because only stubbed binread can be called on not_filename
|
72
|
+
_info = pdf.image(not_filename)
|
73
|
+
expect(pathname_double).to have_received(:binread)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
system_has_lsof = system('lsof -v > /dev/null 2>&1')
|
78
|
+
system_has_grep = system('grep --version > /dev/null 2>&1')
|
79
|
+
if system_has_lsof && system_has_grep
|
80
|
+
it 'closes opened files, spec with lsof' do
|
81
|
+
gc_was_disabled = GC.disable # GC of File would close the file
|
82
|
+
open_before = `lsof -c ruby | grep "#{filename}"`
|
83
|
+
_info = pdf.image(Pathname.new(filename))
|
84
|
+
open_after = `lsof -c ruby | grep "#{filename}"`
|
85
|
+
GC.enable unless gc_was_disabled
|
86
|
+
expect(open_after).to eq(open_before)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if RUBY_PLATFORM != 'java'
|
91
|
+
it 'closes opened files, spec with ObjectSpace' do
|
92
|
+
gc_was_disabled = GC.disable # GC of File would close the file
|
93
|
+
open_before = ObjectSpace.each_object(File).count { |f| !f.closed? }
|
94
|
+
_info = pdf.image(Pathname.new(filename))
|
95
|
+
open_after = ObjectSpace.each_object(File).count { |f| !f.closed? }
|
96
|
+
GC.enable unless gc_was_disabled
|
97
|
+
expect(open_after).to eq(open_before)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'setting the length of the bytestream' do
|
103
|
+
it 'correctlies work with images from Pathname objects' do
|
104
|
+
pdf.image(Pathname.new(filename))
|
105
|
+
expect(pdf).to have_parseable_xobjects
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'correctlies work with images from IO objects' do
|
109
|
+
pdf.image(File.open(filename, 'rb'))
|
110
|
+
expect(pdf).to have_parseable_xobjects
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'correctlies work with images from IO objects not set to mode rb' do
|
114
|
+
pdf.image(File.open(filename, 'r'))
|
115
|
+
expect(pdf).to have_parseable_xobjects
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'raise_errors an UnsupportedImageType if passed a BMP' do
|
120
|
+
filename = "#{Prawn::DATADIR}/images/tru256.bmp"
|
121
|
+
expect { pdf.image filename, at: [100, 100] }
|
122
|
+
.to raise_error(Prawn::Errors::UnsupportedImageType)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'raise_errors an UnsupportedImageType if passed an interlaced PNG' do
|
126
|
+
filename = "#{Prawn::DATADIR}/images/dice_interlaced.png"
|
127
|
+
expect { pdf.image filename, at: [100, 100] }
|
128
|
+
.to raise_error(Prawn::Errors::UnsupportedImageType)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'bumps PDF version to 1.5 or greater on embedding 16-bit PNGs' do
|
132
|
+
pdf.image "#{Prawn::DATADIR}/images/16bit.png"
|
133
|
+
expect(pdf.state.version).to be >= 1.5
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'embeds 16-bit alpha channels for 16-bit PNGs' do
|
137
|
+
pdf.image "#{Prawn::DATADIR}/images/16bit.png"
|
138
|
+
|
139
|
+
output = pdf.render
|
140
|
+
expect(output).to match(%r{/BitsPerComponent 16})
|
141
|
+
expect(output).to_not match(%r{/BitsPerComponent 8})
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'flows an image to a new page if it will not fit on a page' do
|
145
|
+
pdf.image filename, fit: [600, 600]
|
146
|
+
pdf.image filename, fit: [600, 600]
|
147
|
+
output = StringIO.new(pdf.render, 'r+')
|
148
|
+
hash = PDF::Reader::ObjectHash.new(output)
|
149
|
+
pages = hash.values.find do |obj|
|
150
|
+
obj.is_a?(Hash) && obj[:Type] == :Pages
|
151
|
+
end[:Kids]
|
152
|
+
expect(pages.size).to eq(2)
|
153
|
+
expect(hash[pages[0]][:Resources][:XObject].keys).to eq([:I1])
|
154
|
+
expect(hash[pages[1]][:Resources][:XObject].keys).to eq([:I2])
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'does not flow an image to a new page if it will fit on one page' do
|
158
|
+
pdf.image filename, fit: [400, 400]
|
159
|
+
pdf.image filename, fit: [400, 400]
|
160
|
+
output = StringIO.new(pdf.render, 'r+')
|
161
|
+
hash = PDF::Reader::ObjectHash.new(output)
|
162
|
+
pages = hash.values.find do |obj|
|
163
|
+
obj.is_a?(Hash) && obj[:Type] == :Pages
|
164
|
+
end[:Kids]
|
165
|
+
expect(pages.size).to eq(1)
|
166
|
+
expect(Set.new(hash[pages[0]][:Resources][:XObject].keys)).to eq(
|
167
|
+
Set.new(%i[I1 I2])
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'does not start a new page just for a stretchy bounding box' do
|
172
|
+
allow(pdf).to receive(:start_new_page)
|
173
|
+
pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width) do
|
174
|
+
pdf.image filename
|
175
|
+
end
|
176
|
+
expect(pdf).to_not have_received(:start_new_page)
|
177
|
+
end
|
178
|
+
|
179
|
+
describe ':fit option' do
|
180
|
+
it 'fits inside the defined constraints' do
|
181
|
+
info = pdf.image filename, fit: [100, 400]
|
182
|
+
expect(info.scaled_width).to be <= 100
|
183
|
+
expect(info.scaled_height).to be <= 400
|
184
|
+
|
185
|
+
info = pdf.image filename, fit: [400, 100]
|
186
|
+
expect(info.scaled_width).to be <= 400
|
187
|
+
expect(info.scaled_height).to be <= 100
|
188
|
+
|
189
|
+
info = pdf.image filename, fit: [604, 453]
|
190
|
+
expect(info.scaled_width).to eq(604)
|
191
|
+
expect(info.scaled_height).to eq(453)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'moves text position' do
|
195
|
+
y = pdf.y
|
196
|
+
pdf.image filename, fit: [100, 400]
|
197
|
+
expect(pdf.y).to be < y
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe ':at option' do
|
202
|
+
it 'does not move text position' do
|
203
|
+
y = pdf.y
|
204
|
+
pdf.image filename, at: [100, 400]
|
205
|
+
expect(pdf.y).to eq(y)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe ':width option without :height option' do
|
210
|
+
it 'scales the width and height' do
|
211
|
+
info = pdf.image filename, width: 225
|
212
|
+
expect(info.scaled_height).to eq(168.75)
|
213
|
+
expect(info.scaled_width).to eq(225.0)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe ':height option without :width option' do
|
218
|
+
it 'scales the width and height' do
|
219
|
+
info = pdf.image filename, height: 225
|
220
|
+
expect(info.scaled_height).to eq(225.0)
|
221
|
+
expect(info.scaled_width).to eq(300.0)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'prawn/measurement_extensions'
|
5
|
+
|
6
|
+
describe Prawn::Measurements do
|
7
|
+
describe 'Numeric extensions' do
|
8
|
+
it 'converts units to PostScriptPoints' do
|
9
|
+
expect(1.mm).to be_within(0.000000001).of(2.834645669)
|
10
|
+
expect(1.mm).to eq(72 / 25.4)
|
11
|
+
expect(2.mm).to eq(2 * 72 / 25.4)
|
12
|
+
expect(3.mm).to eq(3 * 72 / 25.4)
|
13
|
+
expect(-3.mm).to eq(-3 * 72 / 25.4)
|
14
|
+
expect(1.cm).to eq(10 * 72 / 25.4)
|
15
|
+
expect(1.dm).to eq(100 * 72 / 25.4)
|
16
|
+
expect(1.m).to eq(1000 * 72 / 25.4)
|
17
|
+
|
18
|
+
expect(1.in).to eq(72)
|
19
|
+
expect(1.ft).to eq(72 * 12)
|
20
|
+
expect(1.yd).to eq(72 * 12 * 3)
|
21
|
+
expect(1.pt).to eq(1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,412 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Prawn::Outline do
|
6
|
+
let(:pdf) do
|
7
|
+
Prawn::Document.new do
|
8
|
+
text 'Page 1. This is the first Chapter. '
|
9
|
+
start_new_page
|
10
|
+
text 'Page 2. More in the first Chapter. '
|
11
|
+
start_new_page
|
12
|
+
outline.define do
|
13
|
+
section 'Chapter 1', destination: 1, closed: true do
|
14
|
+
page destination: 1, title: 'Page 1'
|
15
|
+
page destination: 2, title: 'Page 2'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:output) { StringIO.new(pdf.render, 'r+') }
|
22
|
+
let(:hash) { PDF::Reader::ObjectHash.new(output) }
|
23
|
+
let(:outline_root) do
|
24
|
+
hash.values.find do |obj|
|
25
|
+
obj.is_a?(Hash) && obj[:Type] == :Outlines
|
26
|
+
end
|
27
|
+
end
|
28
|
+
let(:pages) do
|
29
|
+
hash.values.find do |obj|
|
30
|
+
obj.is_a?(Hash) && obj[:Type] == :Pages
|
31
|
+
end[:Kids]
|
32
|
+
end
|
33
|
+
let(:section1) { find_by_title('Chapter 1') }
|
34
|
+
let(:page1) { find_by_title('Page 1') }
|
35
|
+
let(:page2) { find_by_title('Page 2') }
|
36
|
+
let(:section2) { find_by_title('Added Section') }
|
37
|
+
let(:page3) { find_by_title('Page 3') }
|
38
|
+
let(:inserted_page) { find_by_title('Inserted Page') }
|
39
|
+
let(:subsection) { find_by_title('Added SubSection') }
|
40
|
+
let(:added_page3) { find_by_title('Added Page 3') }
|
41
|
+
let(:custom_dest) { find_by_title('Custom Destination') }
|
42
|
+
|
43
|
+
describe 'outline encoding' do
|
44
|
+
it 'stores all outline titles as UTF-16' do
|
45
|
+
hash.each_value do |obj|
|
46
|
+
next unless obj.is_a?(Hash) && obj[:Title]
|
47
|
+
|
48
|
+
title = obj[:Title].dup
|
49
|
+
title.force_encoding(Encoding::UTF_16LE)
|
50
|
+
expect(title.valid_encoding?).to eq(true)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#generate_outline' do
|
56
|
+
it 'creates a root outline dictionary item' do
|
57
|
+
expect(outline_root).to_not be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'sets the first and last top items of the root outline dictionary '\
|
61
|
+
'item' do
|
62
|
+
expect(referenced_object(outline_root[:First])).to eq(section1)
|
63
|
+
expect(referenced_object(outline_root[:Last])).to eq(section1)
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#create_outline_item' do
|
67
|
+
it 'creates outline items for each section and page' do
|
68
|
+
expect(section1).to_not be_nil
|
69
|
+
expect(page1).to_not be_nil
|
70
|
+
expect(page2).to_not be_nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#set_relations, #set_variables_for_block, and #reset_parent' do
|
75
|
+
it 'links sibling items' do
|
76
|
+
expect(referenced_object(page1[:Next])).to eq(page2)
|
77
|
+
expect(referenced_object(page2[:Prev])).to eq(page1)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'links child items to parent item' do
|
81
|
+
expect(referenced_object(page1[:Parent])).to eq(section1)
|
82
|
+
expect(referenced_object(page2[:Parent])).to eq(section1)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'sets the first and last child items for parent item' do
|
86
|
+
expect(referenced_object(section1[:First])).to eq(page1)
|
87
|
+
expect(referenced_object(section1[:Last])).to eq(page2)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#increase_count' do
|
92
|
+
it 'adds the count of all descendant items' do
|
93
|
+
expect(outline_root[:Count]).to eq(3)
|
94
|
+
expect(section1[:Count].abs).to eq(2)
|
95
|
+
expect(page1[:Count]).to eq(0)
|
96
|
+
expect(page2[:Count]).to eq(0)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'closed option' do
|
101
|
+
it "sets the item's integer count to negative" do
|
102
|
+
expect(section1[:Count]).to eq(-2)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe 'adding a custom destination' do
|
108
|
+
before do
|
109
|
+
pdf.start_new_page
|
110
|
+
pdf.text 'Page 3 with a destination'
|
111
|
+
pdf.add_dest('customdest', pdf.dest_xyz(200, 200))
|
112
|
+
destination = pdf.dest_xyz(200, 200)
|
113
|
+
pdf.outline.update do
|
114
|
+
page destination: destination, title: 'Custom Destination'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'creates an outline item' do
|
119
|
+
expect(custom_dest).to_not be_nil
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'references the custom destination' do
|
123
|
+
expect(referenced_object(custom_dest[:Dest].first))
|
124
|
+
.to eq(referenced_object(pages.last))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'addding a section later with outline#section' do
|
129
|
+
before do
|
130
|
+
pdf.start_new_page
|
131
|
+
pdf.text 'Page 3. An added section '
|
132
|
+
pdf.outline.update do
|
133
|
+
section 'Added Section', destination: 3 do
|
134
|
+
page destination: 3, title: 'Page 3'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'adds new outline items to document' do
|
140
|
+
expect(section2).to_not be_nil
|
141
|
+
expect(page3).to_not be_nil
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'resets the last items for root outline dictionary' do
|
145
|
+
expect(referenced_object(outline_root[:First])).to eq(section1)
|
146
|
+
expect(referenced_object(outline_root[:Last])).to eq(section2)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'resets the next relation for the previous last top level item' do
|
150
|
+
expect(referenced_object(section1[:Next])).to eq(section2)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'sets the previous relation of the addded to section' do
|
154
|
+
expect(referenced_object(section2[:Prev])).to eq(section1)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'increases the count of root outline dictionary' do
|
158
|
+
expect(outline_root[:Count]).to eq(5)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#outline.add_subsection_to' do
|
163
|
+
context 'when positioned last' do
|
164
|
+
before do
|
165
|
+
pdf.start_new_page
|
166
|
+
pdf.text 'Page 3. An added subsection '
|
167
|
+
pdf.outline.update do
|
168
|
+
add_subsection_to 'Chapter 1' do
|
169
|
+
section 'Added SubSection', destination: 3 do
|
170
|
+
page destination: 3, title: 'Added Page 3'
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'adds new outline items to document' do
|
177
|
+
expect(subsection).to_not be_nil
|
178
|
+
expect(added_page3).to_not be_nil
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'resets the last item for parent item dictionary' do
|
182
|
+
expect(referenced_object(section1[:First])).to eq(page1)
|
183
|
+
expect(referenced_object(section1[:Last])).to eq(subsection)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "sets the prev relation for the new subsection to its parent's old "\
|
187
|
+
'last item' do
|
188
|
+
expect(referenced_object(subsection[:Prev])).to eq(page2)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "the subsection should become the next relation for its parent's old "\
|
192
|
+
'last item' do
|
193
|
+
expect(referenced_object(page2[:Next])).to eq(subsection)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'sets the first relation for the new subsection' do
|
197
|
+
expect(referenced_object(subsection[:First])).to eq(added_page3)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'sets the correct last relation of the added to section' do
|
201
|
+
expect(referenced_object(subsection[:Last])).to eq(added_page3)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'increases the count of root outline dictionary' do
|
205
|
+
expect(outline_root[:Count]).to eq(5)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'when positioned first' do
|
210
|
+
before do
|
211
|
+
pdf.start_new_page
|
212
|
+
pdf.text 'Page 3. An added subsection '
|
213
|
+
pdf.outline.update do
|
214
|
+
add_subsection_to 'Chapter 1', :first do
|
215
|
+
section 'Added SubSection', destination: 3 do
|
216
|
+
page destination: 3, title: 'Added Page 3'
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'adds new outline items to document' do
|
223
|
+
expect(subsection).to_not be_nil
|
224
|
+
expect(added_page3).to_not be_nil
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'resets the first item for parent item dictionary' do
|
228
|
+
expect(referenced_object(section1[:First])).to eq(subsection)
|
229
|
+
expect(referenced_object(section1[:Last])).to eq(page2)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "sets the next relation for the new subsection to its parent's old "\
|
233
|
+
'first item' do
|
234
|
+
expect(referenced_object(subsection[:Next])).to eq(page1)
|
235
|
+
end
|
236
|
+
|
237
|
+
it "the subsection should become the prev relation for its parent's old "\
|
238
|
+
'first item' do
|
239
|
+
expect(referenced_object(page1[:Prev])).to eq(subsection)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'sets the first relation for the new subsection' do
|
243
|
+
expect(referenced_object(subsection[:First])).to eq(added_page3)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'sets the correct last relation of the added to section' do
|
247
|
+
expect(referenced_object(subsection[:Last])).to eq(added_page3)
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'increases the count of root outline dictionary' do
|
251
|
+
expect(outline_root[:Count]).to eq(5)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'requires an existing title' do
|
256
|
+
expect do
|
257
|
+
pdf.go_to_page 1
|
258
|
+
pdf.start_new_page
|
259
|
+
pdf.text 'Inserted Page'
|
260
|
+
pdf.outline.update do
|
261
|
+
add_subsection_to 'Wrong page' do
|
262
|
+
page page_number, title: 'Inserted Page'
|
263
|
+
end
|
264
|
+
end
|
265
|
+
render_and_find_objects
|
266
|
+
end.to raise_error(Prawn::Errors::UnknownOutlineTitle)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
describe '#outline.insert_section_after' do
|
271
|
+
describe 'inserting in the middle of another section' do
|
272
|
+
before do
|
273
|
+
pdf.go_to_page 1
|
274
|
+
pdf.start_new_page
|
275
|
+
pdf.text 'Inserted Page'
|
276
|
+
pdf.outline.update do
|
277
|
+
insert_section_after 'Page 1' do
|
278
|
+
page destination: page_number, title: 'Inserted Page'
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'inserts new outline items to document' do
|
284
|
+
expect(inserted_page).to_not be_nil
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'adjusts the count of all ancestors' do
|
288
|
+
expect(outline_root[:Count]).to eq(4)
|
289
|
+
expect(section1[:Count].abs).to eq(3)
|
290
|
+
end
|
291
|
+
|
292
|
+
describe '#adjust_relations' do
|
293
|
+
it 'resets the sibling relations of adjoining items to inserted item' do
|
294
|
+
expect(referenced_object(page1[:Next])).to eq(inserted_page)
|
295
|
+
expect(referenced_object(page2[:Prev])).to eq(inserted_page)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'sets the sibling relation of added item to adjoining items' do
|
299
|
+
expect(referenced_object(inserted_page[:Next])).to eq(page2)
|
300
|
+
expect(referenced_object(inserted_page[:Prev])).to eq(page1)
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'does not affect the first and last relations of parent item' do
|
304
|
+
expect(referenced_object(section1[:First])).to eq(page1)
|
305
|
+
expect(referenced_object(section1[:Last])).to eq(page2)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'when adding another section afterwards' do
|
310
|
+
it 'has reset the root position so that a new section is added at '\
|
311
|
+
'the end of root sections' do
|
312
|
+
pdf.start_new_page
|
313
|
+
pdf.text 'Another Inserted Page'
|
314
|
+
pdf.outline.update do
|
315
|
+
section 'Added Section' do
|
316
|
+
page destination: page_number, title: 'Inserted Page'
|
317
|
+
end
|
318
|
+
end
|
319
|
+
expect(referenced_object(outline_root[:Last])).to eq(section2)
|
320
|
+
expect(referenced_object(section1[:Next])).to eq(section2)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe 'inserting at the end of another section' do
|
326
|
+
before do
|
327
|
+
pdf.go_to_page 2
|
328
|
+
pdf.start_new_page
|
329
|
+
pdf.text 'Inserted Page'
|
330
|
+
pdf.outline.update do
|
331
|
+
insert_section_after 'Page 2' do
|
332
|
+
page destination: page_number, title: 'Inserted Page'
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe '#adjust_relations' do
|
338
|
+
it 'resets the sibling relations of adjoining item to inserted item' do
|
339
|
+
expect(referenced_object(page2[:Next])).to eq(inserted_page)
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'sets the sibling relation of added item to adjoining items' do
|
343
|
+
expect(referenced_object(inserted_page[:Next])).to be_nil
|
344
|
+
expect(referenced_object(inserted_page[:Prev])).to eq(page2)
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'adjusts the last relation of parent item' do
|
348
|
+
expect(referenced_object(section1[:Last])).to eq(inserted_page)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'requires an existing title' do
|
354
|
+
expect do
|
355
|
+
pdf.go_to_page 1
|
356
|
+
pdf.start_new_page
|
357
|
+
pdf.text 'Inserted Page'
|
358
|
+
pdf.outline.update do
|
359
|
+
insert_section_after 'Wrong page' do
|
360
|
+
page destination: page_number, title: 'Inserted Page'
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end.to raise_error(Prawn::Errors::UnknownOutlineTitle)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
describe '#page' do
|
368
|
+
it 'requires a title option to be set' do
|
369
|
+
expect do
|
370
|
+
Prawn::Document.new do
|
371
|
+
text 'Page 1. This is the first Chapter. '
|
372
|
+
outline.define do
|
373
|
+
page destination: 1, title: nil
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end.to raise_error(Prawn::Errors::RequiredOption)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
describe 'foreign character encoding' do
|
381
|
+
let(:hash) do
|
382
|
+
pdf = Prawn::Document.new do
|
383
|
+
outline.define do
|
384
|
+
section 'La pomme croquée', destination: 1, closed: true
|
385
|
+
end
|
386
|
+
end
|
387
|
+
PDF::Reader::ObjectHash.new(StringIO.new(pdf.render, 'r+'))
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'handles other encodings for the title' do
|
391
|
+
object = find_by_title('La pomme croquée')
|
392
|
+
expect(object).to_not be_nil
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# Outline titles are stored as UTF-16. This method accepts a UTF-8 outline
|
397
|
+
# title and returns the PDF Object that contains an outline with that name
|
398
|
+
def find_by_title(title)
|
399
|
+
hash.values.find do |obj|
|
400
|
+
next unless obj.is_a?(Hash) && obj[:Title]
|
401
|
+
|
402
|
+
title_codepoints = obj[:Title].unpack('n*')
|
403
|
+
title_codepoints.shift
|
404
|
+
utf8_title = title_codepoints.pack('U*')
|
405
|
+
utf8_title == title ? obj : nil
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def referenced_object(reference)
|
410
|
+
hash[reference]
|
411
|
+
end
|
412
|
+
end
|