squib 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/CHANGELOG.md +11 -1
  4. data/README.md +24 -4
  5. data/Rakefile +2 -0
  6. data/benchmarks/antialias_best.rb +13 -0
  7. data/benchmarks/antialias_best.yml +1 -0
  8. data/benchmarks/antialias_fast.rb +13 -0
  9. data/benchmarks/antialias_fast.yml +1 -0
  10. data/benchmarks/backend-memory.rb +14 -0
  11. data/benchmarks/backend-svg.rb +14 -0
  12. data/benchmarks/backend-svg.yml +4 -0
  13. data/lib/squib/api/image.rb +2 -2
  14. data/lib/squib/api/save.rb +3 -3
  15. data/lib/squib/card.rb +23 -4
  16. data/lib/squib/constants.rb +14 -4
  17. data/lib/squib/deck.rb +24 -11
  18. data/lib/squib/graphics/cairo_context_wrapper.rb +2 -1
  19. data/lib/squib/graphics/image.rb +13 -12
  20. data/lib/squib/graphics/save_doc.rb +37 -18
  21. data/lib/squib/graphics/shapes.rb +1 -0
  22. data/lib/squib/project_template/config.yml +8 -1
  23. data/lib/squib/version.rb +1 -1
  24. data/samples/backend-config.yml +5 -0
  25. data/samples/backend.rb +33 -0
  26. data/samples/glass-heart.svg +52 -52
  27. data/samples/load_images.rb +3 -3
  28. data/spec/data/samples/autoscale_font.rb.txt +9 -3
  29. data/spec/data/samples/basic.rb.txt +12 -15
  30. data/spec/data/samples/cairo_access.rb.txt +2 -0
  31. data/spec/data/samples/csv_import.rb.txt +8 -6
  32. data/spec/data/samples/custom_config.rb.txt +10 -7
  33. data/spec/data/samples/draw_shapes.rb.txt +2 -0
  34. data/spec/data/samples/excel.rb.txt +12 -9
  35. data/spec/data/samples/gradients.rb.txt +3 -1
  36. data/spec/data/samples/hello_world.rb.txt +4 -2
  37. data/spec/data/samples/load_images.rb.txt +19 -38
  38. data/spec/data/samples/portrait-landscape.rb.txt +4 -2
  39. data/spec/data/samples/ranges.rb.txt +27 -24
  40. data/spec/data/samples/saves.rb.txt +161 -84
  41. data/spec/data/samples/showcase.rb.txt +13 -21
  42. data/spec/data/samples/text_options.rb.txt +48 -39
  43. data/spec/data/samples/tgc_proofs.rb.txt +5 -7
  44. data/spec/data/samples/units.rb.txt +1 -0
  45. data/spec/deck_spec.rb +1 -1
  46. data/spec/graphics/graphics_images_spec.rb +7 -3
  47. data/spec/graphics/graphics_save_doc_spec.rb +36 -39
  48. data/spec/graphics/graphics_shapes_spec.rb +9 -0
  49. data/spec/graphics/graphics_text_spec.rb +73 -62
  50. data/spec/input_helpers_spec.rb +27 -10
  51. data/spec/spec_helper.rb +12 -9
  52. data/squib.gemspec +2 -2
  53. metadata +27 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b78f6ac8982e7aec43208726ad1e5160a9417a39
4
- data.tar.gz: a22fe5b8bb4347867c89f2d675c410166f104633
3
+ metadata.gz: d42de6fef7642fe4a8502678c202e8fc846d7c23
4
+ data.tar.gz: e04f188ec47f347cdf7aa468d3f035fe40fa4191
5
5
  SHA512:
