Blame lib/dbtexmf/core/imagedata.py.enable-python3

Packit 5164a5
import sys
Packit 5164a5
import os
Packit 5164a5
import re
Packit 5164a5
import shutil
Packit 5164a5
import logging
Packit 5164a5
import urllib
Packit 5164a5
from dbtexmf.core.error import signal_error
Packit 5164a5
from commander import CommandRunner
Packit 5164a5
Packit 5164a5
class ObjectFilter:
Packit 5164a5
    """
Packit 5164a5
    Its purpose is to select some objects from a list according to specified
Packit 5164a5
    criterions. It assumes that '*' applied to a criterion means 'any'.
Packit 5164a5
    """
Packit 5164a5
    def __init__(self):
Packit 5164a5
        pass
Packit 5164a5
Packit 5164a5
    def _re_multi_or_star(self, searched):
Packit 5164a5
        if not(searched):
Packit 5164a5
            searched = r"\w*"
Packit 5164a5
        else:
Packit 5164a5
            s = searched.split()
Packit 5164a5
            #searched = "|".join(["(?<=[/ ])%s" % p for p in s])
Packit 5164a5
            searched = "|".join(["%s" % p for p in s])
Packit 5164a5
        searched += r"|\*"
Packit 5164a5
        return "("+searched+")"
Packit 5164a5
Packit 5164a5
    def select(self, object_list, **filter_criterions):
Packit 5164a5
        for criterion, value in filter_criterions.items():
Packit 5164a5
            filter_criterions[criterion] = self._re_multi_or_star(value)
Packit 5164a5
Packit 5164a5
        founds = []
Packit 5164a5
        for obj in object_list:
Packit 5164a5
            object_criterions = obj.criterions()
Packit 5164a5
            for criterion, re_expr in filter_criterions.items():
Packit 5164a5
                data = object_criterions.get(criterion, "")
Packit 5164a5
                m = re.search(re_expr, data)
Packit 5164a5
                #print "Lookup2:", criterion, re_expr, data, not(m is None)
Packit 5164a5
                if not(m): break
Packit 5164a5
Packit 5164a5
            if m: founds.append(obj)
Packit 5164a5
            #print "Lookup2: found %d" % len(founds)
Packit 5164a5
        return founds
Packit 5164a5
Packit 5164a5
Packit 5164a5
class PoolManager:
Packit 5164a5
    def __init__(self): 
Packit 5164a5
        self._used_pool = None
Packit 5164a5
        self._pending_pools = []
Packit 5164a5
    
Packit 5164a5
    def set_pool(self, pool):
Packit 5164a5
        self._used_pool = pool
Packit 5164a5
        for p in self._pending_pools:
Packit 5164a5
            pool.preprend(p)
Packit 5164a5
        self._pending_pools = []
Packit 5164a5
    
Packit 5164a5
    def prepend_pool(self, pool):
Packit 5164a5
        if self._used_pool:
Packit 5164a5
            self._used_pool.prepend(pool)
Packit 5164a5
        else:
Packit 5164a5
            self._pending_pools.append(pool)
Packit 5164a5
Packit 5164a5
class ImageSetup:
Packit 5164a5
    """
Packit 5164a5
    Central imagedata setup, filled by default object configurations and
Packit 5164a5
    by the XML configuration
Packit 5164a5
    """
Packit 5164a5
    def __init__(self):
Packit 5164a5
        self.converter_pool = PoolManager()
Packit 5164a5
        self.format_pool = PoolManager()
Packit 5164a5
Packit 5164a5
_image_setup = ImageSetup()
Packit 5164a5
    
Packit 5164a5
def image_setup():
Packit 5164a5
    global _image_setup
Packit 5164a5
    return _image_setup
Packit 5164a5
Packit 5164a5
Packit 5164a5
#
Packit 5164a5
# Objects to convert an image format to another. Actually use the underlying
Packit 5164a5
# tools.
Packit 5164a5
#
Packit 5164a5
class ImageConverter:
Packit 5164a5
    _log = logging.getLogger("dblatex")
