#
# The Python Imaging Library.
# $Id$
#
# PIL raster font management
#
# History:
# 1996-08-07 fl   created (experimental)
# 1997-08-25 fl   minor adjustments to handle fonts from pilfont 0.3
# 1999-02-06 fl   rewrote most font management stuff in C
# 1999-03-17 fl   take pth files into account in load_path (from Richard Jones)
# 2001-02-17 fl   added freetype support
# 2001-05-09 fl   added TransposedFont wrapper class
# 2002-03-04 fl   make sure we have a "L" or "1" font
# 2002-12-04 fl   skip non-directory entries in the system path
# 2003-04-29 fl   add embedded default font
# 2003-09-27 fl   added support for truetype charmap encodings
#
# Todo:
# Adapt to PILFONT2 format (16-bit fonts, compressed, single file)
#
# Copyright (c) 1997-2003 by Secret Labs AB
# Copyright (c) 1996-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#

import Image
import os, string, sys

class _imagingft_not_installed:
    # module placeholder
    def __getattr__(self, id):
        raise ImportError("The _imagingft C module is not installed")

try:
    import _imagingft
    core = _imagingft
    del _imagingft
except ImportError:
    core = _imagingft_not_installed()

# FIXME: add support for pilfont2 format (see FontFile.py)

# --------------------------------------------------------------------
# Font metrics format:
#       "PILfont" LF
#       fontdescriptor LF
#       (optional) key=value... LF
#       "DATA" LF
#       binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox)
#
# To place a character, cut out srcbox and paste at dstbox,
# relative to the character position.  Then move the character
# position according to dx, dy.
# --------------------------------------------------------------------

##
# The <b>ImageFont</b> module defines a class with the same name.
# Instances of this class store bitmap fonts, and are used with the
# <b>text</b> method of the <b>ImageDraw</b> class.
# <p>
# PIL uses it's own font file format to store bitmap fonts. You can
# use the <b>pilfont</b> utility to convert BDF and PCF font
# descriptors (X window font formats) to this format.
# <p>
# Starting with version 1.1.4, PIL can be configured to support
# TrueType and OpenType fonts.  For earlier version, TrueType
# support is only available as part of the imToolkit package
#
# @see ImageDraw#ImageDraw.text
# @see pilfont

class ImageFont:
    "PIL font wrapper"

    def _load_pilfont(self, filename):

        file = open(filename, "rb")

        for ext in (".png", ".gif", ".pbm"):
            try:
                fullname = os.path.splitext(filename)[0] + ext
                image = Image.open(fullname)
            except:
                pass
            else:
                if image and image.mode in ("1", "L"):
                    break
        else:
            raise IOError("cannot find glyph data file")

        self.file = fullname

        return self._load_pilfont_data(file, image)

    def _load_pilfont_data(self, file, image):

        # read PILfont header
        if file.readline() != "PILfont\n":
            raise SyntaxError("Not a PILfont file")
        d = string.split(file.readline(), ";")
        self.info = [] # FIXME: should be a dictionary
        while True:
            s = file.readline()
            if not s or s == "DATA\n":
                break
            self.info.append(s)

        # read PILfont metrics
        data = file.read(256*20)

        # check image
        if image.mode not in ("1", "L"):
            raise TypeError("invalid font image mode")

        image.load()

        self.font = Image.core.font(image.im, data)

        # delegate critical operations to internal type
        self.getsize = self.font.getsize
        self.getmask = self.font.getmask

##
# Wrapper for FreeType fonts.  Application code should use the
# <b>truetype</b> factory function to create font objects.

class FreeTypeFont:
    "FreeType font wrapper (requires _imagingft service)"

    def __init__(self, file, size, index=0, encoding=""):
        # FIXME: use service provider instead
        self.font = core.getfont(file, size, index, encoding)

    def getname(self):
        return self.font.family, self.font.style

    def getmetrics(self):
        return self.font.ascent, self.font.descent

    def getsize(self, text):
        return self.font.getsize(text)[0]

    def getmask(self, text, mode=""):
        return self.getmask2(text, mode)[0]

    def getmask2(self, text, mode="", fill=Image.core.fill):
        size, offset = self.font.getsize(text)
        im = fill("L", size, 0)
        self.font.render(text, im.id, mode=="1")
        return im, offset

