lib/squib/graphics/save_sprue.rb in squib-0.18.0 vs lib/squib/graphics/save_sprue.rb in squib-0.19.0a

- old
+ new

@@ -1,231 +1,231 @@ -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 +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