<?xml version="1.0" encoding="utf-8"?><article xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-subset Balisage-1.5">
    <title>Don't Touch My SGML</title>
    <info>
    <confgroup>
      <conftitle>Balisage: The Markup Conference 2026</conftitle>
      <confdates>August 3-7, 2026</confdates>
   </confgroup>
        <abstract>
            <para>SGML used to be the future of documentation. It still is, because no matter how
                much we push for XML, we still need SGML. Entire business models rely on it, and if
                we want to use our newfound technologies (hey, XML is only 28 as I write this), we'd
                better make sure we can include the old. Author in XML? Yes, sure. Publish the XML?
                Yes, absolutely. But don't touch my SGML</para>
            <para>This paper starts off from a use case involving three separate SGML vocabularies
                that had to be supported going forwards, no matter what. That use case evolved into
                a more general approach to converting SGML to XML, including adding other SGML
                vocabularies when needed, to take advantage of modern XML technologies for authoring
                and publishing, including publishing back to SGML when needed.</para>
            <para>The resulting set of scripts and resources has been made freely available in the
                hope that it can help others looking for a way to manage SGML.</para>
        </abstract>
        <author>
            <personname>
                <firstname>Ari</firstname>
                <surname>Nordström</surname>
            </personname>
            <personblurb>
                <para>Ari is an independent markup geek based in Göteborg, Sweden. He has provided
                    angled brackets to many organisations and companies across a number of borders
                    over the years, some of which deliver the rule of law, help dairy farmers make a
                    living, and assist in servicing commercial aircraft. And others are just for
                    fun.</para>
                <para>Ari is the proud owner and head projectionist of Western Sweden's last
                    functioning 35/70mm cinema, situated in his garage, which should explain why he
                    once wrote a paper on automating commercial cinemas using XML.</para>
            </personblurb>
        </author>
