squib_plus 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/lib/squib_plus/card_size.rb +35 -0
- data/lib/squib_plus/deck/avatar.rb +89 -0
- data/lib/squib_plus/deck.rb +50 -0
- data/lib/squib_plus/google_sheets.rb +37 -0
- data/lib/squib_plus/pdf.rb +50 -0
- data/lib/squib_plus/sprues/letter_poker_tight.yml +42 -0
- data/lib/squib_plus/sprues/letter_poker_tight_reverse.yml +42 -0
- data/lib/squib_plus/string.rb +5 -0
- data/lib/squib_plus/subset.rb +21 -0
- data/lib/squib_plus.rb +6 -0
- data/squib_plus.gemspec +14 -0
- metadata +55 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 19f2f11b51db065d28f3be11cbc30293202dac9c1e9bee8b77408ab9b07d255b
|
|
4
|
+
data.tar.gz: 6ee1c5d23a051ff4341ddd6957e4e80f000c1ac94909e7beb3a1cfba3babce9f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ff9ebe8819ff49e08f99dd6d6054e86f62297748341280c4ca291f84fde4a772cff2bd42f222f8fd0513d0c9b337c86740e6f8cb5624466c44a296292d54302f
|
|
7
|
+
data.tar.gz: 4b2d3ab40628d117c1c138f04d5170efb5d82fa32e43ca1479d8da48672a7018629aa64ecfc7df7ce91dd2f6973969f6afc18c6a619f2531bec01fb35743db7e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Squib
|
|
4
|
+
# Default card sizes for use like
|
|
5
|
+
# Squib::Deck.new(**Squib.card_size(:bridge))
|
|
6
|
+
#
|
|
7
|
+
# Note that each of these has 0.25in extra compared
|
|
8
|
+
# to the real-world dimensions of the card in order
|
|
9
|
+
# to account for bleed.
|
|
10
|
+
@@card_sizes = {
|
|
11
|
+
bridge: { width: '2.5in', height: '3.75in' },
|
|
12
|
+
business: { width: '2.25in', height: '3.75in' },
|
|
13
|
+
divider: { width: '3.25in', height: '3.75in' },
|
|
14
|
+
domino: { width: '2in', height: '3.75in' },
|
|
15
|
+
euro_poker: { width: '2.73in', height: '3.71in' },
|
|
16
|
+
euro_square: { width: '3.01in', height: '3.01in' },
|
|
17
|
+
jumbo: { width: '3.75in', height: '5.75in' },
|
|
18
|
+
micro: { width: '1.5in', height: '2in' },
|
|
19
|
+
mini: { width: '2in', height: '2.75in' },
|
|
20
|
+
poker: { width: '2.75in', height: '3.75in' },
|
|
21
|
+
mint_tin: { width: '2.3in', height: '3.71in' },
|
|
22
|
+
small_square: { width: '2.75in', height: '2.75in' },
|
|
23
|
+
square: { width: '3.75in', height: '3.75in' },
|
|
24
|
+
tarot: { width: '2.75in', height: '5in' },
|
|
25
|
+
us_game: { width: '2.45in', height: '3.68in' }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def card_size(name, landscape: false)
|
|
29
|
+
dimensions = @@card_sizes.fetch name
|
|
30
|
+
dimensions = { width: dimensions[:height], height: dimensions[:width] } if landscape
|
|
31
|
+
|
|
32
|
+
{ dpi: 300 }.merge dimensions
|
|
33
|
+
end
|
|
34
|
+
module_function :card_size
|
|
35
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'mechanize'
|
|
5
|
+
|
|
6
|
+
module Squib
|
|
7
|
+
class Deck
|
|
8
|
+
def avatar(opts = {})
|
|
9
|
+
DSL::Avatar.new(self, __callee__).run(opts)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module DSL
|
|
14
|
+
# Add an avatar for placeholder art in your games
|
|
15
|
+
# using https://avatars.dicebear.com/. The image will
|
|
16
|
+
# be downloaded to your configured image directory if
|
|
17
|
+
# it doesn't already exist.
|
|
18
|
+
#
|
|
19
|
+
# Library can be male, female, human, identicon, initials,
|
|
20
|
+
# bottts, avataaars, jdenticon, gridy or micah.
|
|
21
|
+
#
|
|
22
|
+
# Seed can be any random string
|
|
23
|
+
#
|
|
24
|
+
# Example:
|
|
25
|
+
# avatar library: 'micah', seed: '1234'
|
|
26
|
+
class Avatar
|
|
27
|
+
attr_reader :dsl_method, :deck
|
|
28
|
+
|
|
29
|
+
def initialize(deck, dsl_method)
|
|
30
|
+
@deck = deck
|
|
31
|
+
@dsl_method = dsl_method
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.accepted_params
|
|
35
|
+
%i[
|
|
36
|
+
library seed
|
|
37
|
+
x y width height
|
|
38
|
+
blend mask
|
|
39
|
+
crop_x crop_y crop_width crop_height
|
|
40
|
+
crop_corner_radius crop_corner_x_radius crop_corner_y_radius
|
|
41
|
+
flip_horizontal flip_vertical angle
|
|
42
|
+
id force_id data
|
|
43
|
+
range layout
|
|
44
|
+
placeholder
|
|
45
|
+
]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def run(opts)
|
|
49
|
+
Dir.chdir(deck.img_dir) do
|
|
50
|
+
defaults = { library: 'avataaars' }
|
|
51
|
+
options = defaults.merge opts
|
|
52
|
+
|
|
53
|
+
# Extract the default svg options
|
|
54
|
+
range = Args.extract_range opts, deck
|
|
55
|
+
svg_args = Args.extract_svg_special opts, deck
|
|
56
|
+
paint = Args.extract_paint opts, deck
|
|
57
|
+
box = Args.extract_scale_box opts, deck
|
|
58
|
+
trans = Args.extract_transform opts, deck
|
|
59
|
+
|
|
60
|
+
deck.progress_bar.start('Loading Avatar(s)', range.size) do |bar|
|
|
61
|
+
range.each do |i|
|
|
62
|
+
library = options[:library]
|
|
63
|
+
seed = options[:seed][i]
|
|
64
|
+
next if seed.nil?
|
|
65
|
+
|
|
66
|
+
file = "avatar-#{library}-#{seed}.svg"
|
|
67
|
+
|
|
68
|
+
# Check if we need to download the image
|
|
69
|
+
unless File.exist?(file)
|
|
70
|
+
agent = Mechanize.new
|
|
71
|
+
agent.follow_meta_refresh = true
|
|
72
|
+
agent.keep_alive = false
|
|
73
|
+
agent.history.max_size = 10
|
|
74
|
+
|
|
75
|
+
response = agent.get_file("https://avatars.dicebear.com/api/#{library}/#{seed}.svg")
|
|
76
|
+
response = response.encode('ascii-8bit').force_encoding('utf-8')
|
|
77
|
+
|
|
78
|
+
File.open(file, 'w') { |f| f.write(response) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
deck.cards[i].svg(file, svg_args[i], box[i], paint[i], trans[i])
|
|
82
|
+
bar.increment
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Squib
|
|
6
|
+
class Deck
|
|
7
|
+
# Adds a card number to the bottom right of the card by default.
|
|
8
|
+
# Helps with tracking down a specific card in your spreadsheet in
|
|
9
|
+
# games with many unique cards.
|
|
10
|
+
def card_num(opts = {})
|
|
11
|
+
defaults = {
|
|
12
|
+
x: width - 100,
|
|
13
|
+
y: height - 100,
|
|
14
|
+
width: 50,
|
|
15
|
+
height: 50,
|
|
16
|
+
font: 'sans-serif',
|
|
17
|
+
font_size: 9,
|
|
18
|
+
color: '#000',
|
|
19
|
+
align: :center,
|
|
20
|
+
valign: :middle
|
|
21
|
+
}
|
|
22
|
+
range = Args.extract_range opts, self
|
|
23
|
+
|
|
24
|
+
# Index from 1
|
|
25
|
+
text(str: range.map { |i| i + 1 }, **(defaults.merge opts))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Shortcut for save_sheet with defaults that make sense for Tabletop Simulator
|
|
29
|
+
def tts(opts = {})
|
|
30
|
+
defaults = { columns: 10, rows: 7, trim: 37.5 }
|
|
31
|
+
save_sheet(defaults.merge(opts))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Shortcut for creating a Print-n-Play sheet using poker-sized cards
|
|
35
|
+
def pnp(opts = {})
|
|
36
|
+
cut_zone stroke_color: '#888'
|
|
37
|
+
defaults = {
|
|
38
|
+
trim: 37.5,
|
|
39
|
+
sprue: if opts[:rtl]
|
|
40
|
+
"#{File.dirname(__FILE__)}/sprues/letter_poker_tight_reverse.yml"
|
|
41
|
+
else
|
|
42
|
+
"#{File.dirname(__FILE__)}/sprues/letter_poker_tight.yml"
|
|
43
|
+
end
|
|
44
|
+
}
|
|
45
|
+
save_pdf(defaults.merge(opts))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
require_relative 'deck/avatar'
|
|
50
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mechanize'
|
|
4
|
+
|
|
5
|
+
module Squib
|
|
6
|
+
# gsheet allows you to load Deck data using Google Sheets.
|
|
7
|
+
#
|
|
8
|
+
# First, share your sheet using File > Publish to the web
|
|
9
|
+
# Select 'Entire document' and '.csv' as the format
|
|
10
|
+
# Copy the URL like
|
|
11
|
+
# https://docs.google.com/spreadsheets/d/e/2PACX-.../pub?output=csv
|
|
12
|
+
# ^^^^^^^^^
|
|
13
|
+
# The code you need to pass is the long ID after /d/e.
|
|
14
|
+
#
|
|
15
|
+
# If the sheet has multiple tabs, you can specify a later tab using the gid from
|
|
16
|
+
# the non-share URL like
|
|
17
|
+
# https://docs.google.com/spreadsheets/d/.../edit#gid=123456
|
|
18
|
+
# ^^^^^^
|
|
19
|
+
# The gid is at the end of the URL after #gid=
|
|
20
|
+
#
|
|
21
|
+
# Example:
|
|
22
|
+
# deck = Squib.gsheet('2PACX-...', 123456)
|
|
23
|
+
#
|
|
24
|
+
# Squib::Deck.new(cards: deck['name'].size) do
|
|
25
|
+
# ...
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# You can optionally specify the tab to use with the second argument
|
|
29
|
+
def gsheet(sheet_id, index = 0)
|
|
30
|
+
agent = Mechanize.new
|
|
31
|
+
response = agent.get_file("https://docs.google.com/spreadsheets/d/e/#{sheet_id}/pub?gid=#{index}&output=csv")
|
|
32
|
+
response = response.encode('ascii-8bit').force_encoding('utf-8')
|
|
33
|
+
|
|
34
|
+
Squib.csv data: response
|
|
35
|
+
end
|
|
36
|
+
module_function :gsheet
|
|
37
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'combine_pdf'
|
|
4
|
+
|
|
5
|
+
module Squib
|
|
6
|
+
# Merge PDFs together, collated for front/back printing
|
|
7
|
+
# Example:
|
|
8
|
+
# # Deck front
|
|
9
|
+
# Squib::Deck.new(cards: deck['name'].size) do
|
|
10
|
+
# ...
|
|
11
|
+
|
|
12
|
+
# pnp file: 'front.pdf'
|
|
13
|
+
# end
|
|
14
|
+
|
|
15
|
+
# # Deck back
|
|
16
|
+
# Squib::Deck.new(cards: deck['name'].size) do
|
|
17
|
+
# ...
|
|
18
|
+
|
|
19
|
+
# pnp file: 'back.pdf'
|
|
20
|
+
# end
|
|
21
|
+
|
|
22
|
+
# # Combine PnP
|
|
23
|
+
# Squib.combine_pdfs([
|
|
24
|
+
# { :front => "_output/front.pdf", :back => "_output/back.pdf" },
|
|
25
|
+
# ], "_output/pnp.pdf")
|
|
26
|
+
def combine_pdfs(sheets, file)
|
|
27
|
+
pdf = CombinePDF.new
|
|
28
|
+
|
|
29
|
+
sheets.each do |sheet|
|
|
30
|
+
front = CombinePDF.load(sheet[:front])
|
|
31
|
+
|
|
32
|
+
# Collate the back pages immediately after the relevant page,
|
|
33
|
+
# if a back-side is provided.
|
|
34
|
+
back = CombinePDF.load(sheet[:back]) if sheet.key?(:back)
|
|
35
|
+
|
|
36
|
+
i = 0
|
|
37
|
+
front.pages.each do |page|
|
|
38
|
+
pdf << page
|
|
39
|
+
pdf << back.pages[i] if sheet.key?(:back)
|
|
40
|
+
|
|
41
|
+
break if i == front.pages.count - 1
|
|
42
|
+
|
|
43
|
+
i += 1
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
pdf.save file
|
|
48
|
+
end
|
|
49
|
+
module_function :combine_pdfs
|
|
50
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
sheet_width: 8.5in
|
|
3
|
+
sheet_height: 11in
|
|
4
|
+
card_width: 2.5in
|
|
5
|
+
card_height: 3.5in
|
|
6
|
+
cards:
|
|
7
|
+
- x: 0.5in
|
|
8
|
+
y: 0.225in
|
|
9
|
+
- x: 3in
|
|
10
|
+
y: 0.225in
|
|
11
|
+
- x: 5.5in
|
|
12
|
+
y: 0.225in
|
|
13
|
+
- x: 0.5in
|
|
14
|
+
y: 3.725in
|
|
15
|
+
- x: 3in
|
|
16
|
+
y: 3.725in
|
|
17
|
+
- x: 5.5in
|
|
18
|
+
y: 3.725in
|
|
19
|
+
- x: 0.5in
|
|
20
|
+
y: 7.225in
|
|
21
|
+
- x: 3in
|
|
22
|
+
y: 7.225in
|
|
23
|
+
- x: 5.5in
|
|
24
|
+
y: 7.225in
|
|
25
|
+
crop_line:
|
|
26
|
+
lines:
|
|
27
|
+
- type: :vertical
|
|
28
|
+
position: 0.5in
|
|
29
|
+
- type: :vertical
|
|
30
|
+
position: 3in
|
|
31
|
+
- type: :vertical
|
|
32
|
+
position: 5.5in
|
|
33
|
+
- type: :vertical
|
|
34
|
+
position: 8in
|
|
35
|
+
- type: :horizontal
|
|
36
|
+
position: 0.225in
|
|
37
|
+
- type: :horizontal
|
|
38
|
+
position: 3.725in
|
|
39
|
+
- type: :horizontal
|
|
40
|
+
position: 7.225in
|
|
41
|
+
- type: :horizontal
|
|
42
|
+
position: 10.725in
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
sheet_width: 8.5in
|
|
3
|
+
sheet_height: 11in
|
|
4
|
+
card_width: 2.5in
|
|
5
|
+
card_height: 3.5in
|
|
6
|
+
cards:
|
|
7
|
+
- x: 5.5in
|
|
8
|
+
y: 0.225in
|
|
9
|
+
- x: 3in
|
|
10
|
+
y: 0.225in
|
|
11
|
+
- x: 0.5in
|
|
12
|
+
y: 0.225in
|
|
13
|
+
- x: 5.5in
|
|
14
|
+
y: 3.725in
|
|
15
|
+
- x: 3in
|
|
16
|
+
y: 3.725in
|
|
17
|
+
- x: 0.5in
|
|
18
|
+
y: 3.725in
|
|
19
|
+
- x: 5.5in
|
|
20
|
+
y: 7.225in
|
|
21
|
+
- x: 3in
|
|
22
|
+
y: 7.225in
|
|
23
|
+
- x: 0.5in
|
|
24
|
+
y: 7.225in
|
|
25
|
+
crop_line:
|
|
26
|
+
lines:
|
|
27
|
+
- type: :vertical
|
|
28
|
+
position: 0.5in
|
|
29
|
+
- type: :vertical
|
|
30
|
+
position: 3in
|
|
31
|
+
- type: :vertical
|
|
32
|
+
position: 5.5in
|
|
33
|
+
- type: :vertical
|
|
34
|
+
position: 8in
|
|
35
|
+
- type: :horizontal
|
|
36
|
+
position: 0.225in
|
|
37
|
+
- type: :horizontal
|
|
38
|
+
position: 3.725in
|
|
39
|
+
- type: :horizontal
|
|
40
|
+
position: 7.225in
|
|
41
|
+
- type: :horizontal
|
|
42
|
+
position: 10.725in
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Squib
|
|
4
|
+
# Subset a selection of cards based on some criteria. Can be passed
|
|
5
|
+
# directly into the range argument of other DSL functions.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# gems = Squib.subset(deck['category'], -> (c) { c === 'gem' })
|
|
9
|
+
# text range: gems, str: deck['gem_num'], layout: 'gem_label'
|
|
10
|
+
#
|
|
11
|
+
# You can invert the match using matching: false
|
|
12
|
+
# This allows you to use a single criteria function for positive/negative
|
|
13
|
+
# matches.
|
|
14
|
+
def subset(column, criteria, matching: true)
|
|
15
|
+
sub = {}
|
|
16
|
+
column.each_with_index { |c, i| (sub[criteria.call(c)] ||= []) << i }
|
|
17
|
+
|
|
18
|
+
sub[matching]
|
|
19
|
+
end
|
|
20
|
+
module_function :subset
|
|
21
|
+
end
|
data/lib/squib_plus.rb
ADDED
data/squib_plus.gemspec
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'squib_plus'
|
|
5
|
+
s.version = '0.0.1'
|
|
6
|
+
s.files = `git ls-files -z`.split("\x0")
|
|
7
|
+
|
|
8
|
+
s.authors = ['Rob Newton']
|
|
9
|
+
s.summary = 'Extensions for Squib'
|
|
10
|
+
s.description = 'Helper functions extending the functionality of Squib'
|
|
11
|
+
s.email = 'robert@coinflip.games'
|
|
12
|
+
s.homepage = 'https://rubygems.org/gems/squib_plus'
|
|
13
|
+
s.license = 'MIT'
|
|
14
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: squib_plus
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rob Newton
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-11-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Helper functions extending the functionality of Squib
|
|
14
|
+
email: robert@coinflip.games
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- ".gitignore"
|
|
20
|
+
- Gemfile
|
|
21
|
+
- lib/squib_plus.rb
|
|
22
|
+
- lib/squib_plus/card_size.rb
|
|
23
|
+
- lib/squib_plus/deck.rb
|
|
24
|
+
- lib/squib_plus/deck/avatar.rb
|
|
25
|
+
- lib/squib_plus/google_sheets.rb
|
|
26
|
+
- lib/squib_plus/pdf.rb
|
|
27
|
+
- lib/squib_plus/sprues/letter_poker_tight.yml
|
|
28
|
+
- lib/squib_plus/sprues/letter_poker_tight_reverse.yml
|
|
29
|
+
- lib/squib_plus/string.rb
|
|
30
|
+
- lib/squib_plus/subset.rb
|
|
31
|
+
- squib_plus.gemspec
|
|
32
|
+
homepage: https://rubygems.org/gems/squib_plus
|
|
33
|
+
licenses:
|
|
34
|
+
- MIT
|
|
35
|
+
metadata: {}
|
|
36
|
+
post_install_message:
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '0'
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0'
|
|
50
|
+
requirements: []
|
|
51
|
+
rubygems_version: 3.2.22
|
|
52
|
+
signing_key:
|
|
53
|
+
specification_version: 4
|
|
54
|
+
summary: Extensions for Squib
|
|
55
|
+
test_files: []
|