#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (C) 1998-2026 Stephane Galland # # This program is free library; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 3 of the # License, or any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; see the file COPYING. If not, # write to the Free Software Foundation, Inc., 59 Temple Place - Suite # 330, Boston, MA 02111-1307, USA. import re import os import xml.etree.ElementTree as Xml from typing import Callable from autolatex2.tex.beamer import Beamer from autolatex2.utils.runner import Runner, ScriptOutput import autolatex2.utils.utilfunctions as genutils from autolatex2.utils.i18n import T class SvgUtils: @staticmethod def create_pdftex_for_layered_svg(svg_file : str, out_file_with_extension : str, out_file_without_extension : str, out_dir : str, out_extensions : list[str], pdf_mode : bool, is_tex_mode : bool, exporter : Callable[[str, str, str, str | None], ScriptOutput] | None, extra_parameter : str | None = None): """ Generate the '.pdf_t' file from a SVG that contains layers. :param svg_file: the input SVG file. :type svg_file: str :param out_file_with_extension: the output file with extension. :type out_file_with_extension: str :param out_file_without_extension: the output file without extension. :type out_file_without_extension: str :param out_dir: the output directory. :type out_dir: str :param out_extensions: the output file extensions. :type out_extensions: list[str] :param pdf_mode: indicates if the generation mode is PDF; Otherwise Postscript. :type pdf_mode: bool :param is_tex_mode: indicates if the generation mode is for embedded TeX macros. :type is_tex_mode: bool :param exporter: a lambda that is used for creating the image of a layer. Arguments are: * identifier: the identifier of the layer; * svg_file: the path of the svg file to read; * output_file: the path to the PDF/PS file to create for the layer; * extra_param: the extra parameters passed to with function The lambda returns the result of the script run. :type exporter: Callable[[str,str,str,str|None], ScriptOutput] | None :param extra_parameter: any extra parameter value to pass to "pdf_exporter". :type extra_parameter: str | None """ if pdf_mode: ext1 = '.pdftex_t' ext2 = '.pdf' ext3 = '.pdf_tex' else: ext1 = '.pstex_t' ext2 = '.eps' ext3 = '.ps_tex' xml_tree = Xml.parse(svg_file) xml_root = xml_tree.getroot() image_inclusions = list() if re.match('^(?:\\{.*?})?svg$', xml_root.tag, re.S): if 'width' in xml_root.attrib: width = xml_root.attrib['width'] or '0' width = float(re.sub(r'\D+$', '', width)) else: width = 0.0 if width <= 0.0: width = 100.0 elif width > 1000.0: width = 1000.0 layer_index = 1 for element in xml_root: if (re.match('^(?:\\{.*?})?g$', element.tag, re.S) and '{http://www.inkscape.org/namespaces/inkscape}groupmode' in element.attrib and element.attrib['{http://www.inkscape.org/namespaces/inkscape}groupmode'] == 'layer' and ( 'style' not in element.attrib or element.attrib['style'] != 'display:none')): if ('{http://www.inkscape.org/namespaces/inkscape}label' not in element.attrib or not element.attrib['{http://www.inkscape.org/namespaces/inkscape}label']): label = '' else: label = element.attrib['{http://www.inkscape.org/namespaces/inkscape}label'].strip() if 'id' not in element.attrib or not element.attrib['id']: identifier = '' else: identifier = element.attrib['id'].strip() overlay_spec = Beamer.parse_frames_start(label) if not overlay_spec: overlay_spec = str(layer_index) else: frame_spec, min_frame = overlay_spec overlay_spec = frame_spec layer_index = min_frame output_basename = out_file_without_extension + "_" + identifier figure_file = os.path.join(out_dir, output_basename + ext2) if exporter: assert exporter is not None runner_output = exporter(identifier, svg_file, figure_file, extra_parameter) Runner.check_runner_status(runner_output) if is_tex_mode: includable_file = os.path.join(out_dir, output_basename + ext3) else: includable_file = "%s%s" % (str(output_basename), str(ext2)) image_inclusions.append( "\\node<%s> (X) {\\includegraphics[width=%sem]{%s}};%%" % (str(overlay_spec), str(width), str(includable_file))) layer_index += 1 if not image_inclusions: raise Exception(T("No layer in the SVG file: %s") % svg_file) with open(genutils.basename_with_path(out_file_with_extension, *out_extensions) + ext1, 'wt') as out_file: out_file.write("\\begin{tikzpicture}%\n") for inclusion in image_inclusions: out_file.write(inclusion) out_file.write("\n") out_file.write("\\end{tikzpicture}%\n")