src/reportlab/lib/fontfinder.py
author robin <robin@reportlab.com>
Tue, 07 Mar 2017 10:00:34 +0000
changeset 4330 617ffa6bbdc8
parent 4252 fe660f227cac
child 4377 da36c2d6e2d2
permissions -rw-r--r--
changes for release 3.4.0
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
4330
617ffa6bbdc8 changes for release 3.4.0
robin <robin@reportlab.com>
parents: 4252
diff changeset
     1
#Copyright ReportLab Europe Ltd. 2000-2017
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
     2
#see license.txt for license details
4252
fe660f227cac changes for release 3.3.0
robin
parents: 4181
diff changeset
     3
__version__='3.3.0'
2781
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
"""
4181
0a8d6687fde6 reportlab.lib.utils imports cPickle or pickle
robin
parents: 3884
diff changeset
    61
import sys, time, os, tempfile
0a8d6687fde6 reportlab.lib.utils imports cPickle or pickle
robin
parents: 3884
diff changeset
    62
from reportlab.lib.utils import pickle
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    63
from xml.sax.saxutils import quoteattr
3098
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
    64
try:
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
    65
    from hashlib import md5
3126
32755aaac077 fontfinder.py: tighten exception handling for md5
rgbecker
parents: 3098
diff changeset
    66
except ImportError:
3098
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
    67
    from md5 import md5
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    68
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    69
EXTENSIONS = ['.ttf','.ttc','.otf','.pfb','.pfa']
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    70
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    71
# PDF font flags (see PDF Reference Guide table 5.19)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    72
FF_FIXED        = 1 <<  1-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    73
FF_SERIF        = 1 <<  2-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    74
FF_SYMBOLIC     = 1 <<  3-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    75
FF_SCRIPT       = 1 <<  4-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    76
FF_NONSYMBOLIC  = 1 <<  6-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    77
FF_ITALIC       = 1 <<  7-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    78
FF_ALLCAP       = 1 << 17-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    79
FF_SMALLCAP     = 1 << 18-1
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    80
FF_FORCEBOLD    = 1 << 19-1
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
    81
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    82
class FontDescriptor:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    83
    """This is a short descriptive record about a font.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    84
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    85
    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
    86
    """
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    87
    def __init__(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    88
        self.name = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    89
        self.fullName = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    90
        self.familyName = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    91
        self.styleName = None
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    92
        self.isBold = False   #true if it's somehow bold
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    93
        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
    94
        self.isFixedPitch = False
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    95
        self.isSymbolic = False   #false for Dingbats, Symbols etc.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    96
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    97
        self.typeCode = None   #normally the extension minus the dot
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    98
        self.fileName = None  #full path to where we found it.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
    99
        self.metricsFileName = None  #defined only for type='type1pc', or 'type1mac'
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   100
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   101
        self.timeModified = 0
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   102
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   103
    def __repr__(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   104
        return "FontDescriptor(%s)" % self.name
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   105
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   106
    def getTag(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   107
        "Return an XML tag representation"
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   108
        attrs = []
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
   109
        for k, v in self.__dict__.items():
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   110
            if k not in ['timeModified']:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   111
                if v:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   112
                    attrs.append('%s=%s' % (k, quoteattr(str(v))))
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   113
        return '<font ' + ' '.join(attrs) + '/>'
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   114
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   115
from reportlab.lib.utils import rl_isdir, rl_isfile, rl_listdir, rl_getmtime
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   116
class FontFinder:
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   117
    def __init__(self, dirs=[], useCache=True, validate=False):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   118
        self.useCache = useCache
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   119
        self.validate = validate
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   120
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   121
        self._dirs = set(dirs)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   122
        self._fonts = []
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   123
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   124
        self._skippedFiles = [] #list of filenames we did not handle
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   125
        self._badFiles = []  #list of filenames we rejected
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   126
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   127
        self._fontsByName = {}
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   128
        self._fontsByFamily = {}
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   129
        self._fontsByFamilyBoldItalic = {}   #indexed by bold, italic
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   130
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   131
    def addDirectory(self, dirName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   132
        #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
   133
        #be picked up?  might need reversing
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   134
        if rl_isdir(dirName):
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   135
            self._dirs.add(dirName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   136
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   137
    def addDirectories(self, dirNames):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   138
        for dirName in dirNames:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   139
            self.addDirectory(dirName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   140
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   141
    def getFamilyNames(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   142
        "Returns a list of the distinct font families found"
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   143
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   144
        if not self._fontsByFamily:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   145
            fonts = self._fonts
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   146
            for font in fonts:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   147
                fam = font.familyName
3326
ce725978d11c Initial Python3 compatibility fixes
damian
parents: 3126
diff changeset
   148
                if fam in self._fontsByFamily:
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   149
                    self._fontsByFamily[fam].append(font)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   150
                else:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   151
                    self._fontsByFamily[fam] = [font]
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   152
        names = list(self._fontsByFamily.keys())
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   153
        names.sort()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   154
        return names
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   155
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   156
    def getFontsInFamily(self, familyName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   157
        "Return list of all font objects with this family name"
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   158
        return self._fontsByFamily.get(familyName,[])
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   159
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   160
    def getFamilyXmlReport(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   161
        """Reports on all families found as XML.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   162
        """
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   163
        lines = []
2799
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   164
        lines.append('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   165
        lines.append("<font_families>")
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   166
        for dirName in self._dirs:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   167
            lines.append("    <directory name=%s/>" % quoteattr(dirName))
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   168
        for familyName in self.getFamilyNames():
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   169
            if familyName:  #skip null case
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   170
                lines.append('    <family name=%s>' % quoteattr(familyName))
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   171
                for font in self.getFontsInFamily(familyName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   172
                    lines.append('        ' + font.getTag())
2799
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   173
                lines.append('    </family>')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   174
        lines.append("</font_families>")
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   175
        return '\n'.join(lines)
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   176
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   177
    def getFontsWithAttributes(self, **kwds):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   178
        """This is a general lightweight search."""
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   179
        selected = []
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   180
        for font in self._fonts:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   181
            OK = True
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
   182
            for k, v in kwds.items():
3326
ce725978d11c Initial Python3 compatibility fixes
damian
parents: 3126
diff changeset
   183
                if getattr(font, k, None) != v:
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   184
                    OK = False
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   185
            if OK:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   186
                selected.append(font)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   187
        return selected
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   188
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   189
    def getFont(self, familyName, bold=False, italic=False):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   190
        """Try to find a font matching the spec"""
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   191
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   192
        for font in self._fonts:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   193
            if font.familyName == familyName:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   194
                if font.isBold == bold:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   195
                    if font.isItalic == italic:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   196
                        return font
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   197
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   198
        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
   199
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   200
    def _getCacheFileName(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   201
        """Base this on the directories...same set of directories
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   202
        should give same cache"""
3098
318ba6cb2cb1 reportlab: fix md5 usage in pdfencrypt.py & fontfinder.py
rgbecker
parents: 3029
diff changeset
   203
        hash = md5(''.join(self._dirs)).hexdigest()
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   204
        from reportlab.lib.utils import get_rl_tempfile
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   205
        fn = get_rl_tempfile('fonts_%s.dat' % hash)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   206
        return fn
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   207
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   208
    def save(self, fileName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   209
        f = open(fileName, 'w')
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   210
        pickle.dump(self, f)
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   211
        f.close()
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   212
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   213
    def load(self, fileName):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   214
        f = open(fileName, 'r')
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   215
        finder2 = pickle.load(f)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   216
        f.close()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   217
        self.__dict__.update(finder2.__dict__)
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   218
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   219
    def search(self):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   220
        started = time.clock()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   221
        if not self._dirs:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   222
            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
   223
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   224
        if self.useCache:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   225
            cfn = self._getCacheFileName()
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   226
            if rl_isfile(cfn):
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   227
                try:
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   228
                    self.load(cfn)
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   229
                    #print "loaded cached file with %d fonts (%s)" % (len(self._fonts), cfn)
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   230
                    return
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   231
                except:
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   232
                    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
   233
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   234
        from stat import ST_MTIME
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   235
        for dirName in self._dirs:
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   236
            fileNames = rl_listdir(dirName)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   237
            for fileName in fileNames:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   238
                root, ext = os.path.splitext(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   239
                if ext.lower() in EXTENSIONS:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   240
                    #it's a font
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   241
                    f = FontDescriptor()
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   242
                    f.fileName = os.path.normpath(os.path.join(dirName, fileName))
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   243
                    f.timeModified = rl_getmtime(f.fileName)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   244
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   245
                    ext = ext.lower()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   246
                    if ext[0] == '.':
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   247
                        ext = ext[1:]
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   248
                    f.typeCode = ext  #strip the dot
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   249
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   250
                    #what to do depends on type.  We only accept .pfb if we
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   251
                    #have .afm to go with it, and don't handle .otf now.
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   252
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   253
                    if ext in ('otf', 'pfa'):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   254
                        self._skippedFiles.append(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   255
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   256
                    elif ext in ('ttf','ttc'):
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   257
                        #parsing should check it for us
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   258
                        from reportlab.pdfbase.ttfonts import TTFontFile, TTFError
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   259
                        try:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   260
                            font = TTFontFile(fileName,validate=self.validate)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   261
                        except TTFError:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   262
                            self._badFiles.append(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   263
                            continue
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   264
                        f.name = font.name
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   265
                        f.fullName = font.fullName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   266
                        f.styleName = font.styleName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   267
                        f.familyName = font.familyName
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   268
                        f.isBold = (FF_FORCEBOLD == FF_FORCEBOLD & font.flags)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   269
                        f.isItalic = (FF_ITALIC == FF_ITALIC & font.flags)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   270
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   271
                    elif ext == 'pfb':
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   272
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   273
                        # 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
   274
                        if rl_isfile(os.path.join(dirName, root + '.afm')):
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   275
                            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
   276
                        elif rl_isfile(os.path.join(dirName, root + '.AFM')):
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   277
                            f.metricsFileName = os.path.normpath(os.path.join(dirName, root + '.AFM'))
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   278
                        else:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   279
                            self._skippedFiles.append(fileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   280
                            continue
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   281
                        from reportlab.pdfbase.pdfmetrics import parseAFMFile
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   282
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   283
                        (info, glyphs) = parseAFMFile(f.metricsFileName)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   284
                        f.name = info['FontName']
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   285
                        f.fullName = info.get('FullName', f.name)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   286
                        f.familyName = info.get('FamilyName', None)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   287
                        f.isItalic = (float(info.get('ItalicAngle', 0)) > 0.0)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   288
                        #if the weight has the word bold, deem it bold
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   289
                        f.isBold = ('bold' in info.get('Weight','').lower())
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   290
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   291
                    self._fonts.append(f)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   292
        if self.useCache:
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   293
            self.save(cfn)
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   294
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   295
        finished = time.clock()
2799
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   296
##        print "found %d fonts; skipped %d; bad %d.  Took %0.2f seconds" % (
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   297
##            len(self._fonts), len(self._skippedFiles), len(self._badFiles),
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   298
##            finished - started
7966af7e4a40 better XML, print statement removed
andy
parents: 2794
diff changeset
   299
##            )
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   300
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   301
def test():
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   302
    #windows-centric test maybe
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   303
    from reportlab import rl_config
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   304
    ff = FontFinder()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   305
    ff.useCache = True
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   306
    ff.validate = True
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   307
2794
6539e5dc77b8 add support for fontfinder in zip distribution
rgbecker
parents: 2792
diff changeset
   308
    import reportlab
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   309
    ff.addDirectory('C:\\windows\\fonts')
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   310
    rlFontDir = os.path.join(os.path.dirname(reportlab.__file__), 'fonts')
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   311
    ff.addDirectory(rlFontDir)
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   312
    ff.search()
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   313
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   314
    print('cache file name...')
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   315
    print(ff._getCacheFileName())
2800
45bd6fff6cd9 removed print statement and normalised paths
andy
parents: 2799
diff changeset
   316
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   317
    print('families...')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   318
    for familyName in ff.getFamilyNames():
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   319
        print('\t%s' % familyName)
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   320
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   321
    print()
3884
3bc59a4c3c21 attempt to remove python 3 only print stuff
robin
parents: 3723
diff changeset
   322
    outw = sys.stdout.write
3bc59a4c3c21 attempt to remove python 3 only print stuff
robin
parents: 3723
diff changeset
   323
    outw('fonts called Vera:')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   324
    for font in ff.getFontsInFamily('Bitstream Vera Sans'):
3884
3bc59a4c3c21 attempt to remove python 3 only print stuff
robin
parents: 3723
diff changeset
   325
        outw(' %s' % font.name)
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   326
    print()
3884
3bc59a4c3c21 attempt to remove python 3 only print stuff
robin
parents: 3723
diff changeset
   327
    outw('Bold fonts\n\t')
2790
485bc544fb06 fontfinder working and has XML report
andy
parents: 2781
diff changeset
   328
    for font in ff.getFontsWithAttributes(isBold=True, isItalic=False):
3884
3bc59a4c3c21 attempt to remove python 3 only print stuff
robin
parents: 3723
diff changeset
   329
        outw(font.fullName+' ')
3bc59a4c3c21 attempt to remove python 3 only print stuff
robin
parents: 3723
diff changeset
   330
    print()
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   331
    print('family report')
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   332
    print(ff.getFamilyXmlReport())
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   333
2781
c0d7ffd3cc1e initial work on fontfinder class
andy
parents:
diff changeset
   334
if __name__=='__main__':
2792
0e165c88fa6f fontfinder: allow for missing families in getFontsInFamily
rgbecker
parents: 2790
diff changeset
   335
    test()