xquery version "3.1"; (:~ : This modules handles the conversion of the Fontante-TEI/XML into TEI simplePrint : for the edited text. The resulting TEI simplePrint is the basis for the "Editerter : Text" (edited text) view on the website and the book. It represents the latest : layer of text. : : @author Michelle Rodzis : @version 0.1 : @since TODO :) module namespace fontaneSimple="http://fontane-nb.dariah.eu/teisimple"; declare namespace tei="http://www.tei-c.org/ns/1.0"; declare namespace test="http://exist-db.org/xquery/xqsuite"; import module namespace console="http://exist-db.org/xquery/console"; import module namespace simpleHelpers="http://fontane-nb.dariah.eu/teisimplehelpers" at "teisimplehelpers.xqm"; import module namespace index-info="http://fontane-nb.dariah.eu/index-info" at "index-info.xqm"; (:~ : The main function initiates the transformation of a given notebook. : : TODO: adapt to several input files? : : :) declare function fontaneSimple:main($file as xs:string) as node()? { let $doc := try { (doc("/db/sade-projects/textgrid/data/xml/data/" || $file)) } catch * { (console:log("It was not possible to open the requested file " || $file)) } let $front-covers := $doc//tei:sourceDoc/tei:surface[contains(@n, "front_cover")] let $back-covers := $doc//tei:sourceDoc/tei:surface[contains(@n, "back_cover")] let $content := $doc//tei:sourceDoc/tei:surface[not(contains(@n, "cover") or matches(@n, "spine"))] let $tei := {$doc//tei:teiHeader} {fontaneSimple:transform($front-covers)} {fontaneSimple:transform($content)} {fontaneSimple:transform($back-covers)} let $store := xmldb:store("/db/apps/SADE/resources/xml/", "tei-simple-pre.xml", $tei) return $tei }; (:~ : Recursivly iterates the passed nodes and converts them according to the : requirements for the "Edierter Text". While it convers almost all of the : requirements stated in the encoding documentation (c.f. : https://fontane-nb.dariah.eu/doku.html), some parts of it are handled in a : second step (c.f. TODO) - especially the removal of tei:handShift duplicates : and the tei:milestone expansion to tei:div[@type = "section"] resp. tei:p - : because it is easier to perform these steps after the XML hierarchy has been : flattened a bit. : : @author Michelle Rodzis : @param $nodes the elements of the book covers and the book content : @return $node()* a TEI simplePrint element :) declare function fontaneSimple:transform($nodes as node()*) as node()* { for $node in $nodes return typeswitch ($node) case text() return if($node/ancestor::tei:line or $node/ancestor::tei:figDesc or $node/ancestor::tei:desc[@type = "edited_text"] or $node/ancestor::tei:note[@type = "editorial"]) then simpleHelpers:prepare-text($node) else () case element(tei:lb) return if($node[@break = "keepHyphen"]) then () else if($node[@break = "no"]) then fontaneSimple:mark-intervention($node) else fontaneSimple:copy-element($node) case element(tei:g) return if($node[@ref = "#vds"]) then () else if($node/@ref ="#rth" or $node/@ref ="#hb") then fontaneSimple:copy-element($node) else fontaneSimple:transform($node/node()) case element(tei:del) return if($node/parent::tei:restore) then fontaneSimple:transform($node/node()) else if($node/descendant::tei:restore) then fontaneSimple:transform($node/descendant::tei:restore) else () case element(tei:restore) return if(count($node/child::*) = 1 and $node/child::tei:del and $node/ancestor::tei:del) then () else fontaneSimple:transform($node/node()) case element(tei:retrace) return if($node/@rend) then element tei:seg { attribute rendition {$node/@rend}, fontaneSimple:transform($node/node()) } else fontaneSimple:transform($node/node()) case element(tei:add) return if($node/@type = "edited_text" or $node/child::tei:seg[@type = "multiphrase"]) then fontaneSimple:copy-element($node) (: :) (: else if($node[matches(@copyOf, $node/preceding::tei:seg/@xml:id):) (: and not(@cause = "unclear")]) then:) (: fontaneSimple:mark-intervention($node):) else if($node/@cause = "unclear") then () else if($node[matches(@copyOf, $node/preceding::tei:seg/@xml:id)]) then fontaneSimple:mark-intervention($node) else if($node/@cause ="catchword") then () else if($node/@rend ="|") then fontaneSimple:transform($node/node()) else if(not($node/@xml:id)) then fontaneSimple:transform($node/node()) else if(simpleHelpers:is-transposed($node)) then let $corresp := //tei:metamark[matches(@target, $node/@xml:id)] return (fontaneSimple:transform($corresp/node()), fontaneSimple:transform($node/node())) else fontaneSimple:transform($node/node()) case element(tei:addSpan) return if($node/@type = "edited_text") then fontaneSimple:copy-element($node) else () case element(tei:fw) return () case element(tei:line) return if($node/@type = "heading") then fontaneSimple:make-head($node) else if(simpleHelpers:has-valid-style($node) or matches($node/@rendition, "black_letter") or matches($node/@rendition, "roman")) then fontaneSimple:make-seg-with-rendition($node) else if($node/following::*[1][self::tei:lb[@break = "no"]]) then (simpleHelpers:start-line($node), simpleHelpers:trim-last-char($node)) else if ($node/preceding::*[1][self::tei:lb[@break = "no"]]) then (simpleHelpers:start-line($node), simpleHelpers:trim-first-char($node)) else if($node/@type = "verse") then if(not($node/@next)) then (if(not($node/preceding-sibling::tei:line[@type = "verse"])) then fontaneSimple:mark-linegroup-beginning() else (), element tei:l { fontaneSimple:transform($node/node()) }, if(not($node/following-sibling::tei:line[@type = "verse"])) then fontaneSimple:mark-linegroup-end() else ()) (: 3.8.2.2.1.3 Vers mit anderer Beschriftung in einer Zeile :) else let $corresp := $node/following::tei:seg[@type = "verse" and matches($node/@next, @xml:id)] return (fontaneSimple:mark-linegroup-beginning(), element tei:l { fontaneSimple:transform($node/node()), simpleHelpers:start-line($node), fontaneSimple:transform($corresp/node()) }, fontaneSimple:mark-linegroup-end()) else if($node/parent::tei:zone[@type = "verse"]/child::*[1] = $node) then fontaneSimple:transform($node/node()) (: with opting for a tei:milestone we try to meet the project's requirements on the one hand while avoiding to run into hierarchical problems in the further processing on the other hand:) else if(not($node/@type = "item")) then (simpleHelpers:start-line($node), fontaneSimple:transform($node/node())) else if($node/@type = "item") then element tei:item { $node/(@* except (@rend, @type, @style)), if($node/@rend) then attribute rendition {$node/@rend} else (), fontaneSimple:transform($node/node()) } else (simpleHelpers:start-line($node), fontaneSimple:transform($node/node())) case element(tei:handShift) return fontaneSimple:enhance-handshift($node) case element(tei:stamp) return if($node/ancestor::tei:surface[matches(@n, "Beilage")]) then fontaneSimple:copy-element($node) else () case element(tei:seg) return if(count($node/*) = 1 and (($node/tei:stamp and not($node/ancestor::tei:surface[matches(@n, "Beilage")])) or $node/tei:metamark[@function = "caret"])) then () else if($node/@type = "heading") then fontaneSimple:make-head($node) else if(matches($node/@style, "underline") and not(matches($node/@style, "vertical-align"))) then fontaneSimple:transform($node/node()) else if(simpleHelpers:has-valid-style($node) or matches($node/@rendition, "black_letter") or matches($node/@rendition, "roman")) then fontaneSimple:make-seg-with-rendition($node) else if($node/@type = "initials" or $node/@type = "monogram" or $node/@type = "multiphrase" or $node/@xml:lang) then fontaneSimple:copy-element($node) else if($node/@type = "auction_number" or $node/@type = "cancel" or $node/@type = "abort" or $node/@function ="unknown") then () else if($node/parent::tei:add and $node/@copyOf) then fontaneSimple:mark-intervention($node) else if($node/@type = "verse" and $node/@prev) then () else if($node/@type = "said") then if($node/@next) then let $next := replace($node/@next, "#", "") let $corresp := $node/following::*[@xml:id = $next] return (element tei:seg { $node/@type, fontaneSimple:transform($node/node()), fontaneSimple:transform($corresp/node()) }, element tei:lb { attribute type {"edited_text"} }) else element tei:seg { $node/@type, fontaneSimple:transform($node/node()) } else fontaneSimple:transform($node/node()) case element(tei:hi) return fontaneSimple:copy-element($node) (: TODO if $node/@type = "highlighted" then make a hi[@type = "vertical-mark"] in the second stage of creating the simple format. use simpleHelpers:get-xml-chunk($node) for this.:) case element(tei:mod) return if($node/@type = "highlighted" and simpleHelpers:is-hand-contemporary($node/@hand)) then $node else fontaneSimple:transform($node/node()) case element(tei:anchor) return fontaneSimple:copy-element($node) case element(tei:surface) return if(matches($node/@n, "cover")) then (fontaneSimple:make-pb-with-type($node/@n), fontaneSimple:transform($node/node())) else if(matches($node/@n, "Beilage")) then element tei:div { $node/@*, fontaneSimple:transform($node/node()) } else if($node/@type = "pocket") then (fontaneSimple:make-pb($node), element tei:div { $node/(@* except (@n, @ulx, @uly, @lry, @lrx)), fontaneSimple:transform($node/node()) }) else if(simpleHelpers:is-page($node) and $node/@type = "clipping") then (fontaneSimple:make-pb($node), (if(not($node/@subtype = "Kalenderblatt" or $node/@subtype = "Zeitungsausschnitt_Fragment")) then element tei:div { $node/(@* except (@n, @ulx, @uly, @lry, @lrx)), fontaneSimple:transform($node/node()) } else ())) else if(simpleHelpers:is-page($node)) then (fontaneSimple:make-pb($node), fontaneSimple:transform($node/node())) else if($node/@type = "label" and (contains($node/@subtype, "Fontane") or contains($node/@subtype, "Hersteller")) ) then fontaneSimple:make-div($node) else () case element(tei:milestone) return if($node/@unit = "illustration") then () else fontaneSimple:copy-element($node) case element(tei:gap) return fontaneSimple:copy-element($node) case element(tei:metamark) return if($node/@function = "integrate" or $node/@function = "authorial_note") then element tei:ab { $node/@* } else if($node/@function = "placeholder" or $node/@function ="etc." or $node/@function ="caret" or $node/@function = "footnote-mark" or $node/@function ="footnotes" or $node/@function ="ellipsis" or $node/@function = "paragraph") then element tei:ab { attribute type {$node/@function}, fontaneSimple:transform($node/node()) } else () case element(tei:surplus) return fontaneSimple:mark-intervention($node) case element(tei:zone) return if(matches($node/@style, "border-style:solid") and not(matches($node/@style, "border-radius")) and not($node/@rend = "border-style:house")) then element tei:div { attribute type {"frame"}, fontaneSimple:transform($node/node()) } else if(matches($node/@rend, "border-bottom-style:brace")) then (fontaneSimple:transform($node/node()), element tei:ab { attribute type {"bottom-brace"} }) else if($node/@type = "cancel") then () else if($node/@type = "marked_off") then element tei:seg { $node/@type, $node/@xml:id, fontaneSimple:transform($node/node()) } else if($node/@type = "highlighted") then if($node/child::tei:zone[@type = "highlighted"]) then fontaneSimple:transform($node/node()) else element tei:hi { attribute type {"vertical-mark"}, fontaneSimple:transform($node/node()) } else if($node/@type = "illustration" or $node/@type = "printed_illustration") then if(not($node//tei:figure/parent::tei:del)) then element {QName("http://www.tei-c.org/ns/1.0", "ab")}{ (if($node/child::tei:zone[@type = "illustration"]) then attribute type {"composed-sketch"} else (attribute type {"sketch"}, if($node/parent::tei:zone[@type = "illustration"]) then attribute rendition {"margin-left:" || $node/@ulx || "cm; " || "margin-top:" || $node/@uly || "cm"} else () ) ), fontaneSimple:transform($node/node()) } else () else if($node/parent::tei:zone/@type = "illustration" or $node/parent::tei:zone/@type = "printed_illustration") then element {QName("http://www.tei-c.org/ns/1.0", "seg")}{ attribute type {"caption"}, attribute rendition {"margin-left:" || $node/@ulx || "cm; " || "margin-top:" || $node/@uly || "cm"}, fontaneSimple:transform($node/node()) } else if($node/@type = "heading") then fontaneSimple:make-head($node) else if($node/@type = "list" or $node/@type = "item") then element {QName("http://www.tei-c.org/ns/1.0", $node/@type)} { $node/(@xml:id, @subtype, @rendition, @prev, @next), if($node/@rend) then attribute rendition {$node/@rend} else (), fontaneSimple:transform($node/node()) } else if($node/@type = "dialogue") then element tei:div { $node/@type, fontaneSimple:transform($node/node()) } else if($node/@type = "verse") then (fontaneSimple:mark-linegroup-beginning(), element tei:l { fontaneSimple:transform($node/node()) }, fontaneSimple:mark-linegroup-end()) else if($node/@type = "said" and $node/@prev) then () else if($node/@type = "said") then element tei:seg { $node/@type, fontaneSimple:transform($node/node()) } else if($node/@type = "toc" and matches($node/@subtype, "ungültig")) then () else if($node/@type = "toc") then element tei:list { $node/(@type, @subtype), fontaneSimple:transform($node/node()) } else if($node/@type = "legend") then element {QName("http://www.tei-c.org/ns/1.0", "div")}{ (if($node/@style or $node/@rendition) then attribute rendition {simpleHelpers:filter-rendition($node)} else ()), $node/(@* except (@rendition, @style)), fontaneSimple:transform($node/node()) } else if($node/@type = "additional") then element tei:ab { $node/(@* except (@ulx, @uly, @lry, @lrx)), fontaneSimple:transform($node/node()) } else if(simpleHelpers:has-valid-style($node) or matches($node/@rendition, "black_letter") or matches($node/@rendition, "script") or matches($node/@rendition, "roman")) then fontaneSimple:make-seg-with-rendition($node) else if(not($node/@xml:id)) then fontaneSimple:transform($node/node()) else if($node/@xml:id) then element tei:seg { $node/@xml:id, $node/@prev, $node/@next, $node/@corresp, fontaneSimple:transform($node/node()) } else fontaneSimple:transform($node/node()) case element(tei:said) return (element tei:seg { attribute type {"said"}, fontaneSimple:transform($node/node()) }, if($node/ancestor::tei:zone[@type = "dialogue"]/descendant::tei:said[position() != last()] = $node) then element tei:lb { attribute type {"edited_text"} } else ()) case element(tei:figure) return if(count($node/child::*) = 1 and $node/child::tei:figDesc) then (: genealogy lines probably shouldn't be displayed, but I still have to check that. in case they should be serialized, I leave the code :) (: if(matches($node/descendant::tei:ref, "Stammbaumverbindungslinie")) then:) (: element tei:seg {:) (: $node/@*,:) (: fontaneSimple:transform($node/node()):) (: }:) (: else if(matches($node/descendant::tei:ref, "Schlusslinie")):) if(matches($node/descendant::tei:ref, "Schlusslinie")) then element tei:ab { switch ($node/descendant::tei:ref) case "horizontale einfache Schlusslinie" return attribute type {"long-end-line"} case "Schlusslinie; horizontale Halbschleife von links oben nach rechts" return attribute type {"long-end-line"} case "horizontale einfache Schlusslinie (gewellt)" return attribute type {"long-end-line-wavy"} case "Schlusslinien; horizontale Schleife von links oben nach rechts unten" return attribute type {"bottom-brace-short"} default return attribute type {"end-line"} } else if(matches($node/descendant::tei:ref, "Absatzlinie") (: in case of double paragraph lines the single lines are encoded with "oberer" resp. "unterer Teil", but we only serialize the encoding for the upper line :) and not(matches($node/descendant::tei:ref, "unterer Teil")) and not(matches($node/descendant::tei:figDesc, "unsicher"))) then element tei:ab { if(matches($node/descendant::tei:ref, "doppelt")) then attribute type {"short-paragraph-line-double"} else attribute type {"short-paragraph-line"} } else if($node/ancestor::tei:zone[@type = "illustration"]) then element {QName("http://www.tei-c.org/ns/1.0", $node/name())}{ $node/@*, fontaneSimple:transform($nodes/node()) } else () else fontaneSimple:copy-element($node) case element(tei:note) return if($node/@type = "authorial" and not($node/@subtype = "footnote")) then () else fontaneSimple:copy-element($node) case element(tei:certainty) return element {QName("http://www.tei-c.org/ns/1.0", "note")}{ attribute type {"editorial"}, attribute subtype {"certainty"}, $node/@cert, $node/@target, fontaneSimple:transform($node/node()) } case element(tei:figDesc) return fontaneSimple:copy-element($node) case element(tei:ref) return fontaneSimple:copy-element($node) case element(tei:space) return fontaneSimple:copy-element($node) case element(tei:choice) return fontaneSimple:copy-element($node) case element(tei:abbr) return fontaneSimple:copy-element($node) case element(tei:expan) return fontaneSimple:copy-element($node) case element(tei:sic) return fontaneSimple:copy-element($node) case element(tei:corr) return fontaneSimple:copy-element($node) case element(tei:rs) return let $index-type := substring-before($node/@ref, ":") return element tei:rs { $node/@*, fontaneSimple:make-index-infos($node, $index-type), fontaneSimple:transform($node/node()) } case element(tei:date) return element {QName("http://www.tei-c.org/ns/1.0", $node/name())}{ (if($node/@when-iso) then attribute when {$node/@when-iso} else ()), (if($node/@from-iso) then attribute from {$node/@from-iso} else ()), (if($node/@to-iso) then attribute to {$node/@to-iso} else ()), (if($node/@notAfter-iso) then attribute notAfter {$node/@notAfter-iso} else ()), $node/(@* except (@when-iso, @to-iso, @from-iso, @notAfter-iso)), fontaneSimple:transform($node/node()) } case element(tei:ptr) return () default return fontaneSimple:transform($node/node()) }; (:~ : Takes a given element over as is since it is compliant to TEI simplePrint. : : @author Michelle Rodzis : @param $node the current text node : @return node() a copy of the current node :) declare function fontaneSimple:copy-element($node as node()) as node() { element {QName("http://www.tei-c.org/ns/1.0", $node/name())}{ $node/(@* except @rend), if($node/@rend) then attribute rendition {$node/@rend} else (), fontaneSimple:transform($node/node()) } }; (:~ : Creates a tei:head with an @rendition and a font-size, if available. : : @author Michelle Rodzis : @param $node the current tei:line, tei:zone or tei:seg node : @return element(tei:head) : :) declare function fontaneSimple:make-head($node as node()) as element(tei:head) { element tei:head { (if($node/descendant::tei:seg[matches(@style, "font-size")]) then attribute style {simpleHelpers:get-font-size($node)} else attribute style {"default"} ), attribute type {"former-" || $node/name()}, $node/@subtype, (if($node/@rendition) then $node/@rendition else ()), (if($node/@rend) then $node/@rend else ()), fontaneSimple:transform($node/node()) } }; (:~ : Creates a tei:div. : : @author Michelle Rodzis : @param $node the current tei:surface node : @return element(tei:div) : :) declare function fontaneSimple:make-div($node as element(tei:surface)) as element(tei:div) { element tei:div{ $node/(@* except (@facs, @n, @attachment, @subtype, @ulx, @uly, @lrx, @lry, @points)), fontaneSimple:transform($node/node()) } }; (:~ : Creates a new tei:seg with all relevant rendition information. : : @author Michelle Rodzis : @param $node the current tei:zone, tei:line, or tei:seg element : @return element(tei:seg) :) declare function fontaneSimple:make-seg-with-rendition($node as element(*)) as element(tei:seg) { element tei:seg { attribute rendition {simpleHelpers:filter-rendition($node)}, $node/(@* except (@style, @rendition)), (if($node[self::tei:line or self::tei:zone]) then attribute unit {$node/name()} else ()), fontaneSimple:transform($node/node()) } }; (:~ : Creates a tei:pb. : : @author Michelle Rodzis : @param $node the current tei:surface node : @return element(tei:pb) :) declare function fontaneSimple:make-pb($node as element(tei:surface)) as element(tei:pb) { element {QName("http://www.tei-c.org/ns/1.0", "pb")}{ $node/@n } }; (:~ : Creates a tei:pb with a type instead of a @n representing the pagination. : This function is mainly used to separate the inner and outer book covers from : each other. : : @author Michelle Rodzis : @param $node the current tei:surface node : @return element(tei:pb) :) declare function fontaneSimple:make-pb-with-type($type as xs:string) as element(tei:pb) { element {QName("http://www.tei-c.org/ns/1.0", "pb")}{ attribute type {$type} } }; declare function fontaneSimple:enhance-handshift($node as element(tei:handShift)) as element(tei:milestone) { let $prev-hand := $node/preceding::tei:handShift[@new][1] return element tei:milestone { attribute type {"handshift"}, attribute subtype {if($node/@new) then $node/@new else $prev-hand/@new}, attribute rend {concat("script(", (if($node/@script) then $node/@script else ""), ") ", "medium(", (if($node/@medium) then $node/@medium else ""), ")")}, $node/(@* except (@new, @medium, @script)) } }; declare function fontaneSimple:mark-intervention($node as element(*)) as element(tei:seg) { element tei:seg { if($node[self::tei:lb]) then attribute type {"missing-hyphen"} else if($node[self::tei:surplus]) then attribute type {"surplus"} else attribute type {"reduplication"}, , if($node[self::tei:lb]) then simpleHelpers:find-chars($node) else fontaneSimple:transform($node/node()), } }; declare function fontaneSimple:mark-missing-hyphen($node as element(tei:lb)) as element(tei:seg) { element tei:seg { attribute type {"missing-hyphen"}, , simpleHelpers:find-chars($node), } }; (:~ In this first serialization step the beginning and end of line groups are : simply marked with milestones, which are expanded to a full tei:lg in the : second serialization step. :) declare function fontaneSimple:mark-linegroup-beginning() as element(tei:milestone) { element tei:milestone { attribute unit {"start-lg"} } }; declare function fontaneSimple:mark-linegroup-end() as element(tei:milestone) { element tei:milestone { attribute unit {"end-lg"} } }; declare function fontaneSimple:has-pb-inbetween($node as node(), $corresp as node()*) as xs:boolean { if($node/following::tei:surface[. << $corresp]) then true() else false() }; declare function fontaneSimple:separates-corresp-nodes($node as element(tei:surface)) as xs:boolean { some $prev in $node/preceding::*[@next] satisfies contains($prev/@next, $node/descendant::*/@xml:id) or contains($prev/@next, $node/following::*/@xml:id) }; declare function fontaneSimple:make-index-infos($node as element(tei:rs), $index-type as xs:string) as element()* { let $refs := tokenize($node/@ref, " ") let $no-of-refs := count($refs) return for $iii in 1 to $no-of-refs let $ref := $refs[$iii] return element tei:index { attribute indexName {$index-type}, element tei:term { attribute type {"main"}, index-info:get-info-about($index-type, $ref, "regular-name") }, switch ($index-type) case "eve" return let $place := index-info:get-info-about($index-type, $ref, "place") return fontaneSimple:make-term("place", $place) case "org" return () case "plc" return let $subref := index-info:get-info-about($index-type, $ref, "subref-of") return fontaneSimple:make-term("subref-of", $subref) case "psn" return let $birth := index-info:get-info-about($index-type, $ref, "birth") let $death := index-info:get-info-about($index-type, $ref, "death") let $occupation := index-info:get-info-about($index-type, $ref, "occupation") return (fontaneSimple:make-term("birth", $birth), fontaneSimple:make-term("death", $death), fontaneSimple:make-term("occupation", $occupation)) case "wrk" return () default return () } }; declare function fontaneSimple:make-term($type as xs:string, $info as xs:string) as element(tei:term)* { if($info != "") then element tei:term { attribute type {$type}, $info } else () };