Smart curly quotes! Spaces trump all, then worry about \W, you can't convert them all at once or either your opening or your ending (whichever you process first) is going to be too greedy.
diff --git a/system/libraries/Typography.php b/system/libraries/Typography.php
index 808a769..27fa421 100644
--- a/system/libraries/Typography.php
+++ b/system/libraries/Typography.php
@@ -235,26 +235,37 @@
if ( ! isset($table))
{
- $table = array(
+ $table = array(
// nested smart quotes, opening and closing
// note that rules for grammar (English) allow only for two levels deep
// and that single quotes are _supposed_ to always be on the outside
// but we'll accommodate both
- '/(^|\W|\s)\'"/' => '$1‘“',
- '/\'"(\s|\W|$)/' => '’”$1',
- '/(^|\W|\s)"\'/' => '$1“‘',
- '/"\'(\s|\W|$)/' => '”’$1',
+ // Note that in all cases, whitespace is the primary determining factor
+ // on which direction to curl, with non-word characters like punctuation
+ // being a secondary factor only after whitespace is addressed.
+ '/\'"(\s|$)/' => '’”$1',
+ '/(^|\s)\'"/' => '$1‘“',
+ '/\'"(\W)/' => '’”$1',
+ '/(\W)\'"/' => '$1‘“',
+ '/"\'(\s|$)/' => '”’$1',
+ '/(^|\s)"\'/' => '$1“‘',
+ '/"\'(\W)/' => '”’$1',
+ '/(\W)"\'/' => '$1“‘',
// single quote smart quotes
- '/(^|\W|\s)\'/' => '$1‘',
- '/\'(\s|\W|$)/' => '’$1',
+ '/\'(\s|$)/' => '’$1',
+ '/(^|\s)\'/' => '$1‘',
+ '/\'(\W)/' => '’$1',
+ '/(\W)\'/' => '$1‘',
// double quote smart quotes
- '/(^|\W|\s)"/' => '$1“',
- '/"(\s|\W|$)/' => '”$1',
-
+ '/"(\s|$)/' => '”$1',
+ '/(^|\s)"/' => '$1“',
+ '/"(\W)/' => '”$1',
+ '/(\W)"/' => '$1“',
+
// apostrophes
- "/(\w)'(\w)/" => '$1’$2',
+ "/(\w)'(\w)/" => '$1’$2',
// Em dash and ellipses dots
'/\s?\-\-\s?/' => '—',
@@ -265,8 +276,8 @@
// ampersands, if not a character entity
'/&(?!#?[a-zA-Z0-9]{2,};)/' => '&'
- );
- }
+ );
+ }
return preg_replace(array_keys($table), $table, $str);
}