Source code for pm4py.visualization.powl.visualizer

import base64
import os
import re
import shutil
import tempfile
import webbrowser

from pm4py.visualization.powl.variants import basic
from pm4py.visualization.powl.variants import net
from enum import Enum
from pm4py.util import exec_utils, constants
from typing import Optional, Dict, Any
from pm4py.objects.powl.obj import POWL


[docs] class POWLVisualizationVariants(Enum): BASIC = basic NET = net
[docs] class Parameters(Enum): FORMAT = "format" ENCODING = "encoding"
DEFAULT_VARIANT = POWLVisualizationVariants.BASIC
[docs] def apply( powl: POWL, variant=DEFAULT_VARIANT, frequency_tags=True, parameters: Optional[Dict[Any, Any]] = None, ) -> str: """ Method for POWL model representation Parameters ----------- powl POWL model parameters Possible parameters of the algorithm: Parameters.FORMAT -> Format of the image (PDF, PNG, SVG; default PNG) variant Variant of the algorithm to use: - POWLVisualizationVariants.BASIC (default) - POWLVisualizationVariants.NET: BPMN-like visualization with decision gates frequency_tags Simplify the visualization using frequency tags Returns ----------- str SVG Content """ if parameters is None: parameters = {} if frequency_tags: powl = powl.simplify_using_frequent_transitions() viz = exec_utils.get_variant(variant).apply(powl, parameters=parameters) svg_content = viz.pipe().decode("utf-8") def inline_images_and_svgs(svg_content): img_pattern = re.compile( r'<image[^>]+xlink:href=["\'](.*?)["\'][^>]*>' ) def encode_file_to_base64(file_path): with open(file_path, "rb") as file: return base64.b64encode(file.read()).decode("utf-8") def read_file_content_and_viewbox(file_path): with open(file_path, "r") as file: content = file.read() content = re.sub(r"<\?xml.*?\?>", "", content, flags=re.DOTALL) content = re.sub( r"<!DOCTYPE.*?>", "", content, flags=re.DOTALL ) viewBox_match = re.search(r'viewBox="([^"]*)"', content) viewBox = ( viewBox_match.group(1) if viewBox_match else "0 0 1 1" ) svg_content_match = re.search( r"<svg[^>]*>(.*?)</svg>", content, re.DOTALL ) svg_content = ( svg_content_match.group(1) if svg_content_match else content ) return svg_content, viewBox def replace_with_inline_content(match): file_path = match.group(1) if file_path.lower().endswith(".svg"): svg_data, viewBox = read_file_content_and_viewbox(file_path) viewBox_values = [float(v) for v in viewBox.split()] actual_width, actual_height = ( viewBox_values[2], viewBox_values[3], ) intended_width = float( match.group(0) .split('width="')[1] .split('"')[0] .replace("px", "") ) intended_height = float( match.group(0) .split('height="')[1] .split('"')[0] .replace("px", "") ) x = float(match.group(0).split('x="')[1].split('"')[0]) y = float(match.group(0).split('y="')[1].split('"')[0]) scale_x = intended_width / actual_width scale_y = intended_height / actual_height return f'<g transform="translate({x},{y}) scale({scale_x},{scale_y})">{svg_data}</g>' else: base64_data = encode_file_to_base64(file_path) return match.group(0).replace( file_path, f"data:image/png;base64,{base64_data}" ) return img_pattern.sub(replace_with_inline_content, svg_content) svg_content_with_inline_images = inline_images_and_svgs(svg_content) return svg_content_with_inline_images
[docs] def save( svg_content: str, output_file_path: str, parameters: Optional[Dict[Any, Any]] = None, ): """ Save the diagram in a specified format. Parameters ----------- svg_content : str SVG content output_file_path : str Path where the output file should be saved """ if parameters is None: parameters = {} encoding = exec_utils.get_param_value( Parameters.ENCODING, parameters, "utf-8" ) with tempfile.NamedTemporaryFile( delete=False, mode="w+", suffix=".svg" ) as tmpfile: tmpfile.write(svg_content) tmpfile_path = tmpfile.name if output_file_path.endswith("svg"): shutil.move(tmpfile_path, output_file_path) elif output_file_path.endswith("png"): import cairosvg as cairosvg cairosvg.svg2png( bytestring=svg_content.encode(encoding), write_to=output_file_path ) elif output_file_path.endswith("pdf"): import cairosvg as cairosvg cairosvg.svg2pdf( bytestring=svg_content.encode(encoding), write_to=output_file_path ) else: raise Exception( f"Unsupported format! Please use 'svg', 'png', or 'pdf'." ) if os.path.exists(tmpfile_path): os.remove(tmpfile_path)
[docs] def view(svg_content: str, parameters: Optional[Dict[Any, Any]] = None): """ View the diagram Parameters ----------- svg_content SVG content image_format image format """ if parameters is None: parameters = {} if constants.DEFAULT_ENABLE_VISUALIZATIONS_VIEW: image_format = str( exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") ).lower() encoding = exec_utils.get_param_value( Parameters.ENCODING, parameters, "utf-8" ) with tempfile.NamedTemporaryFile( delete=False, mode="w+", suffix=".svg" ) as tmpfile: tmpfile.write(svg_content) tmpfile_path = tmpfile.name if image_format == "svg": absolute_path = os.path.abspath(tmpfile_path) return webbrowser.open("file://" + absolute_path) elif image_format == "png": import cairosvg as cairosvg cairosvg.svg2png( bytestring=svg_content.encode(encoding), write_to=tmpfile_path + ".png", ) webbrowser.open( "file://" + os.path.abspath(tmpfile_path + ".png") ) elif image_format == "pdf": import cairosvg as cairosvg cairosvg.svg2pdf( bytestring=svg_content.encode(encoding), write_to=tmpfile_path + ".pdf", ) webbrowser.open( "file://" + os.path.abspath(tmpfile_path + ".pdf") ) else: raise Exception( f"Unsupported format: {image_format}. Please use 'svg', 'png', or 'pdf'.")