src/reportlab/lib/fontfinder.py
author rptlab
Tue, 30 Apr 2013 14:28:14 +0100
branchpy33
changeset 3723 99aa837b6703
parent 3721 0c93dd8ff567
child 3884 3bc59a4c3c21
permissions -rw-r--r--
second stage of port to Python 3.3; working hello world
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
3617
ae5744e97c42 reportlab: copyright date changes
robin
parents: 3326
diff changeset
     1
#Copyright ReportLab Europe Ltd. 2000-2012
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     2
#see license.txt for license details
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     3
__version__=''' $Id$ '''
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     4
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     5
#modification of users/robin/ttflist.py.
3029
eded59f94021 adding docstrings to lib
andy
parents: 2977
diff changeset
     6
__doc__="""This provides some general-purpose tools for finding fonts.
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     7
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     8
The FontFinder object can search for font files.  It aims to build
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     9
a catalogue of fonts which our framework can work with.  It may be useful
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    10
if you are building GUIs or design-time interfaces and want to present users
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    11
with a choice of fonts.
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    12
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    13
There are 3 steps to using it
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    14
1. create FontFinder and set options and directories
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    15
2. search
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    16
3. query
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    17
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    18
>>> import fontfinder
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    19
>>> ff = fontfinder.FontFinder()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    20
>>> ff.addDirectories([dir1, dir2, dir3])
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    21
>>> ff.search()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    22
>>> ff.getFamilyNames()   #or whichever queries you want...
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    23
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    24
Because the disk search takes some time to find and parse hundreds of fonts,
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    25
it can use a cache to store a file with all fonts found. The cache file name
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    26
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    27
For each font found, it creates a structure with
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    28
- the short font name
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    29
- the long font name
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    30
- the principal file (.pfb for type 1 fonts), and the metrics file if appropriate
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    31
- the time modified (unix time stamp)
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    32
- a type code ('ttf')
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    33
- the family name
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    34
- bold and italic attributes
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    35
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    36
One common use is to display families in a dialog for end users;
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    37
then select regular, bold and italic variants of the font.  To get
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    38
the initial list, use getFamilyNames; these will be in alpha order.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    39
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    40
>>> ff.getFamilyNames()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    41
['Bitstream Vera Sans', 'Century Schoolbook L', 'Dingbats', 'LettErrorRobot',
2977
beca8d75f400 reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents: 2964
diff changeset
    42
'MS Gothic', 'MS Mincho', 'Nimbus Mono L', 'Nimbus Roman No9 L',
beca8d75f400 reportlab: remove LetErrorRobot-Chrome, rina & luxi. Add DarkGardenMK and fix tests
rgbecker
parents: 2964
diff changeset
    43
'Nimbus Sans L', 'Vera', 'Standard Symbols L',
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    44
'URW Bookman L', 'URW Chancery L', 'URW Gothic L', 'URW Palladio L']
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    45
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    46
One can then obtain a specific font as follows
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    47
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    48
>>> f = ff.getFont('Bitstream Vera Sans', bold=False, italic=True)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    49
>>> f.fullName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    50
'Bitstream Vera Sans'
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    51
>>> f.fileName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    52
'C:\\code\\reportlab\\fonts\\Vera.ttf'
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    53
>>>
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    54
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    55
It can also produce an XML report of fonts found by family, for the benefit
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    56
of non-Python applications.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    57
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    58
Future plans might include using this to auto-register fonts; and making it
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    59
update itself smartly on repeated instantiation.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    60
"""
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
    61
import sys, time, os, pickle, tempfile
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    62
from xml.sax.saxutils import quoteattr
3098
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
    63
try:
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
    64
    from hashlib import md5
3126
32755aaac077 fontfinder.py: tighten exception handling for md5
rgbecker
parents: 3098
diff changeset
    65