Packit 5164a5
Packit 5164a5
    def __init__(self, imgsrc, imgdst="", docformat="", backend=""):
Packit 5164a5
        self.imgsrc = imgsrc
Packit 5164a5
        self.imgdst = imgdst or "*"
Packit 5164a5
        self.docformat = docformat or "*"
Packit 5164a5
        self.backend = backend or "*"
Packit 5164a5
        self.command = CommandRunner(log=self._log)
Packit 5164a5
Packit 5164a5
    def criterions(self):
Packit 5164a5
        return { "imgsrc": self.imgsrc,
Packit 5164a5
                 "imgdst": self.imgdst,
Packit 5164a5
                 "docformat": self.docformat,
Packit 5164a5
                 "backend": self.backend }
Packit 5164a5
Packit 5164a5
    def add_command(self, *args, **kwargs):
Packit 5164a5
        self.command.add_command(*args, **kwargs)
Packit 5164a5
Packit 5164a5
    def convert(self, input, output, format, doexec=1):
Packit 5164a5
        rc = self.command.run(kw={"input": input, "output": output,
Packit 5164a5
                                  "dst": format})
Packit 5164a5
        if rc != 0: signal_error(self, "")
Packit 5164a5
Packit 5164a5
class ImageConverterPool:
Packit 5164a5
    def __init__(self):
Packit 5164a5
        self.converters = []
Packit 5164a5
        self._filter = ObjectFilter()
Packit 5164a5
Packit 5164a5
    def add_converter(self, converter):
Packit 5164a5
        self.converters.append(converter)
Packit 5164a5
Packit 5164a5
    def extend(self, other):
Packit 5164a5
        self.converters.extend(other.converters)
Packit 5164a5
Packit 5164a5
    def prepend(self, other):
Packit 5164a5
        self.converters = other.converters + self.converters
Packit 5164a5
Packit 5164a5
    def get_converters(self, imgsrc="", imgdst="", docformat="", backend=""):
Packit 5164a5
        founds = self._filter.select(self.converters,
Packit 5164a5
                                     imgsrc=imgsrc,
Packit 5164a5
                                     imgdst=imgdst,
Packit 5164a5
                                     docformat=docformat,
Packit 5164a5
                                     backend=backend)
Packit 5164a5
        return founds
Packit 5164a5
Packit 5164a5
Packit 5164a5
class ImageConverters(ImageConverterPool):
Packit 5164a5
    def __init__(self):
Packit 5164a5
        ImageConverterPool.__init__(self)
Packit 5164a5
        # Default setup
Packit 5164a5
        self.add_converter(GifConverter("gif"))
Packit 5164a5
        self.add_converter(EpsConverter("eps", "pdf"))
Packit 5164a5
        self.add_converter(EpsConverter("eps", "png"))
Packit 5164a5
        self.add_converter(FigConverter("fig", "pdf"))
Packit 5164a5
        self.add_converter(FigConverter("fig", "png"))
Packit 5164a5
        self.add_converter(SvgConverter("svg"))
Packit 5164a5
Packit 5164a5
        # Register as main pool
Packit 5164a5
        image_setup().converter_pool.set_pool(self)
Packit 5164a5
Packit 5164a5
Packit 5164a5
class GifConverter(ImageConverter):
Packit 5164a5
    def __init__(self, imgsrc, imgdst="", docformat="", backend=""):
Packit 5164a5
        ImageConverter.__init__(self, imgsrc="gif bmp", imgdst="*")
Packit 5164a5
        self.add_command(["convert", "%(input)s", "%(output)s"])
Packit 5164a5
Packit 5164a5
class EpsConverter(ImageConverter):
Packit 5164a5
    def __init__(self, imgsrc, imgdst="", docformat="", backend=""):
Packit 5164a5
        ImageConverter.__init__(self, imgsrc="eps", imgdst=imgdst)
Packit 5164a5
        if imgdst == "pdf":
Packit 5164a5
            self.add_command(["epstopdf", "--outfile=%(output)s", "%(input)s"],
Packit 5164a5
                             shell=True)
