views:

305

answers:

2

Despite offering a nice way to escape output using filters, none of them do the right thing. Taking the string:

x=u"&\u0092"

The filters do the following:

x             Turns the & into an entity but not the \u0092 (valid XML but not XHTML)
h             Exactly the same
u             Escapes both, but obviously uses url escaping
entities      Only converts named entities, so again only the & is escaped
decode.latin1 The same

HTML uses the standard UNICODE Consortium character repertoire, and it leaves undefined (among others) 65 character codes (0 to 31 inclusive and 127 to 159 inclusive)

These seem to be the characters missed. Any ideas?

EDIT

It seems to validate if I use the file offline. Could this be a Content-Type problem?

+1  A: 

It is not necessary to convert Unicode characters to the &#xxxx; form to work in HTML unless you're deliberately using the ASCII charset. It's simpler and more efficient to escape named entities, then encode the whole string to UTF-8 and write it out like that. You should probably declare the encoding being used in the HTTP headers or in a <meta> tag.

EDIT:

It seems to validate if I use the file offline. Could this be a Content-Type problem?

Yes. You can either use HTTP headers to enforce a UTF-8 charset or specify it in the HTML directly via a meta tag:

<meta http-equiv="Content-Type" content="application/xhtml+xml;charset=utf-8" />
Max Shawabkeh
I wish it was that simple, but it's not. I *am* using UTF-8 and it won't validate.
Draemon
Validates fine as XHTML Strict. HTML 4 does have problems with it though. Do you need it to be non-X HTML?
Max Shawabkeh
It's XHTML Strict, UTF-8 and is detected as such correctly by the online and offline w3c validators. But the online validator tells me "You have used an illegal character in your text. HTML uses the standard UNICODE Consortium character repertoire"
Draemon
I could not reproduce it. Using validate-by-upload validates fine for a file that has a `\u0092` (`\xC2\x92` in UTF-8) character in the middle of its body.
Max Shawabkeh
still fails. I'm having to use a local copy of the w3c validator (v0.7.4) to test the live internal dev site (which is serving it as plain/html in the headers for some reason). If I can find a way to change the headers I will, otherwise at least I know it will validate once it's actually live.
Draemon
nope. Changing the headers worked (it's now being served as application/xhtml) but still fails. I guess it's a validator bug.
Draemon
A: 

Validation issues aside, it's useful to be able to remove these characters (which don't display reliably anyway) without necessarily escaping anything else. To this end I added the following function to `lib/helpers.py':

__sgml_invalid = re.compile(r'[\x82-\x8c\x91-\x9c\x9f]', re.UNICODE)

def sgmlsafe(text):
    lookup = {
        130:"&#8218;",    #Single Low-9 Quotation Mark
        131: "&#402;",    #Latin Small Letter F With Hook
        132:"&#8222;",    #Double Low-9 Quotation Mark
        133:"&#8230;",    #Horizontal Ellipsis
        134:"&#8224;",    #Dagger
        135:"&#8225;",    #Double Dagger
        136: "&#710;",    #Modifier Letter Circumflex Accent
        137:"&#8240;",    #Per Mille Sign
        138: "&#352;",    #Latin Capital Letter S With Caron
        139:"&#8249;",    #Single Left-Pointing Angle Quotation Mark
        140: "&#338;",    #Latin Capital Ligature OE
        145:"&#8216;",    #Left Single Quotation Mark
        146:"&#8217;",    #Right Single Quotation Mark
        147:"&#8220;",    #Left Double Quotation Mark
        148:"&#8221;",    #Right Double Quotation Mark
        149:"&#8226;",    #Bullet
        150:"&#8211;",    #En Dash
        151:"&#8212;",    #Em Dash
        152: "&#732;",    #Small Tilde
        153:"&#8482;",    #Trade Mark Sign
        154: "&#353;",    #Latin Small Letter S With Caron
        155:"&#8250;",    #Single Right-Pointing Angle Quotation Mark
        156: "&#339;",    #Latin Small Ligature OE
        159: "&#376;"     #Latin Capital Letter Y With Diaeresis
        }

    return __sgml_invalid.sub(lambda x: lookup[ord(x.group())], text)

And you can make this available as a filter by editing environment.py:

config['pylons.app_globals'].mako_lookup = TemplateLookup(
    ...
    imports=[....,'from appname.lib.helpers import sgmlsafe',...]

It should then be available to your templates:

${c.content|n,sgmlsafe}
Draemon