except ImportError:
3098
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
    66
    from md5 import md5
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    67
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    68
EXTENSIONS = ['.ttf','.ttc','.otf','.pfb','.pfa']
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    69
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    70
# PDF font flags (see PDF Reference Guide table 5.19)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    71
FF_FIXED        = 1 <<  1-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    72
FF_SERIF        = 1 <<  2-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    73
FF_SYMBOLIC     = 1 <<  3-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    74
FF_SCRIPT       = 1 <<  4-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    75
FF_NONSYMBOLIC  = 1 <<  6-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    76
FF_ITALIC       = 1 <<  7-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    77
FF_ALLCAP       = 1 << 17-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    78
FF_SMALLCAP     = 1 << 18-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    79
FF_FORCEBOLD    = 1 << 19-1
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    80
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    81
class FontDescriptor:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    82
    """This is a short descriptive record about a font.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    83
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    84
    typeCode should be a file extension e.g. ['ttf','ttc','otf','pfb','pfa']
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    85
    """
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    86
    def __init__(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    87
        self.name = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    88
        self.fullName = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    89
        self.familyName = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    90
        self.styleName = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    91
        self.isBold = False   #true if it's somehow bold
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    92
        self.isItalic = False #true if it's italic or oblique or somehow slanty
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    93
        self.isFixedPitch = False
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    94
        self.isSymbolic = False   #false for Dingbats, Symbols etc.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    95
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    96
        self.typeCode = None   #normally the extension minus the dot
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    97
        self.fileName = None  #full path to where we found it.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    98
        self.metricsFileName = None  #defined only for type='type1pc', or 'type1mac'
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    99
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   100
        self.timeModified = 0
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   101
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   102
    def __repr__(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   103
        return "FontDescriptor(%s)" % self.name
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   104
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   105
    def getTag(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   106
        "Return an XML tag representation"
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   107
        attrs = []
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
   108
        for k, v in self.__dict__.items():
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   109
            if k not in ['timeModified']:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   110
                if v:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   111
                    attrs.append('%s=%s' % (k, quoteattr(str(v))))
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   112
        return '<font ' + ' '.join(attrs) + '/>'
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   113
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   114
from reportlab.lib.utils import rl_isdir, rl_isfile, rl_listdir, rl_getmtime
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   115
class FontFinder:
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   116
    def __init__(self, dirs=[], useCache=True, validate=False):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   117
        self.useCache = useCache
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   118
        self.validate = validate
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   119
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   120
        self._dirs = set(dirs)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   121
        self._fonts = []
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   122
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   123
        self._skippedFiles = [] #list of filenames we did not handle
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   124
        self._badFiles = []  #list of filenames we rejected
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   125
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   126
        self._fontsByName = {}
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   127
        self._fontsByFamily = {}
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   128
        self._fontsByFamilyBoldItalic = {}   #indexed by bold, italic
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   129
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   130
    def addDirectory(self, dirName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   131
        #aesthetics - if there are 2 copies of a font, should the first or last
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   132
        #be picked up?  might need reversing
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   133
        if rl_isdir(dirName):
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   134
            self._dirs.add(dirName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   135
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   136
    def addDirectories(self, dirNames):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   137
        for dirName in dirNames:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   138
            self.addDirectory(dirName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   139
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   140
    def getFamilyNames(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   141
        "Returns a list of the distinct font families found"
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   142
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   143
        if not self._fontsByFamily:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   144
            fonts = self._fonts
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   145
            for font in fonts:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   146
                fam = font.familyName
3326
ce725978d11c Initial Python3 compatibility fixes
damian
parents: 3126
diff changeset
   147
                if fam in self._fontsByFamily:
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   148
                    self._fontsByFamily[fam].append(font)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   149
                else:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   150
                    self._fontsByFamily[fam] = [font]
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   151
        names = list(self._fontsByFamily.keys())
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   152
        names.sort()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   153
        return names
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   154
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   155
    def getFontsInFamily(self, familyName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   156
        "Return list of all font objects with this family name"
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   157
        return self._fontsByFamily.get(familyName,[])
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   158
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   159
    def getFamilyXmlReport(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   160
        """Reports on all families found as XML.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   161
        """
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   162
        lines = []
2799
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   163
        lines.append('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   164
        lines.append("<font_families>")
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   165
        for dirName in self._dirs:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   166
            lines.append("    <directory name=%s/>" % quoteattr(dirName))
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   167
        for familyName in self.getFamilyNames():
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   168
            if familyName:  #skip null case
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   169
                lines.append('    <family name=%s>' % quoteattr(familyName))
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   170
                for font in self.getFontsInFamily(familyName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   171
                    lines.append('        ' + font.getTag())
2799
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   172
                lines.append('    </family>')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   173
        lines.append("</font_families>")
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   174
        return '\n'.join(lines)
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   175
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   176
    def getFontsWithAttributes(self, **kwds):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   177
        """This is a general lightweight search."""
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   178
        selected = []
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   179
        for font in self._fonts:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   180
            OK = True
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
   181
            for k, v in kwds.items():
3326
ce725978d11c Initial Python3 compatibility fixes
damian
parents: 3126
diff changeset
   182
                if getattr(font, k, None) != v:
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   183
                    OK = False
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   184
            if OK:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   185
                selected.append(font)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   186
        return selected
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   187
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   188
    def getFont(self, familyName, bold=False, italic=False):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   189
        """Try to find a font matching the spec"""
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   190
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   191
        for font in self._fonts:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   192
            if font.familyName == familyName:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   193
                if font.isBold == bold:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   194
                    if font.isItalic == italic:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   195
                        return font
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   196
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   197
        raise KeyError("Cannot find font %s with bold=%s, italic=%s" % (familyName, bold, italic))
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   198
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   199
    def _getCacheFileName(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   200
        """Base this on the directories...same set of directories
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   201
        should give same cache"""
3098
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
   202
        hash = md5(''.join(self._dirs)).hexdigest()
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   203
        from reportlab.lib.utils import get_rl_tempfile
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   204
        fn = get_rl_tempfile('fonts_%s.dat' % hash)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   205
        return fn
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   206
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   207
    def save(self, fileName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   208
        f = open(fileName, 'w')
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   209
        pickle.dump(self, f)
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   210
        f.close()
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   211
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   212
    def load(self, fileName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   213
        f = open(fileName, 'r')
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   214
        finder2 = pickle.load(f)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   215
        f.close()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   216
        self.__dict__.update(finder2.__dict__)
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   217
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   218
    def search(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   219
        started = time.clock()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   220
        if not self._dirs:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   221
            raise ValueError("Font search path is empty!  Please specify search directories using addDirectory or addDirectories")
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   222
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   223
        if self.useCache:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   224
            cfn = self._getCacheFileName()
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   225
            if rl_isfile(cfn):
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   226
                try:
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   227
                    self.load(cfn)
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   228
                    #print "loaded cached file with %d fonts (%s)" % (len(self._fonts), cfn)
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   229
                    return
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   230
                except:
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   231
                    pass  #pickle load failed.  Ho hum, maybe it's an old pickle.  Better rebuild it.
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   232
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   233
        from stat import ST_MTIME
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   234
        for dirName in self._dirs:
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   235
            fileNames = rl_listdir(dirName)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   236
            for fileName in fileNames:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   237
                root, ext = os.path.splitext(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   238
                if ext.lower() in EXTENSIONS:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   239
                    #it's a font
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   240
                    f = FontDescriptor()
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   241
                    f.fileName = os.path.normpath(os.path.join(dirName, fileName))
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   242
                    f.timeModified = rl_getmtime(f.fileName)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   243
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   244
                    ext = ext.lower()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   245
                    if ext[0] == '.':
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   246
                        ext = ext[1:]
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   247
                    f.typeCode = ext  #strip the dot
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   248
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   249
                    #what to do depends on type.  We only accept .pfb if we
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   250
                    #have .afm to go with it, and don't handle .otf now.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   251
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   252
                    if ext in ('otf', 'pfa'):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   253
                        self._skippedFiles.append(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   254
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   255
                    elif ext in ('ttf','ttc'):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   256
                        #parsing should check it for us
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   257
                        from reportlab.pdfbase.ttfonts import TTFontFile, TTFError
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   258
                        try:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   259
                            font = TTFontFile(fileName,validate=self.validate)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   260
                        except TTFError:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   261
                            self._badFiles.append(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   262
                            continue
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   263
                        f.name = font.name
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   264
                        f.fullName = font.fullName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   265
                        f.styleName = font.styleName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   266
                        f.familyName = font.familyName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   267
                        f.isBold = (FF_FORCEBOLD == FF_FORCEBOLD & font.flags)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   268
                        f.isItalic = (FF_ITALIC == FF_ITALIC & font.flags)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   269
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   270
                    elif ext == 'pfb':
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   271
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   272
                        # type 1; we need an AFM file or have to skip.
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   273
                        if rl_isfile(os.path.join(dirName, root + '.afm')):
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   274
                            f.metricsFileName = os.path.normpath(os.path.join(dirName, root + '.afm'))
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   275
                        elif rl_isfile(os.path.join(dirName, root + '.AFM')):
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   276
                            f.metricsFileName = os.path.normpath(os.path.join(dirName, root + '.AFM'))
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   277
                        else:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   278
                            self._skippedFiles.append(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   279
                            continue
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   280
                        from reportlab.pdfbase.pdfmetrics import parseAFMFile
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   281
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   282
                        (info, glyphs) = parseAFMFile(f.metricsFileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   283
                        f.name = info['FontName']
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   284
                        f.fullName = info.get('FullName', f.name)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   285
                        f.familyName = info.get('FamilyName', None)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   286
                        f.isItalic = (float(info.get('ItalicAngle', 0)) > 0.0)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   287
                        #if the weight has the word bold, deem it bold
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   288
                        f.isBold = ('bold' in info.get('Weight','').lower())
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   289
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   290
                    self._fonts.append(f)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   291
        if self.useCache:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   292
            self.save(cfn)
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   293
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   294
        finished = time.clock()
2799
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   295
##        print "found %d fonts; skipped %d; bad %d.  Took %0.2f seconds" % (
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   296
##            len(self._fonts), len(self._skippedFiles), len(self._badFiles),
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   297
##            finished - started
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   298
##            )
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   299
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   300
def test():
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   301
    #windows-centric test maybe
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   302
    from reportlab import rl_config
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   303
    ff = FontFinder()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   304
    ff.useCache = True
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   305
    ff.validate = True
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   306
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   307
    import reportlab
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   308
    ff.addDirectory('C:\\windows\\fonts')
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   309
    rlFontDir = os.path.join(os.path.dirname(reportlab.__file__), 'fonts')
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   310
    ff.addDirectory(rlFontDir)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   311
    ff.search()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   312
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   313
    print('cache file name...')
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   314
    print(ff._getCacheFileName())
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   315
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   316
    print('families...')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   317
    for familyName in ff.getFamilyNames():
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   318
        print('\t%s' % familyName)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   319
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   320
    print()
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   321
    print('fonts called Vera:', end=' ')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   322
    for font in ff.getFontsInFamily('Bitstream Vera Sans'):
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   323
        print('\t%s' % font.name)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   324
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   325
    print()
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   326
    print('Bold fonts\n\t')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   327
    for font in ff.getFontsWithAttributes(isBold=True, isItalic=False):
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   328
        print(font.fullName, end=' ')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   329
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   330
    print('family report')
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   331
    print(ff.getFamilyXmlReport())
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   332
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   333
if __name__=='__main__':
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   334
    test()