squib 0.6.0 → 0.7.0
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 +4 -4
- data/CHANGELOG.md +162 -133
- data/Gemfile +4 -4
- data/README.md +630 -550
- data/RELEASE TODO.md +18 -18
- data/Rakefile +99 -99
- data/lib/squib.rb +32 -32
- data/lib/squib/api/background.rb +20 -19
- data/lib/squib/api/data.rb +100 -99
- data/lib/squib/api/image.rb +90 -76
- data/lib/squib/api/save.rb +149 -103
- data/lib/squib/api/settings.rb +35 -37
- data/lib/squib/api/shapes.rb +230 -228
- data/lib/squib/api/text.rb +65 -66
- data/lib/squib/api/text_embed.rb +96 -66
- data/lib/squib/args/arg_loader.rb +138 -0
- data/lib/squib/args/box.rb +55 -0
- data/lib/squib/args/card_range.rb +32 -0
- data/lib/squib/args/color_validator.rb +12 -0
- data/lib/squib/args/coords.rb +33 -0
- data/lib/squib/args/dir_validator.rb +16 -0
- data/lib/squib/args/draw.rb +92 -0
- data/lib/squib/args/embed_adjust.rb +25 -0
- data/lib/squib/args/embed_key.rb +17 -0
- data/lib/squib/args/hand_special.rb +37 -0
- data/lib/squib/args/input_file.rb +37 -0
- data/lib/squib/args/paint.rb +44 -0
- data/lib/squib/args/paragraph.rb +115 -0
- data/lib/squib/args/save_batch.rb +60 -0
- data/lib/squib/args/scale_box.rb +53 -0
- data/lib/squib/args/sheet.rb +72 -0
- data/lib/squib/args/showcase_special.rb +38 -0
- data/lib/squib/args/svg_special.rb +37 -0
- data/lib/squib/args/transform.rb +25 -0
- data/lib/squib/args/typographer.rb +117 -117
- data/lib/squib/card.rb +67 -67
- data/lib/squib/conf.rb +117 -111
- data/lib/squib/constants.rb +178 -178
- data/lib/squib/deck.rb +113 -111
- data/lib/squib/graphics/cairo_context_wrapper.rb +99 -53
- data/lib/squib/graphics/gradient_regex.rb +46 -46
- data/lib/squib/graphics/hand.rb +42 -43
- data/lib/squib/graphics/image.rb +76 -73
- data/lib/squib/graphics/save_doc.rb +103 -137
- data/lib/squib/graphics/save_images.rb +33 -33
- data/lib/squib/graphics/shapes.rb +119 -152
- data/lib/squib/graphics/showcase.rb +85 -88
- data/lib/squib/graphics/text.rb +176 -216
- data/lib/squib/layout_parser.rb +91 -89
- data/lib/squib/layouts/economy.yml +85 -0
- data/lib/squib/layouts/fantasy.yml +101 -0
- data/lib/squib/layouts/hand.yml +62 -46
- data/lib/squib/layouts/playing-card.yml +35 -18
- data/lib/squib/project_template/config.yml +45 -40
- data/lib/squib/version.rb +10 -10
- data/samples/color_shortcuts.rb +6 -0
- data/samples/csv_import.rb +18 -18
- data/samples/custom-config.yml +5 -5
- data/samples/custom_config.rb +18 -18
- data/samples/draw_shapes.rb +45 -35
- data/samples/embed_text.rb +88 -90
- data/samples/hand.rb +24 -24
- data/samples/layouts.rb +62 -61
- data/samples/layouts_builtin.rb +51 -0
- data/samples/load_images.rb +78 -64
- data/samples/ranges.rb +64 -53
- data/samples/sample.csv +2 -2
- data/samples/text_options.rb +102 -94
- data/spec/api/api_data_spec.rb +57 -50
- data/spec/api/api_settings_spec.rb +37 -17
- data/spec/args/box_spec.rb +127 -0
- data/spec/args/draw_spec.rb +95 -0
- data/spec/args/embed_key_spec.rb +13 -0
- data/spec/args/input_file_spec.rb +21 -0
- data/spec/args/paint_spec.rb +22 -0
- data/spec/args/paragraph_spec.rb +153 -0
- data/spec/args/range_spec.rb +36 -0
- data/spec/args/save_batch_spec.rb +51 -0
- data/spec/args/scale_box_spec.rb +71 -0
- data/spec/args/sheet_spec.rb +58 -0
- data/spec/args/showcase_special_spec.rb +15 -0
- data/spec/data/samples/autoscale_font.rb.txt +84 -87
- data/spec/data/samples/basic.rb.txt +209 -203
- data/spec/data/samples/cairo_access.rb.txt +2 -2
- data/spec/data/samples/config_text_markup.rb.txt +72 -75
- data/spec/data/samples/csv_import.rb.txt +76 -80
- data/spec/data/samples/custom_config.rb.txt +48 -49
- data/spec/data/samples/draw_shapes.rb.txt +100 -42
- data/spec/data/samples/embed_text.rb.txt +283 -295
- data/spec/data/samples/excel.rb.txt +162 -171
- data/spec/data/samples/gradients.rb.txt +79 -67
- data/spec/data/samples/hand.rb.txt +538 -514
- data/spec/data/samples/hello_world.rb.txt +36 -38
- data/spec/data/samples/load_images.rb.txt +41 -5
- data/spec/data/samples/portrait-landscape.rb.txt +49 -51
- data/spec/data/samples/ranges.rb.txt +460 -429
- data/spec/data/samples/saves.rb.txt +801 -785
- data/spec/data/samples/showcase.rb.txt +5910 -5906
- data/spec/data/samples/text_options.rb.txt +1125 -981
- data/spec/data/samples/tgc_proofs.rb.txt +81 -79
- data/spec/data/samples/units.rb.txt +18 -12
- data/spec/data/xlsx/with_macros.xlsm +0 -0
- data/spec/graphics/cairo_context_wrapper_spec.rb +84 -75
- data/spec/graphics/graphics_images_spec.rb +94 -85
- data/spec/graphics/graphics_save_doc_spec.rb +67 -65
- data/spec/samples/expected/hand.png +0 -0
- data/spec/samples/expected/hand_pretty.png +0 -0
- data/spec/samples/expected/layout_00.png +0 -0
- data/spec/samples/expected/load_images_00.png +0 -0
- data/spec/samples/expected/ranges_00.png +0 -0
- data/spec/samples/expected/shape_00.png +0 -0
- data/spec/samples/expected/showcase.png +0 -0
- data/spec/samples/expected/showcase2.png +0 -0
- data/spec/samples/expected/showcase_individual_00.png +0 -0
- data/spec/samples/expected/showcase_individual_01.png +0 -0
- data/spec/samples/expected/showcase_individual_02.png +0 -0
- data/spec/samples/expected/showcase_individual_03.png +0 -0
- data/spec/samples/expected/text_00.png +0 -0
- data/spec/samples/expected/text_01.png +0 -0
- data/spec/samples/expected/text_02.png +0 -0
- data/spec/samples/samples_regression_spec.rb +82 -82
- data/spec/spec_helper.rb +3 -2
- data/squib.gemspec +48 -48
- data/squib.sublime-project +42 -36
- metadata +61 -33
- data/lib/squib/input_helpers.rb +0 -238
- data/spec/api/api_image_spec.rb +0 -38
- data/spec/api/api_text_spec.rb +0 -37
- data/spec/graphics/graphics_shapes_spec.rb +0 -85
- data/spec/graphics/graphics_text_spec.rb +0 -164
- data/spec/input_helpers_spec.rb +0 -238
- data/spec/samples/expected/embed_multi_00.png +0 -0
- data/spec/samples/expected/embed_multi_01.png +0 -0
- data/spec/samples/expected/embed_multi_02.png +0 -0
- data/spec/samples/expected/ranges_01.png +0 -0
- data/spec/samples/expected/ranges_02.png +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'squib/args/arg_loader'
|
|
2
|
+
require 'squib/args/dir_validator'
|
|
3
|
+
|
|
4
|
+
module Squib
|
|
5
|
+
# @api private
|
|
6
|
+
module Args
|
|
7
|
+
class SaveBatch
|
|
8
|
+
include ArgLoader
|
|
9
|
+
include DirValidator
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.parameters
|
|
15
|
+
{ dir: '_output',
|
|
16
|
+
prefix: 'card_',
|
|
17
|
+
count_format: '%02d',
|
|
18
|
+
rotate: false,
|
|
19
|
+
angle: 0,
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.expanding_parameters
|
|
24
|
+
self.parameters.keys # all of them
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.params_with_units
|
|
28
|
+
[] # none of them
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def validate_dir(arg, _i)
|
|
32
|
+
ensure_dir_created(arg)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def validate_rotate(arg, i)
|
|
36
|
+
case arg
|
|
37
|
+
when true, :clockwise
|
|
38
|
+
angle[i] = 0.5 * Math::PI
|
|
39
|
+
return true
|
|
40
|
+
when :counterclockwise
|
|
41
|
+
angle[i] = 1.5 * Math::PI
|
|
42
|
+
return true
|
|
43
|
+
when false
|
|
44
|
+
false
|
|
45
|
+
else
|
|
46
|
+
raise 'invalid option to rotate: only [true, false, :clockwise, :counterclockwise]'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def full_filename(i)
|
|
51
|
+
"#{dir[i]}/#{prefix[i]}#{count_format[i] % i}.png"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def summary
|
|
55
|
+
"#{dir[0]}/#{prefix[0]}_*"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'squib/args/arg_loader'
|
|
2
|
+
|
|
3
|
+
module Squib
|
|
4
|
+
# @api private
|
|
5
|
+
module Args
|
|
6
|
+
|
|
7
|
+
class ScaleBox
|
|
8
|
+
include ArgLoader
|
|
9
|
+
|
|
10
|
+
def initialize(deck)
|
|
11
|
+
@deck = deck
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.parameters
|
|
15
|
+
{ x: 0, y: 0,
|
|
16
|
+
width: :native, height: :native
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.expanding_parameters
|
|
21
|
+
parameters.keys # all of them
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.params_with_units
|
|
25
|
+
parameters.keys # all of them
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def validate_width(arg, i)
|
|
29
|
+
return @deck.width if arg.to_s == 'deck'
|
|
30
|
+
return :native if arg.to_s == 'native'
|
|
31
|
+
return arg if arg.respond_to? :to_f
|
|
32
|
+
if arg.to_s == 'scale'
|
|
33
|
+
raise 'if width is :scale, height must be a number' unless height[i].respond_to? :to_f
|
|
34
|
+
return arg
|
|
35
|
+
end
|
|
36
|
+
raise 'width must be a number, :scale, :native, or :deck'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def validate_height(arg, i)
|
|
40
|
+
return @deck.height if arg.to_s == 'deck'
|
|
41
|
+
return :native if arg.to_s == 'native'
|
|
42
|
+
return arg if arg.respond_to? :to_f
|
|
43
|
+
if arg.to_s == 'scale'
|
|
44
|
+
raise 'if height is \'scale\', width must be a number' unless width[i].respond_to? :to_f
|
|
45
|
+
return arg
|
|
46
|
+
end
|
|
47
|
+
raise 'height must be a number, :scale, :native, or :deck'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'cairo'
|
|
2
|
+
require 'squib/args/arg_loader'
|
|
3
|
+
require 'squib/args/color_validator'
|
|
4
|
+
require 'squib/args/dir_validator'
|
|
5
|
+
|
|
6
|
+
module Squib
|
|
7
|
+
# @api private
|
|
8
|
+
module Args
|
|
9
|
+
|
|
10
|
+
class Sheet
|
|
11
|
+
include ArgLoader
|
|
12
|
+
include ColorValidator
|
|
13
|
+
include DirValidator
|
|
14
|
+
|
|
15
|
+
def initialize(custom_colors = {}, dsl_method_defaults = {}, deck_size = 1)
|
|
16
|
+
@custom_colors = custom_colors
|
|
17
|
+
@dsl_method_defaults = dsl_method_defaults
|
|
18
|
+
@deck_size = deck_size
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.parameters
|
|
22
|
+
{
|
|
23
|
+
dir: '_output',
|
|
24
|
+
file: 'sheet.png',
|
|
25
|
+
fill_color: :white,
|
|
26
|
+
gap: 0,
|
|
27
|
+
height: 2550,
|
|
28
|
+
margin: 75,
|
|
29
|
+
rows: :infinite,
|
|
30
|
+
columns: 5,
|
|
31
|
+
trim_radius: 38,
|
|
32
|
+
trim: 0,
|
|
33
|
+
width: 3300,
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.expanding_parameters
|
|
38
|
+
[] # none of them
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.params_with_units
|
|
42
|
+
[ :gap, :height, :margin, :trim_radius, :trim, :width ]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate_fill_color(arg)
|
|
46
|
+
colorify(arg, @custom_colors)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate_dir(arg)
|
|
50
|
+
ensure_dir_created(arg)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_columns(arg)
|
|
54
|
+
raise 'columns must be an integer' unless arg.respond_to? :to_i
|
|
55
|
+
arg.to_i
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def validate_rows(arg)
|
|
59
|
+
raise 'columns must be an integer' unless columns.respond_to? :to_i
|
|
60
|
+
return 1 if @deck_size < columns
|
|
61
|
+
return arg if arg.respond_to? :to_i
|
|
62
|
+
(@deck_size.to_i / columns.to_i).ceil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def full_filename
|
|
66
|
+
"#{dir}/#{file}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'cairo'
|
|
2
|
+
require 'squib/args/arg_loader'
|
|
3
|
+
require 'squib/args/dir_validator'
|
|
4
|
+
|
|
5
|
+
module Squib
|
|
6
|
+
# @api private
|
|
7
|
+
module Args
|
|
8
|
+
|
|
9
|
+
class ShowcaseSpecial
|
|
10
|
+
include ArgLoader
|
|
11
|
+
include DirValidator
|
|
12
|
+
|
|
13
|
+
def self.parameters
|
|
14
|
+
{ scale: 0.85,
|
|
15
|
+
offset: 1.1,
|
|
16
|
+
reflect_offset: 15,
|
|
17
|
+
reflect_percent: 0.25,
|
|
18
|
+
reflect_strength: 0.2,
|
|
19
|
+
face: :left,
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.expanding_parameters
|
|
24
|
+
[] # none of them
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.params_with_units
|
|
28
|
+
[ :reflect_offset ]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def face_right?
|
|
32
|
+
@face.to_s.strip.downcase == 'right'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'squib/args/arg_loader'
|
|
2
|
+
|
|
3
|
+
module Squib
|
|
4
|
+
# @api private
|
|
5
|
+
module Args
|
|
6
|
+
|
|
7
|
+
class SvgSpecial
|
|
8
|
+
include ArgLoader
|
|
9
|
+
|
|
10
|
+
def self.parameters
|
|
11
|
+
{ data: nil, id: nil, force_id: false }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.expanding_parameters
|
|
15
|
+
parameters.keys # all of them
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.params_with_units
|
|
19
|
+
[]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate_id(arg, _i)
|
|
23
|
+
return nil if arg.nil?
|
|
24
|
+
arg = '#' << arg unless arg.start_with? '#'
|
|
25
|
+
arg
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Only render if we have an ID specified, or we are forcing an ID
|
|
29
|
+
def render?(i)
|
|
30
|
+
return false if force_id[i] && id[i].to_s.empty?
|
|
31
|
+
return true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'squib/args/arg_loader'
|
|
2
|
+
|
|
3
|
+
module Squib
|
|
4
|
+
# @api private
|
|
5
|
+
module Args
|
|
6
|
+
|
|
7
|
+
class Transform
|
|
8
|
+
include ArgLoader
|
|
9
|
+
|
|
10
|
+
def self.parameters
|
|
11
|
+
{ angle: 0 }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.expanding_parameters
|
|
15
|
+
parameters.keys # all of them
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.params_with_units
|
|
19
|
+
parameters.keys # all of them
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
require 'squib/constants'
|
|
2
|
-
module Squib
|
|
3
|
-
#@api private
|
|
4
|
-
module Args
|
|
5
|
-
# Internal class for handling arguments
|
|
6
|
-
#@api private
|
|
7
|
-
class Typographer
|
|
8
|
-
|
|
9
|
-
def initialize(config = Conf::DEFAULTS)
|
|
10
|
-
%w(lsquote ldquote rsquote rdquote smart_quotes
|
|
11
|
-
em_dash en_dash ellipsis).each do |var|
|
|
12
|
-
instance_variable_set("@#{var}", config[var])
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def process(str)
|
|
17
|
-
str = explicit_replacements(str.to_s)
|
|
18
|
-
str = smart_quotes(str) if @smart_quotes
|
|
19
|
-
str
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def explicit_replacements(str)
|
|
23
|
-
[ :left_curly, :right_curly, :apostraphize,
|
|
24
|
-
:ellipsificate, :em_dash, :en_dash ].each do |sym|
|
|
25
|
-
str = each_non_tag(str) do |token|
|
|
26
|
-
self.method(sym).call(token)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
str
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def smart_quotes(str)
|
|
33
|
-
[ :single_inside_double_quote,
|
|
34
|
-
:right_double_quote,
|
|
35
|
-
:left_double_quote,
|
|
36
|
-
:right_single_quote,
|
|
37
|
-
:left_single_quote].each do |sym|
|
|
38
|
-
str = each_non_tag(str) do |token|
|
|
39
|
-
self.method(sym).call(token)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
str
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Iterate over each non-tag for processing
|
|
46
|
-
# Allows us to ignore anything inside < and >
|
|
47
|
-
def each_non_tag(str)
|
|
48
|
-
full_str = ''
|
|
49
|
-
tag_delimit = /(<(?:(?!<).)*>)/ # use non-capturing group w/ negative lookahead
|
|
50
|
-
str.split(tag_delimit).each do |token|
|
|
51
|
-
if token.start_with? '<'
|
|
52
|
-
full_str << token # don't process tags
|
|
53
|
-
else
|
|
54
|
-
full_str << yield(token)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
return full_str
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Straightforward replace
|
|
61
|
-
def left_curly(str)
|
|
62
|
-
str.gsub('``', @ldquote)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Straightforward replace
|
|
66
|
-
def right_curly(str)
|
|
67
|
-
str.gsub(%{''}, @rdquote)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# A quote between two letters is an apostraphe
|
|
71
|
-
def apostraphize(str)
|
|
72
|
-
str.gsub(/(\w)(\')(\w)/, '\1' + @rsquote + '\3')
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Straightforward replace
|
|
76
|
-
def ellipsificate(str)
|
|
77
|
-
str.gsub('...', @ellipsis)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Straightforward replace
|
|
81
|
-
def en_dash(str)
|
|
82
|
-
str.gsub('--', @en_dash)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Straightforward replace
|
|
86
|
-
def em_dash(str)
|
|
87
|
-
str.gsub('---', @em_dash)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Quote next to non-whitespace curls
|
|
91
|
-
def right_double_quote(str)
|
|
92
|
-
str.gsub(/(\S)(\")/, '\1' + @rdquote)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Quote next to non-whitespace curls
|
|
96
|
-
def left_double_quote(str)
|
|
97
|
-
str.gsub(/(\")(\S)/, @ldquote + '\2')
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Handle the cases where a double quote is next to a single quote
|
|
101
|
-
def single_inside_double_quote(str)
|
|
102
|
-
str.gsub(/(\")(\')(\S)/, @ldquote + @lsquote + '\3')
|
|
103
|
-
.gsub(/(\")(\')(\S)/, '\1' + @rsquote + @rdquote)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Quote next to non-whitespace curls
|
|
107
|
-
def right_single_quote(str)
|
|
108
|
-
str.gsub(/(\S)(\')/, '\1' + @rsquote)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Quote next to non-whitespace curls
|
|
112
|
-
def left_single_quote(str)
|
|
113
|
-
str.gsub(/(\')(\S)/, @lsquote + '\2')
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
end
|
|
117
|
-
end
|
|
1
|
+
require 'squib/constants'
|
|
2
|
+
module Squib
|
|
3
|
+
#@api private
|
|
4
|
+
module Args
|
|
5
|
+
# Internal class for handling arguments
|
|
6
|
+
#@api private
|
|
7
|
+
class Typographer
|
|
8
|
+
|
|
9
|
+
def initialize(config = Conf::DEFAULTS)
|
|
10
|
+
%w(lsquote ldquote rsquote rdquote smart_quotes
|
|
11
|
+
em_dash en_dash ellipsis).each do |var|
|
|
12
|
+
instance_variable_set("@#{var}", config[var])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def process(str)
|
|
17
|
+
str = explicit_replacements(str.to_s)
|
|
18
|
+
str = smart_quotes(str) if @smart_quotes
|
|
19
|
+
str
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def explicit_replacements(str)
|
|
23
|
+
[ :left_curly, :right_curly, :apostraphize,
|
|
24
|
+
:ellipsificate, :em_dash, :en_dash ].each do |sym|
|
|
25
|
+
str = each_non_tag(str) do |token|
|
|
26
|
+
self.method(sym).call(token)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
str
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def smart_quotes(str)
|
|
33
|
+
[ :single_inside_double_quote,
|
|
34
|
+
:right_double_quote,
|
|
35
|
+
:left_double_quote,
|
|
36
|
+
:right_single_quote,
|
|
37
|
+
:left_single_quote].each do |sym|
|
|
38
|
+
str = each_non_tag(str) do |token|
|
|
39
|
+
self.method(sym).call(token)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
str
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Iterate over each non-tag for processing
|
|
46
|
+
# Allows us to ignore anything inside < and >
|
|
47
|
+
def each_non_tag(str)
|
|
48
|
+
full_str = ''
|
|
49
|
+
tag_delimit = /(<(?:(?!<).)*>)/ # use non-capturing group w/ negative lookahead
|
|
50
|
+
str.split(tag_delimit).each do |token|
|
|
51
|
+
if token.start_with? '<'
|
|
52
|
+
full_str << token # don't process tags
|
|
53
|
+
else
|
|
54
|
+
full_str << yield(token)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
return full_str
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Straightforward replace
|
|
61
|
+
def left_curly(str)
|
|
62
|
+
str.gsub('``', @ldquote)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Straightforward replace
|
|
66
|
+
def right_curly(str)
|
|
67
|
+
str.gsub(%{''}, @rdquote)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# A quote between two letters is an apostraphe
|
|
71
|
+
def apostraphize(str)
|
|
72
|
+
str.gsub(/(\w)(\')(\w)/, '\1' + @rsquote + '\3')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Straightforward replace
|
|
76
|
+
def ellipsificate(str)
|
|
77
|
+
str.gsub('...', @ellipsis)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Straightforward replace
|
|
81
|
+
def en_dash(str)
|
|
82
|
+
str.gsub('--', @en_dash)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Straightforward replace
|
|
86
|
+
def em_dash(str)
|
|
87
|
+
str.gsub('---', @em_dash)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Quote next to non-whitespace curls
|
|
91
|
+
def right_double_quote(str)
|
|
92
|
+
str.gsub(/(\S)(\")/, '\1' + @rdquote)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Quote next to non-whitespace curls
|
|
96
|
+
def left_double_quote(str)
|
|
97
|
+
str.gsub(/(\")(\S)/, @ldquote + '\2')
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Handle the cases where a double quote is next to a single quote
|
|
101
|
+
def single_inside_double_quote(str)
|
|
102
|
+
str.gsub(/(\")(\')(\S)/, @ldquote + @lsquote + '\3')
|
|
103
|
+
.gsub(/(\")(\')(\S)/, '\1' + @rsquote + @rdquote)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Quote next to non-whitespace curls
|
|
107
|
+
def right_single_quote(str)
|
|
108
|
+
str.gsub(/(\S)(\')/, '\1' + @rsquote)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Quote next to non-whitespace curls
|
|
112
|
+
def left_single_quote(str)
|
|
113
|
+
str.gsub(/(\')(\S)/, @lsquote + '\2')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
118
|
end
|