{"size": 4620, "ext": "scrbl", "max_stars_repo_path": "pollen/scribblings/typography.scrbl", "max_stars_repo_name": "otherjoel/pollen", "max_stars_repo_stars_event_min_datetime": "2015-01-01T19:01:47.000Z", "max_stars_repo_stars_event_max_datetime": "2022-03-30T00:16:55.000Z", "max_issues_repo_path": "pollen/scribblings/typography.scrbl", "max_issues_repo_name": "otherjoel/pollen", "max_issues_repo_issues_event_min_datetime": "2015-02-18T05:33:29.000Z", "max_issues_repo_issues_event_max_datetime": "2022-03-14T23:34:46.000Z", "max_forks_repo_path": "pollen/scribblings/typography.scrbl", "max_forks_repo_name": "otherjoel/pollen", "max_forks_repo_forks_event_min_datetime": "2015-02-04T19:06:28.000Z", "max_forks_repo_forks_event_max_datetime": "2022-02-14T14:06:46.000Z"}
{"max_stars_count": 1031, "max_issues_count": 218, "max_forks_count": 88, "avg_line_length": 35.5384615385, "max_line_length": 386, "alphanum_fraction": 0.7034632035}
#lang scribble/manual

@(require scribble/eval pollen/decode pollen/setup txexpr (for-label pollen/unstable/typography txexpr racket (except-in pollen #%module-begin)))

@(define my-eval (make-base-eval))
@(my-eval `(require pollen pollen/unstable/typography))
@(require "mb-tools.rkt")


@title{Typography}

@defmodule[pollen/unstable/typography]

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


@defproc[
(smart-quotes
[xexpr (or/c string? txexpr?)]
[#:apostophe apostrophe-str string? "’"]
[#:single-open single-open-str string? "‘"]
[#:single-close single-close-str string? "’"]
[#:double-open double-open-str string? "“"]
[#:double-close double-close-str string? "”"])
(or/c string? txexpr?)]{

Convert straight quotes in @racket[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[#:eval my-eval
(define tricky-string 
"\"Why,\" she could've asked, \"are we in O‘ahu watching 'Mame'?\"")
(display tricky-string)
(display (smart-quotes tricky-string))
(display (smart-quotes tricky-string
                      #:double-open "«" #:double-close "»"
                      #:single-open "‹" #:single-close "›"))
(display (smart-quotes tricky-string
                      #:double-open "„" #:double-close "”"
                      #:single-open "‚" #:single-close "’"))
]
}

@defproc[
(smart-dashes
[str string?])
string?]
In @racket[_str], convert three hyphens to an em dash, and two hyphens to an en dash, and remove surrounding spaces.

@examples[#:eval my-eval
(define tricky-string "I had a few --- OK, like 6--8 --- thin mints.")
(display tricky-string)
(display (smart-dashes tricky-string))
(code:comment @#,t{Monospaced font not great for showing dashes, but you get the idea})
]


@defproc[
(smart-ellipses
[str string?])
string?]
In @racket[_str], convert three periods to an ellipsis.

@examples[#:eval my-eval
(define tricky-string "I had a few ... OK, like 6--8 ... thin mints.")
(display tricky-string)
(display (smart-ellipses tricky-string))
]



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

@examples[#:eval my-eval
(wrap-hanging-quotes '(p "No quote to hang."))
(wrap-hanging-quotes '(p "“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 @racket['squo] and @racket['dquo] tags, which provide hooks that let you do the actual hanging via CSS, like so (actual measurement can be refined to taste):

@verbatim{squo {margin-left: -0.25em;}
dquo {margin-left: -0.50em;}
}

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

@examples[#:eval my-eval
(code:comment @#,t{Argh: this edge case is not handled properly})
(wrap-hanging-quotes '(p "“" (em "What?") "We need to hang quotes?”"))
]

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


@examples[#:eval my-eval
(whitespace? "\n\n   ")
(whitespace? (string->symbol "\n\n   "))
(whitespace? "")
(whitespace? '("" "  " "\n\n\n" " \n"))
(define nonbreaking-space (format "~a" #\u00A0))
(whitespace? nonbreaking-space)
]

@defproc[
(whitespace/nbsp?
[v any/c])
boolean?]
Like @racket[whitespace?], but also returns @racket[#t] for nonbreaking spaces.


@examples[#:eval my-eval
(whitespace/nbsp? "\n\n   ")
(whitespace/nbsp? (string->symbol "\n\n   "))
(whitespace/nbsp? "")
(whitespace/nbsp? '("" "  " "\n\n\n" " \n"))
(define nonbreaking-space (format "~a" #\u00A0))
(whitespace/nbsp? nonbreaking-space)
]