On this page:
smart-quotes
smart-dashes
smart-ellipses
wrap-hanging-quotes
whitespace?
whitespace/  nbsp?
8.12

15.2 Typography🔗ℹ

 (require pollen/unstable/typography) package: pollen

Quick & dirty utilities. I use them, but I haven’t tested them with enough edge cases to feel like they deserve to live outside unstable. I welcome improvements.

procedure

(smart-quotes xexpr 
  [#:apostophe apostrophe-str 
  #:single-open single-open-str 
  #:single-close single-close-str 
  #:double-open double-open-str 
  #:double-close double-close-str]) 
  (or/c string? txexpr?)
  xexpr : (or/c string? txexpr?)
  apostrophe-str : string? = "’"
  single-open-str : string? = "‘"
  single-close-str : string? = "’"
  double-open-str : string? = "“"
  double-close-str : string? = "”"
Convert straight quotes in xexpr to curly. By default, American English curly quotes are used. The optional keyword arguments can be used to set different quotes suited to other languages or script systems.

Examples:
> (define tricky-string
  "\"Why,\" she could've asked, \"are we in O‘ahu watching 'Mame'?\"")
> (display tricky-string)

"Why," she could've asked, "are we in O‘ahu watching 'Mame'?"

> (display (smart-quotes tricky-string))

“Why,” she could’ve asked, “are we in O‘ahu watching ‘Mame’?”

> (display (smart-quotes tricky-string
                        #:double-open "«" #:double-close "»"
                        #:single-open "‹" #:single-close "›"))

«Why,» she could’ve asked, «are we in O‘ahu watching ‹Mame›?»

> (display (smart-quotes tricky-string
                        #:double-open "„" #:double-close "”"
                        #:single-open "‚" #:single-close "’"))

„Why,” she could’ve asked, „are we in O‘ahu watching ‚Mame’?”

procedure

(smart-dashes str)  string?

  str : string?
In str, convert three hyphens to an em dash, and two hyphens to an en dash, and remove surrounding spaces.

Examples:
> (define tricky-string "I had a few --- OK, like 6--8 --- thin mints.")
> (display tricky-string)

I had a few --- OK, like 6--8 --- thin mints.

> (display (smart-dashes tricky-string))

I had a few—OK, like 6–8—thin mints.

; Monospaced font not great for showing dashes, but you get the idea

procedure

(smart-ellipses str)  string?

  str : string?
In str, convert three periods to an ellipsis.

Examples:
> (define tricky-string "I had a few ... OK, like 6--8 ... thin mints.")
> (display tricky-string)

I had a few ... OK, like 6--8 ... thin mints.

> (display (smart-ellipses tricky-string))

I had a few … OK, like 6--8 … thin mints.

procedure

(wrap-hanging-quotes tx 
  [#:single-preprend single-preprender 
  #:double-preprend double-preprender]) 
  txexpr?
  tx : txexpr?
  single-preprender : txexpr-tag? = 'squo
  double-preprender : txexpr-tag? = 'dquo
Find single or double quote marks at the beginning of tx and wrap them in an X-expression with the tag single-preprender or double-preprender, respectively. The default values are 'squo and 'dquo.

Examples:
> (wrap-hanging-quotes '(p "No quote to hang."))

'(p "No quote to hang.")

> (wrap-hanging-quotes '(p "“What? We need to hang quotes?”"))

'(p (dquo "“" "What? We need to hang quotes?”"))

In pro typography, quotation marks at the beginning of a line or paragraph are often shifted into the margin slightly to make them appear more optically aligned with the left edge of the text. With a reflowable layout model like HTML, you don’t know where your line breaks will be.

This function will simply insert the 'squo and 'dquo tags, which provide hooks that let you do the actual hanging via CSS, like so (actual measurement can be refined to taste):

squo {margin-left: -0.25em;}

dquo {margin-left: -0.50em;}

Be warned: there are many edge cases this function does not handle well.

Examples:
; Argh: this edge case is not handled properly
> (wrap-hanging-quotes '(p "“" (em "What?") "We need to hang quotes?”"))

'(p "“" (em "What?") "We need to hang quotes?”")

procedure

(whitespace? v)  boolean?

  v : any/c
A predicate that returns #t for any stringlike v that’s entirely whitespace, but also the empty string, as well as lists and vectors that are made only of whitespace? members. Following the regexp-match convention, whitespace? does not return #t for a nonbreaking space. If you prefer that behavior, use whitespace/nbsp?.

Examples:
> (whitespace? "\n\n   ")

#t

> (whitespace? (string->symbol "\n\n   "))

#t

> (whitespace? "")

#t

> (whitespace? '("" "  " "\n\n\n" " \n"))

#t

> (define nonbreaking-space (format "~a" #\u00A0))
> (whitespace? nonbreaking-space)

#f

procedure

(whitespace/nbsp? v)  boolean?

  v : any/c
Like whitespace?, but also returns #t for nonbreaking spaces.

Examples:
> (whitespace/nbsp? "\n\n   ")

#t

> (whitespace/nbsp? (string->symbol "\n\n   "))

#t

> (whitespace/nbsp? "")

#t

> (whitespace/nbsp? '("" "  " "\n\n\n" " \n"))

#t

> (define nonbreaking-space (format "~a" #\u00A0))
> (whitespace/nbsp? nonbreaking-space)

#t