svglib icon indicating copy to clipboard operation
svglib copied to clipboard

Reportlab unable to find Arial font

Open deeplook opened this issue 7 years ago • 5 comments

When running this from the test suite, on reportlab 3.4.0 I get an error indicating some ArialMT file was not found:

Flatty:svglib dinu$ PYTHONPATH=. py.test tests/test_samples.py -k TestW3CSVG
===================================================== test session starts =====================================================
platform darwin -- Python 3.6.3, pytest-3.5.1, py-1.5.2, pluggy-0.6.0
rootdir: /Volumes/SanDisk128/mobiledev/src/repos/github/deeplook/svglib, inifile:
plugins: hypothesis-3.38.5, celery-4.1.0
collected 9 items / 7 deselected

tests/test_samples.py Fs                                                                                                [100%]

========================================================== FAILURES ===========================================================
_______________________________________________ TestW3CSVG.test_convert_pdf_png _______________________________________________

gs = <gstate object at 0x1099c5850>, fontName = 'Arial', fontSize = 30.0

    def _setFont(gs,fontName,fontSize):
        try:
>           gs.setFont(fontName,fontSize)
E           ValueError: _renderPM.gstate_setFont: Can't find font!

../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:242: ValueError

During handling of the above exception, another exception occurred:

gs = <gstate object at 0x1099c5850>, fontName = 'Arial', fontSize = 30.0

    def _setFont(gs,fontName,fontSize):
        try:
            gs.setFont(fontName,fontSize)
        except ValueError as e:
            if not e.args[0].endswith("Can't find font!"): raise
            #here's where we try to add a font to the canvas
            try:
                f = getFont(fontName)
>               _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
E               AttributeError: 'TTEncoding' object has no attribute 'vector'

../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:248: AttributeError

During handling of the above exception, another exception occurred:

self = <test_samples.TestW3CSVG object at 0x10bee5710>

    def test_convert_pdf_png(self):
        """
            Test converting W3C SVG files to PDF and PNG using svglib.

            ``renderPM.drawToFile()`` used in this test is known to trigger an
            error sometimes in reportlab which was fixed in reportlab 3.3.26.
            See https://github.com/deeplook/svglib/issues/47
            """

        exclude_list = [
            "paint-stroke-06-t.svg",
            "paint-stroke-207-t.svg",
            "coords-trans-09-t.svg",  # renderPDF issue (div by 0)
        ]

        paths = glob.glob("%s/svg/*.svg" % self.folder_path)
        msg = "Destination folder '%s/svg' not found." % self.folder_path
        assert len(paths) > 0, msg

        for i, path in enumerate(paths):
            print("working on [%d] %s" % (i, path))

            if basename(path) in exclude_list:
                print("excluded (to be tested later)")
                continue

            # convert
            drawing = svglib.svg2rlg(path)

            # save as PDF
            base = splitext(path)[0] + '-svglib.pdf'
            renderPDF.drawToFile(drawing, base, showBoundary=0)

            # save as PNG
            # (endless loop for file paint-stroke-06-t.svg)
            base = splitext(path)[0] + '-svglib.png'
            try:
                # Can trigger an error in reportlab < 3.3.26.
>               renderPM.drawToFile(drawing, base, 'PNG')

