module Prawn::Graphics::Patterns

Constants

Gradient
GradientStop

Public Instance Methods

fill_gradient(*args, **kwargs) click to toggle source

Sets the fill gradient. old arguments:

from, to, color1, color2
or
from, r1, to, r2, color1, color2

new arguments:

from: [x, y]
to: [x, y]
r1: radius
r2: radius
stops: [color, color, ...] or
       { position => color, position => color, ... }
apply_transformations: true

Examples:

# draws a horizontal axial gradient that starts at red on the left
# and ends at blue on the right
fill_gradient from: [0, 0], to: [100, 0], stops: ['red', 'blue']

# draws a horizontal radial gradient that starts at red, is green
# 80% of the way through, and finishes blue
fill_gradient from: [0, 0], r1: 0, to: [100, 0], r2: 180,
  stops: { 0 => 'red', 0.8 => 'green', 1 => 'blue' }

from and to specify the axis of where the gradient should be painted.

r1 and r2, if specified, make a radial gradient with the starting circle of radius r1 centered at from and ending at a circle of radius r2 centered at to. If r1 is not specified, a axial gradient will be drawn.

stops is an array or hash of stops. Each stop is either just a string indicating the color, in which case the stops will be evenly distributed across the gradient, or a hash where the key is a position between 0 and 1 indicating what distance through the gradient the color should change, and the value is a color string.

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, and is default true if you use the new arguments format. The default for the old arguments format, 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.

# File lib/prawn/graphics/patterns.rb, line 67
def fill_gradient(*args, **kwargs)
  set_gradient(:fill, *args, **kwargs)
end
stroke_gradient(*args, **kwargs) click to toggle source

Sets the stroke gradient. See fill_gradient for a description of the arguments to this method.

# File lib/prawn/graphics/patterns.rb, line 73
def stroke_gradient(*args, **kwargs)
  set_gradient(:stroke, *args, **kwargs)
end

Private Instance Methods

create_gradient_pattern(gradient) click to toggle source
# File lib/prawn/graphics/patterns.rb, line 181
def create_gradient_pattern(gradient)
  if gradient.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

  shader_stops = gradient.stops.each_cons(2).map do |first, second|
    ref!(
      FunctionType: 2,
      Domain: [0.0, 1.0],
      C0: first.color,
      C1: second.color,
      N: 1.0
    )
  end

  # If there's only two stops, we can use the single shader.
  # Otherwise we stitch the multiple shaders together.
  shader = if shader_stops.length == 1
             shader_stops.first
           else
             ref!(
               FunctionType: 3, # stitching function
               Domain: [0.0, 1.0],
               Functions: shader_stops,
               Bounds: gradient.stops[1..-2].map(&:position),
               Encode: [0.0, 1.0] * shader_stops.length
             )
           end

  x1, y1, x2, y2, transformation = gradient_coordinates(gradient)

  coords = if gradient.type == :axial
             [0, 0, x2 - x1, y2 - y1]
           else
             [0, 0, gradient.r1, x2 - x1, y2 - y1, gradient.r2]
           end

  shading = ref!(
    ShadingType: gradient.type == :axial ? 2 : 3,
    ColorSpace: color_space(gradient.stops.first.color),
    Coords: coords,
    Function: shader,
    Extend: [true, true]
  )

  ref!(
    PatternType: 2, # shading pattern
    Shading: shading,
    Matrix: transformation
  )
end
gradient_coordinates(gradient) click to toggle source
# File lib/prawn/graphics/patterns.rb, line 240
def gradient_coordinates(gradient)
  x1, y1 = map_to_absolute(gradient.from)
  x2, y2 = map_to_absolute(gradient.to)

  transformation =
    if gradient.apply_transformations
      current_transformation_matrix_with_translation(x1, y1)
    else
      [1, 0, 0, 1, x1, y1]
    end

  [x1, y1, x2, y2, transformation]
end
gradient_registry() click to toggle source
# File lib/prawn/graphics/patterns.rb, line 177
def gradient_registry
  @gradient_registry ||= {}
end
gradient_registry_key(gradient) click to toggle source
# File lib/prawn/graphics/patterns.rb, line 163
def gradient_registry_key(gradient)
  _x1, _y1, x2, y2, transformation = gradient_coordinates(gradient)

  key = [
    gradient.type.to_s,
    transformation,
    x2, y2,
    gradient.r1 || -1, gradient.r2 || -1,
    gradient.stops.length,
    gradient.stops.map { |s| [s.position, s.color] }
  ].flatten
  Digest::SHA1.hexdigest(key.pack('HC*'))
end
parse_gradient_arguments( *arguments, from: nil, to: nil, r1: nil, r2: nil, stops: nil, apply_transformations: nil ) click to toggle source
# File lib/prawn/graphics/patterns.rb, line 109
def parse_gradient_arguments( # rubocop: disable Metrics/ParameterLists
  *arguments, from: nil, to: nil, r1: nil, r2: nil, stops: nil,
  apply_transformations: nil
)
  case arguments.length
  when 0
    apply_transformations = true if apply_transformations.nil?
  when 4
    from, to, *stops = arguments
  when 6
    from, r1, to, r2, *stops = arguments
  else
    raise ArgumentError, "Unknown type of gradient: #{arguments.inspect}"
  end

  if stops.length < 2
    raise ArgumentError, 'At least two stops must be specified'
  end

  stops = stops.map.with_index do |stop, index|
    case stop
    when Array, Hash
      position, color = stop
    else
      position = index / (stops.length.to_f - 1)
      color = stop
    end

    unless (0..1).cover?(position)
      raise ArgumentError, 'position must be between 0 and 1'
    end
    GradientStop.new(position, normalize_color(color))
  end

  if stops.first.position != 0
    raise ArgumentError, 'The first stop must have a position of 0'
  end
  if stops.last.position != 1
    raise ArgumentError, 'The last stop must have a position of 1'
  end

  if stops.map { |stop| color_type(stop.color) }.uniq.length > 1
    raise ArgumentError, 'All colors must be of the same color space'
  end

  Gradient.new(
    r1 ? :radial : :axial,
    apply_transformations,
    stops,
    from, to,
    r1, r2
  )
end
set_gradient(type, *grad, **kwargs) click to toggle source
# File lib/prawn/graphics/patterns.rb, line 79
def set_gradient(type, *grad, **kwargs)
  gradient = parse_gradient_arguments(*grad, **kwargs)

  patterns = page.resources[:Pattern] ||= {}

  registry_key = gradient_registry_key gradient

  unless patterns.key? "SP#{registry_key}"
    shading = gradient_registry[registry_key]
    unless shading
      shading = create_gradient_pattern(gradient)
      gradient_registry[registry_key] = shading
    end

    patterns["SP#{registry_key}"] = shading
  end

  operator = case type
             when :fill
               'scn'
             when :stroke
               'SCN'
             else
               raise ArgumentError, "unknown type '#{type}'"
             end

  set_color_space type, :Pattern
  renderer.add_content "/SP#{registry_key} #{operator}"
end