You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

231 lines
6.6 KiB

module Squib
module Graphics
# Helper class to generate templated sheet.
class SaveSprue
def initialize(deck, tmpl, sheet_args)
@deck = deck
@tmpl = tmpl
@page_number = 0
@sheet_args = sheet_args # might be Args::Sheet or Args::SaveBatch
@overlay_lines = @tmpl.crop_lines.select do |line|
line['overlay_on_cards']
end
end
def render_sheet(range)
cc = init_cc
cc.set_source_color(:white) # white backdrop TODO make option
cc.paint
slots = @tmpl.cards
per_sheet = slots.size
check_oversized_card
draw_overlay_below_cards cc if range.size
track_progress(range) do |bar|
range.each do |i|
cc = next_page_if_needed(cc, i, per_sheet)
card = @deck.cards[i]
slot = slots[i % per_sheet]
draw_card cc, card,
slot['x'] - @sheet_args.trim,
slot['y'] - @sheet_args.trim,
slot['rotate'],
slot['flip_vertical'], slot['flip_horizontal'],
@sheet_args.trim, @sheet_args.trim_radius
bar.increment
end
draw_overlay_above_cards cc
draw_final_page cc # See bug #320
end
end
protected
# Initialize the Cairo Context
def init_cc
raise NotImplementedError
end
def draw_page(cc)
raise NotImplementedError
end
def full_filename
raise NotImplementedError
end
private
def next_page_if_needed(cc, i, per_sheet)
return cc unless (i != 0) && (i % per_sheet) == 0
draw_overlay_above_cards cc
cc = draw_page cc
draw_overlay_below_cards cc
@page_number += 1
cc
end
def track_progress(range)
msg = "Saving templated sheet to #{full_filename}"
@deck.progress_bar.start(msg, range.size) { |bar| yield(bar) }
end
def draw_overlay_below_cards(cc)
if @tmpl.crop_line_overlay == :on_margin
add_margin_overlay_clip_mask cc
cc.clip
draw_crop_line cc, @tmpl.crop_lines
cc.reset_clip
elsif @tmpl.crop_line_overlay == :beneath_cards
draw_crop_line cc, @tmpl.crop_lines
end
end
def draw_overlay_above_cards(cc)
if @tmpl.crop_line_overlay == :overlay_on_cards
draw_crop_line cc, @tmpl.crop_lines
else
draw_crop_line cc, @overlay_lines
end
end
def add_margin_overlay_clip_mask(cc)
margin = @tmpl.margin
cc.new_path
cc.rectangle(
margin[:left], margin[:top],
margin[:right] - margin[:left],
margin[:bottom] - margin[:top]
)
cc.new_sub_path
cc.move_to @tmpl.sheet_width, 0
cc.line_to 0, 0
cc.line_to 0, @tmpl.sheet_height
cc.line_to @tmpl.sheet_width, @tmpl.sheet_height
cc.close_path
end
def draw_crop_line(cc, crop_lines)
crop_lines.each do |line|
cc.move_to line['line'].x1, line['line'].y1
cc.line_to line['line'].x2, line['line'].y2
cc.set_source_color line['color']
cc.set_line_width line['width']
cc.set_dash(line['style'].pattern) if line['style'].pattern
cc.stroke
end
end
def check_oversized_card
Squib.logger.warn {
"Card size is larger than sprue's expected card size "\
"of #{@tmpl.card_width}x#{@tmpl.card_height}. Cards may overlap."
} if (@deck.width - 2.0 * @sheet_args.trim) > @tmpl.card_width ||
(@deck.height - 2.0 * @sheet_args.trim) > @tmpl.card_height
end
def draw_card(cc, card, x, y, angle, flip_v, flip_h, trim, trim_radius)
# Compute the true size of the card after trimming
w = @deck.width - 2.0 * trim
h = @deck.height - 2.0 * trim
# Normalize the angles first
# TODO do this in the args class
angle = angle % (2 * Math::PI)
angle = 2 * Math::PI - angle if angle < 0
# Perform the actual rotation and drawing
mat = cc.matrix # Save the transformation matrix to revert later
cc.translate x, y
cc.translate @deck.width / 2.0, @deck.height / 2.0
cc.flip(flip_v, flip_h, 0, 0)
cc.rotate angle
cc.translate -@deck.width / 2.0, -@deck.height / 2.0
cc.rounded_rectangle(trim, trim, w, h, trim_radius, trim_radius) # clip
cc.clip
cc.set_source card.cairo_surface, 0, 0
cc.matrix = mat
cc.paint
cc.reset_clip
end
end
# Templated sheet renderer in PDF format.
class SaveSpruePDF < SaveSprue
def init_cc
ratio = 72.0 / @deck.dpi
slots = @tmpl.cards
per_sheet = slots.size
surface = if per_sheet == 1
Cairo::PDFSurface.new(
full_filename,
(@tmpl.sheet_width - 2 * @sheet_args.trim) * ratio,
(@tmpl.sheet_height - 2 *@sheet_args.trim) * ratio
)
else
Cairo::PDFSurface.new(
full_filename,
@tmpl.sheet_width * ratio,
@tmpl.sheet_height * ratio
)
end
cc = CairoContextWrapper.new(Cairo::Context.new(surface))
# cc = Cairo::Context.new(surface)
cc.scale(72.0 / @deck.dpi, 72.0 / @deck.dpi) # make it like pixels
cc
end
def draw_page(cc)
cc.show_page
cc.set_source_color(:white) # white backdrop TODO make option
cc.paint
cc
end
def draw_final_page(cc)
# PDF doesn't need to create a last page. See bug #320
cc.target.finish
end
def full_filename
@sheet_args.full_filename
end
end
# Templated sheet renderer in PNG format.
class SaveSpruePNG < SaveSprue
def init_cc
surface = Cairo::ImageSurface.new @tmpl.sheet_width, @tmpl.sheet_height
CairoContextWrapper.new(Cairo::Context.new(surface))
# Cairo::Context.new(surface)
end
def draw_page(cc)
cc.target.write_to_png(full_filename)
init_cc
cc.set_source_color(:white) # white backdrop TODO make option
cc.paint
cc
end
# The last page always gets written out for PNGs because they are separate
# files and don't get "flushed" automatically. See bug #320.
def draw_final_page(cc)
draw_page cc
cc.target.finish
end
def full_filename
@sheet_args.full_filename @page_number
end
end
end
end