Browse Source

Convert save_sheet and save_pdf to args classes

dev
Andy Meneely 10 years ago
parent
commit
3f2b057466
  1. 47
      lib/squib/api/save.rb
  2. 8
      lib/squib/args/save_batch.rb
  3. 24
      lib/squib/args/sheet.rb
  4. 102
      lib/squib/graphics/save_doc.rb
  5. 42
      spec/args/sheet_spec.rb
  6. 6
      spec/graphics/graphics_save_doc_spec.rb

47
lib/squib/api/save.rb

@ -22,6 +22,26 @@ module Squib
self
end
# Lays out the cards in range and renders a PDF
#
# @example
# save_pdf file: 'deck.pdf', margin: 75, gap: 5, trim: 37
#
# @option opts file [String] the name of the PDF file to save. See {file:README.md#Specifying_Files Specifying Files}
# @option opts dir [String] (_output) the directory to save to. Created if it doesn't exist.
# @option opts width [Integer] (3300) the height of the page in pixels. Default is 11in * 300dpi. Supports unit conversion.
# @option opts height [Integer] (2550) the height of the page in pixels. Default is 8.5in * 300dpi. Supports unit conversion.
# @option opts margin [Integer] (75) the margin around the outside of the page. Supports unit conversion.
# @option opts gap [Integer] (0) the space in pixels between the cards. Supports unit conversion.
# @option opts trim [Integer] (0) the space around the edge of each card to trim (e.g. to cut off the bleed margin for print-and-play). Supports unit conversion.
# @return [nil]
# @api public
def save_pdf(opts = {})
range = Args::CardRange.new(opts[:range], deck_size: size)
sheet = Args::Sheet.new(custom_colors, {file: 'output.pdf'}).load!(opts, expand_by: size, layout: layout, dpi: dpi)
render_pdf(range, sheet)
end
# Saves the given range of cards to a PNG
#
# @example
@ -47,6 +67,33 @@ module Squib
end
end
# Lays out the cards in range and renders a stitched PNG sheet
#
# @example
# save_sheet prefix: 'sheet_', margin: 75, gap: 5, trim: 37
#
# @option opts [Enumerable] range (:all) the range of cards over which this will be rendered. See {file:README.md#Specifying_Ranges Specifying Ranges}
# @option opts columns [Integer] (5) the number of columns in the grid. Must be an integer
# @option opts rows [Integer] (:infinite) the number of rows in the grid. When set to :infinite, the sheet scales to the rows needed. If there are more cards than rows*columns, new sheets are started.
# @option opts [String] prefix (card_) the prefix of the file name(s)
# @option opts [String] count_format (%02d) the format string used for formatting the card count (e.g. padding zeros). Uses a Ruby format string (see the Ruby doc for Kernel::sprintf for specifics)
# @option opts dir [String] (_output) the directory to save to. Created if it doesn't exist.
# @option opts margin [Integer] (0) the margin around the outside of the sheet.
# @option opts gap [Integer] (0) the space in pixels between the cards
# @option opts trim [Integer] (0) the space around the edge of each card to trim (e.g. to cut off the bleed margin for print-and-play)
# @return [nil]
# @api public
def save_sheet(opts = {})
range = Args::CardRange.new(opts[:range], deck_size: size)
batch = Args::SaveBatch.new.load!(opts, expand_by: size, layout: layout, dpi: dpi)
sheet = Args::Sheet.new(custom_colors, {margin: 0}, size).load!(opts, expand_by: size, layout: layout, dpi: dpi)
opts = {margin: 0}.merge(opts) # overriding the non-system default
p = needs(opts, [:range,
:prefix, :count_format, :creatable_dir,
:margin, :gap, :trim, :rows, :columns])
render_sheet(range, batch, sheet, p)
end
# Renders a range of cards in a showcase as if they are sitting in 3D on a reflective surface
# See {file:samples/showcase.rb} for full example
#

