Skip to content

Commit e55a9e4

Browse files
committed
New implementation of TEI to Markdown, and Markdown to TEI conversions using XSLT
1 parent 37dc142 commit e55a9e4

File tree

9 files changed

+6045
-1
lines changed

9 files changed

+6045
-1
lines changed

pom.xml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
<public.xar.repo.uri>https://exist-db.org/exist/apps/public-repo</public.xar.repo.uri>
5555

5656
<!-- NOTE(AR): this is used for generating forms in the xml-maven-plugin below -->
57-
<saxon.version>9.9.1-8</saxon.version>
57+
<saxon.version>12.9</saxon.version>
58+
59+
<xspec.version>3.1.3</xspec.version>
5860

5961
<!-- used in the EXPath Package Descriptor -->
6062
<package-title>${project.name}</package-title>
@@ -214,6 +216,35 @@
214216
</execution>
215217
</executions>
216218
</plugin>
219+
<plugin>
220+
<groupId>io.xspec.maven</groupId>
221+
<artifactId>xspec-maven-plugin</artifactId>
222+
<version>${xspec.version}</version>
223+
<executions>
224+
<execution>
225+
<id>services-xspec-tests</id>
226+
<phase>test</phase>
227+
<goals>
228+
<goal>run-xspec</goal>
229+
</goals>
230+
<configuration>
231+
<testDir>src/test/xar-resources</testDir>
232+
</configuration>
233+
</execution>
234+
</executions>
235+
<dependencies>
236+
<dependency>
237+
<groupId>io.xspec</groupId>
238+
<artifactId>xspec</artifactId>
239+
<version>${xspec.version}</version>
240+
</dependency>
241+
<dependency>
242+
<groupId>net.sf.saxon</groupId>
243+
<artifactId>Saxon-HE</artifactId>
244+
<version>${saxon.version}</version>
245+
</dependency>
246+
</dependencies>
247+
</plugin>
217248
<plugin>
218249
<groupId>ro.kuberam.maven.plugins</groupId>
219250
<artifactId>kuberam-expath-plugin</artifactId>
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsl:stylesheet
3+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
5+
xmlns:tei="http://www.tei-c.org/ns/1.0"
6+
xmlns:m2t="http://manuforma-form-to-tei"
7+
exclude-result-prefixes="xs"
8+
version="2.0">
9+
10+
<!--
11+
Convert TEI like submission from a manuForma form to TEI
12+
@author Adam Retter
13+
-->
14+
15+
<xsl:param name="new" as="xs:boolean" select="false()"/>
16+
<xsl:param name="id" as="xs:string" required="yes"/>
17+
<xsl:param name="uri" as="xs:string?" required="yes"/>
18+
<xsl:param name="user-id" as="xs:string" select="'guest'"/>
19+
<xsl:param name="user-name" as="xs:string" select="'Guest'"/>
20+
21+
22+
<xsl:import href="tei-to-markdown.xslt"/>
23+
24+
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>
25+
26+
27+
<xsl:template match="tei:TEI">
28+
<xsl:copy>
29+
<xsl:apply-templates select="node()|@*[local-name() ne 'class']"/>
30+
</xsl:copy>
31+
</xsl:template>
32+
33+
<xsl:template match="tei:note|tei:summary[not(parent::tei:layoutDesc)]|tei:quote">
34+
<xsl:copy>
35+
<xsl:apply-templates select="@*"/>
36+
<xsl:apply-templates select="node()" mode="tei-to-markdown"/>
37+
</xsl:copy>
38+
</xsl:template>
39+
40+
<xsl:template match="tei:ab[@type eq 'factoid'][@subtype eq 'relation']">
41+
<xsl:call-template name="copy-with-updated-id-and-source">
42+
<xsl:with-param name="id" select="concat('factoid-', $id)"/>
43+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
44+
</xsl:call-template>
45+
</xsl:template>
46+
47+
<xsl:template match="tei:ab">
48+
<xsl:copy>
49+
<xsl:apply-templates select="@*"/>
50+
<xsl:apply-templates select="node()" mode="tei-to-markdown"/>
51+
</xsl:copy>
52+
</xsl:template>
53+
54+
<!-- Add a Contributor into the titleStmt Element -->
55+
<xsl:template match="tei:titleStmt">
56+
<xsl:copy>
57+
<xsl:apply-templates select="@*"/>
58+
<xsl:apply-templates select="tei:title,tei:author,tei:sponsor,tei:funder,tei:principal"/>
59+
<xsl:apply-templates select="tei:editor[exists((text(), @ref)[normalize-space(.) ne ''])]"/>
60+
<xsl:if test="$user-id ne 'guest' and empty(tei:editor[. eq $user-name])">
61+
<tei:editor xml:id="{$user-id}" role="contributor"><xsl:value-of select="$user-name"/></tei:editor>
62+
</xsl:if>
63+
<xsl:apply-templates select="tei:meeting,tei:respStmt"/>
64+
</xsl:copy>
65+
</xsl:template>
66+
67+
<xsl:template match="tei:idno[parent::tei:ab[@type eq 'factoid'][@subtype eq 'relation']]">
68+
<xsl:call-template name="copy-with-updated-id-and-source">
69+
<xsl:with-param name="id" select="concat('name-', count(preceding-sibling::tei:idno) + 1)"/>
70+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
71+
</xsl:call-template>
72+
</xsl:template>
73+
74+
<xsl:template match="tei:idno[parent::tei:publicationStmt]">
75+
<xsl:copy>
76+
<xsl:apply-templates select="node()|@*"/>
77+
<xsl:if test="$new">
78+
<xsl:value-of select="$uri"/>
79+
</xsl:if>
80+
</xsl:copy>
81+
</xsl:template>
82+
83+
<xsl:template match="tei:place[parent::tei:listPlace]">
84+
<xsl:call-template name="copy-with-updated-id-and-source">
85+
<xsl:with-param name="id" select="concat('place-', $id)"/>
86+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
87+
</xsl:call-template>
88+
</xsl:template>
89+
90+
<xsl:template match="tei:placeName[parent::tei:place/parent::tei:listPlace]">
91+
<xsl:call-template name="copy-with-updated-id-and-source">
92+
<xsl:with-param name="id" select="concat('name-', count(preceding-sibling::tei:placeName) + 1)"/>
93+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
94+
</xsl:call-template>
95+
</xsl:template>
96+
97+
<xsl:template match="tei:bibl[parent::tei:place/parent::tei:listPlace or parent::tei:person/parent::tei:listPerson or parent::tei:bibl/parent::tei:bibl/parent::tei:body]">
98+
<xsl:copy>
99+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
100+
<!-- xsl:attribute name="xml:id" select="concat('bibl-', count(preceding-sibling::tei:bibl) + 1)"/ -->
101+
<xsl:apply-templates select="node()"/>
102+
</xsl:copy>
103+
</xsl:template>
104+
105+
<xsl:template match="tei:bibl[parent::tei:body/parent::tei:text]">
106+
<xsl:copy>
107+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
108+
<!-- xsl:attribute name="xml:id" select="concat('work-', $id)"/ -->
109+
<xsl:apply-templates select="node()"/>
110+
</xsl:copy>
111+
</xsl:template>
112+
113+
<xsl:template match="tei:bibl[parent::tei:listBibl/parent::tei:additional/parent::tei:msDesc]">
114+
<xsl:copy>
115+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
116+
<!-- xsl:attribute name="xml:id" select="concat('bibl-', (count(parent::tei:listBibl/parent::tei:additional/preceding-sibling::tei:additional) + 1), '.', (count(parent::tei:listBibl/preceding-sibling::tei:listBibl) + 1), '.', (count(preceding-sibling::tei:bibl) + 1))"/ -->
117+
<xsl:apply-templates select="node()"/>
118+
</xsl:copy>
119+
</xsl:template>
120+
121+
<xsl:template match="tei:title[parent::tei:bibl/parent::tei:body]">
122+
<xsl:call-template name="copy-with-updated-id-and-source">
123+
<xsl:with-param name="id" select="concat('title-', count(preceding-sibling::tei:title) + 1)"/>
124+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
125+
</xsl:call-template>
126+
</xsl:template>
127+
128+
<!-- Drop empty title elements -->
129+
<xsl:template match="tei:title[empty((text(), @ref)[normalize-space(.) ne ''])]" priority="2"/>
130+
131+
<xsl:template match="tei:person[parent::tei:listPerson]">
132+
<xsl:call-template name="copy-with-updated-id-and-source">
133+
<xsl:with-param name="id" select="concat('person-', $id)"/>
134+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
135+
</xsl:call-template>
136+
</xsl:template>
137+
138+
<xsl:template match="tei:persName[parent::tei:person/parent::tei:listPerson]">
139+
<xsl:call-template name="copy-with-updated-id-and-source">
140+
<xsl:with-param name="id" select="concat('name-', count(preceding-sibling::tei:persName) + 1)"/>
141+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
142+
</xsl:call-template>
143+
</xsl:template>
144+
145+
<xsl:template match="tei:msDesc[parent::tei:person/parent::tei:listPerson]">
146+
<xsl:call-template name="copy-with-updated-id-and-source">
147+
<xsl:with-param name="id" select="concat('ms-', $id)"/>
148+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
149+
</xsl:call-template>
150+
</xsl:template>
151+
152+
<xsl:template match="tei:msItem[parent::tei:msContents/parent::tei:msDesc]">
153+
<xsl:call-template name="copy-with-updated-id-and-source">
154+
<xsl:with-param name="id" select="concat('item-', (count(parent::tei:msContents/preceding-sibling::tei:msContents) + 1), '.', (count(preceding-sibling::tei:msItem) + 1))"/>
155+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
156+
</xsl:call-template>
157+
</xsl:template>
158+
159+
<xsl:template match="tei:layout[parent::tei:layoutDesc/parent::tei:objectDesc/parent::tei:physDesc/parent::tei:msDesc]">
160+
<xsl:call-template name="copy-with-updated-id-and-source">
161+
<xsl:with-param name="id" select="concat('layout-', (count(parent::tei:layoutDesc/parent::tei:objectDesc/parent::tei:physDesc/preceding-sibling::tei:physDesc) + 1), '.', (count(parent::tei:layoutDesc/parent::tei:objectDesc/preceding-sibling::tei:objectDesc) + 1), '.', (count(parent::tei:layoutDesc/preceding-sibling::tei:layoutDesc) + 1), '.', (count(preceding-sibling::tei:layout) + 1))"/>
162+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
163+
</xsl:call-template>
164+
</xsl:template>
165+
166+
<xsl:template match="tei:handNote[parent::tei:handDesc/parent::tei:physDesc/parent::tei:msDesc]">
167+
<xsl:call-template name="copy-with-updated-id-and-source">
168+
<xsl:with-param name="id" select="concat('hand-', (count(parent::tei:handDesc/parent::tei:physDesc/preceding-sibling::tei:physDesc) + 1), '.', (count(parent::tei:handDesc/preceding-sibling::tei:handDesc) + 1), '.', (count(preceding-sibling::tei:handNote) + 1))"/>
169+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
170+
</xsl:call-template>
171+
</xsl:template>
172+
173+
<xsl:template match="tei:item[parent::tei:list/parent::tei:additions/parent::tei:physDesc/parent::tei:msDesc]">
174+
<xsl:call-template name="copy-with-updated-id-and-source">
175+
<xsl:with-param name="id" select="concat('add-', (count(parent::tei:list/parent::tei:additions/parent::tei:physDesc/preceding-sibling::tei:physDesc) + 1), '.', (count(parent::tei:list/parent::tei:additions/preceding-sibling::tei:additions) + 1), '.', (count(parent::tei:list/preceding-sibling::tei:list) + 1), '.', (count(preceding-sibling::tei:item) + 1))"/>
176+
<xsl:with-param name="source" select="m2t:extract-source(@source)"/>
177+
</xsl:call-template>
178+
</xsl:template>
179+
180+
<xsl:template match="tei:relation[exists((tei:active, tei:mutual, tei:passive))]">
181+
<xsl:copy>
182+
<xsl:apply-templates select="@*[not(name(.) = ('active', 'mutual', 'passive'))]"/>
183+
<xsl:if test="tei:active/@ref">
184+
<xsl:attribute name="active" select="string-join(tei:active/@ref, ' ')"/>
185+
</xsl:if>
186+
<xsl:if test="tei:mutual/@ref">
187+
<xsl:attribute name="mutual" select="string-join(tei:mutual/@ref, ' ')"/>
188+
</xsl:if>
189+
<xsl:if test="tei:passive/@ref">
190+
<xsl:attribute name="passive" select="string-join(tei:passive/@ref, ' ')"/>
191+
</xsl:if>
192+
</xsl:copy>
193+
</xsl:template>
194+
195+
196+
<xsl:function name="m2t:extract-source" as="xs:string">
197+
<xsl:param name="input" as="xs:string" required="yes"/>
198+
<xsl:sequence select="concat('#', tokenize($input, '#')[last()])"/>
199+
</xsl:function>
200+
201+
<!-- Copies an element whilst adding or replacing its 'xml:id' and 'source' attributes -->
202+
<xsl:template name="copy-with-updated-id-and-source">
203+
<xsl:param name="id" as="xs:string" required="yes"/>
204+
<xsl:param name="source" as="xs:string" required="yes"/>
205+
<xsl:copy>
206+
<xsl:attribute name="xml:id" select="$id"/>
207+
<xsl:if test="@source[. ne '']">
208+
<xsl:attribute name="source" select="$source"/>
209+
</xsl:if>
210+
<xsl:apply-templates select="@*[not(name(.) = ('xml:id', 'source'))]|node()"/>
211+
</xsl:copy>
212+
</xsl:template>
213+
214+
215+
<!-- Default: Identity trasform everything whilst updating the 'source' attribute -->
216+
<xsl:template match="node()|@*">
217+
<xsl:copy>
218+
<xsl:apply-templates select="@*[name(.) ne 'source']"/>
219+
<xsl:if test="@source[. ne '']">
220+
<xsl:attribute name="source" select="concat('#', substring-after(@source, '#'))"/>
221+
</xsl:if>
222+
<xsl:apply-templates select="node()"/>
223+
</xsl:copy>
224+
</xsl:template>
225+
226+
</xsl:stylesheet>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsl:stylesheet
3+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
5+
xmlns:tei="http://www.tei-c.org/ns/1.0"
6+
xmlns:m2t="http://markdown-2-tei"
7+
exclude-result-prefixes="xs"
8+
version="2.0">
9+
10+
<!--
11+
Convert Markdown to TEI from manuForma forms
12+
@author Adam Retter
13+
-->
14+
15+
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>
16+
17+
18+
<xsl:template name="markdown-to-tei">
19+
<xsl:apply-templates select="." mode="markdown-to-tei"/>
20+
</xsl:template>
21+
22+
<!-- Replace heading 1 Markdown with tei:hi h1 Element -->
23+
<xsl:template match="text()[matches(., '(^|[^#])#\s+.+')]" mode="markdown-to-tei">
24+
<xsl:apply-templates select="m2t:h1(.)" mode="#current"/>
25+
</xsl:template>
26+
27+
<!-- Replace heading 2 Markdown with tei:hi h2 Element -->
28+
<xsl:template match="text()[matches(., '(^|[^#])##\s+.+')]" mode="markdown-to-tei">
29+
<xsl:apply-templates select="m2t:h2(.)" mode="#current"/>
30+
</xsl:template>
31+
32+
<!-- Replace heading 3 Markdown with tei:hi h3 Element -->
33+
<xsl:template match="text()[matches(., '(^|[^#])###\s+.+')]" mode="markdown-to-tei">
34+
<xsl:apply-templates select="m2t:h3(.)" mode="#current"/>
35+
</xsl:template>
36+
37+
<!-- Replace bold Markdown with tei:emph bold Element -->
38+
<xsl:template match="text()[matches(., '\*\*([^*]+)\*\*')]" mode="markdown-to-tei">
39+
<xsl:apply-templates select="m2t:bold(.)" mode="#current"/>
40+
</xsl:template>
41+
42+
<!-- Replace italic Markdown with tei:emph italic Element -->
43+
<xsl:template match="text()[matches(., '(^|[^*\\])\*[^*]*[^*\\]\*([^*]|$)]')]" mode="markdown-to-tei">
44+
<xsl:apply-templates select="m2t:italic(.)" mode="#current"/>
45+
</xsl:template>
46+
47+
<!-- Default: Identity trasform everything -->
48+
<xsl:template match="node()|@*" mode="markdown-to-tei" priority="-3">
49+
<xsl:copy>
50+
<xsl:apply-templates select="node()|@*" mode="#current"/>
51+
</xsl:copy>
52+
</xsl:template>
53+
54+
<xsl:function name="m2t:h1" as="node()+">
55+
<xsl:param name="markdown" as="text()" required="true"/>
56+
<xsl:analyze-string select="$markdown" regex="(^|[^#])#\s+(.+)">
57+
<xsl:matching-substring>
58+
<xsl:if test="m2t:non-empty(regex-group(1))"><xsl:value-of select="regex-group(1)"/></xsl:if><tei:hi rend="h1"><xsl:value-of select="normalize-space(regex-group(2))"/></tei:hi>
59+
</xsl:matching-substring>
60+
<xsl:non-matching-substring>
61+
<xsl:value-of select="."/>
62+
</xsl:non-matching-substring>
63+
</xsl:analyze-string>
64+
</xsl:function>
65+
66+
<xsl:function name="m2t:h2" as="node()+">
67+
<xsl:param name="markdown" as="text()" required="true"/>
68+
<xsl:analyze-string select="$markdown" regex="(^|[^#])##\s+(.+)">
69+
<xsl:matching-substring>
70+
<xsl:if test="m2t:non-empty(regex-group(1))"><xsl:value-of select="regex-group(1)"/></xsl:if><tei:hi rend="h2"><xsl:value-of select="normalize-space(regex-group(2))"/></tei:hi>
71+
</xsl:matching-substring>
72+
<xsl:non-matching-substring>
73+
<xsl:value-of select="."/>
74+
</xsl:non-matching-substring>
75+
</xsl:analyze-string>
76+
</xsl:function>
77+
78+
<xsl:function name="m2t:h3" as="node()+">
79+
<xsl:param name="markdown" as="text()" required="true"/>
80+
<xsl:analyze-string select="$markdown" regex="(^|[^#])###\s+(.+)">
81+
<xsl:matching-substring>
82+
<xsl:if test="m2t:non-empty(regex-group(1))"><xsl:value-of select="regex-group(1)"/></xsl:if><tei:hi rend="h3"><xsl:value-of select="normalize-space(regex-group(2))"/></tei:hi>
83+
</xsl:matching-substring>
84+
<xsl:non-matching-substring>
85+
<xsl:value-of select="."/>
86+
</xsl:non-matching-substring>
87+
</xsl:analyze-string>
88+
</xsl:function>
89+
90+
<xsl:function name="m2t:bold" as="node()+">
91+
<xsl:param name="markdown" as="text()" required="true"/>
92+
<xsl:analyze-string select="$markdown" regex="(^|[^\\])\*\*([^*]*[^*\\])\*\*">
93+
<xsl:matching-substring>
94+
<xsl:if test="m2t:non-empty(regex-group(1))"><xsl:value-of select="regex-group(1)"/></xsl:if><tei:emph rend="bold"><xsl:value-of select="regex-group(2)"/></tei:emph>
95+
</xsl:matching-substring>
96+
<xsl:non-matching-substring>
97+
<xsl:value-of select="."/>
98+
</xsl:non-matching-substring>
99+
</xsl:analyze-string>
100+
</xsl:function>
101+
102+
<xsl:function name="m2t:italic" as="node()+">
103+
<xsl:param name="markdown" as="text()" required="true"/>
104+
<xsl:analyze-string select="$markdown" regex="(^|[^*\\])\*([^*]*[^*\\])\*([^*]|$)">
105+
<xsl:matching-substring>
106+
<xsl:if test="m2t:non-empty(regex-group(1))"><xsl:value-of select="regex-group(1)"/></xsl:if><tei:emph rend="italic"><xsl:value-of select="regex-group(2)"/></tei:emph><xsl:if test="m2t:non-empty(regex-group(3))"><xsl:value-of select="regex-group(3)"/></xsl:if>
107+
</xsl:matching-substring>
108+
<xsl:non-matching-substring>
109+
<xsl:value-of select="."/>
110+
</xsl:non-matching-substring>
111+
</xsl:analyze-string>
112+
</xsl:function>
113+
114+
<xsl:function name="m2t:non-empty" as="xs:string*">
115+
<xsl:param name="inputs" as="xs:string*" required="true"/>
116+
<xsl:sequence select="$inputs[string-length(.) gt 0]"/>
117+
</xsl:function>
118+
119+
</xsl:stylesheet>

0 commit comments

Comments
 (0)