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

Imported Upstream version 2.1.0+dfsg

parent 2d197802
.*.sw?
nbproject
pkg
pkg
.rvmrc
.bundle
Gemfile.lock
drop_to_console.rb
manual.pdf
/.idea
/doc
/bin
.DS_Store
*.pdf
language: ruby
before_install:
- gem install bundler
- bundle --version
rvm:
- 2.0
- 2.1
- 2.2
- 2.0.0-p0
- 2.0.0-p648
- 2.1.0-p0
- 2.1.8
- 2.2.0-p0
- 2.2.4
- 2.3.0
- rbx-2
- jruby
env:
JRUBY_OPTS=--2.0
- jruby-9.0.4.0
matrix:
include:
- rvm: jruby-1.7.22
env: JRUBY_OPTS=--2.0
allow_failures:
- 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
### 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
## History
Prawn was originally developed by Gregory Brown, under the auspices of the Ruby
Mendicant Project, a grassroots initiative in which the Ruby community
collectively provided funding so that Gregory could take several months off from
Prawn was originally developed by [Gregory Brown](http://twitter.com/practicingruby),
under the auspices of the Ruby Mendicant Project, a grassroots initiative in which
the Ruby community collectively provided funding so that Gregory could take
several months off from
work to focus on this project.
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
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.
#
# Example:
......@@ -76,6 +76,7 @@ require_relative "prawn/images/png"
require_relative "prawn/stamp"
require_relative "prawn/soft_mask"
require_relative "prawn/security"
require_relative "prawn/transformation_stack"
require_relative "prawn/document"
require_relative "prawn/font"
require_relative "prawn/measurements"
......
......@@ -57,6 +57,7 @@ module Prawn
include Prawn::Images
include Prawn::Stamp
include Prawn::SoftMask
include Prawn::TransformationStack
# @group Extension API
......
......@@ -24,10 +24,18 @@ module Prawn
# Perhaps they will become part of the extension API?
# Anyway, for now it's not clear what we should do w. them.
delegate [ :graphic_state,
:save_graphics_state,
:restore_graphics_state,
: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
# be passing instances of renderer to PDF::Core::Page, but it's
# passing Prawn::Document objects instead.
......
......@@ -396,7 +396,20 @@ module Prawn
# generate a font identifier that hasn't been used on the current page yet
#
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
def size
......
......@@ -6,6 +6,7 @@
#
# 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/dash"
require_relative "graphics/cap_style"
......@@ -22,6 +23,7 @@ module Prawn
# ruby-pdf.rubyforge.org
#
module Graphics
include BlendMode
include Color
include Dash
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
# Sets the fill gradient from color1 to color2.
# old arguments: point, width, height, color1, color2, options = {}
# new arguments: from, to, color1, color1
# or from, r1, to, r2, color1, color2
# new arguments: from, to, color1, color1, options = {}
# 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)
set_gradient(:fill, *args)
end
# Sets the stroke gradient from color1 to color2.
# old arguments: point, width, height, color1, color2, options = {}
# new arguments: from, to, color1, color2
# or from, r1, to, r2, color1, color2
# new arguments: from, to, color1, color2, options = {}
# 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)
set_gradient(:stroke, *args)
end
......@@ -31,15 +45,17 @@ module Prawn
private
def set_gradient(type, *grad)
opts = grad.last.is_a?(Hash) ? grad.pop : {}
patterns = page.resources[:Pattern] ||= {}
registry_key = gradient_registry_key grad
registry_key = gradient_registry_key grad, opts
if patterns["SP#{registry_key}"]
shading = patterns["SP#{registry_key}"]
else
unless shading = gradient_registry[registry_key]
shading = gradient(*grad)
shading = gradient(grad, opts)
gradient_registry[registry_key] = shading
end
......@@ -59,18 +75,20 @@ module Prawn
renderer.add_content "/SP#{registry_key} #{operator}"
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
[
map_to_absolute(gradient[0]),
map_to_absolute(gradient[1]),
transformation,
x2, y2,
gradient[2], gradient[3]
]
else # radial
[
map_to_absolute(gradient[0]),
transformation,
x2, y2,
gradient[1],
map_to_absolute(gradient[2]),
gradient[3],
gradient[4], gradient[5]
]
......@@ -81,11 +99,15 @@ module Prawn
@gradient_registry ||= {}
end
def gradient(*args)
def gradient(args, opts)
if args.length != 4 && args.length != 6
fail ArgumentError, "Unknown type of gradient: #{args.inspect}"
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
color2 = normalize_color(args[-1]).dup.freeze
......@@ -104,10 +126,12 @@ module Prawn
:N => 1.0
)
x1, y1, x2, y2, transformation = gradient_coordinates(args, opts)
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
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
shading = ref!(
......@@ -121,10 +145,22 @@ module Prawn
ref!(
:PatternType => 2, # shading pattern
:Shading => shading,
:Matrix => [1, 0,
0, 1] + map_to_absolute(args[0])
:Matrix => transformation
)
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
......@@ -145,6 +145,9 @@ module Prawn
def transformation_matrix(a, b, c, d, e, f)
values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ")
save_graphics_state if block_given?
add_to_transformation_stack(a, b, c, d, e, f)
renderer.add_content "#{values} cm"
if block_given?
yield
......
......@@ -68,9 +68,7 @@ module Prawn
@transparency = {}
case @color_type
when 3
fail Errors::UnsupportedImageType,
"Pallete-based transparency in PNG is not currently supported.\n" \
"See https://github.com/prawnpdf/prawn/issues/783"
@transparency[:palette] = data.read(chunk_size).unpack('C*')
when 0
# Greyscale. Corresponding to entries in the PLTE chunk.
# Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1
......@@ -109,11 +107,20 @@ module Prawn
# where it's required.
#
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
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
# Build a PDF object representing this image in +document+, and return
......@@ -285,6 +292,37 @@ module Prawn
@img_data = color_data
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
......@@ -16,6 +16,7 @@ module Prawn
attr_reader :max_line_height
attr_reader :max_descender
attr_reader :max_ascender
attr_reader :finalized
attr_accessor :consumed
# The following present only for testing purposes
......@@ -31,34 +32,38 @@ module Prawn
end
def space_count
if @unfinalized_line
unless finalized
fail "Lines must be finalized before calling #space_count"
end
@fragments.inject(0) do |sum, fragment|
sum + fragment.space_count
end
end
def line_width
if @unfinalized_line
unless finalized
fail "Lines must be finalized before calling #line_width"
end
@fragments.inject(0) do |sum, fragment|
sum + fragment.width
end
end
def line
if @unfinalized_line
unless finalized
fail "Lines must be finalized before calling #line"
end
@fragments.collect do |fragment|
fragment.text.dup.force_encoding(::Encoding::UTF_8)
end.join
end
def finalize_line
@unfinalized_line = false
@finalized = true
omit_trailing_whitespace_from_line_width
@fragments = []
@consumed.each do |hash|
......@@ -85,7 +90,7 @@ module Prawn
end
def initialize_line
@unfinalized_line = true
@finalized = false
@max_line_height = 0
@max_descender = 0
@max_ascender = 0
......@@ -99,24 +104,26 @@ module Prawn
end
def next_string
unless @unfinalized_line
if finalized
fail "Lines must not be finalized when calling #next_string"
end
hash = @unconsumed.shift
if hash.nil?
nil
else
@consumed << hash.dup
@current_format_state = hash.dup
next_unconsumed_hash = @unconsumed.shift
if next_unconsumed_hash
@consumed << next_unconsumed_hash.dup
@current_format_state = next_unconsumed_hash.dup
@current_format_state.delete(:text)
hash[:text]
next_unconsumed_hash[:text]
end
end
def preview_next_string
hash = @unconsumed.first
if hash.nil? then nil
else hash[:text]
next_unconsumed_hash = @unconsumed.first
if next_unconsumed_hash
next_unconsumed_hash[:text]
end
end
......@@ -181,9 +188,10 @@ module Prawn
end
def retrieve_fragment
if @unfinalized_line
unless finalized
fail "Lines must be finalized before fragments can be retrieved"
end
@fragments.shift
end
......
......@@ -21,6 +21,8 @@ module Prawn
# The number of spaces in the last wrapped line
attr_reader :space_count
attr_reader :soft_hyphen
attr_reader :zero_width_space
# Whether this line is the last line in the paragraph
def paragraph_finished?
......@@ -150,9 +152,6 @@ module Prawn
"-"
end
attr_reader :soft_hyphen
attr_reader :zero_width_space
def line_empty?
@line_empty && @accumulated_width == 0
end
......
# encoding: utf-8
#
# transformation_stack.rb : Stores the transformations that have been applied to the document
#
# Copyright 2015, Roger Nesbitt. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
#
require 'matrix'
module Prawn
module TransformationStack
def add_to_transformation_stack(a, b, c, d, e, f)
@transformation_stack ||= [[]]
@transformation_stack.last.push([a, b, c, d, e, f].map(&:to_f))
end
def save_transformation_stack
@transformation_stack ||= [[]]
@transformation_stack.push(@transformation_stack.last.dup)
end
def restore_transformation_stack
@transformation_stack.pop if @transformation_stack
end
def current_transformation_matrix_with_translation(x = 0, y = 0)
transformations = (@transformation_stack || [[]]).last
matrix = Matrix.identity(3)
transformations.each do |a, b, c, d, e, f|
matrix *= Matrix[[a, c, e], [b, d, f], [0, 0, 1]]
end
matrix *= Matrix[[1, 0, x], [0, 1, y], [0, 0, 1]]
matrix.to_a[0..1].transpose.flatten
end
end
end
# encoding: utf-8
module Prawn
VERSION = "2.0.2"
VERSION = "2.1.0"
end
# encoding: utf-8
#
# Blend modes can be used to change the way two layers (images, graphics,
# text, etc.) are blended together. The <code>blend_mode</code> 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.
#
require File.expand_path(File.join(File.dirname(__FILE__),
%w[.. example_helper]))
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
Prawn::ManualBuilder::Example.generate(filename) do
start_new_page
# 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 = [: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 = index % 4 * 135
y = cursor - (index / 4 * 200)
image bottom_layer, :at => [x, y], :fit => [125, 125]
blend_mode(blend_mode) do
image top_layer, :at => [x, y], :fit => [125, 125]
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], 75, 25
end
y -= 30
fill_color '000000'
text_box blend_mode.to_s, :at => [x, y]
end
end
<
......@@ -29,6 +29,7 @@ Prawn::ManualBuilder::Example.generate("graphics.pdf", :page_size => "FOLIO") do
s.example "gradients"
s.example "transparency"
s.example "soft_masks"
s.example "blend_mode"
s.example "fill_rules"