diff --git a/.gitignore b/.gitignore index ccab349..50b5d96 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,6 @@ samples/_output/*.pdf samples/_output/foo rubocop.txt benchmarks/_output/*.png -benchmarks/_output/*.pdf \ No newline at end of file +benchmarks/_output/*.pdf +benchmarks/_output/ +samples/_output/*.svg diff --git a/Rakefile b/Rakefile index ed263a3..6d93679 100644 --- a/Rakefile +++ b/Rakefile @@ -2,6 +2,7 @@ require 'bundler/gem_tasks' require 'rspec/core/rake_task' require 'yard' require 'benchmark' +require 'byebug' task default: [:install, :spec] @@ -26,6 +27,7 @@ YARD::Rake::YardocTask.new(:yarddoc) do |t| end task benchmark: [:install] do + require 'squib' Squib::logger.level = Logger::ERROR #silence warnings Dir.chdir('benchmarks') do Benchmark.bm(15) do |bm| diff --git a/benchmarks/backend-memory.rb b/benchmarks/backend-memory.rb new file mode 100644 index 0000000..7a6271b --- /dev/null +++ b/benchmarks/backend-memory.rb @@ -0,0 +1,14 @@ +require 'squib' + +Squib::Deck.new(cards: 200) do + background color: :white + text str: "Hello, world!", y: 500, width: 825, font: 'Sans bold 72', align: :center + rect x: 10, y: 10, width: 20, height: 20 + circle x: 40, y: 40, radius: 25 + triangle x1: 50, y1: 15, x2: 60, y2: 25, x3: 75, y3: 25 + line x1: 100, y1: 620, x2: 720, y2: 620, stroke_width: 15.0 + svg file: 'spanner.svg', x: 100, y: 20 + png file: 'shiny-purse.png', x: 250, y: 20 + save_png prefix: 'rasterized_' + save_pdf file: 'backend.pdf' +end \ No newline at end of file diff --git a/benchmarks/backend-svg.rb b/benchmarks/backend-svg.rb new file mode 100644 index 0000000..bdd0d2f --- /dev/null +++ b/benchmarks/backend-svg.rb @@ -0,0 +1,14 @@ +require 'squib' + +Squib::Deck.new(cards: 200, config: 'backend-svg.yml') do + background color: :white + text str: "Hello, world!", y: 500, width: 825, font: 'Sans bold 72', align: :center + rect x: 10, y: 10, width: 20, height: 20 + circle x: 40, y: 40, radius: 25 + triangle x1: 50, y1: 15, x2: 60, y2: 25, x3: 75, y3: 25 + line x1: 100, y1: 620, x2: 720, y2: 620, stroke_width: 15.0 + svg file: 'spanner.svg', x: 100, y: 20 + png file: 'shiny-purse.png', x: 250, y: 20 + save_png prefix: 'rasterized_' + save_pdf file: 'backend.pdf' +end \ No newline at end of file diff --git a/benchmarks/backend-svg.yml b/benchmarks/backend-svg.yml new file mode 100644 index 0000000..4fb9657 --- /dev/null +++ b/benchmarks/backend-svg.yml @@ -0,0 +1,4 @@ +backend: svg +prefix: vector_backend_ +count_format: '%02d' +dir: _output \ No newline at end of file diff --git a/lib/squib/card.rb b/lib/squib/card.rb index b996d11..868c1bb 100644 --- a/lib/squib/card.rb +++ b/lib/squib/card.rb @@ -17,12 +17,29 @@ module Squib # :nodoc: # @api private - def initialize(deck, width, height) - @deck=deck; @width=width; @height=height - @cairo_surface = Cairo::ImageSurface.new(width,height) + def initialize(deck, width, height, backend=:memory, index=-1) + @deck = deck + @width = width + @height = height + @svgfile = "#{deck.dir}/#{deck.prefix}#{deck.count_format % index}.svg" + @cairo_surface = make_surface(@svgfile, backend) @cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface)) end + def make_surface(svgfile, backend) + case backend + when :memory + Cairo::ImageSurface.new(@width, @height) + when :svg + Dir.mkdir @deck.dir unless Dir.exists?(@deck.dir) + Cairo::SVGSurface.new(svgfile, @width, @height) + else + Squib.logger.fatal "Back end not recognized: '#{backend}'" + abort + end + end + + # A save/restore wrapper for using Cairo # :nodoc: # @api private diff --git a/lib/squib/constants.rb b/lib/squib/constants.rb index 80ae9f2..87f67ad 100644 --- a/lib/squib/constants.rb +++ b/lib/squib/constants.rb @@ -64,10 +64,13 @@ module Squib # @api public CONFIG_DEFAULTS = { 'custom_colors' => {}, - 'dpi' => 300, - 'hint' => :none, - 'progress_bar' => false, - 'img_dir' => '.', + 'dpi' => 300, + 'hint' => :none, + 'progress_bar' => false, + 'img_dir' => '.', + 'dir' => SYSTEM_DEFAULTS[:dir], + 'backend' => 'memory', + 'count_format' => SYSTEM_DEFAULTS[:count_format] } # These are parameters that are intended to be "expanded" across diff --git a/lib/squib/deck.rb b/lib/squib/deck.rb index 672d36e..693e68d 100644 --- a/lib/squib/deck.rb +++ b/lib/squib/deck.rb @@ -36,6 +36,8 @@ module Squib # @api private attr_reader :layout, :config + attr_reader :dir, :prefix, :count_format + # Squib's constructor that sets the immutable properties. # # This is the starting point for Squib. In providing a block to the constructor, you have access to all of Deck's instance methods. @@ -57,17 +59,21 @@ module Squib # @api public def initialize(width: 825, height: 1125, cards: 1, dpi: 300, config: 'config.yml', layout: nil, &block) @dpi = dpi - @width = Args::UnitConversion.parse width, dpi - @height = Args::UnitConversion.parse height, dpi - @font = Squib::SYSTEM_DEFAULTS[:default_font] + @font = SYSTEM_DEFAULTS[:default_font] @cards = [] @custom_colors = {} @img_dir = '.' - @progress_bar = Squib::Progress.new(false) + @progress_bar = Progress.new(false) @text_hint = :off - cards.times{ @cards << Squib::Card.new(self, @width, @height) } + @backend = :memory + @dir = SYSTEM_DEFAULTS[:dir] + @prefix = SYSTEM_DEFAULTS[:prefix] + @count_format = SYSTEM_DEFAULTS[:count_format] show_info(config, layout) load_config(config) + @width = Args::UnitConversion.parse width, dpi + @height = Args::UnitConversion.parse height, dpi + cards.times{ |i| @cards << Squib::Card.new(self, @width, @height, @backend, i) } @layout = LayoutParser.load_layout(layout) if block_given? instance_eval(&block) # here we go. wheeeee! @@ -93,12 +99,16 @@ module Squib def load_config(file) if File.exists?(file) && config = YAML.load_file(file) Squib::logger.info { " using config: #{file}" } - config = Squib::CONFIG_DEFAULTS.merge(config) - @dpi = config['dpi'].to_i - @text_hint = config['text_hint'] + config = CONFIG_DEFAULTS.merge(config) + @dpi = config['dpi'].to_i + @text_hint = config['text_hint'] @progress_bar.enabled = config['progress_bars'] - @custom_colors = config['custom_colors'] - @img_dir = config['img_dir'] + @custom_colors = config['custom_colors'] + @img_dir = config['img_dir'] + @backend = (config['backend'].to_s.downcase.strip == 'svg') ? :svg : :memory + @dir = config['dir'] + @prefix = config['prefix'] + @count_format = config['count_format'] end end @@ -108,6 +118,7 @@ module Squib def show_info(config, layout) Squib::logger.info "Squib v#{Squib::VERSION}" Squib::logger.info " building #{@cards.size} #{@width}x#{@height} cards" + Squib::logger.info " using #{@backend}" end ################## diff --git a/lib/squib/graphics/image.rb b/lib/squib/graphics/image.rb index a7c473e..6b7631b 100644 --- a/lib/squib/graphics/image.rb +++ b/lib/squib/graphics/image.rb @@ -27,7 +27,7 @@ module Squib cc.scale(width.to_f / png.width.to_f, height.to_f / png.height.to_f) end cc.rotate(angle) - cc.translate(-1 * x, -1 * y) + cc.translate(-x, -y) cc.set_source(png, x, y) cc.operator = blend unless blend == :none if mask.nil? @@ -44,24 +44,25 @@ module Squib def svg(file, id, x, y, width, height, alpha, blend, angle, mask) Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"} return if file.nil? or file.eql? '' - svg = RSVG::Handle.new_from_file(file) - width = svg.width if width == :native - height = svg.height if height == :native - tmp = Cairo::ImageSurface.new(width, height) - tmp_cc = Cairo::Context.new(tmp) - tmp_cc.scale(width.to_f / svg.width.to_f, height.to_f / svg.height.to_f) - tmp_cc.render_rsvg_handle(svg, id) + svg = RSVG::Handle.new_from_file(file) + width = svg.width if width == :native + height = svg.height if height == :native + scale_width = width.to_f / svg.width.to_f + scale_height = height.to_f / svg.height.to_f use_cairo do |cc| cc.translate(x, y) cc.rotate(angle) - cc.translate(-1 * x, -1 * y) + cc.scale(scale_width, scale_height) cc.operator = blend unless blend == :none + #FIXME Alpha is no longer used since we are not using cc.paint anymore if mask.nil? - cc.set_source(tmp, x, y) - cc.paint(alpha) + cc.render_rsvg_handle(svg, id) else + tmp = Cairo::ImageSurface.new(width / scale_width, height / scale_height) + tmp_cc = Cairo::Context.new(tmp) + tmp_cc.render_rsvg_handle(svg, id) cc.set_source_squibcolor(mask) - cc.mask(tmp, x, y) + cc.mask(tmp, 0, 0) end end end diff --git a/lib/squib/graphics/save_doc.rb b/lib/squib/graphics/save_doc.rb index 74edd71..c21149e 100644 --- a/lib/squib/graphics/save_doc.rb +++ b/lib/squib/graphics/save_doc.rb @@ -16,22 +16,28 @@ module Squib # @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]) - cc = Cairo::Context.new(Cairo::PDFSurface.new("#{p[:dir]}/#{p[:file]}", p[:width], p[:height])) - x = p[:margin] - y = p[:margin] - @progress_bar.start("Saving PDF to #{p[:dir]}/#{p[:file]}", p[:range].size) do |bar| + opts = {width: 3300, height: 2550}.merge(opts) + p = needs(opts, [:range, :paper_width, :paper_height, :file_to_save, + :creatable_dir, :margin, :gap, :trim]) + width = p[:width] + height = p[:height] + file = "#{p[:dir]}/#{p[:file]}" + cc = Cairo::Context.new(Cairo::PDFSurface.new(file, width, height)) + x = p[:margin] + y = p[:margin] + @progress_bar.start("Saving PDF to #{file}", p[:range].size) do |bar| p[:range].each do |i| surface = trim(@cards[i].cairo_surface, p[:trim], @width, @height) cc.set_source(surface, x, y) + surface = @cards[i].cairo_surface + cc.set_source(surface, x, y) cc.paint bar.increment x += surface.width + p[:gap] - if x > (p[:width] - surface.width - p[:margin]) + if x > (width - surface.width - p[:margin]) x = p[:margin] y += surface.height + p[:gap] - if y > (p[:height] - surface.height - p[:margin]) + if y > (height - surface.height - p[:margin]) x = p[:margin] y = p[:margin] cc.show_page #next page diff --git a/lib/squib/project_template/config.yml b/lib/squib/project_template/config.yml index 55cefc0..9b0b983 100644 --- a/lib/squib/project_template/config.yml +++ b/lib/squib/project_template/config.yml @@ -17,4 +17,9 @@ #For reading image file command (e.g. png and svg), read from this directory instead #img_dir: img-color -#img_dir: img-bw \ No newline at end of file +#img_dir: img-bw + +# Use a SVG cairo back end, instead of an in-memory buffer +# backend: :memory # default +# backend: :svg # slower, but can create scalable pdfs +# tmp_dir: _tmp # for SVG backend, you MUST specify a tmp_dir diff --git a/samples/backend-config.yml b/samples/backend-config.yml new file mode 100644 index 0000000..330b987 --- /dev/null +++ b/samples/backend-config.yml @@ -0,0 +1,5 @@ +# To configure for using SVG as our backend, we need to set this option +# This will create a series of SVG files that get updated with Squib command +backend: svg +# They are auto-saved with this background +prefix: backend_ diff --git a/samples/backend.rb b/samples/backend.rb new file mode 100644 index 0000000..eef9c39 --- /dev/null +++ b/samples/backend.rb @@ -0,0 +1,32 @@ +require 'squib' + +# Our SVGs are auto-saved after each step using the configuration parameters +Squib::Deck.new(config: 'backend-config.yml') do + + # These are all supported by the SVG backend + background color: :white + text str: "Hello, world!", y: 500, width: 825, font: 'Sans bold 72', align: :center + rect x: 10, y: 10, width: 20, height: 20 + circle x: 40, y: 40, radius: 25 + triangle x1: 50, y1: 15, x2: 60, y2: 25, x3: 75, y3: 25 + line x1: 100, y1: 620, x2: 720, y2: 620, stroke_width: 15.0 + svg file: 'spanner.svg', x: 100, y: 20 + svg file: 'glass-heart.svg', x: 100, y: 200, width: 100, height: 100, mask: :sangria + png file: 'shiny-purse.png', x: 250, y: 20 + png file: 'shiny-purse.png', x: 250, y: 200, mask: :red + + # We can still rasterize whenever we want + save_png prefix: 'backend_' + + # And our PDFs will be vectorized. + save_pdf file: 'backend_vectorized.pdf' + + # This one is, unfortunately, not possible with svg back ends + # Cairo lacks a perspective transform (currently), so we have to + # use a striping method, which assumes raster. Fortunately, Cairo + # has perspective transforms on its roadmap, + # so perhaps this can be done someday with all vectors. + # + # showcase file: 'showcase.png', fill_color: 'white' + +end diff --git a/samples/cairopdfsvg.rb b/samples/cairopdfsvg.rb new file mode 100644 index 0000000..12bd887 --- /dev/null +++ b/samples/cairopdfsvg.rb @@ -0,0 +1,11 @@ +require 'cairo' +require 'pango' +require 'rsvg2' + +svg = RSVG::Handle.new_from_file('spanner.svg') +pdf = Cairo::PDFSurface.new("_output/blahblah2.pdf",8.5*300, 11*300) +cxt = Cairo::Context.new(pdf) +cxt.translate(1900,500) +cxt.render_rsvg_handle(svg, nil) +cxt.translate(-500,0) +cxt.render_rsvg_handle(svg, nil) \ No newline at end of file diff --git a/spec/data/samples/basic.rb.txt b/spec/data/samples/basic.rb.txt index f394e9c..4613f72 100644 --- a/spec/data/samples/basic.rb.txt +++ b/spec/data/samples/basic.rb.txt @@ -183,23 +183,17 @@ cairo: translate([-620, -75]) cairo: set_source([ImageSurface, 620, 75]) cairo: paint([1.0]) cairo: restore([]) -cairo: scale([1.0, 1.0]) -cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: save([]) cairo: translate([620, 218]) cairo: rotate([0]) -cairo: translate([-620, -218]) -cairo: set_source([MockDouble, 620, 218]) -cairo: paint([1.0]) -cairo: restore([]) cairo: scale([1.0, 1.0]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: restore([]) cairo: save([]) cairo: translate([620, 218]) cairo: rotate([0]) -cairo: translate([-620, -218]) -cairo: set_source([MockDouble, 620, 218]) -cairo: paint([1.0]) +cairo: scale([1.0, 1.0]) +cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: restore([]) surface: write_to_png(["_output/basic_00.png"]) surface: write_to_png(["_output/basic_01.png"]) diff --git a/spec/data/samples/custom_config.rb.txt b/spec/data/samples/custom_config.rb.txt index 8b6c17f..988f1c7 100644 --- a/spec/data/samples/custom_config.rb.txt +++ b/spec/data/samples/custom_config.rb.txt @@ -31,14 +31,11 @@ cairo: translate([-620, -75]) cairo: set_source([ImageSurface, 620, 75]) cairo: paint([1.0]) cairo: restore([]) -cairo: scale([1.0, 1.0]) -cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: save([]) cairo: translate([620, 218]) cairo: rotate([0]) -cairo: translate([-620, -218]) -cairo: set_source([MockDouble, 620, 218]) -cairo: paint([1.0]) +cairo: scale([1.0, 1.0]) +cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: restore([]) surface: write_to_png(["_output/custom-config_00.png"]) cairo: set_source([MockDouble, 75, 75]) diff --git a/spec/data/samples/load_images.rb.txt b/spec/data/samples/load_images.rb.txt index 9aecf26..1d7a912 100644 --- a/spec/data/samples/load_images.rb.txt +++ b/spec/data/samples/load_images.rb.txt @@ -18,23 +18,17 @@ cairo: translate([-620, -75]) cairo: set_source([ImageSurface, 620, 75]) cairo: paint([1.0]) cairo: restore([]) -cairo: scale([1.0, 1.0]) -cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: save([]) cairo: translate([620, 218]) cairo: rotate([0]) -cairo: translate([-620, -218]) -cairo: set_source([MockDouble, 620, 218]) -cairo: paint([1.0]) -cairo: restore([]) -cairo: scale([1.953125, 1.953125]) +cairo: scale([1.0, 1.0]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: restore([]) cairo: save([]) cairo: translate([50, 50]) cairo: rotate([0]) -cairo: translate([-50, -50]) -cairo: set_source([MockDouble, 50, 50]) -cairo: paint([1.0]) +cairo: scale([1.953125, 1.953125]) +cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: restore([]) cairo: save([]) cairo: translate([305, 50]) @@ -44,32 +38,23 @@ cairo: translate([-305, -50]) cairo: set_source([ImageSurface, 305, 50]) cairo: paint([1.0]) cairo: restore([]) -cairo: scale([0.5859375, 0.5859375]) -cairo: render_rsvg_handle([RSVG::Handle, "#backdrop"]) cairo: save([]) cairo: translate([50, 350]) cairo: rotate([0]) -cairo: translate([-50, -350]) -cairo: set_source([MockDouble, 50, 350]) -cairo: paint([1.0]) -cairo: restore([]) -cairo: scale([0.9765625, 0.9765625]) +cairo: scale([0.5859375, 0.5859375]) cairo: render_rsvg_handle([RSVG::Handle, "#backdrop"]) +cairo: restore([]) cairo: save([]) cairo: translate([50, 450]) cairo: rotate([0]) -cairo: translate([-50, -450]) -cairo: set_source([MockDouble, 50, 450]) -cairo: paint([1.0]) +cairo: scale([0.9765625, 0.9765625]) +cairo: render_rsvg_handle([RSVG::Handle, "#backdrop"]) cairo: restore([]) -cairo: scale([0.8021390374331551, 0.8032128514056225]) -cairo: render_rsvg_handle([RSVG::Handle, "#thing"]) cairo: save([]) cairo: translate([0, 0]) cairo: rotate([0]) -cairo: translate([0, 0]) -cairo: set_source([MockDouble, 0, 0]) -cairo: paint([1.0]) +cairo: scale([0.8021390374331551, 0.8032128514056225]) +cairo: render_rsvg_handle([RSVG::Handle, "#thing"]) cairo: restore([]) cairo: save([]) cairo: translate([50, 700]) @@ -100,32 +85,27 @@ cairo: translate([-300, -800]) cairo: set_source([ImageSurface, 300, 800]) cairo: paint([1.0]) cairo: restore([]) -cairo: scale([1.0, 1.0]) -cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: save([]) cairo: translate([300, 900]) cairo: rotate([1.4707963267948965]) -cairo: translate([-300, -900]) -cairo: set_source([MockDouble, 300, 900]) -cairo: paint([1.0]) -cairo: restore([]) -cairo: scale([0.390625, 0.390625]) +cairo: scale([1.0, 1.0]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: restore([]) cairo: save([]) cairo: translate([500, 600]) cairo: rotate([0]) -cairo: translate([-500, -600]) -cairo: set_source_color(["#00ff00"]) -cairo: mask([MockDouble, 500, 600]) -cairo: restore([]) cairo: scale([0.390625, 0.390625]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: set_source_color(["#00ff00"]) +cairo: mask([MockDouble, 0, 0]) +cairo: restore([]) cairo: save([]) cairo: translate([500, 800]) cairo: rotate([0]) -cairo: translate([-500, -800]) +cairo: scale([0.390625, 0.390625]) +cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: set_source([LinearPattern]) -cairo: mask([MockDouble, 500, 800]) +cairo: mask([MockDouble, 0, 0]) cairo: restore([]) cairo: save([]) cairo: translate([650, 950]) diff --git a/spec/data/samples/saves.rb.txt b/spec/data/samples/saves.rb.txt index c28b593..771ac00 100644 --- a/spec/data/samples/saves.rb.txt +++ b/spec/data/samples/saves.rb.txt @@ -478,73 +478,41 @@ cairo: update_pango_layout([MockDouble]) cairo: update_pango_layout([MockDouble]) cairo: show_pango_layout([MockDouble]) cairo: restore([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 180, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 285, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 390, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 495, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 600, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 705, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 810, 75]) -cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) -cairo: paint([]) -cairo: set_source([MockDouble, 915, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1020, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1125, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1230, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1335, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1440, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1545, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, -37, -37]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 1650, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) -cairo: set_source([MockDouble, 175, 75]) +cairo: set_source([MockDouble, 75, 75]) cairo: paint([]) surface: write_to_png(["_output/saves_notrim_01.png"]) cairo: set_source([MockDouble, -37, -37]) diff --git a/spec/data/samples/showcase.rb.txt b/spec/data/samples/showcase.rb.txt index e241671..8f5b61a 100644 --- a/spec/data/samples/showcase.rb.txt +++ b/spec/data/samples/showcase.rb.txt @@ -122,41 +122,29 @@ cairo: update_pango_layout([MockDouble]) cairo: update_pango_layout([MockDouble]) cairo: show_pango_layout([MockDouble]) cairo: restore([]) -cairo: scale([3.90625, 3.90625]) -cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: save([]) cairo: translate([162, 500]) cairo: rotate([0]) -cairo: translate([-162, -500]) -cairo: set_source([MockDouble, 162, 500]) -cairo: paint([1.0]) -cairo: restore([]) cairo: scale([3.90625, 3.90625]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: restore([]) cairo: save([]) cairo: translate([162, 500]) cairo: rotate([0]) -cairo: translate([-162, -500]) -cairo: set_source([MockDouble, 162, 500]) -cairo: paint([1.0]) -cairo: restore([]) cairo: scale([3.90625, 3.90625]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: restore([]) cairo: save([]) cairo: translate([162, 500]) cairo: rotate([0]) -cairo: translate([-162, -500]) -cairo: set_source([MockDouble, 162, 500]) -cairo: paint([1.0]) -cairo: restore([]) cairo: scale([3.90625, 3.90625]) cairo: render_rsvg_handle([RSVG::Handle, nil]) +cairo: restore([]) cairo: save([]) cairo: translate([162, 500]) cairo: rotate([0]) -cairo: translate([-162, -500]) -cairo: set_source([MockDouble, 162, 500]) -cairo: paint([1.0]) +cairo: scale([3.90625, 3.90625]) +cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: restore([]) cairo: set_source_color([:white]) cairo: paint([]) diff --git a/spec/data/samples/tgc_proofs.rb.txt b/spec/data/samples/tgc_proofs.rb.txt index d72b650..86853a9 100644 --- a/spec/data/samples/tgc_proofs.rb.txt +++ b/spec/data/samples/tgc_proofs.rb.txt @@ -62,14 +62,11 @@ cairo: translate([-620, -75]) cairo: set_source([ImageSurface, 620, 75]) cairo: paint([1.0]) cairo: restore([]) -cairo: scale([1.0, 1.0]) -cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: save([]) cairo: translate([620, 218]) cairo: rotate([0]) -cairo: translate([-620, -218]) -cairo: set_source([MockDouble, 620, 218]) -cairo: paint([1.0]) +cairo: scale([1.0, 1.0]) +cairo: render_rsvg_handle([RSVG::Handle, nil]) cairo: restore([]) cairo: save([]) cairo: translate([0, 0]) diff --git a/spec/graphics/graphics_images_spec.rb b/spec/graphics/graphics_images_spec.rb index 0d96969..97350f2 100644 --- a/spec/graphics/graphics_images_spec.rb +++ b/spec/graphics/graphics_images_spec.rb @@ -12,6 +12,9 @@ describe Squib::Card do allow(Cairo::ImageSurface).to receive(:from_png).and_return(@png) allow(Cairo::ImageSurface).to receive(:new).and_return(@png) allow(RSVG::Handle).to receive(:new_from_file).and_return(@svg) + allow(@deck).to receive(:dir).and_return('_output') + allow(@deck).to receive(:count_format).and_return('%02d') + allow(@deck).to receive(:prefix).and_return('card_') end context '#png' do @@ -43,13 +46,10 @@ describe Squib::Card do expect(@svg).to receive(:width).and_return(100).twice expect(@svg).to receive(:height).and_return(100).twice expect(@context).to receive(:save).once - expect(@context).to receive(:translate).with(-37, -38).once expect(@context).to receive(:rotate).with(0.0).once expect(@context).to receive(:translate).with(37, 38).once expect(@context).to receive(:scale).with(1.0, 1.0).once expect(@context).to receive(:render_rsvg_handle).with(@svg, 'id').once - expect(@context).to receive(:set_source).with(@png, 37, 38).once - expect(@context).to receive(:paint).with(0.9).once expect(@context).to receive(:restore).once card = Squib::Card.new(@deck, 100, 150) diff --git a/spec/graphics/graphics_save_doc_spec.rb b/spec/graphics/graphics_save_doc_spec.rb index 9daab1d..48c68b6 100644 --- a/spec/graphics/graphics_save_doc_spec.rb +++ b/spec/graphics/graphics_save_doc_spec.rb @@ -2,21 +2,20 @@ require 'spec_helper' require 'squib' describe Squib::Deck, '#save_pdf' do - def expect_card_place(x, y) - expect(@context).to receive(:set_source) - .with(instance_of(Cairo::ImageSurface), -37, -37) - .once # trim the card - expect(@context).to receive(:paint).once # paint trimmed card - expect(@context).to receive(:set_source) # place the card - .with(instance_of(Cairo::ImageSurface), x, y).once - expect(@context).to receive(:paint).once # paint placed card + expect(cxt).to receive(:set_source) + .with(instance_of(Cairo::ImageSurface), -37, -37) + .once # trim the card + expect(cxt).to receive(:paint).once # paint trimmed card + expect(cxt).to receive(:set_source) # place the card + .with(instance_of(Cairo::ImageSurface), x, y).at_least(1).times + expect(cxt).to receive(:paint).once # paint placed card end context 'typical inputs' do + let(:cxt) { double(Cairo::Context) } before(:each) do - @context = double(Cairo::Context) allow(Cairo::PDFSurface).to receive(:new).and_return(nil) #don't create the file end @@ -25,7 +24,7 @@ describe Squib::Deck, '#save_pdf' do args = { 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(Cairo::Context).to receive(:new).and_return(@context).exactly(num_cards + 1).times + expect(Cairo::Context).to receive(:new).and_return(cxt).at_least(num_cards + 1).times expect(deck).to receive(:dirify) { |arg| arg } #don't create the dir expect_card_place(75, 75) @@ -36,7 +35,7 @@ describe Squib::Deck, '#save_pdf' do expect_card_place(831, 1131) expect_card_place(1587, 1131) expect_card_place(2343, 1131) - expect(@context).to receive(:show_page).once + expect(cxt).to receive(:show_page).once expect_card_place(75, 75) deck.save_pdf(args) @@ -47,7 +46,7 @@ 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(Cairo::Context).to receive(:new).and_return(@context).exactly(4).times + expect(Cairo::Context).to receive(:new).and_return(cxt).exactly(4).times expect(deck).to receive(:dirify) { |arg| arg } #don't create the dir expect_card_place(75, 75) diff --git a/spec/graphics/graphics_shapes_spec.rb b/spec/graphics/graphics_shapes_spec.rb index 4547d83..08dff0e 100644 --- a/spec/graphics/graphics_shapes_spec.rb +++ b/spec/graphics/graphics_shapes_spec.rb @@ -15,6 +15,9 @@ describe Squib::Card do @deck = double(Squib::Deck) @context = double(Cairo::Context) allow(Cairo::Context).to receive(:new).and_return(@context) + allow(@deck).to receive(:dir).and_return('_output') + allow(@deck).to receive(:count_format).and_return('%02d') + allow(@deck).to receive(:prefix).and_return('card_') end context 'rect' do diff --git a/spec/graphics/graphics_text_spec.rb b/spec/graphics/graphics_text_spec.rb index 2ac0342..11584f6 100644 --- a/spec/graphics/graphics_text_spec.rb +++ b/spec/graphics/graphics_text_spec.rb @@ -11,6 +11,9 @@ describe Squib::Card, '#text' do before(:each) do allow(Cairo::Context).to receive(:new).and_return(context) + allow(deck).to receive(:dir).and_return('_output') + allow(deck).to receive(:count_format).and_return('%02d') + allow(deck).to receive(:prefix).and_return('card_') end it 'make all the expected calls on a smoke test' do @@ -39,7 +42,7 @@ describe Squib::Card, '#text' do expect(context).to receive(:show_pango_layout).once expect(context).to receive(:restore).once - card = Squib::Card.new(@deck, 100, 150) + card = Squib::Card.new(deck, 100, 150) # text(str, font, font_size, color, # x, y, width, height, # markup, justify, wrap, ellipsize, @@ -53,18 +56,22 @@ describe Squib::Card, '#text' do end context 'convenience lookups' do + let(:deck) { double(Squib::Deck) } + let(:context) { double(Cairo::Context).as_null_object } + let(:layout) { double(Pango::Layout).as_null_object } + let(:extents) { double("extents") } + before(:each) do - @deck = double(Squib::Deck) - @context = double(Cairo::Context).as_null_object - @layout = double(Pango::Layout).as_null_object - @extents = double("extents") - allow(Cairo::Context).to receive(:new).and_return(@context) - expect(@context).to receive(:create_pango_layout).once.and_return(@layout) + allow(Cairo::Context).to receive(:new).and_return(context) + expect(context).to receive(:create_pango_layout).once.and_return(layout) + allow(deck).to receive(:dir).and_return('_output') + allow(deck).to receive(:count_format).and_return('%02d') + allow(deck).to receive(:prefix).and_return('card_') end it 'aligns right with strings' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:alignment=).with(Pango::Layout::ALIGN_RIGHT).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:alignment=).with(Pango::Layout::ALIGN_RIGHT).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, false, false, @@ -72,8 +79,8 @@ describe Squib::Card, '#text' do end it 'aligns center with strings' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:alignment=).with(Pango::Layout::ALIGN_CENTER).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:alignment=).with(Pango::Layout::ALIGN_CENTER).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, false, false, @@ -81,8 +88,8 @@ describe Squib::Card, '#text' do end it 'sets wrap to char with string char' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_CHAR).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:wrap=).with(Pango::Layout::WRAP_CHAR).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, 'char', false, @@ -90,8 +97,8 @@ describe Squib::Card, '#text' do end it 'sets wrap to word with word string' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, 'word', false, @@ -99,8 +106,8 @@ describe Squib::Card, '#text' do end it 'sets wrap to word_char with symbol word_char' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD_CHAR).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD_CHAR).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, :word_char, false, @@ -108,8 +115,8 @@ describe Squib::Card, '#text' do end it 'sets wrap to word_char with true' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD_CHAR).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD_CHAR).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, true, false, @@ -117,8 +124,8 @@ describe Squib::Card, '#text' do end it 'sets ellipsize to start properly' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_START).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_START).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, true, :start, @@ -126,8 +133,8 @@ describe Squib::Card, '#text' do end it 'sets ellipsize to middle properly' do - card = Squib::Card.new(@deck, 100, 150) - expect(@layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_MIDDLE).once + card = Squib::Card.new(deck, 100, 150) + expect(layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_MIDDLE).once card.text('foo', 'Sans 12', nil, '#abc', 10, 15, 20, 50, nil, false, true, 'middle', diff --git a/spec/samples/samples_regression_spec.rb b/spec/samples/samples_regression_spec.rb index c05f2eb..deb80af 100644 --- a/spec/samples/samples_regression_spec.rb +++ b/spec/samples/samples_regression_spec.rb @@ -69,7 +69,7 @@ describe "Squib samples" do log = StringIO.new mock_cairo(log) load sample - overwrite_sample(sample, log) # Use TEMPORARILY once happy with the new sample log + # overwrite_sample(sample, log) # Use TEMPORARILY once happy with the new sample log test_file_str = File.open(sample_regression_file(sample), 'r:UTF-8').read expect(log.string).to eq(test_file_str) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dafdcb1..b773814 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'simplecov' require 'coveralls' +require 'byebug' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ SimpleCov::Formatter::HTMLFormatter,