Blame lib/dbtexmf/dblatex/xetex/fcmanager.py.enable-python3

Packit 5164a5
# 
Packit 5164a5
# Slow interface to fontconfig for Dblatex, that only parses some commmand line
Packit 5164a5
# output to store the fonts available on the system and their characteristics.
Packit 5164a5
#
Packit 5164a5
# An efficient solution should use some python bindings to directly call the
Packit 5164a5
# C fontconfig library.
Packit 5164a5
#
Packit 5164a5
import logging
Packit 5164a5
from subprocess import Popen, PIPE
Packit 5164a5
Packit 5164a5
def execute(cmd):
Packit 5164a5
    p = Popen(cmd, stdout=PIPE)
Packit 5164a5
    data = p.communicate()[0]
Packit 5164a5
    rc = p.wait()
Packit 5164a5
    if rc != 0:
Packit 5164a5
        raise OSError("'%s' failed (%d)" % (" ".join(cmd), rc))
Packit 5164a5
    return data
Packit 5164a5
Packit 5164a5
Packit 5164a5
class FcFont:
Packit 5164a5
    """
Packit 5164a5
    Font Object with properties filled with the fc-match command output.
Packit 5164a5
    """
Packit 5164a5
    def __init__(self, fontnames, partial=False):
Packit 5164a5
        self.log = logging.getLogger("dblatex")
Packit 5164a5
        self.name = fontnames[0]
Packit 5164a5
        self.aliases = fontnames[1:]
Packit 5164a5
        self._completed = False
Packit 5164a5
        if not(partial):
Packit 5164a5
            self.complete()
Packit 5164a5
Packit 5164a5
    def complete(self):
Packit 5164a5
        if not(self._completed):
Packit 5164a5
            d = execute(["fc-match", "--verbose", self.name])
Packit 5164a5
            d = d.strip()
Packit 5164a5
            self._build_attr_from(d)
Packit 5164a5
            self._completed = True
Packit 5164a5
Packit 5164a5
    def _build_attr_from(self, data):
Packit 5164a5
        ninfos = self._splitinfos(data)
Packit 5164a5
Packit 5164a5
        # Remove the first line
Packit 5164a5
        ninfos[0] = ninfos[0].split("\n")[1]
Packit 5164a5
        for i in ninfos:
Packit 5164a5
            if i: self._buildattr(i)
Packit 5164a5
        
Packit 5164a5
        # Check the consistency
Packit 5164a5
        if self.family != self.name.replace("\-", "-"):
Packit 5164a5
            raise ValueError("Unknown font '%s' vs '%s'" % (self.name,
Packit 5164a5
            self.family))
Packit 5164a5
Packit 5164a5
    def _splitinfos(self, data):
Packit 5164a5
        ninfos = [data]
Packit 5164a5
        for sep in ("(s)", "(w)", "(=)"):
Packit 5164a5
            infos = ninfos
Packit 5164a5
            ninfos = []
Packit 5164a5
            for i in infos:
Packit 5164a5
                ni = i.split(sep)
Packit 5164a5
                ninfos += ni
Packit 5164a5
        return ninfos
Packit 5164a5
Packit 5164a5
    def _buildattr(self, infos):
Packit 5164a5
        """
Packit 5164a5
        Parse things like:
Packit 5164a5
           'fullname: "Mukti"(s)
Packit 5164a5
            fullnamelang: "en"(s)
Packit 5164a5
            slant: 0(i)(s)
Packit 5164a5
            weight: 80(i)(s)
Packit 5164a5
            width: 100(i)(s)
Packit 5164a5
            size: 12(f)(s)'
Packit 5164a5
        """
Packit 5164a5
        try:
Packit 5164a5
            attrname, attrdata = infos.split(":", 1)
Packit 5164a5
        except:
Packit 5164a5
            # Skip this row
Packit 5164a5
            self.log.warning("Wrong data? '%s'" % infos)
