Embed images into SVG output
This commit is contained in:
parent
e31ed72655
commit
6f9bb67d02
@ -20,6 +20,7 @@ from wireviz.DataClasses import (
|
||||
Tweak,
|
||||
Side,
|
||||
)
|
||||
from wireviz.svgembed import embed_svg_images_file
|
||||
from wireviz.wv_bom import (
|
||||
HEADER_MPN,
|
||||
HEADER_PN,
|
||||
@ -646,13 +647,9 @@ class Harness:
|
||||
|
||||
@property
|
||||
def svg(self):
|
||||
from io import BytesIO
|
||||
|
||||
graph = self.graph
|
||||
data = BytesIO()
|
||||
data.write(graph.pipe(format="svg"))
|
||||
data.seek(0)
|
||||
return data.read()
|
||||
return embed_svg_images(graph.pipe(format="svg").decode("utf-8"), Path.cwd())
|
||||
|
||||
|
||||
def output(
|
||||
self,
|
||||
@ -671,9 +668,14 @@ class Harness:
|
||||
if f in ("png", "svg", "html"):
|
||||
if f == "html": # if HTML format is specified,
|
||||
f = "svg" # generate SVG for embedding into HTML
|
||||
# SVG file will be renamed/deleted later
|
||||
_filename = f"{filename}.tmp" if f == "svg" else filename
|
||||
# TODO: prevent rendering SVG twice when both SVG and HTML are specified
|
||||
graph.format = f
|
||||
graph.render(filename=filename, view=view, cleanup=cleanup)
|
||||
graph.render(filename=_filename, view=view, cleanup=cleanup)
|
||||
# embed images into SVG output
|
||||
if "svg" in fmt or "html" in fmt:
|
||||
embed_svg_images_file(f"{filename}.tmp.svg")
|
||||
# GraphViz output
|
||||
if "gv" in fmt:
|
||||
graph.save(filename=f"{filename}.gv")
|
||||
@ -692,8 +694,11 @@ class Harness:
|
||||
# TODO: implement PDF output
|
||||
print("PDF output is not yet supported")
|
||||
# delete SVG if not needed
|
||||
if "html" in fmt and not "svg" in fmt and not svg_already_exists:
|
||||
Path(f"{filename}.svg").unlink()
|
||||
if "html" in fmt and not "svg" in fmt:
|
||||
# SVG file was just needed to generate HTML
|
||||
Path(f"{filename}.tmp.svg").unlink()
|
||||
elif "svg" in fmt:
|
||||
Path(f"{filename}.tmp.svg").replace(f"{filename}.svg")
|
||||
|
||||
def bom(self):
|
||||
if not self._bom:
|
||||
|
||||
52
src/wireviz/svgembed.py
Normal file
52
src/wireviz/svgembed.py
Normal file
@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
mime_subtype_replacements = {"jpg": "jpeg", "tif": "tiff"}
|
||||
|
||||
|
||||
def embed_svg_images(svg_in: str, base_path: Union[str, Path] = Path.cwd()) -> str:
|
||||
images_b64 = {} # cache of base64-encoded images
|
||||
|
||||
def image_tag(pre: str, url: str, post: str) -> str:
|
||||
return f'<image{pre} xlink:href="{url}"{post}>'
|
||||
|
||||
def replace(match: re.Match) -> str:
|
||||
imgurl = match["URL"]
|
||||
if not imgurl in images_b64: # only encode/cache every unique URL once
|
||||
imgurl_abs = (Path(base_path) / imgurl).resolve()
|
||||
image = imgurl_abs.read_bytes()
|
||||
images_b64[imgurl] = base64.b64encode(image).decode("utf-8")
|
||||
return image_tag(
|
||||
match["PRE"] or "",
|
||||
f"data:image/{get_mime_subtype(imgurl)};base64, {images_b64[imgurl]}",
|
||||
match["POST"] or "",
|
||||
)
|
||||
|
||||
pattern = re.compile(
|
||||
image_tag(r"(?P<PRE> [^>]*?)?", r'(?P<URL>[^"]*?)', r"(?P<POST> [^>]*?)?"),
|
||||
re.IGNORECASE,
|
||||
)
|
||||
return pattern.sub(replace, svg_in)
|
||||
|
||||
|
||||
def get_mime_subtype(filename: Union[str, Path]) -> str:
|
||||
mime_subtype = Path(filename).suffix.lstrip(".").lower()
|
||||
if mime_subtype in mime_subtype_replacements:
|
||||
mime_subtype = mime_subtype_replacements[mime_subtype]
|
||||
return mime_subtype
|
||||
|
||||
|
||||
def embed_svg_images_file(
|
||||
filename_in: Union[str, Path], overwrite: bool = True
|
||||
) -> None:
|
||||
filename_in = Path(filename_in).resolve()
|
||||
filename_out = filename_in.with_suffix(".b64.svg")
|
||||
filename_out.write_text(
|
||||
embed_svg_images(filename_in.read_text(), filename_in.parent)
|
||||
)
|
||||
if overwrite:
|
||||
filename_out.replace(filename_in)
|
||||
@ -37,7 +37,7 @@ def generate_html_output(
|
||||
html = open_file_read(templatefile).read()
|
||||
|
||||
# embed SVG diagram
|
||||
with open_file_read(f"{filename}.svg") as file:
|
||||
with open_file_read(f"{filename}.tmp.svg") as file:
|
||||
svgdata = re.sub(
|
||||
"^<[?]xml [^?>]*[?]>[^<]*<!DOCTYPE [^>]*>",
|
||||
"<!-- XML and DOCTYPE declarations from SVG file removed -->",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user