Skip to content

Commit

Permalink
Merge pull request #910 from iamjohnford/patch-1
Browse files Browse the repository at this point in the history
Add Blend Modes
  • Loading branch information
packetmonkey committed Jan 26, 2016
2 parents 68c9530 + 47a29c0 commit 7aa777f
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ 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
Expand Down
Binary file added data/images/blend_modes_bottom_layer.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/blend_modes_top_layer.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions lib/prawn/graphics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -22,6 +23,7 @@ module Prawn
# ruby-pdf.rubyforge.org
#
module Graphics
include BlendMode
include Color
include Dash
include CapStyle
Expand Down
64 changes: 64 additions & 0 deletions lib/prawn/graphics/blend_mode.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 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
49 changes: 49 additions & 0 deletions manual/graphics/blend_mode.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 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
1 change: 1 addition & 0 deletions manual/graphics/graphics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
s.example "gradients"
s.example "transparency"
s.example "soft_masks"
s.example "blend_mode"
s.example "fill_rules"
end

Expand Down
71 changes: 71 additions & 0 deletions spec/blend_mode_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# encoding: utf-8

require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")

module BlendModeHelper
def make_blend_mode(blend_mode)
@pdf.blend_mode(blend_mode) do
yield if block_given?
end
end
end

describe "Document with with blend_mode" do
include BlendModeHelper

it "the PDF version should be at least 1.4" do
create_pdf
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
create_pdf
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
create_pdf
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
create_pdf
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
create_pdf
make_blend_mode([:Multiply, :Screen, :Overlay])
extgstate = PDF::Inspector::ExtGState.analyze(@pdf.render).extgstates.first
expect(extgstate[:blend_mode]).to eq([:Multiply, :Screen, :Overlay])
end

describe "with more than one page" do
include BlendModeHelper

it "the extended graphic state resource should be added to both pages" do
create_pdf
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

0 comments on commit 7aa777f

Please sign in to comment.