Packit 5164a5
            return
Packit 5164a5
        
Packit 5164a5
        #print infos
Packit 5164a5
        attrname = attrname.strip() # Remove \t
Packit 5164a5
        attrdata = attrdata.strip() # Remove space
Packit 5164a5
Packit 5164a5
        # Specific case
Packit 5164a5
        if attrname == "charset":
Packit 5164a5
            self._build_charset(attrdata)
Packit 5164a5
            return
Packit 5164a5
Packit 5164a5
        # Get the data type
Packit 5164a5
        if (not(attrdata) or (attrdata[0] == '"' and attrdata[-1] == '"')):
Packit 5164a5
            setattr(self, attrname, attrdata.strip('"'))
Packit 5164a5
            return
Packit 5164a5
        
Packit 5164a5
        if (attrdata.endswith("(i)")):
Packit 5164a5
            setattr(self, attrname, int(attrdata.strip("(i)")))
Packit 5164a5
            return
Packit 5164a5
Packit 5164a5
        if (attrdata.endswith("(f)")):
Packit 5164a5
            setattr(self, attrname, float(attrdata.strip("(f)")))
Packit 5164a5
            return
Packit 5164a5
Packit 5164a5
        if (attrdata == "FcTrue"):
Packit 5164a5
            setattr(self, attrname, True)
Packit 5164a5
            return
Packit 5164a5
Packit 5164a5
        if (attrdata == "FcFalse"):
Packit 5164a5
            setattr(self, attrname, False)
Packit 5164a5
            return
Packit 5164a5
Packit 5164a5
    def _build_charset(self, charset):
Packit 5164a5
        """
Packit 5164a5
        Parse something like:
Packit 5164a5
           '0000: 00000000 ffffffff ffffffff 7fffffff 00000000 00002001 00800000 00800000
Packit 5164a5
            0009: 00000000 00000000 00000000 00000030 fff99fee f3c5fdff b080399f 07ffffcf
Packit 5164a5
            0020: 30003000 00000000 00000010 00000000 00000000 00001000 00000000 00000000
Packit 5164a5
            0025: 00000000 00000000 00000000 00000000 00000000 00000000 00001000 00000000'
Packit 5164a5
        """
Packit 5164a5
        self.charsetstr = charset
Packit 5164a5
        self.charset = []
Packit 5164a5
        lines = charset.split("\n")
Packit 5164a5
        for l in lines:
Packit 5164a5
            umajor, row = l.strip().split(":", 1)
Packit 5164a5
            int32s = row.split()
Packit 5164a5
            p = 0
Packit 5164a5
            for w in int32s:
Packit 5164a5
                #print "=> %s" % w
Packit 5164a5
                v = int(w, 16)
Packit 5164a5
                for i in range(0, 32):
Packit 5164a5
                    m = 1 << i
Packit 5164a5
                    #m = 0x80000000 >> i
Packit 5164a5
                    if (m & v):
Packit 5164a5
                        uchar = umajor + "%02X" % (p + i)
Packit 5164a5
                        #print uchar
Packit 5164a5
                        self.charset.append(int(uchar, 16))
Packit 5164a5
                p += 32
Packit 5164a5
Packit 5164a5
    def remove_char(self, char):
Packit 5164a5
        try:
Packit 5164a5
            self.charset.remove(char)
Packit 5164a5
        except:
Packit 5164a5
            pass
Packit 5164a5
Packit 5164a5
    def has_char(self, char):
Packit 5164a5
        #print self.family, char, self.charset
Packit 5164a5
        return (ord(char) in self.charset)
Packit 5164a5
Packit 5164a5
Packit 5164a5
class FcManager:
Packit 5164a5
    """
Packit 5164a5
    Collect all the fonts available in the system. The building can be partial,
Packit 5164a5
    i.e. the font objects can be partially created, and updated later (when
Packit 5164a5
    used).
Packit 5164a5
Packit 5164a5
    The class tries to build three ordered list of fonts, one per standard
Packit 5164a5
    generic font family:
Packit 5164a5
    - Serif      : main / body font
Packit 5164a5
    - Sans-serif : used to render sans-serif forms
Packit 5164a5
    - Monospace  : used to render verbatim / monospace characters
Packit 5164a5
    """