<legalnotice><para>© 2026 Ari Nordström</para></legalnotice>
    </info>
    
    <section>
        <title>This May Not Be A Good Idea</title>
        <para>A well-known software provider asked me to help implement the ATA iSpec 2200 aerospace
            standard [<xref linkend="ata"/>] for their
            client. I warned them that SGML is outdated and poorly supported by their own software—I
            have plenty of experience trying to implement both SGML and XML for it<footnote>
                <para>Which, I guess, is why they contacted me in the first place.</para>
            </footnote>. They insisted their SGML support was fixed, so I accepted the contract,
            though I recommended authoring in XML and only publishing to SGML.  </para>
        <para>Testing proved otherwise and I found numerous issues. Crucially, while the latest
            build had some functionality, the client’s older version failed entirely.</para>
        <para>Then I got to meet the client.</para>
        <para>It quickly emerged that ATA is only one of the many SGML and XML markup vocabularies
            they use. There is the SGML-based standard for technical manuals, MIL-STD-38784 [<xref linkend="milspec"/>], but also several
            flavours of the aerospace standard S1000D [<xref linkend="s1000d"/>], some SGML (issues
            1.8, and 2.3) and some XML (the 4.x range , of which issue 4.1 for authoring was a
            must).</para>
        <note>
            <para>The aerospace industry is very, very conservative. Keeping planes airborne is the
                priority, making system upgrades a secondary concern to data consistency<footnote>
                    <para>S1000D Change 1.8, for example, is more than 25 years old.</para>
                </footnote>. Aerospace manufacturing is largely a collaborative process, involving
                hundreds or even thousands of organisations and, sometimes, multiple nations working together<footnote>
                    <para>Witness, for example, the Eurofighter Typhoon, a wholly European
                        collaboration to produce a multirole fighter aircraft. The Eurofighter is a
                        collaboration between several European nations and thousands of European
                        companies.</para>
                </footnote> to produce aircraft. To collaborate, the partners need to agree to use
                specific versions of the standards; the information is shared and reused by multiple
                organisations.</para>
        </note>
        <para>Being a mere contractor, enlisted to help out with implementing SGML, I'll admit that
            all this felt rather uncomfortable. SGML wouldn't work, but neither would proposing to
            move everything to XML. A middle ground was needed.</para>
    </section>
    <section>
        <title>The Middle Ground</title>
        <para>XML is the better choice today, of course. It's not because XML would somehow be
                <emphasis>better</emphasis> than SGML, whatever that means<footnote>
                <para>In fact, I'd argue that it isn't. I love SGML and its flexibility, and I
                    really miss some of the constructs SGML allowed but XML does not.</para>
            </footnote>, but it's important to note that there's a lot of support for XML nowadays,
            be it tools, processes or related standards, but very, very little for SGML. XML may not
            be the hottest new thing anymore, but it's mature and literally all over the place.
            SGML, on the other hand, is literally what XML replaced. The infrastructure, the
            standardisation, the tools, it all happened for XML<footnote>
                <para>And keeps on happening. Just the other day, oXygen announced S1000D 4.x
                    support. </para>
            </footnote>.</para>
        <para>I've sold in XML in place of SGML for aerospace before, the idea always being to
            convert the SGML to XML, author everything in XML, and use XML technologies to publish
            to all required output formats, including SGML. For an ATA iSpec 2200 project I did in
            the past [<xref linkend="topic-based-sgml"/>], we had something like this:</para>
        <figure>
            <title>ATA SGML to XML Conversion and Publishing</title>
            <mediaobject>
                <imageobject>
                    <imagedata fileref="../../../vol31/graphics/Nordstrom01/Nordstrom01-001.png" width="80%"/>
                </imageobject>
            </mediaobject>
        </figure>
        <para>Of course, now I also needed to support MIL-STD-38784 and S1000D.</para>
        <para>Looking closer at MIL-STD-38784, I found that the US Government had already done some
            of my work for me. There was an XML version of the DTD from around 2005, so rather than
            spending time writing a new XML <quote>MIL-SPEC-like</quote> DTD, I could use
            theirs:</para>
        <figure>
            <title>MIL-STD-38784 SGML to XML</title>
            <mediaobject>
                <imageobject>
                    <imagedata fileref="../../../vol31/graphics/Nordstrom01/Nordstrom01-002.png" width="80%"/>
                </imageobject>
            </mediaobject>
        </figure>
        <para>The S1000D Issue 1.8 SGML DTDs (6 of them) evolved into XML Schemas from issue 3.0 and
            onwards, so I resolved to use issue 4.1 XML, now 26 XML Schemas and a host of new
            features. Importantly, issue 4.1 includes a way to define <emphasis>business
                rules</emphasis> (the <quote>BREX</quote> data module type) declaring how the
            standard is to be used. In the case of converting 1.8 SGML to 4.1 XML, then, I could
            define business rules that limit the use of 4.1 to features also available in
            1.8.:</para>
        <figure>
            <title>S1000D 1.8 SGML to 4.1 XML</title>
            <mediaobject>
                <imageobject>
                    <imagedata fileref="../../../vol31/graphics/Nordstrom01/Nordstrom01-003.png" width="80%"/>
                </imageobject>
            </mediaobject>
        </figure>
        <para>Of course, the 4.1 XML would be published to any required format, including issue 1.8
            SGML.</para>
        <para>Rather than creating yet another one-off, I resolved to write a more generic library,
            extensible to other SGML vocabularies, and make it freely available to anyone still
            needing to convert SGML to XML. The results are on GitHub [<xref linkend="sgml2xml-tng"/>].</para>
    </section>
    <section>
        <title>SGML to XML</title>
        <para>Common to all three is that they all need to convert SGML to well-formed XML; we
            always need to handle the following:</para>
        <itemizedlist>
            <listitem>
                <para>DTDs; we'll need to know about things like tag omission<footnote>
                        <para>SGML would allow you to, for example, leave out end tags if the parser
                            could infer its position.</para>
                    </footnote>, empty elements, default attribute values, etc.</para>
                <note>
                    <para>Tag omission, as indicated in the DTD, might frequently leave out end
                        tags.</para>
                </note>
            </listitem>
            <listitem>
                <para>Entities (graphic, reusable text fragments, character sets, etc)</para>
            </listitem>
            <listitem>
                <para>Syntax</para>
            </listitem>
        </itemizedlist>
        <para>There's no such thing as a well-formed SGML file; they must all reference a DTD<footnote>
                <para>Otherwise known as the DTD <quote>external subset</quote>.</para>
            </footnote> using a <code>DOCTYPE</code> declaration. The <code>DOCTYPE</code> will
            usually have an <quote>internal subset</quote> that contains entity declarations for
            graphics and reusable text, but it can also include part of, or the entire DTD.</para>
        <para>This is important, because when we process SGML, we'll need to use James Clark's SP
            package [<xref linkend="sp-clark"/>]—SP is
            the only game in town, really, but includes everything we need, from parsing
                (<code>nsgmls</code>) and normalisation (<code>spam</code>) to SGML to XML syntax
            conversion (<code>sx</code>). SP is all about parsing and processing instances, however,
            so we need to normalise the SGML—pull the DTD and everything else into a single
            file—before we can convert it to XML syntax.</para>
        <para>In addition, an SGML document will use an <emphasis>SGML declaration</emphasis><footnote>
                <para>Referenced via a catalog file, included directly in the instance, or implied
                    by the SGML application.</para>
            </footnote>, a text file that configures the SGML as used by the instances. It'll
            commonly define things like character sets to be used, attribute literal lengths, and
            whether or not we care about case for tag names, but there's a lot more. For example, we
            may redefine the tag limiters to be used.</para>
        <para>That's pretty much where I drew the line; whilst case sensitivity (or the lack of it)
            is important, I don't intend to support curly-bracket tag limiters just yet. And there
            are other features that give me pause.</para>
        <section xml:id="sgml-processing">
            <title>SGML Processing</title>
            <para>Let's assume we have this rather trivial SGML DTD:</para>
