Search code examples
pythonubuntusvgtornadofontforge

python - fontforge can't parse a svg file in a Tornado server


I'm trying to mount a Tornado server on an Ubuntu VM, whose task will be to generate a complete font pack, starting from a .svg file. I have run the following script without my server, that worked kinda perfectly. https://gist.github.com/jorgegarciadev/6127832

And now that I try to execute this task on my server, Fontforge seem to have trouble with the .svg file. First I thought it was a chunk problem, as if my file wasn't read or sent totally, but when I open the recently uploaded file on the server, it's complete and has the same size than the original file. And what's still more disturbing is that Fontforge informs me that this file is not a font file. That's what made me think about the chunk thing (because it seems to be parsing errors)

SVG File on pastebin right there : http://pastebin.com/ai5pr1DG

The python server code:

import tornado, tornado.ioloop, tornado.web
import os, uuid
import fontforge, re, zipfile

__UPLOADS__ = "uploads/"
EXTS = [".woff", ".ttf", ".otf", ".svg", ".eot"]

class Userform(tornado.web.RequestHandler):
    def get(self):
        self.render("fileuploadform.html")

class Upload(tornado.web.RequestHandler):
    @staticmethod
    def cssGenerator(name, fullname):
        cssFile = name + ".css"
        template = "@font-face {\
            \n\tfont-family: '" + fullname + "';\
            \n\tsrc: url('" + name + ".eot');\
            \n\tsrc: url('" + name + ".eot?#iefix') format('embedded-opentype'),\
            \n\turl('" + name + ".woff') format('woff'),\
            \n\turl('" + name + ".ttf') format('truetype'),\
            \n\turl('" + name + ".svg#ywftsvg') format('svg');\
            \n\tfont-style: normal;\
            \n\tfont-weight: normal;\
            \n}\n\n"
        open(cssFile, 'w+').writelines(template)

    @staticmethod
    def fontGenerator(filename):
        #exts = ["woff", "ttf", "otf", "svg", "eot"]
        name = os.path.splitext(filename)[0]
        '''if not os.path.exists(name):
            os.makedirs(name)'''

        font = fontforge.open(filename)
        fullname = font.fullname

        for ext in EXTS:
            f = name + ext
            font.generate(f)

    def post(self):
        fileinfo = self.request.files['filearg'][0]
        fname = fileinfo['filename']
        extn = os.path.splitext(fname)[1]
        cname = str(uuid.uuid4()) + extn
        zname = os.path.splitext(fname)[0]
        fh = open(cname, 'w+')
        fh.write(fileinfo['body'])
        os.system("mv " + cname + " " + zname + extn)
        #zname is default, extn is .svg, fname is default.svg, cname is file name in the server
        self.cssGenerator(zname, 'testfont')
        self.fontGenerator(zname + extn)

application = tornado.web.Application([
    (r"/", Userform),
    (r"/upload", Upload),
    ], debug=True)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Output after launching server and clicking "upload" button on the client:

strippedname:/home/bill/proto.svg
/home/bill/proto.svg:109: parser error : AttValue: ' expected
-18 403 -18 L 403 -18 C 352 -18 301 0 251 0 C 225 0 210 15 210 41 C 210 63 225 7
                                                                               ^
/home/bill/proto.svg:109: parser error : attributes construct error
-18 403 -18 L 403 -18 C 352 -18 301 0 251 0 C 225 0 210 15 210 41 C 210 63 225 7
                                                                               ^
/home/bill/proto.svg:109: parser error : Couldn't find end of Start Tag glyph line 109
-18 403 -18 L 403 -18 C 352 -18 301 0 251 0 C 225 0 210 15 210 41 C 210 63 225 7
                                                                               ^
/home/bill/proto.svg:109: parser error : Premature end of data in tag font line 6
-18 403 -18 L 403 -18 C 352 -18 301 0 251 0 C 225 0 210 15 210 41 C 210 63 225 7
                                                                               ^
/home/bill/proto.svg:109: parser error : Premature end of data in tag defs line 5
-18 403 -18 L 403 -18 C 352 -18 301 0 251 0 C 225 0 210 15 210 41 C 210 63 225 7
                                                                               ^
/home/bill/proto.svg:109: parser error : Premature end of data in tag svg line 3
-18 403 -18 L 403 -18 C 352 -18 301 0 251 0 C 225 0 210 15 210 41 C 210 63 225 7
                                                                               ^
Couldn't find a font file named /home/bill/proto.svg
proto.svg is not in a known format (or uses features of that format fontforge does not support, or is so badly corrupted as to be unreadable)
ERROR:tornado.application:Uncaught exception POST /upload (82.225.61.131)
HTTPServerRequest(protocol='http', host='py-fontconv.cloudapp.net', method='POST', uri='/upload', version='HTTP/1.1', remote_ip='82.225.61.131', headers={'Content-Length': '32378', 'Accept-Language': 'en-us,en;q=0.8,fr;q=0.5,fr-fr;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': 'py-fontconv.cloudapp.net', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:28.0) Gecko/20100101 Firefox/28.0', 'Connection': 'keep-alive', 'Content-Type': 'multipart/form-data; boundary=---------------------------191335620930032341323215978'})
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 1332, in _execute
    result = method(*self.path_args, **self.path_kwargs)
  File "tornadofileupload.py", line 53, in post
    self.fontGenerator(zname + extn)
  File "tornadofileupload.py", line 35, in fontGenerator
    font = fontforge.open(filename)
EnvironmentError: Open failed
ERROR:tornado.access:500 POST /upload (82.225.61.131) 283.75ms

If anyone finds out what's missing here, that would be such a relief because I'm really struggling on this one.


Solution

  • You need to flush or close fh after writing to it. The best way to do this is with the with statement:

    with open(cname, 'w+') as f:
        f.write(fileinfo['body'])
    

    The use of mv on the next line is dangerously insecure. Someone could upload a file with the "name" $(rm -rf *) and that command would be executed. Never use os.system with untrusted input. In this case the shutil module provides alternatives. It's also problematic to use the user-supplied filename as zname directly - it's always better to generate your own filenames instead of using one supplied by the client.