class TexText(inkex.EffectExtension): DEFAULT_ALIGNMENT = "middle center" DEFAULT_TEXCMD = "pdflatex" def __init__(self): self.config = Settings("config.json") self.cache = Cache() previous_exit_code = self.cache.get("previous_exit_code", None) if previous_exit_code is None: logging.disable(logging.NOTSET) logger.debug("First run of TexText. Enforcing DEBUG mode.") elif previous_exit_code == EXIT_CODE_OK: logging.disable(logging.CRITICAL) elif previous_exit_code == EXIT_CODE_UNEXPECTED_ERROR: logging.disable(logging.NOTSET) logger.debug( "Enforcing DEBUG mode due to previous exit code `%d`" % previous_exit_code) else: logging.disable(logging.DEBUG) logger.debug("TexText initialized") with open(__file__) as fhl: logger.debug( "TexText version = %s (md5sum = %s)" % (repr(__version__), hashlib.md5( fhl.read().encode('utf-8')).hexdigest())) logger.debug("platform.system() = %s" % repr(platform.system())) logger.debug("platform.release() = %s" % repr(platform.release())) logger.debug("platform.version() = %s" % repr(platform.version())) logger.debug("platform.machine() = %s" % repr(platform.machine())) logger.debug("platform.uname() = %s" % repr(platform.uname())) logger.debug("platform.mac_ver() = %s" % repr(platform.mac_ver())) logger.debug("sys.executable = %s" % repr(sys.executable)) logger.debug("sys.version = %s" % repr(sys.version)) logger.debug("os.environ = %s" % repr(os.environ)) self.requirements_checker = TexTextRequirementsChecker( logger, self.config) if self.requirements_checker.check() == False: raise TexTextFatalError("TexText requirements are not met. " "Please follow instructions " "https://textext.github.io/textext/") super(TexText, self).__init__() self.arg_parser.add_argument("--text", type=str, default=None) self.arg_parser.add_argument("--preamble-file", type=str, default=self.config.get( 'preamble', "default_packages.tex")) self.arg_parser.add_argument("--scale-factor", type=float, default=self.config.get('scale', 1.0)) # Identical to inkex.Effect.getDocumentWidth() in Inkscape >= 0.91, but to provide compatibility with # Inkscape 0.48 we implement it here explicitly again as long as we provide compatibility with that version def get_document_width(self): width = self.document.getroot().get('width') if width: return width else: viewbox = self.document.getroot().get('viewBox') if viewbox: return viewbox.split()[2] else: return '0' # Identical to inkex.Effect.getDocumentHeight() in Inkscape >= 0.91, but to provide compatibility with # Inkscape 0.48 we implement it here explicitly again as long as we provide compatibility with that version def get_document_height(self): height = self.document.getroot().get('height') if height: return height else: viewbox = self.document.getroot().get('viewBox') if viewbox: return viewbox.split()[3] else: return '0' def effect(self): """Perform the effect: create/modify TexText objects""" from asktext import AskerFactory with logger.debug("TexText.effect"): # Find root element old_svg_ele, text, preamble_file, current_scale = self.get_old( ) alignment = TexText.DEFAULT_ALIGNMENT preferred_tex_cmd = self.config.get("previous_tex_command", TexText.DEFAULT_TEXCMD) if preferred_tex_cmd in self.requirements_checker.available_tex_to_pdf_converters.keys( ): current_tex_command = preferred_tex_cmd else: current_tex_command = self.requirements_checker.available_tex_to_pdf_converters.keys( )[0] if text: logger.debug("Old node text = %s" % repr(text)) logger.debug("Old node scale = %s" % repr(current_scale)) # This is very important when re-editing nodes which have been created using TexText <= 0.7. It ensures that # the scale factor which is displayed in the AskText dialog is adjusted in such a way that the size of the node # is preserved when recompiling the LaTeX code. ("version" attribute introduced in 0.7.1) if old_svg_ele is not None: if old_svg_ele.get_meta("version", '<=0.7') == '<=0.7': logger.debug( "Adjust scale factor for node created with TexText<=0.7" ) current_scale *= self.svg.uutounit(1, "pt") jac_sqrt = float(old_svg_ele.get_meta( "jacobian_sqrt", 1.0)) if jac_sqrt != 1.0: logger.debug( "Adjust scale factor to account transformations in inkscape" ) current_scale *= old_svg_ele.get_jacobian_sqrt( ) / jac_sqrt alignment = old_svg_ele.get_meta("alignment", TexText.DEFAULT_ALIGNMENT) current_tex_command = old_svg_ele.get_meta( "texconverter", current_tex_command) gui_config = self.config.get("gui", {}) # Ask for TeX code if self.options.text is None: global_scale_factor = self.options.scale_factor if not preamble_file: logger.debug("Using default preamble file `%s`" % self.options.preamble_file) preamble_file = self.options.preamble_file else: logger.debug("Using node preamble file") # Check if preamble file exists at the specified absolute path location. If not, check to find # the file in the default path. If this fails, too, fallback to the default. if not os.path.exists(preamble_file): logger.debug( "Preamble file is NOT found by absolute path") preamble_file = os.path.join( os.path.dirname(self.options.preamble_file), os.path.basename(preamble_file)) if not os.path.exists(preamble_file): logger.debug( "Preamble file is NOT found along with default preamble file" ) preamble_file = self.options.preamble_file else: logger.debug( "Preamble file is found along with default preamble file" ) else: logger.debug( "Preamble file found by absolute path") if not os.path.isfile(preamble_file): logger.debug("Preamble file is not found") preamble_file = "" asker = AskerFactory().asker( __version__, text, preamble_file, global_scale_factor, current_scale, current_alignment=alignment, current_texcmd=current_tex_command, tex_commands=sorted( list(self.requirements_checker. available_tex_to_pdf_converters.keys())), gui_config=gui_config) def save_callback(_text, _preamble, _scale, alignment=TexText.DEFAULT_ALIGNMENT, tex_cmd=TexText.DEFAULT_TEXCMD): return self.do_convert(_text, _preamble, _scale, old_svg_ele, alignment, tex_command=tex_cmd, original_scale=current_scale) def preview_callback(_text, _preamble, _preview_callback, _tex_command): return self.preview_convert(_text, _preamble, _preview_callback, _tex_command) with logger.debug("Run TexText GUI"): gui_config = asker.ask(save_callback, preview_callback) with logger.debug("Saving global GUI settings"): self.config["gui"] = gui_config self.config.save() else: # ToDo: I think this is completely broken... self.do_convert(self.options.text, self.options.preamble_file, self.options.scale_factor, old_svg_ele, self.DEFAULT_ALIGNMENT, self.DEFAULT_TEXCMD, original_scale=current_scale) def preview_convert(self, text, preamble_file, image_setter, tex_command): """ Generates a preview PNG of the LaTeX output using the selected converter. :param text: :param preamble_file: :param image_setter: A callback to execute with the file path of the generated PNG :param tex_command: Command for tex -> pdf """ tex_executable = self.requirements_checker.available_tex_to_pdf_converters[ tex_command] with logger.debug("TexText.preview"): with logger.debug("args:"): for k, v in locals().items(): logger.debug("%s = %s" % (k, repr(v))) if not text: logger.debug("no text, return") return if isinstance(text, bytes): text = text.decode('utf-8') with ChangeToTemporaryDirectory(): with logger.debug("Converting tex to pdf"): converter = TexToPdfConverter( self.requirements_checker) converter.tex_to_pdf(tex_executable, text, preamble_file) converter.pdf_to_png() image_setter(converter.tmp('png')) def do_convert(self, text, preamble_file, user_scale_factor, old_svg_ele, alignment, tex_command, original_scale=None): """ Does the conversion using the selected converter. :param text: :param preamble_file: :param user_scale_factor: :param old_svg_ele: :param alignment: :param tex_cmd: The tex command to be used for tex -> pdf ("pdflatex", "xelatex", "lualatex") """ from inkex.transforms import Transform tex_executable = self.requirements_checker.available_tex_to_pdf_converters[ tex_command] with logger.debug("TexText.do_convert"): with logger.debug("args:"): for k, v in locals().items(): logger.debug("%s = %s" % (k, repr(v))) if not text: logger.debug("no text, return") return if isinstance(text, bytes): text = text.decode('utf-8') # Coordinates in node from converter are always in pt, we have to scale them such that the node size is correct # even if the document user units are not in pt scale_factor = user_scale_factor * self.svg.unittouu("1pt") # Convert with logger.debug("Converting tex to svg"): with ChangeToTemporaryDirectory(): converter = TexToPdfConverter( self.requirements_checker) converter.tex_to_pdf(tex_executable, text, preamble_file) converter.pdf_to_svg() tt_node = TexTextElement(converter.tmp("svg")) # -- Store textext attributes tt_node.set_meta("version", __version__) tt_node.set_meta("texconverter", tex_command) tt_node.set_meta("pdfconverter", 'inkscape') tt_node.set_meta("text", text) tt_node.set_meta("preamble", preamble_file) tt_node.set_meta("scale", str(user_scale_factor)) tt_node.set_meta("alignment", str(alignment)) try: inkscape_version = self.svg.getroot().get('version') tt_node.set("inkscapeversion", inkscape_version.split(' ')[0]) except AttributeError as ignored: # Unfortunately when this node comes from an Inkscape document that has never been saved before # no version attribute is provided by Inkscape :-( pass # -- Copy style if old_svg_ele is None: with logger.debug("Adding new node to document"): root = self.document.getroot() width = self.svg.unittouu(self.get_document_width()) height = self.svg.unittouu(self.get_document_height()) x, y, w, h = tt_node.bounding_box() tt_node.transform = Transform( tt_node.transform) * Transform( translate=(-x + width / 2 - w / 2, -y + height / 2 - h / 2)) tt_node.set_meta('jacobian_sqrt', str(tt_node.get_jacobian_sqrt())) self.svg.get_current_layer().add(tt_node) else: with logger.debug("Replacing node in document"): relative_scale = user_scale_factor / original_scale tt_node.align_to_node(old_svg_ele, alignment, relative_scale) # If no non-black color has been explicitily set by TeX we copy the color information from the old node # so that coloring done in Inkscape is preserved. if not tt_node.is_colorized(): tt_node.import_group_color_style(old_svg_ele) self.replace_node(old_svg_ele, tt_node) with logger.debug("Saving global settings"): # -- Save settings if os.path.isfile(preamble_file): self.config['preamble'] = preamble_file else: self.config['preamble'] = '' # ToDo: Do we really need this if statement? if scale_factor is not None: self.config['scale'] = user_scale_factor self.config["previous_tex_command"] = tex_command self.config.save() def get_old(self): """ Dig out LaTeX code and name of preamble file from old TexText-generated objects. :return: (old_svg_ele, latex_text, preamble_file_name, scale) :rtype: (TexTextElement, str, str, float) """ for node in self.svg.selected.values(): # TexText node must be a group if node.tag_name != 'g': continue node.__class__ = TexTextElement try: text = node.get_meta('text') preamble = node.get_meta('preamble') scale = float(node.get_meta('scale', 1.0)) return node, text, preamble, scale except (TypeError, AttributeError) as ignored: pass return None, "", "", None def replace_node(self, old_node, new_node): """ Replace an XML node old_node with new_node """ parent = old_node.getparent() parent.remove(old_node) parent.append(new_node) self.copy_style(old_node, new_node) @staticmethod def copy_style(old_node, new_node): # ToDo: Implement this later depending on the choice of the user (keep Inkscape colors vs. Tex colors) return
% old_filename) if query_yes_no( "Keep `%s` from previous installation?" % old_filename): files_to_keep[old_filename] = new_filename args.keep_previous_installation_files = True else: args.keep_previous_installation_files = False if not args.keep_previous_installation_files: files_to_keep = {} with TemporaryDirectory() as tmp_dir, \ StashFiles(stash_from=args.inkscape_extensions_path, rel_filenames=files_to_keep, tmp_dir=tmp_dir ): remove_previous_installation(args.inkscape_extensions_path) copy_extension_files(src="extension/*", dst=args.inkscape_extensions_path, if_already_exists="overwrite") settings.save() logger.log( SUCCESS, "--> TexText has been SUCCESSFULLY installed on your system <--") exit(EXIT_SUCCESS)