<programlisting xml:space="preserve">
&lt;!-- NOTATIONS --&gt;
&lt;!NOTATION cgm PUBLIC 
"-//USA-DOD//NOTATION Computer Graphics Metafile//EN"               &gt;

&lt;!NOTATION ccitt4 PUBLIC 
"-//USA-DOD//NOTATION CCITT Group4 Facsimile//EN"                   &gt;

&lt;!NOTATION gcf PUBLIC
"-//ATA-TEXT//NOTATION Graphic Companion File//EN"                  &gt;

&lt;!NOTATION pdf PUBLIC
"-//Adobe Inc.//NOTATION Portable Document Format//EN"              &gt;

&lt;!NOTATION png PUBLIC
"-//W3C//NOTATION Portable Network Graphics//EN"                    &gt;

&lt;!NOTATION jpeg PUBLIC
"+//ISBN 0-7923-9432-1::Graphic Notation//NOTATION Joint
 Photographic Experts Group Raster//EN"                             &gt;

&lt;!NOTATION vrml PUBLIC 
"-//ATA-TEXT//NOTATION Virtual Reality Modeling Language//EN"       &gt;

&lt;!NOTATION mpeg PUBLIC
"-//ATA-TEXT//NOTATION Motion Pictures Expert Group//EN"            &gt;

&lt;!NOTATION mp3 PUBLIC
"-//ATA-TEXT//NOTATION Motion Pictures Expert Group, Audio
 Layer 3//EN"                                                       &gt;

&lt;!-- ENTITIES --&gt;

&lt;!ENTITY % id.att   "id ID #IMPLIED" &gt;

&lt;!ELEMENT doc   - - (title, chapter+) &gt;
&lt;!ATTLIST doc   %id.att; &gt;