##
# Wrapper that creates a transposed font from any existing font
# object.
#
# @param font A font object.
# @param orientation An optional orientation.  If given, this should
#     be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
#     Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270.

class TransposedFont:
    "Wrapper for writing rotated or mirrored text"

    def __init__(self, font, orientation=None):
        self.font = font
        self.orientation = orientation # any 'transpose' argument, or None

    def getsize(self, text):
        w, h = self.font.getsize(text)
        if self.orientation in (Image.ROTATE_90, Image.ROTATE_270):
            return h, w
        return w, h

    def getmask(self, text, mode=""):
        im = self.font.getmask(text, mode)
        if self.orientation is not None:
            return im.transpose(self.orientation)
        return im

##
# Load font file.  This function loads a font object from the given
# bitmap font file, and returns the corresponding font object.
#
# @param filename Name of font file.
# @return A font object.
# @exception IOError If the file could not be read.

def load(filename):
    "Load a font file."
    f = ImageFont()
    f._load_pilfont(filename)
    return f

##
# Load a TrueType or OpenType font file, and create a font object.
# This function loads a font object from the given file, and creates
# a font object for a font of the given size.
# <p>
# This function requires the _imagingft service.
#
# @param filename A truetype font file.  Under Windows, if the file
#    is not found in this filename, the loader also looks in Windows
#    <b>fonts</b> directory
# @param size The requested size, in points.
# @param index Which font face to load (default is first available face).
# @param encoding Which font encoding to use (default is Unicode).  Common
#    encodings are "unic" (Unicode), "symb" (Microsoft Symbol), "ADOB"
#    (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman).
#    See the FreeType documentation for more information.
# @return A font object.
# @exception IOError If the file could not be read.

def truetype(filename, size, index=0, encoding=""):
    "Load a truetype font file."
    try:
        return FreeTypeFont(filename, size, index, encoding)
    except IOError:
        if sys.platform == "win32":
            # check the windows font repository
            # NOTE: must use uppercase WINDIR, to work around bugs in
            # 1.5.2's os.environ.get()
            windir = os.environ.get("WINDIR")
            if windir:
                filename = os.path.join(windir, "fonts", filename)
                return FreeTypeFont(filename, size, index, encoding)
        raise

##
# Load font file.  Same as load, but searches for a bitmap font along
# the Python path.
#
# @param filename Name of font file.
# @return A font object.
# @exception IOError If the file could not be read.
# @see #load

def load_path(filename):
    "Load a font file, searching along the Python path."
    for dir in sys.path:
        if Image.isDirectory(dir):
            try:
                return load(os.path.join(dir, filename))
            except IOError:
                pass
    raise IOError("cannot find font file")

##
# Load a (probably rather ugly) default font.
#
# @return A font object.

def load_default():
    "Load a default font."
    from StringIO import StringIO
    import base64
    f = ImageFont()
    f._load_pilfont_data(
         # courB08
         StringIO(base64.decodestring('''
[encoded data, 6930 characters]
+QAGAAIAzgAKANUAEw==
''')), Image.open(StringIO(base64.decodestring('''
iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F
IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA
Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791
NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx
in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9
SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY
AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt
y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG
ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY
lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H
/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3
AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47
c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/
/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw
pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv
oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR
evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA
AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v//
Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR
w7IkEbzhVQAAAABJRU5ErkJggg==
'''))))
    return f

if __name__ == "__main__":
    # create font data chunk for embedding
    import base64, os, sys
    font = "../Images/courB08"
    print "    f._load_pilfont_data("
    print "         # %s" % os.path.basename(font)
    print "         StringIO(base64.decodestring('''"
    base64.encode(open(font + ".pil", "rb"), sys.stdout)
    print "''')), Image.open(StringIO(base64.decodestring('''"
    base64.encode(open(font + ".pbm", "rb"), sys.stdout)
    print "'''))))"
