#!/usr/bin/env python # -*- coding: utf-8 -*- # Time-stamp: <2008-06-23 22:21:26 ah> """ Provide an encoder for a font specification configuration: the encoder is fed with Unicode characters one by one and determines the needed font switches between the preceding and the current character. """ import sys import re import xml.dom.minidom from fontspec import UnicodeInterval from fsconfig import FontSpecConfig class FontSpecEncoder: """ Encoder working with font specifications: it is fed with Unicode characters one by one and it inserts the needed font switches between the preceding and the current character. """ def __init__(self, configuration): """ Create a font specification encoder from the specified configuration file (file name or file-like object). """ self._conf = FontSpecConfig(configuration) self._cur_fontspec = None self._ref_stack = [self._conf.default_fontspec] def reset(self): # Restart from the default fontspec to avoid a useless 'enter' from None self._cur_fontspec = self._conf.default_fontspec self._ref_stack = [self._conf.default_fontspec] def _switch_to(self, fontspec): """ Insert the transition string, according to the newly selected fontspec and the previously selected fontspec """ s = "" # If the font hasn't changed, just insert optional inter-char material if fontspec == self._cur_fontspec: return fontspec.interchar() # A new font is selected, so exit from current font stream if self._cur_fontspec: s += self._cur_fontspec.exit() # Enter into the current font stream self._cur_fontspec = fontspec s += fontspec.enter() return s def _encode(self, char): """ Select the fontspec matching the specified , and switch to this font as current font. The principle to find out the fontspec is: - to find from the current font level a matching font (the current font leaf or the direct font children) - if no font is found try with the parent font, and so on, up to the default root font (that must exist). """ fontspec = self._cur_fontspec or self._conf.default_fontspec print >>sys.stderr, "Current:", fontspec.id fontspec = fontspec.match(char) while not(fontspec): leaf = self._ref_stack.pop() fontspec = self._ref_stack[-1].match(char, excluded=leaf) if fontspec != self._ref_stack[-1]: self._ref_stack.append(fontspec) return self._switch_to(fontspec) def ignorechars(self, charset): "Characters to ignore in font selection (maintain the current one)" intervals = [ UnicodeInterval().from_char(c) for c in charset ] self._conf.default_fontspec.add_ignored(intervals) def encode(self, char): """ Return a string consisting of the specified character prepended by all necessary font switching commands. """ return (self._encode(char), char) def stop(self): """ Cleanly exit from the current fontspec """ if self._cur_fontspec: s = self._cur_fontspec.exit() self._cur_fontspec = None return s