Allow configuration of typographical replacements, e.g. smart quotes
If you're living in a non-UTF-8 character set, then you can configure what quotes are smartly changed. If my regexes are messing things up, you can also turn this off with smart_quotes: false Also updated docs. This closes #50dev
parent
beb46a9e1c
commit
063d5156d6
26
README.md
26
README.md
|
|
@ -250,7 +250,21 @@ If you want to do specialized formatting within a given string, Squib has lots o
|
|||
* Apostraphes are converted to curly as well.
|
||||
* LaTeX-style quotes are explicitly converted (<tt>``like this''</tt>)
|
||||
* Em-dash and en-dash are converted with triple and double-dashes respectively (<tt>--</tt> is an en-dash, and <tt>---</tt> becomes an em-dash.)
|
||||
* Ellipses can be specified with <tt>...</tt>. Note that this is entirely different from the `ellipsize` option (which determines what to do with overflowing text).
|
||||
* Ellipses can be specified with <tt>...</tt>. Note that this is entirely different from the `ellipsize` option, which is determining what to do with overflowing text.
|
||||
|
||||
The above replacements assume you are using the UTF-8 character set. If you wish to change the characters, set the following configuration options in your `config.yml`
|
||||
|
||||
```yaml
|
||||
lsquote: "\u2018" #note that Yaml wants double quotes here to use escape chars
|
||||
rsquote: "\u2019"
|
||||
ldquote: "\u201C"
|
||||
rdquote: "\u201D"
|
||||
em_dash: "\u2014"
|
||||
en_dash: "\u2013"
|
||||
ellipsis: "\u2026"
|
||||
```
|
||||
|
||||
You can also disable the auto-quoting mechanism by setting `smart_quotes: false` in your config. Explicit replacements will still be performed.
|
||||
|
||||
### Text Samples
|
||||
|
||||
|
|
@ -258,10 +272,18 @@ Examples of all of the above are crammed into the `text_options.rb` sample [foun
|
|||
|
||||
{include:file:samples/text_options.rb}
|
||||
|
||||
Also, the `embed_text.rb` sample has more examples of embedding text, which can be [found here](https://github.com/andymeneely/squib/tree/master/samples/embed_text.rb).
|
||||
The `embed_text.rb` sample has more examples of embedding text, which can be [found here](https://github.com/andymeneely/squib/tree/master/samples/embed_text.rb).
|
||||
|
||||
{include:file:samples/embed_text.rb}
|
||||
|
||||
The `config_text_markup.rb` sample demonstrates how quoting can be configured, [found here](https://github.com/andymeneely/squib/tree/master/samples/config_text_markup.rb)
|
||||
|
||||
{include:file:samples/config_text_markup.rb}
|
||||
|
||||
{include:file:samples/config_text_markup.yml}
|
||||
|
||||
|
||||
|
||||
## Custom Layouts
|
||||
|
||||
Working with x-y coordinates all the time can be tiresome, and ideally everything in a game prototype should be data-driven and easily changed. For this, many Squib methods allow for a `layout` to be set. In essence, layouts are a way of setting default values for any argument given to the command.
|
||||
|
|
|
|||
|
|
@ -8,17 +8,27 @@ module Squib
|
|||
end
|
||||
|
||||
def process(str)
|
||||
[
|
||||
:left_curly,
|
||||
:right_curly,
|
||||
:apostraphize,
|
||||
str = explicit_replacements(str)
|
||||
str = smart_quotes(str) if @config['smart_quotes']
|
||||
str
|
||||
end
|
||||
|
||||
def explicit_replacements(str)
|
||||
[ :left_curly, :right_curly, :apostraphize,
|
||||
:ellipsificate, :em_dash, :en_dash ].each do |sym|
|
||||
str = each_non_tag(str) do |token|
|
||||
self.method(sym).call(token)
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def smart_quotes(str)
|
||||
[ :single_inside_double_quote,
|
||||
:right_double_quote,
|
||||
:left_double_quote,
|
||||
:right_single_quote,
|
||||
:left_single_quote,
|
||||
:ellipsificate,
|
||||
:em_dash,
|
||||
:en_dash ].each do |sym|
|
||||
:left_single_quote].each do |sym|
|
||||
str = each_non_tag(str) do |token|
|
||||
self.method(sym).call(token)
|
||||
end
|
||||
|
|
@ -43,52 +53,58 @@ module Squib
|
|||
|
||||
# Straightforward replace
|
||||
def left_curly(str)
|
||||
str.gsub('``', "\u201C")
|
||||
str.gsub('``', @config['ldquote'])
|
||||
end
|
||||
|
||||
# Straightforward replace
|
||||
def right_curly(str)
|
||||
str.gsub(%{''}, "\u201D")
|
||||
str.gsub(%{''}, @config['rdquote'])
|
||||
end
|
||||
|
||||
# A quote between two letters is an apostraphe
|
||||
def apostraphize(str)
|
||||
str.gsub(/(\w)(\')(\w)/, '\1' + "\u2019" + '\3')
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def right_double_quote(str)
|
||||
str.gsub(/(\S)(\")/, '\1' + "\u201D")
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def left_double_quote(str)
|
||||
str.gsub(/(\")(\S)/, "\u201C" + '\2')
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def right_single_quote(str)
|
||||
str.gsub(/(\S)(\')/, '\1' + "\u2019")
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def left_single_quote(str)
|
||||
str.gsub(/(\')(\S)/, "\u2018" + '\2')
|
||||
str.gsub(/(\w)(\')(\w)/, '\1' + @config['rsquote'] + '\3')
|
||||
end
|
||||
|
||||
# Straightforward replace
|
||||
def ellipsificate(str)
|
||||
str.gsub('...', "\u2026")
|
||||
str.gsub('...', @config['ellipsis'])
|
||||
end
|
||||
|
||||
# Straightforward replace
|
||||
def en_dash(str)
|
||||
str.gsub('--', "\u2013")
|
||||
str.gsub('--', @config['en_dash'])
|
||||
end
|
||||
|
||||
# Straightforward replace
|
||||
def em_dash(str)
|
||||
str.gsub('---', "\u2014")
|
||||
str.gsub('---', @config['em_dash'])
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def right_double_quote(str)
|
||||
str.gsub(/(\S)(\")/, '\1' + @config['rdquote'])
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def left_double_quote(str)
|
||||
str.gsub(/(\")(\S)/, @config['ldquote'] + '\2')
|
||||
end
|
||||
|
||||
# Handle the cases where a double quote is next to a single quote
|
||||
def single_inside_double_quote(str)
|
||||
str.gsub(/(\")(\')(\S)/, @config['ldquote'] + @config['lsquote'] + '\3')
|
||||
.gsub(/(\")(\')(\S)/, '\1' + @config['rsquote'] + @config['rdquote'])
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def right_single_quote(str)
|
||||
str.gsub(/(\S)(\')/, '\1' + @config['rsquote'])
|
||||
end
|
||||
|
||||
# Quote next to non-whitespace curls
|
||||
def left_single_quote(str)
|
||||
str.gsub(/(\')(\S)/, @config['lsquote'] + '\2')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -80,6 +80,14 @@ module Squib
|
|||
'hint' => :none,
|
||||
'img_dir' => '.',
|
||||
'progress_bar' => false,
|
||||
'ldquote' => "\u201C", # UTF8 chars
|
||||
'rdquote' => "\u201D",
|
||||
'lsquote' => "\u2018",
|
||||
'rsquote' => "\u2019",
|
||||
'em_dash' => "\u2014",
|
||||
'en_dash' => "\u2013",
|
||||
'ellipsis' => "\u2026",
|
||||
'smart_quotes' => true,
|
||||
}
|
||||
|
||||
#Translate the hints to the methods.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module Squib
|
|||
|
||||
# :nodoc:
|
||||
# @api private
|
||||
attr_reader :layout, :config
|
||||
attr_reader :layout, :config, :quote_chars
|
||||
|
||||
attr_reader :dir, :prefix, :count_format
|
||||
|
||||
|
|
@ -70,6 +70,7 @@ module Squib
|
|||
@dir = SYSTEM_DEFAULTS[:dir]
|
||||
@prefix = SYSTEM_DEFAULTS[:prefix]
|
||||
@count_format = SYSTEM_DEFAULTS[:count_format]
|
||||
@quote_chars = CONFIG_DEFAULTS.select {|k,v| %w(lsquote rsquote ldquote rdquote smart_quotes).include?(k) }
|
||||
show_info(config, layout)
|
||||
load_config(config)
|
||||
@width = Args::UnitConversion.parse width, dpi
|
||||
|
|
@ -111,6 +112,10 @@ module Squib
|
|||
@prefix = config['prefix']
|
||||
@count_format = config['count_format']
|
||||
@antialias = config['antialias']
|
||||
@quote_chars ||= {}
|
||||
%w(lsquote rsquote ldquote rdquote smart_quotes em_dash en_dash ellipsis).each do |key|
|
||||
@quote_chars[key] = config[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ module Squib
|
|||
layout.font_description = font_desc
|
||||
layout.text = str
|
||||
if markup
|
||||
str = Args::Typographer.new(@deck.config).process(layout.text)
|
||||
str = Args::Typographer.new(@deck.quote_chars).process(layout.text)
|
||||
layout.markup = str
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -25,3 +25,16 @@
|
|||
# Use a SVG cairo back end, instead of an in-memory buffer
|
||||
# backend: :memory # default
|
||||
# backend: :svg # can create scalable pdfs, but rendering done at the printer level is not as good as Cairo.
|
||||
|
||||
# Configure what text markup uses replace characters
|
||||
# Below are the defaults
|
||||
# lsquote: "\u2018" #note that Yaml wants double quotes here to use escape chars
|
||||
# rsquote: "\u2019"
|
||||
# ldquote: "\u201C"
|
||||
# rdquote: "\u201D"
|
||||
# em_dash: "\u2014"
|
||||
# en_dash: "\u2013"
|
||||
# ellipsis: "\u2026"
|
||||
|
||||
# We can also disallow smart quotes and only allow explicit replacements with ``LaTeX-style'' quotes.
|
||||
# smart_quotes: false
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# If we want to disable smart quoting and only allow explicit quoting within markup,
|
||||
# use this option
|
||||
smart_quotes: false
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
require 'squib'
|
||||
|
||||
Squib::Deck.new(config: 'config_text_markup.yml') do
|
||||
text str: %{"'Yaml ain't markup', he says"},
|
||||
x: 10, y: 10, width: 300, height: 200, font: 'Serif 20',
|
||||
markup: true, hint: :cyan
|
||||
save_png prefix: 'config_text_'
|
||||
end
|
||||
|
||||
Squib::Deck.new(config: 'config_disable_quotes.yml') do
|
||||
text str: %{This has typographic sugar --- and ``explicit'' quotes --- but the quotes are "dumb"},
|
||||
x: 10, y: 10, width: 300, height: 200, font: 'Serif 20',
|
||||
markup: true, hint: :cyan
|
||||
save_png prefix: 'config_disable_text_'
|
||||
end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# We can configure what characters actually get replaced by quoting them with unicode code points.
|
||||
lsquote: "\u2018" #note that Yaml wants double quotes here to use escape chars
|
||||
rsquote: "\u2019"
|
||||
ldquote: "\u201C"
|
||||
rdquote: "\u201D"
|
||||
em_dash: "\u2014"
|
||||
en_dash: "\u2013"
|
||||
ellipsis: "\u2026"
|
||||
|
|
@ -45,7 +45,7 @@ describe Squib::Args::Typographer do
|
|||
end
|
||||
|
||||
it "single quotes inside double quotes" do
|
||||
expect(t.process(%{"'I can't do that', he said"})).to eq(%{\u201C\u2019I can\u2019t do that\u2019, he said\u201D})
|
||||
expect(t.process(%{"'I can't do that', he said"})).to eq(%{\u201C\u2018I can\u2019t do that\u2019, he said\u201D})
|
||||
end
|
||||
|
||||
it "replaces the straightforward ones" do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
cairo: antialias=(["subpixel"])
|
||||
cairo: save([])
|
||||
cairo: set_source_color([:black])
|
||||
cairo: translate([10, 10])
|
||||
cairo: rotate([0])
|
||||
cairo: move_to([0, 0])
|
||||
pango: font_description=([MockDouble])
|
||||
pango: text=(["\"'Yaml ain't markup', he says\""])
|
||||
pango: markup=(["foo"])
|
||||
pango: width=([307200])
|
||||
pango: height=([204800])
|
||||
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||
pango: justify=([false])
|
||||
pango: spacing=([0])
|
||||
cairo: update_pango_layout([MockDouble])
|
||||
cairo: move_to([0, 0])
|
||||
cairo: update_pango_layout([MockDouble])
|
||||
cairo: show_pango_layout([MockDouble])
|
||||
cairo: rounded_rectangle([0, 0, 0, 0, 0, 0])
|
||||
cairo: set_source_color([:cyan])
|
||||
cairo: set_line_width([2.0])
|
||||
cairo: stroke([])
|
||||
cairo: restore([])
|
||||
surface: write_to_png(["_output/config_text_00.png"])
|
||||
cairo: antialias=(["subpixel"])
|
||||
cairo: save([])
|
||||
cairo: set_source_color([:black])
|
||||
cairo: translate([10, 10])
|
||||
cairo: rotate([0])
|
||||
cairo: move_to([0, 0])
|
||||
pango: font_description=([MockDouble])
|
||||
pango: text=(["This has typographic sugar --- and ``explicit'' quotes --- but the quotes are \"dumb\""])
|
||||
pango: markup=(["foo"])
|
||||
pango: width=([307200])
|
||||
pango: height=([204800])
|
||||
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||
pango: justify=([false])
|
||||
pango: spacing=([0])
|
||||
cairo: update_pango_layout([MockDouble])
|
||||
cairo: move_to([0, 0])
|
||||
cairo: update_pango_layout([MockDouble])
|
||||
cairo: show_pango_layout([MockDouble])
|
||||
cairo: rounded_rectangle([0, 0, 0, 0, 0, 0])
|
||||
cairo: set_source_color([:cyan])
|
||||
cairo: set_line_width([2.0])
|
||||
cairo: stroke([])
|
||||
cairo: restore([])
|
||||
surface: write_to_png(["_output/config_disable_text_00.png"])
|
||||
|
|
@ -51,6 +51,7 @@ describe "Squib samples" do
|
|||
basic.rb
|
||||
cairo_access.rb
|
||||
csv_import.rb
|
||||
config_text_markup.rb
|
||||
custom_config.rb
|
||||
draw_shapes.rb
|
||||
embed_text.rb
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
"name": "rake run[draw_shapes]",
|
||||
"shell_cmd": "rake run[draw_shapes]",
|
||||
},
|
||||
{
|
||||
"name": "rake run[config_text_markup]",
|
||||
"shell_cmd": "rake run[config_text_markup]",
|
||||
},
|
||||
{
|
||||
"name": "rspec spec/samples/samples_regression_spec.rb",
|
||||
"shell_cmd": "rspec spec/samples/samples_regression_spec.rb",
|
||||
|
|
@ -39,6 +43,7 @@
|
|||
"shell_cmd": "rspec spec/graphics/graphics_text_spec.rb",
|
||||
"working_dir": "${project_path:${folder}}"
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue