1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
# frozen_string_literal: true
module PDF
module Core
# Graphics state saving and restoring
class GraphicStateStack
# Graphic state stack
attr_accessor :stack
# @param previous_state [GraphicState, nil]
def initialize(previous_state = nil)
self.stack = [GraphicState.new(previous_state)]
end
# Pushes graphic state onto stack
#
# @param graphic_state [GraphicState, nil]
# @return [void]
def save_graphic_state(graphic_state = nil)
stack.push(GraphicState.new(graphic_state || current_state))
end
# Restores previous graphic state
#
# @return [void]
def restore_graphic_state
if stack.empty?
raise PDF::Core::Errors::EmptyGraphicStateStack,
"\n You have reached the end of the graphic state stack"
end
stack.pop
end
# Current graphic state
#
# @return [GraphicState]
def current_state
stack.last
end
# Tells whether there are any saved graphic states
#
# @return [Boolean]
# @see #empty?
def present?
!stack.empty?
end
# Tells whether there are no saved graphic states
#
# @return [Boolean]
# @see #present?
def empty?
stack.empty?
end
end
# Graphics state.
# It's a *partial* represenation of PDF graphics state. Only the parts
# implemented in Prawn are present here.
#
# NOTE: This class may be a good candidate for a copy-on-write hash.
class GraphicState
# Color space
# @return [Hash]
attr_accessor :color_space
# Dash
# @return [Hash<[:dash, :space, :phase], [nil, Numeric]>]
attr_accessor :dash
# Line cap
# @return [Symbol]
attr_accessor :cap_style
# Line Join
# @return [Symbol]
attr_accessor :join_style
# Line width
# @return [Numberic]
attr_accessor :line_width
# Fill color
# @return [String]
attr_accessor :fill_color
# Stroke color
attr_accessor :stroke_color
# @param previous_state [GraphicState, nil]
def initialize(previous_state = nil)
if previous_state
initialize_copy(previous_state)
else
@color_space = {}
@fill_color = '000000'
@stroke_color = '000000'
@dash = { dash: nil, space: nil, phase: 0 }
@cap_style = :butt
@join_style = :miter
@line_width = 1
end
end
# PDF representation of dash settings
#
# @return [String]
def dash_setting
return '[] 0 d' unless @dash[:dash]
array =
if @dash[:dash].is_a?(Array)
@dash[:dash]
else
[@dash[:dash], @dash[:space]]
end
"[#{PDF::Core.real_params(array)}] #{PDF::Core.real(@dash[:phase])} d"
end
private
def initialize_copy(other)
# mutable state
@color_space = other.color_space.dup
@fill_color = other.fill_color.dup
@stroke_color = other.stroke_color.dup
@dash = other.dash.dup
# immutable state that doesn't need to be duped
@cap_style = other.cap_style
@join_style = other.join_style
@line_width = other.line_width
end
end
end
end
|