8
lib/squib/args/save_batch.rb

@ -47,6 +47,14 @@ module Squib
end
end
def full_filename(i)
"#{dir[i]}/#{prefix[i]}#{count_format[i] % i}.png"
end
def summary
"#{dir[0]}/#{prefix[0]}_*"
end
end
end
end

24
lib/squib/args/sheet.rb

@ -12,19 +12,25 @@ module Squib
include ColorValidator
include DirValidator
def initialize(custom_colors = {}, dsl_method_defaults = {})
def initialize(custom_colors = {}, dsl_method_defaults = {}, deck_size = 1)
@custom_colors = custom_colors
@dsl_method_defaults = dsl_method_defaults
@deck_size = deck_size
end
def self.parameters
{ dir: '_output',
{
dir: '_output',
file: 'sheet.png',
fill_color: :white,
gap: 0,
height: 2550,
margin: 75,
rows: :infinite,
columns: 5,
trim_radius: 38,
trim: 0,
width: 3300,
}
end
@ -33,7 +39,7 @@ module Squib
end
def self.params_with_units
[ :gap, :margin, :trim_radius, :trim ]
[ :gap, :height, :margin, :trim_radius, :trim, :width ]
end
def validate_fill_color(arg)
@ -44,6 +50,18 @@ module Squib
ensure_dir_created(arg)
end
def validate_columns(arg)
raise 'columns must be an integer' unless arg.respond_to? :to_i
arg.to_i
end
def validate_rows(arg)
raise 'columns must be an integer' unless columns.respond_to? :to_i
return 1 if @deck_size < columns
return arg if arg.respond_to? :to_i
(@deck_size.to_i / columns.to_i).ceil
end
end
end

102
lib/squib/graphics/save_doc.rb

@ -1,37 +1,20 @@
module Squib
class Deck
# Lays out the cards in range and renders a PDF
#
# @example
# save_pdf file: 'deck.pdf', margin: 75, gap: 5, trim: 37
#
# @option opts file [String] the name of the PDF file to save. See {file:README.md#Specifying_Files Specifying Files}
# @option opts dir [String] (_output) the directory to save to. Created if it doesn't exist.
# @option opts width [Integer] (3300) the height of the page in pixels. Default is 11in * 300dpi. Supports unit conversion.
# @option opts height [Integer] (2550) the height of the page in pixels. Default is 8.5in * 300dpi. Supports unit conversion.
# @option opts margin [Integer] (75) the margin around the outside of the page. Supports unit conversion.
# @option opts gap [Integer] (0) the space in pixels between the cards. Supports unit conversion.
# @option opts trim [Integer] (0) the space around the edge of each card to trim (e.g. to cut off the bleed margin for print-and-play). Supports unit conversion.
# @return [nil]
# @api public
def save_pdf(opts = {})
opts = {width: 3300, height: 2550}.merge(opts)
p = needs(opts, [:range, :paper_width, :paper_height, :file_to_save,
:creatable_dir, :margin, :gap, :trim])
paper_width = p[:width]
paper_height = p[:height]
file = "#{p[:dir]}/#{p[:file]}"
cc = Cairo::Context.new(Cairo::PDFSurface.new(file, paper_width * 72.0 / @dpi, paper_height * 72.0 / @dpi))
# :nodoc:
# @api private
def render_pdf(range, sheet)
file = "#{sheet.dir}/#{sheet.file}"
cc = Cairo::Context.new(Cairo::PDFSurface.new(file, sheet.width * 72.0 / @dpi, sheet.height * 72.0 / @dpi))
cc.scale(72.0 / @dpi, 72.0 / @dpi) # for bug #62
x, y = p[:margin], p[:margin]
card_width = @width - 2 * p[:trim]
card_height = @height - 2 * p[:trim]
@progress_bar.start("Saving PDF to #{file}", p[:range].size) do |bar|
p[:range].each do |i|
x, y = sheet.margin, sheet.margin
card_width = @width - 2 * sheet.trim
card_height = @height - 2 * sheet.trim
@progress_bar.start("Saving PDF to #{file}", range.size) do |bar|
range.each do |i|
card = @cards[i]
cc.translate(x,y)
cc.rectangle(p[:trim], p[:trim], card_width, card_height)
cc.rectangle(sheet.trim, sheet.trim, card_width, card_height)
cc.clip
case card.backend.downcase.to_sym
when :memory
@ -49,68 +32,51 @@ module Squib
bar.increment
cc.reset_clip
cc.translate(-x,-y)
x += card.width + p[:gap] - 2*p[:trim]
if x > (paper_width - card_width - p[:margin])
x = p[:margin]
y += card.height + p[:gap] - 2*p[:trim]
if y > (paper_height - card_height - p[:margin])
x += card.width + sheet.gap - 2*sheet.trim
if x > (sheet.width - card_width - sheet.margin)
x = sheet.margin
y += card.height + sheet.gap - 2*sheet.trim
if y > (sheet.height - card_height - sheet.margin)
cc.show_page # next page
x,y = p[:margin],p[:margin]
x,y = sheet.margin,sheet.margin
end
end
end
end
end
# Lays out the cards in range and renders a stitched PNG sheet
#
# @example
# save_sheet prefix: 'sheet_', margin: 75, gap: 5, trim: 37
#
# @option opts [Enumerable] range (:all) the range of cards over which this will be rendered. See {file:README.md#Specifying_Ranges Specifying Ranges}
# @option opts colulmns [Integer] (5) the number of columns in the grid
# @option opts rows [Integer] (:infinite) the number of rows in the grid. When set to :infinite, the sheet scales to the rows needed. If there are more cards than rows*columns, new sheets are started.
# @option opts [String] prefix (card_) the prefix of the file name(s)
# @option opts [String] count_format (%02d) the format string used for formatting the card count (e.g. padding zeros). Uses a Ruby format string (see the Ruby doc for Kernel::sprintf for specifics)
# @option opts dir [String] (_output) the directory to save to. Created if it doesn't exist.
# @option opts margin [Integer] (0) the margin around the outside of the page.
# @option opts gap [Integer] (0) the space in pixels between the cards
# @option opts trim [Integer] (0) the space around the edge of each card to trim (e.g. to cut off the bleed margin for print-and-play)
# @return [nil]
# @api public
def save_sheet(opts = {})
opts = {margin: 0}.merge(opts) # overriding the non-system default
p = needs(opts, [:range, :prefix, :count_format, :creatable_dir, :margin, :gap, :trim, :rows, :columns])
# EXTRACT METHOD HERE
sheet_width = (p[:columns] * (@width + 2 * p[:gap] - 2 * p[:trim])) + (2 * p[:margin])
sheet_height = (p[:rows] * (@height + 2 * p[:gap] - 2 * p[:trim])) + (2 * p[:margin])
# :nodoc:
# @api private
def render_sheet(range, batch, sheet, p = {})
sheet_width = (sheet.columns * (@width + 2 * sheet.gap - 2 * sheet.trim)) + (2 * sheet.margin)
sheet_height = (sheet.rows * (@height + 2 * sheet.gap - 2 * sheet.trim)) + (2 * sheet.margin)
cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
num_this_sheet = 0
sheet_num = 0
x, y = p[:margin], p[:margin]
@progress_bar.start("Saving PNG sheet to #{p[:dir]}/#{p[:prefix]}_*", @cards.size + 1) do |bar|
p[:range].each do |i|
if num_this_sheet >= (p[:columns] * p[:rows]) # new sheet
filename = "#{p[:dir]}/#{p[:prefix]}#{p[:count_format] % sheet_num}.png"
x, y = sheet.margin, sheet.margin
@progress_bar.start("Saving PNG sheet to #{batch.summary}", @cards.size + 1) do |bar|
range.each do |i|
if num_this_sheet >= (sheet.columns * sheet.rows) # new sheet
filename = batch.full_filename(sheet_num)
cc.target.write_to_png(filename)
new_sheet = false
num_this_sheet = 0
sheet_num += 1
x, y = p[:margin], p[:margin]
x, y = sheet.margin, sheet.margin
cc = Cairo::Context.new(Cairo::ImageSurface.new(sheet_width, sheet_height))
end
surface = trim(@cards[i].cairo_surface, p[:trim], @width, @height)
surface = trim(@cards[i].cairo_surface, sheet.trim, @width, @height)
cc.set_source(surface, x, y)
cc.paint
num_this_sheet += 1
x += surface.width + p[:gap]
if num_this_sheet % p[:columns] == 0 # new row
x = p[:margin]
y += surface.height + p[:gap]
x += surface.width + sheet.gap
if num_this_sheet % sheet.columns == 0 # new row
x = sheet.margin
y += surface.height + sheet.gap
end
bar.increment
end
cc.target.write_to_png("#{p[:dir]}/#{p[:prefix]}#{p[:count_format] % sheet_num}.png")
cc.target.write_to_png(batch.full_filename(sheet_num))
end
end

42
spec/args/sheet_spec.rb

@ -13,4 +13,46 @@ describe Squib::Args::Sheet do
end
context 'rows and colums' do
subject(:sheet) { Squib::Args::Sheet.new({}, {}, 4) }
it 'does nothing on a perfect fit' do
opts = { columns: 2, rows: 2 }
sheet.load! opts
expect(sheet).to have_attributes(columns: 2, rows: 2)
end
it 'keeps both if specified' do
opts = { columns: 1, rows: 1 }
sheet.load! opts
expect(sheet).to have_attributes(columns: 1, rows: 1)
end
it 'computes properly on non-integer' do
opts = {columns: 1, rows: :infinite}
sheet.load! opts
expect(sheet).to have_attributes( columns: 1, rows: 4 )
end
it 'computes properly on unspecified rows' do
opts = {columns: 1}
sheet.load! opts
expect(sheet).to have_attributes( columns: 1, rows: 4 )
end
it 'computes properly on unspecified, too-big column' do
opts = {}
sheet.load! opts
expect(sheet).to have_attributes( columns: 5, rows: 1 )
end
it 'fails on a non-integer column' do
opts = {columns: :infinite}
expect { sheet.load!(opts) }.to raise_error('columns must be an integer')
end
end
end

6
spec/graphics/graphics_save_doc_spec.rb

@ -26,8 +26,9 @@ describe Squib::Deck, '#save_pdf' do
it 'make all the expected calls on a smoke test' do
num_cards = 9
deck = Squib::Deck.new(cards: 9, width: 825, height: 1125)
expect(deck).to receive(:dirify) { |arg| arg } #don't create the dir
expect(Squib.logger).to receive(:debug).at_least(:once)
expect(Squib.logger).to receive(:warn).exactly(:once) #warn about making the dir
expect(Dir).to receive(:mkdir) {} # don't actually make the dir
expect(cxt).to receive(:scale).with(0.24, 0.24)
expect_card_place(75, 75)
@ -50,7 +51,8 @@ describe Squib::Deck, '#save_pdf' do
args = { range: 2..4, file: 'foo.pdf', dir: '_out', margin: 75, gap: 5, trim: 37 }
deck = Squib::Deck.new(cards: num_cards, width: 825, height: 1125)
expect(Squib.logger).to receive(:debug).at_least(:once)
expect(deck).to receive(:dirify) { |arg| arg } #don't create the dir
expect(Squib.logger).to receive(:warn).exactly(:once) #warn about making the dir
expect(Dir).to receive(:mkdir) {} # don't actually make the dir
expect(cxt).to receive(:scale).with(0.24, 0.24)
expect_card_place(75, 75)

Loading…
Cancel
Save