Packit 5164a5
        elif imgdst == "png":
Packit 5164a5
            self.add_command(["convert", "%(input)s", "%(output)s"])
Packit 5164a5
Packit 5164a5
class FigConverter(ImageConverter):
Packit 5164a5
    def __init__(self, imgsrc, imgdst="", docformat="", backend=""):
Packit 5164a5
        ImageConverter.__init__(self, imgsrc="fig", imgdst=imgdst)
Packit 5164a5
        self.add_command(["fig2dev", "-L", "eps", "%(input)s"],
Packit 5164a5
                         stdout="%(output)s")
Packit 5164a5
        if imgdst != "eps":
Packit 5164a5
            self.conv_next = EpsConverter("eps", imgdst=imgdst)
Packit 5164a5
        else:
Packit 5164a5
            self.conv_next = None
Packit 5164a5
Packit 5164a5
    def convert(self, input, output, format):
Packit 5164a5
        if self.conv_next:
Packit 5164a5
            epsfile = "tmp_fig.eps"
Packit 5164a5
        else:
Packit 5164a5
            epsfile = output
Packit 5164a5
        ImageConverter.convert(self, input, epsfile, "eps")
Packit 5164a5
        if self.conv_next:
Packit 5164a5
            self.conv_next.convert(epsfile, output, format)
Packit 5164a5
Packit 5164a5
class SvgConverter(ImageConverter):
Packit 5164a5
    def __init__(self, imgsrc, imgdst="", docformat="", backend=""):
Packit 5164a5
        ImageConverter.__init__(self, imgsrc="svg", imgdst=imgdst)
Packit 5164a5
        self.add_command(["inkscape", "-z", "-D", "--export-%(dst)s=%(output)s",
Packit 5164a5
                          "%(input)s"])
Packit 5164a5
Packit 5164a5
Packit 5164a5
class FormatRule:
Packit 5164a5
    def __init__(self, imgsrc="", imgdst="", docformat="", backend=""):
Packit 5164a5
        self.imgsrc = imgsrc or "*"
Packit 5164a5
        self.imgdst = imgdst or "*"
Packit 5164a5
        self.docformat = docformat or "*"
Packit 5164a5
        self.backend = backend or "*"
Packit 5164a5
Packit 5164a5
    def criterions(self):
Packit 5164a5
        return { "imgsrc": self.imgsrc,
Packit 5164a5
                 "imgdst": self.imgdst,
Packit 5164a5
                 "docformat": self.docformat,
Packit 5164a5
                 "backend": self.backend }
Packit 5164a5
Packit 5164a5
class ImageFormatPool:
Packit 5164a5
    def __init__(self):
Packit 5164a5
        self.rules = []
Packit 5164a5
        self._filter = ObjectFilter()
Packit 5164a5
Packit 5164a5
    def add_rule(self, rule):
Packit 5164a5
        self.rules.append(rule)
Packit 5164a5
Packit 5164a5
    def prepend(self, other):
Packit 5164a5
        self.rules = other.rules + self.rules
Packit 5164a5
Packit 5164a5
    def output_format(self, imgsrc="", docformat="", backend=""):
Packit 5164a5
        founds = self._filter.select(self.rules,
Packit 5164a5
                                     imgsrc=imgsrc,
Packit 5164a5
                                     docformat=docformat,
Packit 5164a5
                                     backend=backend)
Packit 5164a5
        if founds:
Packit 5164a5
            return founds[0].imgdst
Packit 5164a5
        else:
Packit 5164a5
            return ""
Packit 5164a5
Packit 5164a5
class ImageFormatRuleset(ImageFormatPool):
Packit 5164a5
    def __init__(self):
Packit 5164a5
        ImageFormatPool.__init__(self)
Packit 5164a5
        # There can be a mismatch between PDF-1.4 images and PDF-1.3
Packit 5164a5
        # document produced by XeTeX
Packit 5164a5
        self.add_rule(FormatRule(docformat="pdf", backend="xetex", 
Packit 5164a5
                                 imgdst="png"))
