Ejemplo n.º 1
0
class SVGEngine:
    """ Renders SVG data on Qt paint devices. """
    def __init__(self, *, render_hints=QPainter.Antialiasing) -> None:
        self._data = b""  # Current XML SVG binary data.
        self._renderer = QSvgRenderer()  # Qt SVG renderer.
        self._render_hints = render_hints  # SVG rendering quality hints (such as anti-aliasing).

    def loads(self, data: QtSVGData) -> None:
        """ Load the renderer with SVG data. """
        if isinstance(data, str):
            data = data.encode('utf-8')
        self._data = data
        self._renderer.load(data)

    def load(self, filename: str) -> None:
        """ Load SVG data directly from disk. """
        with open(filename) as fp:
            data = fp.read()
        self.loads(data)

    def viewbox_size(self) -> QSize:
        """ If no valid viewbox is defined, 100x100 is a typical default. """
        size = self._renderer.viewBox().size()
        if size.isEmpty():
            size = QSize(100, 100)
        return size

    def render(self, target: QPaintDevice, bounds: QRectF = None) -> None:
        """ Render the current SVG data on <target> with optional <bounds> on the area. """
        args = () if bounds is None else (bounds, )
        with QPainter(target) as p:
            p.setRenderHints(self._render_hints)
            self._renderer.render(p, *args)

    def render_fit(self, target: QPaintDevice) -> None:
        """ Render the current SVG data on <target>, centered at maximum scale while preserving aspect ratio. """
        width = target.width()
        height = target.height()
        v_size = self.viewbox_size()
        vw = v_size.width()
        vh = v_size.height()
        scale = min(width / vw, height / vh)
        rw = vw * scale
        rh = vh * scale
        rx = (width - rw) / 2
        ry = (height - rh) / 2
        bounds = QRectF(rx, ry, rw, rh)
        self.render(target, bounds)

    def dumps(self) -> str:
        """ Return the current SVG data as a string. """
        return self._data.decode('utf-8')

    def dump(self, filename: str) -> None:
        """ Save the current SVG data directly to disk. """
        with open(filename, 'wb') as fp:
            fp.write(self._data)
Ejemplo n.º 2
0
    def _draw_plate(self, background_path, font_path, target_template,
                    symbols):

        if background_path.suffix == '.svg':
            renderer = QSvgRenderer(str(background_path))
            assert renderer.isValid()
            width = target_template['width']
            w, h = renderer.viewBox().width(), renderer.viewBox().height()
            aspect_ratio = w / h
            w, h = width, width / aspect_ratio
            image = QImage(w, h, QImage.Format_ARGB32)
            image.fill(Qt.transparent)  # workaround for clean image
            painter = QPainter(image)
            painter.setRenderHint(QPainter.Antialiasing, True)
            painter.setRenderHint(QPainter.TextAntialiasing, True)
            painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
            renderer.render(painter)
        else:
            image = QPixmap(str(background_path))
            assert not image.isNull()
            width = target_template['width']
            w, h = image.width(), image.height()
            aspect_ratio = w / h
            w, h = width, width / aspect_ratio
            image.scaled(w, h, Qt.KeepAspectRatio)
            image = image.toImage()
            painter = QPainter(image)

        font = get_font(font_path)
        pen = QPen(QColor.fromRgb(int(target_template['font_color'], base=16)),
                   2, Qt.SolidLine)
        painter.setPen(pen)

        for _, sym in symbols.items():
            rect = QRect(sym.x, sym.y, sym.w, sym.h)
            font.setPointSizeF(sym.font_size)
            painter.setFont(font)
            painter.drawText(rect, Qt.AlignCenter, sym.sym)

        painter.end()

        return qimage_to_nparr(image)
Ejemplo n.º 3
0
 def _render(self, data: XMLIconData) -> QIcon:
     """ Create a template image, render the XML data in place, and convert it to an icon.
         Use the viewbox dimensions as pixel sizes. """
     svg = QSvgRenderer(data)
     viewbox = svg.viewBox().size()
     im = QImage(viewbox, QImage.Format_ARGB32)
     im.fill(self._bg_color)
     with QPainter(im) as p:
         p.setRenderHints(self._render_hints)
         svg.render(p)
     return QIcon(QPixmap.fromImage(im))
Ejemplo n.º 4
0
 def _render(self, svg_data: str) -> QImage:
     """ Create a new raster image with the current background color and render an SVG image to it.
         Pixel dimensions will fit the viewbox at maximum scale. """
     svg = QSvgRenderer(svg_data.encode("utf-8"))
     v_size = svg.viewBox().size()
     vw = v_size.width()
     vh = v_size.height()
     scale = min(self._w_max / vw, self._h_max / vh)
     w = round(vw * scale)
     h = round(vh * scale)
     im = QImage(w, h, QImage.Format_ARGB32)
     im.fill(self._bg_color)
     with QPainter(im) as p:
         p.setRenderHints(QPainter.Antialiasing)
         svg.render(p, QRectF(0, 0, w, h))
     return im