]> granicus.if.org Git - graphviz/commitdiff
Migrate output format doc generation ksh to jinja2
authorMark Hansen <mark@markhansen.co.nz>
Sat, 16 May 2020 08:08:12 +0000 (18:08 +1000)
committerMark Hansen <mark@markhansen.co.nz>
Fri, 29 May 2020 12:20:29 +0000 (22:20 +1000)
This commit replaces the ksh-based templating with Python Jinja2
templating.

Previously output.html was generated with:

- output.1
- the output of mkoutput.sh
- output.2

My end goal here is to make it much simpler to generate more complex
output format docs - however I've made this change so that the output is
virtually unchanged, except for HTML-escaping a single apostrophe.

My plan is to follow this up with adding some more features to the
output format documentation.

Templating in general provides a few opportunities:

- safe auto-html-escaping
- easier editing, easier adding of more data
- some separation of logic and templating
- inclusion of subtemplates

I chose Jinja2 because:

- It's the most popular Python templating engine, used by mature
  products like Ansible and Flask.
- Graphviz's docs generation already takes a dependency on Python for
  jconvert.py.
- The Python language is pretty accessible to contributors.
- Jinja2 seems pretty stable, going back to 2007. Unlikely to break
  soon.

Alternatives considered:
- I could just as well have gone with Go's stdlib template/html but I
  don't think Go is already used to build Graphviz, and Go isn't as
  popular as Python, so not as accessible to contributors.
- Python Django templates: they're pretty similar to Jinja2, but I think
  Django's templates are more heavyweight

doc/info/output.html
doc/infosrc/Makefile
doc/infosrc/mkoutput.py [new file with mode: 0755]
doc/infosrc/mkoutput.sh [deleted file]
doc/infosrc/outputs
doc/infosrc/requirements.txt [new file with mode: 0644]
doc/infosrc/templates/output.html.j2 [new file with mode: 0644]

index 95f5410800f4a7ce3f660f8519998de36754d676..36144bc3120bb02b48335a4131390b2bf6135125 100644 (file)
@@ -92,7 +92,7 @@ formats need to be interpreted in this manner.
  <TR><TD ALIGN=CENTER><A NAME=a:pdf HREF=#d:pdf>pdf</A>
 </TD><TD>Portable Document Format (PDF)</TD> </TR>
  <TR><TD ALIGN=CENTER><A NAME=a:pic HREF=#d:pic>pic</A>
-</TD><TD>Kernighan's PIC graphics language</TD> </TR>
+</TD><TD>Kernighan&#39;s PIC graphics language </TD> </TR>
  <TR><TD ALIGN=CENTER><A NAME=a:plain HREF=#d:plain>plain</A>
 <BR><A NAME=a:plain-ext HREF=#d:plain-ext>plain-ext</A>
 </TD><TD>Simple text format</TD> </TR>
index 9a5b20308d1f29285fefec2eb53bff4abdb98aef..01ce342f45107123477294f874f3ecbff86310b6 100644 (file)
@@ -6,9 +6,14 @@
 #  ps_to_png.sh and other make rules rely on netpbm tools 
 #  and psconvert, which is part of the GMT library.
 #  
-#  python is used to run jconvert.py, which converts the json schema
-#  graphviz_json_schema.json to html. This also relies on the python
-#  package json2html.
+#  Install python modules with:
+#  $ pip install --user -r requirements.txt
+#
+#  python is used to run:
+#  - jconvert.py, which converts the json schema graphviz_json_schema.json to html.
+#    This also relies on the python package json2html.
+#  - mkoutput.py, which converts templates/output.html to output.html
+#    This requires the jinja2 python package.
 #
 # The main product are 7 web pages:
 #    arrows.html  - arrow_grammar
@@ -142,14 +147,15 @@ colors.html : colors.1 colors.n ../../lib/common/color_names ../../lib/common/sv
        cat colors.n >> colors.html
        rm -rf colortmp
 
-schema.html : jconvert.py graphviz_json_schema.json
-       ./jconvert.py graphviz_json_schema.json schema.html
 
-output.html : output.1 output.2 outputs mkoutput.sh plugins.png jconvert.py schema.html
+output.html : output.1 output.2 outputs mkoutput.py plugins.png jconvert.py schema.html templates/output.html.j2
        cat output.1 > output.html
-       ./mkoutput.sh < outputs >> output.html
+       ./mkoutput.py < outputs >> output.html
        cat output.2 >> output.html
 
+schema.html : jconvert.py graphviz_json_schema.json
+       ./jconvert.py graphviz_json_schema.json schema.html
+
 html.html : html.1 html_grammar html.2 html.3 html1.gif html2.gif html3.gif html4.gif mklang
        ./mklang html_grammar gramtmp
        cat html.1 > html.html
@@ -222,7 +228,7 @@ distclean : clean
        (for s in $$(cat shapelist); do rm -f $$s.gif; done)
 
 EXTRA_DIST = $(XGIF) mklang.y mkarrows.sh mkattrs.sh mkshapes.sh mkstyles.sh mktapers.sh \