&lt;!ELEMENT title - - (#PCDATA) &gt;

&lt;!ELEMENT chapter   - - (title, (para | graphic)+, section*) &gt;
&lt;!ATTLIST chapter   %id.att; &gt;

&lt;!ELEMENT section   - - (title, (para | graphic)+, section*) &gt;
&lt;!ATTLIST section   %id.att; &gt;

&lt;!ELEMENT para      - - (#PCDATA) &gt;

&lt;!ELEMENT graphic   - O EMPTY &gt;
&lt;!ATTLIST graphic   href CDATA #IMPLIED
                    name ENTITY #IMPLIED
                    %id.att; &gt;
</programlisting>
            <note>
                <para>The <code>graphic</code> element has two ways of referencing a graphic, an
                        <code>ENTITY</code>-based using <code>@name</code> and a direct link using
                        <code>@href</code>. Before you protest, remember that this DTD is just for
                    testing (it's part of my repo for that purpose—see <xref linkend="sgml2xml-tng"/>).</para>
            </note>
            <para>A valid SGML instance might look like this:</para>
<programlisting xml:space="preserve">
&lt;!DOCTYPE doc PUBLIC "-//CWORD//DTD DOC TEST//EN" "doc.dtd" [
&lt;!ENTITY bill SYSTEM "Bill_Murray.jpg" NDATA jpeg &gt;
]&gt;
&lt;doc&gt;
    &lt;title&gt;My Test SGML Document&lt;/title&gt;
    &lt;chapter&gt;
        &lt;title&gt;Chapter 1 Title&lt;/title&gt;
        &lt;para&gt;Some content.&lt;/para&gt;
        &lt;graphic href="Bill_Murray.jpg"&gt;
    &lt;/chapter&gt;
    &lt;chapter&gt;
        &lt;title&gt;Chapter 2 Title&lt;/title&gt;
        &lt;para&gt;More content.&lt;/para&gt;
        &lt;section&gt;
            &lt;title&gt;Subsection 1 Title&lt;/title&gt;
            &lt;para&gt;Subsection content.&lt;/para&gt;
            &lt;graphic name="bill"&gt;
        &lt;/section&gt;
    &lt;/chapter&gt;
&lt;/doc&gt;
</programlisting>
            <para>This is assumed to use Unicode, but most real-life SGML DTDs will reference
                external ISO character entity sets since the DTDs were usually conceived before
                Unicode. For example, ATA DTDs include something along these lines<footnote>
                    <para>The pretty formatting always makes me nostalgic.</para>
                </footnote>:</para>
<programlisting xml:space="preserve">
&lt;!ENTITY   % ISOtech     PUBLIC 
"ISO 8879-1986//ENTITIES General Technical//EN"                &gt;
&lt;!ENTITY   % ISOpub      PUBLIC 
"ISO 8879-1986//ENTITIES Publishing//EN"                       &gt;
&lt;!ENTITY   % ISOnum      PUBLIC 
"ISO 8879-1986//ENTITIES Numeric and Special Graphic//EN"      &gt;
&lt;!ENTITY   % ISOgrk1     PUBLIC 
"ISO 8879-1986//ENTITIES Greek Letters//EN"                    &gt;
&lt;!ENTITY   % ISOgrk2     PUBLIC 
"ISO 8879-1986//ENTITIES Monotoniko Greek//EN"                 &gt;
&lt;!ENTITY   % ISOgrk3     PUBLIC 
"ISO 8879-1986//ENTITIES Greek Symbols//EN"                    &gt;
&lt;!ENTITY   % ISOgrk4     PUBLIC 
"ISO 8879-1986//ENTITIES Alternative Greek Symbols//EN"        &gt;
&lt;!ENTITY   % ISOlat1     PUBLIC 
"ISO 8879-1986//ENTITIES Added Latin 1//EN"                    &gt;
&lt;!ENTITY   % ISObox      PUBLIC
"ISO 8879-1986//ENTITIES Box and Line Drawing//EN"             &gt;
&lt;!ENTITY   % ISOamso     PUBLIC
"ISO 8879-1986//ENTITIES Added Math Symbols: Ordinary//EN"     &gt;
&lt;!ENTITY   % ISOamsc     PUBLIC
"ISO 8879-1986//ENTITIES Added Math Symbols: Delimiters//EN"   &gt;

%ISOtech; %ISOpub; %ISOnum; %ISOgrk1; %ISOgrk2; %ISOgrk3;
%ISOgrk4; %ISOlat1; %ISObox; %ISOamso; %ISOamsc;
</programlisting>
            <para>An entity like <code>&amp;mdash;</code> would have a corresponding entity
                declaration:</para>
<programlisting xml:space="preserve">
&lt;!ENTITY mdash  SDATA "[mdash ]"--=em dash--&gt;
</programlisting>
            <para>The SGML processing, all of which is in an Ant build file, involves the
                following:</para>
            <orderedlist>
                <listitem>
                    <para>Preprocessing specific to the SGML at hand. When I need to do this, it's
                        usually about removing weirdly placed newlines that cause havoc later on if
                        left alone.</para>
                </listitem>
                <listitem>
                    <para>Normalisation with James Clark's <code>spam</code> application from the SP
                        package [<xref linkend="sp-clark"/>]. This is all about bringing in the entire DTD into the internal subset,
                        warts and all<footnote>
                            <para>Meaning anything and everything referenced by the DTD internal
                                subset or the DTD external subset.</para>
                        </footnote>, because otherwise <code>sx</code> will have trouble with some
                        markup constructs in the next step.</para>
                </listitem>
                <listitem>
                    <para>Convert the normalised SGML into XML syntax using <code>sx</code> [<xref linkend="sp-clark"/>].
                        Unfortunately, <code>sx</code> gets rid of the DTD after processing, leaving
                        behind the bare minimum required to resolve graphic entities, e.g.:</para>
<programlisting xml:space="preserve">
&lt;!DOCTYPE doc [
&lt;!NOTATION jpeg SYSTEM ""&gt;
&lt;!ENTITY bill SYSTEM "/home/ari/Documents/projects/tng-sgml2xml/doc/data/Bill_Murray.jpg" NDATA jpeg&gt;
]&gt;
</programlisting>
                    <para>For Unicode content, this is fine, but anything with character entities
                        will have problems<footnote>
                            <para>A single <code>&amp;mdash;</code> without its matching entity
                                declaration will cause the XML to not be well-formed.</para>
                        </footnote>.</para>
                </listitem>
                <listitem>
                    <para>For anything NOT Unicode, we need to insert the missing entity
                        declarations and references to the internal subset, but rather than using
                        SGML-only <code>SDATA</code> entities, they need to reference the Unicode
                        characters: </para>
<programlisting xml:space="preserve">
&lt;!ENTITY mdash	"&amp;#x2014;"&gt; &lt;!-- EM DASH --&gt;
</programlisting>
                    <para>Since they won't harm anything if not used, I choose to always insert a
                        standard set. We get this:</para>
<programlisting xml:space="preserve">
&lt;!DOCTYPE doc [&lt;!-- This maps SGML character entities to their Unicode equivalents --&gt;
&lt;!-- Based on DocBook 4.1.2 XML entities --&gt;&lt;!-- ISO 8879 official entity sets --&gt;
&lt;!ENTITY % iso-amsa PUBLIC "ISO 8879:1986//ENTITIES Added Math Symbols: Arrow Relations//EN"
"/home/ari/Documents/repos/sgml2xml-tng/modules/common/schemas/sgml/xml-entities/iso-amsa.ent"&gt;
%iso-amsa;
&lt;!ENTITY % iso-amsb PUBLIC "ISO 8879:1986//ENTITIES Added Math Symbols: Binary Operators//EN"
"/home/ari/Documents/repos/sgml2xml-tng/modules/common/schemas/sgml/xml-entities/iso-amsb.ent"&gt;
%iso-amsb;

&lt;!– (etc) --&gt;

&lt;!ENTITY bill SYSTEM "/home/ari/Documents/projects/tng-sgml2xml/doc/data/Bill_Murray.jpg" NDATA jpeg&gt;
]&gt;
</programlisting>
                    <para>We should now have well-formed XML.</para>
                </listitem>
            </orderedlist>
        </section>
        <section xml:id="xml-processing">
            <title>XML Processing</title>
            <para>The well-formed XML needs to be converted to whatever the end vocabulary (DTD,
                schema...) requires. Whilst the specifics will differ—certainly for the three
                formats, above—the principle is the same: tweak the XML as necessary to produce
                valid XML. This involves running a series of XSLT stylesheets, different for each
                vocabulary, but the pipeline mechanism is the same.</para>
            <para>A first step is usually to encode the XML in UTF-8 and get rid of the entity
                declarations. Another is often to convert any entity-based graphics to direct
                references; XML processing can do a lot, but entity queries and manipulation remain
                an absolute pain<footnote>
                    <para>Even though S1000D XML documents frequently do use entities and thus
                        internal <code>DOCTYPE</code> subsets.</para>
                </footnote>.</para>
            <para>The same Ant build file that converts the SGML to XML calls an XProc pipeline that
                runs a manifest file listing the required XSLT stylesheets in the order in which
                they run.</para>
        </section>
        <section>
            <title>The sgml2xml-tng Repo</title>
            <para>The SGML-to-XML conversion repository [<xref linkend="sgml2xml-tng"/>] is
                organised like this:</para>
            <figure>
                <title>SGML to XML TNG</title>
                <mediaobject>
                    <imageobject>
                        <imagedata fileref="../../../vol31/graphics/Nordstrom01/Nordstrom01-004.png" width="90%"/>
                    </imageobject>
                </mediaobject>
            </figure>
            <itemizedlist>
                <listitem>
                    <para><code>modules/</code> contains the resources for each input SGML
                        vocabulary.</para>
                    <para>The modules here include <code>ata/</code>, <code>s1000d/</code>, and
                            <code>milspec/</code>. Each of these contain the SGML and XML DTDs (and
                        schemas), plus any pipelines, XSLT, and other resources required. There's
                        also an example module <code>doc/</code> to provide a runnable
                        example.</para>
                    <para>And finally, <code>common/</code> contains resources common to the
                        modules, including <code>SDATA</code> entities (SGML), Unicode entities
                        (XML), common XSLT, etc.</para>
                </listitem>
                <listitem>
                    <para><code>lib/</code> contains the SP package for all things SGML, plus the
                            <emphasis>MorganaXProc-IIIse</emphasis> XProc 3.x engine [<xref linkend="morgana-xproc"/>]
                        for XML conversions and the <emphasis>SchXslt 2 Schematron to XSLT
                            transpiler</emphasis> [<xref linkend="schxslt"/>] for Schematron validation. There's also the
                            <emphasis>XProc Batch</emphasis> library [<xref linkend="xproc-batch"/>] to
                        run pipelined XSLT transforms.</para>
                </listitem>
                <listitem>
                    <para><code>catalogs/</code> contains catalog files.</para>
                </listitem>
                <listitem>
                    <para><code>ant/</code> contains Ant macrodefs for the main Ant script.</para>
                </listitem>
                <listitem>
                    <para>Ant build files (<code>build_sgml2xml.xml</code>,
                            <code>build_sgml2xml.properties.xml</code>, and a local properties file
                        not shown here) to run the main conversion. The build file contains the main
                        logic.</para>
                </listitem>
            </itemizedlist>
            <para>Here's the idea:</para>
            <itemizedlist>
                <listitem>
                    <para><code>build_sgml2xml.xml</code> contains all the steps required to process
                        the SGML (see <xref linkend="sgml-processing"/>) and the resulting (well-formed) XML (see
                            <xref linkend="xml-processing"/>). As a reminder, we do
                            <emphasis>preprocess</emphasis>, <emphasis>normalise</emphasis>,
                            <emphasis>convert to XML syntax</emphasis>, <emphasis>insert character
                            entity declarations to the internal subset</emphasis>, <emphasis>encode
                            to UTF-8</emphasis>, and <emphasis>run the XML
                        pipeline</emphasis>.</para>
                    <para>The build is configured in an Ant local property file (a tweak of
                            <code>build_sgml2xml.properties.xml</code>, named
                            <code>build_sgml2xml.properties.local.xml</code>).</para>
                </listitem>
                <listitem>
                    <para>The SGML vocabularies are all <emphasis>modules</emphasis>. Each requires
                        source DTDs, target DTDs/schemas, and any processing, including XSLT and
                        XProc.</para>
                    <para>The repo [<xref linkend="sgml2xml-tng"/>] contains a rather trivial example, in the
                            <code>doc/</code> folder.</para>
                    <para>If you want to add an SGML vocabulary (i.e., another module, check out the
                        included examples. You may be able to use the common entity sets, but then
                        again, you may not. The code makes the rather optimistic assumption that you
                        can, in fact, reuse what is there now but will not force you to do
                        so.</para>
                </listitem>
                <listitem>
                    <para>The initial well-formed XML is intended to go through a series of
                        XSLT-based steps, transforming the XML into some valid vocabulary. If this
                        is all that is needed, a new module will only require you to write the XSLT
                        steps and list them in an XML manifest file similar to the existing
                        ones.</para>
                    <para>If you need more, you can copy and edit, or fully replace, the default
                        XProc pipelines with your own to do whatever might be missing.</para>
                </listitem>
            </itemizedlist>
            <para>The step outputs can all be saved to help debugging:</para>
<programlisting xml:space="preserve">
doc/
├── data
│   └── (SGML and graphics input)
└── tmp
    └── 20260311133633
        ├── debug
        │   └── (XProc XSLT pipeline debug output)
        ├── out
        │   └── (valid XML and graphics)
        ├── pre
        │   └── (preprocessed SGML)
        ├── reports
        │   └── (reports)
        ├── spam
        │   └── (spam output)
        ├── sx
        │   └── (sx output)
        └── xml
            └── (well-formed XML)
</programlisting>
            <para>The Ant build also provides an optional <code>nsgmls</code> validation
                step.</para>
        </section>
        <section>
            <title>Libraries and Tools</title>
            <para>The SGML processing exists thanks to James Clark's fabulous SP toolkit [<xref linkend="sp-clark"/>]. Without it,
                this paper would not exist.</para>
            <para>The XML processing was always going to be a series of XSLT transforms. I much
                prefer the pipelined approach [<xref linkend="pipelined-xslt"/>], and the default XML processing assumes a series of
                XSLTs listed in a manifest.</para>
            <para>The pipelines are XProc 3.0 run with Achim Berndzen's MorganaXProc-IIIse [<xref linkend="morgana-xproc"/>]. The
                default pipelines use XProc Batch [<xref linkend="xproc-batch"/>], but this doesn't have to be the case.</para>
            <para>There's also David Maus's SchXslt2 Schematron to XSLT 3.0 transpiler [<xref linkend="schxslt"/>] to run
                Schematron validation where needed.</para>
        </section>
        
        <section>
            <title>Modelling XML DTDs and Schemas</title>
            <para>While XML is the modern standard, SGML’s modeling capabilities were, in some
                respects, more robust. For example, SGML featured a logical AND group—allowing
                elements to appear in any order but exactly once. In XML, achieving this level of
                validation typically requires moving beyond DTDs to Schematron or XSD.</para>
            
            <para>SGML also permitted <quote>exclusions</quote> and
                <quote>inclusions</quote>—convenient shortcuts that often complicate conversion.
                Exclusions allowed a designer to prohibit specific elements (like a table) from all
                descendants of an element. Inclusions did the opposite, allowing a specific element
                to appear anywhere within a hierarchy, bypassing standard model rules.</para>
            
            <para>The <quote>inclusion</quote> mechanism is a particular challenge for XML DTDs. In ATA iSpec 2200, for example, revision markers (<code>REVST</code> and <code>REVEND</code>) were inclusions. Because they could legally appear at different hierarchy levels, they are impossible to model in an XML DTD without resorting to messy, repeatable OR groups.</para>
            
            <para>Two primary strategies exist for resolving these models during conversion:</para>
            <itemizedlist>
                <listitem>
                    <para><emphasis>Processing Instructions (PIs):</emphasis> Since PIs can occur
                        anywhere, they mimic SGML inclusions well. However, they require authoring
                        environments that can protect them from accidental deletion.</para>
                </listitem>
                <listitem>
                    <para><emphasis>Structural Remodelling:</emphasis> This involves a significant
                        rewrite of content models, often replacing markers with attributes and
                        external validation logic. While more labour-intensive, remodelling is
                        usually the superior long-term solution as it avoids relying on PIs to carry
                        semantic meaning.</para>
                </listitem>
            </itemizedlist>
        </section>
    </section>
    <section>
        <title>XML to SGML</title>
        <para>Publishing from XML to SGML is akin to the SGML to XML conversion process, but in
            reverse:</para>
        <orderedlist>
            <listitem>
                <para>The <code>DOCTYPE</code> declaration is removed; we are moving from validating
                    to well-formed XML.</para>
            </listitem>
            <listitem>
                <para>An SGML <code>DOCTYPE</code> is generated, including, for example, graphic
                        <code>ENTITY</code> declarations based on <code>@href</code> attributes,
                        <code>NOTATION</code> declarations, etc.</para>
            </listitem>
            <listitem>
                <para>Various XML-only constructs are removed and any SGML-only content models are
                    generated. For example, we may want to remove the <code>@href</code> graphic
                    references and insert <code>ENTITY</code>-based references instead.</para>
            </listitem>
            <listitem>
                <para>All <code>EMPTY</code> XML elements are expanded to explicitly include an end
                    tag (so <code>&lt;graphic&gt;&lt;/graphic&gt;</code> in place of
                        <code>&lt;graphic/&gt;</code>, which is required for normalisation by
                        <code>spam</code>  [<xref linkend="sp-clark"/>] in a later step.</para>
            </listitem>
            <listitem>
                <para>The XML is converted from UTF-8 to ISO character encoding.</para>
            </listitem>
            <listitem>
                <para>The generated <code>DOCTYPE</code> declaration is inserted into the XML
                    document, <emphasis>replacing</emphasis> the XML declaration. This, by the way,
                    is where we leave XML-based processing behind and rely on Ant's text-based
                    capabilities and SP.</para>
            </listitem>
            <listitem>
                <para>The XML is normalised to SGML syntax using <code>spam</code> [<xref linkend="sp-clark"/>]. This
                    includes <code>EMPTY</code> element syntax as per the SGML DTD, but also tag
                    minimisation as indicated by the DTD.</para>
            </listitem>
            <listitem>
                <para>The SGML <code>DOCTYPE</code> is reinserted into the SGML, as it is not
                    retained by <code>spam</code>.</para>
            </listitem>
        </orderedlist>
    </section>
    <section>
        <title>End Notes</title>
        <para>Firstly, I confess to using AI (Gemini) to help generate the images. If you think they
            are ugly, then ping me and I'll silence you with my original art created in
                <emphasis>draw.io</emphasis>. Just don't say I didn't warn you.</para>
        <para>A couple of notes regarding <emphasis>sgml2xml-tng</emphasis>:</para>
        <itemizedlist>
            <listitem>
                <para>The <emphasis>-tng</emphasis> in the name means that this is <emphasis>the
                        next generation</emphasis>. The first was always meant only for a single set
                    of SGML DTDs, and it was very difficult to extend it to support any other
                    DTD.</para>
                <para>There was a separate XML to SGML pipeline, equally difficult to extend.</para>
            </listitem>
            <listitem>
                <para>The XML to SGML conversion is a tricky thing because there are so many ways to
                    produce valid SGML. Most of my code uses XML and XML technologies, which limits
                    what I can do, and then relies on Ant and James Clark's SP package to do
                    anything I can't. SGML empty elements were miserably complicated, yet they are
                    just one problem among many.</para>
            </listitem>
            <listitem>
                <para>Which means that I drew the line at some of the more esoteric ways to produce
                    valid SGML. Curly rather than angled brackets will need to wait, I'm afraid<footnote>
                        <para>Although it's a problem I'd quite like to solve at some point.</para>
                    </footnote>.</para>
            </listitem>
            <listitem>
                <para>A reviewer asked about any SP limitations or issues I encountered. The overall
                    answer would be <quote>not that many</quote>, but I'd certainly highlight its
                    difficulties in outputting SGML <code>EMPTY</code> elements (i.e., start tags
                    only) without me first using a trick to expand those while still in XML syntax.
                    I have not seen the kinds of ambiguous constructs the reviewer says SP knows
                    nothing about, but it's difficult to comment further without seeing examples<footnote>
                        <para>Thankfully, there is a Q&amp;A session at the end of every
                            presentation.</para>
                    </footnote>.</para>
            </listitem>
            <listitem>
                <para>You'll notice that the ATA and S1000D DTDs and XSDs are missing from the repo.
                    The ATA DTDs are proprietary and cost money to obtain, so I cannot legally
                    include them. For S1000D, you'll need to approve the terms of use before
                    downloading any DTD or schema.</para>
            </listitem>
            <listitem>
                <para>Some will (rightly) ask why I use Ant. The answer nowadays is historical<footnote>
                        <para>And laziness on my part.</para>
                    </footnote>; it's what I used for the first-generation implementations. I do
                    want to move everything to XProc 3.1, but it's going to be a huge job and one
                    that I currently hesitate to start<footnote>
                        <para>MorganaXProc-IIIse (standard edition) does not support
                                <code>p:run</code>—you'll need the extended edition for that. Yet my
                            current thinking is that I'd rely on <code>p:run</code> for quite a
                            lot.</para>
                    </footnote>, partly because of my current implementation relies on the free
                    version of MorganaXProc-III; XML Calabash wasn't ready when much of the initial
                    work was done.</para>
            </listitem>
        </itemizedlist>
        <para>I'd also like to thank some folks:</para>
        <itemizedlist>
            <listitem>
                <para>Henrik Mårtensson, who left the markup business long ago, but not before we
                    did a lot of SGML together</para>
            </listitem>
            <listitem>
                <para>Sheila Thomson, who is awesome at writing Ant and who substantially helped
                    improve mine</para>
            </listitem>
            <listitem>
                <para>Chris Pedoe, who not only believed me when I pointed out the built-in problems
                    in the software we had to work with, but who also encouraged me to go ahead with
                    my alternate solution</para>
            </listitem>
            <listitem>
                <para>Nic Gibson, who not only convinced me of the virtues of pipelined XSLT, but
                    who also gave me the means to follow through</para>
            </listitem>
            <listitem>
                <para>James Clark, who's been at the centre of everything I've done in SGML and XML;
                    for the longest time, I wanted him to sign my copy of SP</para>
            </listitem>
        </itemizedlist>
    </section>
    <bibliography>
        <title>Bibliography</title>
        <bibliomixed xml:id="ata" xreflabel="ATA iSpec 2200"><quote>iSpec 2200: Information
                Standards for Aviation Maintenance, Revision 2025.1</quote> [online, fetched on 1
            April 2026]. <link xlink:href="https://publications.airlines.org/products/ispec-2200-information-standards-for-aviation-maintenance-revision-2025-1" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="milspec" xreflabel="MIL-STD-38784"><quote>MIL-STD-38784</quote>
            [online, fetched on 1 April 2026]. <link xlink:href="https://www.navsea.navy.mil/Home/Warfare-Centers/NSWC-Carderock/Resources/Technical-Information-Systems/Navy-XML-SGML-Repository/DTDs-Schemas/MIL-STD-38784/" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="s1000d" xreflabel="S1000D"><quote>S1000D</quote> [online, fetched on 30
            March, 2026]. <link xlink:href="https://s1000d.org/" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="sp-clark" xreflabel="SP - An SGML System Conforming to International Standard ISO 8879 -- Standard Generalized Markup Language"><quote>SP</quote> by James Clark [online, fetched on 30 March, 2026]. <link xlink:href="http://www.jclark.com/sp/" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="topic-based-sgml" xreflabel="Topic-based SGML? Really?"><quote>Topic-based SGML? Really?</quote> by Ari Nordström [fetched on 4 April 2026]. 
                doi:<biblioid class="doi">10.4242/BalisageVol26.Nordstrom01</biblioid></bibliomixed>
        <bibliomixed xml:id="sgml2xml-tng" xreflabel="SGML to XML TNG"><quote>sgml2xml-tng</quote>
            by Ari Nordström [online, fetched on 30 March, 2026]. <link xlink:href="https://github.com/sgmlguru/sgml2xml-tng" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="xproc-batch" xreflabel="XProc Batch"><quote>XProc Batch</quote> by Ari
            Nordström and Nic Gibson [online, fetched on 3 April, 2026]. <link xlink:href="https://github.com/sgmlguru/xproc-batch" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="pipelined-xslt" xreflabel="Pipelined XSLT Transformations"><quote>Pipelined XSLT Transformations</quote> by Ari
            Nordström [online, fetched on 3 April 2026]. doi:<biblioid class="doi">10.4242/BalisageVol25.Nordstrom01</biblioid></bibliomixed>
        <bibliomixed xml:id="morgana-xproc" xreflabel="MorganaXProc-III"><quote>MorganaXProc-III</quote> by Achim Berndzen
            [online, fetched on 3 April 2026]. <link xlink:href="https://www.xml-project.com/morganaxproc-iii.html" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
        <bibliomixed xml:id="schxslt" xreflabel="SchXslt2"><quote>SchXslt2 Schematron to XSLT 3.0 transpiler</quote> by
            David Maus [online, fetched on 5 April 2026]. <link xlink:href="https://codeberg.org/SchXslt/schxslt2" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/></bibliomixed>
    </bibliography>
</article>