Skip to content

Commit c164e24

Browse files
committed
New implementation of TEI to Markdown, and Markdown to TEI conversions using XSLT
1 parent b987904 commit c164e24

4 files changed

Lines changed: 1188 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
fail-fast: false
99
matrix:
1010
os: [ubuntu-latest, macos-latest, windows-latest]
11-
jdk: [8,11,17,21]
11+
jdk: [11,25]
1212
runs-on: ${{ matrix.os }}
1313
steps:
1414
- uses: actions/checkout@v4
@@ -19,7 +19,7 @@ jobs:
1919
java-version: ${{ matrix.jdk }}
2020
- run: mvn clean package
2121
- name: Persist built XAR
22-
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '8'
22+
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '11'
2323
uses: actions/upload-artifact@v4
2424
with:
2525
name: xar
@@ -28,7 +28,7 @@ jobs:
2828
retention-days: 1
2929
overwrite: true
3030
- name: Persist built ci-resources
31-
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '8'
31+
if: matrix.os == 'ubuntu-latest' && matrix.jdk == '11'
3232
uses: actions/upload-artifact@v4
3333
with:
3434
name: ci-resources

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: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
4+
xmlns:tei="http://www.tei-c.org/ns/1.0"
5+
xmlns:t2m="http://tei-2-markdown"
6+
exclude-result-prefixes="xs"
7+
version="2.0">
8+
9+
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>
10+
11+
<xsl:template name="tei-to-markdown">
12+
<xsl:apply-templates select="." mode="tei-to-markdown"/>
13+
</xsl:template>
14+
15+
<!-- Replace tei:hi h1 Elements with heading 1 Markdown -->
16+
<xsl:template match="tei:hi[@rend eq 'h1']" mode="tei-to-markdown">
17+
<xsl:variable name="children" as="node()*">
18+
<xsl:apply-templates select="node()" mode="#current"/>
19+
</xsl:variable>
20+
<xsl:text># </xsl:text><xsl:sequence select="t2m:trim-text($children)"/><xsl:text>&#xa;</xsl:text>
21+
</xsl:template>
22+
23+
<!-- Replace tei:hi h2 Elements with heading 2 Markdown -->
24+
<xsl:template match="tei:hi[@rend eq 'h2']" mode="tei-to-markdown">
25+
<xsl:variable name="children" as="node()*">
26+
<xsl:apply-templates select="node()" mode="#current"/>
27+
</xsl:variable>
28+
<xsl:text>## </xsl:text><xsl:apply-templates select="t2m:trim-text($children)" mode="#current"/><xsl:text>&#xa;</xsl:text>
29+
</xsl:template>
30+
31+
<!-- Replace tei:hi h3 Elements with heading 3 Markdown -->
32+
<xsl:template match="tei:hi[@rend eq 'h3']" mode="tei-to-markdown">
33+
<xsl:variable name="children" as="node()*">
34+
<xsl:apply-templates select="node()" mode="#current"/>
35+
</xsl:variable>
36+
<xsl:text>### </xsl:text><xsl:apply-templates select="t2m:trim-text($children)" mode="#current"/><xsl:text>&#xa;</xsl:text>
37+
</xsl:template>
38+
39+
<!-- Replace tei:hi h1, h2, h3 Elements that contain only whitespace with a single space character -->
40+
<xsl:template match="tei:hi[@rend = ('h1', 'h2', 'h3')][matches(., '^\s+$')]" mode="tei-to-markdown">
41+
<xsl:text> </xsl:text>
42+
</xsl:template>
43+
44+
<!-- Drop empty tei:hi Elements -->
45+
<xsl:template match="tei:hi[empty(child::node())]" mode="tei-to-markdown"/>
46+
47+
<!-- Replace tei:emph bold Elements with bold Markdown -->
48+
<xsl:template match="tei:emph[@rend eq 'bold']" mode="tei-to-markdown">
49+
<xsl:text>**</xsl:text><xsl:apply-templates select="node()" mode="#current"/><xsl:text>**</xsl:text>
50+
</xsl:template>
51+
52+
<!-- Replace tei:emph italic Elements with bold Markdown -->
53+
<xsl:template match="tei:emph[@rend eq 'italic']" mode="tei-to-markdown">
54+
<xsl:text>*</xsl:text><xsl:apply-templates select="node()" mode="#current"/><xsl:text>*</xsl:text>
55+
</xsl:template>
56+
57+
<!-- Replace tei:emph bold or italic Elements that contain only whitespace with a single space character -->
58+
<xsl:template match="tei:emph[@rend = ('bold', 'italic')][matches(., '^\s+$')]" mode="tei-to-markdown">
59+
<xsl:text> </xsl:text>
60+
</xsl:template>
61+
62+
<!-- Drop empty tei:emph Elements -->
63+
<xsl:template match="tei:emph[empty(child::node())]" mode="tei-to-markdown"/>
64+
65+
<!-- Replace runs of whitespace between tei:emph Elements with a single space character -->
66+
<xsl:template match="text()[preceding-sibling::tei:emph][following-sibling::tei:emph][matches(., '^\s+$')]" mode="tei-to-markdown">
67+
<xsl:text> </xsl:text>
68+
</xsl:template>
69+
70+
<!-- Remove space characters in text that occurs before tei:lb Elements -->
71+
<xsl:template match="text()[ends-with(., ' ')][following-sibling::tei:lb]" mode="tei-to-markdown">
72+
<xsl:value-of select="replace(replace(., '^(.*)\s+$', '$1'), '^\s+(.*)$', '$1')"/>
73+
</xsl:template>
74+
75+
<!-- Remove space characters in text that occurs after tei:lb Elements -->
76+
<xsl:template match="text()[starts-with(., ' ')][preceding-sibling::tei:lb]" mode="tei-to-markdown">
77+
<xsl:value-of select="replace(replace(., '^\s+(.*)$', '$1'), '^(.*)\s+$', '$1')"/>
78+
</xsl:template>
79+
80+
<!-- Replace tei:lb Elements with a line-break character when they have a following sibling (that is not a tei:lb) -->
81+
<xsl:template match="tei:lb[following-sibling::node()[not(self::tei:lb)]]" mode="tei-to-markdown">
82+
<xsl:text>&#xa;</xsl:text>
83+
</xsl:template>
84+
85+
<!-- Drop tei:lb Elements when they do not have a following sibling -->
86+
<xsl:template match="tei:lb[empty(following-sibling::node())]" mode="tei-to-markdown"/>
87+
88+
<!-- Separate tei:p Elements with a single line-break character (i.e.: '\n') -->
89+
<xsl:template match="tei:p[following-sibling::tei:p]" mode="tei-to-markdown">
90+
<xsl:apply-templates select="node()|@*" mode="#current"/>
91+
<xsl:text>&#xa;</xsl:text>
92+
</xsl:template>
93+
94+
<!-- Remove unknown Elements -->
95+
<xsl:template match="element()" mode="tei-to-markdown">
96+
<xsl:apply-templates select="node()|@*" mode="#current"/>
97+
</xsl:template>
98+
99+
<!-- Default: Identity trasform everything execept Elements (which will be dealt with above) -->
100+
<xsl:template match="node()[not(. instance of element())]|@*" mode="tei-to-markdown" priority="-1">
101+
<xsl:copy>
102+
<xsl:apply-templates select="node()|@*" mode="#current"/>
103+
</xsl:copy>
104+
</xsl:template>
105+
106+
<xsl:function name="t2m:trim-text" as="node()*">
107+
<xsl:param name="input" required="yes" as="node()*"/>
108+
<xsl:variable name="length" select="count($input)"/>
109+
<xsl:choose>
110+
<xsl:when test="$length eq 1">
111+
<xsl:value-of select="normalize-space($input)"/>
112+
</xsl:when>
113+
<xsl:otherwise>
114+
<xsl:sequence select="t2m:trim-text-replace(head($input), '^\s*(.+)$')"/>
115+
<xsl:sequence select="subsequence($input, 2, $length - 1)"/>
116+
<xsl:sequence select="t2m:trim-text-replace($input[$length], '^(.+)\s*$')"/>
117+
</xsl:otherwise>
118+
</xsl:choose>
119+
</xsl:function>
120+
121+
<xsl:function name="t2m:trim-text-replace" as="node()?">
122+
<xsl:param name="node" as="node()?"/>
123+
<xsl:param name="extract-pattern" as="xs:string"/>
124+
<xsl:choose>
125+
<xsl:when test="$node instance of text()">
126+
<xsl:value-of select="replace($node, $extract-pattern, '$1')"/>
127+
</xsl:when>
128+
<xsl:otherwise>
129+
<xsl:sequence select="$node"/>
130+
</xsl:otherwise>
131+
</xsl:choose>
132+
</xsl:function>
133+
134+
</xsl:stylesheet>

0 commit comments

Comments
 (0)