<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:d="http://www.daisy.org/ns/pipeline/data" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:ssml="http://www.w3.org/2001/10/synthesis" version="3.0" exclude-result-prefixes="#all"> <xsl:import href="mathml-serialization.xsl"/> <xsl:import href="mathml-normalization.xsl"/> <xsl:key name="simple-rules" match="d:rule[@constant]" use="@constant"/> <xsl:variable name="rules" select="document('mathml-rules.xml')"/> <xsl:variable name="compiled-rules" as="map(xs:string,document-node(element(d:rules)))"> <xsl:map> <xsl:for-each select="distinct-values(//m:math/@xml:lang/substring-before(concat(.,'-'),'-'))"> <xsl:variable name="lang" select="."/> <xsl:map-entry key="$lang"> <xsl:document> <d:rules> <xsl:for-each select="$rules//d:rule[d:trans[not(@xml:lang) or @xml:lang = $lang][1]]"> <xsl:sort data-type="number" order="descending" select="if (current()/@priority) then current()/@priority else 0"/> <xsl:copy> <xsl:variable name="pattern" select="node() except d:trans"/> <xsl:copy-of select="@* except regex except constant"/> <xsl:choose> <xsl:when test="count($pattern//d:group) + count($pattern//d:text) + count($pattern//d:optional-group) = 0"> <xsl:attribute name="constant"> <xsl:value-of select="string-join(d:serialize($pattern, 'false'), '')"/> </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="regex"> <xsl:value-of select="string-join(d:serialize($pattern, 'true'), '')"/> </xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:sequence select="d:trans[@xml:lang = $lang][1]"/> </xsl:copy> </xsl:for-each> </d:rules> </xsl:document> </xsl:map-entry> </xsl:for-each> </xsl:map> </xsl:variable> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="m:math[@id and @xml:lang]"> <xsl:variable name="lang" select="string(@xml:lang)"/> <xsl:variable name="lang" select="substring-before(concat($lang,'-'),'-')"/> <xsl:variable name="normalized"> <xsl:apply-templates select="." mode="normalize"/> </xsl:variable> <ssml:speak version="1.1"> <xsl:copy-of select="@*"/> <xsl:for-each select="m:*[local-name()!='text'][1]/preceding-sibling::m:text"> <xsl:value-of select="text()"/> </xsl:for-each> <xsl:call-template name="iterate-over-rules"> <xsl:with-param name="serialized" select="string-join(d:serialize($normalized/node(), 'false'), '')"/> <xsl:with-param name="compiled-rules" select="$compiled-rules($lang)"/> </xsl:call-template> <xsl:for-each select="m:*[local-name()!='text'][1]/following-sibling::m:text"> <xsl:value-of select="text()"/> </xsl:for-each> </ssml:speak> </xsl:template> <xsl:template name="iterate-over-rules"> <xsl:param name="serialized"/> <xsl:param name="compiled-rules"/> <xsl:param name="emphasize" select="true()"/> <xsl:variable name="decrement"> <xsl:analyze-string select="$serialized" regex="^:([0-9]+):"> <xsl:matching-substring> <xsl:value-of select="regex-group(1)"/> </xsl:matching-substring> <xsl:non-matching-substring/> </xsl:analyze-string> </xsl:variable> <xsl:variable name="shifted"> <xsl:analyze-string select="$serialized" regex="([:@])([0-9]+)"> <xsl:matching-substring> <xsl:value-of select="concat(regex-group(1), 1 + number(regex-group(2)) - number($decrement))"/> </xsl:matching-substring> <xsl:non-matching-substring> <xsl:copy/> </xsl:non-matching-substring> </xsl:analyze-string> </xsl:variable> <xsl:variable name="simple-rule" select="key('simple-rules', $shifted, $compiled-rules)"/> <xsl:choose> <xsl:when test="$simple-rule"> <xsl:copy-of select="$simple-rule/d:trans/node()"/> </xsl:when> <xsl:when test="not(contains($serialized, ':') and contains($serialized, '@'))"> <xsl:value-of select="concat(' ', $serialized, ' ')"/> </xsl:when> <xsl:otherwise> <xsl:variable name="first-rule" select="$compiled-rules//d:rule[@regex][1]"/> <xsl:if test="$first-rule"> <xsl:call-template name="one-iteration"> <xsl:with-param name="serialized" select="$shifted"/> <xsl:with-param name="rule" select="$first-rule"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="emphasize" select="$emphasize"/> </xsl:call-template> </xsl:if> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="one-iteration"> <xsl:param name="serialized"/> <xsl:param name="rule"/> <xsl:param name="compiled-rules"/> <xsl:param name="emphasize"/> <xsl:variable name="next" select="$rule/following-sibling::d:rule[@regex][1]"/> <xsl:analyze-string select="$serialized" regex="{concat('^', $rule/@regex, '$')}"> <xsl:matching-substring> <xsl:variable name="regex-groups"> <d:regex-groups> <d:regex-group n="1"><xsl:value-of select="regex-group(1)"/></d:regex-group> <d:regex-group n="2"><xsl:value-of select="regex-group(2)"/></d:regex-group> <d:regex-group n="3"><xsl:value-of select="regex-group(3)"/></d:regex-group> <d:regex-group n="4"><xsl:value-of select="regex-group(4)"/></d:regex-group> <d:regex-group n="5"><xsl:value-of select="regex-group(5)"/></d:regex-group> </d:regex-groups> </xsl:variable> <xsl:apply-templates mode="replace" select="$rule/d:trans/node()"> <xsl:with-param name="regex-groups" select="$regex-groups"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="emphasize" select="$emphasize"/> </xsl:apply-templates> </xsl:matching-substring> <xsl:non-matching-substring> <xsl:variable name="expected-ending" select="'@1@[^ ]+ $'"/> <xsl:variable name="without-ending" select="replace($serialized, $expected-ending, '')"/> <xsl:choose> <xsl:when test="$next"> <xsl:call-template name="one-iteration"> <xsl:with-param name="serialized" select="$serialized"/> <xsl:with-param name="rule" select="$next"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="emphasize" select="$emphasize"/> </xsl:call-template> </xsl:when> <xsl:when test="not(contains($without-ending, '@1@'))"> <xsl:call-template name="iterate-over-rules"> <xsl:with-param name="serialized" select="replace($without-ending, '^:1:[^ ]+ ', '')"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="emphasize" select="$emphasize"/> </xsl:call-template> </xsl:when> <xsl:when test="not(matches($serialized, $expected-ending))"> <xsl:value-of select="' ERROR '"/> </xsl:when> <xsl:otherwise> <xsl:analyze-string select="$serialized" regex=":1:"> <xsl:matching-substring/> <xsl:non-matching-substring> <xsl:call-template name="iterate-over-rules"> <xsl:with-param name="serialized" select="concat(':1:', .)"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="emphasize" select="$emphasize"/> </xsl:call-template> </xsl:non-matching-substring> </xsl:analyze-string> </xsl:otherwise> </xsl:choose> </xsl:non-matching-substring> </xsl:analyze-string> </xsl:template> <xsl:template match="d:group" mode="replace" priority="2"> <xsl:param name="regex-groups"/> <xsl:param name="compiled-rules"/> <xsl:param name="emphasize"/> <xsl:variable name="n" select="@n"/> <xsl:variable name="play-emphasis" select="$emphasize and @emph='true'"/> <xsl:if test="$play-emphasis"> <ssml:break time="300ms"/> </xsl:if> <xsl:call-template name="iterate-over-rules"> <xsl:with-param name="serialized" select="$regex-groups//d:regex-group[@n=$n]"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="emphasize" select="@emph-children='true'"/> </xsl:call-template> <xsl:if test="$play-emphasis"> <ssml:break time="300ms"/> </xsl:if> </xsl:template> <xsl:template match="node()" mode="replace" priority="1"> <xsl:param name="regex-groups"/> <xsl:param name="compiled-rules"/> <xsl:param name="emphasize"/> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates select="node()" mode="replace"> <xsl:with-param name="regex-groups" select="$regex-groups"/> <xsl:with-param name="compiled-rules" select="$compiled-rules"/> <xsl:with-param name="depth" select="$emphasize"/> </xsl:apply-templates> </xsl:copy> </xsl:template> </xsl:stylesheet>