-             mktypes.sh mkarrowtbl.sh mkoutput.sh mkshhtml.sh \
+             mktypes.sh mkarrowtbl.sh mkoutput.py mkshhtml.sh \
                   ps_to_png.sh arrow_grammar grammar html_grammar \
              shapelist attrs.1 colors.1 colors.n \
              output.1 output.2 html.1 html.2 html1.dot html.3 \
diff --git a/doc/infosrc/mkoutput.py b/doc/infosrc/mkoutput.py
new file mode 100755 (executable)
index 0000000..ac350c4
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Takes `outputs` as stdin and generates output.html
+# Uses `templates/output.html.j2`
+# See `outputs` file for format documentation.
+
+import jinja2
+import markupsafe
+import re
+import sys
+from typing import Dict, Tuple
+
+HEADER_RE = re.compile(r'^:(?P<params>[^:]+):(?P<format>.*)')
+
+# Tuple of command-line-params for an output format, e.g. ('jpg', 'jpeg', 'jpe')
+params : Tuple[str, ...] = ()
+
+# Map from tuple of command-line-params to full name of the output format
+formats : Dict[Tuple[str, ...], str] = {}
+
+# Map from tuple of command-line-params to an HTML description string
+html_descriptions : Dict[Tuple[str, ...], str]  = {}
+
+for line in sys.stdin:
+    # Skip comment lines.
+    if line.startswith('#'):
+        continue
+
+    m = HEADER_RE.match(line)
+    if m:
+        # This is a header line. Grab out the values.
+
+        # Command-line formats are slash-separated.
+        params = tuple(m.group('params').split('/'))
+
+        # Full format name is plain text
+        formats[params] = m.group('format')
+
+        # Set an empty string html description, ready to append to.
+        html_descriptions[params] = ''
+    else:
+        # This is an HTML line, possibly a continuation of a previous HTML line.
+        html_descriptions[params] += line
+
+
+env = jinja2.Environment(
+    # Load template files from ./templates/
+    loader=jinja2.FileSystemLoader('templates'),
+    # Auto-HTML-escape any html or xml files.
+    autoescape=jinja2.select_autoescape(['html', 'xml', 'html.j2', 'xml.j2']),
+    # Whitespace control
+    trim_blocks=True,
+    lstrip_blocks=True,
+    # Raise exception on any attempt to access undefined variables.
+    undefined=jinja2.StrictUndefined,
+)
+template = env.get_template('output.html.j2')
+print(template.render(
+    formats=formats,
+    # Vouch for the HTML descriptions as being safe and not needing auto-HTML-escaping.
+    # This is reasonable because the HTML descriptions are not attacker-controlled.
+    descriptions={
+        params: markupsafe.Markup(desc)
+        for params, desc in html_descriptions.items()
+    }
+))
diff --git a/doc/infosrc/mkoutput.sh b/doc/infosrc/mkoutput.sh
deleted file mode 100755 (executable)
index 60a13f4..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#! /bin/ksh
-typeset -A desc fullname
-name=
-
-# Given the output specifications as stdin, read each one,
-# storing the formats long name and description in the fullname
-# and desc arrays, respectively, indexed by the format name.
-# The initial line of an item has the format :name:long name 
-# 
-# Lines beginning with '#' are treated as comments.
-OLDIFS="$IFS"
-IFS=
-while read line
-do
-  c=${line:0:1}
-  if [[ $c == '#' ]]
-  then 
-    continue
-  elif [[ $c == ':' ]]
-  then
-    if [[ -n "$name" ]]
-    then
-      desc[$name]=$txt
-      fullname[$name]=$tag
-      txt=""
-    fi
-    line=${line#:}
-    if [[ "$line" == *:* ]]
-    then
-      name=${line%%:*}
-      tag=${line#$name:}
-    else
-      name=$line
-      tag=""
-    fi
-  else
-    txt="$txt${line}\n"
-  fi
-done
-IFS="$OLDIFS"
-
-if [[ -n "$name" ]]
-then
-  desc[$name]=$txt
-  fullname[$name]=$tag
-  txt=""
-fi
-
-#print ${!fullname[@]}
-#print ${desc[@]}
-#exit
-
-set -s ${!desc[@]}
-
-# Output a brief description of the formats as a table.
-# This is followed by a list of the formats, with the long
-# description of each.
-# The formats are alphabetized on output.
-# Note that an item may have multiple names, i.e., the first
-# field may have the format name1/name2/name3.
-# The output format <name> is given the anchor a:<name> in the
-# table and d:<name> in the list.
-
-print "<TABLE ALIGN=CENTER>"
-print "<TR><TH>Command-line<BR>parameter</TH><TH>Format</TH></TR>"
-for i
-do
-  print -n " <TR>";
-  print -n "<TD ALIGN=CENTER>";
-  first=yes
-  for n in ${i//\// }
-  do
-    if [[ -n $first ]]
-    then
-      first=
-    else
-      print -n "<BR>"
-    fi 
-    printf "<A NAME=a:%s HREF=#d:%s>%s</A>\n" $n $n $n
-  done
-  print -n "</TD><TD>"
-  print -n ${fullname[$i]};  print "</TD> </TR>";
-done
-print "</TABLE>"
-
-print "<HR>"
-
-#set -s ${!desc[@]}
-print "<H2>Format Descriptions</H2>\n<DL>"
-for i
-do
-  first=yes
-  for n in ${i//\// }
-  do
-    if [[ -n $first ]]
-    then
-      first=
-    else
-      print -n ","
-    fi 
-    printf "<DT><A NAME=d:%s HREF=#a:%s><STRONG>%s</STRONG></A>\n" $n $n $n
-  done
-  print "<DD>${desc[$i]}"
-done
-print "</DL>\n<HR>"
-
-exit 0
index df3b1a1d2d4a9e0eeff8e7eec0ecbe269e5ae9ce..1ae6f73980b4a35154feb2fded0f609531e8f8dd 100644 (file)
@@ -1,8 +1,8 @@
 # List of Graphviz output formats
-# Each item consists of line of the form :<name>:<long description>
+# Each item consists of line of the form :<param1>[/<param2>...]:<format>
 # followed by a description of the format in HTML
-# The name may consist of multiple, related names, separated by '/'.
-# The long description is just text.
+# The command-line params may consist of multiple, related params, separated by '/'.
+# The format is just text.
 # The items are alphabetized when the page is created.
 #
 :eps:Encapsulated PostScript
@@ -469,28 +469,28 @@ The only real advantages to these formats is their terseness and their
 ease of parsing. In general, the <A HREF=#d:dot>dot</A> and
 <A HREF=#d:xdot>xdot</A> are preferable in terms of the quantity of
 information provided.
-:bmp: Windows Bitmap Format
+:bmp:Windows Bitmap Format
 Outputs images in the Windows <A HREF="http://en.wikipedia.org/wiki/Bitmap">BMP</A> format.
-:ico: Icon Image File Format
+:ico:Icon Image File Format
 Outputs images in the Windows <A HREF="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)">ICO format</A>.
-:pdf: Portable Document Format (PDF)
+:pdf:Portable Document Format (PDF)
 Produces <A HREF="http://www.adobe.com/devnet/pdf/">PDF</A> output.
 (This option assumes Graphviz includes the Cairo renderer.)
 Alternatively, one can use the <A HREF="#d:ps2">ps2</A> option to
 produce PDF-compatible PostScript, and then use a ps-to-pdf converter.
-:tif/tiff: TIFF (Tag Image File Format)
+:tif/tiff:TIFF (Tag Image File Format)
 Produces <A HREF="http://www.libtiff.org/">TIFF</A> output.
-:vml/vmlz: Vector Markup Language (VML)
+:vml/vmlz:Vector Markup Language (VML)
 Produces <A HREF="http://www.w3.org/TR/NOTE-VML">VML</A> output,
 the latter in compressed format.
 <P>
 See <A HREF=#ID>Note</A>.
-:gtk: GTK canvas
+:gtk:GTK canvas
 Creates a <A HREF="http://www.gtk.org/">GTK</A> window and displays the output there.
-:webp: Image format for the Web
+:webp:Image format for the Web
 Produces output in the image format for the Web (WEBP) format, optimized for
 web devices such as tablets.
 See Wikipedia's <a href="http://en.wikipedia.org/wiki/Webp">WebP</a>
 or Google's <a href="http://code.google.com/speed/webp/">webp</a> pages.
-:xlib/x11: Xlib canvas
+:xlib/x11:Xlib canvas
 Creates an <A HREF="http://en.wikipedia.org/wiki/Xlib">Xlib</A> window and displays the output there.
diff --git a/doc/infosrc/requirements.txt b/doc/infosrc/requirements.txt
new file mode 100644 (file)
index 0000000..1097146
--- /dev/null
@@ -0,0 +1,3 @@
+json2html==1.3.0
+Jinja2==2.10
+MarkupSafe==1.0
diff --git a/doc/infosrc/templates/output.html.j2 b/doc/infosrc/templates/output.html.j2
new file mode 100644 (file)
index 0000000..d82c712
--- /dev/null
@@ -0,0 +1,30 @@
+<TABLE ALIGN=CENTER>
+<TR><TH>Command-line<BR>parameter</TH><TH>Format</TH></TR>
+{% for params, format in formats | dictsort %}
+ <TR><TD ALIGN=CENTER>
+  {%- for p in params -%}
+    <A NAME=a:{{p}} HREF=#d:{{p}}>{{p}}</A>
+    {%- if not loop.last %}
+
+<BR>
+    {%- endif %}
+  {%- endfor %}
+
+</TD><TD>{{ format }}</TD> </TR>
+{% endfor %}
+</TABLE>
+<HR>
+<H2>Format Descriptions</H2>
+<DL>
+{% for params, description in descriptions | dictsort %}
+  {% for p in params %}
+    {% if not loop.first -%}
+      ,
+    {%- endif -%}
+    <DT><A NAME=d:{{p}} HREF=#a:{{p}}><STRONG>{{p}}</STRONG></A>
+  {% endfor -%}
+  <DD>
+  {{- description }}
+{% endfor %}
+</DL>
+<HR>