Example #1
0
class ClipExtractor(ContentHandler):
    def __init__(self, output, clipId):
        self._sink = XMLGenerator(output, 'UTF-8', True)
        self._locator = None
        self._id = clipId

    def setDocumentLocator(self, locator):
        self._locator = locator
        self._skip = None
        self._stopAt = None
        return self._sink.setDocumentLocator(locator)

    def startDocument(self):
        #         self._prefixes = {}
        self.scale = self.units = None
        self._context = []
        self._outerSvgRendered = False
        return self._sink.startDocument()

    def endDocument(self):
        #         self._prefixes.clear()
        return self._sink.endDocument()

    def startPrefixMapping(self, prefix, uri):
        self._context.append(('xmlns', None, prefix, uri))
        #         mappings = self._prefixes.get(prefix)
        #         if mappings is None:
        #             self._prefixes[prefix] = mappings = []
        #         mappings.append(uri)
        return self._sink.startPrefixMapping(prefix, uri)

    def endPrefixMapping(self, prefix):
        context = self._context.pop()
        assert ('xmlns', None, prefix) == context[:-1]
        #         mappings = self._prefixes.get(prefix)
        #         assert mappings is not None
        #         mappings.pop()
        return self._sink.endPrefixMapping(prefix)

    def startElement(self, qname, attrs):
        raise xml.sax.SAXNotSupportedException(
            'This handler must be used with feature "%s"'
            ' turned on' % xml.sax.handler.feature_namespaces)
#         if 'xmlns' in attrs:
#             if self._context:
#                 raise xml.sax.SAXNotSupportedException(
#                     'This document must be parsed with feature "%s"'
#                     ' turned on'
#                     % xml.sax.handler.feature_namespaces
#                 )
#             else:
#                 self.startPrefixMapping('', attrs.get('xmlns'))
#         ns, name = self._splitNS(qname)
#         handler = self.ELEMENTS.get((ns, name))
#         if handler and handler[0]:
#             update = handler[0](self, attrs, ns, name)
#             if not update:
#                 if self._skip is None:
#                     self._skip = len(self._context) + 1
#             elif isinstance(update, collections.Sequence):
#                 attrs = update[0]
#                 if len(update) > 1:
#                     ns, name, qname = update[1:]
#         self._context.append((ns, name, qname, attrs.copy()))
#         if self._skip is None:
#             return self._sink.startElement(qname, attrs)

    def endElement(self, qname):
        raise xml.sax.SAXNotSupportedException(
            'This handler must be used with feature "%s"'
            ' turned on' % xml.sax.handler.feature_namespaces)
