diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt
index 2cc921b2..7d4356d4 100644
--- a/thirdparty/CMakeLists.txt
+++ b/thirdparty/CMakeLists.txt
@@ -1,32 +1,32 @@
# This cmake file are managing embedded 3party Cantor dependencies
# 3dparty patched Discount
# Embedded for a while
include(ExternalProject)
set (DISCOUNT_ONLY_LIBRARY ON)
set (DISCOUNT_MAKE_INSTALL OFF)
ExternalProject_Add(
discount_project
- URL ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/discount-2.2.6-patched.tar
+ URL ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/discount-2.2.6-patched
SOURCE_SUBDIR cmake
CMAKE_ARGS DISCOUNT_ONLY_LIBRARY DISCOUNT_MAKE_INSTALL
CMAKE_CACHE_ARGS "-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true"
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/thirdparty
STEP_TARGETS configure build
EXCLUDE_FROM_ALL TRUE
)
ExternalProject_Get_Property(discount_project source_dir)
ExternalProject_Get_Property(discount_project binary_dir)
add_library(Discount::Lib STATIC IMPORTED)
set_target_properties(Discount::Lib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${source_dir}
IMPORTED_LOCATION ${binary_dir}/libmarkdown${CMAKE_STATIC_LIBRARY_SUFFIX}
POSITION_INDEPENDENT_CODE ON
)
add_dependencies(Discount::Lib discount_project-build)
set(Discount_FOUND TRUE)
# preview.sty
install(FILES thirdparty/standalone.cls DESTINATION ${KDE_INSTALL_DATADIR}/cantor/latex )
diff --git a/thirdparty/README.md b/thirdparty/README.md
new file mode 100644
index 00000000..4b1a6993
--- /dev/null
+++ b/thirdparty/README.md
@@ -0,0 +1,20 @@
+## 3rd party libraries
+
+This folder contains (patched) versions of libraries and files Cantor depends on.
+
+
+## DISCOUNT
+
+DISCOUNT is a implementation of Markdown markup language ([link](https://github.com/Orc/discount)).
+
+The version included here provides two additional patches:
+
+* Better LaTeX support: `$` math delimiter and `mkd_e_latex` callback (https://github.com/Orc/discount/pull/214)
+
+* Better recognition of the mathematical expressions between $...$, $$...$$
+
+
+## standalone.cls
+This file provides the LaTeX class and package 'standalone' ([link](https://ctan.org/tex-archive/macros/latex/contrib/standalone)),
+which allows TeX pictures or other TeX code in sub-files to be compiled standalone or as part of a main document.
+This package is used for the rendering of mathematical LaTeX expressions embedded in the Cantor's worksheet.
diff --git a/thirdparty/discount-2.2.6-patched.tar b/thirdparty/discount-2.2.6-patched.tar
deleted file mode 100644
index 28da1cf3..00000000
Binary files a/thirdparty/discount-2.2.6-patched.tar and /dev/null differ
diff --git a/thirdparty/discount-2.2.6-patched/COPYRIGHT b/thirdparty/discount-2.2.6-patched/COPYRIGHT
new file mode 100644
index 00000000..cab1d3d6
--- /dev/null
+++ b/thirdparty/discount-2.2.6-patched/COPYRIGHT
@@ -0,0 +1,30 @@
+->Copyright (C) 2007 David Loren Parsons.
+All rights reserved.<-
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of works must retain the original copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the original copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither my name (David L Parsons) nor the names of contributors to
+ this code may be used to endorse or promote products derived
+ from this work without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
diff --git a/thirdparty/discount-2.2.6-patched/CREDITS b/thirdparty/discount-2.2.6-patched/CREDITS
new file mode 100644
index 00000000..a806a53b
--- /dev/null
+++ b/thirdparty/discount-2.2.6-patched/CREDITS
@@ -0,0 +1,35 @@
+Discount is primarily my work, but it has only reached the point
+where it is via contributions, critiques, and bug reports from a
+host of other people, some of which are listed before. If your
+name isn't on this list, please remind me
+ -david parsons (orc@pell.portland.or.us)
+
+
+Josh Wood -- Plan9 support.
+Mike Schiraldi -- Reddit style automatic links, MANY MANY MANY
+ bug reports about boundary conditions and
+ places where I didn't get it right.
+Jjgod Jiang -- Table of contents support.
+Petite Abeille -- Many bug reports about places where I didn't
+ get it right.
+Tim Channon -- inspiration for the `mkd_xhtmlpage()` function
+Christian Herenz-- Many bug reports regarding my implementation of
+ `[]()` and `![]()`
+A.S.Bradbury -- Portability bug reports for 64 bit systems.
+Joyent -- Loan of a solaris box so I could get discount
+ working under solaris.
+Ryan Tomayko -- Portability requests (and the rdiscount ruby
+ binding.)
+yidabu -- feedback on the documentation, bug reports
+ against utf-8 support.
+Pierre Joye -- bug reports, php discount binding.
+Masayoshi Sekimura- perl discount binding.
+Jeremy Hinegardner- bug reports about list handling.
+Andrew White -- bug reports about the format of generated urls.
+Steve Huff -- bug reports about Makefile portability (for Fink)
+Ignacio Burgue?o-- bug reports about `>%class%`
+Henrik Nyh -- bug reports about embedded html handling.
+John J. Foerch -- bug reports about incorrect `–` and `—`
+ translations.
+
+
diff --git a/thirdparty/discount-2.2.6-patched/Csio.c b/thirdparty/discount-2.2.6-patched/Csio.c
new file mode 100644
index 00000000..1b418e0c
--- /dev/null
+++ b/thirdparty/discount-2.2.6-patched/Csio.c
@@ -0,0 +1,61 @@
+#include ", " " };
+ static char *End[] = { "", "\n");
+ for (i=0; i < NR(flagnames); i++) {
+ set = flags & flagnames[i].flag;
+ name = flagnames[i].name;
+ if ( not = (*name == '!') ) {
+ ++name;
+ set = !set;
+ }
+
+ if ( htmlplease ) {
+ if ( even ) fprintf(f, "
\n");
+ }
+}
+
+void
+mkd_mmiot_flags(FILE *f, MMIOT *m, int htmlplease)
+{
+ if ( m )
+ mkd_flags_are(f, m->flags, htmlplease);
+}
diff --git a/thirdparty/discount-2.2.6-patched/generate.c b/thirdparty/discount-2.2.6-patched/generate.c
new file mode 100644
index 00000000..adfa7abc
--- /dev/null
+++ b/thirdparty/discount-2.2.6-patched/generate.c
@@ -0,0 +1,2107 @@
+/* markdown: a C implementation of John Gruber's Markdown markup language.
+ *
+ * Copyright (C) 2007 David L Parsons.
+ * The redistribution terms are provided in the COPYRIGHT file that must
+ * be distributed with this source code.
+ */
+#include ");
+ fprintf(f, " \n");
+ }
+ even = !even;
+ }
+ if ( htmlplease ) {
+ if ( even ) fprintf(f, "\n");
+ fprintf(f, "");
+ }
+ else
+ fputc(' ', f);
+
+ if ( !set )
+ fprintf(f, htmlplease ? " ");
+ if ( !even ) fprintf(f, "" : "!");
+
+ fprintf(f, "%s", name);
+
+ if ( htmlplease ) {
+ if ( !set )
+ fprintf(f, "");
+ fprintf(f, "...
+ */
+static void
+delspan(MMIOT *f, int size)
+{
+ Qstring("", f);
+ ___mkd_reparse(cursor(f)-1, size, 0, f, 0);
+ Qstring("", f);
+}
+
+
+/* codespan() -- write out a chunk of text as code, trimming one
+ * space off the front and/or back as appropriate.
+ */
+static void
+codespan(MMIOT *f, int size)
+{
+ int i=0;
+
+ if ( size > 1 && peek(f, size-1) == ' ' ) --size;
+ if ( peek(f,i) == ' ' ) ++i, --size;
+
+ Qstring("", f);
+ code(f, cursor(f)+(i-1), size);
+ Qstring("
", f);
+} /* codespan */
+
+
+/* before letting a tag through, validate against
+ * MKD_NOLINKS and MKD_NOIMAGE
+ */
+static int
+forbidden_tag(MMIOT *f)
+{
+ int c = toupper(peek(f, 1));
+
+ if ( is_flag_set(f->flags, MKD_NOHTML) )
+ return 1;
+
+ if ( c == 'A' && is_flag_set(f->flags, MKD_NOLINKS) && !isthisalnum(f,2) )
+ return 1;
+ if ( c == 'I' && is_flag_set(f->flags, MKD_NOIMAGE)
+ && strncasecmp(cursor(f)+1, "MG", 2) == 0
+ && !isthisalnum(f,4) )
+ return 1;
+ return 0;
+}
+
+
+/* Check a string to see if it looks like a mail address
+ * "looks like a mail address" means alphanumeric + some
+ * specials, then a `@`, then alphanumeric + some specials,
+ * but with a `.`
+ */
+static int
+maybe_address(char *p, int size)
+{
+ int ok = 0;
+
+ for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size)
+ ;
+
+ if ( ! (size && *p == '@') )
+ return 0;
+
+ --size, ++p;
+
+ if ( size && *p == '.' ) return 0;
+
+ for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size )
+ if ( *p == '.' && size > 1 ) ok = 1;
+
+ return size ? 0 : ok;
+}
+
+
+/* The size-length token at cursor(f) is either a mailto:, an
+ * implicit mailto:, one of the approved url protocols, or just
+ * plain old text. If it's a mailto: or an approved protocol,
+ * linkify it, otherwise say "no"
+ */
+static int
+process_possible_link(MMIOT *f, int size)
+{
+ int address= 0;
+ int mailto = 0;
+ char *text = cursor(f);
+
+ if ( is_flag_set(f->flags, MKD_NOLINKS) ) return 0;
+
+ if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) {
+ /* if it says it's a mailto, it's a mailto -- who am
+ * I to second-guess the user?
+ */
+ address = 1;
+ mailto = 7; /* 7 is the length of "mailto:"; we need this */
+ }
+ else
+ address = maybe_address(text, size);
+
+ if ( address ) {
+ Qstring("", f);
+ mangle(text+mailto, size-mailto, f);
+ Qstring("", f);
+ return 1;
+ }
+ else if ( isautoprefix(text, size) ) {
+ printlinkyref(f, &linkt, text, size);
+ Qchar('>', f);
+ puturl(text,size,f, 1);
+ Qstring("
", f);
+ break;
+
+ case '>': if ( tag_text(f) )
+ Qstring(">", f);
+ else
+ Qchar(c, f);
+ break;
+
+ case '"': if ( tag_text(f) )
+ Qstring(""", f);
+ else
+ Qchar(c, f);
+ break;
+
+ case '!': if ( peek(f,1) == '[' ) {
+ pull(f);
+ if ( tag_text(f) || !linkylinky(1, f) )
+ Qstring("![", f);
+ }
+ else
+ Qchar(c, f);
+ break;
+
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
+ Qchar(c, f);
+ break;
+ /* A^B -> AB */
+ case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT)
+ || is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || (f->last == 0)
+ || ((ispunct(f->last) || isspace(f->last))
+ && f->last != ')')
+ || isthisspace(f,1) )
+ Qchar(c,f);
+ else {
+ char *sup = cursor(f);
+ int len = 0;
+
+ if ( peek(f,1) == '(' ) {
+ int here = mmiottell(f);
+ pull(f);
+
+ if ( (len = parenthetical('(',')',f)) <= 0 ) {
+ mmiotseek(f,here);
+ Qchar(c, f);
+ break;
+ }
+ sup++;
+ }
+ else {
+ while ( isthisalnum(f,1+len) )
+ ++len;
+ if ( !len ) {
+ Qchar(c,f);
+ break;
+ }
+ shift(f,len);
+ }
+ Qstring("",f);
+ ___mkd_reparse(sup, len, 0, f, "()");
+ Qstring("", f);
+ }
+ break;
+ case '_':
+ /* Underscores don't count if they're in the middle of a word */
+ if ( !(is_flag_set(f->flags, MKD_NORELAXED) || is_flag_set(f->flags, MKD_STRICT))
+ && isthisalnum(f,-1) && isthisalnum(f,1) ) {
+ Qchar(c, f);
+ break;
+ }
+ case '*':
+ /* Underscores & stars don't count if they're out in the middle
+ * of whitespace */
+ if ( isthisspace(f,-1) && isthisspace(f,1) ) {
+ Qchar(c, f);
+ break;
+ }
+ /* else fall into the regular old emphasis case */
+ if ( tag_text(f) )
+ Qchar(c, f);
+ else {
+ for (rep = 1; peek(f,1) == c; pull(f) )
+ ++rep;
+ Qem(f,c,rep);
+ }
+ break;
+
+ case '~': if ( is_flag_set(f->flags, MKD_NOSTRIKETHROUGH)
+ || is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || ! tickhandler(f,c,2,0, delspan) )
+ Qchar(c, f);
+ break;
+
+ case '`': if ( tag_text(f) || !tickhandler(f,c,1,1,codespan) )
+ Qchar(c, f);
+ break;
+
+ case '\\': switch ( c = pull(f) ) {
+ case '&': Qstring("&", f);
+ break;
+ case '<': c = peek(f,1);
+ if ( (c == EOF) || isspace(c) )
+ Qstring("<", f);
+ else {
+ /* Markdown.pl does not escape <[nonwhite]
+ * sequences */
+ Qchar('\\', f);
+ shift(f, -1);
+ }
+
+ break;
+ case '^': if ( is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_NOSUPERSCRIPT) ) {
+ Qchar('\\', f);
+ shift(f,-1);
+ break;
+ }
+ Qchar(c, f);
+ break;
+
+ case ':': case '|':
+ if ( is_flag_set(f->flags, MKD_NOTABLES) ) {
+ Qchar('\\', f);
+ shift(f,-1);
+ break;
+ }
+ Qchar(c, f);
+ break;
+
+ case EOF: Qchar('\\', f);
+ break;
+
+ case '[':
+ case '(':
+ Qchar(c, f);
+ break;
+
+ case '$': if ( is_flag_set(f->flags, MKD_LATEX) ) {
+ Qchar(c, f);
+ break;
+ }
+
+ default:
+ if ( escaped(f,c) ||
+ strchr(">#.-+{}]![*_\\()`", c) )
+ Qchar(c, f);
+ else {
+ Qchar('\\', f);
+ shift(f, -1);
+ if ( is_flag_set(f->flags, MKD_LATEX) ) {
+ mathhandlerExtended(f, "\\begin{equation}", "\\end{equation}")
+ || mathhandlerExtended(f, "\\begin{equation*}", "\\end{equation*}")
+ || mathhandlerExtended(f, "\\begin{align}", "\\end{align}")
+ || mathhandlerExtended(f, "\\begin{align*}", "\\end{align*}")
+ || mathhandlerExtended(f, "\\begin{bmatrix}", "\\end{bmatrix}")
+ || mathhandlerExtended(f, "\\begin{cases}", "\\end{cases}");
+ }
+ }
+ break;
+ }
+ break;
+
+ case '<': if ( !maybe_tag_or_link(f) )
+ Qstring("<", f);
+ break;
+
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
+ while ( isthisalnum(f,j) )
+ ++j;
+
+ if ( peek(f,j) != ';' )
+ Qstring("&", f);
+ else
+ Qchar(c, f);
+ break;
+
+ case '$': if ( is_flag_set(f->flags, MKD_LATEX) ) {
+ if (peek(f, 1) == '$' ) {
+ pull(f);
+ if ( mathhandler(f, '$', '$') )
+ break;
+ Qchar('$', f);
+ }
+ else {
+ int c2;
+ int i = 1;
+
+ while ( ((c2=peek(f,i)) != '$') && (c2 != EOF) )
+ i++;
+ if ( c2 != EOF ) {
+ Qchar('$', f);
+ cputc(6, f);
+ EXPAND(f->latex) = '$';
+ EXPAND(f->latex) = 6;
+ while (i-- > 0 ) {
+ char sym = pull(f);
+ EXPAND(f->latex) = sym;
+ Qchar(sym, f);
+ }
+ EXPAND(f->latex) = 31;
+ break;
+ }
+ }
+ }
+ /* fall through to default */
+
+ default: f->last = c;
+ Qchar(c, f);
+ break;
+ }
+ }
+ /* truncate the input string after we've finished processing it */
+ S(f->in) = f->isp = 0;
+} /* text */
+
+
+/* print a header block
+ */
+static void
+printheader(Paragraph *pp, MMIOT *f)
+{
+ if ( is_flag_set(f->flags, MKD_IDANCHOR) ) {
+ Qprintf(f, "\n", f);
+ while ( idx < S(p->text) ) {
+ first = idx;
+ if ( force && (colno >= S(align)-1) )
+ idx = S(p->text);
+ else
+ while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') ) {
+ if ( T(p->text)[idx] == '\\' )
+ ++idx;
+ ++idx;
+ }
+
+ Qprintf(f, "<%s%s>",
+ block,
+ alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]);
+ ___mkd_reparse(T(p->text)+first, idx-first, 0, f, "|");
+ Qprintf(f, "%s>\n", block);
+ idx++;
+ colno++;
+ }
+ if ( force )
+ while (colno < S(align) ) {
+ Qprintf(f, "<%s>%s>\n", block, block);
+ ++colno;
+ }
+ Qstring(" \n", f);
+ return colno;
+}
+
+
+static int
+printtable(Paragraph *pp, MMIOT *f)
+{
+ /* header, dashes, then lines of content */
+
+ Line *hdr, *dash, *body;
+ Istring align;
+ int hcols,start;
+ char *p;
+ enum e_alignments it;
+
+ hdr = pp->text;
+ dash= hdr->next;
+ body= dash->next;
+
+ if ( T(hdr->text)[hdr->dle] == '|' ) {
+ /* trim leading pipe off all lines
+ */
+ Line *r;
+ for ( r = pp->text; r; r = r->next )
+ r->dle ++;
+ }
+
+ /* figure out cell alignments */
+
+ CREATE(align);
+
+ for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) {
+ char first, last;
+ int end;
+
+ last=first=0;
+ for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
+ if ( p[end] == '\\' )
+ ++ end;
+ else if ( !isspace(p[end]) ) {
+ if ( !first) first = p[end];
+ last = p[end];
+ }
+ }
+ it = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT)
+ : (( last == ':') ? a_RIGHT : a_NONE );
+
+ EXPAND(align) = it;
+ start = 1+end;
+ }
+
+ Qstring("\n", f);
+ Qstring("\n", f);
+ hcols = splat(hdr, "th", align, 0, f);
+ Qstring("\n", f);
+
+ if ( hcols < S(align) )
+ S(align) = hcols;
+ else
+ while ( hcols > S(align) )
+ EXPAND(align) = a_NONE;
+
+ Qstring("\n", f);
+ for ( ; body; body = body->next)
+ splat(body, "td", align, 1, f);
+ Qstring("\n", f);
+ Qstring("
\n", f);
+
+ DELETE(align);
+ return 1;
+}
+
+
+static int
+printblock(Paragraph *pp, MMIOT *f)
+{
+ static char *Begin[] = { "", "
", f);
+ for ( blanks = 0; t ; t = t->next ) {
+ if ( S(t->text) > t->dle ) {
+ while ( blanks ) {
+ Qchar('\n', f);
+ --blanks;
+ }
+ code(f, T(t->text), S(t->text));
+ Qchar('\n', f);
+ }
+ else blanks++;
+ }
+ Qstring("
", f);
+}
+
+
+static void
+printhtml(Line *t, MMIOT *f)
+{
+ int blanks;
+
+ for ( blanks=0; t ; t = t->next )
+ if ( S(t->text) ) {
+ for ( ; blanks; --blanks )
+ Qchar('\n', f);
+
+ Qwrite(T(t->text), S(t->text), f);
+ Qchar('\n', f);
+ }
+ else
+ blanks++;
+}
+
+
+static void
+htmlify_paragraphs(Paragraph *p, MMIOT *f)
+{
+ ___mkd_emblock(f);
+
+ while (( p = display(p, f) )) {
+ ___mkd_emblock(f);
+ Qstring("\n\n", f);
+ }
+}
+
+
+#ifdef GITHUB_CHECKBOX
+static void
+li_htmlify(Paragraph *p, char *arguments, mkd_flag_t flags, MMIOT *f)
+{
+ ___mkd_emblock(f);
+
+ Qprintf(f, "\n", lang);
+ x = strlen(res);
+ for ( i=0; i < len; i++ ) {
+ switch (src[i]) {
+ case '&': strcpy(&src[x], "&");
+ x += 5 /*strlen(&)*/ ;
+ break;
+ case '<': strcpy(&src[x], "<");
+ x += 4 /*strlen(<)*/ ;
+ break;
+ case '>': strcpy(&src[x], ">");
+ x += 4 /*strlen(>)*/ ;
+ break;
+ default: res[x++] = src[i];
+ break;
+ }
+ }
+ strcpy(&res[x], "
\n");
+ return res;
+}
+
+
+struct h_opt opts[] = {
+ { 0, "html5", '5', 0, "recognise html5 block elements" },
+ { 0, "base", 'b', "url-base", "URL prefix" },
+ { 0, "debug", 'd', 0, "debugging" },
+ { 0, "version",'V', 0, "show version info" },
+ { 0, 0, 'E', "flags", "url flags" },
+ { 0, 0, 'F', "bitmap", "set/show hex flags" },
+ { 0, 0, 'f', "{+-}flags", "set/show named flags" },
+ { 0, 0, 'G', 0, "github flavoured markdown" },
+ { 0, 0, 'n', 0, "don't write generated html" },
+ { 0, 0, 's', "text", "format `text`" },
+ { 0, "style", 'S', 0, "output
+at the end of the line or a
+.Em
+at the beginning of a subsequent line.
+.Pp
+Be warned that style blocks work like footnote links -- no matter
+where you define them they are valid for the entire document.
+.Ss alpha lists
+Alphabetic lists (like regular numeric lists, but with alphabetic
+items) are supported. So:
+.nf
+ a. this
+ b. is
+ c. an alphabetic
+ d. list
+.fi
+will produce:
+.nf
+ header | +header | +
---|---|
text | +text | +