tests/test_samples.py:434:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:680: in drawToFile
    c = drawToPMCanvas(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:666: in drawToPMCanvas
    draw(d, c, 0, 0, showBoundary=showBoundary)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:51: in draw
    R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:201: in draw
    self.drawNode(drawing)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:108: in drawNode
    self.drawNodeDispatcher(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:282: in drawNodeDispatcher
    self.drawGroup(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:311: in drawGroup
    self.drawNode(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:108: in drawNode
    self.drawNodeDispatcher(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:282: in drawNodeDispatcher
    self.drawGroup(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:311: in drawGroup
    self.drawNode(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:108: in drawNode
    self.drawNodeDispatcher(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:282: in drawNodeDispatcher
    self.drawGroup(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:311: in drawGroup
    self.drawNode(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:108: in drawNode
    self.drawNodeDispatcher(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:282: in drawNodeDispatcher
    self.drawGroup(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:311: in drawGroup
    self.drawNode(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:108: in drawNode
    self.drawNodeDispatcher(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:282: in drawNodeDispatcher
    self.drawGroup(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:311: in drawGroup
    self.drawNode(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:108: in drawNode
    self.drawNodeDispatcher(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:282: in drawNodeDispatcher
    self.drawGroup(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderbase.py:311: in drawGroup
    self.drawNode(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:105: in drawNode
    self.push(node)
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:66: in push
    self.applyState()
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:92: in applyState
    self._canvas.setFont(s['fontName'], s['fontSize'])
../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:399: in setFont
    _setFont(self._gs,fontName,fontSize)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

gs = <gstate object at 0x1099c5850>, fontName = 'Arial', fontSize = 30.0

    def _setFont(gs,fontName,fontSize):
        try:
            gs.setFont(fontName,fontSize)
        except ValueError as e:
            if not e.args[0].endswith("Can't find font!"): raise
            #here's where we try to add a font to the canvas
            try:
                f = getFont(fontName)
                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
            except:
                s1, s2 = list(map(str,sys.exc_info()[:2]))
>               raise RenderPMError("Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2))
E               reportlab.graphics.renderPM.RenderPMError: Can't setFont(Arial) missing the T1 files?
E               Originally <class 'AttributeError'>: 'TTEncoding' object has no attribute 'vector'

../../../../../bin/miniconda3/lib/python3.6/site-packages/reportlab/graphics/renderPM.py:251: RenderPMError
---------------------------------------------------- Captured stdout call -----------------------------------------------------
working on [0] /Volumes/SanDisk128/mobiledev/src/repos/github/deeplook/svglib/tests/samples/W3C_SVG_12_TinyTestSuite/svg/animate-elem-02-t.svg
---------------------------------------------------- Captured stderr call -----------------------------------------------------
Warn: Can't find .pfb for face 'b'ArialMT''
====================================== 1 failed, 1 skipped, 7 deselected in 1.67 seconds ======================================

My reportlab/fonts folder in this case (barely ever used) has this content, where nothing looks like Arial. On reportlab.com I have not found immediately some additional fonts to install. And I have likely never touched rl_config.py in this installation. @replabrobin any ideas?

Flatty:svglib dinu$ ll /Volumes/SanDisk128/mobiledev/bin/miniconda3/lib/python3.6/site-packages/reportlab/fonts/
total 3224
-rw-r--r--  1 dinu  staff     318 Jan  6  2018 00readme.txt
-rw-r--r--  1 dinu  staff   17976 Jan  6  2018 DarkGarden-copying-gpl.txt
-rw-r--r--  1 dinu  staff    1318 Jan  6  2018 DarkGarden-copying.txt
-rw-r--r--  1 dinu  staff    4122 Jan  6  2018 DarkGarden-readme.txt
-rw-r--r--  1 dinu  staff  519634 Jan  6  2018 DarkGarden.sfd
-rw-r--r--  1 dinu  staff   10351 Jan  6  2018 DarkGardenMK.afm
-rw-r--r--  1 dinu  staff   79824 Jan  6  2018 DarkGardenMK.pfb
-rw-r--r--  1 dinu  staff   65932 Jan  6  2018 Vera.ttf
-rw-r--r--  1 dinu  staff   63208 Jan  6  2018 VeraBI.ttf
-rw-r--r--  1 dinu  staff   58716 Jan  6  2018 VeraBd.ttf
-rw-r--r--  1 dinu  staff   63684 Jan  6  2018 VeraIt.ttf
-rw-r--r--  1 dinu  staff   32084 Jan  6  2018 _a______.pfb
-rw-r--r--  1 dinu  staff   31966 Jan  6  2018 _ab_____.pfb
-rw-r--r--  1 dinu  staff   32019 Jan  6  2018 _abi____.pfb
-rw-r--r--  1 dinu  staff   32115 Jan  6  2018 _ai_____.pfb
-rw-r--r--  1 dinu  staff   35377 Jan  6  2018 _eb_____.pfb
-rw-r--r--  1 dinu  staff   38543 Jan  6  2018 _ebi____.pfb
-rw-r--r--  1 dinu  staff   37518 Jan  6  2018 _ei_____.pfb
-rw-r--r--  1 dinu  staff   35380 Jan  6  2018 _er_____.pfb
-rw-r--r--  1 dinu  staff    5954 Jan  6  2018 bitstream-vera-license.txt
-rw-r--r--  1 dinu  staff   35500 Jan  6  2018 cob_____.pfb
-rw-r--r--  1 dinu  staff   50532 Jan  6  2018 cobo____.pfb
-rw-r--r--  1 dinu  staff   34585 Jan  6  2018 com_____.pfb
-rw-r--r--  1 dinu  staff   48468 Jan  6  2018 coo_____.pfb
-rw-r--r--  1 dinu  staff   34705 Jan  6  2018 sy______.pfb
-rw-r--r--  1 dinu  staff   49593 Jan  6  2018 zd______.pfb
-rw-r--r--  1 dinu  staff   75573 Jan  6  2018 zx______.pfb
-rw-r--r--  1 dinu  staff   96418 Jan  6  2018 zy______.pfb

deeplook avatar Aug 17 '18 22:08 deeplook

The message indicates that the renderPM graphics state is being induced to use a font 'Arial' without that font being registered. I assume Arial is a reference to a font like MS has. Those are usually TTF and as we don't have a magic search by name for such fonts they must be registered using the normal registration mechanism.

On windows you can usually get 'arial' using

from reportlab.pdfbase.pdfmetrics import registerFont from reportlab.pdfbase.ttfonts import TTFont registerFont(TTFont('Arial','ARIAL.ttf'))

and it can then be passed to a canvas using setFont eg

from reportlab.graphics.renderPM import PMCanvas canv=PMCanvas(10,10) canv.setFont('Arial',10)

the main reason that we don't support searching arbitrarily for a font matching the given name is that it is quite hard. 'Arial' is a postscript name, obviously we don't want to support opening & parsing many ttf files to get at their postscript names (ditto for type1 etc etc).

The solution is to write a pluggable search mechanism that will map 'Arial' to the ttf file which can then be registered.

A long time ago I started on something like a caching lookup (reportlab/lib/fontfinder.py), but I have never implemented automatic font lookup psname --> font.

replabrobin avatar Aug 21 '18 15:08 replabrobin

@replabrobin Isn't this something that fontconfig is intended to solve?

deeplook avatar Sep 10 '18 18:09 deeplook

Theoretically yes, but only if you ask it the right questions; I am almost sure it can be made to work on windows and osx, but it's an extra large dependency for reportlab. Nothing prevents end users from using such a tool, but fontconfig itself is not easy to use.

replabrobin avatar Sep 17 '18 08:09 replabrobin

Do I understand correctly that reportlab is the first tool I'm using that cannot pick up system fonts used by all other applications I ever used? I have never been forced to specify fonts by full file system path to be honest.

pavlix avatar Dec 16 '20 07:12 pavlix

reportlab has a search path for fonts; font usage is a very grey area so far as licensing is concerned because font designers want payment. The default search path for ttf is

TTFSearchPath = ( 'c:/winnt/fonts', 'c:/windows/fonts', '/usr/lib/X11/fonts/TrueType/', '/usr/share/fonts/truetype', '/usr/share/fonts', #Linux, Fedora '/usr/share/fonts/dejavu', #Linux, Fedora '%(REPORTLAB_DIR)s/fonts', #special '%(REPORTLAB_DIR)s/../fonts', #special '%(REPORTLAB_DIR)s/../../fonts',#special '%(CWD)s/fonts', #special '~/fonts', '~/.fonts', '%(XDG_DATA_HOME)s/fonts', '~/.local/share/fonts', #mac os X - from #http://developer.apple.com/technotes/tn/tn2024.html '~/Library/Fonts', '/Library/Fonts', '/Network/Library/Fonts', '/System/Library/Fonts', ) but please note that you will likely have to use the ttf filename to register a font so the internal names are likely not of use.

There are several ways to override these settings.

MrBitBucket avatar Dec 16 '20 10:12 MrBitBucket