Packit 5164a5
        self.add_rule(FormatRule(docformat="pdf", imgdst="pdf"))
Packit 5164a5
        self.add_rule(FormatRule(docformat="dvi", imgdst="eps"))
Packit 5164a5
        self.add_rule(FormatRule(docformat="ps", imgdst="eps"))
Packit 5164a5
Packit 5164a5
        # Register as main pool
Packit 5164a5
        image_setup().format_pool.set_pool(self)
Packit 5164a5
Packit 5164a5
#
Packit 5164a5
# The Imagedata class handles all the image transformation
Packit 5164a5
# process, from the discovery of the actual image involved to
Packit 5164a5
# the conversion process.
Packit 5164a5
#
Packit 5164a5
class Imagedata:
Packit 5164a5
    def __init__(self):
Packit 5164a5
        self.paths = []
Packit 5164a5
        self.input_format = "png"
Packit 5164a5
        self.output_format = "pdf"
Packit 5164a5
        self.docformat = "pdf"
Packit 5164a5
        self.backend = ""
Packit 5164a5
        self.rules = ImageFormatRuleset()
Packit 5164a5
        self.converters = ImageConverters()
Packit 5164a5
        self.converted = {}
Packit 5164a5
        self.log = logging.getLogger("dblatex")
Packit 5164a5
        self.output_encoding = ""
Packit 5164a5
Packit 5164a5
    def set_encoding(self, output_encoding):
Packit 5164a5
        self.output_encoding = output_encoding
Packit 5164a5
Packit 5164a5
    def set_format(self, docformat, backend):
Packit 5164a5
        self.docformat = docformat
Packit 5164a5
        self.backend = backend
Packit 5164a5
        self.output_format = self.rules.output_format(docformat=docformat,
Packit 5164a5
                                                      backend=backend)
Packit 5164a5
Packit 5164a5
    def convert(self, fig):
Packit 5164a5
        # Translate the URL to an actual local path
Packit 5164a5
        fig = urllib.url2pathname(fig)
Packit 5164a5
Packit 5164a5
        # Always use '/' in path: work even on windows and is required by tex
Packit 5164a5
        if os.path.sep != '/': fig = fig.replace(os.path.sep, '/')
Packit 5164a5
Packit 5164a5
        # First, scan the available formats
Packit 5164a5
        (realfig, ext) = self.scanformat(fig)
Packit 5164a5
Packit 5164a5
        # No real file found, give up
Packit 5164a5
        if not(realfig):
Packit 5164a5
            self.log.warning("Image '%s' not found" % fig)
Packit 5164a5
            return fig
Packit 5164a5
Packit 5164a5
        # Check if this image has been already converted
Packit 5164a5
        if self.converted.has_key(realfig):
Packit 5164a5
            self.log.info("Image '%s' already converted as %s" % \
Packit 5164a5
                  (fig, self.converted[realfig]))
Packit 5164a5
            return self.converted[realfig]
Packit 5164a5
Packit 5164a5
        # No format found, take the default one
Packit 5164a5
        if not(ext):
Packit 5164a5
            ext = self.input_format
Packit 5164a5
Packit 5164a5
        # Natively supported format?
Packit 5164a5
        if (ext == self.output_format):
Packit 5164a5
            return self._safe_file(fig, realfig, ext)
Packit 5164a5
Packit 5164a5
        # Try to convert
Packit 5164a5
        count = len(self.converted)
Packit 5164a5
        newfig = "fig%d.%s" % (count, self.output_format)
Packit 5164a5
Packit 5164a5
        conv = self.converters.get_converters(imgsrc=ext,
Packit 5164a5
                                              imgdst=self.output_format,
Packit 5164a5
                                              backend=self.backend)
Packit 5164a5
        if not(conv):
Packit 5164a5
            self.log.debug("Cannot convert '%s' to %s" % (fig,
Packit 5164a5
                             self.output_format))
Packit 5164a5
            # Unknown conversion to do, or nothing to do
Packit 5164a5
            return self._safe_file(fig, realfig, ext)
