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 139 140 141 142 143 144 145 146 147 148 149 150 151
|
# frozen_string_literal: true
# stamp.rb : Implements a repeatable stamp
#
# Copyright October 2009, Daniel Nelson. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
#
module Prawn
# The Prawn::Stamp module is used to create content that will be
# included multiple times in a document. Using a stamp has three
# advantages over creating content anew each time it is placed on
# the page:
# i. faster document creation
# ii. smaller final document
# iii. faster display on subsequent displays of the repeated
# element because the viewer application can cache the rendered
# results
#
# Example:
# pdf.create_stamp("my_stamp") {
# pdf.fill_circle([10, 15], 5)
# pdf.draw_text("hello world", :at => [20, 10])
# }
# pdf.stamp("my_stamp")
#
module Stamp
# @group Stable API
# Renders the stamp named <tt>name</tt> to the page
# raises <tt>Prawn::Errors::InvalidName</tt> if name.empty?
# raises <tt>Prawn::Errors::UndefinedObjectName</tt> if no stamp
# has been created with this name
#
# Example:
# pdf.create_stamp("my_stamp") {
# pdf.fill_circle([10, 15], 5)
# pdf.text("hello world", :at => [20, 10])
# }
# pdf.stamp("my_stamp")
#
def stamp(name)
dictionary_name, dictionary = stamp_dictionary(name)
renderer.add_content "/#{dictionary_name} Do"
update_annotation_references dictionary.data[:Annots]
state.page.xobjects.merge!(dictionary_name => dictionary)
end
# Renders the stamp named <tt>name</tt> at a position offset from
# the initial coords at which the elements of the stamp was
# created
#
# Example:
# pdf.create_stamp("circle") do
# pdf.fill_circle([0, 0], 25)
# end
# # draws a circle at 100, 100
# pdf.stamp_at("circle", [100, 100])
#
# See stamp() for exceptions that might be raised
#
def stamp_at(name, point)
translate(point[0], point[1]) { stamp(name) }
end
# Creates a re-usable stamp named <tt>name</tt>
#
# raises <tt>Prawn::Errors::NameTaken</tt> if a stamp already
# exists in this document with this name
# raises <tt>Prawn::Errors::InvalidName</tt> if name.empty?
#
# Example:
# pdf.create_stamp("my_stamp") {
# pdf.fill_circle([10, 15], 5)
# pdf.draw_text("hello world", :at => [20, 10])
# }
#
def create_stamp(name, &block)
dictionary = create_stamp_dictionary(name)
state.page.stamp_stream(dictionary, &block)
end
private
def stamp_dictionary_registry
@stamp_dictionary_registry ||= {}
end
def next_stamp_dictionary_id
stamp_dictionary_registry.length + 1
end
def stamp_dictionary(name)
raise Prawn::Errors::InvalidName if name.empty?
if stamp_dictionary_registry[name].nil?
raise Prawn::Errors::UndefinedObjectName
end
dict = stamp_dictionary_registry[name]
dictionary_name = dict[:stamp_dictionary_name]
dictionary = dict[:stamp_dictionary]
[dictionary_name, dictionary]
end
def create_stamp_dictionary(name)
raise Prawn::Errors::InvalidName if name.empty?
raise Prawn::Errors::NameTaken unless stamp_dictionary_registry[name].nil?
# BBox origin is the lower left margin of the page, so we need
# it to be the full dimension of the page, or else things that
# should appear near the top or right margin are invisible
dictionary = ref!(
Type: :XObject,
Subtype: :Form,
BBox: [
0, 0,
state.page.dimensions[2], state.page.dimensions[3]
]
)
dictionary_name = "Stamp#{next_stamp_dictionary_id}"
stamp_dictionary_registry[name] = {
stamp_dictionary_name: dictionary_name,
stamp_dictionary: dictionary
}
dictionary
end
# Referencing annotations from a stamp XObject doesn't result
# in a working link. Instead, the references must be appended
# to the /Annot dictionary of the object that contains the
# call to the stamp object.
def update_annotation_references(annots)
if annots&.any?
state.page.dictionary.data[:Annots] ||= []
state.page.dictionary.data[:Annots] |= annots
end
end
def freeze_stamp_graphics
update_colors
write_line_width
write_stroke_cap_style
write_stroke_join_style
write_stroke_dash
end
end
end
|