ruby-prawn-2.5.0.orig/ 0000775 0000000 0000000 00000000000 14571572164 013277 5 ustar root root ruby-prawn-2.5.0.orig/spec/ 0000775 0000000 0000000 00000000000 14571572164 014231 5 ustar root root ruby-prawn-2.5.0.orig/spec/spec_helper.rb 0000664 0000000 0000000 00000002144 14571572164 017050 0 ustar root root # frozen_string_literal: true
puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}"
if ENV['COVERAGE']
require 'simplecov'
SimpleCov.start do
add_filter '/spec/'
end
end
require_relative '../lib/prawn'
Prawn.debug = true
Prawn::Fonts::AFM.hide_m17n_warning = true
require 'rspec'
require 'pdf/reader'
require 'pdf/inspector'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/extensions/ and its subdirectories.
Dir[File.join(__dir__, 'extensions', '**', '*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
config.include(EncodingHelpers)
end
# Create a document.
def create_pdf(klass = Prawn::Document, &block)
klass.new(margin: 0, &block)
end
RSpec::Matchers.define(:have_parseable_xobjects) do
match do |actual|
expect { PDF::Inspector::XObject.analyze(actual.render) }.to_not raise_error
true
end
failure_message do |actual|
"expected that #{actual}'s XObjects could be successfully parsed"
end
end
# Make some methods public to assist in testing
# module Prawn
# module Graphics
# public :map_to_absolute
# end
# end
ruby-prawn-2.5.0.orig/spec/prawn_manual_spec.rb 0000664 0000000 0000000 00000002170 14571572164 020254 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'digest/sha2'
MANUAL_HASH =
case RUBY_ENGINE
when 'ruby'
'cc870b1f374b1da862c7bd08fa9f23c2815b4a12616b734e8dc30a8226ed307cb11503ec4621bb935bec3bd94837a32c1b186f493a0e3c2137cb8e1122518ba0'
when 'jruby'
'3583b193ec8698ba752916b8a4c37cd9d2d90f1bf7284922759c3e10b41e36e146149f12e2f96ae7716915089ff6f82945895a5de7d1f536f749404d1c1b1627'
end
RSpec.describe Prawn do
describe 'manual' do
# JRuby's zlib is a bit quirky. It sometimes produces different output to
# libzlib (used by MRI). It's still a proper deflate stream and can be
# decompressed just fine but for whatever reason compressin produses
# different output.
#
# See: https://github.com/jruby/jruby/issues/4244
it 'contains no unexpected changes' do
ENV['CI'] ||= 'true'
manual_path = File.expand_path('../manual/manual.rb', __dir__)
manual = eval(File.read(manual_path), TOPLEVEL_BINDING, manual_path) # rubocop: disable Security/Eval
s = manual.generate
hash = Digest::SHA512.hexdigest(s)
expect(hash).to eq MANUAL_HASH
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/ 0000775 0000000 0000000 00000000000 14571572164 015360 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/view_spec.rb 0000664 0000000 0000000 00000003460 14571572164 017674 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::View do
let(:view_object) { Object.new.tap { |o| o.extend(described_class) } }
it 'provides a Prawn::Document object by default' do
expect(view_object.document).to be_a(Prawn::Document)
end
it 'delegates unhandled methods to object returned by document method' do
doc = instance_double(Prawn::Document)
allow(view_object).to receive(:document).and_return(doc)
allow(doc).to receive(:fill_gradient)
block = proc {}
view_object.fill_gradient([1, 2], [3, 4], 'ff0000', [0, 0, 0, 1], apply_margin_options: true, &block)
expect(doc).to have_received(:fill_gradient)
.with([1, 2], [3, 4], 'ff0000', [0, 0, 0, 1], apply_margin_options: true, &block)
end
it 'allows a block-like DSL via the update method' do
doc = instance_double(Prawn::Document)
allow(view_object).to receive(:document).and_return(doc)
allow(doc).to receive(:font)
allow(doc).to receive(:cap_style)
view_object.update do
font
cap_style
end
expect(doc).to have_received(:font)
expect(doc).to have_received(:cap_style)
end
it 'aliases save_as() to document.render_file()' do
doc = instance_double(Prawn::Document)
allow(doc).to receive(:render_file)
allow(view_object).to receive(:document).and_return(doc)
view_object.save_as('foo.pdf')
expect(doc).to have_received(:render_file)
end
describe '#respond_to?', issue: 1064 do
subject { view_object.respond_to?(method) }
context 'when called with an existing method from Prawn::Document' do
let(:method) { :text }
it { is_expected.to be_truthy }
end
context 'when called with a non-existing method' do
let(:method) { :non_existing_method }
it { is_expected.to be_falsey }
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/transformation_stack_spec.rb 0000664 0000000 0000000 00000003452 14571572164 023156 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::TransformationStack do
let(:pdf) do
create_pdf do |document|
document.add_to_transformation_stack(2, 0, 0, 2, 100, 100)
end
end
let(:stack) { pdf.instance_variable_get(:@transformation_stack) }
describe '#add_to_transformation_stack' do
it 'creates and adds to the stack' do
pdf.add_to_transformation_stack(1, 0, 0, 1, 20, 20)
expect(stack).to eq [[[2, 0, 0, 2, 100, 100], [1, 0, 0, 1, 20, 20]]]
end
it 'adds to the last stack' do
pdf.save_transformation_stack
pdf.add_to_transformation_stack(1, 0, 0, 1, 20, 20)
expect(stack).to eq [
[[2, 0, 0, 2, 100, 100]],
[[2, 0, 0, 2, 100, 100], [1, 0, 0, 1, 20, 20]],
]
end
end
describe '#save_transformation_stack' do
it 'clones the last stack' do
pdf.save_transformation_stack
expect(stack.length).to eq 2
expect(stack.first).to eq stack.last
expect(stack.first).to_not be stack.last
end
end
describe '#restore_transformation_stack' do
it 'pops off the last stack' do
pdf.save_transformation_stack
pdf.add_to_transformation_stack(1, 0, 0, 1, 20, 20)
pdf.restore_transformation_stack
expect(stack).to eq [[[2, 0, 0, 2, 100, 100]]]
end
end
describe 'current_transformation_matrix_with_translation' do
before do
pdf.add_to_transformation_stack(1, 0, 0, 1, 20, 20)
end
it 'calculates the last transformation' do
expect(pdf.current_transformation_matrix_with_translation)
.to eq [2, 0, 0, 2, 140, 140]
end
it 'adds the supplied x and y coordinates to the transformation stack' do
expect(pdf.current_transformation_matrix_with_translation(15, 15))
.to eq [2, 0, 0, 2, 170, 170]
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text_with_inline_formatting_spec.rb 0000664 0000000 0000000 00000002157 14571572164 024533 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text do
let(:pdf) { create_pdf }
describe '#formatted_text' do
it 'draws text' do
string = 'hello world'
format_array = [{ text: string }]
pdf.formatted_text(format_array)
# grab the text from the rendered PDF and ensure it matches
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq(string)
end
end
describe '#text with inline styling' do
it "automatically moves to a new page if the tallest fragment on the next line won't fit in the available space" do
pdf.move_cursor_to(pdf.font.height)
formatted = "this contains sized text"
pdf.text(formatted, inline_format: true)
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
end
it 'embeds links as literal strings' do
pdf.text(
"wiki",
inline_format: true,
)
expect(pdf.render).to match(%r{/URI\s+\(http://wiki\.github\.com})
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text_spec.rb 0000664 0000000 0000000 00000052704 14571572164 017713 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text do
describe 'NBSP' do
it 'is defined' do
expect(Prawn::Text::NBSP).to eq("\u00a0")
end
end
describe '#height_of' do
let(:pdf) { create_pdf }
it 'returns the height that would be required to print a particular string of text' do
original_y = pdf.y
pdf.text('Foo')
new_y = pdf.y
expect(pdf.height_of('Foo')).to be_within(0.0001).of(original_y - new_y)
end
it 'omits the gap below the last descender if :final_gap => false is given' do
original_y = pdf.y
pdf.text('Foo', final_gap: false)
new_y = pdf.y
expect(pdf.height_of('Foo', final_gap: false))
.to be_within(0.0001).of(original_y - new_y)
end
it 'raise_errors CannotFit if a too-small width is given' do
expect {
pdf.height_of('text', width: 1)
}.to raise_error(Prawn::Errors::CannotFit)
end
it 'raises NotImplementedError if :indent_paragraphs option is provided' do
expect {
pdf.height_of(
'hai',
width: 300,
indent_paragraphs: 60,
)
}.to raise_error(NotImplementedError)
end
it 'does not raise Prawn::Errors::UnknownOption if :final_gap option is provided' do
expect {
pdf.height_of('hai', width: 300, final_gap: true)
}.to_not raise_error
end
end
describe '#text' do
let(:pdf) { create_pdf }
it 'does not fail when @output is nil when Prawn::Text::Formatted::LineWrap#finalize_line is called' do
# need a document with margins for these particulars to produce the
# condition that was throwing the error
pdf = Prawn::Document.new
expect { pdf.text('transparency ' * 150, size: 18) }
.to_not raise_error
end
it 'allows drawing empty strings to the page' do
pdf.text(' ')
text = PDF::Inspector::Text.analyze(pdf.render)
# If anything is rendered to the page, it should be whitespace.
expect(text.strings).to all(match(/\A\s*\z/))
end
it 'ignores call when string is nil' do
expect(pdf.text(nil)).to be false
end
it 'correctlies render empty paragraphs' do
pdf.text("text\n\ntext")
text = PDF::Inspector::Text.analyze(pdf.render)
expect(pdf.page_count).to eq(1)
expect(text.strings.reject(&:empty?)).to eq(%w[text text])
end
it 'correctlies render empty paragraphs with :indent_paragraphs' do
pdf.text("text\n\ntext", indent_paragraphs: 5)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(pdf.page_count).to eq(1)
expect(text.strings.reject(&:empty?)).to eq(%w[text text])
end
it 'correctly renders strings ending with empty paragraphs and :inline_format and :indent_paragraphs' do
pdf.text("text\n\n", inline_format: true, indent_paragraphs: 5)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(pdf.page_count).to eq(1)
expect(text.strings).to eq(['text'])
end
it 'defaults to use kerning information' do
pdf.text('hello world')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.kerned[0]).to be true
end
it 'is able to disable kerning with an option' do
pdf.text('hello world', kerning: false)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.kerned[0]).to be false
end
it 'is able to disable kerning document-wide' do
pdf.default_kerning(false)
pdf.default_kerning = false
pdf.text('hello world')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.kerned[0]).to be false
end
it 'option should be able to override document-wide kerning disabling' do
pdf.default_kerning = false
pdf.text('hello world', kerning: true)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.kerned[0]).to be true
end
it 'raise_errors ArgumentError if :at option included' do
expect { pdf.text('hai', at: [0, 0]) }.to raise_error(ArgumentError)
end
it 'advances down the document based on font_height' do
position = pdf.y
pdf.text('Foo')
expect(pdf.y).to be_within(0.0001).of(position - pdf.font.height)
position = pdf.y
pdf.text("Foo\nBar\nBaz")
expect(pdf.y).to be_within(0.0001).of(position - (3 * pdf.font.height))
end
it 'advances down the document based on font_height with size option' do
position = pdf.y
pdf.text('Foo', size: 15)
pdf.font_size = 15
expect(pdf.y).to be_within(0.0001).of(position - pdf.font.height)
position = pdf.y
pdf.text("Foo\nBar\nBaz")
expect(pdf.y).to be_within(0.0001).of(position - (3 * pdf.font.height))
end
it 'advances down the document based on font_height with leading option' do
position = pdf.y
leading = 2
pdf.text('Foo', leading: leading)
expect(pdf.y).to be_within(0.0001)
.of(position - pdf.font.height - leading)
position = pdf.y
pdf.text("Foo\nBar\nBaz")
expect(pdf.y).to be_within(0.0001).of(position - (3 * pdf.font.height))
end
it 'advances only to the bottom of the final descender if final_gap is false' do
position = pdf.y
pdf.text('Foo', final_gap: false)
expect(pdf.y).to be_within(0.0001).of(position - pdf.font.ascender - pdf.font.descender)
position = pdf.y
pdf.text("Foo\nBar\nBaz", final_gap: false)
expect(pdf.y).to be_within(0.0001)
.of(position - (2 * pdf.font.height) - pdf.font.ascender - pdf.font.descender)
end
it 'is able to print text starting at the last line of a page' do
pdf.move_cursor_to(pdf.font.height)
pdf.text('hello world')
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(1)
end
it 'defaults to 12 point helvetica' do
pdf.text('Blah')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:name]).to eq(:Helvetica)
expect(text.font_settings[0][:size]).to eq(12)
expect(text.strings.first).to eq('Blah')
end
it 'allows setting font size' do
pdf.text('Blah', size: 16)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
end
it 'allows setting a default font size' do
pdf.font_size = 16
pdf.text('Blah')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
end
it 'allows overriding default font for a single instance' do
pdf.font_size = 16
pdf.text('Blah', size: 11)
pdf.text('Blaz')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(11)
expect(text.font_settings[1][:size]).to eq(16)
end
it 'allows setting a font size transaction with a block' do
pdf.font_size(16) do
pdf.text('Blah')
end
pdf.text('blah')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
expect(text.font_settings[1][:size]).to eq(12)
end
it 'allows manual setting the font size when in a font size block' do
pdf.font_size(16) do
pdf.text('Foo')
pdf.text('Blah', size: 11)
pdf.text('Blaz')
end
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
expect(text.font_settings[1][:size]).to eq(11)
expect(text.font_settings[2][:size]).to eq(16)
end
it 'allows registering of built-in font_settings on the fly' do
pdf.font('Times-Roman')
pdf.text('Blah')
pdf.font('Courier')
pdf.text('Blaz')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:name]).to eq(:'Times-Roman')
expect(text.font_settings[1][:name]).to eq(:Courier)
end
it 'utilises the same default font across multiple pages' do
pdf.text('Blah')
pdf.start_new_page
pdf.text('Blaz')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings.size).to eq(2)
expect(text.font_settings[0][:name]).to eq(:Helvetica)
expect(text.font_settings[1][:name]).to eq(:Helvetica)
end
it 'raise_errors an exception when an unknown font is used' do
expect { pdf.font('Pao bu') }.to raise_error(Prawn::Errors::UnknownFont)
end
it 'does not raise an exception when providing Pathname instance as font' do
expect { pdf.font(Pathname.new("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")) }
.to_not raise_error
end
it 'correctlies render a utf-8 string when using a built-in font' do
str = '©' # copyright symbol
pdf.text(str)
# grab the text from the rendered PDF and ensure it matches
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq(str)
end
it 'correctlies render a utf-8 string when using a TTF font' do
str = '©' # copyright symbol
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
pdf.text(str)
# grab the text from the rendered PDF and ensure it matches
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq(str)
end
it 'subsets mixed low-ASCII and non-ASCII characters when they can be subsetted together' do
str = 'It’s super effective!'
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
pdf.text(str)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq(str)
end
it 'correctly renders a string with higher bit characters across a page break when using a built-in font' do
str = '©'
pdf.move_cursor_to(pdf.font.height)
pdf.text("#{str}\n#{str}")
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
expect(pages[0][:strings]).to eq([str])
expect(pages[1][:strings]).to eq([str])
end
it 'correctly renders a string with higher bit characters across a page break when using a built-in font and ' \
':indent_paragraphs option' do
str = '©'
pdf.move_cursor_to(pdf.font.height)
pdf.text("#{str}\n#{str}", indent_paragraphs: 20)
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
expect(pages[0][:strings]).to eq([str])
expect(pages[1][:strings]).to eq([str])
end
it 'raises an exception when a utf-8 incompatible string is rendered' do
str = "Blah \xDD"
expect { pdf.text(str) }.to raise_error(Prawn::Errors::IncompatibleStringEncoding)
end
it 'does not raise an exception when a shift-jis string is rendered' do
datafile = "#{Prawn::DATADIR}/shift_jis_text.txt"
sjis_str = File.open(datafile, 'r:shift_jis', &:gets)
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
# Expect that the call to text will not raise an encoding error
expect { pdf.text(sjis_str) }.to_not raise_error
end
it 'calls move_past_bottom when printing more text than can fit between the current document.y and bounds.bottom' do
pdf.y = pdf.font.height
pdf.text('Hello')
pdf.text('World')
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
expect(pages[0][:strings]).to eq(['Hello'])
expect(pages[1][:strings]).to eq(['World'])
end
describe 'with :indent_paragraphs option' do
it 'indents the paragraphs' do
hello = 'hello ' * 50
hello2 = 'hello ' * 50
pdf.text("#{hello}\n#{hello2}", indent_paragraphs: 60)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq(('hello ' * 19).strip)
expect(text.strings[1]).to eq(('hello ' * 21).strip)
expect(text.strings[3]).to eq(('hello ' * 19).strip)
expect(text.strings[4]).to eq(('hello ' * 21).strip)
end
it 'indents from right side when using :rtl direction' do
para1 = 'The rain in spain falls mainly on the plains ' * 3
para2 = 'The rain in spain falls mainly on the plains ' * 3
pdf.text("#{para1}\n#{para2}", indent_paragraphs: 60, direction: :rtl)
text = PDF::Inspector::Text.analyze(pdf.render)
lines = text.strings
x_positions = text.positions.map { |e| e[0] }
# NOTE: The code below reflects Prawn's current kerning behavior for RTL
# text, which isn't necessarily correct. If we change that behavior,
# this test will need to be updated.
expect(x_positions[0]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - 60 - pdf.width_of(lines[0].reverse, kerning: true),
),
)
expect(x_positions[1]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - pdf.width_of(lines[1].reverse, kerning: true),
),
)
expect(x_positions[2]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - 60 - pdf.width_of(lines[2].reverse, kerning: true),
),
)
expect(x_positions[3]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - pdf.width_of(lines[3].reverse, kerning: true),
),
)
end
it 'indents from right side when document has :rtl direction' do
para1 = 'The rain in spain falls mainly on the plains ' * 3
para2 = 'The rain in spain falls mainly on the plains ' * 3
pdf.text_direction = :rtl
pdf.text("#{para1}\n#{para2}", indent_paragraphs: 60)
text = PDF::Inspector::Text.analyze(pdf.render)
lines = text.strings
x_positions = text.positions.map { |e| e[0] }
# NOTE: The code below reflects Prawn's current kerning behavior for RTL
# text, which isn't necessarily correct. If we change that behavior,
# this test will need to be updated.
expect(x_positions[0]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - 60 - pdf.width_of(lines[0].reverse, kerning: true),
),
)
expect(x_positions[1]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - pdf.width_of(lines[1].reverse, kerning: true),
),
)
expect(x_positions[2]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - 60 - pdf.width_of(lines[2].reverse, kerning: true),
),
)
expect(x_positions[3]).to(
be_within(0.001).of(
pdf.bounds.absolute_right - pdf.width_of(lines[3].reverse, kerning: true),
),
)
end
it 'indents from left side when using :ltr direction' do
para1 = 'The rain in spain falls mainly on the plains ' * 3
para2 = 'The rain in spain falls mainly on the plains ' * 3
pdf.text("#{para1}\n#{para2}", indent_paragraphs: 60, direction: :ltr)
text = PDF::Inspector::Text.analyze(pdf.render)
x_positions = text.positions.map { |e| e[0] }
expect(x_positions[0]).to eq(60)
expect(x_positions[1]).to eq(0)
expect(x_positions[2]).to eq(60)
expect(x_positions[3]).to eq(0)
end
it 'indents from left side when document has :ltr direction' do
para1 = 'The rain in spain falls mainly on the plains ' * 3
para2 = 'The rain in spain falls mainly on the plains ' * 3
pdf.text_direction = :ltr
pdf.text("#{para1}\n#{para2}", indent_paragraphs: 60)
text = PDF::Inspector::Text.analyze(pdf.render)
x_positions = text.positions.map { |e| e[0] }
expect(x_positions[0]).to eq(60)
expect(x_positions[1]).to eq(0)
expect(x_positions[2]).to eq(60)
expect(x_positions[3]).to eq(0)
end
describe 'when paragraph has only one line, it should not add additional leading' do
let(:leading) { 100 }
it 'adds leading only once' do
original_y = pdf.y
pdf.text('hello', indent_paragraphs: 10, leading: leading)
expect(original_y - pdf.y).to be < leading * 2
end
end
describe 'when :final_gap is set to false, there should still be a gap between the first line and the rest' do
it 'spaces the first and the second line correctly' do
text = 'hello ' * 30
original_y = pdf.y
# First rendering with no indentation so we know what the correct
# render height should be
pdf.text(text, final_gap: false)
height_with_no_indentation = original_y - pdf.y
y_between_paragraphs = pdf.y
# The indentation size doesn't matter, it's about triggering
# a different path in the code
pdf.text(text, indent_paragraphs: 1, final_gap: false)
height_with_indentation = y_between_paragraphs - pdf.y
expect(height_with_indentation).to be_within(0.0001)
.of(height_with_no_indentation)
end
end
describe 'single line height with no final_gap should not depend on indentation' do
it 'does not affect the height of a single line' do
text = 'hello'
original_y = pdf.y
# First rendering with no indentation so we know what the correct
# render height should be
pdf.text(text, final_gap: false)
height_with_no_indentation = original_y - pdf.y
y_between_paragraphs = pdf.y
# The indentation size doesn't matter, it's about triggering
# a different path in the code
pdf.text(text, indent_paragraphs: 1, final_gap: false)
height_with_indentation = y_between_paragraphs - pdf.y
expect(height_with_indentation).to be_within(0.0001)
.of(height_with_no_indentation)
end
end
describe 'when wrap to new page, and first line of new page is not the start of a new paragraph, that line ' \
'should not be indented' do
it 'indents the paragraphs' do
hello = 'hello ' * 50
hello2 = 'hello ' * 50
pdf.move_cursor_to(pdf.font.height)
pdf.text("#{hello}\n#{hello2}", indent_paragraphs: 60)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq(('hello ' * 19).strip)
expect(text.strings[1]).to eq(('hello ' * 21).strip)
expect(text.strings[3]).to eq(('hello ' * 19).strip)
expect(text.strings[4]).to eq(('hello ' * 21).strip)
end
end
describe 'when wrap to new page, and first line of new page is the start of a new paragraph, that line should ' \
'be indented' do
it 'indents the paragraphs' do
hello = 'hello ' * 50
hello2 = 'hello ' * 50
pdf.move_cursor_to(pdf.font.height * 3)
pdf.text("#{hello}\n#{hello2}", indent_paragraphs: 60)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq(('hello ' * 19).strip)
expect(text.strings[1]).to eq(('hello ' * 21).strip)
expect(text.strings[3]).to eq(('hello ' * 19).strip)
expect(text.strings[4]).to eq(('hello ' * 21).strip)
end
end
end
describe 'kerning' do
it 'respects text kerning setting (document default)' do
allow(pdf.font).to receive(:compute_width_of)
.with('VAT', hash_including(kerning: true))
.and_return(10)
pdf.text('VAT')
expect(pdf.font).to have_received(:compute_width_of)
.with('VAT', hash_including(kerning: true))
end
it 'respects text kerning setting (kerning=true)' do
allow(pdf.font).to receive(:compute_width_of)
.with('VAT', hash_including(kerning: true))
.at_least(:once)
.and_return(10)
pdf.text('VAT', kerning: true)
expect(pdf.font).to have_received(:compute_width_of)
.with('VAT', hash_including(kerning: true))
.at_least(:once)
end
it 'respects text kerning setting (kerning=false)' do
allow(pdf.font).to receive(:compute_width_of)
.with('VAT', hash_including(kerning: false))
.at_least(:once)
.and_return(10)
pdf.text('VAT', kerning: false)
expect(pdf.font).to have_received(:compute_width_of)
.with('VAT', hash_including(kerning: false))
.at_least(:once)
end
end
describe '#shrink_to_fit with special utf-8 text' do
it 'does not throw an exception', :unresolved, issue: 603 do
expect {
Prawn::Document.new(page_size: 'A4', margin: [2, 2, 2, 2]) do |pdf|
add_unicode_fonts(pdf)
pdf.bounding_box([1, 1], width: 90, height: 50) do
pdf.text(
"Sample Text\nSAMPLE SAMPLE SAMPLEoddělení ZMĚN\nSAMPLE",
overflow: :shrink_to_fit,
)
end
end
}.to_not raise_error
end
end
def add_unicode_fonts(pdf)
dejavu = "#{::Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf"
pdf.font_families.update(
'dejavu' => {
normal: dejavu,
italic: dejavu,
bold: dejavu,
bold_italic: dejavu,
},
)
pdf.fallback_fonts = ['dejavu']
end
describe 'fallback_fonts' do
it 'preserves font style' do
create_pdf
pdf.fallback_fonts(['Helvetica'])
pdf.font('Times-Roman', style: :italic) do
pdf.text('hello')
end
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(1)
expect(fonts_used[0]).to eq(:'Times-Italic')
expect(text.strings[0]).to eq('hello')
end
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text_spacing_spec.rb 0000664 0000000 0000000 00000005527 14571572164 021420 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text do
let(:pdf) { create_pdf }
describe '#character_spacing' do
it 'draws the character spacing to the document' do
pdf.character_spacing(10.555555) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.character_spacing.first).to eq(10.55556)
end
it 'does not draw the character spacing to the document when the new character spacing matches the old' do
pdf.character_spacing(0) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.character_spacing).to be_empty
end
it 'restores character spacing to 0' do
pdf.character_spacing(10.555555) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.character_spacing.last).to eq(0)
end
it 'functions as an accessor when no parameter given' do
pdf.character_spacing(10.555555) do
pdf.text('hello world')
expect(pdf.character_spacing).to eq(10.555555)
end
expect(pdf.character_spacing).to eq(0)
end
# ensure that we properly internationalize by using the number of characters
# in a string, not the number of bytes, to insert character spaces
#
it 'calculates character spacing widths by characters, not bytes' do
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
str = 'こんにちは世界'
raw_width = nil
pdf.character_spacing(0) do
raw_width = pdf.width_of(str)
end
pdf.character_spacing(10) do
# the new width should include six 10-pt character spaces.
expect(pdf.width_of(str)).to be_within(0.001).of(raw_width + (10 * 6))
end
end
end
describe '#word_spacing' do
it 'draws the word spacing to the document' do
pdf.word_spacing(10.555555) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.word_spacing.first).to eq(10.55556)
end
it 'draws the word spacing to the document when the new word spacing matches the old' do
pdf.word_spacing(0) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.word_spacing).to be_empty
end
it 'restores word spacing to 0' do
pdf.word_spacing(10.555555) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.word_spacing.last).to eq(0)
end
it 'functions as an accessor when no parameter given' do
pdf.word_spacing(10.555555) do
pdf.text('hello world')
expect(pdf.word_spacing).to eq(10.555555)
end
expect(pdf.word_spacing).to eq(0)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text_rendering_mode_spec.rb 0000664 0000000 0000000 00000003013 14571572164 022741 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text do
let(:pdf) { create_pdf }
describe '#text_rendering_mode' do
it 'draws the text rendering mode to the document' do
pdf.text_rendering_mode(:stroke) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.text_rendering_mode.first).to eq(1)
end
it 'does not draw the text rendering mode to the document when the new mode matches the old' do
pdf.text_rendering_mode(:fill) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.text_rendering_mode).to be_empty
end
it 'restores character spacing to 0' do
pdf.text_rendering_mode(:stroke) do
pdf.text('hello world')
end
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.text_rendering_mode).to eq([1, 0])
end
it 'functions as an accessor when no parameter given' do
pdf.text_rendering_mode(:fill_stroke) do
pdf.text('hello world')
expect(pdf.text_rendering_mode).to eq(:fill_stroke)
end
expect(pdf.text_rendering_mode).to eq(:fill)
end
it 'raise_errors an exception when passed an invalid mode' do
expect { pdf.text_rendering_mode(-1) }.to raise_error(ArgumentError)
expect { pdf.text_rendering_mode(8) }.to raise_error(ArgumentError)
expect { pdf.text_rendering_mode(:flil) }.to raise_error(ArgumentError)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text_draw_text_spec.rb 0000664 0000000 0000000 00000011171 14571572164 021765 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text do
describe '#draw_text' do
let(:pdf) { create_pdf }
it 'raise_errors ArgumentError if :at option omitted' do
expect { pdf.draw_text('hai', {}) }.to raise_error(ArgumentError)
end
it 'raise_errors ArgumentError if :align option included' do
expect {
pdf.draw_text('hai', at: [0, 0], align: :center)
}.to raise_error(ArgumentError)
end
it 'allows drawing empty strings to the page' do
pdf.draw_text(' ', at: [100, 100])
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq(' ')
end
it 'defaults to 12 point helvetica' do
pdf.draw_text('Blah', at: [100, 100])
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:name]).to eq(:Helvetica)
expect(text.font_settings[0][:size]).to eq(12)
expect(text.strings.first).to eq('Blah')
end
it 'allows setting font size' do
pdf.draw_text('Blah', at: [100, 100], size: 16)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
end
it 'allows setting a default font size' do
pdf.font_size = 16
pdf.draw_text('Blah', at: [0, 0])
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
end
rotated_text_inspector =
Class.new(PDF::Inspector) do
attr_reader :tm_operator_used
def initialize
super
@tm_operator_used = false
end
def set_text_matrix_and_text_line_matrix(*_arguments)
@tm_operator_used = true
end
end
it 'allows rotation' do
pdf.draw_text('Test', at: [100, 100], rotate: 90)
text = rotated_text_inspector.analyze(pdf.render)
expect(text.tm_operator_used).to be true
end
it 'does not use rotation matrix by default' do
pdf.draw_text('Test', at: [100, 100])
text = rotated_text_inspector.analyze(pdf.render)
expect(text.tm_operator_used).to be false
end
it 'allows overriding default font for a single instance' do
pdf.font_size = 16
pdf.draw_text('Blah', size: 11, at: [0, 0])
pdf.draw_text('Blaz', at: [0, 0])
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(11)
expect(text.font_settings[1][:size]).to eq(16)
end
it 'allows setting a font size transaction with a block' do
pdf.font_size(16) do
pdf.draw_text('Blah', at: [0, 0])
end
pdf.draw_text('blah', at: [0, 0])
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
expect(text.font_settings[1][:size]).to eq(12)
end
it 'allows manual setting the font size when in a font size block' do
pdf.font_size(16) do
pdf.draw_text('Foo', at: [0, 0])
pdf.draw_text('Blah', size: 11, at: [0, 0])
pdf.draw_text('Blaz', at: [0, 0])
end
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(16)
expect(text.font_settings[1][:size]).to eq(11)
expect(text.font_settings[2][:size]).to eq(16)
end
it 'allows registering of built-in font_settings on the fly' do
pdf.font('Times-Roman')
pdf.draw_text('Blah', at: [100, 100])
pdf.font('Courier')
pdf.draw_text('Blaz', at: [150, 150])
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:name]).to eq(:'Times-Roman')
expect(text.font_settings[1][:name]).to eq(:Courier)
end
it 'raise_errors an exception when an unknown font is used' do
expect { pdf.font('Pao bu') }.to raise_error(Prawn::Errors::UnknownFont)
end
it 'correctlies render a utf-8 string when using a built-in font' do
str = '©' # copyright symbol
pdf.draw_text(str, at: [0, 0])
# grab the text from the rendered PDF and ensure it matches
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq(str)
end
it 'raises an exception when a utf-8 incompatible string is rendered' do
str = "Blah \xDD"
expect { pdf.draw_text(str, at: [0, 0]) }.to raise_error(Prawn::Errors::IncompatibleStringEncoding)
end
it 'does not raise an exception when a shift-jis string is rendered' do
datafile = "#{Prawn::DATADIR}/shift_jis_text.txt"
sjis_str = File.open(datafile, 'r:shift_jis', &:gets)
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
expect { pdf.draw_text(sjis_str, at: [0, 0]) }.to_not raise_error
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text/ 0000775 0000000 0000000 00000000000 14571572164 016344 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/text/formatted/ 0000775 0000000 0000000 00000000000 14571572164 020331 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/text/formatted/parser_spec.rb 0000664 0000000 0000000 00000042065 14571572164 023173 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text::Formatted::Parser do
describe '#format' do
it 'handles sup' do
string = 'superscript'
array = described_class.format(string)
expect(array[0]).to eq(
text: 'superscript',
styles: [:superscript],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles sub' do
string = 'subscript'
array = described_class.format(string)
expect(array[0]).to eq(
text: 'subscript',
styles: [:subscript],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles rgb' do
string = "red text"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'red text',
styles: [],
color: 'ff0000',
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it '# should be optional in rgb' do
string = "red text"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'red text',
styles: [],
color: 'ff0000',
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles cmyk' do
string = "magenta text"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'magenta text',
styles: [],
color: [0, 100, 0, 0],
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles fonts' do
string = "Courier text"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'Courier text',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: 'Courier',
size: nil,
character_spacing: nil,
)
end
it 'handles size' do
string = "14 point text"
array = described_class.format(string)
expect(array[0]).to eq(
text: '14 point text',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: 14,
character_spacing: nil,
)
end
it 'handles character_spacing' do
string = "extra character spacing"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'extra character spacing',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: 2.5,
)
end
it 'handles links' do
string = "external link"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'external link',
styles: [],
color: nil,
link: 'http://example.com',
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles local links' do
string = "local link"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'local link',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: '/home/example/foo.bar',
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles anchors' do
string = "internal link"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'internal link',
styles: [],
color: nil,
link: nil,
anchor: 'ToC',
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles higher order characters properly' do
string = "©\n©"
array = described_class.format(string)
expect(array[0]).to eq(
text: '©',
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[1]).to eq(
text: "\n",
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[2]).to eq(
text: '©',
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'converts < >, and & to <, >, and &, respectively' do
string = 'hello <, >, and &'
array = described_class.format(string)
expect(array[1]).to eq(
text: '<, >, and &',
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'handles double qoutes around tag attributes' do
string = 'some sized text'
array = described_class.format(string)
expect(array[1]).to eq(
text: 'sized',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: 14,
character_spacing: nil,
)
end
it 'handles single qoutes around tag attributes' do
string = "some sized text"
array = described_class.format(string)
expect(array[1]).to eq(
text: 'sized',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: 14,
character_spacing: nil,
)
end
it 'constructs a formatted text array from a string' do
string = "hello world\nhow are you?"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'hello ',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[1]).to eq(
text: 'world',
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[2]).to eq(
text: "\n",
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[3]).to eq(
text: 'how ',
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[4]).to eq(
text: 'are',
styles: %i[bold italic],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[5]).to eq(
text: ' you?',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'accepts as an alternative to ' do
string = 'bold not bold'
array = described_class.format(string)
expect(array[0]).to eq(
text: 'bold',
styles: [:bold],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[1]).to eq(
text: ' not bold',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'accepts as an alternative to ' do
string = 'italic not italic'
array = described_class.format(string)
expect(array[0]).to eq(
text: 'italic',
styles: [:italic],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[1]).to eq(
text: ' not italic',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'accepts as an alternative to ' do
string = "link not a link"
array = described_class.format(string)
expect(array[0]).to eq(
text: 'link',
styles: [],
color: nil,
link: 'http://example.com',
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
expect(array[1]).to eq(
text: ' not a link',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
)
end
it 'turns
,
into newline' do
array = described_class.format('hello
big
world')
expect(array.map { |frag| frag[:text] }.join).to eq("hello\nbig\nworld")
end
end
describe '#to_string' do
it 'handles sup' do
string = 'superscript'
array = [
{
text: 'superscript',
styles: [:superscript],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles sub' do
string = 'subscript'
array = [
{
text: 'subscript',
styles: [:subscript],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles rgb' do
string = "red text"
array = [
{
text: 'red text',
styles: [],
color: 'ff0000',
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles cmyk' do
string = "magenta text"
array = [
{
text: 'magenta text',
styles: [],
color: [0, 100, 0, 0],
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles fonts' do
string = "Courier text"
array = [
{
text: 'Courier text',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: 'Courier',
size: nil,
character_spacing: nil,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles size' do
string = "14 point text"
array = [
{
text: '14 point text',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: 14,
character_spacing: nil,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles character spacing' do
string = "2.5 extra character spacing"
array = [
{
text: '2.5 extra character spacing',
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: 2.5,
},
]
expect(described_class.to_string(array)).to eq(string)
end
it 'handles links' do
array = [
{
text: 'external link',
styles: [],
color: nil,
link: 'http://example.com',
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
string = "external link"
expect(described_class.to_string(array)).to eq(string)
end
it 'handles anchors' do
array = [
{
text: 'internal link',
styles: [],
color: nil,
link: nil,
anchor: 'ToC',
font: nil,
size: nil,
character_spacing: nil,
},
]
string = "internal link"
expect(described_class.to_string(array)).to eq(string)
end
it 'converts <, >, and & to < >, and &, respectively' do
array = [
{
text: 'hello ',
styles: [],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
{
text: '<, >, and &',
styles: [:bold],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
string = 'hello <, >, and &'
expect(described_class.to_string(array)).to eq(string)
end
it 'constructs an HTML-esque string from a formatted text array' do
array = [
{
text: 'hello ',
styles: [],
color: nil,
link: nil,
font: nil,
size: 14,
character_spacing: nil,
},
{
text: 'world',
styles: [:bold],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
{
text: "\n",
styles: [:bold],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
{
text: 'how ',
styles: [:bold],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
{
text: 'are',
styles: %i[bold italic],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
{
text: ' you?',
styles: [],
color: nil,
link: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
string = "hello world\nhow are you?"
expect(described_class.to_string(array)).to eq(string)
end
end
describe '#array_paragraphs' do
it 'groups fragments separated by newlines' do
array = [
{ text: "\nhello\nworld" },
{ text: "\n\n" },
{ text: 'how' },
{ text: 'are' },
{ text: 'you' },
]
target = [
[{ text: "\n" }],
[{ text: 'hello' }],
[{ text: 'world' }],
[{ text: "\n" }],
[
{ text: 'how' },
{ text: 'are' },
{ text: 'you' },
],
]
expect(described_class.array_paragraphs(array)).to eq(target)
end
it 'works properly if ending in an empty paragraph' do
array = [{ text: "\nhello\nworld\n" }]
target = [
[{ text: "\n" }],
[{ text: 'hello' }],
[{ text: 'world' }],
]
expect(described_class.array_paragraphs(array)).to eq(target)
end
end
describe '#escape' do
it 'escapes < > & chars' do
string = %(> < gt; hello < > < world & & & " ' \n )
value = described_class.escape(string)
exp = %(> < gt; hello < > < world & & & " ' \n )
expect(value).to eq(exp)
end
end
describe '#unescape' do
it 'unescapes > < & chars' do
string = "> < gt; hello < > < world & & & \" ' \n "
value = described_class.unescape(string)
expect(value).to eq("> < gt; hello < > < world & & & \" ' \n ")
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text/formatted/line_wrap_spec.rb 0000664 0000000 0000000 00000033242 14571572164 023654 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text::Formatted::LineWrap do
let(:pdf) { create_pdf }
let(:arranger) do
Prawn::Text::Formatted::Arranger.new(pdf).tap do |a|
a.format_array = [
{ text: "hello\nworld\n\n\nhow are you?" },
{ text: "\n" },
{ text: "\n" },
{ text: '' },
{ text: 'fine, thanks. ' * 4 },
{ text: '' },
{ text: "\n" },
{ text: '' },
]
end
end
let(:line_wrap) { described_class.new }
it 'only returns an empty string if nothing fit or there was nothing to wrap' do
8.times do
line = line_wrap.wrap_line(
arranger: arranger,
width: 200,
document: pdf,
)
expect(line).to_not be_empty
end
line = line_wrap.wrap_line(
arranger: arranger,
width: 200,
document: pdf,
)
expect(line).to be_empty
end
it 'tokenizes a string using the scan_pattern' do
tokens = line_wrap.tokenize('one two three')
expect(tokens.length).to eq(5)
end
describe 'Core::Text::Formatted::LineWrap#wrap_line' do
let(:arranger) { Prawn::Text::Formatted::Arranger.new(pdf) }
let(:one_word_width) { 50 }
it 'strips leading and trailing spaces' do
array = [
{ text: ' hello world, ' },
{ text: 'goodbye ', style: [:bold] },
]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(string).to eq('hello world, goodbye')
end
it 'strips trailing spaces when a white-space-only fragment was ' \
'successfully pushed onto the end of a line but no other non-white ' \
'space fragment fits after it' do
array = [
{ text: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ' },
{ text: ' ', style: [:bold] },
{ text: ' bbbbbbbbbbbbbbbbbbbbbbbbbbbb' },
]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(string).to eq('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
end
it 'raise_errors CannotFit if a too-small width is given' do
array = [
{ text: ' hello world, ' },
{ text: 'goodbye ', style: [:bold] },
]
arranger.format_array = array
expect {
line_wrap.wrap_line(
arranger: arranger,
width: 1,
document: pdf,
)
}.to raise_error(Prawn::Errors::CannotFit)
end
it 'breaks on space' do
array = [{ text: 'hello world' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello')
end
it 'breaks on zero-width space' do
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
array = [{ text: "hello#{Prawn::Text::ZWSP}world" }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello')
end
it 'does not display zero-width space' do
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
array = [{ text: "hello#{Prawn::Text::ZWSP}world" }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(string).to eq('helloworld')
end
it 'does not raise CannotFit if first fragment is a zero-width space' do
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
array = [{ text: Prawn::Text::ZWSP }, { text: 'stringofchars' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: 50,
document: pdf,
)
expect(string).to eq('stringof')
end
it 'breaks on tab' do
array = [{ text: "hello\tworld" }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello')
end
it 'does not break on NBSP' do
array = [{ text: "hello#{Prawn::Text::NBSP}world" }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq("hello#{Prawn::Text::NBSP}wor")
end
it 'does not break on NBSP in a Win-1252 encoded string' do
array = [
{
text: "hello#{Prawn::Text::NBSP}world".encode(Encoding::Windows_1252),
},
]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq("hello#{Prawn::Text::NBSP}wor")
end
it 'breaks on hyphens' do
array = [{ text: 'hello-world' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello-')
end
it 'does not break after a hyphen that follows white space and precedes a word' do
array = [{ text: 'hello -' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello -')
array = [{ text: 'hello -world' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello')
end
it 'breaks on a soft hyphen' do
string = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}world")
array = [{ text: string }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq("hello#{Prawn::Text::SHY}")
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
line_wrap = described_class.new
string = "hello#{Prawn::Text::SHY}world"
array = [{ text: string }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq("hello#{Prawn::Text::SHY}")
end
it 'ignores width of a soft-hyphen during adding fragments to line',
issue: 775 do
hyphen_string = "Hy#{Prawn::Text::SHY}phe#{Prawn::Text::SHY}nat#{Prawn::Text::SHY}ions "
string1 = pdf.font.normalize_encoding(hyphen_string * 5)
string2 = pdf.font.normalize_encoding(('Hyphenations ' * 3) + hyphen_string)
array1 = [{ text: string1 }]
array2 = [{ text: string2 }]
arranger.format_array = array1
res1 = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
line_wrap = described_class.new
arranger.format_array = array2
res2 = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(res1).to eq(res2)
end
it 'does not display soft hyphens except at the end of a line for more than one element in format_array',
issue: 347 do
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
line_wrap = described_class.new
string1 = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}world ")
string2 = pdf.font.normalize_encoding("hi#{Prawn::Text::SHY}earth")
array = [{ text: string1 }, { text: string2 }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(string).to eq('helloworld hiearth')
end
it 'does not break before a hard hyphen that follows a word' do
enough_width_for_hello_world = 60
array = [{ text: 'hello world' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: enough_width_for_hello_world,
document: pdf,
)
expect(string).to eq('hello world')
array = [{ text: 'hello world-' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: enough_width_for_hello_world,
document: pdf,
)
expect(string).to eq('hello')
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
line_wrap = described_class.new
enough_width_for_hello_world = 68
array = [{ text: 'hello world' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: enough_width_for_hello_world,
document: pdf,
)
expect(string).to eq('hello world')
array = [{ text: 'hello world-' }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: enough_width_for_hello_world,
document: pdf,
)
expect(string).to eq('hello')
end
it 'does not break after a hard hyphen that follows a soft hyphen and precedes a word' do
string = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}-")
array = [{ text: string }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello-')
string = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}-world")
array = [{ text: string }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq("hello#{Prawn::Text::SHY}")
pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
line_wrap = described_class.new
string = "hello#{Prawn::Text::SHY}-"
array = [{ text: string }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq('hello-')
string = "hello#{Prawn::Text::SHY}-world"
array = [{ text: string }]
arranger.format_array = array
string = line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(string).to eq("hello#{Prawn::Text::SHY}")
end
it 'does not process UTF-8 chars with default font', issue: 693 do
array = [{ text: 'Test' }]
arranger.format_array = array
expect {
line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
}.to raise_exception(Prawn::Errors::IncompatibleStringEncoding)
end
it 'processes UTF-8 chars with UTF-8 font', issue: 693 do
array = [{ text: 'Test' }]
arranger.format_array = array
pdf.font(Pathname.new("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"))
string = line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(string).to eq('Test')
end
end
describe '#space_count' do
let(:arranger) { Prawn::Text::Formatted::Arranger.new(pdf) }
it 'returns the number of spaces in the last wrapped line' do
array = [
{ text: 'hello world, ' },
{ text: 'goodbye', style: [:bold] },
]
arranger.format_array = array
line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(line_wrap.space_count).to eq(2)
end
it 'excludes preceding and trailing spaces from the count' do
array = [
{ text: ' hello world, ' },
{ text: 'goodbye ', style: [:bold] },
]
arranger.format_array = array
line_wrap.wrap_line(
arranger: arranger,
width: 300,
document: pdf,
)
expect(line_wrap.space_count).to eq(2)
end
end
describe '#paragraph_finished?' do
let(:arranger) { Prawn::Text::Formatted::Arranger.new(pdf) }
let(:line_wrap) { described_class.new }
let(:one_word_width) { 50 }
it 'is false when the last printed line is not the end of the paragraph' do
array = [{ text: 'hello world' }]
arranger.format_array = array
line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(line_wrap.paragraph_finished?).to be false
end
it 'is true when the last printed line is the last fragment to print' do
array = [{ text: 'hello world' }]
arranger.format_array = array
line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(line_wrap.paragraph_finished?).to be true
end
it 'be_trues when a newline exists on the current line' do
array = [{ text: "hello\n world" }]
arranger.format_array = array
line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(line_wrap.paragraph_finished?).to be true
end
it 'be_trues when a newline exists in the next fragment' do
array = [
{ text: 'hello ' },
{ text: " \n" },
{ text: 'world' },
]
arranger.format_array = array
line_wrap.wrap_line(
arranger: arranger,
width: one_word_width,
document: pdf,
)
expect(line_wrap.paragraph_finished?).to be true
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text/formatted/fragment_spec.rb 0000664 0000000 0000000 00000017570 14571572164 023505 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text::Formatted::Fragment do
let(:pdf) { create_pdf }
describe 'Text::Formatted::Fragment' do
let(:fragment) do
format_state = {
styles: %i[bold italic],
color: nil,
link: nil,
anchor: nil,
font: nil,
size: nil,
}
described_class.new('hello world', format_state, pdf).tap do |fragment|
fragment.width = 100
fragment.left = 50
fragment.baseline = 200
fragment.line_height = 27
fragment.descender = 7
fragment.ascender = 17
end
end
describe '#width' do
it 'returns the width' do
expect(fragment.width).to eq(100)
end
end
describe '#styles' do
it 'returns the styles array' do
expect(fragment.styles).to eq(%i[bold italic])
end
it 'nevers return nil' do
format_state = {
styles: nil,
color: nil,
link: nil,
anchor: nil,
font: nil,
size: nil,
}
fragment = described_class.new('hello world', format_state, pdf)
expect(fragment.styles).to eq([])
end
end
describe '#line_height' do
it 'returns the line_height' do
expect(fragment.line_height).to eq(27)
end
end
describe '#ascender' do
it 'returns the ascender' do
expect(fragment.ascender).to eq(17)
end
end
describe '#descender' do
it 'returns the descender' do
expect(fragment.descender).to eq(7)
end
end
describe '#y_offset' do
it 'is zero' do
expect(fragment.y_offset).to eq(0)
end
end
describe '#bounding_box' do
it 'returns the bounding box surrounding the fragment' do
target_box = [50, 193, 150, 217]
expect(fragment.bounding_box).to eq(target_box)
end
end
describe '#absolute_bounding_box' do
it 'returns the bounding box surrounding the fragment in absolute coordinates' do
target_box = [50, 193, 150, 217]
target_box[0] += pdf.bounds.absolute_left
target_box[1] += pdf.bounds.absolute_bottom
target_box[2] += pdf.bounds.absolute_left
target_box[3] += pdf.bounds.absolute_bottom
expect(fragment.absolute_bounding_box).to eq(target_box)
end
end
describe '#underline_points' do
it 'defines a line under the fragment' do
y = 198.75
target_points = [[50, y], [150, y]]
expect(fragment.underline_points).to eq(target_points)
end
end
describe '#strikethrough_points' do
it 'defines a line through the fragment' do
y = 200 + (fragment.ascender * 0.3)
target_points = [[50, y], [150, y]]
expect(fragment.strikethrough_points).to eq(target_points)
end
end
end
describe '#space_count' do
it 'returns the number of spaces in the fragment' do
format_state = {}
fragment = described_class.new('hello world ', format_state, pdf)
expect(fragment.space_count).to eq(2)
end
it 'excludes trailing spaces from the count when :exclude_trailing_white_space => true' do
format_state = { exclude_trailing_white_space: true }
fragment = described_class.new('hello world ', format_state, pdf)
expect(fragment.space_count).to eq(1)
end
end
describe '#include_trailing_white_space!' do
it 'makes the fragment include trailing white space' do
format_state = { exclude_trailing_white_space: true }
fragment = described_class.new('hello world ', format_state, pdf)
expect(fragment.space_count).to eq(1)
fragment.include_trailing_white_space!
expect(fragment.space_count).to eq(2)
end
end
describe '#text' do
it 'returns the fragment text' do
format_state = {}
fragment = described_class.new('hello world ', format_state, pdf)
expect(fragment.text).to eq('hello world ')
end
it 'returns the fragment text without trailing spaces when :exclude_trailing_white_space => true' do
format_state = { exclude_trailing_white_space: true }
fragment = described_class.new('hello world ', format_state, pdf)
expect(fragment.text).to eq('hello world')
end
end
describe '#word_spacing=' do
let(:fragment) do
format_state = {
styles: %i[bold italic],
color: nil,
link: nil,
anchor: nil,
font: nil,
size: nil,
}
described_class.new('hello world', format_state, pdf).tap do |fragment|
fragment.width = 100
fragment.left = 50
fragment.baseline = 200
fragment.line_height = 27
fragment.descender = 7
fragment.ascender = 17
fragment.word_spacing = 10
end
end
it 'accounts for word_spacing in #width' do
expect(fragment.width).to eq(110)
end
it 'accounts for word_spacing in #bounding_box' do
target_box = [50, 193, 160, 217]
expect(fragment.bounding_box).to eq(target_box)
end
it 'accounts for word_spacing in #absolute_bounding_box' do
target_box = [50, 193, 160, 217]
target_box[0] += pdf.bounds.absolute_left
target_box[1] += pdf.bounds.absolute_bottom
target_box[2] += pdf.bounds.absolute_left
target_box[3] += pdf.bounds.absolute_bottom
expect(fragment.absolute_bounding_box).to eq(target_box)
end
it 'accounts for word_spacing in #underline_points' do
y = 198.75
target_points = [[50, y], [160, y]]
expect(fragment.underline_points).to eq(target_points)
end
it 'accounts for word_spacing in #strikethrough_points' do
y = 200 + (fragment.ascender * 0.3)
target_points = [[50, y], [160, y]]
expect(fragment.strikethrough_points).to eq(target_points)
end
end
describe 'subscript' do
let(:fragment) do
format_state = {
styles: [:subscript],
color: nil,
link: nil,
anchor: nil,
font: nil,
size: nil,
}
described_class.new('hello world', format_state, pdf).tap do |fragment|
fragment.line_height = 27
fragment.descender = 7
fragment.ascender = 17
end
end
describe '#subscript?' do
it 'be_trues' do
expect(fragment).to be_subscript
end
end
describe '#y_offset' do
it 'returns a negative value' do
expect(fragment.y_offset).to be < 0
end
end
end
describe 'superscript' do
let(:fragment) do
format_state = {
styles: [:superscript],
color: nil,
link: nil,
anchor: nil,
font: nil,
size: nil,
}
described_class.new('hello world', format_state, pdf).tap do |fragment|
fragment.line_height = 27
fragment.descender = 7
fragment.ascender = 17
end
end
describe '#superscript?' do
it 'be_trues' do
expect(fragment).to be_superscript
end
end
describe '#y_offset' do
it 'returns a positive value' do
expect(fragment.y_offset).to be > 0
end
end
end
context 'with :direction => :rtl' do
it '#text should be reversed' do
format_state = { direction: :rtl }
fragment = described_class.new('hello world', format_state, pdf)
expect(fragment.text).to eq('dlrow olleh')
end
end
describe '#default_direction=' do
it 'sets the direction if there is no fragment level direction specification' do
format_state = {}
fragment = described_class.new('hello world', format_state, pdf)
fragment.default_direction = :rtl
expect(fragment.direction).to eq(:rtl)
end
it 'does not set the direction if there is a fragment level direction specification' do
format_state = { direction: :rtl }
fragment = described_class.new('hello world', format_state, pdf)
fragment.default_direction = :ltr
expect(fragment.direction).to eq(:rtl)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text/formatted/box_spec.rb 0000664 0000000 0000000 00000067332 14571572164 022473 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text::Formatted::Box do
let(:pdf) { create_pdf }
describe 'wrapping' do
it 'does not wrap between two fragments' do
texts = [
{ text: 'Hello ' },
{ text: 'World' },
{ text: '2', styles: [:superscript] },
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Hello World'),
)
text_box.render
expect(text_box.text).to eq("Hello\nWorld2")
end
it 'does not raise an Encoding::CompatibilityError when keeping a TTF and an AFM font together' do
file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
pdf.font_families['Kai'] = {
normal: { file: file, font: 'Kai' },
}
texts = [
{ text: 'Hello ' },
{ text: '再见', font: 'Kai' },
{ text: 'World' },
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Hello World'),
)
expect { text_box.render }.to_not raise_error
end
it 'wraps between two fragments when the preceding fragment ends with a white space' do
texts = [
{ text: 'Hello ' },
{ text: 'World ' },
{ text: '2', styles: [:superscript] },
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Hello World'),
)
text_box.render
expect(text_box.text).to eq("Hello World\n2")
texts = [
{ text: 'Hello ' },
{ text: "World\n" },
{ text: '2', styles: [:superscript] },
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Hello World'),
)
text_box.render
expect(text_box.text).to eq("Hello World\n2")
end
it 'wraps between two fragments when the final fragment begins with a white space' do
texts = [
{ text: 'Hello ' },
{ text: 'World' },
{ text: ' 2', styles: [:superscript] },
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Hello World'),
)
text_box.render
expect(text_box.text).to eq("Hello World\n2")
texts = [
{ text: 'Hello ' },
{ text: 'World' },
{ text: "\n2", styles: [:superscript] },
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Hello World'),
)
text_box.render
expect(text_box.text).to eq("Hello World\n2")
end
it 'properlies handle empty slices using default encoding' do
texts = [
{
text: 'Noua Delineatio Geographica generalis | Apostolicarum ' \
'peregrinationum | S FRANCISCI XAUERII | Indiarum & Iaponiæ Apostoli',
font: 'Courier',
size: 10,
},
]
text_box = described_class.new(
texts,
document: pdf,
width: pdf.width_of('Noua Delineatio Geographica gen'),
)
expect {
text_box.render
}.to_not raise_error
expect(text_box.text).to eq(
"Noua Delineatio Geographica\ngeneralis | Apostolicarum\n" \
"peregrinationum | S FRANCISCI\nXAUERII | Indiarum & Iaponi\346\n" \
'Apostoli',
)
end
end
describe 'Text::Formatted::Box with :fallback_fonts option that includes a Chinese font and set of Chinese glyphs ' \
'not in the current font' do
it 'changes the font to the Chinese font for the Chinese glyphs' do
file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
pdf.font_families['Kai'] = {
normal: { file: file, font: 'Kai' },
}
formatted_text = [
{ text: 'hello你好' },
{ text: '再见goodbye' },
]
pdf.formatted_text_box(formatted_text, fallback_fonts: ['Kai'])
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(4)
expect(fonts_used[0]).to eq(:Helvetica)
expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[2].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[3]).to eq(:Helvetica)
expect(text.strings[0]).to eq('hello')
expect(text.strings[1]).to eq('你好')
expect(text.strings[2]).to eq('再见')
expect(text.strings[3]).to eq('goodbye')
end
end
describe 'Text::Formatted::Box with :fallback_fonts option that includes an AFM font and Win-Ansi glyph not ' \
'in the current Chinese font' do
it 'changes the font to the AFM font for the Win-Ansi glyph' do
file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
pdf.font_families['Kai'] = {
normal: { file: file, font: 'Kai' },
}
pdf.font('Kai')
formatted_text = [
{ text: 'hello你好' },
{ text: '再见€' },
]
pdf.formatted_text_box(formatted_text, fallback_fonts: ['Helvetica'])
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(4)
expect(fonts_used[0].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[2].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[3]).to eq(:Helvetica)
expect(text.strings[0]).to eq('hello')
expect(text.strings[1]).to eq('你好')
expect(text.strings[2]).to eq('再见')
expect(text.strings[3]).to eq('€')
end
end
describe 'Text::Formatted::Box with :fallback_fonts option and fragment level font' do
it 'uses the fragment level font except for glyphs not in that font' do
file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
pdf.font_families['Kai'] = {
normal: { file: file, font: 'Kai' },
}
file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
pdf.font_families['DejaVu Sans'] = {
normal: { file: file },
}
formatted_text = [
{ text: 'hello你好' },
{ text: '再见goodbye', font: 'Times-Roman' },
]
pdf.formatted_text_box(formatted_text, fallback_fonts: ['Kai'])
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(4)
expect(fonts_used[0]).to eq(:Helvetica)
expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[2].to_s).to match(/GBZenKai-Medium/)
expect(fonts_used[3]).to eq(:'Times-Roman')
expect(text.strings[0]).to eq('hello')
expect(text.strings[1]).to eq('你好')
expect(text.strings[2]).to eq('再见')
expect(text.strings[3]).to eq('goodbye')
end
it 'considers style when looking for glyph in font' do
dustismo_file = "#{Prawn::DATADIR}/fonts/Dustismo_Roman.ttf"
dejavu_sans_file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
dejavu_sans_bold_file = "#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf"
pdf.font_families['Dustismo'] = {
normal: { file: dustismo_file },
bold: { file: dejavu_sans_bold_file },
}
pdf.font_families['Fallback'] = {
normal: { file: dejavu_sans_file },
bold: { file: dejavu_sans_file },
}
formatted_text = [{ text: ?\u203b, styles: [:bold], font: 'Dustismo' }]
pdf.formatted_text_box(formatted_text, fallback_fonts: ['Fallback'])
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(1)
expect(fonts_used[0].to_s).to match(/DejaVuSans-Bold/)
expect(text.strings[0]).to eq(?\u203b)
end
end
describe 'Text::Formatted::Box' do
let(:formatted_text) { [{ text: 'hello你好' }] }
before do
file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
pdf.font_families['Kai'] = {
normal: { file: file, font: 'Kai' },
}
file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
pdf.font_families['DejaVu Sans'] = {
normal: { file: file },
}
pdf.fallback_fonts(['Kai'])
pdf.fallback_fonts = ['Kai']
end
it '#fallback_fonts should return the document-wide fallback fonts' do
expect(pdf.fallback_fonts).to eq(['Kai'])
end
it 'is able to set text fallback_fonts document-wide' do
pdf.formatted_text_box(formatted_text)
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(2)
expect(fonts_used[0]).to eq(:Helvetica)
expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
end
it 'is able to override document-wide fallback_fonts' do
pdf.fallback_fonts = ['DejaVu Sans']
pdf.formatted_text_box(formatted_text, fallback_fonts: ['Kai'])
text = PDF::Inspector::Text.analyze(pdf.render)
fonts_used = text.font_settings.map { |e| e[:name] }
expect(fonts_used.length).to eq(2)
expect(fonts_used[0]).to eq(:Helvetica)
expect(fonts_used[1]).to match(/Kai/)
end
it 'omits the fallback fonts overhead when passing an empty array as the :fallback_fonts' do
pdf.font('Kai')
box = described_class.new(
formatted_text,
document: pdf,
fallback_fonts: [],
)
allow(box).to receive(:process_fallback_fonts)
box.render
expect(box).to_not have_received(:process_fallback_fonts)
end
it 'is able to clear document-wide fallback_fonts' do
pdf.fallback_fonts([])
box = described_class.new(formatted_text, document: pdf)
pdf.font('Kai')
allow(box).to receive(:process_fallback_fonts)
box.render
expect(box).to_not have_received(:process_fallback_fonts)
end
end
describe 'Text::Formatted::Box with :fallback_fonts option with glyphs not in the primary or the fallback fonts' do
it 'raises an exception' do
formatted_text = [{ text: 'hello world. 世界你好。' }]
expect {
pdf.formatted_text_box(formatted_text, fallback_fonts: ['Courier'])
}.to raise_error(Prawn::Errors::IncompatibleStringEncoding)
end
end
describe 'Text::Formatted::Box#extensions' do
let(:formatted_wrap_override) do
Module.new do
# rubocop: disable RSpec/InstanceVariable
def wrap(_array)
initialize_wrap([{ text: 'all your base are belong to us' }])
@line_wrap.wrap_line(
document: @document,
kerning: @kerning,
width: 10_000,
arranger: @arranger,
)
fragment = @arranger.retrieve_fragment
format_and_draw_fragment(fragment, 0, @line_wrap.width, 0)
[]
end
# rubocop: enable RSpec/InstanceVariable
end
end
it 'is able to override default line wrapping' do
described_class.extensions << formatted_wrap_override
pdf.formatted_text_box([{ text: 'hello world' }], {})
described_class.extensions.delete(formatted_wrap_override)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('all your base are belong to us')
end
it 'overrides Text::Formatted::Box line wrapping does not affect Text::Box wrapping' do
described_class.extensions << formatted_wrap_override
pdf.text_box('hello world', {})
described_class.extensions.delete(formatted_wrap_override)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('hello world')
end
it "overring Text::Box line wrapping doesn't override Text::Box wrapping" do
Prawn::Text::Box.extensions << formatted_wrap_override
pdf.text_box('hello world', {})
Prawn::Text::Box.extensions.delete(formatted_wrap_override)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('all your base are belong to us')
end
end
describe 'Text::Formatted::Box#render' do
let(:fragment_callback_class) do
Class.new do
def render_behind(fragment); end
def render_in_front(fragment); end
end
end
it 'handles newlines' do
array = [{ text: "hello\nworld" }]
options = { document: pdf }
text_box = described_class.new(array, options)
text_box.render
expect(text_box.text).to eq("hello\nworld")
end
it 'omits spaces from the beginning of the line' do
array = [{ text: " hello\n world" }]
options = { document: pdf }
text_box = described_class.new(array, options)
text_box.render
expect(text_box.text).to eq("hello\nworld")
end
it 'is okay printing a line of whitespace' do
array = [{ text: "hello\n \nworld" }]
options = { document: pdf }
text_box = described_class.new(array, options)
text_box.render
expect(text_box.text).to eq("hello\n\nworld")
array = [
{ text: "hello#{' ' * 500}" },
{ text: ' ' * 500 },
{ text: "#{' ' * 500}\n" },
{ text: 'world' },
]
options = { document: pdf }
text_box = described_class.new(array, options)
text_box.render
expect(text_box.text).to eq("hello\n\nworld")
end
it 'enables fragment level direction setting' do
number_of_hellos = 18
array = [
{ text: 'hello ' * number_of_hellos },
{ text: 'world', direction: :ltr },
{ text: ', how are you?' },
]
options = { document: pdf, direction: :rtl }
text_box = described_class.new(array, options)
text_box.render
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('era woh ,')
expect(text.strings[1]).to eq('world')
expect(text.strings[2]).to eq(' olleh' * number_of_hellos)
expect(text.strings[3]).to eq('?uoy')
end
it 'is able to perform fragment callbacks' do
callback_object = fragment_callback_class.new
allow(callback_object).to receive(:render_behind)
allow(callback_object).to receive(:render_in_front)
array = [
{ text: 'hello world ' },
{ text: 'callback now', callback: callback_object },
]
text_box = described_class.new(array, document: pdf)
text_box.render
expect(callback_object).to have_received(:render_behind).with(kind_of(Prawn::Text::Formatted::Fragment))
expect(callback_object).to have_received(:render_in_front).with(kind_of(Prawn::Text::Formatted::Fragment))
end
it 'is able to perform fragment callbacks on multiple objects' do
callback_object = fragment_callback_class.new
allow(callback_object).to receive(:render_behind)
allow(callback_object).to receive(:render_in_front)
callback_object2 = fragment_callback_class.new
allow(callback_object2).to receive(:render_behind)
allow(callback_object2).to receive(:render_in_front)
array = [
{ text: 'hello world ' },
{ text: 'callback now', callback: [callback_object, callback_object2] },
]
text_box = described_class.new(array, document: pdf)
text_box.render
expect(callback_object).to have_received(:render_behind).with(kind_of(Prawn::Text::Formatted::Fragment))
expect(callback_object).to have_received(:render_in_front).with(kind_of(Prawn::Text::Formatted::Fragment))
expect(callback_object2).to have_received(:render_behind).with(kind_of(Prawn::Text::Formatted::Fragment))
expect(callback_object2).to have_received(:render_in_front).with(kind_of(Prawn::Text::Formatted::Fragment))
end
it 'fragment callbacks is able to define only the callback they need' do
behind = (
Class.new do
def render_behind(fragment); end
end
).new
allow(behind).to receive(:render_behind)
in_front = (
Class.new do
def render_in_front(fragment); end
end
).new
allow(in_front).to receive(:render_in_front)
array = [
{ text: 'hello world ' },
{ text: 'callback now', callback: [behind, in_front] },
]
text_box = described_class.new(array, document: pdf)
text_box.render # trigger callbacks
expect(behind).to have_received(:render_behind)
expect(in_front).to have_received(:render_in_front)
end
it 'is able to set the font' do
array = [
{ text: 'this contains ' },
{
text: 'Times-Bold',
styles: [:bold],
font: 'Times-Roman',
},
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
fonts = contents.font_settings.map { |e| e[:name] }
expect(fonts).to eq(%i[Helvetica Times-Bold Helvetica])
expect(contents.strings[0]).to eq('this contains ')
expect(contents.strings[1]).to eq('Times-Bold')
expect(contents.strings[2]).to eq(' text')
end
it 'is able to set bold' do
array = [
{ text: 'this contains ' },
{ text: 'bold', styles: [:bold] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
fonts = contents.font_settings.map { |e| e[:name] }
expect(fonts).to eq(%i[Helvetica Helvetica-Bold Helvetica])
expect(contents.strings[0]).to eq('this contains ')
expect(contents.strings[1]).to eq('bold')
expect(contents.strings[2]).to eq(' text')
end
it 'is able to set italics' do
array = [
{ text: 'this contains ' },
{ text: 'italic', styles: [:italic] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
fonts = contents.font_settings.map { |e| e[:name] }
expect(fonts).to eq(%i[Helvetica Helvetica-Oblique Helvetica])
end
it 'is able to set subscript' do
array = [
{ text: 'this contains ' },
{ text: 'subscript', size: 18, styles: [:subscript] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.font_settings[0][:size]).to eq(12)
expect(contents.font_settings[1][:size])
.to be_within(0.0001).of(18 * 0.583)
end
it 'is able to set superscript' do
array = [
{ text: 'this contains ' },
{ text: 'superscript', size: 18, styles: [:superscript] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.font_settings[0][:size]).to eq(12)
expect(contents.font_settings[1][:size])
.to be_within(0.0001).of(18 * 0.583)
end
it 'is able to set compound bold and italic text' do
array = [
{ text: 'this contains ' },
{ text: 'bold italic', styles: %i[bold italic] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
fonts = contents.font_settings.map { |e| e[:name] }
expect(fonts).to eq(%i[Helvetica Helvetica-BoldOblique Helvetica])
end
it 'is able to underline' do
array = [
{ text: 'this contains ' },
{ text: 'underlined', styles: [:underline] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points.length).to eq(2)
end
it 'is able to strikethrough' do
array = [
{ text: 'this contains ' },
{ text: 'struckthrough', styles: [:strikethrough] },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points.length).to eq(2)
end
it 'is able to add URL links' do
allow(pdf).to receive(:link_annotation)
array = [
{ text: 'click ' },
{ text: 'here', link: 'http://example.com' },
{ text: ' to visit' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
expect(pdf).to have_received(:link_annotation).with(
kind_of(Array),
Border: [0, 0, 0],
A: {
Type: :Action,
S: :URI,
URI: 'http://example.com',
},
)
end
it 'is able to add destination links' do
allow(pdf).to receive(:link_annotation)
array = [
{ text: 'Go to the ' },
{ text: 'Table of Contents', anchor: 'ToC' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
expect(pdf).to have_received(:link_annotation).with(
kind_of(Array),
Border: [0, 0, 0],
Dest: 'ToC',
)
end
it 'is able to add local actions' do
allow(pdf).to receive(:link_annotation)
array = [
{ text: 'click ' },
{ text: 'here', local: '../example.pdf' },
{ text: ' to open a local file' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
expect(pdf).to have_received(:link_annotation).with(
kind_of(Array),
Border: [0, 0, 0],
A: {
Type: :Action,
S: :Launch,
F: '../example.pdf',
NewWindow: true,
},
)
end
it 'is able to set font size' do
array = [
{ text: 'this contains ' },
{ text: 'sized', size: 24 },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.font_settings[0][:size]).to eq(12)
expect(contents.font_settings[1][:size]).to eq(24)
end
it 'sets the baseline based on the tallest fragment on a given line' do
array = [
{ text: 'this contains ' },
{ text: 'sized', size: 24 },
{ text: ' text' },
]
text_box = described_class.new(array, document: pdf)
text_box.render
pdf.font_size(24) do
expect(text_box.height).to be_within(0.001)
.of(pdf.font.ascender + pdf.font.descender)
end
end
it 'is able to set color via an rgb hex string' do
array = [
{
text: 'rgb',
color: 'ff0000',
},
]
text_box = described_class.new(array, document: pdf)
text_box.render
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.fill_color_count).to eq(2)
expect(colors.stroke_color_count).to eq(2)
end
it 'is able to set color using a cmyk array' do
array = [
{
text: 'cmyk',
color: [100, 0, 0, 0],
},
]
text_box = described_class.new(array, document: pdf)
text_box.render
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.fill_color_count).to eq(2)
expect(colors.stroke_color_count).to eq(2)
end
end
describe 'Text::Formatted::Box#render(:dry_run => true)' do
it 'does not change the graphics state of the document' do
state_before = PDF::Inspector::Graphics::Color.analyze(pdf.render)
fill_color_count = state_before.fill_color_count
stroke_color_count = state_before.stroke_color_count
stroke_color_space_count = state_before.stroke_color_space_count
array = [
{
text: 'Foo',
color: [0, 0, 0, 100],
},
]
options = { document: pdf }
text_box = described_class.new(array, options)
text_box.render(dry_run: true)
state_after = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(state_after.fill_color_count).to eq(fill_color_count)
expect(state_after.stroke_color_count).to eq(stroke_color_count)
expect(state_after.stroke_color_space_count)
.to eq(stroke_color_space_count)
end
end
describe 'Text::Formatted::Box#render with fragment level :character_spacing option' do
it 'draws the character spacing to the document' do
array = [
{
text: 'hello world',
character_spacing: 7,
},
]
options = { document: pdf }
text_box = described_class.new(array, options)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.character_spacing[0]).to eq(7)
end
it 'lays out text properly' do
array = [
{
text: 'hello world',
font: 'Courier',
character_spacing: 10,
},
]
options = {
document: pdf,
width: 100,
overflow: :expand,
}
text_box = described_class.new(array, options)
text_box.render
expect(text_box.text).to eq("hello\nworld")
end
end
describe 'Text::Formatted::Box#render with :align => :justify' do
it 'does not justify the last line of a paragraph' do
array = [
{ text: 'hello world ' },
{ text: "\n" },
{ text: 'goodbye' },
]
options = { document: pdf, align: :justify }
text_box = described_class.new(array, options)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.word_spacing).to be_empty
end
it 'raise an exception when align value is not a symbol' do
array = [
{ text: 'hello world ' },
{ text: "\n" },
{ text: 'goodbye' },
]
options = { document: pdf, align: 'justify' }
text_box = described_class.new(array, options)
expect { text_box.render }.to raise_error(
ArgumentError,
'align must be one of :left, :right, :center or :justify symbols',
)
end
end
describe 'Text::Formatted::Box#render with :valign => :center' do
it 'has a bottom gap equal to baseline and bottom of box' do
box_height = 100
y = 450
array = [{ text: 'Vertical Align' }]
options = {
document: pdf,
valign: :center,
at: [0, y],
width: 100,
height: box_height,
size: 16,
}
text_box = described_class.new(array, options)
text_box.render
line_padding = (box_height - text_box.height + text_box.descender) * 0.5
baseline = y - line_padding
expect(text_box.at[1]).to be_within(0.01).of(baseline)
end
end
describe 'Text::Formatted::Box#render with :valign => :bottom' do
it 'does not render a gap between the text and bottom of box' do
box_height = 100
y = 450
array = [{ text: 'Vertical Align' }]
options = {
document: pdf,
valign: :bottom,
at: [0, y],
width: 100,
height: box_height,
size: 16,
}
text_box = described_class.new(array, options)
text_box.render
top_padding = y - (box_height - text_box.height)
expect(text_box.at[1]).to be_within(0.01).of(top_padding)
end
end
describe 'Text::Formatted::Box#render with :valign => invalid argument' do
it 'raise an exception when valign value is not a symbol' do
array = [{ text: 'Invalid Vertical Align' }]
options = { document: pdf, valign: 'center' }
text_box = described_class.new(array, options)
expect { text_box.render }.to raise_error(
ArgumentError,
'valign must be one of :left, :right or :center symbols',
)
end
it 'raise an exception when valign value is an invalid symbol' do
array = [{ text: 'Invalid Vertical Align' }]
options = { document: pdf, valign: ':justify' }
text_box = described_class.new(array, options)
expect { text_box.render }.to raise_error(
ArgumentError,
'valign must be one of :left, :right or :center symbols',
)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text/formatted/arranger_spec.rb 0000664 0000000 0000000 00000031461 14571572164 023476 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text::Formatted::Arranger do
let(:pdf) { create_pdf }
let(:arranger) { described_class.new(pdf) }
describe '#format_array' do
it 'populates the unconsumed array' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you?' },
]
arranger.format_array = array
expect(arranger.unconsumed[0]).to eq(text: 'hello ')
expect(arranger.unconsumed[1]).to eq(text: 'world how ', styles: [:bold])
expect(arranger.unconsumed[2])
.to eq(text: 'are', styles: %i[bold italic])
expect(arranger.unconsumed[3]).to eq(text: ' you?')
end
it 'splits newlins into their own elements' do
array = [
{ text: "\nhello\nworld" },
]
arranger.format_array = array
expect(arranger.unconsumed[0]).to eq(text: "\n")
expect(arranger.unconsumed[1]).to eq(text: 'hello')
expect(arranger.unconsumed[2]).to eq(text: "\n")
expect(arranger.unconsumed[3]).to eq(text: 'world')
end
end
describe '#preview_next_string' do
context 'with a formatted array' do
let(:array) { [{ text: 'hello' }] }
before do
arranger.format_array = array
end
it 'does not populate the consumed array' do
arranger.preview_next_string
expect(arranger.consumed).to eq([])
end
it 'returns the text of the next unconsumed hash' do
expect(arranger.preview_next_string).to eq('hello')
end
it 'returns nil if there is no more unconsumed text' do
arranger.next_string
expect(arranger.preview_next_string).to be_nil
end
end
end
describe '#next_string' do
let(:array) do
[
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you?' },
]
end
before do
arranger.format_array = array
end
it 'raises an error if called after a line was finalized' do
arranger.finalize_line
expect { arranger.next_string }.to raise_error(Prawn::Text::Formatted::Arranger::NotFinalized)
end
it 'populates the conumed array' do
while arranger.next_string
end
expect(arranger.consumed[0]).to eq(text: 'hello ')
expect(arranger.consumed[1]).to eq(text: 'world how ', styles: [:bold])
expect(arranger.consumed[2]).to eq(text: 'are', styles: %i[bold italic])
expect(arranger.consumed[3]).to eq(text: ' you?')
end
it 'populates the current_format_state array' do
arranger.next_string
expect(arranger.current_format_state).to eq({})
arranger.next_string
expect(arranger.current_format_state).to eq(styles: [:bold])
arranger.next_string
expect(arranger.current_format_state).to eq(styles: %i[bold italic])
arranger.next_string
expect(arranger.current_format_state).to eq({})
end
it 'returns the text of the newly consumed hash' do
expect(arranger.next_string).to eq('hello ')
end
it 'returns nil when there are no more unconsumed hashes' do
4.times do
arranger.next_string
end
expect(arranger.next_string).to be_nil
end
end
describe '#retrieve_fragment' do
context 'with a formatted array whos text is an empty string' do
let(:array) do
[
{ text: "hello\nworld\n\n\nhow are you?" },
{ text: "\n" },
{ text: "\n" },
{ text: "\n" },
{ text: '' },
{ text: 'fine, thanks.' },
{ text: '' },
{ text: "\n" },
{ text: '' },
]
end
before do
arranger.format_array = array
while arranger.next_string
end
arranger.finalize_line
end
# rubocop: disable Lint/AssignmentInCondition
it 'never returns a fragment whose text is an empty string' do
while fragment = arranger.retrieve_fragment
expect(fragment.text).to_not be_empty
end
end
# rubocop: enable Lint/AssignmentInCondition
end
context 'with formatted array' do
let(:array) do
[
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you?' },
]
end
before do
arranger.format_array = array
end
describe 'after all strings have been consumed' do
before do
while arranger.next_string
end
end
it 'raises an error if not finalized' do
expect { arranger.retrieve_fragment }.to raise_error(Prawn::Text::Formatted::Arranger::NotFinalized)
end
describe 'and finalized' do
before do
arranger.finalize_line
end
it 'returns the consumed fragments in order of consumption' do
expect(arranger.retrieve_fragment.text).to eq('hello ')
expect(arranger.retrieve_fragment.text).to eq('world how ')
expect(arranger.retrieve_fragment.text).to eq('are')
expect(arranger.retrieve_fragment.text).to eq(' you?')
end
it 'does not alter the current font style' do
arranger.retrieve_fragment
expect(arranger.current_format_state[:styles]).to be_nil
end
end
end
end
end
describe '#update_last_string' do
it 'updates the last retrieved string with what actually fit on the line and the list of unconsumed with ' \
'what did not' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?', styles: %i[bold italic] },
]
arranger.format_array = array
while arranger.next_string
end
arranger.update_last_string(' you', ' now?', nil)
expect(arranger.consumed[3]).to eq(
text: ' you',
styles: %i[bold italic],
)
expect(arranger.unconsumed).to eq(
[
{ text: ' now?', styles: %i[bold italic] },
],
)
end
it 'sets the format state to the previously processed fragment' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
3.times { arranger.next_string }
expect(arranger.current_format_state).to eq(styles: %i[bold italic])
arranger.update_last_string('', 'are', '-')
expect(arranger.current_format_state).to eq(styles: [:bold])
end
context 'when the entire string was used' do
it 'does not push empty string onto unconsumed' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
while arranger.next_string
end
arranger.update_last_string(' you now?', '', nil)
expect(arranger.unconsumed).to eq([])
end
end
end
describe '#space_count' do
before do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you?' },
]
arranger.format_array = array
while arranger.next_string
end
end
it 'raises an error if called before finalize_line was called' do
expect { arranger.space_count }.to raise_error(Prawn::Text::Formatted::Arranger::NotFinalized)
end
it 'returns the total number of spaces in all fragments' do
arranger.finalize_line
expect(arranger.space_count).to eq(4)
end
end
describe '#finalize_line' do
it 'makes it so that all trailing white space fragments exclude trailing white space' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: ' ', styles: %i[bold italic] },
]
arranger.format_array = array
while arranger.next_string
end
arranger.finalize_line
expect(arranger.fragments.length).to eq(3)
fragment = arranger.retrieve_fragment
expect(fragment.text).to eq('hello ')
fragment = arranger.retrieve_fragment
expect(fragment.text).to eq('world how')
fragment = arranger.retrieve_fragment
expect(fragment.text).to eq('')
end
end
describe '#line_width' do
before do
array = [
{ text: 'hello ' },
{ text: 'world', styles: [:bold] },
]
arranger.format_array = array
while arranger.next_string
end
end
it 'raises an error if called before finalize_line was called' do
expect { arranger.line_width }.to raise_error(Prawn::Text::Formatted::Arranger::NotFinalized)
end
it 'returns the width of the complete line' do
arranger.finalize_line
expect(arranger.line_width).to be > 0
end
end
describe '#line_width with character_spacing > 0' do
it 'returns a width greater than a line without a character_spacing' do
array = [
{ text: 'hello ' },
{ text: 'world', styles: [:bold] },
]
arranger.format_array = array
while arranger.next_string
end
arranger.finalize_line
base_line_width = arranger.line_width
array = [
{ text: 'hello ' },
{ text: 'world', styles: [:bold], character_spacing: 7 },
]
arranger.format_array = array
while arranger.next_string
end
arranger.finalize_line
expect(arranger.line_width).to be > base_line_width
end
end
describe '#line' do
before do
array = [
{ text: 'hello ' },
{ text: 'world', styles: [:bold] },
]
arranger.format_array = array
while arranger.next_string
end
end
it 'raises an error if called before finalize_line was called' do
expect { arranger.line }.to raise_error(Prawn::Text::Formatted::Arranger::NotFinalized)
end
it 'returns the complete line' do
arranger.finalize_line
expect(arranger.line).to eq('hello world')
end
end
describe '#unconsumed' do
it 'returns the original array if nothing was consumed' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
expect(arranger.unconsumed).to eq(array)
end
it 'returns an empty array if everything was consumed' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
while arranger.next_string
end
expect(arranger.unconsumed).to eq([])
end
end
describe '#finished' do
it 'be_falses if anything was not printed' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
while arranger.next_string
end
arranger.update_last_string(' you', 'now?', nil)
expect(arranger).to_not be_finished
end
it 'be_falses if everything was printed' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
while arranger.next_string
end
expect(arranger).to be_finished
end
end
describe '#max_line_height' do
it 'is the height of the maximum consumed fragment' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic], size: 28 },
{ text: ' you now?' },
]
arranger.format_array = array
while arranger.next_string
end
arranger.finalize_line
expect(arranger.max_line_height).to be_within(0.0001).of(33.32)
end
end
describe '#repack_unretrieved' do
it 'restores part of the original string' do
array = [
{ text: 'hello ' },
{ text: 'world how ', styles: [:bold] },
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
]
arranger.format_array = array
while arranger.next_string
end
arranger.finalize_line
arranger.retrieve_fragment
arranger.retrieve_fragment
arranger.repack_unretrieved
expect(arranger.unconsumed).to eq(
[
{ text: 'are', styles: %i[bold italic] },
{ text: ' you now?' },
],
)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/text/box_spec.rb 0000664 0000000 0000000 00000102537 14571572164 020503 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Text::Box do
let(:pdf) { create_pdf }
it 'is able to set leading document-wide' do
pdf.default_leading(7)
pdf.default_leading = 7
text_box = described_class.new('hello world', document: pdf)
expect(text_box.leading).to eq(7)
end
it 'option should be able to override document-wide leading' do
pdf.default_leading = 7
text_box = described_class.new(
'hello world',
document: pdf,
leading: 20,
)
expect(text_box.leading).to eq(20)
end
it 'is able to set text direction document-wide' do
pdf.text_direction(:rtl)
pdf.text_direction = :rtl
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(string, document: pdf)
text_box.render
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('?uoy era woh ,dlrow olleH')
expect(text.strings[1]).to eq(".uoy knaht ,enif m'I")
end
it 'is able to reverse multi-byte text' do
pdf.text_direction(:rtl)
pdf.text_direction = :rtl
pdf.text_direction = :rtl
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf", size: 16) do
pdf.text('写个小')
end
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('小个写')
end
it 'option should be able to override document-wide text direction' do
pdf.text_direction = :rtl
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(
string,
document: pdf,
direction: :ltr,
)
text_box.render
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings[0]).to eq('Hello world, how are you?')
expect(text.strings[1]).to eq("I'm fine, thank you.")
end
it 'only requires enough space for the descender and the ascender when determining whether a line can fit' do
text = 'Oh hai text rect'
options = {
document: pdf,
height: pdf.font.ascender + pdf.font.descender,
}
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to eq('Oh hai text rect')
text = "Oh hai text rect\nOh hai text rect"
options = {
document: pdf,
height: pdf.font.height + pdf.font.ascender + pdf.font.descender,
}
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to eq("Oh hai text rect\nOh hai text rect")
end
describe '#nothing_printed?' do
it 'returns true when nothing printed' do
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(string, height: 2, document: pdf)
text_box.render
expect(text_box.nothing_printed?).to be true
end
it 'returns false when something printed' do
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(string, height: 14, document: pdf)
text_box.render
expect(text_box.nothing_printed?).to be false
end
end
describe '#everything_printed?' do
it 'returns false when not everything printed' do
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(string, height: 14, document: pdf)
text_box.render
expect(text_box.everything_printed?).to be false
end
it 'returns true when everything printed' do
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(string, document: pdf)
text_box.render
expect(text_box.everything_printed?).to be true
end
end
describe '#line_gap' do
it '==S the line gap of the font when using a single font and font size' do
string = "Hello world, how are you?\nI'm fine, thank you."
text_box = described_class.new(string, document: pdf)
text_box.render
expect(text_box.line_gap).to be_within(0.0001).of(pdf.font.line_gap)
end
end
describe '#render with :align => :justify' do
it 'draws the word spacing to the document' do
string = 'hello world ' * 20
options = { document: pdf, align: :justify }
text_box = described_class.new(string, options)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.word_spacing[0]).to be > 0
end
it 'does not justify the last line of a paragraph' do
string = 'hello world '
options = { document: pdf, align: :justify }
text_box = described_class.new(string, options)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.word_spacing).to be_empty
end
end
describe '#height without leading' do
it 'is the sum of the height of each line, not including the space below the last line' do
text = "Oh hai text rect.\nOh hai text rect."
options = { document: pdf }
text_box = described_class.new(text, options)
text_box.render
expect(text_box.height).to be_within(0.001).of((pdf.font.height * 2) - pdf.font.line_gap)
end
end
describe '#height with leading' do
it 'is the sum of the height of each line plus leading, but not including the space below the last line' do
text = "Oh hai text rect.\nOh hai text rect."
leading = 12
options = { document: pdf, leading: leading }
text_box = described_class.new(text, options)
text_box.render
expect(text_box.height).to be_within(0.001).of(((pdf.font.height + leading) * 2) - pdf.font.line_gap - leading)
end
end
context 'with :draw_text_callback' do
it 'hits the callback whenever text is drawn' do
draw_block = instance_spy(Proc, 'Draw block')
pdf.text_box(
'this text is long enough to span two lines',
width: 150,
draw_text_callback: draw_block,
)
expect(draw_block).to have_received(:call).with('this text is long enough to', instance_of(Hash))
expect(draw_block).to have_received(:call).with('span two lines', instance_of(Hash))
end
it 'hits the callback once per fragment for :inline_format' do
draw_block = instance_spy(Proc, 'Draw block')
pdf.text_box(
'this text has fancy formatting',
inline_format: true,
width: 500,
draw_text_callback: draw_block,
)
expect(draw_block).to have_received(:call).with('this text has ', instance_of(Hash))
expect(draw_block).to have_received(:call).with('fancy', instance_of(Hash))
expect(draw_block).to have_received(:call).with(' formatting', instance_of(Hash))
end
it 'does not call #draw_text!' do
allow(pdf).to receive(:draw_text!)
pdf.text_box(
'some text',
width: 500,
draw_text_callback: ->(_, _) {},
)
expect(pdf).to_not have_received(:draw_text!)
end
end
describe '#valid_options' do
it 'returns an array' do
text_box = described_class.new('', document: pdf)
expect(text_box.valid_options).to be_a(Array)
end
end
describe '#render' do
it 'does not fail if height is smaller than 1 line' do
text = 'Oh hai text rect. ' * 10
options = {
height: pdf.font.height * 0.5,
document: pdf,
}
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to eq('')
end
it 'draws content to the page' do
text = 'Oh hai text rect. ' * 10
options = { document: pdf }
text_box = described_class.new(text, options)
text_box.render
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
it 'does not draw a transformation matrix' do
text = 'Oh hai text rect. ' * 10
options = { document: pdf }
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
expect(matrices.matrices.length).to eq(0)
end
end
describe '#render(:single_line => true)' do
it 'draws only one line to the page' do
text = 'Oh hai text rect. ' * 10
options = {
document: pdf,
single_line: true,
}
text_box = described_class.new(text, options)
text_box.render
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.length).to eq(1)
end
end
describe '#render(:dry_run => true)' do
it 'does not draw any content to the page' do
text = 'Oh hai text rect. ' * 10
options = { document: pdf }
text_box = described_class.new(text, options)
text_box.render(dry_run: true)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to be_empty
end
it 'subsequent calls to render do not raise an ArgumentError exception' do
text = '™©'
options = { document: pdf }
text_box = described_class.new(text, options)
text_box.render(dry_run: true)
expect {
text_box.render
}.to_not raise_exception
end
end
describe '#render(:valign => :bottom)' do
it '#at should be the same from one dry run to the next' do
text = 'this is center text ' * 12
options = {
width: 162,
valign: :bottom,
document: pdf,
}
text_box = described_class.new(text, options)
text_box.render(dry_run: true)
original_at = text_box.at.dup
text_box.render(dry_run: true)
expect(text_box.at).to eq(original_at)
end
end
describe '#render(:valign => :center)' do
it '#at should be the same from one dry run to the next' do
text = 'this is center text ' * 12
options = {
width: 162,
valign: :center,
document: pdf,
}
text_box = described_class.new(text, options)
text_box.render(dry_run: true)
original_at = text_box.at.dup
text_box.render(dry_run: true)
expect(text_box.at).to eq(original_at)
end
end
describe '#render with :rotate option of 30)' do
let(:cos) { Math.cos(30 * Math::PI / 180) }
let(:sin) { Math.sin(30 * Math::PI / 180) }
let(:text) { 'Oh hai text rect. ' * 10 }
let(:options) do
{
document: pdf,
rotate: 30,
at: [300, 70],
width: 100,
height: 50,
}
end
context 'with :rotate_around option of :center' do
it 'draws content to the page rotated about the center of the text' do
options[:rotate_around] = :center
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
x = 350
y = 45
x_prime = (x * cos) - (y * sin)
y_prime = (x * sin) + (y * cos)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x - x_prime),
reduce_precision(y - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
end
context 'with :rotate_around option of :upper_left' do
it 'draws content to the page rotated about the upper left corner of the text' do
options[:rotate_around] = :upper_left
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
x_prime = (300 * cos) - (70 * sin)
y_prime = (300 * sin) + (70 * cos)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(300 - x_prime),
reduce_precision(70 - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
end
context 'with default :rotate_around' do
it 'draws content to the page rotated about the upper left corner of the text' do
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
x_prime = (300 * cos) - (70 * sin)
y_prime = (300 * sin) + (70 * cos)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(300 - x_prime),
reduce_precision(70 - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
end
context 'with :rotate_around option of :upper_right' do
it 'draws content to the page rotated about the upper right corner of the text' do
options[:rotate_around] = :upper_right
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
x = 400
y = 70
x_prime = (x * cos) - (y * sin)
y_prime = (x * sin) + (y * cos)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x - x_prime),
reduce_precision(y - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
end
context 'with :rotate_around option of :lower_right' do
it 'draws content to the page rotated about the lower right corner of the text' do
options[:rotate_around] = :lower_right
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
x = 400
y = 20
x_prime = (x * cos) - (y * sin)
y_prime = (x * sin) + (y * cos)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x - x_prime),
reduce_precision(y - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
end
context 'with :rotate_around option of :lower_left' do
it 'draws content to the page rotated about the lower left corner of the text' do
options[:rotate_around] = :lower_left
text_box = described_class.new(text, options)
text_box.render
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
x = 300
y = 20
x_prime = (x * cos) - (y * sin)
y_prime = (x * sin) + (y * cos)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x - x_prime),
reduce_precision(y - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings).to_not be_empty
end
end
end
describe 'default height' do
it 'is the height from the bottom bound to document.y' do
target_height = pdf.y - pdf.bounds.bottom
text = "Oh hai\n" * 60
text_box = described_class.new(text, document: pdf)
text_box.render
expect(text_box.height).to be_within(pdf.font.height).of(target_height)
end
it 'uses the margin-box bottom if only in a stretchy bbox' do
pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width) do
target_height = pdf.y - pdf.bounds.bottom
text = "Oh hai\n" * 60
text_box = described_class.new(text, document: pdf)
text_box.render
expect(text_box.height).to be_within(pdf.font.height).of(target_height)
end
end
it 'uses the parent-box bottom if in a stretchy bbox and overflow is :expand, even with an explicit height' do
pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width) do
target_height = pdf.y - pdf.bounds.bottom
text = "Oh hai\n" * 60
text_box = described_class.new(
text,
document: pdf,
height: 100,
overflow: :expand,
)
text_box.render
expect(text_box.height).to be_within(pdf.font.height).of(target_height)
end
end
it 'uses the innermost non-stretchy bbox, not the margin box' do
pdf.bounding_box(
[0, pdf.cursor],
width: pdf.bounds.width,
height: 200,
) do
pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width) do
text = "Oh hai\n" * 60
text_box = described_class.new(text, document: pdf)
text_box.render
expect(text_box.height).to be_within(pdf.font.height).of(200)
end
end
end
end
describe 'default at' do
it 'is the left corner of the bounds, and the current document.y' do
target_at = [pdf.bounds.left, pdf.y]
text = 'Oh hai text rect. ' * 100
options = { document: pdf }
text_box = described_class.new(text, options)
text_box.render
expect(text_box.at).to eq(target_at)
end
end
context 'with text than can fit in the box' do
let(:text) { 'Oh hai text rect. ' * 10 }
let(:options) do
{
width: 162.0,
height: 162.0,
document: pdf,
}
end
it 'printed text should match requested text, except that preceding and ' \
'trailing white space will be stripped from each line, and newlines ' \
'may be inserted' do
text_box = described_class.new(" #{text}", options)
text_box.render
expect(text_box.text.tr("\n", ' ')).to eq(text.strip)
end
it 'render returns an empty string because no text remains unprinted' do
text_box = described_class.new(text, options)
expect(text_box.render).to eq('')
end
it 'is truncated when the leading is set high enough to prevent all the lines from being printed' do
options[:leading] = 40
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text.tr("\n", ' ')).to_not eq(text.strip)
end
end
context 'with text that fits exactly in the box' do
let(:lines) { 3 }
let(:interlines) { lines - 1 }
let(:text) { (1..lines).to_a.join("\n") }
let(:options) do
{
width: 162.0,
height: pdf.font.ascender + (pdf.font.height * interlines) +
pdf.font.descender,
document: pdf,
}
end
it 'has the expected height' do
expected_height = options.delete(:height)
text_box = described_class.new(text, options)
text_box.render
expect(text_box.height).to be_within(0.0001).of(expected_height)
end
it 'prints everything' do
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to eq(text)
end
describe 'with leading' do
before do
options[:leading] = 15
end
it 'does not overflow when enough height is added' do
options[:height] += options[:leading] * interlines
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to eq(text)
end
it 'overflows when insufficient height is added' do
options[:height] += (options[:leading] * interlines) - 1
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to_not eq(text)
end
end
context 'with negative leading' do
before do
options[:leading] = -4
end
it 'does not overflow when enough height is removed' do
options[:height] += options[:leading] * interlines
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to eq(text)
end
it 'overflows when too much height is removed' do
options[:height] += (options[:leading] * interlines) - 1
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text).to_not eq(text)
end
end
end
context 'when printing UTF-8 string with higher bit characters' do
let(:text) { '©' }
let(:text_box) do
# not enough height to print any text, so we can directly compare against
# the input string
bounding_height = 1.0
options = {
height: bounding_height,
document: pdf,
}
described_class.new(text, options)
end
before do
file = "#{Prawn::DATADIR}/fonts/Panic+Sans.dfont"
pdf.font_families['Panic Sans'] = {
normal: { file: file, font: 'PanicSans' },
italic: { file: file, font: 'PanicSans-Italic' },
bold: { file: file, font: 'PanicSans-Bold' },
bold_italic: { file: file, font: 'PanicSans-BoldItalic' },
}
end
describe 'when using a TTF font' do
it 'unprinted text should be in UTF-8 encoding' do
pdf.font('Panic Sans')
remaining_text = text_box.render
expect(remaining_text).to eq(text)
end
end
describe 'when using an AFM font' do
it 'unprinted text should be in UTF-8 encoding' do
remaining_text = text_box.render
expect(remaining_text).to eq(text)
end
end
end
context 'with more text than can fit in the box' do
let(:text) { 'Oh hai text rect. ' * 30 }
let(:bounding_height) { 162.0 }
let(:options) do
{
width: 162.0,
height: bounding_height,
document: pdf,
}
end
context 'when truncated overflow' do
let(:text_box) do
described_class.new(text, options.merge(overflow: :truncate))
end
it 'is truncated' do
text_box.render
expect(text_box.text.tr("\n", ' ')).to_not eq(text.strip)
end
it 'render does not return an empty string because some text remains unprinted' do
expect(text_box.render).to_not be_empty
end
it '#height should be no taller than the specified height' do
text_box.render
expect(text_box.height).to be <= bounding_height
end
it '#height should be within one font height of the specified height' do
text_box.render
expect(bounding_height).to be_within(pdf.font.height).of(text_box.height)
end
context 'with :rotate option' do
it 'unrendered text should be the same as when not rotated' do
remaining_text = text_box.render
rotate = 30
options[:document] = pdf
options[:rotate] = rotate
options[:at] = [300, 70]
rotated_text_box = described_class.new(text, options)
expect(rotated_text_box.render).to eq(remaining_text)
end
end
end
context 'when truncated with text and size taken from the manual' do
it 'returns the right text' do
text = 'This is the beginning of the text. It will be cut somewhere ' \
'and the rest of the text will proceed to be rendered this time by ' \
'calling another method.' + (' . ' * 50)
options[:width] = 300
options[:height] = 50
options[:size] = 18
text_box = described_class.new(text, options)
remaining_text = text_box.render
expect(remaining_text).to eq(
'text will proceed to be rendered this time by calling another ' \
'method. . . . . . . . . . . . . . . . . . . . ' \
'. . . . . . . . . . . . . . . . . . . . . . ' \
'. . . . . . . . . ',
)
end
end
context 'when expand overflow' do
let(:text_box) do
described_class.new(text, options.merge(overflow: :expand))
end
it 'height expands to encompass all the text (but not exceed the height of the page)' do
text_box.render
expect(text_box.height).to be > bounding_height
end
it 'displays the entire string (as long as there was space remaining on the page to print all the text)' do
text_box.render
expect(text_box.text.tr("\n", ' ')).to eq(text.strip)
end
it 'render returns an empty string because no text remains unprinted ' \
'(as long as there was space remaining on the page to print all ' \
'the text)' do
expect(text_box.render).to eq('')
end
end
context 'when shrink_to_fit overflow' do
let(:text_box) do
described_class.new(
text,
options.merge(
overflow: :shrink_to_fit,
min_font_size: 2,
),
)
end
it 'displays the entire text' do
text_box.render
expect(text_box.text.tr("\n", ' ')).to eq(text.strip)
end
it 'render returns an empty string because no text remains unprinted' do
expect(text_box.render).to eq('')
end
it 'does not drop below the minimum font size' do
options[:overflow] = :shrink_to_fit
options[:min_font_size] = 10.1
text_box = described_class.new(text, options)
text_box.render
actual_text = PDF::Inspector::Text.analyze(pdf.render)
expect(actual_text.font_settings[0][:size]).to eq(10.1)
end
end
end
context 'with enough space to fit the text but using the shrink_to_fit overflow' do
it 'does not shrink the text when there is no need to' do
bounding_height = 162.0
options = {
width: 162.0,
height: bounding_height,
overflow: :shrink_to_fit,
min_font_size: 5,
document: pdf,
}
text_box = described_class.new("hello\nworld", options)
text_box.render
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings[0][:size]).to eq(12)
end
end
context 'with a solid block of Chinese characters' do
it 'printed text should match requested text, except for newlines' do
text = '写中国字' * 10
options = {
width: 162.0,
height: 162.0,
document: pdf,
overflow: :truncate,
}
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
text_box = described_class.new(text, options)
text_box.render
expect(text_box.text.delete("\n")).to eq(text)
end
end
describe 'drawing bounding boxes' do
it 'restores the margin box when bounding box exits' do
margin_box = pdf.bounds
pdf.text_box('Oh hai text box. ' * 11, height: pdf.font.height * 10)
expect(pdf.bounds).to eq(margin_box)
end
end
describe '#render with :character_spacing option' do
it 'draws the character spacing to the document' do
string = 'hello world'
options = { document: pdf, character_spacing: 10 }
text_box = described_class.new(string, options)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.character_spacing[0]).to eq(10)
end
it 'takes character spacing into account when wrapping' do
pdf.font('Courier')
text_box = described_class.new(
'hello world',
width: 100,
overflow: :expand,
character_spacing: 10,
document: pdf,
)
text_box.render
expect(text_box.text).to eq("hello\nworld")
end
end
describe 'wrapping' do
it 'wraps text' do
text = 'Please wrap this text about HERE. More text that should be wrapped'
expect = "Please wrap this text about\nHERE. More text that should be\nwrapped"
pdf.font('Courier')
text_box = described_class.new(
text,
width: 220,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
# white space was being stripped after the entire line was generated,
# meaning that leading white space characters reduced the amount of space on
# the line for other characters, so wrapping "hello hello" resulted in
# "hello\n\nhello", rather than "hello\nhello"
#
it 'white space at beginning of line should not be taken into account hen computing line width' do
text = 'hello hello'
expect = "hello\nhello"
pdf.font('Courier')
text_box = described_class.new(
text,
width: 40,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
it 'respects end of line when wrapping text' do
text = "Please wrap only before\nTHIS word. Don't wrap this"
expect = text
pdf.font('Courier')
text_box = described_class.new(
text,
width: 220,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
it 'respects multiple newlines when wrapping text' do
text = "Please wrap only before THIS\n\nword. Don't wrap this"
expect = "Please wrap only before\nTHIS\n\nword. Don't wrap this"
pdf.font('Courier')
text_box = described_class.new(
text,
width: 200,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
it 'respects multiple newlines when wrapping text when those newlines coincide with a line break' do
text = "Please wrap only before\n\nTHIS word. Don't wrap this"
expect = text
pdf.font('Courier')
text_box = described_class.new(
text,
width: 220,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
it 'respects initial newlines' do
text = "\nThis should be on line 2"
expect = text
pdf.font('Courier')
text_box = described_class.new(
text,
width: 220,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
it 'wraps lines comprised of a single word of the bounds when wrapping text' do
text = 'You_can_wrap_this_text_HERE'
expect = "You_can_wrap_this_text_HE\nRE"
pdf.font('Courier')
text_box = described_class.new(
text,
width: 180,
overflow: :expand,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
it 'wraps lines comprised of a single non-alpha word of the bounds when wrapping text' do
text = '©' * 30
pdf.font('Courier')
text_box = described_class.new(
text,
width: 180,
overflow: :expand,
document: pdf,
)
text_box.render
expected = "#{'©' * 25}\n#{'©' * 5}"
expected = pdf.font.normalize_encoding(expected)
expected = expected.force_encoding(Encoding::UTF_8)
expect(text_box.text).to eq(expected)
end
it 'wraps non-unicode strings using single-byte word-wrapping' do
text = 'continúa esforzandote ' * 5
text_box = described_class.new(
text,
width: 180,
document: pdf,
)
text_box.render
results_with_accent = text_box.text
text = 'continua esforzandote ' * 5
text_box = described_class.new(
text,
width: 180,
document: pdf,
)
text_box.render
results_without_accent = text_box.text
expect(first_line(results_with_accent).length)
.to eq(first_line(results_without_accent).length)
end
it 'allows you to disable wrapping by char' do
text = 'You_cannot_wrap_this_text_at_all_because_we_are_disabling_wrapping_by_char_and_there_are_no_word_breaks'
pdf.font('Courier')
text_box = described_class.new(
text,
width: 180,
overflow: :shrink_to_fit,
disable_wrap_by_char: true,
document: pdf,
)
expect { text_box.render }.to raise_error(Prawn::Errors::CannotFit)
end
it 'retains full words with :shrink_to_fit if char wrapping is disabled' do
text = 'Wrapped_words'
expect = 'Wrapped_words'
pdf.font('Courier')
text_box = described_class.new(
text,
width: 50,
height: 50,
size: 50,
overflow: :shrink_to_fit,
disable_wrap_by_char: true,
document: pdf,
)
text_box.render
expect(text_box.text).to eq(expect)
end
end
describe 'Text::Box#render with :mode option' do
it 'alters the text rendering mode of the document' do
string = 'hello world'
options = { document: pdf, mode: :fill_stroke }
text_box = described_class.new(string, options)
text_box.render
contents = PDF::Inspector::Text.analyze(pdf.render)
expect(contents.text_rendering_mode).to eq([2, 0])
end
end
def reduce_precision(float)
float.round(5)
end
def first_line(str)
str.lines.first
end
end
ruby-prawn-2.5.0.orig/spec/prawn/stamp_spec.rb 0000664 0000000 0000000 00000013071 14571572164 020045 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Stamp do
describe 'create_stamp before any page is added' do
let(:pdf) { Prawn::Document.new(skip_page_creation: true) }
it 'works with the font class' do
# If anything goes wrong, Prawn::Errors::NotOnPage will be raised
expect {
pdf.create_stamp('my_stamp') do
pdf.font.height
end
}.to_not raise_error
end
it 'works with setting color' do
# If anything goes wrong, Prawn::Errors::NotOnPage will be raised
expect {
pdf.create_stamp('my_stamp') do
pdf.fill_color = 'ff0000'
end
}.to_not raise_error
end
end
describe '#stamp_at' do
let(:pdf) { create_pdf }
it 'draws a stamp' do
pdf.create_stamp('MyStamp')
pdf.stamp_at('MyStamp', [100, 200])
# I had modified PDF::Inspector::XObject to receive the
# invoke_xobject message and count the number of times it was
# called, but it was only called once, so I reverted checking the
# output with a regular expression
expect(pdf.render).to match(%r{/Stamp1 Do.*?}m)
end
end
describe 'Document with a stamp' do
let(:pdf) { create_pdf }
it 'raises NameTaken error when attempt to create stamp with same name as an existing stamp' do
pdf.create_stamp('MyStamp')
expect { pdf.create_stamp('MyStamp') }.to raise_error(Prawn::Errors::NameTaken)
end
it 'raises InvalidName error when attempt to create stamp with a blank name' do
expect { pdf.create_stamp('') }.to raise_error(Prawn::Errors::InvalidName)
end
it 'a new XObject should be defined for each stamp created' do
pdf.create_stamp('MyStamp')
pdf.create_stamp('AnotherStamp')
pdf.stamp('MyStamp')
pdf.stamp('AnotherStamp')
inspector = PDF::Inspector::XObject.analyze(pdf.render)
xobjects = inspector.page_xobjects.last
expect(xobjects.length).to eq(2)
end
it 'calling stamp with a name that does not match an existing stamp should raise_error UndefinedObjectName' do
pdf.create_stamp('MyStamp')
expect { pdf.stamp('OtherStamp') }.to raise_error(Prawn::Errors::UndefinedObjectName)
end
it 'stamp should be drawn into the document each time stamp is called' do
pdf.create_stamp('MyStamp')
pdf.stamp('MyStamp')
pdf.stamp('MyStamp')
pdf.stamp('MyStamp')
# I had modified PDF::Inspector::XObject to receive the
# invoke_xobject message and count the number of times it was
# called, but it was only called once, so I reverted checking the
# output with a regular expression
expect(pdf.render).to match(%r{(/Stamp1 Do.*?){3}}m)
end
it 'stamp should render clickable links' do
pdf.create_stamp('bar') do
pdf.text('Prawn GitHub', inline_format: true)
end
pdf.stamp('bar')
output = pdf.render
objects = output.split('endobj')
objects.each do |obj|
next unless %r{/Type /Page$}.match?(obj)
# The page object must contain the annotation reference
# to render a clickable link
expect(obj).to match(%r{/Annots \[\d+ \d+ R\]($|/\w+|>)})
end
end
it 'resources added during stamp creation should be added to the stamp XObject, not the page' do
pdf.create_stamp('MyStamp') do
pdf.transparent(0.5) { pdf.circle([100, 100], 10) }
end
pdf.stamp('MyStamp')
# Inspector::XObject does not give information about resources, so
# resorting to string matching
output = pdf.render
objects = output.split('endobj')
objects.each do |object|
case object
when %r{/Type /Page$}
expect(object).to_not match(%r{/ExtGState})
when %r{/Type /XObject$}
expect(object).to match(%r{/ExtGState})
end
end
end
it 'stamp stream should be wrapped in a graphic state' do
pdf.create_stamp('MyStamp') do
pdf.text("This should have a 'q' before it and a 'Q' after it")
end
pdf.stamp('MyStamp')
stamps = PDF::Inspector::XObject.analyze(pdf.render)
expect(stamps.xobject_streams[:Stamp1].data.chomp).to match(/q(.|\s)*Q\Z/)
end
it 'does not add to the page graphic state stack' do
expect(pdf.state.page.stack.stack.size).to eq(1)
pdf.create_stamp('MyStamp') do
pdf.save_graphics_state
pdf.save_graphics_state
pdf.save_graphics_state
pdf.text("This should have a 'q' before it and a 'Q' after it")
pdf.restore_graphics_state
end
expect(pdf.state.page.stack.stack.size).to eq(1)
end
it 'is able to change fill and stroke colors within the stamp stream' do
pdf.create_stamp('MyStamp') do
pdf.fill_color(100, 100, 20, 0)
pdf.stroke_color(100, 100, 20, 0)
end
pdf.stamp('MyStamp')
stamps = PDF::Inspector::XObject.analyze(pdf.render)
stamp_stream = stamps.xobject_streams[:Stamp1].data
expect(stamp_stream).to include("/DeviceCMYK cs\n1.0 1.0 0.2 0.0 scn")
expect(stamp_stream).to include("/DeviceCMYK CS\n1.0 1.0 0.2 0.0 SCN")
end
it 'saves the color space even when same as current page color space' do
pdf.stroke_color(100, 100, 20, 0)
pdf.create_stamp('MyStamp') do
pdf.stroke_color(100, 100, 20, 0)
end
pdf.stamp('MyStamp')
stamps = PDF::Inspector::XObject.analyze(pdf.render)
stamp_stream = stamps.xobject_streams[:Stamp1].data
expect(stamp_stream).to include("/DeviceCMYK CS\n1.0 1.0 0.2 0.0 SCN")
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/soft_mask_spec.rb 0000664 0000000 0000000 00000003573 14571572164 020715 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::SoftMask do
let(:pdf) { create_pdf }
def make_soft_mask
pdf.save_graphics_state do
pdf.soft_mask do
if block_given?
yield
else
pdf.fill_color('808080')
pdf.fill_rectangle([100, 100], 200, 200)
end
end
pdf.fill_color('000000')
pdf.fill_rectangle([0, 0], 200, 200)
end
end
it 'has PDF version at least 1.4' do
make_soft_mask
str = pdf.render
expect(str[0, 8]).to eq('%PDF-1.4')
end
it 'creates a new extended graphics state for each unique soft mask' do
make_soft_mask do
pdf.fill_color('808080')
pdf.fill_rectangle([100, 100], 200, 200)
end
make_soft_mask do
pdf.fill_color('808080')
pdf.fill_rectangle([10, 10], 200, 200)
end
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
expect(extgstates.length).to eq(2)
end
it 'a new extended graphics state contains soft mask with drawing instructions' do
make_soft_mask do
pdf.fill_color('808080')
pdf.fill_rectangle([100, 100], 200, 200)
end
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
expect(extgstate[:soft_mask][:G].data).to eq(
"q\n/DeviceRGB cs\n0.0 0.0 0.0 scn\n/DeviceRGB CS\n0.0 0.0 0.0 SCN\n" \
"1 w\n0 J\n0 j\n[] 0 d\n/DeviceRGB cs\n0.50196 0.50196 0.50196 scn\n" \
"100.0 -100.0 200.0 200.0 re\nf\nQ\n",
)
end
it 'does not create duplicate extended graphics states' do
make_soft_mask do
pdf.fill_color('808080')
pdf.fill_rectangle([100, 100], 200, 200)
end
make_soft_mask do
pdf.fill_color('808080')
pdf.fill_rectangle([100, 100], 200, 200)
end
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
expect(extgstates.length).to eq(1)
end
end
ruby-prawn-2.5.0.orig/spec/prawn/repeater_spec.rb 0000664 0000000 0000000 00000011660 14571572164 020532 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Repeater do
it 'creates a stamp and increments Prawn::Repeater.count on initialize' do
orig_count = described_class.count
doc = sample_document
allow(doc).to receive(:create_stamp).with("prawn_repeater(#{orig_count})")
repeater(doc, :all) { :do_nothing }
expect(doc).to have_received(:create_stamp)
.with("prawn_repeater(#{orig_count})")
expect(described_class.count).to eq(orig_count + 1)
end
it 'must provide an :all filter' do
doc = sample_document
r = repeater(doc, :all) { :do_nothing }
expect((1..doc.page_count).all? { |i| r.match?(i) }).to be true
end
it 'must provide an :odd filter' do
doc = sample_document
r = repeater(doc, :odd) { :do_nothing }
odd, even = (1..doc.page_count).partition(&:odd?)
expect(odd.all? { |i| r.match?(i) }).to be true
expect(even.any? { |i| r.match?(i) }).to be false
end
it 'must be able to filter by an array of page numbers' do
doc = sample_document
r = repeater(doc, [1, 2, 7]) { :do_nothing }
expect((1..10).select { |i| r.match?(i) }).to eq([1, 2, 7]) # rubocop: disable Style/SelectByRegexp
end
it 'must be able to filter by a range of page numbers' do
doc = sample_document
r = repeater(doc, 2..4) { :do_nothing }
expect((1..10).select { |i| r.match?(i) }).to eq([2, 3, 4]) # rubocop: disable Style/SelectByRegexp
end
it 'must be able to filter by an arbitrary proc' do
doc = sample_document
r = repeater(doc, ->(x) { x == 1 || x % 3 == 0 })
expect((1..10).select { |i| r.match?(i) }).to eq([1, 3, 6, 9]) # rubocop: disable Style/SelectByRegexp
end
it 'must try to run a stamp if the page number matches' do
doc = sample_document
allow(doc).to receive(:stamp)
repeater(doc, :odd).run(3)
expect(doc).to have_received(:stamp)
end
it 'must not try to run a stamp unless the page number matches' do
doc = sample_document
allow(doc).to receive(:stamp)
repeater(doc, :odd).run(2)
expect(doc).to_not have_received(:stamp)
end
it 'must not try to run a stamp if dynamic is selected' do
doc = sample_document
allow(doc).to receive(:stamp)
(1..10).each { |p| repeater(doc, :all, true) { :do_nothing }.run(p) }
expect(doc).to_not have_received(:stamp)
end
it 'must try to run a block if the page number matches' do
doc = sample_document
allow(doc).to receive(:draw_text)
(1..10).each do |p|
repeater(doc, [1, 2], true) { doc.draw_text('foo') }.run(p)
end
expect(doc).to have_received(:draw_text).twice
end
it 'must not try to run a block unless the page number matches' do
doc = sample_document
allow(doc).to receive(:draw_text)
repeater(doc, :odd, true) { doc.draw_text('foo') }.run(2)
expect(doc).to_not have_received(:draw_text)
end
it 'must treat any block as a closure' do
doc = sample_document
page = 'Page' # ensure access to ivars
doc.repeat(:all, dynamic: true) do
doc.draw_text("#{page} #{doc.page_number}", at: [500, 0])
end
text = PDF::Inspector::Text.analyze(doc.render)
expect(text.strings).to eq((1..10).to_a.map { |p| "Page #{p}" })
end
it 'must treat any block as a closure (Document.new instance_eval form)' do
doc =
Prawn::Document.new(skip_page_creation: true) do
10.times { start_new_page }
page = 'Page'
repeat(:all, dynamic: true) do
# ensure self is accessible here
draw_text("#{page} #{page_number}", at: [500, 0])
end
end
text = PDF::Inspector::Text.analyze(doc.render)
expect(text.strings).to eq((1..10).to_a.map { |p| "Page #{p}" })
end
def sample_document
doc = Prawn::Document.new(skip_page_creation: true)
10.times { |_e| doc.start_new_page }
doc
end
def repeater(...)
Prawn::Repeater.new(...)
end
describe 'graphic state' do
let(:pdf) { create_pdf }
it 'does not alter the graphic state stack color space' do
starting_color_space = pdf.state.page.graphic_state.color_space.dup
pdf.repeat(:all) do
pdf.text('Testing', size: 24, style: :bold)
end
expect(pdf.state.page.graphic_state.color_space)
.to eq(starting_color_space)
end
context 'with dynamic repeaters' do
it 'preserves the graphic state at creation time' do
pdf.repeat(:all, dynamic: true) do
pdf.text("fill_color: #{pdf.graphic_state.fill_color}")
pdf.text("cap_style: #{pdf.graphic_state.cap_style}")
end
pdf.fill_color('666666')
pdf.cap_style(:round)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.include?('fill_color: 666666')).to be(false)
expect(text.strings.include?('fill_color: 000000')).to be(true)
expect(text.strings.include?('cap_style: round')).to be(false)
expect(text.strings.include?('cap_style: butt')).to be(true)
end
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/outline_spec.rb 0000664 0000000 0000000 00000036722 14571572164 020410 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Outline do
let(:pdf) do
Prawn::Document.new do
text 'Page 1. This is the first Chapter. '
start_new_page
text 'Page 2. More in the first Chapter. '
start_new_page
outline.define do
section 'Chapter 1', destination: 1, closed: true do
page destination: 1, title: 'Page 1'
page destination: 2, title: 'Page 2'
end
end
end
end
let(:hash) do
output = StringIO.new(pdf.render, 'r+')
PDF::Reader::ObjectHash.new(output)
end
let(:outline_root) do
hash.values.find do |obj|
obj.is_a?(Hash) && obj[:Type] == :Outlines
end
end
describe 'outline encoding' do
it 'stores all outline titles as UTF-16' do
hash.each_value do |obj|
next unless obj.is_a?(Hash) && obj[:Title]
title = obj[:Title].dup
title.force_encoding(Encoding::UTF_16LE)
expect(title.valid_encoding?).to be(true)
end
end
end
describe '#generate_outline' do
it 'creates a root outline dictionary item' do
expect(outline_root).to_not be_nil
end
it 'sets the first and last top items of the root outline dictionary item' do
section1 = find_by_title('Chapter 1')
expect(referenced_object(outline_root[:First])).to eq(section1)
expect(referenced_object(outline_root[:Last])).to eq(section1)
end
describe '#create_outline_item' do
it 'creates outline items for each section and page' do
section1 = find_by_title('Chapter 1')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(section1).to_not be_nil
expect(page1).to_not be_nil
expect(page2).to_not be_nil
end
end
describe '#set_relations, #set_variables_for_block, and #reset_parent' do
it 'links sibling items' do
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(referenced_object(page1[:Next])).to eq(page2)
expect(referenced_object(page2[:Prev])).to eq(page1)
end
it 'links child items to parent item' do
section1 = find_by_title('Chapter 1')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(referenced_object(page1[:Parent])).to eq(section1)
expect(referenced_object(page2[:Parent])).to eq(section1)
end
it 'sets the first and last child items for parent item' do
section1 = find_by_title('Chapter 1')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(referenced_object(section1[:First])).to eq(page1)
expect(referenced_object(section1[:Last])).to eq(page2)
end
end
describe '#increase_count' do
it 'adds the count of all descendant items' do
section1 = find_by_title('Chapter 1')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(outline_root[:Count]).to eq(3)
expect(section1[:Count].abs).to eq(2)
expect(page1[:Count]).to eq(0)
expect(page2[:Count]).to eq(0)
end
end
describe 'closed option' do
it "sets the item's integer count to negative" do
section1 = find_by_title('Chapter 1')
expect(section1[:Count]).to eq(-2)
end
end
end
describe 'adding a custom destination' do
before do
pdf.start_new_page
pdf.text('Page 3 with a destination')
pdf.add_dest('customdest', pdf.dest_xyz(200, 200))
destination = pdf.dest_xyz(200, 200)
pdf.outline.update do
page destination: destination, title: 'Custom Destination'
end
end
it 'creates an outline item' do
custom_dest = find_by_title('Custom Destination')
expect(custom_dest).to_not be_nil
end
it 'references the custom destination' do
custom_dest = find_by_title('Custom Destination')
last_page =
hash.values
.find { |obj| obj.is_a?(Hash) && obj[:Type] == :Pages }[:Kids]
.last
expect(referenced_object(custom_dest[:Dest].first)).to eq(referenced_object(last_page))
end
end
describe 'addding a section later with outline#section' do
before do
pdf.start_new_page
pdf.text('Page 3. An added section ')
pdf.outline.update do
section 'Added Section', destination: 3 do
page destination: 3, title: 'Page 3'
end
end
end
it 'adds new outline items to document' do
section2 = find_by_title('Added Section')
page3 = find_by_title('Page 3')
expect(section2).to_not be_nil
expect(page3).to_not be_nil
end
it 'resets the last items for root outline dictionary' do
section1 = find_by_title('Chapter 1')
section2 = find_by_title('Added Section')
expect(referenced_object(outline_root[:First])).to eq(section1)
expect(referenced_object(outline_root[:Last])).to eq(section2)
end
it 'resets the next relation for the previous last top level item' do
section1 = find_by_title('Chapter 1')
section2 = find_by_title('Added Section')
expect(referenced_object(section1[:Next])).to eq(section2)
end
it 'sets the previous relation of the addded to section' do
section1 = find_by_title('Chapter 1')
section2 = find_by_title('Added Section')
expect(referenced_object(section2[:Prev])).to eq(section1)
end
it 'increases the count of root outline dictionary' do
expect(outline_root[:Count]).to eq(5)
end
end
describe '#outline.add_subsection_to' do
context 'when positioned last' do
before do
pdf.start_new_page
pdf.text('Page 3. An added subsection ')
pdf.outline.update do
add_subsection_to 'Chapter 1' do
section 'Added SubSection', destination: 3 do
page destination: 3, title: 'Added Page 3'
end
end
end
end
it 'adds new outline items to document' do
subsection = find_by_title('Added SubSection')
added_page = find_by_title('Added Page 3')
expect(subsection).to_not be_nil
expect(added_page).to_not be_nil
end
it 'resets the last item for parent item dictionary' do
section1 = find_by_title('Chapter 1')
page1 = find_by_title('Page 1')
subsection = find_by_title('Added SubSection')
expect(referenced_object(section1[:First])).to eq(page1)
expect(referenced_object(section1[:Last])).to eq(subsection)
end
it "sets the prev relation for the new subsection to its parent's old last item" do
subsection = find_by_title('Added SubSection')
page2 = find_by_title('Page 2')
expect(referenced_object(subsection[:Prev])).to eq(page2)
end
it "the subsection should become the next relation for its parent's old last item" do
subsection = find_by_title('Added SubSection')
page2 = find_by_title('Page 2')
expect(referenced_object(page2[:Next])).to eq(subsection)
end
it 'sets the first relation for the new subsection' do
subsection = find_by_title('Added SubSection')
added_page = find_by_title('Added Page 3')
expect(referenced_object(subsection[:First])).to eq(added_page)
end
it 'sets the correct last relation of the added to section' do
subsection = find_by_title('Added SubSection')
added_page = find_by_title('Added Page 3')
expect(referenced_object(subsection[:Last])).to eq(added_page)
end
it 'increases the count of root outline dictionary' do
expect(outline_root[:Count]).to eq(5)
end
end
context 'when positioned first' do
before do
pdf.start_new_page
pdf.text('Page 3. An added subsection ')
pdf.outline.update do
add_subsection_to 'Chapter 1', :first do
section 'Added SubSection', destination: 3 do
page destination: 3, title: 'Added Page 3'
end
end
end
end
it 'adds new outline items to document' do
subsection = find_by_title('Added SubSection')
added_page = find_by_title('Added Page 3')
expect(subsection).to_not be_nil
expect(added_page).to_not be_nil
end
it 'resets the first item for parent item dictionary' do
section1 = find_by_title('Chapter 1')
subsection = find_by_title('Added SubSection')
page2 = find_by_title('Page 2')
expect(referenced_object(section1[:First])).to eq(subsection)
expect(referenced_object(section1[:Last])).to eq(page2)
end
it "sets the next relation for the new subsection to its parent's old first item" do
subsection = find_by_title('Added SubSection')
page1 = find_by_title('Page 1')
expect(referenced_object(subsection[:Next])).to eq(page1)
end
it "the subsection should become the prev relation for its parent's old first item" do
subsection = find_by_title('Added SubSection')
page1 = find_by_title('Page 1')
expect(referenced_object(page1[:Prev])).to eq(subsection)
end
it 'sets the first relation for the new subsection' do
subsection = find_by_title('Added SubSection')
added_page = find_by_title('Added Page 3')
expect(referenced_object(subsection[:First])).to eq(added_page)
end
it 'sets the correct last relation of the added to section' do
subsection = find_by_title('Added SubSection')
added_page = find_by_title('Added Page 3')
expect(referenced_object(subsection[:Last])).to eq(added_page)
end
it 'increases the count of root outline dictionary' do
expect(outline_root[:Count]).to eq(5)
end
end
it 'requires an existing title' do
expect {
pdf.go_to_page(1)
pdf.start_new_page
pdf.text('Inserted Page')
pdf.outline.update do
add_subsection_to('Wrong page') do
page(page_number, title: 'Inserted Page')
end
end
render_and_find_objects
}.to raise_error(Prawn::Errors::UnknownOutlineTitle)
end
end
describe '#outline.insert_section_after' do
describe 'inserting in the middle of another section' do
before do
pdf.go_to_page(1)
pdf.start_new_page
pdf.text('Inserted Page')
pdf.outline.update do
insert_section_after 'Page 1' do
page destination: page_number, title: 'Inserted Page'
end
end
end
it 'inserts new outline items to document' do
inserted_page = find_by_title('Inserted Page')
expect(inserted_page).to_not be_nil
end
it 'adjusts the count of all ancestors' do
section1 = find_by_title('Chapter 1')
expect(outline_root[:Count]).to eq(4)
expect(section1[:Count].abs).to eq(3)
end
describe '#adjust_relations' do
it 'resets the sibling relations of adjoining items to inserted item' do
inserted_page = find_by_title('Inserted Page')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(referenced_object(page1[:Next])).to eq(inserted_page)
expect(referenced_object(page2[:Prev])).to eq(inserted_page)
end
it 'sets the sibling relation of added item to adjoining items' do
inserted_page = find_by_title('Inserted Page')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(referenced_object(inserted_page[:Next])).to eq(page2)
expect(referenced_object(inserted_page[:Prev])).to eq(page1)
end
it 'does not affect the first and last relations of parent item' do
section1 = find_by_title('Chapter 1')
page1 = find_by_title('Page 1')
page2 = find_by_title('Page 2')
expect(referenced_object(section1[:First])).to eq(page1)
expect(referenced_object(section1[:Last])).to eq(page2)
end
end
context 'when adding another section afterwards' do
it 'has reset the root position so that a new section is added at the end of root sections' do
pdf.start_new_page
pdf.text('Another Inserted Page')
pdf.outline.update do
section 'Added Section' do
page destination: page_number, title: 'Inserted Page'
end
end
section1 = find_by_title('Chapter 1')
section2 = find_by_title('Added Section')
expect(referenced_object(outline_root[:Last])).to eq(section2)
expect(referenced_object(section1[:Next])).to eq(section2)
end
end
end
describe 'inserting at the end of another section' do
before do
pdf.go_to_page(2)
pdf.start_new_page
pdf.text('Inserted Page')
pdf.outline.update do
insert_section_after 'Page 2' do
page destination: page_number, title: 'Inserted Page'
end
end
end
describe '#adjust_relations' do
it 'resets the sibling relations of adjoining item to inserted item' do
page2 = find_by_title('Page 2')
inserted_page = find_by_title('Inserted Page')
expect(referenced_object(page2[:Next])).to eq(inserted_page)
end
it 'sets the sibling relation of added item to adjoining items' do
page2 = find_by_title('Page 2')
inserted_page = find_by_title('Inserted Page')
expect(referenced_object(inserted_page[:Next])).to be_nil
expect(referenced_object(inserted_page[:Prev])).to eq(page2)
end
it 'adjusts the last relation of parent item' do
section1 = find_by_title('Chapter 1')
inserted_page = find_by_title('Inserted Page')
expect(referenced_object(section1[:Last])).to eq(inserted_page)
end
end
end
it 'requires an existing title' do
expect {
pdf.go_to_page(1)
pdf.start_new_page
pdf.text('Inserted Page')
pdf.outline.update do
insert_section_after('Wrong page') do
page(destination: page_number, title: 'Inserted Page')
end
end
}.to raise_error(Prawn::Errors::UnknownOutlineTitle)
end
end
describe '#page' do
it 'requires a title option to be set' do
expect {
Prawn::Document.new do
text('Page 1. This is the first Chapter. ')
outline.define do
page(destination: 1, title: nil)
end
end
}.to raise_error(Prawn::Errors::RequiredOption)
end
end
describe 'foreign character encoding' do
let(:hash) do
pdf =
Prawn::Document.new do
outline.define do
section('La pomme croquée', destination: 1, closed: true)
end
end
PDF::Reader::ObjectHash.new(StringIO.new(pdf.render, 'r+'))
end
it 'handles other encodings for the title' do
object = find_by_title('La pomme croquée')
expect(object).to_not be_nil
end
end
# Outline titles are stored as UTF-16. This method accepts a UTF-8 outline
# title and returns the PDF Object that contains an outline with that name
def find_by_title(title)
hash.values.find do |obj|
next unless obj.is_a?(Hash) && obj[:Title]
title_codepoints = obj[:Title].unpack('n*')
title_codepoints.shift
utf8_title = title_codepoints.pack('U*')
utf8_title == title ? obj : nil
end
end
def referenced_object(reference)
hash[reference]
end
end
ruby-prawn-2.5.0.orig/spec/prawn/measurements_extensions_spec.rb 0000664 0000000 0000000 00000001273 14571572164 023711 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'prawn/measurement_extensions'
describe Prawn::Measurements do
describe 'Numeric extensions' do
it 'converts units to PostScriptPoints' do
expect(1.mm).to be_within(0.000000001).of(2.834645669)
expect(1.mm).to eq(72 / 25.4)
expect(2.mm).to eq(2 * 72 / 25.4)
expect(3.mm).to eq(3 * 72 / 25.4)
expect(-3.mm).to eq(-3 * 72 / 25.4)
expect(1.cm).to eq(10 * 72 / 25.4)
expect(1.dm).to eq(100 * 72 / 25.4)
expect(1.m).to eq(1000 * 72 / 25.4)
expect(1.in).to eq(72)
expect(1.ft).to eq(72 * 12)
expect(1.yd).to eq(72 * 12 * 3)
expect(1.pt).to eq(1)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/images_spec.rb 0000664 0000000 0000000 00000016105 14571572164 020167 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'set'
require 'pathname'
describe Prawn::Images do
let(:pdf) { create_pdf }
let(:filename) { "#{Prawn::DATADIR}/images/pigs.jpg" }
it "onlies embed an image once, even if it's added multiple times" do
pdf.image(filename, at: [100, 100])
pdf.image(filename, at: [300, 300])
output = pdf.render
images = PDF::Inspector::XObject.analyze(output)
# there should be 2 images in the page resources
expect(images.page_xobjects.first.size).to eq(2)
# but only 1 image xobject
expect(output.scan(%r{/Type /XObject}).size).to eq(1)
end
it 'returns the image info object' do
info = pdf.image(filename)
expect(info).to be_a(Prawn::Images::JPG)
expect(info.height).to eq(453)
end
it 'accepts IO objects' do
info =
File.open(filename, 'rb') { |file| pdf.image(file) }
expect(info.height).to eq(453)
end
it 'rewinds IO objects to be able to embed them multiply' do
info =
File.open(filename, 'rb') { |file|
pdf.image(file)
pdf.image(file)
}
expect(info.height).to eq(453)
end
it 'does not close passed-in IO objects' do
File.open(filename, 'rb') do |file|
pdf.image(file)
expect(file).to_not be_closed
end
end
it 'accepts Pathname objects' do
info = pdf.image(Pathname.new(filename))
expect(info.height).to eq(453)
end
describe 'closes opened files again after getting Pathnames', issue: 975 do
describe 'spec with File message spy' do
let(:not_filename) { 'non-existent filename' }
let(:pathname_double) { instance_double(Pathname, file?: true) }
before do
file_content = File.new(filename, 'rb').read
allow(Pathname).to receive(:new).with(not_filename) { pathname_double }
allow(pathname_double).to receive(:binread) { file_content }
end
it 'uses only binread, which closes opened files' do
# this implicitly tests that a file handle is closed again
# because only stubbed binread can be called on not_filename
_info = pdf.image(not_filename)
expect(pathname_double).to have_received(:binread)
end
end
system_has_lsof = system('lsof -v > /dev/null 2>&1')
system_has_grep = system('grep --version > /dev/null 2>&1')
if system_has_lsof && system_has_grep
it 'closes opened files, spec with lsof' do
gc_was_disabled = GC.disable # GC of File would close the file
open_before = `lsof -c ruby | grep "#{filename}"`
_info = pdf.image(Pathname.new(filename))
open_after = `lsof -c ruby | grep "#{filename}"`
GC.enable unless gc_was_disabled
expect(open_after).to eq(open_before)
end
end
if RUBY_PLATFORM != 'java'
it 'closes opened files, spec with ObjectSpace' do
gc_was_disabled = GC.disable # GC of File would close the file
open_before = ObjectSpace.each_object(File).count { |f| !f.closed? }
_info = pdf.image(Pathname.new(filename))
open_after = ObjectSpace.each_object(File).count { |f| !f.closed? }
GC.enable unless gc_was_disabled
expect(open_after).to eq(open_before)
end
end
end
describe 'setting the length of the bytestream' do
it 'correctlies work with images from Pathname objects' do
pdf.image(Pathname.new(filename))
expect(pdf).to have_parseable_xobjects
end
it 'correctlies work with images from IO objects' do
pdf.image(File.open(filename, 'rb'))
expect(pdf).to have_parseable_xobjects
end
it 'correctlies work with images from IO objects not set to mode rb' do
pdf.image(File.open(filename, 'r'))
expect(pdf).to have_parseable_xobjects
end
end
it 'raise_errors an UnsupportedImageType if passed a BMP' do
filename = "#{Prawn::DATADIR}/images/tru256.bmp"
expect { pdf.image(filename, at: [100, 100]) }
.to raise_error(Prawn::Errors::UnsupportedImageType)
end
it 'raise_errors an UnsupportedImageType if passed an interlaced PNG' do
filename = "#{Prawn::DATADIR}/images/dice_interlaced.png"
expect { pdf.image(filename, at: [100, 100]) }
.to raise_error(Prawn::Errors::UnsupportedImageType)
end
it 'bumps PDF version to 1.5 or greater on embedding 16-bit PNGs' do
pdf.image("#{Prawn::DATADIR}/images/16bit.png")
expect(pdf.state.version).to be >= 1.5
end
it 'embeds 16-bit alpha channels for 16-bit PNGs' do
pdf.image("#{Prawn::DATADIR}/images/16bit.png")
output = pdf.render
expect(output).to match(%r{/BitsPerComponent 16})
expect(output).to_not match(%r{/BitsPerComponent 8})
end
it 'flows an image to a new page if it will not fit on a page' do
pdf.image(filename, fit: [600, 600])
pdf.image(filename, fit: [600, 600])
output = StringIO.new(pdf.render, 'r+')
hash = PDF::Reader::ObjectHash.new(output)
pages = hash.values.find { |obj| obj.is_a?(Hash) && obj[:Type] == :Pages }[:Kids]
expect(pages.size).to eq(2)
expect(hash[pages[0]][:Resources][:XObject].keys).to eq([:I1])
expect(hash[pages[1]][:Resources][:XObject].keys).to eq([:I2])
end
it 'does not flow an image to a new page if it will fit on one page' do
pdf.image(filename, fit: [400, 400])
pdf.image(filename, fit: [400, 400])
output = StringIO.new(pdf.render, 'r+')
hash = PDF::Reader::ObjectHash.new(output)
pages = hash.values.find { |obj| obj.is_a?(Hash) && obj[:Type] == :Pages }[:Kids]
expect(pages.size).to eq(1)
expect(Set.new(hash[pages[0]][:Resources][:XObject].keys)).to eq(Set.new(%i[I1 I2]))
end
it 'does not start a new page just for a stretchy bounding box' do
allow(pdf).to receive(:start_new_page)
pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width) do
pdf.image(filename)
end
expect(pdf).to_not have_received(:start_new_page)
end
describe ':fit option' do
it 'fits inside the defined constraints' do
info = pdf.image(filename, fit: [100, 400])
expect(info.scaled_width).to be <= 100
expect(info.scaled_height).to be <= 400
info = pdf.image(filename, fit: [400, 100])
expect(info.scaled_width).to be <= 400
expect(info.scaled_height).to be <= 100
info = pdf.image(filename, fit: [604, 453])
expect(info.scaled_width).to eq(604)
expect(info.scaled_height).to eq(453)
end
it 'moves text position' do
y = pdf.y
pdf.image(filename, fit: [100, 400])
expect(pdf.y).to be < y
end
end
describe ':at option' do
it 'does not move text position' do
y = pdf.y
pdf.image(filename, at: [100, 400])
expect(pdf.y).to eq(y)
end
end
describe ':width option without :height option' do
it 'scales the width and height' do
info = pdf.image(filename, width: 225)
expect(info.scaled_height).to eq(168.75)
expect(info.scaled_width).to eq(225.0)
end
end
describe ':height option without :width option' do
it 'scales the width and height' do
info = pdf.image(filename, height: 225)
expect(info.scaled_height).to eq(225.0)
expect(info.scaled_width).to eq(300.0)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/images/ 0000775 0000000 0000000 00000000000 14571572164 016625 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/images/png_spec.rb 0000664 0000000 0000000 00000023277 14571572164 020763 0 ustar root root # frozen_string_literal: true
# Spec'ing the PNG class. Not complete yet - still needs to check the
# contents of palette and transparency to ensure they're correct.
# Need to find files that have these sections first.
#
# see http://www.w3.org/TR/PNG/ for a detailed description of the PNG spec,
# particularly Table 11.1 for the different color types
require 'spec_helper'
describe Prawn::Images::PNG do
describe 'When making a pdf file with png images' do
image_dir = "#{Prawn::BASEDIR}/data/images"
images = [
['Type 0', "#{image_dir}/web-links.png"],
['Type 0 with transparency', "#{image_dir}/ruport_type0.png"],
['Type 2', "#{image_dir}/ruport.png"],
['Type 2 with transparency', "#{image_dir}/arrow2.png"],
['Type 3', "#{image_dir}/indexed_color.png"],
['Type 3 with transparency', "#{image_dir}/indexed_transparency.png"],
['Type 4', "#{image_dir}/page_white_text.png"],
['Type 6', "#{image_dir}/dice.png"],
['Type 6 in 16bit', "#{image_dir}/16bit.png"],
]
images.each do |header, file|
describe "and the image is #{header}" do
it 'does not error' do
expect {
Prawn::Document.generate("#{header}.pdf", page_size: 'A5') do
fill_color('00FF00')
fill_rectangle(bounds.top_left, bounds.width, bounds.height)
text(header)
image(file, at: [50, 450])
end
}.to_not raise_error
end
end
end
end
describe 'When reading a greyscale PNG file (color type 0)' do
let(:data_filename) { "#{Prawn::DATADIR}/images/web-links.dat" }
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/web-links.png") }
it 'reads the attributes from the header chunk correctly' do
png = described_class.new(img_data)
expect(png.width).to eq(21)
expect(png.height).to eq(14)
expect(png.bits).to eq(8)
expect(png.color_type).to eq(0)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'reads the image data chunk correctly' do
png = described_class.new(img_data)
data = Zlib::Inflate.inflate(File.binread(data_filename))
expect(png.img_data).to eq(data)
end
end
describe 'When reading a greyscale PNG with transparency (color type 0)' do
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/ruport_type0.png") }
# In a greyscale type 0 PNG image, the tRNS chunk should contain a single
# value that indicates the color that should be interpreted as transparent.
#
# http://www.w3.org/TR/PNG/#11tRNS
it 'reads the tRNS chunk correctly' do
png = described_class.new(img_data)
expect(png.transparency[:grayscale]).to eq(255)
end
end
describe 'When reading an RGB PNG file (color type 2)' do
let(:data_filename) { "#{Prawn::DATADIR}/images/ruport_data.dat" }
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/ruport.png") }
it 'reads the attributes from the header chunk correctly' do
png = described_class.new(img_data)
expect(png.width).to eq(258)
expect(png.height).to eq(105)
expect(png.bits).to eq(8)
expect(png.color_type).to eq(2)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'reads the image data chunk correctly' do
png = described_class.new(img_data)
data = Zlib::Inflate.inflate(File.binread(data_filename))
expect(png.img_data).to eq(data)
end
end
describe 'When reading an RGB PNG file with transparency (color type 2)' do
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/arrow2.png") }
# In a RGB type 2 PNG image, the tRNS chunk should contain a single RGB
# value that indicates the color that should be interpreted as transparent.
# In this case it's green.
#
# http://www.w3.org/TR/PNG/#11tRNS
it 'reads the tRNS chunk correctly' do
png = described_class.new(img_data)
expect(png.transparency[:rgb]).to eq([0, 255, 0])
end
end
describe 'When reading an indexed color PNG file with transparency (color type 3)' do
let(:filename) { "#{Prawn::DATADIR}/images/indexed_transparency.png" }
let(:color_filename) do
"#{Prawn::DATADIR}/images/indexed_transparency_color.dat"
end
let(:transparency_filename) do
"#{Prawn::DATADIR}/images/indexed_transparency_alpha.dat"
end
let(:img_data) { File.binread(filename) }
let(:png) { described_class.new(img_data) }
it 'reads the attributes from the header chunk correctly' do
expect(png.width).to eq(200)
expect(png.height).to eq(200)
expect(png.bits).to eq(8)
expect(png.color_type).to eq(3)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'reads the image data correctly' do
data = Zlib::Inflate.inflate(File.binread(color_filename))
expect(png.img_data).to eq(data)
end
it 'reads the image transparency correctly' do
png.split_alpha_channel!
data = Zlib::Inflate.inflate(File.binread(transparency_filename))
expect(png.alpha_channel).to eq(data)
end
end
describe 'When reading an indexed color PNG file (color type 3)' do
let(:data_filename) { "#{Prawn::DATADIR}/images/indexed_color.dat" }
let(:img_data) do
File.binread("#{Prawn::DATADIR}/images/indexed_color.png")
end
it 'reads the attributes from the header chunk correctly' do
png = described_class.new(img_data)
expect(png.width).to eq(150)
expect(png.height).to eq(200)
expect(png.bits).to eq(8)
expect(png.color_type).to eq(3)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'reads the image data chunk correctly' do
png = described_class.new(img_data)
data = Zlib::Inflate.inflate(File.binread(data_filename))
expect(png.img_data).to eq(data)
end
end
describe 'When reading a greyscale+alpha PNG file (color type 4)' do
let(:color_data_filename) do
"#{Prawn::DATADIR}/images/page_white_text.color"
end
let(:alpha_data_filename) do
"#{Prawn::DATADIR}/images/page_white_text.alpha"
end
let(:img_data) do
File.binread("#{Prawn::DATADIR}/images/page_white_text.png")
end
it 'reads the attributes from the header chunk correctly' do
png = described_class.new(img_data)
expect(png.width).to eq(16)
expect(png.height).to eq(16)
expect(png.bits).to eq(8)
expect(png.color_type).to eq(4)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'correctly returns the raw image data (with no alpha channel) from the image data chunk' do
png = described_class.new(img_data)
png.split_alpha_channel!
data = File.binread(color_data_filename)
expect(png.img_data).to eq(data)
end
it 'correctly extracts the alpha channel data from the image data chunk' do
png = described_class.new(img_data)
png.split_alpha_channel!
data = File.binread(alpha_data_filename)
expect(png.alpha_channel).to eq(data)
end
end
describe 'When reading an RGB+alpha PNG file (color type 6)' do
let(:color_data_filename) { "#{Prawn::DATADIR}/images/dice.color" }
let(:alpha_data_filename) { "#{Prawn::DATADIR}/images/dice.alpha" }
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/dice.png") }
it 'reads the attributes from the header chunk correctly' do
png = described_class.new(img_data)
expect(png.width).to eq(320)
expect(png.height).to eq(240)
expect(png.bits).to eq(8)
expect(png.color_type).to eq(6)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'correctly returns the raw image data (with no alpha channel) from the image data chunk' do
png = described_class.new(img_data)
png.split_alpha_channel!
data = File.binread(color_data_filename)
expect(png.img_data).to eq(data)
end
it 'correctly extracts the alpha channel data from the image data chunk' do
png = described_class.new(img_data)
png.split_alpha_channel!
data = File.binread(alpha_data_filename)
expect(png.alpha_channel).to eq(data)
end
end
describe 'When reading a 16bit RGB+alpha PNG file (color type 6)' do
let(:color_data_filename) { "#{Prawn::DATADIR}/images/16bit.color" }
# alpha channel truncated to 8-bit
let(:alpha_data_filename) { "#{Prawn::DATADIR}/images/16bit.alpha" }
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/16bit.png") }
it 'reads the attributes from the header chunk correctly' do
png = described_class.new(img_data)
expect(png.width).to eq(32)
expect(png.height).to eq(32)
expect(png.bits).to eq(16)
expect(png.color_type).to eq(6)
expect(png.compression_method).to eq(0)
expect(png.filter_method).to eq(0)
expect(png.interlace_method).to eq(0)
end
it 'correctly returns the raw image data (with no alpha channel) from the image data chunk' do
png = described_class.new(img_data)
png.split_alpha_channel!
data = File.binread(color_data_filename)
expect(png.img_data).to eq(data)
end
it 'correctly extracts the alpha channel data from the image data chunk' do
png = described_class.new(img_data)
png.split_alpha_channel!
data = File.binread(alpha_data_filename)
expect(png.alpha_channel).to eq(data)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/images/jpg_spec.rb 0000664 0000000 0000000 00000001100 14571572164 020734 0 ustar root root # frozen_string_literal: true
# Spec'ing the PNG class. Not complete yet - still needs to check the
# contents of palette and transparency to ensure they're correct.
# Need to find files that have these sections first.
require 'spec_helper'
describe Prawn::Images::JPG do
let(:img_data) { File.binread("#{Prawn::DATADIR}/images/pigs.jpg") }
it 'reads the basic attributes correctly' do
jpg = described_class.new(img_data)
expect(jpg.width).to eq(604)
expect(jpg.height).to eq(453)
expect(jpg.bits).to eq(8)
expect(jpg.channels).to eq(3)
end
end
ruby-prawn-2.5.0.orig/spec/prawn/image_handler_spec.rb 0000664 0000000 0000000 00000003110 14571572164 021471 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::ImageHandler do
let(:image_handler) { described_class.new }
let(:dummy_handler) do
Class.new do
def self.can_render?(_blob); end
end
end
let(:handler_a) { object_double(dummy_handler, 'Handler A') }
let(:handler_b) { object_double(dummy_handler, 'Handler B') }
it 'finds the image handler for an image' do
allow(handler_a).to receive(:can_render?).and_return(true)
image_handler.register(handler_a)
image_handler.register(handler_b)
handler = image_handler.find('arbitrary blob')
expect(handler).to eq(handler_a)
end
it 'can prepend handlers' do
allow(handler_b).to receive(:can_render?).and_return(true)
image_handler.register(handler_a)
image_handler.register!(handler_b)
handler = image_handler.find('arbitrary blob')
expect(handler).to eq(handler_b)
end
it 'can unregister a handler' do
allow(handler_b).to receive(:can_render?).and_return(true)
image_handler.register(handler_a)
image_handler.register(handler_b)
image_handler.unregister(handler_a)
handler = image_handler.find('arbitrary blob')
expect(handler).to eq(handler_b)
end
it 'raises an error when no matching handler is found' do
allow(handler_a).to receive(:can_render?).and_return(false)
allow(handler_b).to receive(:can_render?).and_return(false)
image_handler.register(handler_a)
image_handler.register(handler_b)
expect { image_handler.find('arbitrary blob') }
.to(raise_error(Prawn::Errors::UnsupportedImageType))
end
end
ruby-prawn-2.5.0.orig/spec/prawn/graphics_stroke_styles_spec.rb 0000664 0000000 0000000 00000015471 14571572164 023521 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Graphics do
let(:pdf) { create_pdf }
describe 'When stroking with default settings' do
it 'cap_style should be :butt' do
expect(pdf.cap_style).to eq(:butt)
end
it 'join_style should be :miter' do
expect(pdf.join_style).to eq(:miter)
end
it 'dashed? should be_false' do
expect(pdf).to_not be_dashed
end
end
describe 'Cap styles' do
it 'is able to use assignment operator' do
pdf.cap_style = :round
expect(pdf.cap_style).to eq(:round)
end
describe '#cap_style(:butt)' do
it 'rendered PDF should include butt style cap' do
pdf.cap_style(:butt)
cap_style = PDF::Inspector::Graphics::CapStyle.analyze(pdf.render)
.cap_style
expect(cap_style).to eq(0)
end
end
describe '#cap_style(:round)' do
it 'rendered PDF should include round style cap' do
pdf.cap_style(:round)
cap_style = PDF::Inspector::Graphics::CapStyle.analyze(pdf.render)
.cap_style
expect(cap_style).to eq(1)
end
end
describe '#cap_style(:projecting_square)' do
it 'rendered PDF should include projecting_square style cap' do
pdf.cap_style(:projecting_square)
cap_style = PDF::Inspector::Graphics::CapStyle.analyze(pdf.render)
.cap_style
expect(cap_style).to eq(2)
end
end
it 'carries the current cap style settings over to new pages' do
pdf.cap_style(:round)
pdf.start_new_page
cap_styles = PDF::Inspector::Graphics::CapStyle.analyze(pdf.render)
expect(cap_styles.cap_style_count).to eq(2)
expect(cap_styles.cap_style).to eq(1)
end
end
describe 'Join styles' do
it 'is able to use assignment operator' do
pdf.join_style = :round
expect(pdf.join_style).to eq(:round)
end
describe '#join_style(:miter)' do
it 'rendered PDF should include miter style join' do
pdf.join_style(:miter)
join_style = PDF::Inspector::Graphics::JoinStyle.analyze(pdf.render)
.join_style
expect(join_style).to eq(0)
end
end
describe '#join_style(:round)' do
it 'rendered PDF should include round style join' do
pdf.join_style(:round)
join_style = PDF::Inspector::Graphics::JoinStyle.analyze(pdf.render)
.join_style
expect(join_style).to eq(1)
end
end
describe '#join_style(:bevel)' do
it 'rendered PDF should include bevel style join' do
pdf.join_style(:bevel)
join_style = PDF::Inspector::Graphics::JoinStyle.analyze(pdf.render)
.join_style
expect(join_style).to eq(2)
end
end
it 'carries the current join style settings over to new pages' do
pdf.join_style(:round)
pdf.start_new_page
join_styles = PDF::Inspector::Graphics::JoinStyle.analyze(pdf.render)
expect(join_styles.join_style_count).to eq(2)
expect(join_styles.join_style).to eq(1)
end
context 'with invalid arguments' do
it 'raises an exception' do
expect { pdf.join_style(:mitre) }
.to raise_error(Prawn::Errors::InvalidJoinStyle)
end
end
end
describe 'Dashes' do
it 'is able to use assignment operator' do
pdf.dash = 2
expect(pdf).to be_dashed
end
describe 'setting a dash' do
it 'dashed? should be_true' do
pdf.dash(2)
expect(pdf).to be_dashed
end
it 'rendered PDF should include a stroked dash' do
pdf.dash(2)
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[2, 2], 0])
end
end
describe 'setting a dash by passing a single argument' do
it 'space between dashes should be the same length as the dash in the rendered PDF' do
pdf.dash(2)
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[2, 2], 0])
end
end
describe 'with a space option that differs from the first argument' do
it 'space between dashes in the rendered PDF should be different length than the length of the dash' do
pdf.dash(2, space: 3)
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[2, 3], 0])
end
end
describe 'with a non-zero phase option' do
it 'rendered PDF should include a non-zero phase' do
pdf.dash(2, phase: 1)
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[2, 2], 1])
end
end
describe 'setting a dash by using an array' do
it 'dash and spaces should be set from the array' do
pdf.dash([1, 2, 3, 4])
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[1, 2, 3, 4], 0])
end
it 'at least one number in the array must not be zero' do
pdf.dash([1, 0])
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[1, 0], 0])
end
it 'space options has to be ignored' do
pdf.dash([1, 2, 3, 4], space: 3)
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[1, 2, 3, 4], 0])
end
it 'phase options should be correctly used' do
pdf.dash([1, 2, 3, 4], phase: 3)
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[1, 2, 3, 4], 3])
end
end
describe 'clearing stroke dash' do
it 'restores solid line' do
pdf.dash(2)
pdf.undash
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash).to eq([[], 0])
end
end
it 'carries the current dash settings over to new pages' do
pdf.dash(2)
pdf.start_new_page
dashes = PDF::Inspector::Graphics::Dash.analyze(pdf.render)
expect(dashes.stroke_dash_count).to eq(2)
expect(dashes.stroke_dash).to eq([[2, 2], 0])
end
describe '#dashed?' do
it 'an initial document should not be dashed' do
expect(pdf.dashed?).to be(false)
end
it 'returns true if any of the currently active settings are dashed' do
pdf.dash(2)
pdf.save_graphics_state
expect(pdf.dashed?).to be(true)
end
it 'returns false if the document was most recently undashed' do
pdf.dash(2)
pdf.save_graphics_state
pdf.undash
pdf.save_graphics_state
expect(pdf.dashed?).to be(false)
end
it 'returns true when restoring to a state that was dashed' do
pdf.dash(2)
pdf.save_graphics_state
pdf.undash
pdf.restore_graphics_state
expect(pdf.dashed?).to be(true)
end
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/graphics_spec.rb 0000664 0000000 0000000 00000072021 14571572164 020521 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Graphics do
let(:pdf) { create_pdf }
describe 'When drawing a line' do
it 'draws a line from (100,600) to (100,500)' do
pdf.line([100, 600], [100, 500])
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points).to eq([[100, 600], [100, 500]])
end
it 'draws two lines at (100,600) to (100,500) and (75,100) to (50,125)' do
pdf.line(100, 600, 100, 500)
pdf.line(75, 100, 50, 125)
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points).to eq([[100.0, 600.0], [100.0, 500.0], [75.0, 100.0], [50.0, 125.0]])
end
it 'properlies set line width via line_width=' do
pdf.line_width = 10
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line.widths.first).to eq(10)
end
it 'properlies set line width via line_width(width)' do
pdf.line_width(10)
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line.widths.first).to eq(10)
end
it 'carries the current line width settings over to new pages' do
pdf.line_width(10)
pdf.start_new_page
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line.widths.length).to eq(2)
expect(line.widths[1]).to eq(10)
end
describe '(Horizontally)' do
it 'draws from [x1,pdf.y],[x2,pdf.y]' do
pdf.horizontal_line(100, 150)
line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line.points).to eq(
[
[100.0 + pdf.bounds.absolute_left, pdf.y],
[150.0 + pdf.bounds.absolute_left, pdf.y],
],
)
end
it 'draws a line from (200, 250) to (300, 250)' do
pdf.horizontal_line(200, 300, at: 250)
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points).to eq([[200, 250], [300, 250]])
end
end
describe '(Vertically)' do
it 'draws a line from (350, 300) to (350, 400)' do
pdf.vertical_line(300, 400, at: 350)
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points).to eq([[350, 300], [350, 400]])
end
it 'requires a y coordinate' do
expect { pdf.vertical_line(400, 500) }.to raise_error(ArgumentError)
end
end
end
describe 'When drawing a polygon' do
it 'draws each line passed to polygon()' do
pdf.polygon([100, 500], [100, 400], [200, 400])
line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
expect(line_drawing.points).to eq([[100, 500], [100, 400], [200, 400], [100, 500]])
end
end
describe 'When drawing a rectangle' do
it 'uses a point, width, and height for coords' do
pdf.rectangle([200, 200], 50, 100)
rectangles = PDF::Inspector::Graphics::Rectangle.analyze(pdf.render)
.rectangles
# PDF uses bottom left corner
expect(rectangles[0][:point]).to eq([200, 100])
expect(rectangles[0][:width]).to eq(50)
expect(rectangles[0][:height]).to eq(100)
end
end
describe 'When drawing a curve' do
it 'draws a bezier curve from 50,50 to 100,100' do
pdf.move_to([50, 50])
pdf.curve_to([100, 100], bounds: [[20, 90], [90, 70]])
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
expect(curve.coords).to eq([50.0, 50.0, 20.0, 90.0, 90.0, 70.0, 100.0, 100.0])
end
it 'draws a bezier curve from 100,100 to 50,50' do
pdf.curve([100, 100], [50, 50], bounds: [[20, 90], [90, 75]])
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
expect(curve.coords).to eq([100.0, 100.0, 20.0, 90.0, 90.0, 75.0, 50.0, 50.0])
end
end
describe 'When drawing a rounded rectangle' do
before { pdf.rounded_rectangle([50, 550], 50, 100, 10) }
let(:original_point) do
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
curve_points = curve.coords.each_slice(2).to_a
curve_points.shift
end
let(:all_coords) do
curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
curve_points = curve.coords.each_slice(2).to_a
curve_points.shift
curves = curve_points.each_slice(3).to_a
line_points = PDF::Inspector::Graphics::Line.analyze(pdf.render).points
line_points.shift
line_points.zip(curves).flatten.each_slice(2).to_a.unshift(original_point)
end
it 'draws a rectangle by connecting lines with rounded bezier curves' do
expect(all_coords).to eq(
[
[60.0, 550.0], [90.0, 550.0], [95.52285, 550.0], [100.0, 545.52285],
[100.0, 540.0], [100.0, 460.0], [100.0, 454.47715], [95.52285, 450.0],
[90.0, 450.0], [60.0, 450.0], [54.47715, 450.0], [50.0, 454.47715],
[50.0, 460.0], [50.0, 540.0], [50.0, 545.52285], [54.47715, 550.0],
[60.0, 550.0],
],
)
end
it 'starts and end with the same point' do
expect(original_point).to eq(all_coords.last)
end
end
describe 'When drawing an ellipse' do
let(:curve) do
pdf.ellipse([100, 100], 25, 50)
PDF::Inspector::Graphics::Curve.analyze(pdf.render)
end
it 'uses a Bézier approximation' do
expect(curve.coords).to eq(
[
125.0, 100.0,
125.0, 127.61424,
113.80712, 150,
100.0, 150.0,
86.19288, 150.0,
75.0, 127.61424,
75.0, 100.0,
75.0, 72.38576,
86.19288, 50.0,
100.0, 50.0,
113.80712, 50.0,
125.0, 72.38576,
125.0, 100.0,
100.0, 100.0,
],
)
end
it 'moves the pointer to the center of the ellipse after drawing' do
expect(curve.coords[-2..]).to eq([100, 100])
end
end
describe 'When drawing a circle' do
let(:curve) do
pdf.circle([100, 100], 25)
pdf.ellipse([100, 100], 25, 25)
PDF::Inspector::Graphics::Curve.analyze(pdf.render)
end
it 'strokes the same path as the equivalent ellipse' do
middle = curve.coords.length / 2
expect(curve.coords[0...middle]).to eq(curve.coords[middle..])
end
end
describe 'When filling' do
it 'defaults to the f operator (nonzero winding number rule)' do
allow(pdf.renderer).to receive(:add_content).with('f')
pdf.fill
expect(pdf.renderer).to have_received(:add_content).with('f')
end
it 'uses f* for :fill_rule => :even_odd' do
allow(pdf.renderer).to receive(:add_content).with('f*')
pdf.fill(fill_rule: :even_odd)
expect(pdf.renderer).to have_received(:add_content).with('f*')
end
it 'uses b by default for fill_and_stroke (nonzero winding number)' do
allow(pdf.renderer).to receive(:add_content).with('b')
pdf.fill_and_stroke
expect(pdf.renderer).to have_received(:add_content).with('b')
end
it 'uses b* for fill_and_stroke(:fill_rule => :even_odd)' do
allow(pdf.renderer).to receive(:add_content).with('b*')
pdf.fill_and_stroke(fill_rule: :even_odd)
expect(pdf.renderer).to have_received(:add_content).with('b*')
end
end
describe 'When setting colors' do
it 'sets stroke colors' do
pdf.stroke_color('ffcccc')
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
# 100% red, 80% green, 80% blue
expect(colors.stroke_color).to eq([1.0, 0.8, 0.8])
end
it 'sets fill colors' do
pdf.fill_color('ccff00')
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
# 80% red, 100% green, 0% blue
expect(colors.fill_color).to eq([0.8, 1.0, 0])
end
it 'raises an error for a color with a leading #' do
expect { pdf.fill_color('#ccff00') }.to raise_error(ArgumentError)
end
it 'raises an error for a color string that is not a hex' do
expect { pdf.fill_color('zcff00') }.to raise_error(ArgumentError)
end
it 'raises an error for a color string with invalid characters' do
expect { pdf.fill_color('f0f0f?') }.to raise_error(ArgumentError)
end
it 'resets the colors on each new page if they have been defined' do
pdf.fill_color('ccff00')
pdf.start_new_page
pdf.stroke_color('ff00cc')
pdf.start_new_page
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.fill_color_count).to eq(3)
expect(colors.stroke_color_count).to eq(2)
expect(colors.fill_color).to eq([0.8, 1.0, 0.0])
expect(colors.stroke_color).to eq([1.0, 0.0, 0.8])
end
it 'sets the color space when setting colors on new pages to please fussy readers' do
pdf.stroke_color('000000')
pdf.stroke { pdf.rectangle([10, 10], 10, 10) }
pdf.start_new_page
pdf.stroke_color('000000')
pdf.stroke { pdf.rectangle([10, 10], 10, 10) }
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.stroke_color_space_count[:DeviceRGB]).to eq(2)
end
end
describe 'Patterns' do
describe 'linear gradients' do
it 'creates a /Pattern resource' do
pdf.fill_gradient(
[0, pdf.bounds.height],
[pdf.bounds.width, pdf.bounds.height],
'FF0000',
'0000FF',
)
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
pattern = grad.patterns.values.first
expect(pattern).to_not be_nil
expect(pattern[:Shading][:ShadingType]).to eq(2)
expect(pattern[:Shading][:Coords]).to eq([0, 0, pdf.bounds.width, 0])
expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? { |x1, x2| (x1 - x2).abs < 0.01 }).to be true
expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? { |x1, x2| (x1 - x2).abs < 0.01 }).to be true
end
it 'creates a unique ID for each pattern resource' do
pdf.fill_gradient(
[256, 512],
[356, 512],
'ffffff',
'fe00ff',
)
pdf.fill_gradient(
[256, 256],
[356, 256],
'ffffff',
'0000ff',
)
str = pdf.render
pattern_ids = str.scan(/SP\h{40}\s+scn/)
expect(pattern_ids.uniq.length).to eq 2
end
it 'fill_gradient should set fill color to the pattern' do
pdf.fill_gradient(
[0, pdf.bounds.height],
[pdf.bounds.width, pdf.bounds.height],
'FF0000',
'0000FF',
)
str = pdf.render
expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
end
it 'stroke_gradient should set stroke color to the pattern' do
pdf.stroke_gradient(
[0, pdf.bounds.height],
[pdf.bounds.width, pdf.bounds.height],
'FF0000',
'0000FF',
)
str = pdf.render
expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
end
it 'uses a stitching function to render a gradient with multiple stops' do
pdf.fill_gradient(
from: [0, pdf.bounds.height],
to: [pdf.bounds.width, pdf.bounds.height],
stops: { 0 => 'FF0000', 0.8 => '00FF00', 1 => '0000FF' },
)
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
pattern = grad.patterns.values.first
expect(pattern).to_not be_nil
stitching = pattern[:Shading][:Function]
expect(stitching[:FunctionType]).to eq(3)
expect(stitching[:Functions]).to be_an(Array)
expect(stitching[:Functions].map { |f| f[:C0] }).to eq([[1, 0, 0], [0, 1, 0]])
expect(stitching[:Functions].map { |f| f[:C1] }).to eq([[0, 1, 0], [0, 0, 1]])
expect(stitching[:Bounds]).to eq([0.8])
expect(stitching[:Encode]).to eq([0, 1, 0, 1])
end
it 'uses a stitching function to render a gradient with equally spaced stops' do
pdf.fill_gradient(
from: [0, pdf.bounds.height],
to: [pdf.bounds.width, pdf.bounds.height],
stops: %w[FF0000 00FF00 0000FF],
)
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
pattern = grad.patterns.values.first
expect(pattern).to_not be_nil
stitching = pattern[:Shading][:Function]
expect(stitching[:FunctionType]).to eq(3)
expect(stitching[:Functions]).to be_an(Array)
expect(stitching[:Functions].map { |f| f[:C0] }).to eq([[1, 0, 0], [0, 1, 0]])
expect(stitching[:Functions].map { |f| f[:C1] }).to eq([[0, 1, 0], [0, 0, 1]])
expect(stitching[:Bounds]).to eq([0.5])
end
end
describe 'radial gradients' do
it 'creates a /Pattern resource' do
pdf.fill_gradient(
[0, pdf.bounds.height],
10,
[pdf.bounds.width, pdf.bounds.height],
20,
'FF0000',
'0000FF',
)
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
pattern = grad.patterns.values.first
expect(pattern).to_not be_nil
expect(pattern[:Shading][:ShadingType]).to eq(3)
expect(pattern[:Shading][:Coords]).to eq([0, 0, 10, pdf.bounds.width, 0, 20])
expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? { |x1, x2| (x1 - x2).abs < 0.01 }).to be true
expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? { |x1, x2| (x1 - x2).abs < 0.01 }).to be true
end
it 'fill_gradient should set fill color to the pattern' do
pdf.fill_gradient(
[0, pdf.bounds.height],
10,
[pdf.bounds.width, pdf.bounds.height],
20,
'FF0000',
'0000FF',
)
str = pdf.render
expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
end
it 'stroke_gradient should set stroke color to the pattern' do
pdf.stroke_gradient(
[0, pdf.bounds.height],
10,
[pdf.bounds.width, pdf.bounds.height],
20,
'FF0000',
'0000FF',
)
str = pdf.render
expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
end
end
describe 'gradient transformations' do
subject(:transformations) do
pdf.scale(2) do
pdf.translate(40, 40) do
pdf.fill_gradient([0, 10], [15, 15], 'FF0000', '0000FF', **opts)
pdf.fill_gradient([0, 10], 15, [15, 15], 25, 'FF0000', '0000FF', **opts)
end
end
grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
grad.patterns.values.map { |pattern| pattern[:Matrix] }.uniq
end
context 'when :apply_transformations is true' do
let(:opts) { { apply_transformations: true } }
it 'uses the transformation stack to translate user co-ordinates to document co-ordinates required by ' \
'/Pattern' do
expect(transformations).to eq([[2, 0, 0, 2, 80, 100]])
end
end
context 'when :apply_transformations is false' do
let(:opts) { { apply_transformations: false } }
it "doesn't transform the gradient" do
expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
end
end
context 'when :apply_transformations is unset' do
let(:opts) { {} }
it "doesn't transform the gradient and displays a warning" do
allow(pdf).to receive(:warn).twice
expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
expect(pdf).to have_received(:warn).twice
end
end
end
end
describe 'When using painting shortcuts' do
it 'converts stroke_some_method(args) into some_method(args); stroke' do
allow(pdf).to receive(:line_to).with([100, 100])
allow(pdf).to receive(:stroke)
pdf.stroke_line_to([100, 100])
expect(pdf).to have_received(:line_to).with([100, 100])
expect(pdf).to have_received(:stroke)
end
it 'converts fill_some_method(args) into some_method(args); fill' do
allow(pdf).to receive(:line_to).with([100, 100])
allow(pdf).to receive(:fill)
pdf.fill_line_to([100, 100])
expect(pdf).to have_received(:line_to).with([100, 100])
expect(pdf).to have_received(:fill)
end
it 'does not break method_missing' do
expect { pdf.i_have_a_pretty_girlfriend_named_jia }
.to raise_error(NoMethodError)
end
end
describe 'When using graphics states' do
it 'adds the right content on save_graphics_state' do
allow(pdf.renderer).to receive(:add_content).with('q')
pdf.save_graphics_state
expect(pdf.renderer).to have_received(:add_content).with('q')
end
it 'adds the right content on restore_graphics_state' do
allow(pdf.renderer).to receive(:add_content).with('Q')
pdf.restore_graphics_state
expect(pdf.renderer).to have_received(:add_content).with('Q')
end
it 'saves and restore when save_graphics_state is used with a block' do
allow(pdf.renderer).to receive(:add_content).with('q')
allow(pdf).to receive(:foo)
allow(pdf.renderer).to receive(:add_content).with('Q')
pdf.save_graphics_state do
pdf.foo
end
expect(pdf.renderer).to have_received(:add_content).with('q').ordered
expect(pdf).to have_received(:foo).ordered
expect(pdf.renderer).to have_received(:add_content).with('Q').ordered
end
it 'adds the previous color space when restoring to a graphic state with different color space' do
pdf.stroke_color('000000')
pdf.save_graphics_state
pdf.stroke_color(0, 0, 0, 0)
pdf.restore_graphics_state
pdf.stroke_color(0, 0, 100, 0)
expect(pdf.graphic_state.color_space).to eq(stroke: :DeviceCMYK)
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.color_space).to eq(:DeviceCMYK)
expect(colors.stroke_color_space_count[:DeviceCMYK]).to eq(2)
end
it 'uses the correct dash setting after restoring and starting new page' do
pdf.dash(5)
pdf.save_graphics_state
pdf.dash(10)
expect(pdf.graphic_state.dash[:dash]).to eq(10)
pdf.restore_graphics_state
pdf.start_new_page
expect(pdf.graphic_state.dash[:dash]).to eq(5)
end
it 'rounds dash values to four decimal places' do
pdf.dash(5.12345)
expect(pdf.graphic_state.dash_setting).to eq('[5.12345 5.12345] 0.0 d')
end
it 'raises an error when dash is called w. a zero length or space' do
expect { pdf.dash(0) }.to raise_error(ArgumentError)
expect { pdf.dash([0]) }.to raise_error(ArgumentError)
expect { pdf.dash([0, 0]) }.to raise_error(ArgumentError)
end
it 'raises an error when dash is called w. negative lengths' do
expect { pdf.dash(-1) }.to raise_error(ArgumentError)
expect { pdf.dash([1, -3]) }.to raise_error(ArgumentError)
end
it 'the current graphic state keeps track of previous unchanged settings' do
pdf.stroke_color('000000')
pdf.save_graphics_state
pdf.dash(5)
pdf.save_graphics_state
pdf.cap_style(:round)
pdf.save_graphics_state
pdf.fill_color(0, 0, 100, 0)
pdf.save_graphics_state
expect(pdf.graphic_state.stroke_color).to eq('000000')
expect(pdf.graphic_state.join_style).to eq(:miter)
expect(pdf.graphic_state.fill_color).to eq([0, 0, 100, 0])
expect(pdf.graphic_state.cap_style).to eq(:round)
expect(pdf.graphic_state.color_space).to eq(fill: :DeviceCMYK, stroke: :DeviceRGB)
expect(pdf.graphic_state.dash).to eq(space: 5, phase: 0, dash: 5)
expect(pdf.graphic_state.line_width).to eq(1)
end
it "doesn't add extra graphic space closings when rendering multiple times" do
pdf.render
state = PDF::Inspector::Graphics::State.analyze(pdf.render)
expect(state.save_graphics_state_count).to eq(1)
expect(state.restore_graphics_state_count).to eq(1)
end
it 'adds extra graphic state enclosings when content is added on multiple renderings' do
pdf.render
pdf.text('Adding a bit more content')
state = PDF::Inspector::Graphics::State.analyze(pdf.render)
expect(state.save_graphics_state_count).to eq(2)
expect(state.restore_graphics_state_count).to eq(2)
end
it 'adds extra graphic state enclosings when new settings are applied on multiple renderings' do
pdf.render
pdf.stroke_color(0, 0, 0, 0)
state = PDF::Inspector::Graphics::State.analyze(pdf.render)
expect(state.save_graphics_state_count).to eq(2)
expect(state.restore_graphics_state_count).to eq(2)
end
it 'raise_errors error if closing an empty graphic stack' do
expect {
pdf.render
pdf.restore_graphics_state
}.to raise_error(PDF::Core::Errors::EmptyGraphicStateStack)
end
it 'copies mutable attributes when passing a previous_state to the initializer' do
new_state = PDF::Core::GraphicState.new(pdf.graphic_state)
%i[color_space dash fill_color stroke_color].each do |attr|
expect(new_state.public_send(attr)).to eq(pdf.graphic_state.public_send(attr))
expect(new_state.public_send(attr)).to_not equal(pdf.graphic_state.public_send(attr))
end
end
it 'copies mutable attributes when duping' do
new_state = pdf.graphic_state.dup
%i[color_space dash fill_color stroke_color].each do |attr|
expect(new_state.public_send(attr)).to eq(pdf.graphic_state.public_send(attr))
expect(new_state.public_send(attr)).to_not equal(pdf.graphic_state.public_send(attr))
end
end
it 'saves the transformation stack on graphics state save' do
allow(pdf).to receive(:save_transformation_stack)
allow(pdf).to receive(:restore_transformation_stack)
pdf.save_graphics_state
expect(pdf).to have_received(:save_transformation_stack)
expect(pdf).to_not have_received(:restore_transformation_stack)
end
it 'saves and restores the transformation stack when save graphics state used in block form' do
allow(pdf).to receive(:save_transformation_stack)
allow(pdf).to receive(:restore_transformation_stack)
pdf.save_graphics_state do
# Deliberately empty block
end
expect(pdf).to have_received(:save_transformation_stack)
expect(pdf).to have_received(:restore_transformation_stack)
end
it 'restores the transformation stack on graphics state restore' do
allow(pdf).to receive(:restore_transformation_stack)
pdf.restore_graphics_state
expect(pdf).to have_received(:restore_transformation_stack)
end
end
describe 'When using transformation matrix' do
# NOTE: The (approximate) number of significant decimal digits of precision
# in fractional part is 5 (PDF Reference, Third Edition, p. 706)
it 'sends the right content on transformation_matrix' do
allow(pdf.renderer).to receive(:add_content).with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
pdf.transformation_matrix(1, 0, 0.123456789, -1.0, 5.5, 20)
expect(pdf.renderer).to have_received(:add_content).with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
end
it 'uses fixed digits with very small number' do
values = Array.new(6, 0.000000000001)
string = Array.new(6, '0.0').join(' ')
allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
pdf.transformation_matrix(*values)
expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
end
it 'is received by the inspector' do
pdf.transformation_matrix(1, 0, 0, -1, 5.5, 20)
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
expect(matrices.matrices).to eq([[1, 0, 0, -1, 5.5, 20]])
end
it 'saves the graphics state inside the given block' do
values = Array.new(6, 0.000000000001)
string = Array.new(6, '0.0').join(' ')
allow(pdf).to receive(:save_graphics_state).with(no_args)
allow(pdf.renderer).to receive(:add_content).with(any_args).twice
allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
allow(pdf).to receive(:do_something)
allow(pdf).to receive(:restore_graphics_state).with(no_args)
pdf.transformation_matrix(*values) do
pdf.do_something
end
expect(pdf).to have_received(:save_graphics_state).with(no_args).ordered
expect(pdf.renderer).to have_received(:add_content).with("#{string} cm").ordered
expect(pdf).to have_received(:do_something).ordered
expect(pdf).to have_received(:restore_graphics_state).with(no_args).ordered
end
it 'properly serializes the matrix', issue: 1178 do
pdf.transformation_matrix(1, 0, 0, 1, 0.0, 1.8977874316715393e-05)
rendered_document = pdf.render
expect(rendered_document).to_not include('2.0e-05')
matrices = PDF::Inspector::Graphics::Matrix.analyze(rendered_document)
expect(matrices.matrices).to eq([[1, 0, 0, 1, 0.0, 0.00002]])
end
end
describe 'When using transformations shortcuts' do
describe '#rotate' do
let(:angle) { 12.32 }
let(:cos) { Math.cos(angle * Math::PI / 180) }
let(:sin) { Math.sin(angle * Math::PI / 180) }
it 'rotates' do
allow(pdf).to receive(:transformation_matrix).with(cos, sin, -sin, cos, 0, 0)
pdf.rotate(angle)
expect(pdf).to have_received(:transformation_matrix).with(cos, sin, -sin, cos, 0, 0)
end
end
describe '#rotate with :origin option' do
let(:angle) { 12.32 }
let(:cos) { Math.cos(angle * Math::PI / 180) }
let(:sin) { Math.sin(angle * Math::PI / 180) }
it 'rotates around the origin' do
x = 12
y = 54.32
x_prime = (x * cos) - (y * sin)
y_prime = (x * sin) + (y * cos)
pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x - x_prime),
reduce_precision(y - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
end
it 'rotates around the origin in a document with a margin' do
x = 12
y = 54.32
pdf = Prawn::Document.new
pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
x_ = x + pdf.bounds.absolute_left
y_ = y + pdf.bounds.absolute_bottom
x_prime = (x_ * cos) - (y_ * sin)
y_prime = (x_ * sin) + (y_ * cos)
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x_ - x_prime),
reduce_precision(y_ - y_prime),
],
)
expect(matrices.matrices[1]).to eq(
[
reduce_precision(cos),
reduce_precision(sin),
reduce_precision(-sin),
reduce_precision(cos),
0, 0,
],
)
end
it 'raise_errors BlockRequired if no block is given' do
expect {
pdf.rotate(angle, origin: [0, 0])
}.to raise_error(Prawn::Errors::BlockRequired)
end
end
describe '#translate' do
it 'translates' do
x = 12
y = 54.32
allow(pdf).to receive(:transformation_matrix).with(1, 0, 0, 1, x, y)
pdf.translate(x, y)
expect(pdf).to have_received(:transformation_matrix).with(1, 0, 0, 1, x, y)
end
end
describe '#scale' do
it 'scales' do
factor = 0.12
allow(pdf).to receive(:transformation_matrix).with(factor, 0, 0, factor, 0, 0)
pdf.scale(factor)
expect(pdf).to have_received(:transformation_matrix).with(factor, 0, 0, factor, 0, 0)
end
end
describe '#scale with :origin option' do
let(:factor) { 0.12 }
it 'scales from the origin' do
x = 12
y = 54.32
x_prime = factor * x
y_prime = factor * y
pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x - x_prime),
reduce_precision(y - y_prime),
],
)
expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
end
it 'scales from the origin in a document with a margin' do
x = 12
y = 54.32
pdf = Prawn::Document.new
x_ = x + pdf.bounds.absolute_left
y_ = y + pdf.bounds.absolute_bottom
x_prime = factor * x_
y_prime = factor * y_
pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
expect(matrices.matrices[0]).to eq(
[
1, 0, 0, 1,
reduce_precision(x_ - x_prime),
reduce_precision(y_ - y_prime),
],
)
expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
end
it 'raise_errors BlockRequired if no block is given' do
expect {
pdf.scale(factor, origin: [0, 0])
}.to raise_error(Prawn::Errors::BlockRequired)
end
end
end
def reduce_precision(float)
float.round(5)
end
end
ruby-prawn-2.5.0.orig/spec/prawn/graphics/ 0000775 0000000 0000000 00000000000 14571572164 017160 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/graphics/transparency_spec.rb 0000664 0000000 0000000 00000005207 14571572164 023234 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Graphics::Transparency do
def make_transparent(opacity, stroke_opacity = opacity)
pdf.transparent(opacity, stroke_opacity) do
yield if block_given?
end
end
let(:pdf) { create_pdf }
it 'the PDF version should be at least 1.4' do
make_transparent(0.5)
str = pdf.render
expect(str[0, 8]).to eq('%PDF-1.4')
end
it 'a new extended graphics state should be created for each unique transparency setting' do
make_transparent(0.5, 0.2) do
make_transparent(0.5, 0.75)
end
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
expect(extgstates.length).to eq(2)
end
it 'a new extended graphics state should not be created for each duplicate transparency setting' do
make_transparent(0.5, 0.75) do
make_transparent(0.5, 0.75)
end
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
expect(extgstates.length).to eq(1)
end
it 'setting the transparency with only one parameter sets the transparency for both the fill and the stroke' do
make_transparent(0.5)
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
expect(extgstate[:opacity]).to eq(0.5)
expect(extgstate[:stroke_opacity]).to eq(0.5)
end
it 'setting the transparency with a numerical parameter and ' \
'a :stroke should set the fill transparency to the numerical parameter ' \
'and the stroke transparency to the option' do
make_transparent(0.5, 0.2)
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
expect(extgstate[:opacity]).to eq(0.5)
expect(extgstate[:stroke_opacity]).to eq(0.2)
end
it 'does not allow negative values' do
make_transparent(-0.5, -0.2)
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
expect(extgstate[:opacity]).to eq(0.0)
expect(extgstate[:stroke_opacity]).to eq(0.0)
end
it 'does not allow too big values' do
make_transparent(2.0, 3.0)
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates[0]
expect(extgstate[:opacity]).to eq(1.0)
expect(extgstate[:stroke_opacity]).to eq(1.0)
end
describe 'with more than one page' do
it 'the extended graphic state resource should be added to both pages' do
make_transparent(0.5, 0.2)
pdf.start_new_page
make_transparent(0.5, 0.2)
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
extgstate = extgstates[0]
expect(extgstates.length).to eq(2)
expect(extgstate[:opacity]).to eq(0.5)
expect(extgstate[:stroke_opacity]).to eq(0.2)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/graphics/blend_mode_spec.rb 0000664 0000000 0000000 00000003624 14571572164 022614 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Graphics::BlendMode do
def make_blend_mode(blend_mode)
pdf.blend_mode(blend_mode) do
yield if block_given?
end
end
let(:pdf) { create_pdf }
it 'the PDF version should be at least 1.4' do
make_blend_mode(:Multiply)
str = pdf.render
expect(str[0, 8]).to eq('%PDF-1.4')
end
it 'a new extended graphics state should be created for each unique blend mode setting' do
make_blend_mode(:Multiply) do
make_blend_mode(:Screen)
end
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
expect(extgstates.length).to eq(2)
end
it 'a new extended graphics state should not be created for each duplicate blend mode setting' do
make_blend_mode(:Multiply) do
make_blend_mode(:Multiply)
end
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
expect(extgstates.length).to eq(1)
end
it 'setting the blend mode with only one parameter sets a single blend mode value' do
make_blend_mode(:Multiply)
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
expect(extgstate[:blend_mode]).to eq(:Multiply)
end
it 'setting the blend mode with multiple parameters sets an array of blend modes' do
make_blend_mode(%i[Multiply Screen Overlay])
extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
expect(extgstate[:blend_mode]).to eq(%i[Multiply Screen Overlay])
end
describe 'with more than one page' do
it 'the extended graphic state resource should be added to both pages' do
make_blend_mode(:Multiply)
pdf.start_new_page
make_blend_mode(:Multiply)
extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
extgstate = extgstates[0]
expect(extgstates.length).to eq(2)
expect(extgstate[:blend_mode]).to eq(:Multiply)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/fonts/ 0000775 0000000 0000000 00000000000 14571572164 016511 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/fonts/to_unicode_cmap_spec.rb 0000664 0000000 0000000 00000005301 14571572164 023177 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'pathname'
describe Prawn::Fonts::ToUnicodeCMap do
it 'generates a cmap' do
charmap = {
0x20 => 0x20,
0x21 => 0x21,
0x22 => 0x22,
0x30 => 0x30,
}
to_unicode_cmap = described_class.new(charmap)
expect(to_unicode_cmap.generate).to eq(<<~CMAP.chomp)
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo 3 dict dup begin
/Registry (Adobe) def
/Ordering (UCS) def
/Supplement 0 def
end def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<00><30>
endcodespacerange
1 beginbfrange
<20><22><0020>
endbfrange
1 beginbfchar
<30><0030>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
CMAP
end
it 'generates type 2 cmap' do
cmap = described_class.new(0x20 => 0x30).generate
expect(cmap).to match(%r{/CMapType 2\b})
end
it 'properly sets codespace range' do
cmap = described_class.new(0x20 => 0x30).generate
expect(cmap).to include("begincodespacerange\n<00><20>\n")
end
it 'properly sets large codespace range' do
cmap = described_class.new(0x2000 => 0x30).generate
expect(cmap).to include("begincodespacerange\n<0000><20FF>\n")
end
it 'uses codespace size override' do
cmap = described_class.new({ 0x20 => 0x30 }, 2).generate
expect(cmap).to include("begincodespacerange\n<0000><0020>\n")
end
it 'uses ranges for continuous mappings' do
cmap = described_class.new(0x20 => 0x30, 0x21 => 0x31, 0x22 => 0x32).generate
expect(cmap).to include("beginbfrange\n<20><22><0030>\n")
end
it 'uses ranges for continuous code rnages with non-continuous mappings' do
cmap = described_class.new(0x20 => 0x32, 0x21 => 0x31, 0x22 => 0x30).generate
expect(cmap).to include("beginbfrange\n<20><22>[<0032><0031><0030>]\n")
end
it 'uses individual mappings' do
cmap = described_class.new(0x20 => 0x30, 0x21 => 0x31, 0x22 => 0x32, 0x30 => 0x40).generate
expect(cmap).to include("beginbfchar\n<30><0040>\n")
end
it 'splits continuous mappings into groups of 100' do
mapping = (1..142).flat_map { |n| Array.new(3) { |i| [(n * 10) + i, (n * 10) + i] } }.to_h
cmap = described_class.new(mapping).generate
expect(cmap).to include("\n100 beginbfrange\n").and(include("\n42 beginbfrange\n"))
end
it 'splits individual mappings into groups of 100' do
mapping = (1..142).to_h { |n| [n * 2, n * 2] }
cmap = described_class.new(mapping).generate
expect(cmap).to include("\n100 beginbfchar\n").and(include("\n42 beginbfchar\n"))
end
end
ruby-prawn-2.5.0.orig/spec/prawn/font_spec.rb 0000664 0000000 0000000 00000054000 14571572164 017664 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'pathname'
describe Prawn::Font do
let(:pdf) { create_pdf }
describe 'Font behavior' do
it 'defaults to Helvetica if no font is specified' do
pdf = Prawn::Document.new
expect(pdf.font.name).to eq('Helvetica')
end
end
describe 'Font objects' do
it 'is equal' do
font1 = Prawn::Document.new.font
font2 = Prawn::Document.new.font
expect(font1).to eql(font2)
end
it 'alwayses be the same key' do
font1 = Prawn::Document.new.font
font2 = Prawn::Document.new.font
hash = {}
hash[font1] = 'Original'
hash[font2] = 'Overwritten'
expect(hash.size).to eq(1)
expect(hash[font1]).to eq('Overwritten')
expect(hash[font2]).to eq('Overwritten')
end
end
describe '#width_of' do
it 'takes character spacing into account' do
original_width = pdf.width_of('hello world')
pdf.character_spacing(7) do
expect(pdf.width_of('hello world')).to eq(original_width + (10 * 7))
end
end
it 'excludes newlines' do
# Use a TTF font that has a non-zero width for \n
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
expect(pdf.width_of("\nhello world\n")).to eq(pdf.width_of('hello world'))
end
it 'takes formatting into account' do
normal_hello = pdf.width_of('hello')
inline_bold_hello = pdf.width_of('hello', inline_format: true)
expect(inline_bold_hello).to be > normal_hello
pdf.font('Helvetica', style: :bold) do
expect(inline_bold_hello).to eq(pdf.width_of('hello'))
end
end
it 'accepts :style as an argument' do
styled_bold_hello = pdf.width_of('hello', style: :bold)
pdf.font('Helvetica', style: :bold) do
expect(styled_bold_hello).to eq(pdf.width_of('hello'))
end
end
it 'reports missing font with style' do
expect {
pdf.font('Nada', style: :bold) do
pdf.width_of('hello')
end
}.to raise_error(Prawn::Errors::UnknownFont, /Nada \(bold\)/)
end
it 'calculates styled widths correctly using TTFs' do
pdf.font_families.update(
'DejaVu Sans' => {
normal: "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf",
bold: "#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf",
},
)
styled_bold_hello = nil
bold_hello = nil
plain_hello = nil
pdf.font('DejaVu Sans') do
styled_bold_hello = pdf.width_of('hello', style: :bold)
end
pdf.font('DejaVu Sans', style: :bold) do
bold_hello = pdf.width_of('hello')
end
pdf.font('DejaVu Sans') do
plain_hello = pdf.width_of('hello')
end
expect(plain_hello).to_not eq(bold_hello)
expect(styled_bold_hello).to eq(bold_hello)
end
it 'does not treat minus as if it were a hyphen', issue: 578 do
expect(pdf.width_of('-0.75')).to be < pdf.width_of('25.00')
end
end
describe '#font_size' do
it 'allows setting font size in DSL style' do
pdf.font_size(20)
expect(pdf.font_size).to eq(20)
end
end
describe 'font style support' do
it 'complains if there is no @current_page' do
pdf_without_page = Prawn::Document.new(skip_page_creation: true)
expect { pdf_without_page.font('Helvetica') }
.to raise_error(Prawn::Errors::NotOnPage)
end
it 'allows specifying font style by style name and font family' do
pdf.font('Courier', style: :bold)
pdf.text('In Courier bold')
pdf.font('Courier', style: :bold_italic)
pdf.text('In Courier bold-italic')
pdf.font('Courier', style: :italic)
pdf.text('In Courier italic')
pdf.font('Courier', style: :normal)
pdf.text('In Normal Courier')
pdf.font('Helvetica')
pdf.text('In Normal Helvetica')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings.map { |e| e[:name] }).to eq(
%i[Courier-Bold Courier-BoldOblique Courier-Oblique Courier Helvetica],
)
end
it 'allows font families to be defined in a single dfont' do
file = "#{Prawn::DATADIR}/fonts/Panic+Sans.dfont"
pdf.font_families['Panic Sans'] = {
normal: { file: file, font: 'PanicSans' },
italic: { file: file, font: 'PanicSans-Italic' },
bold: { file: file, font: 'PanicSans-Bold' },
bold_italic: { file: file, font: 'PanicSans-BoldItalic' },
}
pdf.font('Panic Sans', style: :italic)
pdf.text('In PanicSans-Italic')
text = PDF::Inspector::Text.analyze(pdf.render)
name = text.font_settings.map { |e| e[:name] }.first.to_s
name = name.sub(/\w+\+/, 'subset+')
expect(name).to eq('subset+PanicSans-Italic')
end
it 'allows font families to be defined in a single ttc' do
file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttc"
pdf.font_families['DejaVu Sans'] = {
normal: { file: file, font: 0 },
bold: { file: file, font: 1 },
}
pdf.font('DejaVu Sans', style: :bold)
pdf.text('In PanicSans-Bold')
text = PDF::Inspector::Text.analyze(pdf.render)
name = text.font_settings.map { |e| e[:name] }.first.to_s
name = name.sub(/\w+\+/, 'subset+')
expect(name).to eq('subset+DejaVuSans-Bold')
end
it 'allows fonts to be indexed by name in a ttc file' do
file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttc"
pdf.font_families['DejaVu Sans'] = {
normal: { file: file, font: 'DejaVu Sans' },
bold: { file: file, font: 'DejaVu Sans Bold' },
}
pdf.font('DejaVu Sans', style: :bold)
pdf.text('In PanicSans-Bold')
text = PDF::Inspector::Text.analyze(pdf.render)
name = text.font_settings.map { |e| e[:name] }.first.to_s
name = name.sub(/\w+\+/, 'subset+')
expect(name).to eq('subset+DejaVuSans-Bold')
end
it 'accepts Pathname objects for font files' do
file = Pathname.new("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
pdf.font_families['DejaVu Sans'] = {
normal: file,
}
pdf.font('DejaVu Sans')
pdf.text('In DejaVu Sans')
text = PDF::Inspector::Text.analyze(pdf.render)
name = text.font_settings.map { |e| e[:name] }.first.to_s
name = name.sub(/\w+\+/, 'subset+')
expect(name).to eq('subset+DejaVuSans')
end
it 'accepts IO objects for font files' do
File.open("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf") do |io|
pdf.font_families['DejaVu Sans'] = {
normal: described_class.load(pdf, io),
}
pdf.font('DejaVu Sans')
pdf.text('In DejaVu Sans')
text = PDF::Inspector::Text.analyze(pdf.render)
name = text.font_settings.map { |e| e[:name] }.first.to_s
name = name.sub(/\w+\+/, 'subset+')
expect(name).to eq('subset+DejaVuSans')
end
end
end
describe 'Transactional font handling' do
it 'allows setting of size directly when font is created' do
pdf.font('Courier', size: 16)
expect(pdf.font_size).to eq(16)
end
it 'allows temporary setting of a new font using a transaction' do
pdf.font('Helvetica', size: 12)
pdf.font('Courier', size: 16) do
expect(pdf.font.name).to eq('Courier')
expect(pdf.font_size).to eq(16)
end
expect(pdf.font.name).to eq('Helvetica')
expect(pdf.font_size).to eq(12)
end
it 'masks font size when using a transacation' do
pdf.font('Courier', size: 16) do
expect(pdf.font_size).to eq(16)
end
pdf.font('Times-Roman')
pdf.font('Courier')
expect(pdf.font_size).to eq(12)
end
end
describe 'Document#page_fonts' do
it 'registers fonts properly by page' do
pdf.font('Courier')
pdf.text('hello')
pdf.font('Helvetica')
pdf.text('hello')
pdf.font('Times-Roman')
pdf.text('hello')
%w[Courier Helvetica Times-Roman].each do |font|
expect(page_includes_font?(font)).to be true
end
pdf.start_new_page
pdf.font('Helvetica')
pdf.text('hello')
expect(page_includes_font?('Helvetica')).to be true
expect(page_includes_font?('Courier')).to be false
expect(page_includes_font?('Times-Roman')).to be false
end
def page_includes_font?(font)
pdf.page.fonts.values.map { |e| e.data[:BaseFont] }.include?(font.to_sym)
end
end
describe 'AFM fonts' do
let(:times) { pdf.find_font('Times-Roman') }
it 'calculates string width taking into account accented characters' do
input = win1252_string("\xE9") # é in win-1252
expect(times.compute_width_of(input, size: 12))
.to eq(times.compute_width_of('e', size: 12))
end
it 'calculates string width taking into account kerning pairs' do
expect(times.compute_width_of(win1252_string('To'), size: 12)).to eq(13.332)
expect(
times.compute_width_of(
win1252_string('To'),
size: 12,
kerning: true,
),
).to eq(12.372)
input = win1252_string("T\xF6") # Tö in win-1252
expect(times.compute_width_of(input, size: 12, kerning: true)).to eq(12.372)
end
it 'encodes text without kerning by default' do
expect(times.encode_text(win1252_string('To'))).to eq([[0, 'To']])
input = win1252_string("T\xE9l\xE9") # Télé in win-1252
expect(times.encode_text(input)).to eq([[0, input]])
expect(times.encode_text(win1252_string('Technology')))
.to eq([[0, 'Technology']])
expect(times.encode_text(win1252_string('Technology...')))
.to eq([[0, 'Technology...']])
end
it 'encodes text with kerning if requested' do
expect(times.encode_text(win1252_string('To'), kerning: true))
.to eq([[0, ['T', 80, 'o']]])
input = win1252_string("T\xE9l\xE9") # Télé in win-1252
output = win1252_string("\xE9l\xE9") # élé in win-1252
expect(times.encode_text(input, kerning: true))
.to eq([[0, ['T', 70, output]]])
expect(times.encode_text(win1252_string('Technology'), kerning: true))
.to eq([[0, ['T', 70, 'echnology']]])
expect(times.encode_text(win1252_string('Technology...'), kerning: true))
.to eq([[0, ['T', 70, 'echnology', 65, '...']]])
end
describe 'when normalizing encoding' do
it 'does not modify the original string when normalize_encoding() is used' do
original = 'Foo'
normalized = times.normalize_encoding(original)
expect(original.equal?(normalized)).to be false
end
end
it 'omits /Encoding for symbolic fonts' do
zapf = pdf.find_font('ZapfDingbats')
font_dict = zapf.__send__(:register, nil)
expect(font_dict.data[:Encoding]).to be_nil
end
end
describe '#glyph_present' do
it 'returns true when present in an AFM font' do
font = pdf.find_font('Helvetica')
expect(font.glyph_present?('H')).to be true
end
it 'returns false when absent in an AFM font' do
font = pdf.find_font('Helvetica')
expect(font.glyph_present?('再')).to be false
end
it 'returns true when present in a TTF font' do
font = pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
expect(font.glyph_present?('H')).to be true
end
it 'returns false when absent in a TTF font' do
font = pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
expect(font.glyph_present?('再')).to be false
font = pdf.find_font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
expect(font.glyph_present?('€')).to be false
end
end
describe 'TTF fonts' do
let(:font) { pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf") }
it 'calculates string width taking into account accented characters' do
expect(font.compute_width_of('é', size: 12)).to eq font.compute_width_of('e', size: 12)
end
it 'calculates string width taking into account kerning pairs' do
expect(font.compute_width_of('To', size: 12)).to be_within(0.01).of(14.65)
expect(font.compute_width_of('To', size: 12, kerning: true)).to be_within(0.01).of(12.61)
end
it 'encodes text without kerning by default' do
expect(font.encode_text('To')).to eq([[0, 'To']])
tele = "T\216l\216"
result = font.encode_text('Télé')
expect(result.length).to eq(1)
expect(result[0][0]).to eq(0)
expect(result[0][1].bytes.to_a).to eq(tele.bytes.to_a)
expect(font.encode_text('Technology')).to eq([[0, 'Technology']])
expect(font.encode_text('Technology...')).to eq([[0, 'Technology...']])
expect(font.encode_text('Teχnology...')).to eq(
[
[0, 'Te'],
[1, '!'], [0, 'nology...'],
],
)
end
it 'encodes text with kerning if requested' do
expect(font.encode_text('To', kerning: true)).to eq(
[
[0, ['T', 169.921875, 'o']],
],
)
expect(font.encode_text('Technology', kerning: true)).to eq(
[
[0, ['T', 169.921875, 'echnology']],
],
)
expect(font.encode_text('Technology...', kerning: true)).to eq(
[
[0, ['T', 169.921875, 'echnology', 142.578125, '...']],
],
)
expect(font.encode_text('Teχnology...', kerning: true)).to eq(
[
[0, ['T', 169.921875, 'e']],
[1, '!'],
[0, ['nology', 142.578125, '...']],
],
)
end
it 'uses the ascender, descender, and cap height from the TTF verbatim' do
# These metrics are relative to the font's own bbox. They should not be
# scaled with font size.
ref = pdf.ref!({})
font.__send__(:embed, ref, 0)
# Pull out the embedded font descriptor
descriptor = ref.data[:FontDescriptor].data
expect(descriptor[:Ascent]).to eq(759)
expect(descriptor[:Descent]).to eq(-240)
expect(descriptor[:CapHeight]).to eq(759)
end
describe 'when normalizing encoding' do
it 'does not modify the original string with normalize_encoding()' do
original = 'Foo'
normalized = font.normalize_encoding(original)
expect(original.equal?(normalized)).to be false
end
end
describe 'full font embedding' do
let(:font) { pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf", subset: false) }
let(:ref) { pdf.ref!({}).tap { |ref| font.__send__(:embed, ref, nil) } }
it 'is a composite font' do
font_obj = ref.data
expect(font_obj[:Subtype]).to eq(:Type0)
expect(font_obj[:DescendantFonts]).to be_an(Array)
expect(font_obj[:DescendantFonts].length).to eq(1)
desc_font = font_obj[:DescendantFonts].first.data
expect(desc_font[:Type]).to eq(:Font)
expect(desc_font[:Subtype]).to eq(:CIDFontType2)
end
it 'has proper metrics' do
descriptor = ref.data[:DescendantFonts].first.data[:FontDescriptor].data
expect(descriptor[:Ascent]).to eq(759)
expect(descriptor[:Descent]).to eq(-240)
expect(descriptor[:CapHeight]).to eq(759)
end
it 'has proper encoding' do
font_obj = ref.data
expect(font_obj[:Encoding]).to eq(:'Identity-H')
desc_font = font_obj[:DescendantFonts].first.data
expect(desc_font[:CIDToGIDMap]).to eq(:Identity)
end
it 'contains glyph widths' do
desc_font = ref.data[:DescendantFonts].first.data
expect(desc_font[:W]).to be_an(Array)
expect(desc_font[:W].length).to eq(2)
expect(desc_font[:W][0]).to eq(0)
expect(desc_font[:W][1]).to be_an(Array)
expect(desc_font[:W][1].length).to eq(6108) # All glyph metrics
end
it 'propely embeds font data' do
descriptor = ref.data[:DescendantFonts].first.data[:FontDescriptor].data
expect(descriptor).to have_key(:FontFile2)
expect(descriptor[:FontFile2].data[:Length1]).to eq(741_536)
expect(descriptor[:FontFile2].stream).to_not be_empty
end
end
end
describe 'OTF fonts' do
let(:font) { pdf.find_font("#{Prawn::DATADIR}/fonts/Bodoni-Book.otf") }
it 'calculates string width taking into account accented characters' do
expect(font.compute_width_of('é', size: 12)).to eq font.compute_width_of('e', size: 12)
end
it 'uses the ascender, descender, and cap height from the OTF verbatim' do
# These metrics are relative to the font's own bbox. They should not be
# scaled with font size.
ref = pdf.ref!({})
font.__send__(:embed, ref, 0)
# Pull out the embedded font descriptor
descriptor = ref.data[:FontDescriptor].data
expect(descriptor[:Ascent]).to eq(1023)
expect(descriptor[:Descent]).to eq(-200)
expect(descriptor[:CapHeight]).to eq(3072)
end
describe 'when normalizing encoding' do
it 'does not modify the original string with normalize_encoding()' do
original = 'Foo'
normalized = font.normalize_encoding(original)
expect(original).to_not equal(normalized)
end
end
describe 'full font embedding' do
let(:font) { pdf.find_font("#{Prawn::DATADIR}/fonts/Bodoni-Book.otf", subset: false) }
let(:ref) { pdf.ref!({}).tap { |ref| font.__send__(:embed, ref, nil) } }
it 'is a composite font' do
font_obj = ref.data
expect(font_obj[:Subtype]).to eq(:Type0)
expect(font_obj[:DescendantFonts]).to be_an(Array)
expect(font_obj[:DescendantFonts].length).to eq(1)
desc_font = font_obj[:DescendantFonts].first.data
expect(desc_font[:Type]).to eq(:Font)
expect(desc_font[:Subtype]).to eq(:CIDFontType0)
end
it 'has proper metrics' do
descriptor = ref.data[:DescendantFonts].first.data[:FontDescriptor].data
expect(descriptor[:Ascent]).to eq(1023)
expect(descriptor[:Descent]).to eq(-200)
expect(descriptor[:CapHeight]).to eq(3072)
end
it 'has proper encoding' do
font_obj = ref.data
expect(font_obj[:Encoding]).to eq(:'Identity-H')
desc_font = font_obj[:DescendantFonts].first.data
expect(desc_font).to_not have_key(:CIDToGIDMap)
end
it 'contains glyph widths' do
desc_font = ref.data[:DescendantFonts].first.data
expect(desc_font[:W]).to be_an(Array)
expect(desc_font[:W].length).to eq(2)
expect(desc_font[:W][0]).to eq(0)
expect(desc_font[:W][1]).to be_an(Array)
expect(desc_font[:W][1].length).to eq(353) # All glyph metrics
end
it 'propely embeds font data' do
descriptor = ref.data[:DescendantFonts].first.data[:FontDescriptor].data
expect(descriptor).to have_key(:FontFile3)
expect(descriptor[:FontFile3].stream).to_not be_empty
end
end
end
describe 'DFont fonts' do
let(:file) { "#{Prawn::DATADIR}/fonts/Panic+Sans.dfont" }
it 'lists all named fonts' do
list = Prawn::Fonts::DFont.named_fonts(file)
expect(list).to match_array(%w[PanicSans PanicSans-Bold PanicSans-BoldItalic PanicSans-Italic])
end
it 'counts the number of fonts in the file' do
expect(Prawn::Fonts::DFont.font_count(file)).to eq(4)
end
it 'defaults selected font to the first one if not specified' do
font = pdf.find_font(file)
expect(font.basename).to eq('PanicSans')
end
it 'allows font to be selected by index' do
font = pdf.find_font(file, font: 2)
expect(font.basename).to eq('PanicSans-Italic')
end
it 'allows font to be selected by name' do
font = pdf.find_font(file, font: 'PanicSans-BoldItalic')
expect(font.basename).to eq('PanicSans-BoldItalic')
end
it 'caches font object based on selected font' do
f1 = pdf.find_font(file, font: 'PanicSans')
f2 = pdf.find_font(file, font: 'PanicSans-Bold')
expect(f2.object_id).to_not eq(f1.object_id)
expect(pdf.find_font(file, font: 'PanicSans').object_id)
.to eq(f1.object_id)
expect(pdf.find_font(file, font: 'PanicSans-Bold').object_id)
.to eq(f2.object_id)
end
end
describe '#character_count(text)' do
it 'works on TTF fonts' do
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
expect(pdf.font.character_count('こんにちは世界')).to eq(7)
expect(pdf.font.character_count('Hello, world!')).to eq(13)
end
it 'works on AFM fonts' do
expect(pdf.font.character_count('Hello, world!')).to eq(13)
end
end
it 'properly caches fonts' do
pdf.font_families.update(
'DejaVu Sans' => {
normal: "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf",
bold: "#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf",
},
'Dustismo' => {
# This has to be the same font file as in the other family.
normal: "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf",
bold: "#{Prawn::DATADIR}/fonts/Dustismo_Roman.ttf",
},
)
pdf.font('DejaVu Sans')
pdf.font('Dustismo') do
pdf.text('Dustismo bold', style: :bold)
end
text = PDF::Inspector::Text.analyze(pdf.render)
font_name = text.font_settings.first[:name].to_s.sub(/\w+\+/, 'subset+')
expect(font_name).to eq 'subset+DustismoRoman'
end
it 'does not change width of unknown glyph' do
text_with_string_widths =
Class.new(PDF::Inspector::Text) do
attr_reader :string_widths
def initialize(*)
super
@string_widths = []
end
def show_text(text, kerned = false)
super
@string_widths << (@state.current_font.unpack(text).reduce(0) { |width, code|
width + (@state.current_font.glyph_width(code) * @font_settings[-1][:size] / 1000.0)
})
end
end
pdf =
Prawn::Document.new do
font_families.update(
'DejaVu Sans' => {
normal: "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf",
bold: "#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf",
},
)
# changing option to subset: false fixes issue (albeit using different behavior)
font('DejaVu Sans', subset: true) do
text('日本語end', inline_format: true)
end
end
rendered_pdf = pdf.render
analyzed_pdf = text_with_string_widths.analyze(rendered_pdf)
expect(analyzed_pdf.string_widths.length).to be 2
expect(analyzed_pdf.string_widths[0]).to be > 0.0
end
end
ruby-prawn-2.5.0.orig/spec/prawn/font_metric_cache_spec.rb 0000664 0000000 0000000 00000004063 14571572164 022356 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'pathname'
describe Prawn::FontMetricCache do
let(:document) { Prawn::Document.new }
let(:font_metric_cache) { described_class.new(document) }
it 'starts with an empty cache' do
expect(font_metric_cache.instance_variable_get(:@cache)).to be_empty
end
it 'caches the width of the provided string' do
font_metric_cache.width_of('M', {})
expect(font_metric_cache.instance_variable_get(:@cache).size).to eq(1)
end
it 'onlies cache a single copy of the same string' do
font_metric_cache.width_of('M', {})
font_metric_cache.width_of('M', {})
expect(font_metric_cache.instance_variable_get(:@cache).size).to eq(1)
end
it 'caches different copies for different strings' do
font_metric_cache.width_of('M', {})
font_metric_cache.width_of('W', {})
expect(font_metric_cache.instance_variable_get(:@cache).entries.size)
.to eq 2
end
it 'caches different copies of the same string with different font sizes' do
font_metric_cache.width_of('M', {})
document.font_size(24)
font_metric_cache.width_of('M', {})
expect(font_metric_cache.instance_variable_get(:@cache).entries.size)
.to eq 2
end
it 'caches different copies of the same string with different fonts' do
font_metric_cache.width_of('M', {})
document.font('Courier')
font_metric_cache.width_of('M', {})
expect(font_metric_cache.instance_variable_get(:@cache).entries.size)
.to eq 2
end
it 'does not use the cached width of a different font size' do
pdf =
Prawn::Document.new do
font('Helvetica', size: 42, style: :bold) do
text('First part M')
end
font('Helvetica', size: 12) do
text('First part M second part', inline_format: true)
text('First part W second part.', inline_format: true)
end
end
x_positions = PDF::Inspector::Text.analyze(pdf.render).positions.map(&:first)
expect(x_positions[2]).to be_within(3.0).of(x_positions[4])
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document_spec.rb 0000664 0000000 0000000 00000060374 14571572164 020547 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'tempfile'
describe Prawn::Document do
let(:pdf) { create_pdf }
describe '.new' do
it 'does not modify its argument' do
options = { page_layout: :landscape }
described_class.new(options)
expect(options).to eq(page_layout: :landscape)
end
end
describe 'The cursor' do
it 'equals pdf.y - bounds.absolute_bottom' do
pdf = described_class.new
expect(pdf.cursor).to eq(pdf.bounds.top)
pdf.y = 300
expect(pdf.cursor).to eq(pdf.y - pdf.bounds.absolute_bottom)
end
it 'is able to move relative to the bottom margin' do
pdf = described_class.new
pdf.move_cursor_to(10)
expect(pdf.cursor).to eq(10)
expect(pdf.y).to eq(pdf.cursor + pdf.bounds.absolute_bottom)
end
end
describe 'when generating a document with a custom text formatter' do
it 'uses the provided text formatter' do
text_formatter =
Class.new do
def self.format(string)
[
{
text: string.gsub('Dr. Who?', "Just 'The Doctor'."),
styles: [],
color: nil,
link: nil,
anchor: nil,
local: nil,
font: nil,
size: nil,
character_spacing: nil,
},
]
end
end
pdf = described_class.new(text_formatter: text_formatter)
pdf.text('Dr. Who?', inline_format: true)
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.first).to eq("Just 'The Doctor'.")
end
end
describe 'when generating a document from a subclass' do
it 'is an instance of the subclass' do
custom_document = Class.new(described_class)
custom_document.generate(Tempfile.new('generate_test').path) do |e|
expect(e.class).to eq(custom_document)
expect(e).to be_a(described_class)
end
end
it 'retains any extensions found on Prawn::Document' do
mod1 = Module.new { attr_reader :test_extensions1 }
mod2 = Module.new { attr_reader :test_extensions2 }
described_class.extensions << mod1 << mod2
custom_document = Class.new(described_class)
expect(custom_document.extensions).to eq([mod1, mod2])
# remove the extensions we added to prawn document
described_class.extensions.delete(mod1)
described_class.extensions.delete(mod2)
expect(described_class.new.respond_to?(:test_extensions1)).to be false
expect(described_class.new.respond_to?(:test_extensions2)).to be false
# verify these still exist on custom class
expect(custom_document.extensions).to eq([mod1, mod2])
expect(custom_document.new.respond_to?(:test_extensions1)).to be true
expect(custom_document.new.respond_to?(:test_extensions2)).to be true
end
end
describe 'When creating multi-page documents' do
it 'initializes with a single page' do
page_counter = PDF::Inspector::Page.analyze(pdf.render)
expect(page_counter.pages.size).to eq(1)
expect(pdf.page_count).to eq(1)
end
it 'provides an accurate page_count' do
3.times { pdf.start_new_page }
page_counter = PDF::Inspector::Page.analyze(pdf.render)
expect(page_counter.pages.size).to eq(4)
expect(pdf.page_count).to eq(4)
end
end
describe 'When beginning each new page' do
describe 'Background image feature' do
let(:filename) { "#{Prawn::DATADIR}/images/pigs.jpg" }
let(:pdf) { described_class.new(background: filename) }
it 'places a background image if it is in options block' do
output = pdf.render
images = PDF::Inspector::XObject.analyze(output)
# there should be 2 images in the page resources
expect(images.page_xobjects.first.size).to eq(1)
end
it 'places a background image interntally if it is in options block' do
expect(pdf.instance_variable_defined?(:@background)).to be(true)
expect(pdf.instance_variable_get(:@background)).to eq(filename)
end
end
end
describe '#float' do
it 'restores the original y-position' do
orig_y = pdf.y
pdf.float { pdf.text('Foo') }
expect(pdf.y).to eq(orig_y)
end
it 'teleports across pages if necessary' do
pdf.float do
pdf.text('Foo')
pdf.start_new_page
pdf.text('Bar')
end
pdf.text('Baz')
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
expect(pages[0][:strings]).to eq(%w[Foo Baz])
expect(pages[1][:strings]).to eq(['Bar'])
end
end
describe '#start_new_page' do
it "doesn't modify the options hash" do
expect {
described_class.new.start_new_page({ margin: 0 }.freeze)
}.to_not raise_error
end
it 'sets individual page margins' do
doc = described_class.new
doc.start_new_page(top_margin: 42)
expect(doc.page.margins[:top]).to eq(42)
end
end
describe '#delete_page(index)' do
before do
pdf.text('Page one')
pdf.start_new_page
pdf.text('Page two')
pdf.start_new_page
pdf.text('Page three')
end
it 'destroy a specific page of the document' do
pdf.delete_page(1)
expect(pdf.page_number).to eq(2)
text_analysis = PDF::Inspector::Text.analyze(pdf.render)
expect(text_analysis.strings).to eq(['Page one', 'Page three'])
end
it 'destroy the last page of the document' do
pdf.delete_page(-1)
expect(pdf.page_number).to eq(2)
text_analysis = PDF::Inspector::Text.analyze(pdf.render)
expect(text_analysis.strings).to eq(['Page one', 'Page two'])
end
context 'with an invalid index' do
let(:expected_content) { ['Page one', 'Page two', 'Page three'] }
it 'does not destroy an invalid positve index' do
pdf.delete_page(42)
expect(pdf.page_number).to eq(3)
text_analysis = PDF::Inspector::Text.analyze(pdf.render)
expect(text_analysis.strings).to eq(expected_content)
end
it 'does not destroy an invalid negative index' do
pdf.delete_page(-42)
expect(pdf.page_number).to eq(3)
text_analysis = PDF::Inspector::Text.analyze(pdf.render)
expect(text_analysis.strings).to eq(expected_content)
end
end
end
describe '#page_number' do
it 'is 1 for a new document' do
pdf = described_class.new
expect(pdf.page_number).to eq(1)
end
it 'is 0 for documents with no pages' do
pdf = described_class.new(skip_page_creation: true)
expect(pdf.page_number).to eq(0)
end
it 'is changed by go_to_page' do
pdf = described_class.new
10.times { pdf.start_new_page }
pdf.go_to_page(3)
expect(pdf.page_number).to eq(3)
end
end
describe 'on_page_create callback' do
it 'is delegated from Document to renderer' do
expect(pdf.respond_to?(:on_page_create)).to be true
end
it 'is invoked with document' do
called_with = nil
pdf.renderer.on_page_create { |*args| called_with = args }
pdf.start_new_page
expect(called_with).to eq([pdf])
end
it 'is invoked for each new page' do
trigger = instance_spy(Proc, 'trigger')
allow(trigger).to receive(:call)
pdf.renderer.on_page_create { trigger.call }
5.times { pdf.start_new_page }
expect(trigger).to have_received(:call).exactly(5).times
end
it 'is replaceable' do
trigger1 = instance_spy(Proc, 'trigger 1')
allow(trigger1).to receive(:call)
trigger2 = instance_spy(Proc, 'trigger 2')
allow(trigger2).to receive(:call)
pdf.renderer.on_page_create { trigger1.call }
pdf.start_new_page
pdf.renderer.on_page_create { trigger2.call }
pdf.start_new_page
expect(trigger1).to have_received(:call).once
expect(trigger2).to have_received(:call).once
end
it 'is clearable by calling on_page_create without a block' do
trigger = instance_spy(Proc, 'trigger')
allow(trigger).to receive(:call)
pdf.renderer.on_page_create { trigger.call }
pdf.start_new_page
pdf.renderer.on_page_create
pdf.start_new_page
expect(trigger).to have_received(:call).once
end
end
describe 'compression' do
it 'does not compress the page content stream if compression is disabled' do
pdf = described_class.new(compress: false)
allow(pdf.page.content.stream).to receive(:compress!).and_return(true)
pdf.text('Hi There' * 20)
pdf.render
expect(pdf.page.content.stream).to_not have_received(:compress!)
end
it 'compresses the page content stream if compression is enabled' do
pdf = described_class.new(compress: true)
allow(pdf.page.content.stream).to receive(:compress!).and_return(true)
pdf.text('Hi There' * 20)
pdf.render
expect(pdf.page.content.stream).to have_received(:compress!).once
end
it 'results in a smaller file size when compressed' do
doc_uncompressed = described_class.new
doc_compressed = described_class.new(compress: true)
[doc_compressed, doc_uncompressed].each do |pdf|
pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
pdf.text('更可怕的是,同质化竞争对手可以按照URL中后面这个ID来遍历' * 10)
end
expect(doc_compressed.render.length).to be <
doc_uncompressed.render.length
end
end
describe 'Dometadata' do
it 'outputs strings as UTF-16 with a byte order mark' do
pdf = described_class.new(info: { Author: 'Lóránt' })
expect(pdf.state.store.info.object).to match(
# UTF-16: BOM L ó r á n t
%r{/Author\s*}i,
)
end
end
describe 'When reopening pages' do
it 'modifies the content stream size' do
pdf =
described_class.new do
text('Page 1')
start_new_page
text('Page 2')
go_to_page(1)
text('More for page 1')
end
# Indirectly verify that the actual length does not match dictionary
# length. If it isn't, a MalformedPDFError will be raised
expect {
PDF::Inspector::Page.analyze(pdf.render)
}.to_not raise_error
end
it 'inserts pages after the current page when calling start_new_page' do
pdf = described_class.new
3.times do |i|
pdf.text("Old page #{i + 1}")
pdf.start_new_page
end
pdf.go_to_page(1)
pdf.start_new_page
pdf.text('New page 2')
expect(pdf.page_number).to eq(2)
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(5)
expect(pages[1][:strings]).to eq(['New page 2'])
expect(pages[2][:strings]).to eq(['Old page 2'])
end
it 'restores the layout of the page' do
doc =
described_class.new do
start_new_page(layout: :landscape)
end
lsize = [doc.bounds.width, doc.bounds.height]
expect([doc.bounds.width, doc.bounds.height]).to eq lsize
doc.go_to_page(1)
expect([doc.bounds.width, doc.bounds.height]).to eq lsize.reverse
end
it 'restores the margin box of the page' do
doc = described_class.new(margin: [100, 100])
page1_bounds = doc.bounds
doc.start_new_page(margin: [200, 200])
expect([doc.bounds.width, doc.bounds.height]).to eq([page1_bounds.width - 200, page1_bounds.height - 200])
doc.go_to_page(1)
expect(doc.bounds.width).to eq page1_bounds.width
expect(doc.bounds.height).to eq page1_bounds.height
end
end
describe 'When setting page size' do
it 'defaults to LETTER' do
pdf = described_class.new
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.first[:size]).to eq(PDF::Core::PageGeometry::SIZES['LETTER'])
end
(PDF::Core::PageGeometry::SIZES.keys - ['LETTER']).each do |k|
it "provides #{k} geometry" do
pdf = described_class.new(page_size: k)
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.first[:size]).to eq(PDF::Core::PageGeometry::SIZES[k])
end
end
it 'allows custom page size' do
pdf = described_class.new(page_size: [1920, 1080])
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.first[:size]).to eq([1920, 1080])
end
it 'retains page size by default when starting a new page' do
pdf = described_class.new(page_size: 'LEGAL')
pdf.start_new_page
pages = PDF::Inspector::Page.analyze(pdf.render).pages
pages.each do |page|
expect(page[:size]).to eq(PDF::Core::PageGeometry::SIZES['LEGAL'])
end
end
end
describe 'When setting page layout' do
it 'reverses coordinates for landscape' do
pdf = described_class.new(page_size: 'A4', page_layout: :landscape)
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.first[:size]).to eq(PDF::Core::PageGeometry::SIZES['A4'].reverse)
end
it 'retains page layout by default when starting a new page' do
pdf = described_class.new(page_layout: :landscape)
pdf.start_new_page(trace: true)
pages = PDF::Inspector::Page.analyze(pdf.render).pages
pages.each do |page|
expect(page[:size]).to eq(PDF::Core::PageGeometry::SIZES['LETTER'].reverse)
end
end
it 'swaps the bounds when starting a new page with different layout' do
pdf = described_class.new
size = [pdf.bounds.width, pdf.bounds.height]
pdf.start_new_page(layout: :landscape)
expect([pdf.bounds.width, pdf.bounds.height]).to eq(size.reverse)
end
end
describe '#mask' do
it 'allows transactional restoration of attributes' do
pdf = described_class.new
y = pdf.y
line_width = pdf.line_width
pdf.mask(:y, :line_width) do
pdf.y = y + 1
pdf.line_width = line_width + 1
expect(pdf.y).to_not eq(y)
expect(pdf.line_width).to_not eq(line_width)
end
expect(pdf.y).to eq(y)
expect(pdf.line_width).to eq(line_width)
end
end
describe '#render' do
it 'returns a 8 bit encoded string on a m17n aware VM' do
pdf = described_class.new(page_size: 'A4', page_layout: :landscape)
pdf.line([100, 100], [200, 200])
str = pdf.render
expect(str.encoding.to_s).to eq('ASCII-8BIT')
end
it 'triggers before_render callbacks just before rendering' do
pdf = described_class.new
# Verify the order: finalize -> fire callbacks -> render body
allow(pdf.renderer).to receive(:finalize_all_page_contents)
.and_call_original
trigger = instance_spy(Proc, 'trigger')
allow(trigger).to receive(:call)
pdf.renderer.before_render { trigger.call }
allow(pdf.renderer).to receive(:render_body).and_call_original
pdf.render(StringIO.new)
expect(pdf.renderer).to have_received(:finalize_all_page_contents).ordered
expect(trigger).to have_received(:call).ordered
expect(pdf.renderer).to have_received(:render_body).ordered
end
it 'is idempotent' do
pdf = described_class.new
contents = pdf.render
contents2 = pdf.render
expect(contents2).to eq(contents)
end
end
describe 'PDF file versions' do
it 'defaults to 1.3' do
pdf = described_class.new
str = pdf.render
expect(str[0, 8]).to eq('%PDF-1.3')
end
it 'allows the default to be changed' do
pdf = described_class.new
pdf.renderer.min_version(1.4)
str = pdf.render
expect(str[0, 8]).to eq('%PDF-1.4')
end
end
describe '#go_to_page' do
it 'has 2 pages after calling start_new_page and go_to_page' do
pdf = described_class.new
pdf.text('James')
pdf.start_new_page
pdf.text('Anthony')
pdf.go_to_page(1)
pdf.text('Healy')
page_counter = PDF::Inspector::Page.analyze(pdf.render)
expect(page_counter.pages.size).to eq(2)
end
it 'correctlies add text to pages' do
pdf = described_class.new
pdf.text('James')
pdf.start_new_page
pdf.text('Anthony')
pdf.go_to_page(1)
pdf.text('Healy')
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.strings.size).to eq(3)
expect(text.strings.include?('James')).to be(true)
expect(text.strings.include?('Anthony')).to be(true)
expect(text.strings.include?('Healy')).to be(true)
end
end
describe 'content stream characteristics' do
it 'has 1 single content stream for a single page PDF' do
pdf = described_class.new
pdf.text('James')
output = StringIO.new(pdf.render)
hash = PDF::Reader::ObjectHash.new(output)
streams = hash.values.select { |obj| obj.is_a?(PDF::Reader::Stream) }
expect(streams.size).to eq(1)
end
it 'has 1 single content stream for a single page PDF, even if go_to_page is used' do
pdf = described_class.new
pdf.text('James')
pdf.go_to_page(1)
pdf.text('Healy')
output = StringIO.new(pdf.render)
hash = PDF::Reader::ObjectHash.new(output)
streams = hash.values.select { |obj| obj.is_a?(PDF::Reader::Stream) }
expect(streams.size).to eq(1)
end
end
describe '#number_pages' do
let(:pdf) { described_class.new(skip_page_creation: true) }
it "replaces the '' string with the proper page number" do
pdf.start_new_page
allow(pdf).to receive(:text_box)
pdf.number_pages(', test', page_filter: :all)
expect(pdf).to have_received(:text_box).with('1, test', height: 50)
end
it "replaces the '' string with the total page count" do
pdf.start_new_page
allow(pdf).to receive(:text_box)
pdf.number_pages('test, ', page_filter: :all)
expect(pdf).to have_received(:text_box).with('test, 1', height: 50)
end
it 'must print each page if given the :all page_filter' do
10.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages('test', page_filter: :all)
expect(pdf).to have_received(:text_box).exactly(10).times
end
it 'must print each page if no :page_filter is specified' do
10.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages('test')
expect(pdf).to have_received(:text_box).exactly(10).times
end
it 'must not print the page number if given a nil filter' do
10.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages('test', page_filter: nil)
expect(pdf).to_not have_received(:text_box)
end
[1, 2].each do |startat|
context "with start_count_at option equal to #{startat}" do
it 'increments the pages' do
2.times { pdf.start_new_page }
options = { page_filter: :all, start_count_at: startat }
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', options)
expect(pdf).to have_received(:text_box)
.with("#{startat} 2", height: 50)
expect(pdf).to have_received(:text_box)
.with("#{startat + 1} 2", height: 50)
end
end
end
[0, nil].each do |val|
context "with start_count_at option equal to #{val}" do
it 'defaults to start at page 1' do
3.times { pdf.start_new_page }
options = { page_filter: :all, start_count_at: val }
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', options)
expect(pdf).to have_received(:text_box).with('1 3', height: 50)
expect(pdf).to have_received(:text_box).with('2 3', height: 50)
expect(pdf).to have_received(:text_box).with('3 3', height: 50)
end
end
end
context 'with total_pages option' do
it 'allows the total pages count to be overridden' do
2.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', page_filter: :all, total_pages: 10)
expect(pdf).to have_received(:text_box).with('1 10', height: 50)
expect(pdf).to have_received(:text_box).with('2 10', height: 50)
end
end
context 'with special page filter' do
describe 'such as :odd' do
it 'increments the pages' do
3.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', page_filter: :odd)
expect(pdf).to have_received(:text_box).with('1 3', height: 50)
expect(pdf).to have_received(:text_box).with('3 3', height: 50)
expect(pdf).to_not have_received(:text_box).with('2 3', height: 50)
end
end
describe 'missing' do
it 'does not print any page numbers' do
3.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', page_filter: nil)
expect(pdf).to_not have_received(:text_box)
end
end
end
context 'with both a special page filter and a start_count_at parameter' do
describe 'such as :odd and 7' do
it 'increments the pages' do
3.times { pdf.start_new_page }
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', page_filter: :odd, start_count_at: 5)
expect(pdf).to_not have_received(:text_box).with('1 3', height: 50)
# page 1
expect(pdf).to have_received(:text_box).with('5 3', height: 50)
# page 2
expect(pdf).to_not have_received(:text_box).with('6 3', height: 50)
# page 3
expect(pdf).to have_received(:text_box).with('7 3', height: 50)
end
end
context 'with some crazy proc and 2' do
it 'increments the pages' do
6.times { pdf.start_new_page }
options = {
page_filter: ->(p) { p != 2 && p != 5 },
start_count_at: 4,
}
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', options)
# page 1
expect(pdf).to have_received(:text_box).with('4 6', height: 50)
# page 2
expect(pdf).to_not have_received(:text_box).with('5 6', height: 50)
# page 3
expect(pdf).to have_received(:text_box).with('6 6', height: 50)
# page 4
expect(pdf).to have_received(:text_box).with('7 6', height: 50)
# page 5
expect(pdf).to_not have_received(:text_box).with('8 6', height: 50)
# page 6
expect(pdf).to have_received(:text_box).with('9 6', height: 50)
end
end
end
describe 'height option' do
before do
pdf.start_new_page
end
it 'with 10 height' do
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', height: 10)
expect(pdf).to have_received(:text_box).with('1 1', height: 10)
end
it 'with nil height' do
allow(pdf).to receive(:text_box)
pdf.number_pages(' ', height: nil)
expect(pdf).to have_received(:text_box).with('1 1', height: nil)
end
it 'with no height' do
allow(pdf).to receive(:text_box)
pdf.number_pages(' ')
expect(pdf).to have_received(:text_box).with('1 1', height: 50)
end
end
end
describe '#page_match?' do
let(:pdf) do
described_class.new(skip_page_creation: true) do |pdf|
10.times { pdf.start_new_page }
end
end
it 'returns nil given no filter' do
expect(pdf).to_not be_page_match(:nil, 1)
end
it 'must provide an :all filter' do
expect((1..pdf.page_count).all? { |i| pdf.page_match?(:all, i) })
.to be true
end
it 'must provide an :odd filter' do
odd, even = (1..pdf.page_count).partition(&:odd?)
expect(odd.all? { |i| pdf.page_match?(:odd, i) }).to be true
expect(even).to_not(be_any { |i| pdf.page_match?(:odd, i) })
end
it 'must be able to filter by an array of page numbers' do
fltr = [1, 2, 7]
expect((1..10).select { |i| pdf.page_match?(fltr, i) }).to eq([1, 2, 7])
end
it 'must be able to filter by a range of page numbers' do
fltr = 2..4
expect((1..10).select { |i| pdf.page_match?(fltr, i) }).to eq([2, 3, 4])
end
it 'must be able to filter by an arbitrary proc' do
fltr = ->(x) { x == 1 || (x % 3).zero? }
expect((1..10).select { |i| pdf.page_match?(fltr, i) })
.to eq([1, 3, 6, 9])
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document_span_spec.rb 0000664 0000000 0000000 00000001764 14571572164 021566 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document do
let(:pdf) { create_pdf }
it 'onlies accept :position as option in debug mode' do
Prawn.debug = true
expect {
pdf.span(350, x: 3) do
# draw
end
}.to raise_error(Prawn::Errors::UnknownOption)
end
it 'has raise an error if :position is invalid' do
expect {
pdf.span(350, position: :x) do
# draw
end
}.to raise_error(ArgumentError)
end
it 'restores the margin box when bounding box exits' do
margin_box = pdf.bounds
pdf.span(350, position: :center) do
pdf.text("Here's some centered text in a 350 point column. " * 100)
end
expect(pdf.bounds).to eq(margin_box)
end
it 'does create a margin box' do
margin_box =
pdf.span(350, position: :center) {
pdf.text("Here's some centered text in a 350 point column. " * 100)
}
expect(margin_box.top).to eq(792.0)
expect(margin_box.bottom).to eq(0)
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document_reference_spec.rb 0000664 0000000 0000000 00000001344 14571572164 022555 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document do
describe 'A Reference object' do
describe 'generated via Prawn::Document' do
it 'returns a proper reference on ref!' do
pdf = described_class.new
expect(pdf.ref!({}).is_a?(PDF::Core::Reference)).to be(true)
end
it 'returns an identifier on ref' do
pdf = described_class.new
r = pdf.ref({})
expect(r.is_a?(Integer)).to be(true)
end
it 'has :Length of stream if it has one when compression disabled' do
pdf = described_class.new(compress: false)
ref = pdf.ref!({})
ref << 'Hello'
expect(ref.stream.data[:Length]).to eq(5)
end
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document_grid_spec.rb 0000664 0000000 0000000 00000006600 14571572164 021544 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document do
describe 'grid' do
let(:pdf) { described_class.new }
it 'allows definition of a grid' do
pdf.define_grid(columns: 5, rows: 8, gutter: 0.1)
expect(pdf.grid.columns).to eq(5)
expect(pdf.grid.rows).to eq(8)
expect(pdf.grid.gutter).to eq(0.1)
end
it 'allows re-definition of a grid' do
pdf.define_grid(columns: 5, rows: 8, gutter: 0.1)
expect(pdf.grid.columns).to eq(5)
expect(pdf.grid.rows).to eq(8)
expect(pdf.grid.gutter).to eq(0.1)
pdf.define_grid(columns: 3, rows: 6, gutter: 0.1)
expect(pdf.grid.columns).to eq(3)
expect(pdf.grid.rows).to eq(6)
expect(pdf.grid.gutter).to eq(0.1)
end
describe 'when a grid is defined' do
let(:num_columns) { 5 }
let(:num_rows) { 8 }
let(:gutter) { 10.0 }
before do
pdf.define_grid(
columns: num_columns,
rows: num_rows,
gutter: gutter,
)
end
it 'computes the column width' do
expect((pdf.grid.column_width * Float(num_columns)) + (gutter * Float((num_columns - 1))))
.to eq(pdf.bounds.width)
end
it 'computes the row height' do
expect((pdf.grid.row_height * Float(num_rows)) + (gutter * Float((num_rows - 1)))).to eq(pdf.bounds.height)
end
it 'gives the edges of a grid box' do
grid_width = (Float(pdf.bounds.width) -
(gutter * Float((num_columns - 1)))) / Float(num_columns)
grid_height = (Float(pdf.bounds.height) -
(gutter * Float((num_rows - 1)))) / Float(num_rows)
exp_tl_x = (grid_width + Float(gutter)) * 4.0
exp_tl_y = Float(pdf.bounds.height) - (grid_height + Float(gutter))
expect(pdf.grid(1, 4).top_left).to eq([exp_tl_x, exp_tl_y])
expect(pdf.grid(1, 4).top_right).to eq [exp_tl_x + grid_width, exp_tl_y]
expect(pdf.grid(1, 4).bottom_left)
.to eq([exp_tl_x, exp_tl_y - grid_height])
expect(pdf.grid(1, 4).bottom_right)
.to eq([exp_tl_x + grid_width, exp_tl_y - grid_height])
end
it 'gives the edges of a multiple grid boxes' do
# Hand verified. Cheating a bit. Don't tell.
expect(pdf.grid([1, 3], [2, 5]).top_left).to eq([330.0, 628.75])
expect(pdf.grid([1, 3], [2, 5]).top_right).to eq([650.0, 628.75])
expect(pdf.grid([1, 3], [2, 5]).bottom_left).to eq([330.0, 456.25])
expect(pdf.grid([1, 3], [2, 5]).bottom_right).to eq([650.0, 456.25])
end
it 'draws outlines without changing global default colors' do
pdf.grid.show_all('cccccc')
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.fill_color).to_not eq([0.8, 0.8, 0.8])
expect(colors.stroke_color).to_not eq([0.8, 0.8, 0.8])
# Hardcoded default color as I haven't been able to come up with
# a stable converter between fill_color without lots code.
expect(colors.stroke_color).to eq([0.0, 0.0, 0.0])
end
it 'draws outlines without curent color settings' do
pdf.fill_color('ccff00')
pdf.stroke_color('ffcc00')
pdf.grid.show_all
colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
expect(colors.fill_color).to eq([0.8, 1.0, 0.0])
expect(colors.stroke_color).to eq([1.0, 0.8, 0.0])
end
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document_destinations_spec.rb 0000664 0000000 0000000 00000000533 14571572164 023322 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document do
describe 'When creating destinations' do
let(:pdf) { create_pdf }
it 'adds entry to Dests name tree' do
expect(pdf.dests.data.empty?).to be(true)
pdf.add_dest('candy', 'chocolate')
expect(pdf.dests.data.size).to eq(1)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document_annotations_spec.rb 0000664 0000000 0000000 00000004056 14571572164 023157 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document do
let(:pdf) { create_pdf }
describe 'When creating annotations' do
it 'appends annotation to current page' do
pdf.start_new_page
pdf.annotate(
Rect: [0, 0, 10, 10],
Subtype: :Text,
Contents: 'Hello world!',
)
PDF::Reader.open(StringIO.new(pdf.render)) do |pdf|
expect(pdf.page(1).attributes[:Annots]).to be_nil
expect(pdf.page(2).attributes[:Annots].size).to eq(1)
end
end
it 'forces :Type to be :Annot' do
opts = pdf.annotate(
Rect: [0, 0, 10, 10],
Subtype: :Text,
Contents: 'Hello world!',
)
expect(opts[:Type]).to eq(:Annot)
opts = pdf.annotate(
Type: :Bogus,
Rect: [0, 0, 10, 10],
Subtype: :Text,
Contents: 'Hello world!',
)
expect(opts[:Type]).to eq(:Annot)
end
end
describe 'When creating text annotations' do
let(:rect) { [0, 0, 10, 10] }
let(:content) { 'Hello, world!' }
it 'builds appropriate annotation' do
opts = pdf.text_annotation(rect, content)
expect(opts[:Type]).to eq(:Annot)
expect(opts[:Subtype]).to eq(:Text)
expect(opts[:Rect]).to eq(rect)
expect(opts[:Contents]).to eq(content)
end
it 'merges extra options' do
opts = pdf.text_annotation(rect, content, Open: true, Subtype: :Bogus)
expect(opts[:Subtype]).to eq(:Text)
expect(opts[:Open]).to be(true)
end
end
describe 'When creating link annotations' do
let(:rect) { [0, 0, 10, 10] }
let(:dest) { 'home' }
it 'builds appropriate annotation' do
opts = pdf.link_annotation(rect, Dest: dest)
expect(opts[:Type]).to eq(:Annot)
expect(opts[:Subtype]).to eq(:Link)
expect(opts[:Rect]).to eq(rect)
expect(opts[:Dest]).to eq(dest)
end
it 'merges extra options' do
opts = pdf.link_annotation(rect, Dest: dest, Subtype: :Bogus)
expect(opts[:Subtype]).to eq(:Link)
expect(opts[:Dest]).to eq(dest)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document/ 0000775 0000000 0000000 00000000000 14571572164 017176 5 ustar root root ruby-prawn-2.5.0.orig/spec/prawn/document/security_spec.rb 0000664 0000000 0000000 00000012665 14571572164 022416 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
require 'tempfile'
describe Prawn::Document::Security do
describe 'Password padding' do
include described_class
it 'truncates long passwords' do
pw = 'Long long string' * 30
padded = pad_password(pw)
expect(padded.length).to eq(32)
expect(padded).to eq(pw[0, 32])
end
it 'pads short passwords' do
pw = 'abcd'
padded = pad_password(pw)
expect(padded.length).to eq(32)
expect(padded).to eq(pw + Prawn::Document::Security::PASSWORD_PADDING[0, 28])
end
it 'fullies pad null passwords' do
pw = ''
padded = pad_password(pw)
expect(padded.length).to eq(32)
expect(padded).to eq(Prawn::Document::Security::PASSWORD_PADDING)
end
end
describe 'Setting permissions' do
def doc_with_permissions(permissions)
pdf = Prawn::Document.new
# Make things easier to test
pdf.singleton_class.__send__(:public, :permissions_value)
# class << pdf
# public :permissions_value
# end
pdf.encrypt_document(permissions: permissions)
pdf
end
it 'defaults to full permissions' do
expect(doc_with_permissions({}).permissions_value).to eq(0xFFFFFFFF)
expect(
doc_with_permissions(
print_document: true,
modify_contents: true,
copy_contents: true,
modify_annotations: true,
).permissions_value,
)
.to eq(0xFFFFFFFF)
end
it 'clears the appropriate bits for each permission flag' do
expect(doc_with_permissions(print_document: false).permissions_value)
.to eq(0b1111_1111_1111_1111_1111_1111_1111_1011)
expect(doc_with_permissions(modify_contents: false).permissions_value)
.to eq(0b1111_1111_1111_1111_1111_1111_1111_0111)
expect(doc_with_permissions(copy_contents: false).permissions_value)
.to eq(0b1111_1111_1111_1111_1111_1111_1110_1111)
expect(doc_with_permissions(modify_annotations: false).permissions_value)
.to eq(0b1111_1111_1111_1111_1111_1111_1101_1111)
end
it 'raise_errors ArgumentError if invalid option is provided' do
expect {
doc_with_permissions(modify_document: false)
}.to raise_error(ArgumentError)
end
end
describe 'Encryption keys' do
# Since PDF::Reader doesn't read encrypted PDF files, we just take the
# roundabout method of verifying each step of the encryption. This works
# fine because the encryption method is deterministic.
let(:pdf) do
Prawn::Document.new do |pdf|
class << pdf
public :owner_password_hash, :user_password_hash, :user_encryption_key
end
pdf.encrypt_document(
user_password: 'foo',
owner_password: 'bar',
permissions: { print_document: false },
)
end
end
it 'calculates the correct owner hash' do
expect(pdf.owner_password_hash.unpack1('H*'))
.to match(/^61CA855012/i)
end
it 'calculates the correct user hash' do
expect(pdf.user_password_hash.unpack1('H*'))
.to match(/^6BC8C51031/i)
end
it 'calculates the correct user_encryption_key' do
expect(pdf.user_encryption_key.unpack1('H*').upcase)
.to eq('B100AB6429')
end
end
describe 'encrypted_pdf_object' do
it 'delegates to PdfObject for simple types' do
expect(PDF::Core.encrypted_pdf_object(true, nil, nil, nil)).to eq('true')
expect(PDF::Core.encrypted_pdf_object(42, nil, nil, nil)).to eq('42')
end
it 'encrypts strings properly' do
expect(PDF::Core.encrypted_pdf_object('foo', '12345', 123, 0))
.to eq('<4ad6e3>')
end
it 'encrypts literal strings properly' do
expect(
PDF::Core.encrypted_pdf_object(
PDF::Core::LiteralString.new('foo'), '12345', 123, 0,
),
).to eq(bin_string("(J\xD6\xE3)"))
expect(
PDF::Core.encrypted_pdf_object(
PDF::Core::LiteralString.new("\xAF\xC5fh\x9A\x14\x97,\xD3,\x06\x87\xCDSS"), nil, 123, 0,
),
).to eq(
bin_string("(2&\\(\x02P\x92\x9C\e\xAF\\)\\r\x83\x94\x11\x0F)"),
)
end
it 'encrypts time properly' do
expect(
PDF::Core.encrypted_pdf_object(
Time.utc(10_002, 0o4, 26, 10, 17, 10), '12345', 123, 0,
),
).to eq bin_string("(h\x83\xBD\xDC\xE9\x99\\r\xD3/!\x14\xD5%\xBE\xF6\x17\xA3\x9B\xC5\xFE&+\xD8\x93)")
end
it 'properlies handle compound types' do
expect(PDF::Core.encrypted_pdf_object({ Bar: 'foo' }, '12345', 123, 0))
.to eq("<< /Bar <4ad6e3>\n>>")
expect(PDF::Core.encrypted_pdf_object(%w[foo bar], '12345', 123, 0))
.to eq('[<4ad6e3> <4ed8fe>]')
end
end
describe 'Reference#encrypted_object' do
it 'encrypts references properly' do
ref = PDF::Core::Reference.new(1, ['foo'])
expect(ref.encrypted_object(nil)).to eq("1 0 obj\n[<4fca3f>]\nendobj\n")
end
it 'encrypts references with streams properly' do
ref = PDF::Core::Reference.new(1, {})
ref << 'foo'
result = bin_string("1 0 obj\n<< /Length 3\n>>\nstream\nO\xCA?\nendstream\nendobj\n")
expect(ref.encrypted_object(nil)).to eq(result)
end
end
describe 'String#encrypted_object' do
it 'encrypts stream properly' do
stream = PDF::Core::Stream.new
stream << 'foo'
result = bin_string("stream\nO\xCA?\nendstream\n")
expect(stream.encrypted_object(nil, 1, 0)).to eq(result)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document/column_box_spec.rb 0000664 0000000 0000000 00000003504 14571572164 022704 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document::ColumnBox do
let(:pdf) { create_pdf }
it 'has sensible left and right values' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
height: 200,
columns: 3,
spacer: 25,
) do
left = pdf.bounds.left
right = pdf.bounds.right
pdf.bounds.move_past_bottom # next column
expect(pdf.bounds.left).to be > left
expect(pdf.bounds.left).to be > right
expect(pdf.bounds.right).to be > pdf.bounds.left
end
end
it 'includes spacers between columns but not at the end' do
pdf.column_box(
[0, pdf.cursor],
width: 500,
height: 200,
columns: 3,
spacer: 25,
) do
expect(pdf.bounds.width).to eq(150) # (500 - (25 * 2)) / 3
pdf.bounds.move_past_bottom
pdf.bounds.move_past_bottom
expect(pdf.bounds.right).to eq(500)
end
end
it 'does not reset the top margin on a new page by default' do
page_top = pdf.cursor
pdf.move_down(50)
init_column_top = pdf.cursor
pdf.column_box([0, pdf.cursor], width: 500, height: 200, columns: 2) do
pdf.bounds.move_past_bottom
pdf.bounds.move_past_bottom
expect(pdf.bounds.absolute_top).to eq(init_column_top)
expect(pdf.bounds.absolute_top).to_not eq(page_top)
end
end
it 'does reset the top margin when reflow_margins is set' do
page_top = pdf.cursor
pdf.move_down(50)
init_column_top = pdf.cursor
pdf.column_box(
[0, pdf.cursor],
width: 500,
reflow_margins: true,
height: 200,
columns: 2,
) do
pdf.bounds.move_past_bottom
pdf.bounds.move_past_bottom
expect(pdf.bounds.absolute_top).to eq(page_top)
expect(pdf.bounds.absolute_top).to_not eq(init_column_top)
end
end
end
ruby-prawn-2.5.0.orig/spec/prawn/document/bounding_box_spec.rb 0000664 0000000 0000000 00000036555 14571572164 023230 0 ustar root root # frozen_string_literal: true
require 'spec_helper'
describe Prawn::Document::BoundingBox do
let(:pdf) { create_pdf }
describe 'attributes' do
let(:box) do
described_class.new(
nil,
nil,
[100, 125],
width: 50,
height: 75,
)
end
it 'has an anchor at (x, y - height)' do
expect(box.anchor).to eq([100, 50])
end
it 'has a left boundary of 0' do
expect(box.left).to eq(0)
end
it 'has a right boundary equal to the width' do
expect(box.right).to eq(50)
end
it 'has a top boundary of height' do
expect(box.top).to eq(75)
end
it 'has a bottom boundary of 0' do
expect(box.bottom).to eq(0)
end
it 'has a top-left of [0, height]' do
expect(box.top_left).to eq([0, 75])
end
it 'has a top-right of [width, height]' do
expect(box.top_right).to eq([50, 75])
end
it 'has a bottom-left of [0, 0]' do
expect(box.bottom_left).to eq([0, 0])
end
it 'has a bottom-right of [width ,0]' do
expect(box.bottom_right).to eq([50, 0])
end
it 'has an absolute left boundary of x' do
expect(box.absolute_left).to eq(100)
end
it 'has an absolute right boundary of x + width' do
expect(box.absolute_right).to eq(150)
end
it 'has an absolute top boundary of y' do
expect(box.absolute_top).to eq(125)
end
it 'has an absolute bottom boundary of y - height' do
expect(box.absolute_bottom).to eq(50)
end
it 'has an absolute bottom-left of [x, y - height]' do
expect(box.absolute_bottom_left).to eq([100, 50])
end
it 'has an absolute bottom-right of [x + width, y - height]' do
expect(box.absolute_bottom_right).to eq([150, 50])
end
it 'has an absolute top-left of [x, y]' do
expect(box.absolute_top_left).to eq([100, 125])
end
it 'has an absolute top-right of [x + width, y]' do
expect(box.absolute_top_right).to eq([150, 125])
end
end
describe 'validations' do
it 'requires width to be set' do
expect {
described_class.new(nil, nil, [100, 100])
}.to raise_error(ArgumentError)
end
it 'raise_errors an ArgumentError if a block is not passed' do
pdf = Prawn::Document.new
expect {
pdf.bounding_box([0, 0], width: 200)
}.to raise_error(ArgumentError)
end
end
describe 'drawing' do
it 'does not stomp on the arguments to bounding_box' do
pdf = Prawn::Document.new
x = [100, 500]
pdf.bounding_box(x, width: 100) do
pdf.text('bork-bork-bork')
end
expect(x).to eq([100, 500])
end
it 'restores Document#bounds to the correct margin box on exit' do
pdf = Prawn::Document.new(margin: 200)
# add a multi-page bounding box
pdf.bounding_box([100, pdf.bounds.top], width: 400) do
pdf.text("The rain in spain falls mainly in the plains.\n" * 30)
end
pdf.start_new_page(margin: 0)
x_min, y_min, x_max, y_max = pdf.page.dimensions
expect(pdf.bounds.absolute_top_left).to eq([x_min, y_max])
expect(pdf.bounds.absolute_bottom_right).to eq([x_max, y_min])
end
it 'restores the parent bounding box when calls are nested' do
pdf.bounding_box([100, 500], width: 300, height: 300) do
expect(pdf.bounds.absolute_top)
.to eq(500 + pdf.margin_box.absolute_bottom)
expect(pdf.bounds.absolute_left)
.to eq(100 + pdf.margin_box.absolute_left)
parent_box = pdf.bounds
pdf.bounding_box([50, 200], width: 100, height: 100) do
expect(pdf.bounds.absolute_top)
.to eq(200 + parent_box.absolute_bottom)
expect(pdf.bounds.absolute_left).to eq(50 + parent_box.absolute_left)
end
expect(pdf.bounds.absolute_top)
.to eq(500 + pdf.margin_box.absolute_bottom)
expect(pdf.bounds.absolute_left)
.to eq(100 + pdf.margin_box.absolute_left)
end
end
it 'calculates a height if none is specified' do
pdf.bounding_box([100, 500], width: 100) do
pdf.text('The rain in Spain falls mainly on the plains.')
end
expect(pdf.y).to be_within(0.001).of(458.384)
end
it 'keeps track of the max height the box was stretched to' do
box =
pdf.bounding_box(pdf.bounds.top_left, width: 100) {
pdf.move_down(100)
pdf.move_up(15)
}
expect(box.height).to eq(100)
end
it 'advances the y-position by bbox.height by default' do
orig_y = pdf.y
pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width, height: 30) do
pdf.text('hello')
end
expect(pdf.y).to be_within(0.001).of(orig_y - 30)
end
it 'does not advance y-position if passed :hold_position => true' do
orig_y = pdf.y
pdf.bounding_box(
[0, pdf.cursor],
width: pdf.bounds.width,
hold_position: true,
) do
pdf.text('hello')
end
# y only advances by height of one line ("hello")
expect(pdf.y).to be_within(0.001).of(orig_y - pdf.height_of('hello'))
end
it 'does not advance y-position of a stretchy bbox if it would stretch the bbox further' do
bottom = pdf.y = pdf.margin_box.absolute_bottom
pdf.bounding_box([0, pdf.margin_box.top], width: pdf.bounds.width) do
pdf.y = bottom
pdf.text('hello') # starts a new page
end
expect(pdf.page_count).to eq(2)
# Restoring the position (to the absolute bottom) would stretch the bbox
# to the bottom of the page, which we don't want. This should be
# equivalent to a bbox with :hold_position => true, where we only advance
# by the amount that was actually drawn.
expect(pdf.y).to be_within(0.001).of(
pdf.margin_box.absolute_top - pdf.height_of('hello'),
)
end
end
describe 'Indentation' do
it 'temporarilies shift the x coordinate and width' do
pdf.bounding_box([100, 100], width: 200) do
pdf.indent(20) do
expect(pdf.bounds.absolute_left).to eq(120)
expect(pdf.bounds.width).to eq(180)
end
end
end
it 'restores the x coordinate and width after block exits' do
pdf.bounding_box([100, 100], width: 200) do
pdf.indent(20) do
# no-op
end
expect(pdf.bounds.absolute_left).to eq(100)
expect(pdf.bounds.width).to eq(200)
end
end
it 'restores the x coordinate and width on error' do
pdf.bounding_box([100, 100], width: 200) do
pdf.indent(20) { raise }
rescue StandardError
expect(pdf.bounds.absolute_left).to eq(100)
expect(pdf.bounds.width).to eq(200)
end
end
it 'maintains left indentation across a page break' do
original_left = pdf.bounds.absolute_left
pdf.indent(20) do
expect(pdf.bounds.absolute_left).to eq(original_left + 20)
pdf.start_new_page
expect(pdf.bounds.absolute_left).to eq(original_left + 20)
end
expect(pdf.bounds.absolute_left).to eq(original_left)
end
it 'maintains right indentation across a page break' do
original_width = pdf.bounds.width
pdf.indent(0, 20) do
expect(pdf.bounds.width).to eq(original_width - 20)
pdf.start_new_page
expect(pdf.bounds.width).to eq(original_width - 20)
end
expect(pdf.bounds.width).to eq(original_width)
end
it 'optionally allows adjustment of the right bound as well' do
pdf.bounding_box([100, 100], width: 200) do
pdf.indent(20, 30) do
expect(pdf.bounds.absolute_left).to eq(120)
expect(pdf.bounds.width).to eq(150)
end
expect(pdf.bounds.absolute_left).to eq(100)
expect(pdf.bounds.width).to eq(200)
end
end
describe 'in a ColumnBox' do
it 'subtracts the given indentation from the available width' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
height: 200,
columns: 2,
spacer: 20,
) do
width = pdf.bounds.width
pdf.indent(20) do
expect(pdf.bounds.width).to be_within(0.01).of(width - 20)
end
end
end
it 'subtracts right padding from the available width' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
height: 200,
columns: 2,
spacer: 20,
) do
width = pdf.bounds.width
pdf.indent(20, 30) do
expect(pdf.bounds.width).to be_within(0.01).of(width - 50)
end
end
end
it 'maintains the same left indentation across column breaks' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
columns: 3,
spacer: 15,
) do
3.times do |_column|
x = pdf.bounds.left_side
pdf.indent(20) do
expect(pdf.bounds.left_side).to eq(x + 20)
end
pdf.bounds.move_past_bottom
end
end
end
it 'does not change the right margin if only left indentation is requested' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
columns: 3,
spacer: 15,
) do
3.times do |_column|
x = pdf.bounds.right_side
pdf.indent(20) do
expect(pdf.bounds.right_side).to eq(x)
end
pdf.bounds.move_past_bottom
end
end
end
it 'maintains the same right indentation across columns' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
columns: 3,
spacer: 15,
) do
3.times do |_column|
x = pdf.bounds.right_side
pdf.indent(20, 10) do
expect(pdf.bounds.right_side).to eq(x - 10)
end
pdf.bounds.move_past_bottom
end
end
end
it 'keeps the right indentation after nesting indents' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
columns: 3,
spacer: 15,
) do
3.times do |_column|
# I am giving a right indent of 10...
pdf.indent(20, 10) do
x = pdf.bounds.right_side
# ...and no right indent here...
pdf.indent(20) do
# right indent is inherited from the parent!
expect(pdf.bounds.right_side).to eq(x)
end
end
pdf.bounds.move_past_bottom
end
end
end
it 'reverts the right indentation if negative indent is given in nested indent' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
columns: 3,
spacer: 15,
) do
3.times do |_column|
x = pdf.bounds.right_side
pdf.indent(20, 10) do
# requesting a negative right-indent of equivalent size...
pdf.indent(20, -10) do
# ...resets the right margin to that of the column!
expect(pdf.bounds.right_side).to eq(x)
end
end
pdf.bounds.move_past_bottom
end
end
end
it 'reduces the available column width by the sum of all nested indents' do
pdf.column_box(
[0, pdf.cursor],
width: pdf.bounds.width,
columns: 3,
spacer: 15,
) do
3.times do |_column|
w = pdf.bounds.width
pdf.indent(20, 10) do
pdf.indent(20, 10) do
expect(pdf.bounds.width).to eq(w - 60)
end
end
pdf.bounds.move_past_bottom
end
end
end
end
end
describe 'A canvas' do
it 'uses whatever the last set y position is' do
pdf.canvas do
pdf.bounding_box([100, 500], width: 200) { pdf.move_down(50) }
end
expect(pdf.y).to eq(450)
end
it 'restores the original ypos after execution', issue: 523 do
doc = Prawn::Document.new(skip_page_creation: true)
doc.start_new_page
original_ypos = doc.y
doc.canvas do
# draw
end
expect(doc.y).to eq(original_ypos)
end
end
describe 'Deep-copying' do
it 'creates a new object that does not copy @document' do
Prawn::Document.new do |pdf|
orig = pdf.bounds
copy = orig.deep_copy
expect(copy).to_not eq(pdf.bounds)
expect(copy.document).to be_nil
end
end
it 'deep-copies parent bounds' do
Prawn::Document.new do |pdf|
outside = pdf.bounds
pdf.bounding_box([100, 100], width: 100) do
copy = pdf.bounds.deep_copy
# the parent bounds should have the same parameters
expect(copy.parent.width).to eq(outside.width)
expect(copy.parent.height).to eq(outside.height)
# but should not be the same object
expect(copy.parent).to_not eq(outside)
end
end
end
end
describe 'Prawn::Document#reference_bounds' do
it 'returns self for non-stretchy bounds' do
pdf.bounding_box([0, pdf.cursor], width: 100, height: 100) do
expect(pdf.reference_bounds).to eq(pdf.bounds)
end
end
it 'returns the parent bounds if in a stretchy box' do
pdf.bounding_box([0, pdf.cursor], width: 100, height: 100) do
correct_bounds = pdf.bounds
pdf.bounding_box([0, pdf.cursor], width: 100) do
expect(pdf.reference_bounds).to eq(correct_bounds)
end
end
end
it 'finds the non-stretchy box through 2 levels' do
pdf.bounding_box([0, pdf.cursor], width: 100, height: 100) do
correct_bounds = pdf.bounds
pdf.bounding_box([0, pdf.cursor], width: 100) do
pdf.bounding_box([0, pdf.cursor], width: 100) do
expect(pdf.reference_bounds).to eq(correct_bounds)
end
end
end
end
it "returns the margin box if there's no explicit bbox" do
expect(pdf.reference_bounds).to eq(pdf.margin_box)
pdf.bounding_box([0, pdf.cursor], width: 100) do
expect(pdf.reference_bounds).to eq(pdf.margin_box)
end
end
it "returns the canvas box if we're in a canvas" do
pdf.canvas do
canvas_box = pdf.bounds
expect(pdf.reference_bounds).to eq(canvas_box)
pdf.bounding_box([0, pdf.cursor], width: 100) do
expect(pdf.reference_bounds).to eq(canvas_box)
end
end
end
end
describe '#move_past_bottom' do
it 'ordinarilies start a new page' do
pdf.bounds.move_past_bottom
pdf.text('Foo')
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
expect(pages[0][:strings]).to eq([])
expect(pages[1][:strings]).to eq(['Foo'])
end
it 'moves to the top of the next page if it exists already' do
# save away the y-position at the top of a page
top_y = pdf.y
# create a blank page but go to the page before it
pdf.start_new_page
pdf.go_to_page(1)
pdf.text('Foo')
pdf.bounds.move_past_bottom
expect(pdf.y).to be_within(0.001).of(top_y)
pdf.text('Bar')
pages = PDF::Inspector::Page.analyze(pdf.render).pages
expect(pages.size).to eq(2)
expect(pages[0][:strings]).to eq(['Foo'])
expect(pages[1][:strings]).to eq(['Bar'])
end
end
end
ruby-prawn-2.5.0.orig/spec/extensions/ 0000775 0000000 0000000 00000000000 14571572164 016430 5 ustar root root ruby-prawn-2.5.0.orig/spec/extensions/encoding_helpers.rb 0000664 0000000 0000000 00000000524 14571572164 022266 0 ustar root root # frozen_string_literal: true
# String encoding helpers.
module EncodingHelpers
# Make sure the string is Windows-1252 -encoded.
def win1252_string(str)
str.dup.force_encoding(Encoding::Windows_1252)
end
# Make sure the string is binary-encoded
def bin_string(str)
str.dup.force_encoding(Encoding::ASCII_8BIT)
end
end
ruby-prawn-2.5.0.orig/spec/data/ 0000775 0000000 0000000 00000000000 14571572164 015142 5 ustar root root ruby-prawn-2.5.0.orig/spec/data/curves.pdf 0000664 0000000 0000000 00000001675 14571572164 017155 0 ustar root root %PDF-1.3
%
1 0 obj
<< /Creator (Prawn)
/Producer (Prawn)
>>
endobj
2 0 obj
<< /Type /Pages
/Count 1
/Kids [5 0 R]
>>
endobj
3 0 obj
<< /Type /Catalog
/Pages 2 0 R
>>
endobj
4 0 obj
<< /Length 380
>>
stream
/DeviceRGB cs
0.000 0.000 0.000 scn
/DeviceRGB CS
0.000 0.000 0.000 SCN
q
136.000 136.000 m
96.000 126.000 96.000 126.000 86.000 86.000 c
S
246.000 236.000 m
246.000 241.523 241.523 246.000 236.000 246.000 c
230.477 246.000 226.000 241.523 226.000 236.000 c
226.000 230.477 230.477 226.000 236.000 226.000 c
241.523 226.000 246.000 230.477 246.000 236.000 c
236.000 236.000 m
f
Q
endstream
endobj
5 0 obj
<< /Type /Page
/Parent 2 0 R
/MediaBox [0 0 612.0 792.0]
/Contents 4 0 R
/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
>>
>>
endobj
xref
0 6
0000000000 65535 f
0000000015 00000 n
0000000071 00000 n
0000000128 00000 n
0000000177 00000 n
0000000608 00000 n
trailer
<< /Size 6
/Root 3 0 R
/Info 1 0 R
>>
startxref
762
%%EOF
ruby-prawn-2.5.0.orig/prawn.gemspec 0000664 0000000 0000000 00000004213 14571572164 015773 0 ustar root root # frozen_string_literal: true
basedir = __dir__
require "#{basedir}/lib/prawn/version"
Gem::Specification.new do |spec|
spec.name = 'prawn'
spec.version = Prawn::VERSION
spec.platform = Gem::Platform::RUBY
spec.summary = 'A fast and nimble PDF generator for Ruby'
spec.description = 'Prawn is a fast, tiny, and nimble PDF generator for Ruby'
spec.files = Dir.glob('{lib}/**/**/*') +
Dir.glob('data/fonts/{MustRead.html,*.afm}') +
%w[COPYING LICENSE GPLv2 GPLv3]
if File.basename($PROGRAM_NAME) == 'gem' && ARGV.include?('build')
signing_key = File.expand_path('~/.gem/gem-private_key.pem')
if File.exist?(signing_key)
spec.cert_chain = ['certs/pointlessone.pem']
spec.signing_key = signing_key
else
warn 'WARNING: Signing key is missing. The gem is not signed and its authenticity can not be verified.'
end
end
spec.required_ruby_version = '>= 2.7'
spec.required_rubygems_version = '>= 1.3.6'
spec.authors = [
'Alexander Mankuta', 'Gregory Brown', 'Brad Ediger', 'Daniel Nelson',
'Jonathan Greenberg', 'James Healy',
]
spec.email = [
'alex@pointless.one', 'gregory.t.brown@gmail.com', 'brad@bradediger.com',
'dnelson@bluejade.com', 'greenberg@entryway.net', 'jimmy@deefa.com',
]
spec.licenses = %w[Nonstandard GPL-2.0-only GPL-3.0-only]
spec.homepage = 'http://prawnpdf.org/'
spec.metadata = {
'rubygems_mfa_required' => 'true',
'homepage_uri' => spec.homepage,
'changelog_uri' => "https://github.com/prawnpdf/prawn/blob/#{spec.version}/CHANGELOG.md",
'source_code_uri' => 'https://github.com/prawnpdf/prawn',
'documentation_uri' => "https://prawnpdf.org/docs/prawn/#{spec.version}/",
'bug_tracker_uri' => 'https://github.com/prawnpdf/prawn/issues',
}
spec.add_dependency('matrix', '~> 0.4')
spec.add_dependency('pdf-core', '~> 0.10.0')
spec.add_dependency('ttfunk', '~> 1.8')
spec.add_development_dependency('pdf-inspector', '>= 1.2.1', '< 2.0.a')
spec.add_development_dependency('pdf-reader', '~> 1.4', '>= 1.4.1')
spec.add_development_dependency('prawn-dev', '~> 0.4.0')
spec.add_development_dependency('prawn-manual_builder', '~> 0.4.0')
end
ruby-prawn-2.5.0.orig/manual/ 0000775 0000000 0000000 00000000000 14571572164 014554 5 ustar root root ruby-prawn-2.5.0.orig/manual/text/ 0000775 0000000 0000000 00000000000 14571572164 015540 5 ustar root root ruby-prawn-2.5.0.orig/manual/text/win_ansi_charset.rb 0000664 0000000 0000000 00000003056 14571572164 021411 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Windown-1252 Charset'
text do
prose <<~TEXT
Here's a list of all of the glyphs that can be rendered by Adobe's built
in fonts, along with their character widths and WinAnsi codes. Be sure
to pass these glyphs as UTF-8, and Prawn will transcode them for you.
TEXT
end
example new_page: true do
font 'Helvetica', size: 10
x = 0
y = bounds.top
fields = [[20, :right], [8, :left], [12, :center], [30, :right], [8, :left], [0, :left]]
Prawn::Encoding::WinAnsi::CHARACTERS.each_with_index do |name, index|
next if name == '.notdef'
y -= font_size
if y < font_size
y = bounds.top - font_size
x += 170
end
code = format('%d.', index: index)
char = index.chr.force_encoding(::Encoding::Windows_1252)
width = 1000 * width_of(char, size: font_size) / font_size
size = format('%d', width: width)
data = [code, nil, char, size, nil, name]
dx = x
fields.zip(data).each do |(total_width, align), field|
if field
width = width_of(field, size: font_size)
case align
when :left then offset = 0
when :right then offset = total_width - width
when :center then offset = (total_width - width) / 2
end
text_box(field.dup.force_encoding('windows-1252').encode('UTF-8'), at: [dx + offset, y])
end
dx += total_width
end
end
end
end
ruby-prawn-2.5.0.orig/manual/text/utf8.rb 0000664 0000000 0000000 00000001615 14571572164 016756 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'UTF-8'
text do
prose <<~TEXT
Multilingualization isn't much of a problem on Prawn as its default
encoding is UTF-8. The only thing you need to worry about is if the font
support the glyphs of your language.
TEXT
end
example do
text 'Take this example, a simple Euro sign:'
text '€', size: 32
move_down 20
text 'This works, because € is one of the few ' \
'non-ASCII glyphs supported in PDF built-in fonts.'
move_down 20
text 'For full internationalized text support, we need to use external fonts:'
move_down 20
font("#{Prawn::ManualBuilder::DATADIR}/fonts/DejaVuSans.ttf") do
text 'ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.'
text 'There you go.'
end
end
end
ruby-prawn-2.5.0.orig/manual/text/text_box_overflow.rb 0000664 0000000 0000000 00000003537 14571572164 021654 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Text Box Overflow'
text do
prose <<~TEXT
The text_box method accepts both :width and
:height options. So what happens if the text doesn't fit the
box?
The default behavior is to truncate the text but this can be changed with
the :overflow option. Available modes are
:expand (the box will increase to fit the text) and
:shrink_to_fit (the text font size will be shrunk to fit).
If :shrink_to_fit mode is used with the
:min_font_size option set, the font size will not be reduced
to less than the value provided even if it means truncating some text.
If the :disable_wrap_by_char is set to true
then any text wrapping done while using the :shrink_to_fit
mode will not break up the middle of words.
TEXT
end
example new_page: true do
string = 'This is the sample text used for the text boxes. See how it ' \
'behave with the various overflow options used.'
text string
y_position = cursor - 20
%i[truncate expand shrink_to_fit].each_with_index do |mode, i|
text_box string,
at: [i * 150, y_position],
width: 100,
height: 50,
overflow: mode
end
string = 'If the box is too small for the text, :shrink_to_fit ' \
'can render the text in a really small font size.'
move_down 120
text string
y_position = cursor - 20
[nil, 8, 10, 12].each_with_index do |value, index|
text_box string,
at: [index * 150, y_position],
width: 50,
height: 50,
overflow: :shrink_to_fit,
min_font_size: value
end
end
end
ruby-prawn-2.5.0.orig/manual/text/text_box_extensions.rb 0000664 0000000 0000000 00000002724 14571572164 022205 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Text Box Extensions'
text do
prose <<~TEXT
We've already seen one way of using text boxes with the
text_box method. Turns out this method is just a convenience
for using the Prawn::Text::Box class as it creates a new
object and call render on it.
Knowing that any extensions we add to Prawn::Text::Box will
take effect when we use the text_box method. To add an
extension all we need to do is append the
Prawn::Text::Box.extensions array with a module.
TEXT
end
example new_page: true do
module TriangleBox
def available_width
height + 25
end
end
y_position = cursor
width = 100
height = 100
Prawn::Text::Box.extensions << TriangleBox
stroke_rectangle([0, y_position], width, height)
text_box(
'A' * 100,
at: [0, y_position],
width: width,
height: height,
)
Prawn::Text::Formatted::Box.extensions << TriangleBox
stroke_rectangle([200, y_position], width, height)
formatted_text_box(
[{ text: 'A' * 100, color: '009900' }],
at: [200, y_position],
width: width,
height: height,
)
# Here we clear the extensions array
Prawn::Text::Box.extensions.clear
Prawn::Text::Formatted::Box.extensions.clear
end
end
ruby-prawn-2.5.0.orig/manual/text/text_box_excess.rb 0000664 0000000 0000000 00000002104 14571572164 021270 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Text Box Excess'
text do
prose <<~TEXT
Whenever the text_box method truncates text, this truncated
bit is not lost, it is the method return value and we can take advantage
of that.
We just need to take some precautions.
This example renders as much of the text as will fit in a larger font
inside one text_box and then proceeds to render the remaining text in the
default size in a second text_box.
TEXT
end
example do
string = 'This is the beginning of the text. It will be cut somewhere and ' \
'the rest of the text will proceed to be rendered this time by ' \
'calling another method.' + (' . ' * 50)
y_position = cursor - 20
excess_text = text_box(
string,
width: 300,
height: 50,
overflow: :truncate,
at: [100, y_position],
size: 18,
)
text_box(
excess_text,
width: 300,
at: [100, y_position - 100],
)
end
end
ruby-prawn-2.5.0.orig/manual/text/single_usage.rb 0000664 0000000 0000000 00000002467 14571572164 020543 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Single Usage Fonts'
text do
prose <<~TEXT
The PDF format has some built-in font support. If you want to use other
fonts in Prawn you need to embed the font file.
Doing this for a single font is extremely simple. Remember the Styling
font example? Another use of the font method is to provide a
font file path and the font will be embedded in the document and set as
the current font.
This is reasonable if a font is used only once, but, if a font used
several times, providing the path each time it is used becomes
cumbersome. The example on the next page shows a better way to deal with
fonts which are used several times in a document.
TEXT
end
example do
# Using a TTF font file
font("#{Prawn::ManualBuilder::DATADIR}/fonts/DejaVuSans.ttf") do
text 'Written with the DejaVu Sans TTF font.'
end
move_down 20
text 'Written with the default font.'
move_down 20
# Using an DFONT font file
font("#{Prawn::ManualBuilder::DATADIR}/fonts/Panic+Sans.dfont") do
text 'Written with the Panic Sans DFONT font'
end
move_down 20
text 'Written with the default font once more.'
end
end
ruby-prawn-2.5.0.orig/manual/text/rotation.rb 0000664 0000000 0000000 00000002774 14571572164 017736 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Rotation'
text do
prose <<~TEXT
Rotating text is best avoided on free flowing text, so this example
will only use the text_box method as we can have much more
control over its output.
To rotate text all we need to do is use the :rotate option
passing an angle in degrees and an optional :rotate_around
to indicate the origin of the rotation (the default is
:upper_left).
TEXT
end
example new_page: true do
width = 100
height = 60
angle = 30
x = 200
y = cursor - 30
stroke_rectangle [0, y], width, height
text_box(
'This text was not rotated',
at: [0, y],
width: width,
height: height,
)
stroke_rectangle [0, y - 100], width, height
text_box(
'This text was rotated around the center',
at: [0, y - 100],
width: width,
height: height,
rotate: angle,
rotate_around: :center,
)
%i[lower_left upper_left lower_right upper_right].each_with_index do |corner, index|
y -= 100 if index == 2
stroke_rectangle [x + ((index % 2) * 200), y], width, height
text_box(
"This text was rotated around the #{corner} corner.",
at: [x + ((index % 2) * 200), y],
width: width,
height: height,
rotate: angle,
rotate_around: corner,
)
end
end
end
ruby-prawn-2.5.0.orig/manual/text/right_to_left_text.rb 0000664 0000000 0000000 00000004626 14571572164 021772 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Right-to-Left Text'
text do
prose <<~TEXT
Prawn can be used with right-to-left text. The direction can be set
document-wide, on particular text, or on a text-box. Setting the
direction to :rtl automatically changes the default
alignment to :right.
You can even override direction on an individual fragment. The one caveat
is that two fragments going against the main direction cannot be placed
next to each other without appearing in the wrong order.
Writing bidirectional text that combines both left-to-right and
right-to-left languages is easy using the bidi Ruby Gem and
its render_visual function. See
https://github.com/elad/ruby-bidi for instructions and an example using Prawn.
TEXT
end
example new_page: true do
# set the direction document-wide
self.text_direction = :rtl
font("#{Prawn::ManualBuilder::DATADIR}/fonts/Jigmo.ttf", size: 16) do
long_text = '写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的' \
'关键信息顺次爬下来也不是什么难事写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事写' \
'个小'
text long_text
move_down 20
text 'You can override the document direction.', direction: :ltr
move_down 20
formatted_text [
{ text: '更可怕的是,同质化竞争对手可以按照' },
{ text: 'URL', direction: :ltr },
{ text: '中后面这个' },
{ text: 'ID', direction: :ltr },
{ text: '来遍历您的' },
{ text: 'DB', direction: :ltr },
{
text: '中的内容、写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事、这样的话、' \
'你就非常被动了。',
},
]
move_down 20
formatted_text [
{ text: '更可怕的是、同质化竞争对手可以按照' },
{ text: 'this', direction: :ltr },
{ text: "won't", direction: :ltr, size: 24 },
{ text: 'work', direction: :ltr },
{ text: '中的内容、写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事' },
]
end
end
end
ruby-prawn-2.5.0.orig/manual/text/rendering_and_color.rb 0000664 0000000 0000000 00000002340 14571572164 022061 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Text Rendering Modes'
text do
prose <<~TEXT
You have already seen how to set the text color using both inline
formatting and the format text methods. There is another way by using the
graphics methods fill_color and stroke_color.
When reading the graphics reference you learned about fill and stroke. If
you haven't read it before, read it now before continuing.
Text can be rendered by being filled (the default mode) or just stroked,
or both filled and stroked. This can be set using the
text_rendering_mode method or the :mode option
on the text methods.
TEXT
end
example do
fill_color '00ff00'
stroke_color '0000ff'
font_size(40) do
# normal rendering mode: fill
text 'This text is filled with green.'
# inline rendering mode: stroke
text 'This text is stroked with blue', mode: :stroke
# block rendering mode: fill and stroke
text_rendering_mode(:fill_stroke) do
text 'This text is filled with green and stroked with blue'
end
end
end
end
ruby-prawn-2.5.0.orig/manual/text/registering_families.rb 0000664 0000000 0000000 00000003273 14571572164 022265 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Registering Font Families'
text do
prose <<~TEXT
Registering font families will help you when you want to use a font over
and over or if you would like to take advantage of the :style
option of the text methods and the b and i tags
when using inline formatting.
To register a font family update the font_families hash with
the font path for each style you want to use.
TEXT
end
example new_page: true do
# Registering a single external font
font_families.update(
'DejaVu Sans' => {
normal: "#{Prawn::ManualBuilder::DATADIR}/fonts/DejaVuSans.ttf",
},
)
font('DejaVu Sans') do
text 'Using the DejaVu Sans font providing only its name to the font method'
end
move_down 20
# Registering a DFONT package
font_path = "#{Prawn::ManualBuilder::DATADIR}/fonts/Panic+Sans.dfont"
font_families.update(
'Panic Sans' => {
normal: { file: font_path, font: 'PanicSans' },
italic: { file: font_path, font: 'PanicSans-Italic' },
bold: { file: font_path, font: 'PanicSans-Bold' },
bold_italic: { file: font_path, font: 'PanicSans-BoldItalic' },
},
)
font 'Panic Sans'
text 'Also using Panic Sans by providing only its name'
move_down 20
text 'Taking advantage of the inline formatting',
inline_format: true
move_down 20
%i[bold bold_italic italic normal].each do |style|
text "Using the #{style} style option.", style: style
move_down 10
end
end
end
ruby-prawn-2.5.0.orig/manual/text/positioned_text.rb 0000664 0000000 0000000 00000003050 14571572164 021304 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Positioned Text'
text do
prose <<~TEXT
Sometimes we want the text on a specific position on the page. The
text method just won't help us.
There are two other methods for this task: draw_text and
text_box.
draw_text is very simple. It will render text starting at
the position provided to the :at option. It won't flow to a
new line even if it hits the document boundaries so it is best suited for
short text.
text_box gives us much more control over the output. Just
provide :width and :height options and the text
will flow accordingly. Even if you don't provide a :width
option the text will flow to a new line if it reaches the right border.
Given that, text_box is the better option available.
TEXT
end
example do
draw_text "This draw_text line is absolute positioned. However don't " \
'expect it to flow even if it hits the document border',
at: [200, 170]
text_box 'This is a text box, you can control where it will flow by ' \
'specifying the :height and :width options',
at: [50, 150],
height: 100,
width: 100
text_box 'Another text box with no :width option passed, so it will ' \
'flow to a new line whenever it reaches the right margin. ',
at: [200, 100]
end
end
ruby-prawn-2.5.0.orig/manual/text/paragraph_indentation.rb 0000664 0000000 0000000 00000002203 14571572164 022423 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Paragraph Indentation'
text do
prose <<~TEXT
Prawn strips all whitespace from the beginning and the end of strings so
there are two ways to indent paragraphs:
One is to use non-breaking spaces which Prawn won't strip. One shortcut
to using them is the Prawn::Text::NBSP.
The other is to use the :indent_paragraphs option with the
text methods. Just pass a number with the space to indent the first line
in each paragraph.
TEXT
end
example new_page: true do
# Using non-breaking spaces
text (' ' * 10) + ("This paragraph won't be indented. " * 10) +
"\n#{Prawn::Text::NBSP * 10}" + ('This one will with NBSP. ' * 10)
move_down 20
text "#{'This paragraph will be indented. ' * 10}\n#{'This one will too. ' * 10}",
indent_paragraphs: 60
move_down 20
text 'FROM RIGHT TO LEFT:'
text "#{'This paragraph will be indented. ' * 10}\n#{'This one will too. ' * 10}",
indent_paragraphs: 60,
direction: :rtl
end
end
ruby-prawn-2.5.0.orig/manual/text/line_wrapping.rb 0000664 0000000 0000000 00000006146 14571572164 020732 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Line Wrapping'
text do
prose <<~TEXT
Line wrapping happens on white space or hyphens. Soft hyphens can be used
to indicate where words can be hyphenated. Non-breaking spaces can be
used to display space without allowing for a break.
For writing styles that do not make use of spaces, the zero width space
serves to mark word boundaries. Zero width spaces are available only with
external fonts.
TEXT
end
example new_page: true do
text "Hard hyphens:\n" \
'Slip-sliding away, slip sliding awaaaay. You know the ' \
"nearer your destination the more you're slip-sliding away."
move_down 20
shy = Prawn::Text::SHY
text "Soft hyphens:\n" \
"Slip slid#{shy}ing away, slip slid#{shy}ing away. You know the " \
"nearer your destinat#{shy}ion the more you're slip slid#{shy}ing away."
move_down 20
nbsp = Prawn::Text::NBSP
text "Non-breaking spaces:\n" \
"Slip#{nbsp}sliding away, slip#{nbsp}sliding awaaaay. You know the " \
"nearer your destination the more you're slip#{nbsp}sliding away."
move_down 20
font_families.update('Jigmo' => { normal: "#{Prawn::ManualBuilder::DATADIR}/fonts/Jigmo.ttf" })
font('Jigmo', size: 16) do
text "No word boundaries:\n更可怕的是、同质化竞争对手可以按照URL中后面这个ID来遍历" \
'您的DB中的内容、写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事、这样的话、' \
'你就非常被动了。更可怕的是,同质化竞争对手可以按照URL中后面这个ID来遍历您的DB中的内容、' \
'写个小爬虫把你的页面上的关键信息顺次爬下来也不是什么难事、这样的话、你就非常被动了。'
move_down 20
zwsp = Prawn::Text::ZWSP
text "Invisible word boundaries:\n更#{zwsp}可怕的#{zwsp}是、#{zwsp}同质化#{zwsp}竞争" \
"#{zwsp}对#{zwsp}手#{zwsp}可以#{zwsp}按照#{zwsp}URL#{zwsp}中#{zwsp}后面#{zwsp}这个" \
"#{zwsp}ID#{zwsp}来#{zwsp}遍历#{zwsp}您的#{zwsp}DB#{zwsp}中的#{zwsp}内容、#{zwsp}写个" \
"#{zwsp}小爬虫#{zwsp}把#{zwsp}你的#{zwsp}页面#{zwsp}上的#{zwsp}关#{zwsp}键#{zwsp}信" \
"#{zwsp}息顺#{zwsp}次#{zwsp}爬#{zwsp}下来#{zwsp}也#{zwsp}不是#{zwsp}什么#{zwsp}难事、" \
"#{zwsp}这样的话,#{zwsp}你#{zwsp}就#{zwsp}非常#{zwsp}被动了。#{zwsp}更#{zwsp}可怕的" \
"#{zwsp}是、#{zwsp}同质化#{zwsp}竞争#{zwsp}对#{zwsp}手#{zwsp}可以#{zwsp}按照#{zwsp}URL" \
"#{zwsp}中#{zwsp}后面#{zwsp}这个#{zwsp}ID#{zwsp}来#{zwsp}遍历#{zwsp}您的#{zwsp}DB#{zwsp}" \
"中的#{zwsp}内容、#{zwsp}写个#{zwsp}小爬虫#{zwsp}把#{zwsp}你的#{zwsp}页面#{zwsp}上的" \
"#{zwsp}关#{zwsp}键#{zwsp}信#{zwsp}息顺#{zwsp}次#{zwsp}爬#{zwsp}下来#{zwsp}也#{zwsp}不是" \
"#{zwsp}什么#{zwsp}难事、#{zwsp}这样的话、#{zwsp}你#{zwsp}就#{zwsp}非常#{zwsp}被动了。"
end
end
end
ruby-prawn-2.5.0.orig/manual/text/leading.rb 0000664 0000000 0000000 00000001321 14571572164 017465 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Leading'
text do
prose <<~TEXT
Leading is the additional space between lines of text.
The leading can be set using the default_leading method
which applies to the rest of the document or until it is changed, or
inline in the text methods with the :leading option.
The default leading is 0.
TEXT
end
example do
string = 'Hey, what did you do with the space between my lines? ' * 8
text string, leading: 0
move_down 20
default_leading 5
text string
move_down 20
text string, leading: 10
end
end
ruby-prawn-2.5.0.orig/manual/text/kerning_and_character_spacing.rb 0000664 0000000 0000000 00000002761 14571572164 024072 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Kerning and Character Spacing'
text do
prose <<~TEXT
Kerning is the process of adjusting the spacing between characters in a
proportional font. It is usually done with specific letter pairs. We can
switch it on and off if it is available with the current font. Just pass
a boolean value to the :kerning option of the text methods.
Character Spacing is the space between characters. It can be increased or
decreased and will have effect on the whole text. Just pass a number to
the :character_spacing option from the text methods.
TEXT
end
example new_page: true do
font_size(30) do
text_box 'With kerning:', kerning: true, at: [0, y - 40]
text_box 'Without kerning:', kerning: false, at: [0, y - 80]
text_box 'Tomato', kerning: true, at: [230, y - 40]
text_box 'Tomato', kerning: false, at: [230, y - 80]
text_box 'WAR', kerning: true, at: [360, y - 40]
text_box 'WAR', kerning: false, at: [360, y - 80]
text_box 'F.', kerning: true, at: [470, y - 40]
text_box 'F.', kerning: false, at: [470, y - 80]
end
move_down 80
string = 'What have you done to the space between the characters?'
[-2, -1, 0, 0.5, 1, 2].each do |spacing|
move_down 20
text "#{string} (character_spacing: #{spacing})",
character_spacing: spacing
end
end
end
ruby-prawn-2.5.0.orig/manual/text/inline.rb 0000664 0000000 0000000 00000003212 14571572164 017341 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Inline Formatting'
text do
prose <<~TEXT
Inline formatting gives you the option to format specific portions of a
text. It uses HTML-esque syntax inside the text string. Supported tags
are: b (bold), i (italic), u
(underline), strikethrough, sub (subscript),
sup (superscript).
The following tags accept specific attributes: font accepts
size, name, and character_spacing;
color accepts rgb and set of c,
m, y, and k;
link accepts href for external links.
TEXT
end
example do
%w[b i u strikethrough sub sup].each do |tag|
text "Just your regular text <#{tag}>except this portion#{tag}> " \
"is using the #{tag} tag",
inline_format: true
# move_down 10
end
text "This line uses " \
"all the font tag attributes in " \
"a single line. ",
inline_format: true
# move_down 10
text "Coloring in both RGB " \
"and CMYK",
inline_format: true
# move_down 10
text 'This an external link to the ' \
"Prawn home page" \
'',
inline_format: true
end
end
ruby-prawn-2.5.0.orig/manual/text/free_flowing_text.rb 0000664 0000000 0000000 00000004100 14571572164 021572 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Free Flowing Text'
text do
prose <<~TEXT
Text rendering can be as simple or as complex as you want.
This example covers the most basic method: text. It is meant
for free flowing text. The provided string will flow according to the
current bounding box width and height. It will also flow onto the next
page if the bottom of the bounding box is reached.
The text will start being rendered on the current cursor position. When
it finishes rendering, the cursor is left directly below the text.
This example also shows text flowing across pages following the margin
box and other bounding boxes.
TEXT
end
example do
text 'This text will flow to the next page. ' * 20
y_position = cursor
bounding_box([0, y_position], width: 200, height: 150) do
transparent(0.5) { stroke_bounds }
text 'This text will flow along this bounding box we created for it. ' * 5
end
bounding_box([300, y_position], width: 200, height: 150) do
transparent(0.5) { stroke_bounds } # This will stroke on one page
text 'Now look what happens when the free flowing text reaches the end of a bounding box ' \
"that is narrower than the margin box.#{' . ' * 200}It continues on the next page as if " \
'the previous bounding box was cloned. If we want it to have the same border as the one on ' \
'the previous page we will need to stroke the boundaries again.'
transparent(0.5) { stroke_bounds } # And this will stroke on the next
end
move_cursor_to 200
span(350, position: :center) do
text 'Span is a different kind of bounding box as it lets the text flow gracefully onto the ' \
"next page. It doesn't matter if the text started on the middle of the previous page, when " \
"it flows to the next page it will start at the beginning.#{' _ ' * 500}" \
'I told you it would start on the beginning of this page.'
end
end
end
ruby-prawn-2.5.0.orig/manual/text/formatted_text.rb 0000664 0000000 0000000 00000004542 14571572164 021123 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Formatted Text'
text do
prose <<~TEXT
There are two other text methods available: formatted_text
and formatted_text_box.
These are useful when the provided text has numerous portions that need
to be formatted differently. As you might imply from their names the
first should be used for free flowing text just like the
text method and the last should be used for positioned text
just like text_box.
The main difference between these methods and the text and
text_box methods is how the text is provided. The
formatted_text and formatted_text_box methods
accept an array of hashes. Each hash must provide a :text
option which is the text string and may provide the following options:
:styles (an array of symbols), :size (the font
size), :character_spacing (additional space between the
characters), :font (the name of a registered font),
:color (the same input accepted by fill_color
and stroke_color), :link (an URL to create a
link), and :local (a link to a local file).
TEXT
end
example new_page: true do
formatted_text [
{ text: 'Some bold. ', styles: [:bold] },
{ text: 'Some italic. ', styles: [:italic] },
{ text: 'Bold italic. ', styles: %i[bold italic] },
{ text: 'Bigger Text. ', size: 20 },
{ text: 'More spacing. ', character_spacing: 3 },
{ text: 'Different Font. ', font: 'Courier' },
{ text: 'Some coloring. ', color: 'FF00FF' },
{ text: 'Link to the home page. ', color: '0000FF', link: 'https://prawnpdf.org/' },
{ text: 'Link to a local file. ', color: '0000FF', local: 'README.md' },
]
formatted_text_box(
[
{ text: 'Just your regular' },
{ text: ' text_box ', font: 'Courier' },
{
text: 'with some additional formatting options added to the mix.',
color: [50, 100, 0, 0],
styles: [:italic],
},
],
at: [100, 100],
width: 200,
height: 100,
)
end
end
ruby-prawn-2.5.0.orig/manual/text/formatted_callbacks.rb 0000664 0000000 0000000 00000003643 14571572164 022057 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Formatted Text Callbacks'
text do
prose <<~TEXT
The :callback option is also available for the formatted
text methods.
This option accepts an object (or array of objects) on which two methods
will be called if defined: render_behind and
render_in_front. They are called before and after rendering
the text fragment and are passed the fragment as an argument.
This example defines two new callback classes and provide callback
objects for the formatted_text.
TEXT
end
example new_page: true do
class HighlightCallback
def initialize(options)
@color, @document = options.values_at(:color, :document)
end
def render_behind(fragment)
original_color = @document.fill_color
@document.fill_color = @color
@document.fill_rectangle(fragment.top_left, fragment.width, fragment.height)
@document.fill_color = original_color
end
end
class ConnectedBorderCallback
def initialize(options)
@radius, @document = options.values_at(:radius, :document)
end
def render_in_front(fragment)
points = [fragment.top_left, fragment.top_right, fragment.bottom_right, fragment.bottom_left]
@document.stroke_polygon(*points)
points.each { |point| @document.fill_circle(point, @radius) }
end
end
highlight = HighlightCallback.new(color: 'ffff00', document: self)
border = ConnectedBorderCallback.new(radius: 2.5, document: self)
formatted_text(
[
{ text: 'hello', callback: highlight },
{ text: ' ' },
{ text: 'world', callback: border },
{ text: ' ' },
{ text: 'hello world', callback: [highlight, border] },
],
size: 20,
)
end
end
ruby-prawn-2.5.0.orig/manual/text/font_style.rb 0000664 0000000 0000000 00000001526 14571572164 020257 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Font Style'
text do
prose <<~TEXT
Most font families come with some styles other than normal. Most common
are bold, italic and bold_italic.
The style can be set the using the :style option, with
either the font method which will set the font and style for
rest of the document, or with the inline text methods.
TEXT
end
example do
fonts = %w[Courier Helvetica Times-Roman]
styles = %i[bold bold_italic italic normal]
fonts.each do |example_font|
move_down 20
styles.each do |style|
font example_font, style: style
text "I'm writing in #{example_font} (#{style})"
end
end
end
end
ruby-prawn-2.5.0.orig/manual/text/font_size.rb 0000664 0000000 0000000 00000002006 14571572164 020063 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Font Size'
text do
prose <<~TEXT
The font_size method works just like the font
method.
In fact we can even use font with the :size
option to declare which size we want.
Another way to change the font size is by supplying the
:size option to the text methods.
The default font size is 12.
TEXT
end
example do
text "Let's see which is the current font_size: #{font_size.inspect}"
font_size 16
text 'Yeah, something bigger!'
font_size(25) { text 'Even bigger!' }
text 'Back to 16 again.'
text 'Single line on 20 using the :size option.', size: 20
text 'Back to 16 once more.'
font('Courier', size: 10) do
text 'Yeah, using Courier 10 courtesy of the font method.'
end
font('Helvetica', size: 12)
text 'Back to normal'
end
end
ruby-prawn-2.5.0.orig/manual/text/font.rb 0000664 0000000 0000000 00000002053 14571572164 017033 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Fonts'
text do
prose <<~TEXT
The font method can be used in three different ways.
If we don't pass it any arguments it will return the current font being
used to render text.
If we just pass it a font name it will use that font for rendering text
through the rest of the document.
It can also be used by passing a font name and a block. In this case the
specified font will only be used to render text inside the block.
The default font is Helvetica.
TEXT
end
example do
text "Let's see which font we are using: #{font.inspect}"
font 'Times-Roman'
text 'Written in Times.'
font('Courier') do
text 'Written in Courier because we are inside the block.'
end
text 'Written in Times again as we left the previous block.'
text "Let's see which font we are using again: #{font.inspect}"
font 'Helvetica'
text 'Back to normal.'
end
end
ruby-prawn-2.5.0.orig/manual/text/fallback_fonts.rb 0000664 0000000 0000000 00000002614 14571572164 021040 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Fallback Fonts'
text do
prose <<~TEXT
Prawn enables the declaration of fallback fonts for those glyphs that may
not be present in the desired font. Use the :fallback_fonts
option with any of the text or text box methods, or set fallback_fonts
document-wide.
TEXT
end
example do
jigmo_file = "#{Prawn::ManualBuilder::DATADIR}/fonts/Jigmo.ttf"
font_families['Jigmo'] = { normal: { file: jigmo_file, font: 'Jigmo' } }
panic_sans_file = "#{Prawn::ManualBuilder::DATADIR}/fonts/Panic+Sans.dfont"
font_families['Panic Sans'] = { normal: { file: panic_sans_file, font: 'PanicSans' } }
font('Panic Sans') do
text(
'When fallback fonts are included, each glyph will be rendered ' \
'using the first font that includes the glyph, starting with the ' \
'current font and then moving through the fallback fonts from left ' \
'to right.' \
"\n\n" \
"hello ƒ 你好\n再见 ƒ goodbye",
fallback_fonts: %w[Times-Roman Jigmo],
)
end
move_down 20
formatted_text(
[
{ text: 'Fallback fonts can even override' },
{ text: 'fragment fonts (你好)', font: 'Times-Roman' },
],
fallback_fonts: %w[Times-Roman Jigmo],
)
end
end
ruby-prawn-2.5.0.orig/manual/text/column_box.rb 0000664 0000000 0000000 00000002746 14571572164 020243 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Column Box'
text do
prose <<~TEXT
The column_box method allows you to define columns that flow
their contents from one section to the next. You can have a number of
columns on the page, and only when the last column overflows will a new
page be created.
TEXT
end
example new_page: true do
move_down 30
text 'The Prince', align: :center, size: 18
text 'Niccolò Machiavelli', align: :center, size: 14
move_down 30
column_box([0, cursor], columns: 2, width: bounds.width) do
text("#{<<~TEXT.gsub(/\s+/, ' ')}\n\n" * 5)
All the States and Governments by which men are or ever have been ruled,
have been and are either Republics or Princedoms. Princedoms are either
hereditary, in which the sovereignty is derived through an ancient line
of ancestors, or they are new. New Princedoms are either wholly new, as
that of Milan to Francesco Sforza; or they are like limbs joined on to
the hereditary possessions of the Prince who acquires them, as the
Kingdom of Naples to the dominions of the King of Spain. The States thus
acquired have either been used to live under a Prince or have been free;
and he who acquires them does so either by his own arms or by the arms of
others, and either by good fortune or by merit.
TEXT
end
end
end
ruby-prawn-2.5.0.orig/manual/text/color.rb 0000664 0000000 0000000 00000001156 14571572164 017206 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Color'
text do
prose <<~TEXT
The :color attribute can give a block of text a default
color, in RGB hex format or 4-value CMYK.
TEXT
end
example do
text 'Default color is black'
move_down 25
text 'Changed to red', color: 'FF0000'
move_down 25
text 'CMYK color', color: [22, 55, 79, 30]
move_down 25
text(
"Also works with inline formatting",
color: '0000FF',
inline_format: true,
)
end
end
ruby-prawn-2.5.0.orig/manual/text/alignment.rb 0000664 0000000 0000000 00000003217 14571572164 020046 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Alignment'
text do
prose <<~TEXT
Horizontal text alignment can be achieved by supplying the
:align option to the text methods. Available options are
:left (default), :right, :center,
and :justify.
Vertical text alignment can be achieved using the :valign
option with the text methods. Available options are :top
(default), :center, and :bottom.
Both forms of alignment will be evaluated in the context of the current
bounding_box.
TEXT
end
example new_page: true do
text 'This text should be left aligned'
text 'This text should be centered', align: :center
text 'This text should be right aligned', align: :right
y = cursor - 20
bounding_box([0, y], width: 250, height: 220) do
text 'This text is flowing from the left. ' * 4
move_down 15
text 'This text is flowing from the center. ' * 3, align: :center
move_down 15
text 'This text is flowing from the right. ' * 4, align: :right
move_down 15
text 'This text is justified. ' * 6, align: :justify
transparent(0.5) { stroke_bounds }
end
bounding_box([270, y], width: 250, height: 220) do
text 'This text should be vertically top aligned'
text 'This text should be vertically centered', valign: :center
text 'This text should be vertically bottom aligned', valign: :bottom
transparent(0.5) { stroke_bounds }
end
end
end
ruby-prawn-2.5.0.orig/manual/text.rb 0000664 0000000 0000000 00000002331 14571572164 016064 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Text')
prose <<-TEXT
This is probably the feature people will use the most. There is no
shortage of options when it comes to text. You'll be hard pressed to
find a use case that is not covered by one of the text methods and
configurable options.
The examples show:
TEXT
list(
'Text that flows from page to page automatically starting new pages when necessary',
'How to use text boxes and place them on specific positions',
'What to do when a text box is too small to fit its content',
'Flowing text in columns',
'How to change the text style configuring font, size, alignment and many other settings',
'How to style specific portions of a text with inline styling and formatted text',
'How to define formatted callbacks to reuse common styling definitions',
'How to use the different rendering modes available for the text methods',
'How to create your custom text box extensions',
'How to use external fonts on your pdfs',
'What happens when rendering text in different languages',
)
end
end
ruby-prawn-2.5.0.orig/manual/table.rb 0000664 0000000 0000000 00000000542 14571572164 016171 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Prawn::Table'
text do
prose <<~TEXT
As of Prawn 1.2.0, Prawn::Table has been extracted into its own
semi-officially supported gem.
Please see https://github.com/prawnpdf/prawn-table for more details.
TEXT
end
end
ruby-prawn-2.5.0.orig/manual/security/ 0000775 0000000 0000000 00000000000 14571572164 016423 5 ustar root root ruby-prawn-2.5.0.orig/manual/security/permissions.rb 0000664 0000000 0000000 00000003517 14571572164 021331 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Permissions'
text do
prose <<~TEXT
Some permissions may be set for the regular user with the following
options: :print_document, :modify_contents,
:copy_contents, :modify_annotations. All this
options default to true, so if you'd like to revoke just set
them to false.
A user may bypass all permissions if he provides the owner password which
may be set with the :owner_password option. This option may
be set to :random so that users will never be able to bypass
permissions.
There are some caveats when encrypting your PDFs. Be sure to read the source
documentation (you can find it here:
https://github.com/prawnpdf/prawn/blob/master/lib/prawn/security.rb\u200B)
before using this for anything super serious.
TEXT
end
example eval: false, standalone: true do
# User cannot print the document.
Prawn::Document.generate('cannot_print.pdf') do
text "If you used the user password you won't be able to print the doc."
encrypt_document(
user_password: 'foo',
owner_password: 'bar',
permissions: { print_document: false },
)
end
# All permissions revoked and owner password set to random
Prawn::Document.generate('no_permissions.pdf') do
text "You may only view this and won't be able to use the owner password."
encrypt_document(
user_password: 'foo',
owner_password: :random,
permissions: {
print_document: false,
modify_contents: false,
copy_contents: false,
modify_annotations: false,
},
)
end
end
end
ruby-prawn-2.5.0.orig/manual/security/encryption.rb 0000664 0000000 0000000 00000002516 14571572164 021146 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Encryption'
text do
prose <<~TEXT
The encrypt_document method, as you might have already
guessed, is used to encrypt the PDF document.
Once encrypted whoever is using the document will need the user password
to read the document. This password can be set with the
:user_password option. If this is not set the document will
be encrypted but a password will not be needed to read the document.
There are some caveats when encrypting your PDFs. Be sure to read the
source documentation (you can find it here:
https://github.com/prawnpdf/prawn/blob/master/lib/prawn/security.rb\u200B)
before using this for anything super serious.
TEXT
end
example eval: false, standalone: true do
# Bare encryption. No password needed.
Prawn::ManualBuilder::Example.generate('bare_encryption.pdf') do
text 'See, no password was asked but the document is still encrypted.'
encrypt_document
end
# Simple password. All permissions granted.
Prawn::ManualBuilder::Example.generate('simple_password.pdf') do
text 'You was asked for a password.'
encrypt_document(user_password: 'foo', owner_password: 'bar')
end
end
end
ruby-prawn-2.5.0.orig/manual/security.rb 0000664 0000000 0000000 00000001070 14571572164 016746 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Security')
prose <<-TEXT
Security lets you control who can read the document by defining
a password.
The examples include:
TEXT
list(
'How to encrypt the document without the need for a password',
'How to configure the regular user permissions',
'How to require a password for the regular user',
'How to set a owner password that bypass the document permissions',
)
end
end
ruby-prawn-2.5.0.orig/manual/repeatable_content/ 0000775 0000000 0000000 00000000000 14571572164 020412 5 ustar root root ruby-prawn-2.5.0.orig/manual/repeatable_content/stamp.rb 0000664 0000000 0000000 00000002607 14571572164 022070 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Stamp'
text do
prose <<~TEXT
Stamps should be used when you have content that will be included
multiple times in a document. Its advantages over creating the content
anew each time are:
TEXT
ordered_list(
'Faster document creation',
'Smaller final document',
'Faster display on subsequent displays of the repeated element because ' \
'the viewer application can cache the rendered results',
)
prose <<~TEXT
The create_stamp method does just what it says. Pass it a
block with the content that should be generated and the stamp will be
created.
There are two methods to render the stamp on a page stamp
and stamp_at. The first will render the stamp as is while
the second accepts a point to serve as an offset to the stamp content.
TEXT
end
example do
create_stamp('approved') do
rotate(30, origin: [-5, -5]) do
stroke_color 'FF3333'
stroke_ellipse [0, 0], 29, 15
stroke_color '000000'
fill_color '993333'
font('Times-Roman') do
draw_text 'Approved', at: [-23, -3]
end
fill_color '000000'
end
end
stamp 'approved'
stamp_at 'approved', [200, 100]
end
end
ruby-prawn-2.5.0.orig/manual/repeatable_content/repeater.rb 0000664 0000000 0000000 00000003140 14571572164 022544 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Repeater'
text do
prose <<~TEXT
The repeat method is quite versatile when it comes to define
the intervals at which the content block should repeat.
The interval may be a symbol (:all, :odd,
:even), an array listing the pages, a range or a
Proc that receives the page number as an argument and should
return true if the content is to be repeated on the given page.
You may also pass an option :dynamic to reevaluate the code
block on every call which is useful when the content changes based on the
page number.
It is also important to say that no matter where you define the repeater
it will be applied to all matching pages.
TEXT
end
example eval: false do
repeat(:all) do
draw_text 'All pages', at: bounds.top_left
end
repeat(:odd) do
draw_text 'Only odd pages', at: [0, 0]
end
repeat(:even) do
draw_text 'Only even pages', at: [0, 0]
end
repeat([1, 3, 7]) do
draw_text 'Only on pages 1, 3 and 7', at: [100, 0]
end
repeat(2..4) do
draw_text 'From the 2nd to the 4th page', at: [300, 0]
end
repeat(->(pg) { (pg % 3).zero? }) do
draw_text 'Every third page', at: [250, 20]
end
repeat(:all, dynamic: true) do
draw_text page_number, at: [500, 0]
end
10.times do
start_new_page
draw_text 'A wonderful page', at: [400, 400]
end
end
end
ruby-prawn-2.5.0.orig/manual/repeatable_content/page_numbering.rb 0000664 0000000 0000000 00000003567 14571572164 023734 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Page Numbering'
text do
prose <<~TEXT
The number_pages method is a simple way to number the pages
of your document. It should be called towards the end of the document
since pages created after the call won't be numbered.
It accepts a string and a hash of options:
TEXT
list(
'start_count_at is the value from which to start numbering pages',
'total_pages If provided, will replace total ' \
'with the value given. Useful for overriding the total number of pages ' \
'when using the start_count_at option.',
'page_filter, which is one of: :all, ' \
':odd, :even, an array, a range, or a Proc that ' \
'receives the page number as an argument and should return true if the page ' \
'number should be printed on that page.',
'color which accepts the same values as fill_color',
'As well as any option accepted by text_box',
)
end
example eval: false do
text 'This is the first page!'
10.times do
start_new_page
text 'Here comes yet another page.'
end
string = 'page of '
# Green page numbers 1 to 7
options = {
at: [bounds.right - 150, 0],
width: 150,
align: :right,
page_filter: (1..7),
start_count_at: 1,
color: '007700',
}
number_pages string, options
# Gray page numbers from 8 on up
options[:page_filter] = ->(pg) { pg > 7 }
options[:start_count_at] = 8
options[:color] = '333333'
number_pages string, options
start_new_page
text "See. This page isn't numbered and doesn't count towards the total."
end
end
ruby-prawn-2.5.0.orig/manual/repeatable_content/alternate_page_numbering.rb 0000664 0000000 0000000 00000002021 14571572164 025753 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Alternating Page Numbering'
text do
prose <<~TEXT
Below is the code to generate page numbers that alternate being rendered
on the right and left side of the page. The first page will have a "1" in
the bottom right corner. The second page will have a "2" in the bottom
left corner of the page. The third a "3" in the bottom right, etc.
TEXT
end
example eval: false do
text 'This is the first page!'
10.times do
start_new_page
text 'Here comes yet another page.'
end
string = ''
odd_options = {
at: [bounds.right - 150, 0],
width: 150,
align: :right,
page_filter: :odd,
start_count_at: 1,
}
even_options = {
at: [0, bounds.left],
width: 150,
align: :left,
page_filter: :even,
start_count_at: 2,
}
number_pages string, odd_options
number_pages string, even_options
end
end
ruby-prawn-2.5.0.orig/manual/repeatable_content.rb 0000664 0000000 0000000 00000001375 14571572164 020745 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Repeatable Content')
prose <<-TEXT
Prawn offers two ways to handle repeatable content blocks. Repeater is
useful for content that gets repeated at well defined intervals while
Stamp is more appropriate if you need better control of when to repeat
it.
There is also one very specific helper for numbering pages.
The examples show:
TEXT
list(
'How to repeat content on several pages with a single invocation',
'How to create a new Stamp',
'How to "stamp" the content block on the page',
'How to number the document pages with one simple call',
)
end
end
ruby-prawn-2.5.0.orig/manual/outline/ 0000775 0000000 0000000 00000000000 14571572164 016233 5 ustar root root ruby-prawn-2.5.0.orig/manual/outline/sections_and_pages.rb 0000664 0000000 0000000 00000004761 14571572164 022420 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Sections and Pages'
text do
prose <<~TEXT
The document outline tree is the set of links used to navigate through
the various document sections and pages.
To define the document outline we first use the outline
method to lazily instantiate an outline object. Then we use the
define method with a block to start the outline tree.
The basic methods for creating outline nodes are section and
page. The only difference between the two is that
page doesn't accept a block and will only create leaf nodes
while section accepts a block to create nested nodes.
section accepts the title of the section and two options:
:destination - a page number to link and
:closed - a boolean value that defines if the nested outline
nodes are shown when the document is open (defaults to true).
page is very similar to section. It requires a
:title option to be set and accepts a
:destination.
section and page may also be used without the
define method but they will need to instantiate the
outline object every time.
TEXT
end
example eval: false do
# First we create 10 pages just to have something to link to
(1..10).each do |index|
text "Page #{index}"
start_new_page
end
outline.define do
section('Section 1', destination: 1) do
page title: 'Page 2', destination: 2
page title: 'Page 3', destination: 3
end
section('Section 2', destination: 4) do
page title: 'Page 5', destination: 5
section('Subsection 2.1', destination: 6, closed: true) do
page title: 'Page 7', destination: 7
end
end
end
# Outside of the define block
outline.section('Section 3', destination: 8) do
outline.page(title: 'Page 9', destination: 9)
end
outline.page(title: 'Page 10', destination: 10)
# Section and Pages without links. While a section without a link may be
# useful to group some pages, a page without a link is useless
outline.update do # update is an alias to define
section('Section without link') do
page title: 'Page without link'
end
end
end
end
ruby-prawn-2.5.0.orig/manual/outline/insert_section_after.rb 0000664 0000000 0000000 00000002643 14571572164 022776 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Insert Section After'
text do
prose <<~TEXT
Another way to insert nodes into an existing outline is the
insert_section_after method.
It accepts the title of the node that the new section will go after and a
block declaring the new section.
As is the case with add_subsection_to the section added
doesn't need to be a section, it may be just a page.
TEXT
end
example eval: false do
# First we create 10 pages and some default outline
(1..10).each do |index|
text "Page #{index}"
start_new_page
end
outline.define do
section('Section 1', destination: 1) do
page title: 'Page 2', destination: 2
page title: 'Page 3', destination: 3
end
end
# Now we will start adding nodes to the previous outline
outline.insert_section_after('Page 2') do
outline.section('Section after Page 2') do
outline.page(title: 'Page 4', destination: 4)
end
end
outline.insert_section_after('Section 1') do
outline.section('Section after Section 1') do
outline.page(title: 'Page 5', destination: 5)
end
end
# Adding just a page
outline.insert_section_after('Page 3') do
outline.page(title: 'Page after Page 3', destination: 6)
end
end
end
ruby-prawn-2.5.0.orig/manual/outline/add_subsection_to.rb 0000664 0000000 0000000 00000004076 14571572164 022257 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Adding a Subsection to the Outline Tree'
text do
prose <<~TEXT
We have already seen how to define an outline tree sequentially.
If you'd like to add nodes to the middle of an outline tree the
add_subsection_to may help you.
It allows you to insert sections to the outline tree at any point. Just
provide the title of the parent section, the
position you want the new subsection to be inserted
:first or :last (defaults to
:last) and a block to declare the subsection.
The add_subsection_to block doesn't necessarily create new
sections, it may also create new pages.
If the parent title provided is the title of a page. The page will be
converted into a section to receive the subsection created.
TEXT
end
example eval: false do
# First we create 10 pages and some default outline
(1..10).each do |index|
text "Page #{index}"
start_new_page
end
outline.define do
section('Section 1', destination: 1) do
page title: 'Page 2', destination: 2
page title: 'Page 3', destination: 3
end
end
# Now we will start adding nodes to the previous outline
outline.add_subsection_to('Section 1', :first) do
outline.section('Added later - first position') do
outline.page(title: 'Page 4', destination: 4)
outline.page(title: 'Page 5', destination: 5)
end
end
outline.add_subsection_to('Section 1') do
outline.page(title: 'Added later - last position', destination: 6)
end
outline.add_subsection_to('Added later - first position') do
outline.page(title: 'Another page added later', destination: 7)
end
# The title provided is for a page which will be converted into a section
outline.add_subsection_to('Page 3') do
outline.page(title: 'Last page added', destination: 8)
end
end
end
ruby-prawn-2.5.0.orig/manual/outline.rb 0000664 0000000 0000000 00000000732 14571572164 016562 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Outline')
prose <<-TEXT
The outline of a PDF document is the table of contents tab you see to
the right or left of your PDF viewer.
The examples include:
TEXT
list(
'How to define sections and pages',
'How to insert sections and/or pages to a previously defined outline structure',
)
end
end
ruby-prawn-2.5.0.orig/manual/manual.rb 0000664 0000000 0000000 00000010747 14571572164 016367 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Manual.new(__dir__, page_size: 'A4') do
part 'cover'
part 'how_to_read_this_manual'
# Core chapters
section 'Basic Concepts' do
part 'basic_concepts'
chapter 'basic_concepts/creation'
chapter 'basic_concepts/origin'
chapter 'basic_concepts/cursor'
chapter 'basic_concepts/other_cursor_helpers'
chapter 'basic_concepts/adding_pages'
chapter 'basic_concepts/measurement'
chapter 'basic_concepts/view'
end
section 'Graphics' do
part 'graphics'
section 'Basics' do
chapter 'graphics/helper'
chapter 'graphics/fill_and_stroke'
end
section 'Shapes' do
chapter 'graphics/lines_and_curves'
chapter 'graphics/common_lines'
chapter 'graphics/rectangle'
chapter 'graphics/polygon'
chapter 'graphics/circle_and_ellipse'
end
section 'Fill and Stroke Settings' do
chapter 'graphics/line_width'
chapter 'graphics/stroke_cap'
chapter 'graphics/stroke_join'
chapter 'graphics/stroke_dash'
chapter 'graphics/color'
chapter 'graphics/gradients'
chapter 'graphics/transparency'
chapter 'graphics/soft_masks'
chapter 'graphics/blend_mode'
chapter 'graphics/fill_rules'
end
section 'Transformations' do
chapter 'graphics/rotate'
chapter 'graphics/translate'
chapter 'graphics/scale'
end
end
section 'Text' do
part 'text'
section 'Basics' do
chapter 'text/free_flowing_text'
chapter 'text/positioned_text'
chapter 'text/text_box_overflow'
chapter 'text/text_box_excess'
chapter 'text/column_box'
end
section 'Styling' do
chapter 'text/font'
chapter 'text/font_size'
chapter 'text/font_style'
chapter 'text/color'
chapter 'text/alignment'
chapter 'text/leading'
chapter 'text/kerning_and_character_spacing'
chapter 'text/paragraph_indentation'
chapter 'text/rotation'
end
section 'Advanced Styling' do
chapter 'text/inline'
chapter 'text/formatted_text'
chapter 'text/formatted_callbacks'
chapter 'text/rendering_and_color'
chapter 'text/text_box_extensions'
end
section 'External Fonts' do
chapter 'text/single_usage'
chapter 'text/registering_families'
end
section 'Multilingualization' do
chapter 'text/utf8'
chapter 'text/line_wrapping'
chapter 'text/right_to_left_text'
chapter 'text/fallback_fonts'
chapter 'text/win_ansi_charset'
end
end
section 'Bounding Box' do
part 'bounding_box'
section 'Basics' do
chapter 'bounding_box/creation'
chapter 'bounding_box/bounds'
end
section 'Advanced' do
chapter 'bounding_box/stretchy'
chapter 'bounding_box/nesting'
chapter 'bounding_box/indentation'
chapter 'bounding_box/canvas'
chapter 'bounding_box/recursive_boxes'
end
end
# Remaining chapters
section 'Layout' do
part 'layout'
chapter 'layout/simple_grid'
chapter 'layout/boxes'
chapter 'layout/content'
end
chapter 'table'
section 'Images' do
part 'images'
section 'Basics' do
chapter 'images/plain_image'
chapter 'images/absolute_position'
end
section 'Relative Positioning' do
chapter 'images/horizontal'
chapter 'images/vertical'
end
section 'Size' do
chapter 'images/width_and_height'
chapter 'images/scale'
chapter 'images/fit'
end
end
section 'Document and Page Options' do
part 'document_and_page_options'
chapter 'document_and_page_options/page_size'
chapter 'document_and_page_options/page_margins'
chapter 'document_and_page_options/background'
chapter 'document_and_page_options/metadata'
chapter 'document_and_page_options/print_scaling'
end
section 'Outline' do
part 'outline'
section 'Basics' do
chapter 'outline/sections_and_pages'
end
section 'Adding Nodes Later' do
chapter 'outline/add_subsection_to'
chapter 'outline/insert_section_after'
end
end
section 'Repeatable Content' do
part 'repeatable_content'
chapter 'repeatable_content/repeater'
chapter 'repeatable_content/stamp'
chapter 'repeatable_content/page_numbering'
chapter 'repeatable_content/alternate_page_numbering'
end
section 'Security' do
part 'security'
chapter 'security/encryption'
chapter 'security/permissions'
end
end
ruby-prawn-2.5.0.orig/manual/layout/ 0000775 0000000 0000000 00000000000 14571572164 016071 5 ustar root root ruby-prawn-2.5.0.orig/manual/layout/simple_grid.rb 0000664 0000000 0000000 00000001652 14571572164 020720 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Simple Grid'
text do
prose <<~TEXT
The document grid on Prawn is just a table-like structure with a defined
number of rows and columns. There are some helpers to create boxes of
content based on the grid coordinates.
define_grid accepts the following options which are pretty
much self-explanatory: :rows, :columns,
:gutter, :row_gutter,
:column_gutter.
TEXT
end
example do
# The grid only need to be defined once, but since all the examples should be
# able to run alone we are repeating it on every example
define_grid(columns: 5, rows: 8, gutter: 10)
text 'We defined the grid, roll over to the next page to see its outline'
start_new_page
grid.show_all
end
end
ruby-prawn-2.5.0.orig/manual/layout/content.rb 0000664 0000000 0000000 00000001473 14571572164 020075 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Content'
text do
prose <<~TEXT
Now that we know how to access the boxes we might as well add some
content to them.
This can be done by taping into the bounding box for a given grid box or
multi-box with the bounding_box method.
TEXT
end
example do
# The grid only need to be defined once, but since all the examples should be
# able to run alone we are repeating it on every example
define_grid(rows: 4, columns: 5, gutter: 10)
grid([1, 0], [3, 1]).bounding_box do
text "Adding some content to this multi_box.\n#{' _ ' * 200}"
end
grid(2, 3).bounding_box do
text "Just a little snippet here.\n#{' _ ' * 10}"
end
end
end
ruby-prawn-2.5.0.orig/manual/layout/boxes.rb 0000664 0000000 0000000 00000001702 14571572164 017536 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Boxes'
text do
prose <<~TEXT
After defined the grid is there but nothing happens. To start taking
effect we need to use the grid boxes.
grid has three different return values based on the
arguments received. With no arguments it will return the grid itself.
With integers it will return the grid box at those indices. With two
arrays it will return a multi-box spanning the region of the two grid
boxes at the arrays indices.
TEXT
end
example do
# The grid only need to be defined once, but since all the examples should be
# able to run alone we are repeating it on every example
define_grid(columns: 5, rows: 4, gutter: 10)
grid(0, 0).show
grid(1, 1).show
grid([2, 2], [3, 3]).show
grid([0, 4], [3, 4]).show
grid([3, 0], [3, 1]).show
end
end
ruby-prawn-2.5.0.orig/manual/layout.rb 0000664 0000000 0000000 00000000704 14571572164 016417 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Layout')
prose <<-TEXT
Prawn has support for two-dimensional grid based layouts out of the box.
The examples show:
TEXT
list(
'How to define the document grid',
'How to configure the grid rows and columns gutters',
'How to create boxes according to the grid',
)
end
end
ruby-prawn-2.5.0.orig/manual/images/ 0000775 0000000 0000000 00000000000 14571572164 016021 5 ustar root root ruby-prawn-2.5.0.orig/manual/images/width_and_height.rb 0000664 0000000 0000000 00000001544 14571572164 021643 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Width and Height'
text do
prose <<~TEXT
The image size can be set with the :width and
:height options.
If only one of those is provided, the image will be scaled
proportionally. When both are provided, the image will be stretched to
fit the dimensions without maintaining the aspect ratio.
TEXT
end
example do
text 'Scale by setting only the width'
image "#{Prawn::DATADIR}/images/pigs.jpg", width: 150
move_down 10
text 'Scale by setting only the height'
image "#{Prawn::DATADIR}/images/pigs.jpg", height: 80
move_down 10
text 'Stretch to fit the width and height provided'
image "#{Prawn::DATADIR}/images/pigs.jpg", width: 500, height: 100
end
end
ruby-prawn-2.5.0.orig/manual/images/vertical.rb 0000664 0000000 0000000 00000002010 14571572164 020150 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Vertical Positioning'
text do
prose <<~TEXT
To set the vertical position of an image use the :vposition
option.
It may be :top, :center, :bottom
or a number representing the y-offset from the top boundary.
TEXT
end
example new_page: true do
bounding_box([0, cursor], width: 500, height: 450) do
stroke_bounds
%i[top center bottom].each do |vposition|
text "Image vertically aligned to the #{vposition}.", valign: vposition
image "#{Prawn::DATADIR}/images/stef.jpg",
position: 220,
vposition: vposition
end
text_box 'The next image has a 100 point offset from the top boundary',
at: [bounds.width - 110, bounds.top - 10],
width: 100
image "#{Prawn::DATADIR}/images/stef.jpg",
position: :right,
vposition: 100
end
end
end
ruby-prawn-2.5.0.orig/manual/images/scale.rb 0000664 0000000 0000000 00000001117 14571572164 017435 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Scaling Pmages'
text do
prose <<~TEXT
To scale an image use the :scale option.
It scales the image proportionally given the provided value.
TEXT
end
example do
text 'Normal size'
image "#{Prawn::DATADIR}/images/stef.jpg"
move_down 10
text 'Scaled to 50%'
image "#{Prawn::DATADIR}/images/stef.jpg", scale: 0.5
move_down 10
text 'Scaled to 200%'
image "#{Prawn::DATADIR}/images/stef.jpg", scale: 2
end
end
ruby-prawn-2.5.0.orig/manual/images/plain_image.rb 0000664 0000000 0000000 00000001300 14571572164 020605 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Plain Image'
text do
prose <<~TEXT
To embed images onto your PDF file use the image method.
It accepts the file path of the image to be loaded and some optional
arguments.
If only the image path is provided the image will be rendered starting on
the cursor position. No manipulation is done with the image even if it
doesn't fit entirely on the page like the following snippet.
TEXT
end
example new_page: true do
text 'The image will go right below this line of text.'
image "#{Prawn::DATADIR}/images/pigs.jpg"
end
end
ruby-prawn-2.5.0.orig/manual/images/horizontal.rb 0000664 0000000 0000000 00000001625 14571572164 020543 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Horizontal Positioning'
text do
prose <<~TEXT
The image may be positioned relatively to the current bounding box. The
horizontal position may be set with the :position option.
It may be :left, :center, :right
or a number representing an x-offset from the left boundary.
TEXT
end
example new_page: true do
bounding_box([50, cursor], width: 400, height: 450) do
stroke_bounds
%i[left center right].each do |position|
text "Image aligned to the #{position}."
image "#{Prawn::DATADIR}/images/stef.jpg", position: position
end
text 'The next image has a 50 point offset from the left boundary'
image "#{Prawn::DATADIR}/images/stef.jpg", position: 50
end
end
end
ruby-prawn-2.5.0.orig/manual/images/fit.rb 0000664 0000000 0000000 00000001147 14571572164 017133 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Fiting'
text do
prose <<~TEXT
:fit option is useful when you want the image to have the
maximum size within a container preserving the aspect ratio without
overlapping.
Just provide the container width and height pair.
TEXT
end
example do
size = 300
text 'Using the fit option'
bounding_box([0, cursor], width: size, height: size) do
image "#{Prawn::DATADIR}/images/pigs.jpg", fit: [size, size]
stroke_bounds
end
end
end
ruby-prawn-2.5.0.orig/manual/images/absolute_position.rb 0000664 0000000 0000000 00000001600 14571572164 022105 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Absolute Positioning'
text do
prose <<~TEXT
One of the options that the image method accepts is
:at. If you've read some of the graphics examples you are
probably already familiar with it. Just provide it the upper-left corner
where you want the image placed.
While sometimes useful this option won't be practical. Notice that the
cursor won't be moved after the image is rendered and there is nothing
forbidding the text to overlap with the image.
TEXT
end
example do
y_position = cursor
text "The image won't go below this line of text."
image "#{Prawn::DATADIR}/images/fractal.jpg", at: [200, y_position]
text 'And this line of text will go just below the previous one.'
end
end
ruby-prawn-2.5.0.orig/manual/images.rb 0000664 0000000 0000000 00000001013 14571572164 016341 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Images')
prose <<-TEXT
Embedding images on PDF documents is fairly easy. Prawn supports both JPG
and PNG images.
The examples show:
TEXT
list(
'How to add an image to a page',
'How place the image on a specific position',
'How to configure the image dimensions by setting the width and ' \
'height or by scaling it',
)
end
end
ruby-prawn-2.5.0.orig/manual/how_to_read_this_manual.rb 0000664 0000000 0000000 00000004337 14571572164 021766 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
doc.move_down(Prawn::ManualBuilder::INNER_MARGIN)
header('How to read this manual')
prose <<~END_TEXT
This manual is a collection of examples categorized by theme and
organized from the least to the most complex. While it covers most of the
common use cases it is not a comprehensive guide.
The best way to read it depends on your previous knowledge of Prawn and
what you need to accomplish.
If you are beginning with Prawn the first chapter will teach you the most
basic concepts and how to create pdf documents. For an overview of the
other features each chapter beyond the first either has a Basics section
(which offer enough insight on the feature without showing all the
advanced stuff you might never use) or is simple enough with only a few
examples.
Once you understand the basics you might want to come back to this manual
looking for examples that accomplish tasks you need.
Advanced users are encouraged to go beyond this manual and read the
source code directly if any doubt is not directly covered on this manual.
END_TEXT
doc.move_down(Prawn::ManualBuilder::RHYTHM + Prawn::ManualBuilder::INNER_MARGIN)
header('Reading the examples')
prose <<~END_TEXT
The title of each example is the relative path from the Prawn source
manual/ folder.
The first body of text is the introductory text for the example.
Generally it is a short description of the features illustrated by the
example.
Next comes the example source code block in fixed width font.
Most of the example snippets illustrate features that alter the page in
place. The effect of these snippets is shown right below a dashed line.
If it doesn't make sense to evaluate the snippet inline, a box with the
link for the example file is shown instead.
Note that the stroke_axis method used throughout the manual
is part of standard Prawn. It is defined in this file:
https://github.com/prawnpdf/prawn/blob/master/lib/prawn/graphics.rb
END_TEXT
end
end
ruby-prawn-2.5.0.orig/manual/graphics/ 0000775 0000000 0000000 00000000000 14571572164 016354 5 ustar root root ruby-prawn-2.5.0.orig/manual/graphics/transparency.rb 0000664 0000000 0000000 00000001757 14571572164 021424 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Transparency'
text do
prose <<~TEXT
Although the name of the method is transparency, what we are
actually setting is the opacity for fill and stroke. So 0
means completely transparent and 1.0 means completely
opaque.
You may call it providing one or two values. The first value sets fill
opacity and the second value sets stroke opacity. If the second value is
omitted fill and stroke will have the same opacity.
TEXT
end
example do
self.line_width = 5
fill_color 'ff0000'
fill_rectangle [0, 100], 500, 100
fill_color '000000'
stroke_color 'ffffff'
base_x = 100
[[0.5, 1], 0.5, [1, 0.5]].each do |args|
transparent(*args) do
fill_circle [base_x, 100], 50
stroke_rectangle [base_x - 20, 100], 40, 80
end
base_x += 150
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/translate.rb 0000664 0000000 0000000 00000001267 14571572164 020704 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Translation'
text do
prose <<~TEXT
This transformation is used to translate the user space. Just provide the
x and y coordinates for the new origin.
TEXT
end
example axes: true do
1.upto(3) do |i|
x = i * 50
y = i * 100
translate(x, y) do
# Draw a point on the new origin
fill_circle [0, 0], 2
draw_text "New origin after translation to [#{x}, #{y}]", at: [5, -3]
stroke_rectangle [100, 50], 200, 30
text_box 'Top left corner at [100, 50]', at: [110, 40], width: 180
end
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/stroke_join.rb 0000664 0000000 0000000 00000001427 14571572164 021233 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Stroke Join'
text do
prose <<~TEXT
The join style defines how the intersection between two lines is drawn.
There are three types: :miter (the default),
:round and :bevel.
Just like cap_style, the difference between styles is better
seen with thicker lines.
TEXT
end
example do
self.line_width = 25
%i[miter round bevel].each_with_index do |style, i|
self.join_style = style
y = 200 - (i * 100)
stroke do
move_to(100, y)
line_to(200, y + 100)
line_to(300, y)
end
stroke_rectangle [400, y + 75], 50, 50
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/stroke_dash.rb 0000664 0000000 0000000 00000003042 14571572164 021206 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Stroke Dash Pattern'
text do
prose <<~TEXT
This sets the dashed pattern for lines and curves. The (dash) length
defines how long each dash will be.
The :space option defines the length of the space between
the dashes.
The :phase option defines the start point of the sequence of
dashes and spaces.
Complex dash patterns can be specified by using an array with alternating
dash/gap lengths for the first parameter (note that the
:space option is ignored in this case).
TEXT
end
example new_page: true do
move_down 20
dash([1, 2, 3, 2, 1, 5], phase: 6)
stroke_horizontal_line 50, 500
move_down 10
dash([1, 2, 3, 4, 5, 6, 7, 8])
stroke_horizontal_line 50, 500
base_y = cursor - 10
24.times do |i|
length = (i / 4) + 1
space = length # space between dashes same length as dash
phase = 0 # start with dash
case i % 4
when 0 then base_y -= 10
when 1 then phase = length # start with space between dashes
when 2 then space = length * 0.5 # space between dashes half as long as dash
when 3
space = length * 0.5 # space between dashes half as long as dash
phase = length # start with space between dashes
end
base_y -= 10
dash(length, space: space, phase: phase)
stroke_horizontal_line 50, 500, at: base_y - (2 * i)
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/stroke_cap.rb 0000664 0000000 0000000 00000002015 14571572164 021031 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Stroke Cap'
text do
prose <<~TEXT
The cap style defines how the edge of a line or curve will be drawn.
There are three types: :butt (the default),
:round and :projecting_square.
The difference is better seen with thicker lines. With :butt
lines are drawn starting and ending at the exact points provided. With
both :round and :projecting_square the line is
projected beyond the start and end points.
Just like line_width= the cap_style= method
needs an explicit receiver to work.
TEXT
end
example axes: true do
self.line_width = 25
%i[butt round projecting_square].each_with_index do |cap, i|
self.cap_style = cap
y = 250 - (i * 100)
stroke_horizontal_line 100, 300, at: y
stroke_circle [400, y], 15
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/soft_masks.rb 0000664 0000000 0000000 00000002277 14571572164 021062 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Soft Masks'
text do
prose <<~TEXT
Soft masks are used for more complex alpha channel manipulations. You can
use arbitrary drawing functions for creation of soft masks. The resulting
alpha channel is made of greyscale version of the drawing (luminosity
channel to be precise). So while you can use any combination of colors
for soft masks it's easier to use greyscales. Black will result in full
transparency and white will make region fully opaque.
Soft mask is a part of page graphic state. So if you want to apply soft
mask only to a part of page you need to enclose drawing instructions in
save_graphics_state block.
TEXT
end
example do
save_graphics_state do
soft_mask do
0.upto(15) do |i|
fill_color 0, 0, 0, 100.0 / 16.0 * (15 - i)
fill_circle [75 + (i * 25), 100], 60
end
end
%w[009ddc 963d97 e03a3e f5821f fdb827 61bb46].each_with_index do |color, i|
fill_color color
fill_rectangle [0, 60 + (i * 20)], 600, 20
end
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/scale.rb 0000664 0000000 0000000 00000002221 14571572164 017765 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Scaling'
text do
prose <<~TEXT
This transformation is used to scale the user space. Give it an scale
factor and an :origin point and everything inside the block
will be scaled using the origin point as reference.
If you omit the :origin option the page origin will be used.
TEXT
end
example do
width = 100
height = 50
y = 190
x = 50
stroke_rectangle [x, y], width, height
text_box 'reference rectangle', at: [x + 10, y - 10], width: width - 20
scale(2, origin: [x, y]) do
stroke_rectangle [x, y], width, height
text_box 'rectangle scaled from upper-left corner', at: [x, y - height - 5], width: width
end
x = 350
stroke_rectangle [x, y], width, height
text_box 'reference rectangle', at: [x + 10, y - 10], width: width - 20
scale(2, origin: [x + (width / 2), y - (height / 2)]) do
stroke_rectangle [x, y], width, height
text_box 'rectangle scaled from center', at: [x, y - height - 5], width: width
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/rotate.rb 0000664 0000000 0000000 00000001360 14571572164 020177 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Rotation'
text do
prose <<~TEXT
This transformation is used to rotate the user space. Give it an angle
and an :origin point about which to rotate and a block.
Everything inside the block will be drawn with the rotated coordinates.
The angle is in degrees.
If you omit the :origin option the page origin will be used.
TEXT
end
example do
fill_circle [270, 180], 2
12.times do |i|
rotate(i * 30, origin: [270, 180]) do
stroke_rectangle [350, 200], 80, 40
text_box "Rotated #{i * 30}°", size: 10, at: [360, 185]
end
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/rectangle.rb 0000664 0000000 0000000 00000001054 14571572164 020645 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Rectangles'
text do
prose <<~TEXT
To draw a rectangle, just provide the upper-left corner, width and height
to the rectangle method.
There's also rounded_rectangle. Just provide an additional
radius value for the rounded corners.
TEXT
end
example axes: true do
stroke do
rectangle [100, 300], 100, 200
rounded_rectangle [300, 300], 100, 200, 20
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/polygon.rb 0000664 0000000 0000000 00000001551 14571572164 020372 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Polygons'
text do
prose <<~TEXT
Drawing polygons in Prawn is easy, just pass a sequence of points to one
of the polygon family of methods.
Just like rounded_rectangle we also have
rounded_polygon. The only difference is the radius param
comes before the polygon points.
TEXT
end
example axes: true do
# Triangle
stroke_polygon [50, 200], [50, 300], [150, 300]
# Hexagon
fill_polygon [50, 150], [150, 200], [250, 150], [250, 50], [150, 0], [50, 50]
# Pentagram
pentagon_points = [500, 100], [430, 5], [319, 41], [319, 159], [430, 195]
pentagram_points = [0, 2, 4, 1, 3].map { |i| pentagon_points[i] }
stroke_rounded_polygon(20, *pentagram_points)
end
end
ruby-prawn-2.5.0.orig/manual/graphics/lines_and_curves.rb 0000664 0000000 0000000 00000002436 14571572164 022231 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Lines and Curves'
text do
prose <<~TEXT
Prawn supports drawing both lines and curves starting either at the
current position, or from a specified starting position.
line_to and curve_to set the drawing path from
the current drawing position to the specified point. The initial drawing
position can be set with move_to. They are useful when you
want to chain successive calls because the drawing position will be set
to the specified point afterwards.
line and curve set the drawing path between the
two specified points.
Both curve methods define a Bezier curve bounded by two aditional points
provided as the :bounds param.
TEXT
end
example axes: true do
# line_to and curve_to
stroke do
move_to 0, 0
line_to 100, 100
line_to 0, 100
curve_to [150, 200], bounds: [[20, 150], [120, 150]]
curve_to [200, 0], bounds: [[150, 200], [450, 10]]
end
# line and curve
stroke do
line [300, 200], [400, 50]
curve [500, 0], [400, 100], bounds: [[600, 200], [300, 290]]
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/line_width.rb 0000664 0000000 0000000 00000001704 14571572164 021031 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Line Width'
text do
prose <<~TEXT
The line_width= method sets the stroke width for subsequent
stroke calls.
Since Ruby assumes that an unknown variable on the left hand side of an
assignment is a local temporary, rather than a setter method, if you are
using the block call to Prawn::Document.generate without
passing params you will need to call line_width on self.
TEXT
end
example axes: true do
y = 225
3.times do |i|
case i
when 0 then line_width = 10 # This call will have no effect
when 1 then self.line_width = 10
when 2 then self.line_width = 25
end
stroke do
horizontal_line 25, 75, at: y
rectangle [225, y + 25], 50, 50
circle [450, y], 25
end
y -= 90
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/helper.rb 0000664 0000000 0000000 00000001747 14571572164 020171 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Stroke Axis'
text do
prose <<~TEXT
To produce this manual we use the stroke_axis helper method
within the examples.
stroke_axis prints the x and y axis for the current bounding
box with markers in 100 increments. The defaults can be changed with
various options.
Note that the examples define a custom :height option so
that only the example canvas is used (as seen with the output of the
first line of the example code).
TEXT
end
example do
stroke_axis
stroke_axis(
at: [70, 70],
height: 200,
step_length: 50,
negative_axes_length: 5,
color: '0000FF',
)
stroke_axis(
at: [140, 140],
width: 200,
height: Integer(cursor) - 140,
step_length: 20,
negative_axes_length: 40,
color: 'FF0000',
)
end
end
ruby-prawn-2.5.0.orig/manual/graphics/gradients.rb 0000664 0000000 0000000 00000003162 14571572164 020663 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Gradients'
text do
prose <<~TEXT
Note that because of the way PDF renders radial gradients in order to get
solid fill your start circle must be fully inside your end circle.
Otherwise you will get triangle fill like illustrated in the example
below.
TEXT
end
example new_page: true do
self.line_width = 10
# Linear Gradients
fill_gradient [0, 250], [100, 150], 'ff0000', '0000ff'
fill_rectangle [0, 250], 100, 100
stroke_gradient [150, 150], [250, 250], '00ffff', 'ffff00'
stroke_rectangle [150, 250], 100, 100
fill_gradient [300, 250], [400, 150], 'ff0000', '0000ff'
stroke_gradient [300, 150], [400, 250], '00ffff', 'ffff00'
fill_and_stroke_rectangle [300, 250], 100, 100
rotate 45, origin: [500, 200] do
stops = { 0 => 'ff0000', 0.6 => '999900', 0.8 => '00cc00', 1 => '4444ff' }
fill_gradient from: [460, 240], to: [540, 160], stops: stops
fill_rectangle [460, 240], 80, 80
end
# Radial gradients
fill_gradient [50, 50], 0, [50, 50], 70.71, 'ff0000', '0000ff'
fill_rectangle [0, 100], 100, 100
stroke_gradient [200, 50], 45, [200, 50], 70.71, '00ffff', 'ffff00'
stroke_rectangle [150, 100], 100, 100
stroke_gradient [350, 50], 45, [350, 50], 70.71, '00ffff', 'ffff00'
fill_gradient [350, 50], 0, [350, 50], 70.71, 'ff0000', '0000ff'
fill_and_stroke_rectangle [300, 100], 100, 100
fill_gradient [500, 100], 50, [500, 0], 0, 'ff0000', '0000ff'
fill_rectangle [450, 100], 100, 100
end
end
ruby-prawn-2.5.0.orig/manual/graphics/fill_rules.rb 0000664 0000000 0000000 00000003003 14571572164 021035 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Fill Rules'
text do
prose <<~TEXT
Prawn's fill operators (fill and
fill_and_stroke both accept a :fill_rule
option. These rules determine which parts of the page are counted as
"inside" vs. "outside" the path. There are two fill rules:
TEXT
list(
':nonzero_winding_number (default): a point is inside the ' \
'path if a ray from that point to infinity crosses a nonzero "net ' \
'number" of path segments, where path segments intersecting in one ' \
'direction are counted as positive and those in the other direction ' \
'negative.',
':even_odd: A point is inside the path if a ray from that ' \
'point to infinity crosses an odd number of path segments, regardless ' \
'of direction.',
)
prose <<~TEXT
The differences between the fill rules only come into play with complex
paths; they are identical for simple shapes.
TEXT
end
example axes: true do
pentagram = [[181, 95], [0, 36], [111, 190], [111, 0], [0, 154]]
stroke_color 'ff0000'
line_width 2
text_box 'Nonzero Winding Number', at: [10, 200]
polygon(*pentagram.map { |x, y| [x + 50, y] })
fill_and_stroke
text_box 'Even-Odd', at: [330, 200]
polygon(*pentagram.map { |x, y| [x + 330, y] })
fill_and_stroke(fill_rule: :even_odd)
end
end
ruby-prawn-2.5.0.orig/manual/graphics/fill_and_stroke.rb 0000664 0000000 0000000 00000002402 14571572164 022036 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Fill and Stroke'
text do
prose <<~TEXT
There are two drawing primitives in Prawn: fill and
stroke.
These are the methods that actually draw stuff on the document. All the
other drawing shapes like rectangle, circle or
line_to define drawing paths. These paths need to be either
stroked or filled to gain form on the document.
Calling these methods without a block will act on the drawing path that
has been defined prior to the call.
Calling with a block will act on the drawing path set within the block.
Most of the methods which define drawing paths have methods of the same
name starting with stroke_ and fill_ which create the drawing path and
then stroke or fill it.
TEXT
end
example axes: true do
# No block
line [0, 200], [100, 150]
stroke
rectangle [0, 100], 100, 100
fill
# With block
stroke { line [200, 200], [300, 150] }
fill { rectangle [200, 100], 100, 100 }
# Method hook
stroke_line [400, 200], [500, 150]
fill_rectangle [400, 100], 100, 100
end
end
ruby-prawn-2.5.0.orig/manual/graphics/common_lines.rb 0000664 0000000 0000000 00000001454 14571572164 021367 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Common Lines'
text do
prose <<~TEXT
Prawn provides helpers for drawing some commonly used lines:
vertical_line and horizontal_line do just what
their names imply. Specify the start and end point at a fixed coordinate
to define the line.
horizontal_rule draws a horizontal line on the current
bounding box from border to border, using the current y position.
TEXT
end
example axes: true do
stroke_color 'ff0000'
stroke do
# just lower the current y position
move_down 50
horizontal_rule
vertical_line 100, 300, at: 50
horizontal_line 200, 500, at: 150
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics/color.rb 0000664 0000000 0000000 00000001232 14571572164 020015 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Color'
text do
prose <<~TEXT
We can change the stroke and fill colors providing an HTML rgb 6 digit
color code string ("AB1234") or 4 values for CMYK.
TEXT
end
example do
# Fill with Orange using RGB (Unlike css, there is no leading #)
fill_color 'FF8844'
fill_polygon [50, 150], [150, 200], [250, 150], [250, 50], [150, 0], [50, 50]
# Stroke with Purple using CMYK
stroke_color 50, 100, 0, 0
stroke_rectangle [300, 300], 200, 100
# Both together
fill_and_stroke_circle [400, 100], 50
end
end
ruby-prawn-2.5.0.orig/manual/graphics/circle_and_ellipse.rb 0000664 0000000 0000000 00000001160 14571572164 022477 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Circles and Ellipses'
text do
prose <<~TEXT
To define a circle all you need is the center point and the
radius.
To define an ellipse you provide the center point and two
radii (or axes) values. If the second radius value is omitted, both radii
will be equal and you will end up drawing a circle.
TEXT
end
example axes: true do
stroke_circle [100, 300], 100
fill_ellipse [200, 100], 100, 50
fill_ellipse [400, 100], 50
end
end
ruby-prawn-2.5.0.orig/manual/graphics/blend_mode.rb 0000664 0000000 0000000 00000004102 14571572164 020766 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Blend Modes'
text do
prose <<~TEXT
Blend modes can be used to change the way two layers (images, graphics,
text, etc.) are blended together. The blend_mode method
accepts a single blend mode or an array of blend modes. PDF viewers
should blend the layers based on the first recognized blend mode.
Valid blend modes in v1.4 of the PDF spec include :Normal,
:Multiply, :Screen, :Overlay,
:Darken, :Lighten, :ColorDodge,
:ColorBurn, :HardLight,
:SoftLight, :Difference,
:Exclusion, :Hue, :Saturation,
:Color, and :Luminosity.
TEXT
end
example new_page: true do
# https://commons.wikimedia.org/wiki/File:Blend_modes_2.-bottom-layer.jpg#/media/File:Blend_modes_2.-bottom-layer.jpg
bottom_layer = "#{Prawn::DATADIR}/images/blend_modes_bottom_layer.jpg"
# https://commons.wikimedia.org/wiki/File:Blend_modes_1.-top-layer.jpg#/media/File:Blend_modes_1.-top-layer.jpg
top_layer = "#{Prawn::DATADIR}/images/blend_modes_top_layer.jpg"
blend_modes = %i[
Normal Multiply Screen Overlay Darken Lighten ColorDodge
ColorBurn HardLight SoftLight Difference Exclusion Hue
Saturation Color Luminosity
]
blend_modes.each_with_index do |blend_mode, index|
x = 5 + (index % 4 * 130)
y = cursor - (index / 4 * 195) - 5
image bottom_layer, at: [x, y], fit: [120, 120]
blend_mode(blend_mode) do
image top_layer, at: [x, y], fit: [120, 120]
end
y -= 130
fill_color '009ddc'
fill_rectangle [x, y], 75, 25
blend_mode(blend_mode) do
fill_color 'fdb827'
fill_rectangle [x + 50, y], 70, 25
end
y -= 30
fill_color '000000'
text_box blend_mode.to_s, at: [x, y]
end
end
end
ruby-prawn-2.5.0.orig/manual/graphics.rb 0000664 0000000 0000000 00000001664 14571572164 016710 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Graphics')
prose <<-TEXT
Here we show all the drawing methods provided by Prawn. Use them to draw
the most beautiful imaginable things.
Most of the content that you'll add to your pdf document will use the
graphics package. Even text is rendered on a page just like a rectangle
so even if you never use any of the shapes described here you should at
least read the basic examples.
The examples show:
TEXT
list(
'All the possible ways that you can fill or stroke shapes on a page',
'How to draw all the shapes that Prawn has to offer from a measly ' \
'line to a mighty polygon or ellipse',
'The configuration options for stroking lines and filling shapes',
'How to apply transformations to your drawing space',
)
end
end
ruby-prawn-2.5.0.orig/manual/document_and_page_options/ 0000775 0000000 0000000 00000000000 14571572164 021763 5 ustar root root ruby-prawn-2.5.0.orig/manual/document_and_page_options/print_scaling.rb 0000664 0000000 0000000 00000002152 14571572164 025144 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Print Scaling'
text do
prose <<~TEXT
(Optional; PDF 1.6) The page scaling option to be selected when a print
dialog is displayed for this document. Valid values are
None, which indicates that the print dialog should reflect
no page scaling, and AppDefault, which indicates that
applications should use the current print scaling. If this entry has an
unrecognized value, applications should use the current print scaling.
Default value: AppDefault.
Note: If the print dialog is suppressed and its parameters are provided
directly by the application, the value of this entry should still be
used.
TEXT
end
example eval: false, standalone: true do
Prawn::Document.generate(
'example.pdf',
page_layout: :landscape,
print_scaling: :none,
) do
text 'When you print this document, the scale to fit in print preview ' \
'should be disabled by default.'
end
end
end
ruby-prawn-2.5.0.orig/manual/document_and_page_options/page_size.rb 0000664 0000000 0000000 00000002532 14571572164 024260 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Page Size'
text do
prose <<~TEXT
Prawn comes with support for most of the common page sizes so you'll only
need to provide specific values if your intended format is not supported.
To see a list with all supported sizes take a look at
PDF::Core::PageGeometry.
To define the size use :page_size when creating new
documents and :size when starting new pages. The default
page size for new documents is LETTER (612.00 x 792.00).
You may also define the orientation of the page to be either portrait
(default) or landscape. Use :page_layout when creating new
documents and :layout when starting new pages.
TEXT
end
example eval: false, standalone: true do
Prawn::Document.generate(
'example.pdf',
page_size: 'EXECUTIVE',
page_layout: :landscape,
) do
text 'EXECUTIVE landscape page.'
custom_size = [275, 326]
['A4', 'TABLOID', 'B7', custom_size].each do |size|
start_new_page(size: size, layout: :portrait)
text "#{size} portrait page."
start_new_page(size: size, layout: :landscape)
text "#{size} landscape page."
end
end
end
end
ruby-prawn-2.5.0.orig/manual/document_and_page_options/page_margins.rb 0000664 0000000 0000000 00000002622 14571572164 024746 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Page Margins'
text do
prose <<~TEXT
The default margin for pages is 0.5 inch but you can change that with the
:margin option or if you'd like to have different margins
you can use the :left_margin, :right_margin,
:top_margin, :bottom_margin options.
These options are available both for starting new pages and creating new
documents.
TEXT
end
example eval: false, standalone: true do
Prawn::Document.generate('example.pdf', margin: 100) do
text '100 pts margins.'
stroke_bounds
start_new_page(left_margin: 300)
text '300 pts margin on the left.'
stroke_bounds
start_new_page(top_margin: 300)
text '300 pts margin both on the top and on the left. Notice that whenever ' \
'you set an option for a new page it will remain the default for the ' \
'following pages.'
stroke_bounds
start_new_page(margin: 50)
text '50 pts margins. Using the margin option will reset previous specific ' \
'calls to left, right, top and bottom margins.'
stroke_bounds
start_new_page(margin: [50, 100, 150, 200])
text 'There is also the shorthand CSS like syntax used here.'
stroke_bounds
end
end
end
ruby-prawn-2.5.0.orig/manual/document_and_page_options/metadata.rb 0000664 0000000 0000000 00000001614 14571572164 024072 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Document Metadata'
text do
prose <<~TEXT
To set the document metadata just pass a hash to the :info
option when creating new documents.
The keys in the example below are arbitrary, so you may add whatever keys
you want.
TEXT
end
example eval: false, standalone: true do
info = {
Title: 'My title',
Author: 'John Doe',
Subject: 'My Subject',
Keywords: 'test metadata ruby pdf dry',
Creator: 'ACME Soft App',
Producer: 'Prawn',
CreationDate: Time.now,
}
Prawn::Document.generate('example.pdf', info: info) do
text 'This is a test of setting metadata properties via the info option.'
text 'While the keys are arbitrary, the above example sets common attributes.'
end
end
end
ruby-prawn-2.5.0.orig/manual/document_and_page_options/background.rb 0000664 0000000 0000000 00000001565 14571572164 024436 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Background'
text do
prose <<~TEXT
Pass an image path to the :background option and it will be
used as the background for all pages.
This option can only be used on document creation.
TEXT
end
example eval: false, standalone: true do
img = "#{Prawn::DATADIR}/images/letterhead.jpg"
Prawn::Document.generate('example.pdf', background: img, margin: 100) do
text 'My report caption', size: 18, align: :right
move_down font.height * 2
text 'Here is my text explaining this report. ' * 20,
size: 12,
align: :left,
leading: 2
move_down font.height
text "I'm using a soft background. " * 40,
size: 12,
align: :left,
leading: 2
end
end
end
ruby-prawn-2.5.0.orig/manual/document_and_page_options.rb 0000664 0000000 0000000 00000001215 14571572164 022307 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Document and Page Options')
prose <<-TEXT
So far we've already seen how to create new documents and start new
pages. This chapter expands on the previous examples by showing other
options avialable. Some of the options are only available when creating
new documents.
The examples show:
TEXT
list(
'How to configure page size',
'How to configure page margins',
'How to use a background image',
'How to add metadata to the generated PDF',
)
end
end
ruby-prawn-2.5.0.orig/manual/cover.rb 0000664 0000000 0000000 00000002162 14571572164 016220 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
doc.move_down(200)
doc.image(
"#{Prawn::DATADIR}/images/prawn.png",
scale: 0.9,
at: [0, doc.cursor - 60],
)
doc.formatted_text_box(
[{ text: "Prawn\n", font: 'DejaVu', styles: [:bold], size: 85 }],
at: [160, doc.cursor - 50],
)
doc.formatted_text_box(
[{ text: 'by example', font: 'Iosevka', size: 58 }],
at: [165, doc.cursor - 130],
)
unless ENV['CI']
git_commit =
if Dir.exist?(File.expand_path('../.git', __dir__))
commit = `git show --pretty=%h`
"git commit: #{commit.lines.first}"
else
''
end
doc.canvas do
v_text = [
{
text: "Last Update: #{Time.now.strftime('%Y-%m-%d')}\n" \
"Prawn Version: #{Prawn::VERSION}\n#{git_commit}",
font: 'DejaVu',
size: 12,
},
]
h = doc.height_of_formatted(v_text)
doc.formatted_text_box(v_text, at: [370, h + 50])
end
end
end
end
ruby-prawn-2.5.0.orig/manual/contents.rb 0000664 0000000 0000000 00000001560 14571572164 016740 0 ustar root root # frozen_string_literal: true
# Generates the Prawn by example manual.
require_relative 'example_helper'
def prawn_manual_document
old_default_external_encoding = Encoding.default_external
Encoding.default_external = Encoding::UTF_8
Prawn::ManualBuilder::Example.new(
skip_page_creation: true,
page_size: 'FOLIO',
) do
load_page('', 'cover')
load_page('', 'how_to_read_this_manual')
# Core chapters
load_package('basic_concepts')
load_package('graphics')
load_package('text')
load_package('bounding_box')
# Remaining chapters
load_package('layout')
load_page('', 'table')
load_package('images')
load_package('document_and_page_options')
load_package('outline')
load_package('repeatable_content')
load_package('security')
end
ensure
Encoding.default_external = old_default_external_encoding
end
ruby-prawn-2.5.0.orig/manual/bounding_box/ 0000775 0000000 0000000 00000000000 14571572164 017231 5 ustar root root ruby-prawn-2.5.0.orig/manual/bounding_box/stretchy.rb 0000664 0000000 0000000 00000001670 14571572164 021427 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Stretchy Bounding Box'
text do
prose <<~TEXT
Bounding Boxes accept an optional :height parameter. Unless
it is provided the bounding box will be stretchy. It will expand the
height to fit all content generated inside it.
TEXT
end
example do
y_position = cursor
bounding_box([0, y_position], width: 200, height: 100) do
text 'This bounding box has a height of 100. If this text gets too large ' \
'it will flow to the next page.'
transparent(0.5) { stroke_bounds }
end
bounding_box([300, y_position], width: 200) do
text 'This bounding box has variable height. No matter how much text is ' \
'written here, the height will expand to fit.'
text ' _' * 100
text ' *' * 100
transparent(0.5) { stroke_bounds }
end
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box/recursive_boxes.rb 0000664 0000000 0000000 00000001764 14571572164 022775 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Recursive Boxes'
text do
prose <<~TEXT
This example is mostly just for fun, and shows how nested bounding boxes
can simplify calculations. See the "Bounding Box" section of the manual
for more basic uses.
TEXT
end
example do
def combine(horizontal_span, vertical_span)
vertical_span.flat_map do |y|
horizontal_span.zip([y] * horizontal_span.size)
end
end
def recurse_bounding_box(max_depth = 4, depth = 1)
width = (bounds.width - 15) / 2
height = (bounds.height - 15) / 2
left_top_corners = combine([5, bounds.right - width - 5], [bounds.top - 5, height + 5])
left_top_corners.each do |lt|
bounding_box(lt, width: width, height: height) do
stroke_bounds
recurse_bounding_box(max_depth, depth + 1) if depth < max_depth
end
end
end
recurse_bounding_box
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box/nesting.rb 0000664 0000000 0000000 00000003034 14571572164 021225 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Nesting Bounding Boxes'
text do
prose <<~TEXT
Normally when we provide the top left corner of a bounding box we express
the coordinates relative to the margin box. This is not the case when we
have nested bounding boxes. Once nested the inner bounding box
coordinates are relative to the outer bounding box.
This example shows some nested bounding boxes with fixed and stretchy
heights. Note how the cursor method returns coordinates
relative to the current bounding box.
TEXT
end
example new_page: true do
def box_content(string)
text(string)
transparent(0.5) { stroke_bounds }
end
gap = 20
bounding_box([50, cursor], width: 400, height: 200) do
box_content('Fixed height')
bounding_box([gap, cursor - gap], width: 300) do
text 'Stretchy height'
bounding_box([gap, bounds.top - gap], width: 100) do
text 'Stretchy height'
transparent(0.5) do
dash(1)
stroke_bounds
undash
end
end
bounding_box([gap * 7, bounds.top - gap], width: 100, height: 50) do
box_content('Fixed height')
end
transparent(0.5) do
dash(1)
stroke_bounds
undash
end
end
bounding_box([gap, cursor - gap], width: 300, height: 50) do
box_content('Fixed height')
end
end
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box/indentation.rb 0000664 0000000 0000000 00000002431 14571572164 022072 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Bounding Box Indentation'
text do
prose <<~TEXT
Sometimes you just need to indent a portion of the contents of a bounding
box, and using a nested bounding box is pure overkill. The
indent method is what you might need.
Just provide a number for it to indent all content generated inside the
block.
TEXT
end
example new_page: true do
text 'No indentation on the margin box.'
indent(20) do
text 'Some indentation inside an indent block.'
end
move_down 20
bounding_box([50, cursor], width: 400, height: cursor) do
transparent(0.5) { stroke_bounds }
move_down 10
text 'No indentation inside this bounding box.'
indent(40) do
text 'Inside an indent block. And so is this horizontal line:'
stroke_horizontal_rule
end
move_down 10
text 'No indentation'
move_down 20
indent(60) do
text 'Another indent block.'
bounding_box([0, cursor], width: 200) do
text 'Note that this bounding box coordinates are relative to the indent block'
transparent(0.5) { stroke_bounds }
end
end
end
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box/creation.rb 0000664 0000000 0000000 00000001503 14571572164 021361 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Bounding Box Creation'
text do
prose <<~TEXT
If you've read the basic concepts examples you probably know that the
origin of a page is on the bottom left corner and that the content flows
from top to bottom.
You also know that a Bounding Box is a structure for helping the content
flow.
A bounding box can be created with the bounding_box method.
Just provide the top left corner, a required :width option
and an optional :height.
TEXT
end
example do
bounding_box([200, cursor - 100], width: 200, height: 100) do
text 'Just your regular bounding box'
transparent(0.5) { stroke_bounds }
end
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box/canvas.rb 0000664 0000000 0000000 00000001614 14571572164 021033 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Canvas'
text do
prose <<~TEXT
The origin example already mentions that a new document already comes
with a margin box whose bottom left corner is used as the origin for
calculating coordinates.
What has not been told is that there is one helper for "bypassing" the
margin box: canvas. This method is a shortcut for creating a
bounding box mapped to the absolute coordinates and evaluating the code
inside it.
The following snippet draws a circle on each of the four absolute
corners.
TEXT
end
example do
canvas do
fill_circle [bounds.left, bounds.top], 30
fill_circle [bounds.right, bounds.top], 30
fill_circle [bounds.right, bounds.bottom], 30
fill_circle [0, 0], 30
end
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box/bounds.rb 0000664 0000000 0000000 00000003430 14571572164 021050 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Bounding Box Creation'
text do
prose <<~TEXT
The bounds method returns the current bounding box. This is
useful because the Prawn::BoundingBox exposes some nice
boundary helpers.
top, bottom, left and
right methods return the bounding box boundaries relative to
its translated origin. top_left, top_right,
bottom_left and bottom_right return those
boundaries pairs inside arrays.
All these methods have an "absolute" version like
absolute_right. The absolute version returns the same
boundary relative to the page absolute coordinates.
The following snippet shows the boundaries for the margin box side by
side with the boundaries for a custom bounding box.
TEXT
end
example new_page: true do
def print_coordinates
text("top: #{bounds.top}")
text("bottom: #{bounds.bottom}")
text("left: #{bounds.left}")
text("right: #{bounds.right}")
move_down(10)
text("absolute top: #{Float(bounds.absolute_top).round(2)}")
text("absolute bottom: #{Float(bounds.absolute_bottom).round(2)}")
text("absolute left: #{Float(bounds.absolute_left).round(2)}")
text("absolute right: #{Float(bounds.absolute_right).round(2)}")
end
move_down 20
text 'Margin box bounds:'
move_down 5
print_coordinates
bounding_box([250, cursor + 140], width: 200, height: 150) do
text 'This bounding box bounds:'
move_down 5
print_coordinates
transparent(0.5) { stroke_bounds }
end
end
end
ruby-prawn-2.5.0.orig/manual/bounding_box.rb 0000664 0000000 0000000 00000001153 14571572164 017556 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Bounding Box')
prose <<-TEXT
Bounding boxes are the basic containers for structuring the content
flow. Even being low level building blocks sometimes their simplicity is
very welcome.
The examples show:
TEXT
list(
'How to create bounding boxes with specific dimensions',
'How to inspect the current bounding box for its coordinates',
'Stretchy bounding boxes',
'Nested bounding boxes',
'Indent blocks',
)
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/ 0000775 0000000 0000000 00000000000 14571572164 017533 5 ustar root root ruby-prawn-2.5.0.orig/manual/basic_concepts/view.rb 0000664 0000000 0000000 00000003323 14571572164 021033 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'View'
text do
prose <<~TEXT
The recommended way to extend Prawn's functionality is to include the
Prawn::View mixin in your own class, which will make all
Prawn::Document methods available to your custom objects.
This approach is preferred over inheriting from
Prawn::Document, as your state will be kept completely
separate from Prawn::Document's, thus avoiding accidental
method collisions.
Note that Prawn::View lazily instantiates a
Prawn::Document with default initialization settings, such
as page size, layout, margins, etc.
By defining your own document method you will be able to
override those settings and initialize a Prawn::Document to
your heart's content. This method will be called repeatedly by
Prawn::View, so be sure to memoize the object by assigning
it to an instance variable via the ||= operator.
TEXT
end
# rubocop: disable Lint/ConstantDefinitionInBlock
example eval: false, standalone: true do
class Greeter
include Prawn::View
def initialize(name)
@name = name
end
def document
@document ||= Prawn::Document.new(page_size: 'A4', margin: 30)
end
def say_hello
font('Courier') do
text("Hello, #{@name}!")
end
end
end
greeter = Greeter.new('Gregory')
greeter.say_hello
greeter.save_as('greetings.pdf')
end
# rubocop: enable Lint/ConstantDefinitionInBlock
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/other_cursor_helpers.rb 0000664 0000000 0000000 00000002444 14571572164 024324 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Other Cursor Helpers'
text do
prose <<~TEXT
Another group of helpers for changing the cursor position are the pad
methods. They accept a numeric value and a block. pad will
use the numeric value to move the cursor down both before and after the
block content. pad_top will only move the cursor before the
block while pad_bottom will only move after.
float is a method for not changing the cursor. Pass it a
block and the cursor will remain on the same place when the block
returns.
TEXT
end
example new_page: true do
stroke_horizontal_rule
pad(20) { text 'Text padded both before and after.' }
stroke_horizontal_rule
pad_top(20) { text 'Text padded on the top.' }
stroke_horizontal_rule
pad_bottom(20) { text 'Text padded on the bottom.' }
stroke_horizontal_rule
move_down 30
text 'Text written before the float block.'
float do
move_down 30
bounding_box([0, cursor], width: 200) do
text 'Text written inside the float block.'
stroke_bounds
end
end
text 'Text written after the float block.'
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/origin.rb 0000664 0000000 0000000 00000002627 14571572164 021356 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Origin'
text do
prose <<~TEXT
This is the most important concept you need to learn about Prawn:
PDF documents have the origin [0,0] at the bottom-left corner
of the page.
A bounding box is a structure which provides boundaries for inserting
content. A bounding box also has the property of relocating the origin to
its relative bottom-left corner. However, be aware that the location
specified when creating a bounding box is its top-left corner, not
bottom-left (hence the [100, 300] coordinates below).
Even if you never create a bounding box explicitly, each document already
comes with one called the margin box. This initial bounding box is the
one responsible for the document margins.
So practically speaking the origin of a page on a default generated
document isn't the absolute bottom left corner but the bottom left corner
of the margin box.
The following snippet strokes a circle on the margin box origin. Then
strokes the boundaries of a bounding box and a circle on its origin.
TEXT
end
example axes: true do
stroke_circle [0, 0], 10
bounding_box([100, 200], width: 300, height: 100) do
stroke_bounds
stroke_circle [0, 0], 10
end
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/measurement.rb 0000664 0000000 0000000 00000001476 14571572164 022415 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Measurement Extensions'
text do
prose <<~TEXT
The base unit in Prawn is the PDF Point. One PDF Point is equal to 1/72
of an inch.
There is no need to waste time converting this measure. Prawn provides
helpers for converting from other measurements to PDF Points.
Just require "prawn/measurement_extensions" and it will mix
some helpers onto Numeric for converting common measurement
units to PDF Points.
TEXT
end
example do
require 'prawn/measurement_extensions'
%i[mm cm dm m in yd ft].each do |measurement|
text "1 #{measurement} in PDF Points: #{1.public_send(measurement)} pt"
move_down 5.mm
end
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/cursor.rb 0000664 0000000 0000000 00000002270 14571572164 021376 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Cursor'
text do
prose <<~TEXT
We normally write our documents from top to bottom and it is no different
with Prawn. Even if the origin is on the bottom left corner we still fill
the page from the top to the bottom. In other words the cursor for
inserting content starts on the top of the page.
Most of the functions that insert content on the page will start at the
current cursor position and proceed to the bottom of the page.
The following snippet shows how the cursor behaves when we add some text
to the page and demonstrates some of the helpers to manage the cursor
position. The cursor method returns the current cursor
position.
TEXT
end
example axes: true do
text "the cursor is here: #{cursor}"
text "now it is here: #{cursor}"
move_down 100
text "on the first move the cursor went down to: #{cursor}"
move_up 50
text "on the second move the cursor went up to: #{cursor}"
move_cursor_to 50
text "on the last move the cursor went directly to: #{cursor}"
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/creation.rb 0000664 0000000 0000000 00000003104 14571572164 021662 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Creating a PDF Document'
text do
prose <<~TEXT
There are three ways to create a PDF Document in Prawn: creating a new
Prawn::Document instance, or using the
Prawn::Document.generate method with and without block
arguments.
The following snippet showcase each way by creating a simple document with
some text drawn.
When we instantiate the Prawn::Document object the actual pdf
document will only be created after we call render_file.
The generate method will render the actual pdf object after exiting the
block. When we use it without a block argument the provided block is
evaluated in the context of a newly created Prawn::Document
instance. When we use it with a block argument a
Prawn::Document instance is created and passed to the block.
The generate method without block arguments requires less typing and
defines and renders the pdf document in one shot. Almost all of the
examples are coded this way.
TEXT
end
example eval: false, standalone: true do
# Assignment
pdf = Prawn::Document.new
pdf.text('Hello World')
pdf.render_file('assignment.pdf')
# Implicit Block
Prawn::Document.generate('implicit.pdf') do
text 'Hello World'
end
# Explicit Block
Prawn::Document.generate('explicit.pdf') do |pdf|
pdf.text('Hello World')
end
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts/adding_pages.rb 0000664 0000000 0000000 00000002001 14571572164 022456 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Chapter.new do
title 'Adding Pages'
text do
prose <<~TEXT
A PDF document is a collection of pages. When we create a new document be
it with Document.new or on a Document.generate
block one initial page is created for us.
Some methods might create new pages automatically like text
which will create a new page whenever the text string cannot fit on the
current page.
But what if you want to go to the next page by yourself? That is easy.
Just use the start_new_page method and a shiny new page will
be created for you just like in the following snippet.
TEXT
end
example do
text "We are still on the initial page for this example. Now I'll ask " \
'Prawn to gently start a new page. Please follow me to the next page.'
start_new_page
text "See. We've left the previous page behind."
end
end
ruby-prawn-2.5.0.orig/manual/basic_concepts.rb 0000664 0000000 0000000 00000001744 14571572164 020066 0 ustar root root # frozen_string_literal: true
require 'prawn/manual_builder'
Prawn::ManualBuilder::Peritext.new do
text do
header_with_bg('Basic Concepts')
prose <<~TEXT
This chapter covers the minimum amount of functionality you'll need to
start using Prawn.
If you are new to Prawn this is the first chapter to read. Once you are
comfortable with the concepts shown here you might want to check the
Basics section of the Graphics, Bounding Box and Text sections.
The examples show:
TEXT
list(
'How to create new pdf documents in every possible way',
'Where the origin for the document coordinates is. What are Bounding ' \
'Boxes and how they interact with the origin',
'How the cursor behaves',
'How to start new pages',
'What the base unit for measurement and coordinates is and how to use ' \
'other convenient measures',
"How to build custom view objects that use Prawn's DSL",
)
end
end
ruby-prawn-2.5.0.orig/manual/absolute_position.pdf 0000664 0000000 0000000 00000113256 14571572164 021021 0 ustar root root %PDF-1.3
%
1 0 obj
<< /Creator
/Producer
>>
endobj
2 0 obj
<< /Type /Catalog
/Pages 3 0 R
>>
endobj
3 0 obj
<< /Type /Pages
/Count 1
/Kids [5 0 R]
>>
endobj
4 0 obj
<< /Length 383
>>
stream
q
BT
36 747.384 Td
/F1.0 12 Tf
[<54686520696d6167652077> 10 <6f6e277420676f2062656c6f> 15 <772074686973206c696e65206f66207465> 30 <78742e>] TJ
ET
q
240.000 0 0 240.000 236.000 516.000 cm
/I1 Do
Q
BT
36 733.512 Td
/F1.0 12 Tf
[<416e642074686973206c696e65206f66207465> 30 <78742077696c6c20676f206a7573742062656c6f> 15 <772074686520707265> 30 <76696f7573206f6e65> 15 <2e>] TJ
ET
Q
endstream
endobj
5 0 obj
<< /Type /Page
/Parent 3 0 R
/MediaBox [0 0 612.0 792.0]
/Contents 4 0 R
/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font << /F1.0 6 0 R
>>
/XObject << /I1 7 0 R
>>
>>
>>
endobj
6 0 obj
<< /Type /Font
/Subtype /Type1
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
>>
endobj
7 0 obj
<< /Type /XObject
/Subtype /Image
/ColorSpace /DeviceCMYK
/BitsPerComponent 8
/Width 240
/Height 240
/Decode [1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0]
/Length 37174
/Filter [/DCTDecode]
>>
stream
JFIF H H Adobe d C !"$"$ C M Y K ; !1A"Qaq2#BR$3br4S C M Y K ? Y]qe#sX`20{ї̓&e?,W캗sRj?zagʱcC-\n<;ARw;t,%$w +Վj=A#?b$0[{6ԡ?k2?KgK}hngb%ufEAH >~q V$qom2a|~Ԗtiu1`A#knc*±գ\RZ"pInjhN 9gDD
llǃW>n$y4eJ1>xPNc*g[XJ!