#         ns, name = self._splitNS(qname)
#         handler = self.ELEMENTS.get((ns, name))
#         if handler and handler[1]:
#             handler[1](self, ns, name)
#         context = self._context.pop()
#         assert (ns, name) == context[:2]
#         if self._skip is None:
#             self._sink.endElement(qname)
#         elif len(self._context) < self._skip:
#             self._skip = None
#         if len(self._context) == self._stopAt:
#             raise self.Done('extraction complete',
#                             self._locator.getLineNumber(),
#                             self._locator.getColumnNumber()
#                         )

    def startElementNS(self, name, qname, attrs):
        if (None, 'xmlns') in attrs:
            self.startPrefixMapping(None, attrs.get('xmlns'))
        handler = self.ELEMENTS.get(name)
        if handler and handler[0]:
            update = handler[0](self, attrs, *name, qname)
            if not update:
                if self._skip is None:
                    self._skip = len(self._context) + 1
            elif isinstance(update, collections.Sequence):
                attrs = update[0]
                if len(update) > 1:
                    name = tuple(update[1:3])
                if len(update) > 3:
                    qname = update[3]
        self._context.append(name + (qname, attrs.copy()))
        if self._skip is None:
            return self._sink.startElementNS(name, qname, attrs)

    def endElementNS(self, name, qname):
        handler = self.ELEMENTS.get(name)
        if handler and handler[1]:
            handler[1](self, *name, qname)
        context = self._context.pop()
        assert name == context[:2]
        if self._skip is None:
            self._sink.endElementNS(name, qname)
        elif len(self._context) < self._skip:
            self._skip = None
        if len(self._context) == self._stopAt:
            while self._context:
                toClose = self._context.pop()
                if 'xmlns' == toClose[0]:
                    self._sink.endPrefixMapping(toClose[2])
                else:
                    self._sink.ignorableWhitespace('\n')
                    self._sink.endElementNS(toClose[:2], toClose[2])
            self._sink.endDocument()
            raise self.Done('extraction complete',
                            (self._locator.getLineNumber(),
                             self._locator.getColumnNumber()))

    def elementSvgEnd(self, *args):
        attrs = self._context[-1][3]
        if self.ATTR_ID in attrs and \
                self._id == attrs.get(self.ATTR_ID):
            self._stopAt = len(self._context) - 1

    def elementSvgStart(self, attrs, *args):
        if any((CardBackView.SVG_NAMESPACE, 'svg') == e[:2]
               for e in self._context):
            return self.ATTR_ID in attrs and \
                self._id == attrs.get(self.ATTR_ID)
        elif not 'viewBox' in attrs and not (None, 'viewBox') in attrs:
            self._error(None, 'Main <svg> element has no viewBox attribute')
        else:
            try:
                internalSize = attrs.get((None, 'viewBox'))
                if internalSize is None:
                    internalSize = attrs.get('viewBox')
                internalSize = self.parseBox(internalSize)[2:]
                externalSize = list(internalSize)
                units = [''] * 2
                i = -1
                for attr in CardBackView.ATTRS_SVG_DIMENSION:
                    i += 1
                    value, unit = self._distanceAttr(attrs, attr)
                    if unit.strip() == '%':
                        value = None
                    if value is not None:
                        externalSize[i] = value
                        units[i] = unit
                self.units = tuple(units)
                try:
                    self.scale = tuple(
                        e / i for e, i in zip(externalSize, internalSize))
                except ZeroDivisionError as error:
                    self._error(
                        None,
                        'Found <svg> with zero dimension(s), viewBox="%s"' %
                        attrs.get((None, 'viewBox')))
                return False
            except:
                self._error()

    def elementUseStart(self, attrs, ns, name, qname):
        target = attrs.get(CardBackView.XLINK_HREF_ATTR)
        if not target:
            return False
        target = target.strip()
        if '#' == target[0] and self._id == target[1:] \
             and not self._outerSvgRendered:
            try:
                outerSvg = next(e for e in self._context
                                if (CardBackView.SVG_NAMESPACE,
                                    'svg') == e[:2])
            except StopIteration:
                self._error(None, 'Found <use> element outside of an <svg>')
            size = []
            for attr in CardBackView.ATTRS_SVG_DIMENSION:
                value, unit = self._distanceAttr(attrs, attr)
                if unit.strip():
                    self._error(
                        None, 'Attribute <use %s="%s" ...> is not valid'
                        ' in this context, expected a unit-free number.' %
                        (attr, attrs.get(None, attr)))
                if value is None:
                    self._error(
                        None, 'Attribute `%s` is missing from <use> element,'
                        ' but is expected in this context.' % attr)
                size.append(value)
            sattrs = ('%.6f%s' % (s * v, u)
                      for s, v, u in zip(self.scale, size, self.units))
            qnames = {
                name: outerSvg[3].getQNameByName(name)
                for name in outerSvg[3].getNames()
            }
            attrMap = dict(outerSvg[3].items())
            attrMap[(None, 'viewBox')] = self.boxValue((0, 0) + tuple(size))
            for attr in zip(CardBackView.ATTRS_SVG_DIMENSION, sattrs):
                attrMap[(None, attr[0])] = attr[1]
            self._sink.startElementNS(outerSvg[:2], outerSvg[2],
                                      AttributesNSImpl(attrMap, qnames))
            self._sink.ignorableWhitespace('\n')
            self._outerSvgRendered = True
            qnames = {
                name: attrs.getQNameByName(name)
                for name in attrs.getNames()
            }
            attrMap = dict(attrs.items())
            for attr in ('x', 'y') + CardBackView.ATTRS_SVG_DIMENSION:
                qnames.pop((None, attr), None)
                attrMap.pop((None, attr), None)
            self._sink.startElementNS((ns, name), qname,
                                      AttributesNSImpl(attrMap, qnames))
            self._sink.endElementNS((ns, name), qname)
            self._sink.ignorableWhitespace('\n')
        return False

    def elementDefsStart(self, attrs, *args):
        if not self._outerSvgRendered:
            self._error(
                None, 'Element <use xlink:href="#%s" ...> must precede'
                ' the <defs> element in this context.' % self._id)
        self._skip = None
        return True

    ATTR_ID = (None, 'id')

    ELEMENTS = {
        (CardBackView.SVG_NAMESPACE, 'svg'): (elementSvgStart, elementSvgEnd),
        (CardBackView.SVG_NAMESPACE, 'use'): (elementUseStart, None),
        (CardBackView.SVG_NAMESPACE, 'defs'): (elementDefsStart, None),
    }

    def characters(self, content):
        if self._skip is None:
            return self._sink.characters(content)

    def ignorableWhitespace(self, whitespace):
        if self._skip is None:
            return self._sink.ignorableWhitespace(whitespace)

    def processingInstruction(self, target, data):
        if self._skip is None:
            return self._sink.processingInstruction(target, data)

    def skippedEntity(self, name):
        if self._skip is None:
            return self._sink.skippedEntity(name)

    def _error(self, error=True, message=''):
        if error == True:
            error = sys.exc_info()[1]
        if error is None:
            assert message
            error = SAXParseException(message, None, self._locator)
        elif not isinstance(error, SAXParseException):
            msg = error.args[0] if error.args else ''
            if msg:
                msg += ' '
            if message:
                msg += message + ' '
            error.args = (msg + 'at line %d, column %d.' %
                          (self._locator.getLineNumber(),
                           self._locator.getColumnNumber()), ) + error.args[1:]
        raise error

    class Done(xml.sax.SAXException):
        pass
Example #2
0
 def setDocumentLocator(self, locator):
     XMLGenerator.setDocumentLocator(self, locator)
     self.location = Location(self._locator)
Example #3
0
 def setDocumentLocator(self, locator):
     XMLGenerator.setDocumentLocator(self, locator)
     self.location = Location(self._locator)