Commit bd4607c6 authored by Cédric Boutillier's avatar Cédric Boutillier

Imported Upstream version 2.1.0+dfsg

parent 2d197802
.*.sw? .*.sw?
nbproject nbproject
pkg pkg
.rvmrc .rvmrc
.bundle .bundle
Gemfile.lock Gemfile.lock
drop_to_console.rb drop_to_console.rb
manual.pdf
/.idea /.idea
/doc /doc
/bin /bin
.DS_Store
*.pdf
language: ruby language: ruby
before_install:
- gem install bundler
- bundle --version
rvm: rvm:
- 2.0 - 2.0.0-p0
- 2.1 - 2.0.0-p648
- 2.2 - 2.1.0-p0
- 2.1.8
- 2.2.0-p0
- 2.2.4
- 2.3.0
- rbx-2 - rbx-2
- jruby - jruby-9.0.4.0
env:
JRUBY_OPTS=--2.0
matrix: matrix:
include:
- rvm: jruby-1.7.22
env: JRUBY_OPTS=--2.0
allow_failures: allow_failures:
- rvm: rbx-2 - rvm: rbx-2
## PrawnPDF master branch
## PrawnPDF 2.1.0 -- 2016-02-29
### Added support for PNG images with indexed transparency
Prawn now properly hadles transparency in PNG images with indexed color.
(Maciej Mucha, [#783](https://github.com/prawnpdf/prawn/issues/783); Alexander Mankuta, [#920](https://github.com/prawnpdf/prawn/pull/920))
### Prawn no longer generates IRB warnings
Fix a few issues with code style that were triggering warnings in IRB when run in verbose mode (`irb -w`).
(Jesse Doyle, [#914](https://github.com/prawnpdf/prawn/pull/914))
### Gradients applied inside transformations are now correctly positioned
PDF gradients/patterns take coordinates in the coordinate space of the
document, not the "user space", so if you performed a scale/rotate/translate
and then painted a gradient inside, it wasn't correctly positioned.
This change tracks transformations applied to the document, and multiplies
the gradient matrix with this tracked transformation matrix so that the
gradient appears in the correct place in the document.
Because this changes how the x and y coordinates are interpreted, you must
manually add `apply_transformations: true` to your `stroke_gradient` and
`fill_gradient` calls to use the fixed behaviour in Prawn 2. It is expected
that this will be the default in Prawn 3.
Please [refer to the wiki page on this change](https://github.com/prawnpdf/prawn/wiki/Gradient-Transformations)
for more information.
(Roger Nesbitt, [#891](https://github.com/prawnpdf/prawn/issues/891), [#894](https://github.com/prawnpdf/prawn/pull/894))
### Prawn::Graphics::BlendMode#blend_mode added
Blend modes can be used to change the way two layers are blended together. The
BM key is added to the External Graphics State based on the v1.4 PDF spec. `blend_mode`
accepts a single blend mode or array of blend modes. If an array is passed, the
PDF viewer blends layers based on the first valid blend mode.
## PrawnPDF 2.0.2 -- 2015-07-15 ## PrawnPDF 2.0.2 -- 2015-07-15
### Links in repeaters/stamps are now clickable ### Links in repeaters/stamps are now clickable
......
...@@ -148,9 +148,10 @@ If you contribute to Prawn, you will retain your own copyright but must agree to ...@@ -148,9 +148,10 @@ If you contribute to Prawn, you will retain your own copyright but must agree to
## History ## History
Prawn was originally developed by Gregory Brown, under the auspices of the Ruby Prawn was originally developed by [Gregory Brown](http://twitter.com/practicingruby),
Mendicant Project, a grassroots initiative in which the Ruby community under the auspices of the Ruby Mendicant Project, a grassroots initiative in which
collectively provided funding so that Gregory could take several months off from the Ruby community collectively provided funding so that Gregory could take
several months off from
work to focus on this project. work to focus on this project.
Over the last several years, we've received code contributions from dozens of Over the last several years, we've received code contributions from dozens of
......
data/images/fractal.jpg

6.48 KB | W: | H:

data/images/fractal.jpg

6.49 KB | W: | H:

data/images/fractal.jpg
data/images/fractal.jpg
data/images/fractal.jpg
data/images/fractal.jpg
  • 2-up
  • Swipe
  • Onion skin
data/images/pigs.jpg

9.47 KB | W: | H:

data/images/pigs.jpg

9.47 KB | W: | H:

data/images/pigs.jpg
data/images/pigs.jpg
data/images/pigs.jpg
data/images/pigs.jpg
  • 2-up
  • Swipe
  • Onion skin
data/images/stef.jpg

2.2 KB | W: | H:

data/images/stef.jpg

2.21 KB | W: | H:

data/images/stef.jpg
data/images/stef.jpg
data/images/stef.jpg
data/images/stef.jpg
  • 2-up
  • Swipe
  • Onion skin
...@@ -23,7 +23,7 @@ module Prawn ...@@ -23,7 +23,7 @@ module Prawn
FLOAT_PRECISION = 1.0e-9 FLOAT_PRECISION = 1.0e-9
# Whe set to true, Prawn will verify hash options to ensure only valid keys # When set to true, Prawn will verify hash options to ensure only valid keys
# are used. Off by default. # are used. Off by default.
# #
# Example: # Example:
...@@ -76,6 +76,7 @@ require_relative "prawn/images/png" ...@@ -76,6 +76,7 @@ require_relative "prawn/images/png"
require_relative "prawn/stamp" require_relative "prawn/stamp"
require_relative "prawn/soft_mask" require_relative "prawn/soft_mask"
require_relative "prawn/security" require_relative "prawn/security"
require_relative "prawn/transformation_stack"
require_relative "prawn/document" require_relative "prawn/document"
require_relative "prawn/font" require_relative "prawn/font"
require_relative "prawn/measurements" require_relative "prawn/measurements"
......
...@@ -57,6 +57,7 @@ module Prawn ...@@ -57,6 +57,7 @@ module Prawn
include Prawn::Images include Prawn::Images
include Prawn::Stamp include Prawn::Stamp
include Prawn::SoftMask include Prawn::SoftMask
include Prawn::TransformationStack
# @group Extension API # @group Extension API
......
...@@ -24,10 +24,18 @@ module Prawn ...@@ -24,10 +24,18 @@ module Prawn
# Perhaps they will become part of the extension API? # Perhaps they will become part of the extension API?
# Anyway, for now it's not clear what we should do w. them. # Anyway, for now it's not clear what we should do w. them.
delegate [ :graphic_state, delegate [ :graphic_state,
:save_graphics_state,
:restore_graphics_state,
:on_page_create ] => :renderer :on_page_create ] => :renderer
def save_graphics_state(state = nil, &block)
save_transformation_stack
renderer.save_graphics_state(state, &block)
end
def restore_graphics_state
restore_transformation_stack
renderer.restore_graphics_state
end
# FIXME: This is a circular reference, because in theory Prawn should # FIXME: This is a circular reference, because in theory Prawn should
# be passing instances of renderer to PDF::Core::Page, but it's # be passing instances of renderer to PDF::Core::Page, but it's
# passing Prawn::Document objects instead. # passing Prawn::Document objects instead.
......
...@@ -396,7 +396,20 @@ module Prawn ...@@ -396,7 +396,20 @@ module Prawn
# generate a font identifier that hasn't been used on the current page yet # generate a font identifier that hasn't been used on the current page yet
# #
def generate_unique_id def generate_unique_id
:"F#{@document.font_registry.size + 1}" key = nil
font_count = @document.font_registry.size + 1
loop do
key = :"F#{font_count}"
break if key_is_unique?(key)
font_count += 1
end
key
end
def key_is_unique?(test_key)
!@document.state.page.fonts.keys.any? do |key|
key.to_s.start_with?("#{test_key}.")
end
end end
def size def size
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
# #
# This is free software. Please see the LICENSE and COPYING files for details. # This is free software. Please see the LICENSE and COPYING files for details.
require_relative "graphics/blend_mode"
require_relative "graphics/color" require_relative "graphics/color"
require_relative "graphics/dash" require_relative "graphics/dash"
require_relative "graphics/cap_style" require_relative "graphics/cap_style"
...@@ -22,6 +23,7 @@ module Prawn ...@@ -22,6 +23,7 @@ module Prawn
# ruby-pdf.rubyforge.org # ruby-pdf.rubyforge.org
# #
module Graphics module Graphics
include BlendMode
include Color include Color
include Dash include Dash
include CapStyle include CapStyle
......
# encoding: utf-8
#
# blend_mode.rb : Implements blend modes
#
# Contributed by John Ford. October, 2015
#
# This is free software. Please see the LICENSE and COPYING files for details.
#
module Prawn
module Graphics
# The Prawn::BlendMode module is used to change the way
# two layers are blended together.
#
# Passing an array of blend modes is allowed. PDF viewers should
# blend 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.
#
# Example:
# pdf.fill_color('0000ff')
# pdf.fill_rectangle([x, y+25], 50, 50)
# pdf.blend_mode(:Multiply) do
# pdf.fill_color('ff0000')
# pdf.fill_circle([x, y], 25)
# end
#
module BlendMode
# @group Stable API
def blend_mode(blend_mode = :Normal)
renderer.min_version(1.4)
save_graphics_state if block_given?
renderer.add_content "/#{blend_mode_dictionary_name(blend_mode)} gs"
if block_given?
yield
restore_graphics_state
end
end
private
def blend_mode_dictionary_registry
@blend_mode_dictionary_registry ||= {}
end
def blend_mode_dictionary_name(blend_mode)
key = Array(blend_mode).join('')
dictionary_name = "BM#{key}"
dictionary = blend_mode_dictionary_registry[dictionary_name] ||= ref!(
:Type => :ExtGState,
:BM => blend_mode
)
page.ext_gstates.merge!(dictionary_name => dictionary)
dictionary_name
end
end
end
end
...@@ -14,16 +14,30 @@ module Prawn ...@@ -14,16 +14,30 @@ module Prawn
# Sets the fill gradient from color1 to color2. # Sets the fill gradient from color1 to color2.
# old arguments: point, width, height, color1, color2, options = {} # old arguments: point, width, height, color1, color2, options = {}
# new arguments: from, to, color1, color1 # new arguments: from, to, color1, color1, options = {}
# or from, r1, to, r2, color1, color2 # or from, r1, to, r2, color1, color2, options = {}
#
# Option :apply_transformations, if set true, will transform the
# gradient's co-ordinate space so it matches the current co-ordinate
# space of the document. This option will be the default from Prawn v3.
# The current default, false, will mean if you (for example) scale your
# document by 2 and put a gradient inside, you will have to manually
# multiply your co-ordinates by 2 so the gradient is correctly positioned.
def fill_gradient(*args) def fill_gradient(*args)
set_gradient(:fill, *args) set_gradient(:fill, *args)
end end
# Sets the stroke gradient from color1 to color2. # Sets the stroke gradient from color1 to color2.
# old arguments: point, width, height, color1, color2, options = {} # old arguments: point, width, height, color1, color2, options = {}
# new arguments: from, to, color1, color2 # new arguments: from, to, color1, color2, options = {}
# or from, r1, to, r2, color1, color2 # or from, r1, to, r2, color1, color2, options = {}
#
# Option :apply_transformations, if set true, will transform the
# gradient's co-ordinate space so it matches the current co-ordinate
# space of the document. This option will be the default from Prawn v3.
# The current default, false, will mean if you (for example) scale your
# document by 2 and put a gradient inside, you will have to manually
# multiply your co-ordinates by 2 so the gradient is correctly positioned.
def stroke_gradient(*args) def stroke_gradient(*args)
set_gradient(:stroke, *args) set_gradient(:stroke, *args)
end end
...@@ -31,15 +45,17 @@ module Prawn ...@@ -31,15 +45,17 @@ module Prawn
private private
def set_gradient(type, *grad) def set_gradient(type, *grad)
opts = grad.last.is_a?(Hash) ? grad.pop : {}
patterns = page.resources[:Pattern] ||= {} patterns = page.resources[:Pattern] ||= {}
registry_key = gradient_registry_key grad registry_key = gradient_registry_key grad, opts
if patterns["SP#{registry_key}"] if patterns["SP#{registry_key}"]
shading = patterns["SP#{registry_key}"] shading = patterns["SP#{registry_key}"]
else else
unless shading = gradient_registry[registry_key] unless shading = gradient_registry[registry_key]
shading = gradient(*grad) shading = gradient(grad, opts)
gradient_registry[registry_key] = shading gradient_registry[registry_key] = shading
end end
...@@ -59,18 +75,20 @@ module Prawn ...@@ -59,18 +75,20 @@ module Prawn
renderer.add_content "/SP#{registry_key} #{operator}" renderer.add_content "/SP#{registry_key} #{operator}"
end end
def gradient_registry_key(gradient) def gradient_registry_key(gradient, opts)
_x1, _y1, x2, y2, transformation = gradient_coordinates(gradient, opts)
if gradient[1].is_a?(Array) # axial if gradient[1].is_a?(Array) # axial
[ [
map_to_absolute(gradient[0]), transformation,
map_to_absolute(gradient[1]), x2, y2,
gradient[2], gradient[3] gradient[2], gradient[3]
] ]
else # radial else # radial
[ [
map_to_absolute(gradient[0]), transformation,
x2, y2,
gradient[1], gradient[1],
map_to_absolute(gradient[2]),
gradient[3], gradient[3],
gradient[4], gradient[5] gradient[4], gradient[5]
] ]
...@@ -81,11 +99,15 @@ module Prawn ...@@ -81,11 +99,15 @@ module Prawn
@gradient_registry ||= {} @gradient_registry ||= {}
end end
def gradient(*args) def gradient(args, opts)
if args.length != 4 && args.length != 6 if args.length != 4 && args.length != 6
fail ArgumentError, "Unknown type of gradient: #{args.inspect}" fail ArgumentError, "Unknown type of gradient: #{args.inspect}"
end end
if opts[:apply_transformations].nil? && current_transformation_matrix_with_translation(0, 0) != [1, 0, 0, 1, 0, 0]
warn "Gradients in Prawn 2.x and lower are not correctly positioned when a transformation has been made to the document. Pass 'apply_transformations: true' to correctly transform the gradient, or see https://github.com/prawnpdf/prawn/wiki/Gradient-Transformations for more information."
end
color1 = normalize_color(args[-2]).dup.freeze color1 = normalize_color(args[-2]).dup.freeze
color2 = normalize_color(args[-1]).dup.freeze color2 = normalize_color(args[-1]).dup.freeze
...@@ -104,10 +126,12 @@ module Prawn ...@@ -104,10 +126,12 @@ module Prawn
:N => 1.0 :N => 1.0
) )
x1, y1, x2, y2, transformation = gradient_coordinates(args, opts)
if args.length == 4 if args.length == 4
coords = [0, 0, args[1].first - args[0].first, args[1].last - args[0].last] coords = [0, 0, x2 - x1, y2 - y1]
else else
coords = [0, 0, args[1], args[2].first - args[0].first, args[2].last - args[0].last, args[3]] coords = [0, 0, args[1], x2 - x1, y2 - y1, args[3]]
end end
shading = ref!( shading = ref!(
...@@ -121,10 +145,22 @@ module Prawn ...@@ -121,10 +145,22 @@ module Prawn
ref!( ref!(
:PatternType => 2, # shading pattern :PatternType => 2, # shading pattern
:Shading => shading, :Shading => shading,
:Matrix => [1, 0, :Matrix => transformation
0, 1] + map_to_absolute(args[0])
) )
end end
def gradient_coordinates(args, opts)
x1, y1 = map_to_absolute(args[0])
x2, y2 = map_to_absolute(args[args.length == 4 ? 1 : 2])
transformation = if opts[:apply_transformations]
current_transformation_matrix_with_translation(x1, y1)
else
[1, 0, 0, 1, x1, y1]
end
[x1, y1, x2, y2, transformation]
end
end end
end end
end end
...@@ -145,6 +145,9 @@ module Prawn ...@@ -145,6 +145,9 @@ module Prawn
def transformation_matrix(a, b, c, d, e, f) def transformation_matrix(a, b, c, d, e, f)
values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ") values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ")
save_graphics_state if block_given? save_graphics_state if block_given?
add_to_transformation_stack(a, b, c, d, e, f)
renderer.add_content "#{values} cm" renderer.add_content "#{values} cm"
if block_given? if block_given?
yield yield
......
...@@ -68,9 +68,7 @@ module Prawn ...@@ -68,9 +68,7 @@ module Prawn
@transparency = {} @transparency = {}
case @color_type case @color_type
when 3 when 3
fail Errors::UnsupportedImageType, @transparency[:palette] = data.read(chunk_size).unpack('C*')
"Pallete-based transparency in PNG is not currently supported.\n" \
"See https://github.com/prawnpdf/prawn/issues/783"
when 0 when 0
# Greyscale. Corresponding to entries in the PLTE chunk. # Greyscale. Corresponding to entries in the PLTE chunk.
# Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1 # Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1
...@@ -109,11 +107,20 @@ module Prawn ...@@ -109,11 +107,20 @@ module Prawn
# where it's required. # where it's required.
# #
def split_alpha_channel! def split_alpha_channel!
split_image_data if alpha_channel? if alpha_channel?
if color_type == 3
generate_alpha_channel
else
split_image_data
end
end
end end
def alpha_channel? def alpha_channel?
@color_type == 4 || @color_type == 6 return true if color_type == 4 || color_type == 6
return @transparency.any? if color_type == 3
false
end end
# Build a PDF object representing this image in +document+, and return # Build a PDF object representing this image in +document+, and return
...@@ -285,6 +292,37 @@ module Prawn ...@@ -285,6 +292,37 @@ module Prawn
@img_data = color_data @img_data = color_data
end end
def generate_alpha_channel
alpha_palette = Hash.new(0xff)
0.upto(palette.bytesize / 3) do |n|
alpha_palette[n] = @transparency[:palette][n] || 0xff
end
scanline_length = width + 1
scanlines = @img_data.bytesize / scanline_length
pixels = width * height
data = StringIO.new(@img_data)
data.binmode
@alpha_channel = [0x00].pack('C') * (pixels + scanlines)
alpha = StringIO.new(@alpha_channel)
alpha.binmode
scanlines.times do |line|
data.seek(line * scanline_length)
filter = data.getbyte
alpha.putc filter
width.times do
color = data.read(1).unpack('C').first
alpha.putc alpha_palette[color]
end
end
end
end end
end end
end end
...@@ -16,6 +16,7 @@ module Prawn ...@@ -16,6 +16,7 @@ module Prawn
attr_reader :max_line_height attr_reader :max_line_height
attr_reader :max_descender attr_reader :max_descender
attr_reader :max_ascender attr_reader :max_ascender
attr_reader :finalized
attr_accessor :consumed attr_accessor :consumed
# The following present only for testing purposes # The following present only for testing purposes
...@@ -31,34 +32,38 @@ module Prawn ...@@ -31,34 +32,38 @@ module Prawn
end end
def space_count def space_count
if @unfinalized_line unless finalized
fail "Lines must be finalized before calling #space_count" fail "Lines must be finalized before calling #space_count"
end end
@fragments.inject(0) do |sum, fragment| @fragments.inject(0) do |sum, fragment|
sum + fragment.space_count sum + fragment.space_count
end end
end end
def line_width def line_width
if @unfinalized_line unless finalized
fail "Lines must be finalized before calling #line_width" fail "Lines must be finalized before calling #line_width"
end end
@fragments.inject(0) do |sum, fragment| @fragments.inject(0) do |sum, fragment|
sum + fragment.width sum + fragment.width
end end
end end
def line def line
if @unfinalized_line unless finalized
fail "Lines must be finalized before calling #line" fail "Lines must be finalized before calling #line"
end end
@fragments.collect do |fragment| @fragments.collect do |fragment|
fragment.text.dup.force_encoding(::Encoding::UTF_8) fragment.text.dup.force_encoding(::Encoding::UTF_8)
end.join end.join
end end
def finalize_line def finalize_line
@unfinalized_line = false @finalized = true
omit_trailing_whitespace_from_line_width omit_trailing_whitespace_from_line_width
@fragments = [] @fragments = []
@consumed.each do |hash| @consumed.each do |hash|
...@@ -85,7 +90,7 @@ module Prawn ...@@ -85,7 +90,7 @@ module Prawn
end end
def initialize_line def initialize_line
@unfinalized_line = true @finalized = false
@max_line_height = 0 @max_line_height = 0
@max_descender = 0 @max_descender = 0
@max_ascender = 0 @max_ascender = 0
...@@ -99,24 +104,26 @@ module Prawn ...@@ -99,24 +104,26 @@ module Prawn
end end
def next_string def next_string
unless @unfinalized_line if finalized
fail "Lines must not be finalized when calling #next_string" fail "Lines must not be finalized when calling #next_string"
end end
hash = @unconsumed.shift
if hash.nil? next_unconsumed_hash = @unconsumed.shift
nil
else if next_unconsumed_hash
@consumed << hash.dup @consumed << next_unconsumed_hash.dup
@current_format_state = hash.dup @current_format_state = next_unconsumed_hash.dup
@current_format_state.delete(:text) @current_format_state.delete(:text)
hash[:text]
next_unconsumed_hash[:text]
end