Packit 5164a5
        else:
Packit 5164a5
            # Take the first converter that does the trick
Packit 5164a5
            conv = conv[0]
Packit 5164a5
Packit 5164a5
        # Convert the image and put it in the cache
Packit 5164a5
        conv.log = self.log
Packit 5164a5
        conv.convert(realfig, newfig, self.output_format)
Packit 5164a5
        self.converted[realfig] = newfig
Packit 5164a5
        return newfig
Packit 5164a5
Packit 5164a5
    def _safe_file(self, fig, realfig, ext):
Packit 5164a5
        """
Packit 5164a5
        Copy the file in the working directory if its path contains characters
Packit 5164a5
        unsupported by latex, like spaces.
Packit 5164a5
        """
Packit 5164a5
        # Encode to expected output format. If encoding is OK and 
Packit 5164a5
        # supported by tex, just return the encoded path
Packit 5164a5
        newfig = self._path_encode(fig)
Packit 5164a5
        if newfig and newfig.find(" ") == -1:
Packit 5164a5
            return newfig
Packit 5164a5
Packit 5164a5
        # Added to the converted list
Packit 5164a5
        count = len(self.converted)
Packit 5164a5
        newfig = "figcopy%d.%s" % (count, ext)
Packit 5164a5
        self.converted[realfig] = newfig
Packit 5164a5
Packit 5164a5
        # Do the copy
Packit 5164a5
        shutil.copyfile(realfig, newfig)
Packit 5164a5
        return newfig
Packit 5164a5
Packit 5164a5
    def _path_encode(self, fig):
Packit 5164a5
        # Actually, only ASCII characters are sure to match filesystem encoding
Packit 5164a5
        # so let's be conservative
Packit 5164a5
        if self.output_encoding == "utf8":
Packit 5164a5
            return fig
Packit 5164a5
        try:
Packit 5164a5
            newfig = fig.decode("utf8").encode("ascii")
Packit 5164a5
        except:
Packit 5164a5
            newfig = ""
Packit 5164a5
        return newfig
Packit 5164a5
Packit 5164a5
    def scanformat(self, fig):
Packit 5164a5
        (root, ext) = os.path.splitext(fig)
Packit 5164a5
Packit 5164a5
        if (ext):
Packit 5164a5
            realfig = self.find(fig)
Packit 5164a5
            return (realfig, ext[1:])
Packit 5164a5
        
Packit 5164a5
        # Lookup for the best suited available figure
Packit 5164a5
        if (self.output_format == "pdf"):
Packit 5164a5
            formats = ("png", "pdf", "jpg", "eps", "gif", "fig", "svg")
Packit 5164a5
        else:
Packit 5164a5
            formats = ("eps", "fig", "pdf", "png", "svg")
Packit 5164a5
Packit 5164a5
        for format in formats:
Packit 5164a5
            realfig = self.find("%s.%s" % (fig, format))
Packit 5164a5
            if realfig:
Packit 5164a5
                self.log.info("Found %s for '%s'" % (format, fig))
Packit 5164a5
                break
Packit 5164a5
Packit 5164a5
        # Maybe a figure with no extension
Packit 5164a5
        if not(realfig):
Packit 5164a5
            realfig = self.find(fig)
Packit 5164a5
            format = ""
Packit 5164a5
Packit 5164a5
        return (realfig, format)
Packit 5164a5
        
Packit 5164a5
    def find(self, fig):
Packit 5164a5
        # First, the obvious absolute path case
Packit 5164a5
        if os.path.isabs(fig):
Packit 5164a5
            if os.path.isfile(fig):
Packit 5164a5
                return fig
Packit 5164a5
            else:
Packit 5164a5
                return None
Packit 5164a5
Packit 5164a5
        # Then, look for the file in known paths
Packit 5164a5
        for path in self.paths:
Packit 5164a5
            realfig = os.path.join(path, fig)
Packit 5164a5
            if os.path.isfile(realfig):
Packit 5164a5
                return realfig
Packit 5164a5
Packit 5164a5
        return None
Packit 5164a5