6
- metadata.gz: 86637107259a7b2051c8d85624070859e7084b0830979acc8a537f32f0a9ba38d9c2ed2718b4b5b8b20a5b41e71ee864ec39beea28b4eeb21f785054c1a224cb
7
- data.tar.gz: 77f4a6dbc532038583ad321f47795fc0452af6e310b327d5302493fb5d567fe30b469e42984b3a34e1b8c7883592508b998ad5df1ec238c3e13ffacb61d13822
6
+ metadata.gz: cb9edf34ebf85f39a2dc2175e0e7e8c0fcba2f1b1635979f4ea6d3b35873c30cd5a440db517abda02c68c19565230df9b8976630d70cfffd470a13a23719521e
7
+ data.tar.gz: c014e1b53e200277c5a544f96c0cb792a29c0cf9ac1b5d741430bddb2d8de38ef4409c00e879434187a7d7f916b6ba8e772d132d007c344e6719196224ab6eed
data/.gitignore CHANGED
@@ -29,4 +29,6 @@ samples/_output/*.pdf
29
29
  samples/_output/foo
30
30
  rubocop.txt
31
31
  benchmarks/_output/*.png
32
- benchmarks/_output/*.pdf
32
+ benchmarks/_output/*.pdf
33
+ benchmarks/_output/
34
+ samples/_output/*.svg
@@ -1,6 +1,16 @@
1
1
  # Squib CHANGELOG
2
2
 
3
- ## v0.3.0a
3
+ # v0.4.0
4
+ * SVG backend support! You can now set the deck's back end to work with SVGs instead of images, making the resulting PDFs vectorized. (You can still save to PNGs too.) This was a big change for Squib, and it's got at least one known issue and probably a few more here and there. See discussion on the README for more details.
5
+ * Added config option for antialiasing method. My benchmarks showed that 'best' is only 10% slower than 'fast' on extremely alias-intensive tasks, so that's the Squib default now.
6
+ * Bugfix: Stray stroke on circles after text (#35)
7
+ * Bugfix: Progress bar increment error (#34)
8
+
9
+ Known issues
10
+ * Masking SVGs onto an SVG backend will rasterize as an intermediate step. (#43)
11
+ * Compatibility change: gradient coordinates for the `mask` option in `svg` and `png` commands are relative to the given x,y - NOT to card as it was before.
12
+
13
+ ## v0.3.0
4
14
  * Masks! The `png` and `svg` commands can be used as if they are a mask, so you can color the icon with any color you like. Can be handy for switching to black-and-white, or for reusing the same image but different colors across cards.
5
15
  * Gradients! Can now specify linear or radial gradients anywhere you specify colors. See README and `samples/gradients.rb` for more details.
6
16
  * Number padding! `save_png` will now pad zeros on the filenames for friendlier sorting. You can also specify your own with `count_format` using the classical format string from Ruby's `Kernel::sprintf` (mostly just C-style format strings). Default: `'%02d'. The `prefix:` option is still there too.
data/README.md CHANGED
@@ -1,16 +1,18 @@
1
1
  # Squib [![Gem Version](https://badge.fury.io/rb/squib.svg)](https://rubygems.org/gems/squib) [![Build Status](https://secure.travis-ci.org/andymeneely/squib.svg?branch=master)](https://travis-ci.org/andymeneely/squib) [![Dependency Status](https://gemnasium.com/andymeneely/squib.svg)](https://gemnasium.com/andymeneely/squib) [![Coverage Status](https://img.shields.io/coveralls/andymeneely/squib.svg)](https://coveralls.io/r/andymeneely/squib) [![Inline docs](http://inch-ci.org/github/andymeneely/squib.png?branch=master)](http://inch-ci.org/github/andymeneely/squib)
2
- Squib is a Ruby [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) for prototyping card and board games. Write a little bit of Ruby, define your deck's stats, and you can compile your game into a series of images ready for print-and-play or even print-on-demand. Squib is very data-driven and built on the principle of Don't Repeat Yourself. Think of it like [nanDeck](http://www.nand.it/nandeck/) done "the Ruby way". Squib supports:
2
+ Squib is a Ruby [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) for prototyping card and board games. Write a little bit of Ruby, define your deck's stats, then compile your game into a series of images ready for print-and-play or even print-on-demand. Squib is very data-driven and built on the principle of Don't Repeat Yourself. Think of it like [nanDeck](http://www.nand.it/nandeck/) done "the Ruby way". Squib supports:
3
3
 
4
4
  * A concise set of rules for laying out your cards
5
- * Loading PNGs and SVGs using [Cairo](http://cairographics.org/)
5
+ * Loading PNGs and SVGs
6
6
  * Complex text rendering using [Pango](http://www.pango.org/)
7
7
  * Reading `xlsx` and `csv` files
8
- * Rendering to individual PNGs or PDF sheets
8
+ * Rendering to PNGs, PDFs, and SVGs (sheets or individual files)
9
9
  * Flexible, data-driven layouts in Yaml
10
- * Basic shape drawing
10
+ * Basic shape drawing, blending operators, gradients, etc.
11
11
  * Unit conversion
12
12
  * The full power of Ruby!
13
13
 
14
+ Squib is based on the [Cairo](http://cairographics.org/) graphics rendering engine, the library of choice for WebKit, Gecko, Inkscape and many, many others.
15
+
14
16
  Check this out.
15
17
 
16
18
  ```ruby
@@ -287,6 +289,21 @@ This can hopefully be helpful for:
287
289
 
288
290
  If your layout file is not found in the current directory, Squib will search for its own set of layout files (here's the latest the development version [on GitHub](https://github.com/andymeneely/squib/tree/master/lib/squib/layouts). See the `layouts.rb` sample found [here](https://github.com/andymeneely/squib/tree/master/samples/) for some demonstrative examples.
289
291
 
292
+ ## Backends: Raster vs. Vector
293
+ Under the hood, Cairo has the ability to support a variety of surfaces to draw on, including both raster images stored in memory and vectors stored in SVG files. Thus, Squib supports the ability to handle both. They are options in the configuration file `backend: memory` or `backend: svg`.
294
+
295
+ If you save to a PDF then the backend will determine how your cards are saved too. For `memory`, the PDF will be filled with compressed raster images and be a larger file (yet it will still print at high quality... see discussion below). For SVG backends, PDFs will be smaller. If you have your deck backed by SVG, then the cards are auto-saved, so there is no `save_svg` in Squib. (Technically, the operations are stored and then flushed to the SVG file at the very end.)
296
+
297
+ There are trade-offs that one should consider here.
298
+
299
+ * _Print quality is **higher** for raster images_. This seems counterintuitive at first, but consider where Squib sits in your workflow. It's the final assembly line for your cards before they get printed. Cairo puts _a ton_ of work into rendering each pixel perfectly when it works with raster images. Printers, on the other hand, don't think in vectors and will render your paths in their own memory with their own embedded libraries without putting a lot of work into antialiasing and various other graphical esoterica. You may notice that print-on-demand companies such as The Game Crafter [only accept raster file types](https://www.thegamecrafter.com/help/supported-file-types), because they don't want their customers complaining about printers not rendering vectors with enough care.
300
+ * _PDFs are **smaller** for SVG back ends_. If file size is a limitation for you, and it can be for some printers or internet forums, then an SVG back end for vectorized PDFs is the way to go.
301
+ * _Squib is **greedy** with memory_. While I've tested Squib with big decks on older computers, the `memory` backend is quite greedy with RAM. If memory is at a premium for you, switching to SVG might help.
302
+
303
+ Note: you can still load PNGs into an SVG-backed deck and load SVGs into a memory-backed deck. To me, the sweet spot is to keep all of my icons, text, and other stuff in vector form for infinite scaling and then render them all to pixels with Squib.
304
+
305
+ Fortunately, switching backends in Squib should be as trivial as changing the setting in the config file. So go ahead and experiment with both and see what works for you. See below for how the configuration options work.
306
+
290
307
  ## Configuration File
291
308
 
292
309
  Squib supports various configuration properties that can be specified in an external file. The `config:` option in `Deck.new` can specify an optional configuration file in YML format. The properties there are intended to be immutable for the life of the Deck. The options include:
@@ -295,6 +312,9 @@ Squib supports various configuration properties that can be specified in an exte
295
312
  * `dpi` (Integer, default: 300). Used in calculations when units are used (e.g. for PDF rendering and unit conversion).
296
313
  * `hint` (ColorString, default: off). Text hints are used to show the boundaries of text boxes. Can be enabled/disabled for individual commands, or set globally with the `set` command. This setting is overriden by `set` and individual commands.
297
314
  * `custom_colors` (Hash of Colors, default: {}). Defines globally-available colors available to the deck that can be specified in commands.
315
+ * `antialias` (`fast, good, best, none`, default: best). Set the algorithm that Cairo will use for antialiasing. Using our benchmarks on large decks, `best` is only X% slower anyway. For more info see the [Cairo docs](http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-antialias-t).
316
+ * `backend` (`svg` or `memory`, default: `memory`). Defines how Cairo will store the operations. Memory is recommended for higher quality rendering.
317
+ * `prefix` (default: `card_`). When using an SVG backend, cards are auto-saved with this prefix and `"%02d"` numbering format.
298
318
 
299
319
  The following sample demonstrates the config file.
300
320
 
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'yard'
4
4
  require 'benchmark'
5
+ require 'byebug'
5
6
 
6
7
  task default: [:install, :spec]
7
8
 
@@ -26,6 +27,7 @@ YARD::Rake::YardocTask.new(:yarddoc) do |t|
26
27
  end
27
28
 
28
29
  task benchmark: [:install] do
30
+ require 'squib'
29
31
  Squib::logger.level = Logger::ERROR #silence warnings
30
32
  Dir.chdir('benchmarks') do
31
33
  Benchmark.bm(15) do |bm|
@@ -0,0 +1,13 @@
1
+ require 'squib'
2
+
3
+ Squib::Deck.new(cards: 200, config: 'antialias_best.yml') do
4
+ background color: :white
5
+ # alphabet = 'a'.upto('z').to_a.join + ' ' + 'A'.upto('Z').to_a.join + ' '
6
+ # text str: alphabet * 36 , font: 'Sans Bold 18', width: 825, height: 1125, hint: :red
7
+ 0.upto(500).each do |i|
8
+ circle radius: 50,
9
+ x: (i % 17) * 50,
10
+ y: (i / 17) * 50
11
+ end
12
+ save_png prefix: 'antialias_best_'
13
+ end
@@ -0,0 +1 @@
1
+ antialias: best
@@ -0,0 +1,13 @@
1
+ require 'squib'
2
+
3
+ Squib::Deck.new(cards: 200, config: 'antialias_fast.yml') do
4
+ background color: :white
5
+ # alphabet = 'a'.upto('z').to_a.join + ' ' + 'A'.upto('Z').to_a.join + ' '
6
+ # text str: alphabet * 36 , font: 'Sans Bold 18', width: 825, height: 1125, hint: :red
7
+ 0.upto(500).each do |i|
8
+ circle radius: 50,
9
+ x: (i % 17) * 50,
10
+ y: (i / 17) * 50
11
+ end
12
+ save_png prefix: 'antialias_fast_'
13
+ end
@@ -0,0 +1 @@
1
+ antialias: fast
@@ -0,0 +1,14 @@
1
+ require 'squib'
2
+
3
+ Squib::Deck.new(cards: 200) do
4
+ background color: :white
5
+ text str: "Hello, world!", y: 500, width: 825, font: 'Sans bold 72', align: :center
6
+ rect x: 10, y: 10, width: 20, height: 20
7
+ circle x: 40, y: 40, radius: 25
8
+ triangle x1: 50, y1: 15, x2: 60, y2: 25, x3: 75, y3: 25
9
+ line x1: 100, y1: 620, x2: 720, y2: 620, stroke_width: 15.0
10
+ svg file: 'spanner.svg', x: 100, y: 20
11
+ png file: 'shiny-purse.png', x: 250, y: 20
12
+ save_png prefix: 'rasterized_'
13
+ save_pdf file: 'backend.pdf'
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'squib'
2
+
3
+ Squib::Deck.new(cards: 200, config: 'backend-svg.yml') do
4
+ background color: :white
5
+ text str: "Hello, world!", y: 500, width: 825, font: 'Sans bold 72', align: :center
6
+ rect x: 10, y: 10, width: 20, height: 20
7
+ circle x: 40, y: 40, radius: 25
8
+ triangle x1: 50, y1: 15, x2: 60, y2: 25, x3: 75, y3: 25
9
+ line x1: 100, y1: 620, x2: 720, y2: 620, stroke_width: 15.0
10
+ svg file: 'spanner.svg', x: 100, y: 20
11
+ png file: 'shiny-purse.png', x: 250, y: 20
12
+ save_png prefix: 'rasterized_'
13
+ save_pdf file: 'backend.pdf'
14
+ end
@@ -0,0 +1,4 @@
1
+ backend: svg
2
+ prefix: vector_backend_
3
+ count_format: '%02d'
4
+ dir: _output
@@ -17,7 +17,7 @@ module Squib
17
17
  # @option opts alpha [Decimal] (1.0) the alpha-transparency percentage used to blend this image. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
18
18
  # @option opts blend [:none, :multiply, :screen, :overlay, :darken, :lighten, :color_dodge, :color_burn, :hard_light, :soft_light, :difference, :exclusion, :hsl_hue, :hsl_saturation, :hsl_color, :hsl_luminosity] (:none) the composite blend operator used when applying this image. See Blend Modes at http://cairographics.org/operators. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
19
19
  # @option opts angle [FixNum] (0) Rotation of the in radians. Note that this rotates around the upper-left corner, making the placement of x-y coordinates slightly tricky. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
20
- # @option opts mask [String] (nil) If specified, the image will be used as a mask for the given color/gradient. Transparent pixels are ignored, opaque pixels are the given color.
20
+ # @option opts mask [String] (nil) If specified, the image will be used as a mask for the given color/gradient. Transparent pixels are ignored, opaque pixels are the given color. Note: the origin for gradient coordinates is at the given x,y, not at 0,0 as it is most other places.
21
21
  # @return [nil] Returns nil
22
22
  # @api public
23
23
  def png(opts = {})
@@ -52,7 +52,7 @@ module Squib
52
52
  # @option opts alpha [Decimal] (1.0) the alpha-transparency percentage used to blend this image. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
53
53
  # @option opts blend [:none, :multiply, :screen, :overlay, :darken, :lighten, :color_dodge, :color_burn, :hard_light, :soft_light, :difference, :exclusion, :hsl_hue, :hsl_saturation, :hsl_color, :hsl_luminosity] (:none) the composite blend operator used when applying this image. See Blend Modes at http://cairographics.org/operators. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
54
54
  # @option opts angle [FixNum] (0) Rotation of the in radians. Note that this rotates around the upper-left corner, making the placement of x-y coordinates slightly tricky. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
55
- # @option opts mask [String] (nil) If specified, the image will be used as a mask for the given color/gradient. Transparent pixels are ignored, opaque pixels are the given color.
55
+ # @option opts mask [String] (nil) If specified, the image will be used as a mask for the given color/gradient. Transparent pixels are ignored, opaque pixels are the given color. Note: the origin for gradient coordinates is at the given x,y, not at 0,0 as it is most other places.
56
56
  # @return [nil] Returns nil
57
57
  # @api public
58
58
  def svg(opts = {})
@@ -11,9 +11,9 @@ module Squib
11
11
  # @return self
12
12
  # @api public
13
13
  def save(opts = {})
14
- opts = needs(opts, [:range, :creatable_dir, :formats, :prefix, :rotate])
15
- save_png(opts) if opts[:format].include? :png
16
- save_pdf(opts) if opts[:format].include? :pdf
14
+ # opts = needs(opts, [:range, :creatable_dir, :formats, :prefix, :rotate])
15
+ save_png(opts) if Array(opts[:format]).include? :png
16
+ save_pdf(opts) if Array(opts[:format]).include? :pdf
17
17
  self
18
18
  end
19
19
 
@@ -9,7 +9,7 @@ module Squib
9
9
 
10
10
  # :nodoc:
11
11
  # @api private
12
- attr_reader :width, :height
12
+ attr_reader :width, :height, :backend, :svgfile
13
13
 
14
14
  # :nodoc:
15
15
  # @api private
@@ -17,12 +17,31 @@ module Squib
17
17
 
18
18
  # :nodoc:
19
19
  # @api private
20
- def initialize(deck, width, height)
21
- @deck=deck; @width=width; @height=height
22
- @cairo_surface = Cairo::ImageSurface.new(width,height)
20
+ def initialize(deck, width, height, backend=:memory, index=-1)
21
+ @deck = deck
22
+ @width = width
23
+ @height = height
24
+ @backend = backend
25
+ @svgfile = "#{deck.dir}/#{deck.prefix}#{deck.count_format % index}.svg"
26
+ @cairo_surface = make_surface(@svgfile, backend)
23
27
  @cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface))
28
+ @cairo_context.antialias = ANTIALIAS_OPTS[(@deck.antialias.downcase)] || 'subpixel'
24
29
  end
25
30
 
31
+ def make_surface(svgfile, backend)
32
+ case backend
33
+ when :memory
34
+ Cairo::ImageSurface.new(@width, @height)
35
+ when :svg
36
+ Dir.mkdir @deck.dir unless Dir.exists?(@deck.dir)
37
+ Cairo::SVGSurface.new(svgfile, @width, @height)
38
+ else
39
+ Squib.logger.fatal "Back end not recognized: '#{backend}'"
40
+ abort
41
+ end
42
+ end
43
+
44
+
26
45
  # A save/restore wrapper for using Cairo
27
46
  # :nodoc:
28
47
  # @api private
@@ -63,13 +63,23 @@ module Squib
63
63
  #
64
64
  # @api public
65
65
  CONFIG_DEFAULTS = {
66
+ 'antialias' => 'best',
67
+ 'backend' => 'memory',
68
+ 'count_format' => SYSTEM_DEFAULTS[:count_format],
66
69
  'custom_colors' => {},
67
- 'dpi' => 300,
68
- 'hint' => :none,
69
- 'progress_bar' => false,
70
- 'img_dir' => '.',
70
+ 'dir' => SYSTEM_DEFAULTS[:dir],
71
+ 'dpi' => 300,
72
+ 'hint' => :none,
73
+ 'img_dir' => '.',
74
+ 'progress_bar' => false,
71
75
  }
72
76
 
77
+ #Translate the hints to the methods.
78
+ ANTIALIAS_OPTS = {
79
+ 'best' => 'subpixel',
80
+ 'good' => 'gray',
81
+ 'fast' => 'gray',
82
+ }
73
83
  # These are parameters that are intended to be "expanded" across
74
84
  # range if they are singletons.
75
85
  #
@@ -30,12 +30,14 @@ module Squib
30
30
 
31
31
  # :nodoc:
32
32
  # @api private
33
- attr_reader :text_hint
33
+ attr_reader :text_hint, :antialias
34
34
 
35
35
  # :nodoc:
36
36
  # @api private
37
37
  attr_reader :layout, :config
38
38
 
39
+ attr_reader :dir, :prefix, :count_format
40
+
39
41
  # Squib's constructor that sets the immutable properties.
40
42
  #
41
43
  # This is the starting point for Squib. In providing a block to the constructor, you have access to all of Deck's instance methods.
@@ -56,18 +58,23 @@ module Squib
56
58
  # @param block [Block] the main body of the script.
57
59
  # @api public
58
60
  def initialize(width: 825, height: 1125, cards: 1, dpi: 300, config: 'config.yml', layout: nil, &block)
61
+ @antialias = CONFIG_DEFAULTS['antialias']
59
62
  @dpi = dpi
60
- @width = Args::UnitConversion.parse width, dpi
61
- @height = Args::UnitConversion.parse height, dpi
62
- @font = Squib::SYSTEM_DEFAULTS[:default_font]
63
+ @font = SYSTEM_DEFAULTS[:default_font]
63
64
  @cards = []
64
65
  @custom_colors = {}
65
66
  @img_dir = '.'
66
- @progress_bar = Squib::Progress.new(false)
67
+ @progress_bar = Progress.new(false)
67
68
  @text_hint = :off
68
- cards.times{ @cards << Squib::Card.new(self, @width, @height) }
69
+ @backend = :memory
70
+ @dir = SYSTEM_DEFAULTS[:dir]
71
+ @prefix = SYSTEM_DEFAULTS[:prefix]
72
+ @count_format = SYSTEM_DEFAULTS[:count_format]
69
73
  show_info(config, layout)
70
74
  load_config(config)
75
+ @width = Args::UnitConversion.parse width, dpi
76
+ @height = Args::UnitConversion.parse height, dpi
77
+ cards.times{ |i| @cards << Squib::Card.new(self, @width, @height, @backend, i) }
71
78
  @layout = LayoutParser.load_layout(layout)
72
79
  if block_given?
73
80
  instance_eval(&block) # here we go. wheeeee!
@@ -93,12 +100,17 @@ module Squib
93
100
  def load_config(file)
94
101
  if File.exists?(file) && config = YAML.load_file(file)
95
102
  Squib::logger.info { " using config: #{file}" }
96
- config = Squib::CONFIG_DEFAULTS.merge(config)
97
- @dpi = config['dpi'].to_i
98
- @text_hint = config['text_hint']
103
+ config = CONFIG_DEFAULTS.merge(config)
104
+ @dpi = config['dpi'].to_i
105
+ @text_hint = config['text_hint']
99
106
  @progress_bar.enabled = config['progress_bars']
100
- @custom_colors = config['custom_colors']
101
- @img_dir = config['img_dir']
107
+ @custom_colors = config['custom_colors']
108
+ @img_dir = config['img_dir']
109
+ @backend = (config['backend'].to_s.downcase.strip == 'svg') ? :svg : :memory
110
+ @dir = config['dir']
111
+ @prefix = config['prefix']
112
+ @count_format = config['count_format']
113
+ @antialias = config['antialias']
102
114
  end
103
115
  end
104
116
 
@@ -108,6 +120,7 @@ module Squib
108
120
  def show_info(config, layout)
109
121
  Squib::logger.info "Squib v#{Squib::VERSION}"
110
122
  Squib::logger.info " building #{@cards.size} #{@width}x#{@height} cards"
123
+ Squib::logger.info " using #{@backend}"
111
124
  end
112
125
 
113
126
  ##################
@@ -15,7 +15,8 @@ module Squib
15
15
  :translate, :rotate, :move_to, :update_pango_layout, :width, :height,
16
16
  :show_pango_layout, :rounded_rectangle, :set_line_width, :stroke, :fill,
17
17
  :set_source, :scale, :render_rsvg_handle, :circle, :triangle, :line_to,
18
- :operator=, :show_page, :clip, :transform, :mask, :create_pango_layout
18
+ :operator=, :show_page, :clip, :transform, :mask, :create_pango_layout,
19
+ :antialias=
19
20
 
20
21
  def set_source_squibcolor(arg)
21
22
  if match = arg.match(LINEAR_GRADIENT)
@@ -27,7 +27,7 @@ module Squib
27
27
  cc.scale(width.to_f / png.width.to_f, height.to_f / png.height.to_f)
28
28
  end
29
29
  cc.rotate(angle)
30
- cc.translate(-1 * x, -1 * y)
30
+ cc.translate(-x, -y)
31
31
  cc.set_source(png, x, y)
32
32
  cc.operator = blend unless blend == :none
33
33
  if mask.nil?
@@ -44,24 +44,25 @@ module Squib
44
44
  def svg(file, id, x, y, width, height, alpha, blend, angle, mask)
45
45
  Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
46
46
  return if file.nil? or file.eql? ''
47
- svg = RSVG::Handle.new_from_file(file)
48
- width = svg.width if width == :native
49
- height = svg.height if height == :native
50
- tmp = Cairo::ImageSurface.new(width, height)
51
- tmp_cc = Cairo::Context.new(tmp)
52
- tmp_cc.scale(width.to_f / svg.width.to_f, height.to_f / svg.height.to_f)
53
- tmp_cc.render_rsvg_handle(svg, id)
47
+ svg = RSVG::Handle.new_from_file(file)
48
+ width = svg.width if width == :native
49
+ height = svg.height if height == :native
50
+ scale_width = width.to_f / svg.width.to_f
51
+ scale_height = height.to_f / svg.height.to_f
54
52
  use_cairo do |cc|
55
53
  cc.translate(x, y)
56
54
  cc.rotate(angle)
57
- cc.translate(-1 * x, -1 * y)
55
+ cc.scale(scale_width, scale_height)
58
56
  cc.operator = blend unless blend == :none
57
+ #FIXME Alpha is no longer used since we are not using cc.paint anymore
59
58
  if mask.nil?
60
- cc.set_source(tmp, x, y)
61
- cc.paint(alpha)
59
+ cc.render_rsvg_handle(svg, id)
62
60
  else
61
+ tmp = Cairo::ImageSurface.new(width / scale_width, height / scale_height)
62
+ tmp_cc = Cairo::Context.new(tmp)
63
+ tmp_cc.render_rsvg_handle(svg, id)
63
64
  cc.set_source_squibcolor(mask)
64
- cc.mask(tmp, x, y)
65
+ cc.mask(tmp, 0, 0)
65
66
  end
66
67
  end
67
68
  end
@@ -16,25 +16,45 @@ module Squib
16
16
  # @return [nil]
17
17
  # @api public
18
18
  def save_pdf(opts = {})
19
- opts = {width: 3300, height: 2550}.merge(opts)
20
- p = needs(opts, [:range, :paper_width, :paper_height, :file_to_save, :creatable_dir, :margin, :gap, :trim])
21
- cc = Cairo::Context.new(Cairo::PDFSurface.new("#{p[:dir]}/#{p[:file]}", p[:width], p[:height]))
22
- x = p[:margin]
23
- y = p[:margin]
24
- @progress_bar.start("Saving PDF to #{p[:dir]}/#{p[:file]}", p[:range].size) do |bar|
19
+ opts = {width: 3300, height: 2550}.merge(opts)
20
+ p = needs(opts, [:range, :paper_width, :paper_height, :file_to_save,
21
+ :creatable_dir, :margin, :gap, :trim])
22
+ paper_width = p[:width]
23
+ paper_height = p[:height]
24
+ file = "#{p[:dir]}/#{p[:file]}"
25
+ cc = Cairo::Context.new(Cairo::PDFSurface.new(file, paper_width, paper_height))
26
+ x, y = p[:margin], p[:margin]
27
+ card_width = @width - 2 * p[:trim]
28
+ card_height = @height - 2 * p[:trim]
29
+ @progress_bar.start("Saving PDF to #{file}", p[:range].size) do |bar|
25
30
  p[:range].each do |i|
26
- surface = trim(@cards[i].cairo_surface, p[:trim], @width, @height)
27
- cc.set_source(surface, x, y)
28
- cc.paint
31
+ card = @cards[i]
32
+ cc.translate(x,y)
33
+ cc.rectangle(p[:trim], p[:trim], card_width, card_height)
34
+ cc.clip
35
+ case card.backend
36
+ when :memory
37
+ cc.set_source(card.cairo_surface, 0, 0)
38
+ cc.paint
39
+ when :svg
40
+ card.cairo_surface.finish
41
+ cc.save
42
+ cc.scale(0.8,0.8) # I really don't know why I needed to do this at all. But 0.8 is the magic number to get this to scale right
43
+ cc.render_rsvg_handle(RSVG::Handle.new_from_file(card.svgfile), nil)
44
+ cc.restore
45
+ else
46
+ abort "No such back end supported for save_pdf: #{backend}"
47
+ end
29
48
  bar.increment
30
- x += surface.width + p[:gap]
31
- if x > (p[:width] - surface.width - p[:margin])
49
+ cc.reset_clip
50
+ cc.translate(-x,-y)
51
+ x += card.width + p[:gap] - 2*p[:trim]
52
+ if x > (paper_width - card_width - p[:margin])
32
53
  x = p[:margin]
33
- y += surface.height + p[:gap]
34
- if y > (p[:height] - surface.height - p[:margin])
35
- x = p[:margin]
36
- y = p[:margin]
37
- cc.show_page #next page
54
+ y += card.height + p[:gap] - 2*p[:trim]
55
+ if y > (paper_height - card_height - p[:margin])
56
+ cc.show_page # next page
57
+ x,y = p[:margin],p[:margin]
38
58
  end
39
59
  end
40
60
  end
@@ -44,7 +64,7 @@ module Squib
44
64
  # Lays out the cards in range and renders a stitched PNG sheet
45
65
  #
46
66
  # @example
47
- # save_png_sheet prefix: 'sheet_', margin: 75, gap: 5, trim: 37
67
+ # save_sheet prefix: 'sheet_', margin: 75, gap: 5, trim: 37
48
68
  #
49
69
  # @option opts [Enumerable] range (:all) the range of cards over which this will be rendered. See {file:README.md#Specifying_Ranges Specifying Ranges}
50
70
  # @option opts colulmns [Integer] (1) the number of columns in the grid
@@ -81,7 +101,6 @@ module Squib
81
101
  surface = trim(@cards[i].cairo_surface, p[:trim], @width, @height)
82
102
  cc.set_source(surface, x, y)
83
103
  cc.paint
84
- bar.increment
85
104
  num_this_sheet += 1
86
105
  x += surface.width + p[:gap]
87
106
  if num_this_sheet % p[:columns] == 0 # new row