From f472c7d95d4b2d679f4b799cb7f6f2e950bd4cbe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 27 Feb 2014 10:30:34 +0530 Subject: [PATCH] Smarten punctuation: Correct handling for decade abbreviations like '60s and measurements in feet and inches like 1' 2". Fixes #1285351 [Edit Book: Smarten Punctuation handles years and measurements incorrectly](https://bugs.launchpad.net/calibre/+bug/1285351) --- src/calibre/utils/smartypants.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/calibre/utils/smartypants.py b/src/calibre/utils/smartypants.py index 04b454b402..4198d3d469 100644 --- a/src/calibre/utils/smartypants.py +++ b/src/calibre/utils/smartypants.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# vim:fileencoding=utf-8 __author__ = "Chad Miller , Kovid Goyal " __description__ = "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries in pyblosxom" @@ -605,6 +606,12 @@ def educateQuotes(str): str = re.sub(r'''""''', """””""", str) str = re.sub(r"""''""", """’’""", str) + # Special case for decade abbreviations (the '80s --> ’80s): + # See http://practicaltypography.com/apostrophes.html + str = re.sub(r"""(\W|^)'(?=\d{2}s)""", r"""\1’""", str) + # Measurements in feet and inches or longitude/latitude: 19' 43.5" --> 19′ 43.5″ + str = re.sub(r'''(\W|^)([-0-9.]+\s*)'(\s*[-0-9.]+)"''', r'\1\2′\3″', str) + # Special case for Quotes at inside of other entities, e.g.: #

A double quote--"within dashes"--would be nice.

str = re.sub(r"""(?<=\W)"(?=\w)""", r"""“""", str) @@ -625,9 +632,6 @@ def educateQuotes(str): # str = re.sub(r"""^"(?=\s)""", r"""“""", str) # str = re.sub(r"""^'(?=\s)""", r"""‘""", str) - # Special case for decade abbreviations (the '80s): - str = re.sub(r"""\b'(?=\d{2}s)""", r"""’""", str) - close_class = r"""[^\ \t\r\n\[\{\(\-]""" dec_dashes = r"""–|—""" @@ -887,13 +891,19 @@ def run_tests(): # the default attribute is "1", which means "all". def test_dates(self): + self.assertEqual(sp("one two '60s"), "one two ’60s") self.assertEqual(sp("1440-80's"), "1440-80’s") - self.assertEqual(sp("1440-'80s"), "1440-‘80s") - self.assertEqual(sp("1440---'80s"), "1440–‘80s") + self.assertEqual(sp("1440-'80s"), "1440-’80s") + self.assertEqual(sp("1440---'80s"), "1440–’80s") self.assertEqual(sp("1960s"), "1960s") # no effect. self.assertEqual(sp("1960's"), "1960’s") - self.assertEqual(sp("one two '60s"), "one two ‘60s") - self.assertEqual(sp("'60s"), "‘60s") + self.assertEqual(sp("one two '60s"), "one two ’60s") + self.assertEqual(sp("'60s"), "’60s") + + def test_measurements(self): + ae = self.assertEqual + ae(sp("one two 1.1'2.2\""), "one two 1.1′2.2″") + ae(sp("1' 2\""), "1′ 2″") def test_skip_tags(self): self.assertEqual(