Packit 5164a5
    def __init__(self):
Packit 5164a5
        self.log = logging.getLogger("dblatex")
Packit 5164a5
        self.fonts = {}
Packit 5164a5
        self.fonts_family = {}
Packit 5164a5
Packit 5164a5
    def get_font(self, fontname):
Packit 5164a5
        font = self.fonts.get(fontname)
Packit 5164a5
        if font:
Packit 5164a5
            font.complete()
Packit 5164a5
        return font
Packit 5164a5
Packit 5164a5
    def get_font_handling(self, char, all=False, family_type=""):
Packit 5164a5
        if not(family_type):
Packit 5164a5
            font_family = self.fonts.values()
Packit 5164a5
        else:
Packit 5164a5
            font_family = self.fonts_family.get(family_type, None)
Packit 5164a5
        
Packit 5164a5
        if not(font_family):
Packit 5164a5
            return []
Packit 5164a5
Packit 5164a5
        fonts = self.get_font_handling_from(font_family, char, all=all)
Packit 5164a5
        return fonts
Packit 5164a5
Packit 5164a5
    def get_font_handling_from(self, fontlist, char, all=False):
Packit 5164a5
        fonts = []
Packit 5164a5
        # Brutal method to get something...
Packit 5164a5
        for f in fontlist:
Packit 5164a5
            f.complete()
Packit 5164a5
            if f.has_char(char):
Packit 5164a5
                if all:
Packit 5164a5
                    fonts.append(f)
Packit 5164a5
                else:
Packit 5164a5
                    return f
Packit 5164a5
        return fonts
Packit 5164a5
Packit 5164a5
    def build_fonts(self, partial=False):
Packit 5164a5
        self.build_fonts_all(partial=partial)
Packit 5164a5
        self.build_fonts_family("serif")
Packit 5164a5
        self.build_fonts_family("sans-serif")
Packit 5164a5
        self.build_fonts_family("monospace")
Packit 5164a5
Packit 5164a5
    def build_fonts_all(self, partial=False):
Packit 5164a5
        # Grab all the fonts installed on the system
Packit 5164a5
        d = execute(["fc-list"])
Packit 5164a5
        fonts = d.strip().split("\n")
Packit 5164a5
        for f in fonts:
Packit 5164a5
            fontnames = f.split(":")[0].split(",")
Packit 5164a5
            mainname = fontnames[0]
Packit 5164a5
            if not(mainname):
Packit 5164a5
                continue
Packit 5164a5
            if self.fonts.get(mainname):
Packit 5164a5
                self.log.debug("'%s': duplicated" % mainname)
Packit 5164a5
                continue
Packit 5164a5
Packit 5164a5
            #print fontnames
Packit 5164a5
            font = FcFont(fontnames, partial=partial)
Packit 5164a5
            self.fonts[mainname] = font
Packit 5164a5
Packit 5164a5
    def build_fonts_family(self, family_type):
Packit 5164a5
        # Create a sorted list matching a generic family
Packit 5164a5
        # Use --sort to have only fonts completing unicode range
Packit 5164a5
        font_family = []
Packit 5164a5
        self.fonts_family[family_type] = font_family
Packit 5164a5
        d = execute(["fc-match", "--sort", family_type, "family"])
Packit 5164a5
        fonts = d.strip().split("\n")
Packit 5164a5
        for f in fonts:
Packit 5164a5
            font = self.fonts.get(f)
Packit 5164a5
            if not(font in font_family):
Packit 5164a5
                font_family.append(font)
Packit 5164a5
        #print family_type
Packit 5164a5
        #print font_family
Packit 5164a5