From 97d31e535a0ecd7ec1af66169ca9bf7bd5447c97 Mon Sep 17 00:00:00 2001 From: Richard Williamson Date: Sat, 2 Feb 2019 23:09:59 +0100 Subject: [PATCH] Robustify xymatrix rendering and improve nowiki handling We make some tweaks to the xymatrix diagram renderer to allow it to handle correctly a wider range of cases: curved arrows for example. We move from \xymatrix{...} to \begin{xymatrix} ... \end{xymatrix} to make it easier to detect the diagram correctly when mathematics is used inside it. We also tweak the ... renderer and the way it is called in certain cases to allow Tikz and Xymatrix diagram code, as well as centred code, to be able to be displayed verbatim. --- script/author_to_user | 1 + script/src/diagrams/diagram.py | 58 +++++++++++++++++++++- script/src/diagrams/xypic_diagram_template | 8 ++- script/src/renderer/centre_block.py | 8 ++- script/src/renderer/nowiki_block.py | 8 +-- script/src/renderer/tikz_diagram_block.py | 7 ++- script/src/renderer/xypic_diagram_block.py | 35 ++++++++++--- 7 files changed, 106 insertions(+), 19 deletions(-) diff --git a/script/author_to_user b/script/author_to_user index 2c8d81bd..efd64de4 100644 --- a/script/author_to_user +++ b/script/author_to_user @@ -2,3 +2,4 @@ Bas Spitters, spitters Max S. New, maxsnew N. Raghavendra, raghu Nyshadham Raghavendra, raghu +Pieter Cuijpers, Pieter diff --git a/script/src/diagrams/diagram.py b/script/src/diagrams/diagram.py index 3d699d96..d564a63f 100755 --- a/script/src/diagrams/diagram.py +++ b/script/src/diagrams/diagram.py @@ -29,6 +29,10 @@ logging_file_handler.setFormatter(logging_formatter) logger.addHandler(logging_file_handler) +class DocumentParametersException(Exception): + def __init__(self, message): + super().__init__(message) + class PdfRenderingException(Exception): def __init__(self, message): super().__init__(message) @@ -95,11 +99,56 @@ def tikz_tex_source(diagram, is_commutative_diagram): tikz_libraries = "\\usetikzlibrary{cd}", tikz_diagram = diagram) +def parse_document_parameters(document_parameters): + parsed_parameters = { "document_header_parameters": [ "12pt" ] } + for parameter in document_parameters.split(","): + split_at_equals = parameter.split("=") + if len(split_at_equals) != 2: + raise DocumentParametersException( + "The following does not have the expected format: " + + parameter) + key = split_at_equals[0].strip() + value = split_at_equals[1].strip() + if key == "font": + parsed_parameters["font_size"] = value + elif key == "border": + parsed_parameters["document_header_parameters"].append(parameter) + else: + raise DocumentParametersException( + "The key " + + key + + " of the following parameter is not recognised: " + + parameter) + return parsed_parameters + +def extract_document_parameters(diagram): + split_at_first_curly_brace = diagram.split("{", 1) + document_parameters = { + "font_size": "", + "document_header_parameters": ["12pt"] } + document_parameters_block = find_block.Block( + "[", + "]", + lambda within_square_brackets: document_parameters.update( + parse_document_parameters(within_square_brackets)), + False) + document_parameters_processor = find_block.Processor([ + document_parameters_block]) + processed_preamble = document_parameters_processor.process( + split_at_first_curly_brace[0]) + return ( + document_parameters, + processed_preamble + "{" + split_at_first_curly_brace[1]) + def xypic_tex_source(diagram): + document_parameters, diagram = extract_document_parameters(diagram) xypic_diagram_template_path = os.environ["NLAB_XYPIC_DIAGRAM_TEMPLATE"] with open(xypic_diagram_template_path, "r") as xypic_diagram_template_file: xypic_diagram_template = xypic_diagram_template_file.read() return string.Template(xypic_diagram_template).substitute( + document_parameters = ", ".join( + document_parameters["document_header_parameters"]), + font_size = document_parameters["font_size"], xypic_diagram = diagram) def create_pdf( @@ -207,8 +256,15 @@ def main(): "\nThe error was: " + str(svgRenderingException)) sys.exit(message) + except DocumentParametersException as documentParametersException: + message = ( + "An error occurred when rendering the following diagram. \n" + + diagram + + "\n " + + str(documentParametersException)) + logger.warning(message) + sys.exit(message) except Exception as exception: - raise exception message = ( "An unexpected error occurred when creating an SVG from a PDF " + "for the following diagram. \n" + diff --git a/script/src/diagrams/xypic_diagram_template b/script/src/diagrams/xypic_diagram_template index e97ef416..b6b734b2 100644 --- a/script/src/diagrams/xypic_diagram_template +++ b/script/src/diagrams/xypic_diagram_template @@ -1,9 +1,13 @@ -\documentclass[12pt]{standalone} +\documentclass[$document_parameters]{standalone} -\usepackage[all]{xy} +\usepackage[all, 2cell]{xy} + +\UseTwocells \begin{document} +$font_size + $xypic_diagram \end{document} diff --git a/script/src/renderer/centre_block.py b/script/src/renderer/centre_block.py index 335f103e..27be99ae 100644 --- a/script/src/renderer/centre_block.py +++ b/script/src/renderer/centre_block.py @@ -5,6 +5,7 @@ """ import find_block +import nowiki_block def initial_centre_processor(content): return ( @@ -33,8 +34,11 @@ def define_centre(): True) def handle_initial_centring(content): - return find_block.Processor([define_center(), define_centre()]).process( - content) + processor = find_block.Processor([ + define_center(), + define_centre(), + nowiki_block.define(True)]) + return processor.process(content) def handle_post_centring(content): centre_paragraph_block = find_block.Block( diff --git a/script/src/renderer/nowiki_block.py b/script/src/renderer/nowiki_block.py index 09832799..c5467da5 100644 --- a/script/src/renderer/nowiki_block.py +++ b/script/src/renderer/nowiki_block.py @@ -6,14 +6,14 @@ """ import find_block -def nowiki_processor(nowiki_content): - if "[[!include" in nowiki_content: +def nowiki_processor(nowiki_content, retain_nowiki_block): + if retain_nowiki_block or ("[[!include" in nowiki_content): return "" + nowiki_content + "" return nowiki_content -def define(): +def define(retain_nowiki_block = False): return find_block.Block( "", "", - nowiki_processor, + lambda content: nowiki_processor(content, retain_nowiki_block), True) diff --git a/script/src/renderer/tikz_diagram_block.py b/script/src/renderer/tikz_diagram_block.py index 063b5cdd..84157299 100644 --- a/script/src/renderer/tikz_diagram_block.py +++ b/script/src/renderer/tikz_diagram_block.py @@ -7,6 +7,7 @@ import find_block import os import subprocess +import nowiki_block class TikzDiagramException(Exception): def __init__(self, message): @@ -66,7 +67,9 @@ def define_tikz_commutative_diagram(): True) def handle_tikz_diagrams(content): - processor = find_block.Processor( - [ define_tikz(), define_tikz_commutative_diagram() ]) + processor = find_block.Processor([ + define_tikz(), + define_tikz_commutative_diagram(), + nowiki_block.define(True) ]) return processor.process(content) diff --git a/script/src/renderer/xypic_diagram_block.py b/script/src/renderer/xypic_diagram_block.py index ebcfd442..6b51d385 100644 --- a/script/src/renderer/xypic_diagram_block.py +++ b/script/src/renderer/xypic_diagram_block.py @@ -7,15 +7,23 @@ import find_block import os import subprocess +import nowiki_block class XyPicDiagramException(Exception): def __init__(self, message): super().__init__(message) +def remove_new_lines_at_start_of_block(diagram): + split_at_opening_brace = diagram.split("}", 1) + if len(split_at_opening_brace) == 0: + raise XyPicDiagramException( + "Missing end brace } after \\begin{xymatrix") + return split_at_opening_brace[0] + "{" + split_at_opening_brace[1].strip() + def xypic_diagram_processor(xypic_diagram): xypic_diagram = ( "\\xymatrix@" + - xypic_diagram + + remove_new_lines_at_start_of_block(xypic_diagram) + "}") diagram_api_path = os.environ["NLAB_DIAGRAM_API_PATH"] completed_xypic_diagram_process = subprocess.run( @@ -30,21 +38,32 @@ def xypic_diagram_processor(xypic_diagram): def define_xymatrix_default_size(): return find_block.Block( - "\\xymatrix{", - "}", + "\\begin{xymatrix}", + "\\end{xymatrix}", + lambda diagram: xypic_diagram_processor( + "=5em}" + diagram), + True) + +def define_xymatrix_document_parameters(): + return find_block.Block( + "\\begin{xymatrix[", + "\end{xymatrix}", lambda diagram: xypic_diagram_processor( - "=5em{" + diagram), + "=5em[" + diagram), True) def define_xymatrix(): return find_block.Block( - "\\xymatrix@", - "}", + "\\begin{xymatrix@", + "\\end{xymatrix}", xypic_diagram_processor, True) def handle_xypic_diagrams(content): - processor = find_block.Processor( - [ define_xymatrix(), define_xymatrix_default_size() ]) + processor = find_block.Processor([ + define_xymatrix(), + define_xymatrix_default_size(), + define_xymatrix_document_parameters(), + nowiki_block.define(True) ]) return processor.process(content)