// inline_svg.ysl2 // // Produce Inline SVG element of resulting XHTML page. // Since stylesheet output namespace is xhtml, templates that output svg have to be explicitely declared as such in xsl decl svgtmpl(match, xmlns="http://www.w3.org/2000/svg") alias template; in xsl decl svgfunc(name, xmlns="http://www.w3.org/2000/svg") alias template; // Identity template : // - copy every attributes // - copy every sub-elements svgtmpl "@*", mode="inline_svg" xsl:copy; template "node()", mode="inline_svg" { // use real xsl:copy instead copy-of alias from yslt.yml2 if "not(@id = $discardable_elements/@id)" xsl:copy apply "@* | node()", mode="inline_svg"; } // replaces inkscape's height and width hints. forces fit template "svg:svg/@width", mode="inline_svg"; template "svg:svg/@height", mode="inline_svg"; svgtmpl "svg:svg", mode="inline_svg" svg { attrib "preserveAspectRatio" > none attrib "height" > 100vh attrib "width" > 100vw apply "@* | node()", mode="inline_svg"; } // ensure that coordinate in CSV file generated by inkscape are in default reference frame template "svg:svg[@viewBox!=concat('0 0 ', @width, ' ', @height)]", mode="inline_svg" { error > ViewBox settings other than X=0, Y=0 and Scale=1 are not supported } // ensure that coordinate in CSV file generated by inkscape match svg default unit template "sodipodi:namedview[@units!='px' or @inkscape:document-units!='px']", mode="inline_svg" { error > All units must be set to "px" in Inkscape's document properties } // remove i18n markers, so that defs_by_labels can find text elements svgtmpl "svg:text/@inkscape:label[starts-with(., '_')]", mode="inline_svg" { attrib "{name()}" > «substring(., 2)» } ////// Clone unlinking // // svg:use (inkscape's clones) inside a widgets are // replaced by real elements they refer in order to : // - allow finding "needle" element in "meter" widget, // even if "needle" is in a group refered by a svg use. // - if "needle" is visible through a svg:use for // each instance of the widget, then needle would show // the same position in all instances // // For now, clone unlinkink applies to descendants of all widget except HMI:Page // TODO: narrow application of clone unlinking to active elements, // while keeping static decoration cloned const "targets_not_to_unlink", "$hmi_lists/descendant-or-self::svg:*"; const "to_unlink", "$hmi_widgets/descendant-or-self::svg:use"; def "func:is_unlinkable" { param "targetid"; param "eltid"; result "$eltid = $to_unlink/@id and not($targetid = $targets_not_to_unlink/@id)"; } svgtmpl "svg:use", mode="inline_svg"{ const "targetid","substring-after(@xlink:href,'#')"; choose { when "func:is_unlinkable($targetid, @id)" { call "unlink_clone" { with "targetid", "$targetid"; } } otherwise xsl:copy apply "@*", mode="inline_svg"; } } // to unlink a clone, an group containing a copy of target element is created // that way, style and transforms can be preserved const "_excluded_use_attrs" { name > href name > width name > height name > x name > y name > id } const "excluded_use_attrs","exsl:node-set($_excluded_use_attrs)"; const "_merge_use_attrs" { name > transform name > style } const "merge_use_attrs","exsl:node-set($_merge_use_attrs)"; svgfunc "unlink_clone"{ param "targetid"; param "seed","''"; const "target", "//svg:*[@id = $targetid]"; const "seeded_id" choose { when "string-length($seed) > 0" > «$seed»_«@id» otherwise value "@id"; } g{ attrib "id" value "$seeded_id"; attrib "original" value "@id"; choose { when "$target[self::svg:g]" { foreach "@*[not(local-name() = $excluded_use_attrs/name | $merge_use_attrs)]" attrib "{name()}" > «.» if "@style | $target/@style" attrib "style" { > «@style» if "@style and $target/@style" > ; > «$target/@style» } if "@transform | $target/@transform" attrib "transform" { > «@transform» if "@transform and $target/@transform" > > «$target/@transform» } apply "$target/*", mode="unlink_clone"{ with "seed","$seeded_id"; } } otherwise { // include non excluded attributes foreach "@*[not(local-name() = $excluded_use_attrs/name)]" attrib "{name()}" > «.» apply "$target", mode="unlink_clone"{ with "seed","$seeded_id"; } } } } } // clone unlinking is really similar to deep-copy // all nodes are sytematically copied svgtmpl "@id", mode="unlink_clone" { param "seed"; attrib "id" > «$seed»_«.» attrib "original" > «.» } svgtmpl "@*", mode="unlink_clone" xsl:copy; svgtmpl "svg:use", mode="unlink_clone" { param "seed"; const "targetid","substring-after(@xlink:href,'#')"; choose { when "func:is_unlinkable($targetid, @id)" { call "unlink_clone" { with "targetid", "$targetid"; with "seed","$seed"; } } otherwise xsl:copy apply "@*", mode="unlink_clone" { with "seed","$seed"; } } } // copying widgets would have unwanted effect // instead widget is refered through a svg:use. svgtmpl "svg:*", mode="unlink_clone" { param "seed"; choose { // node recursive copy ends when finding a widget when "@id = $hmi_widgets/@id" { // place a clone instead of copying use{ attrib "xlink:href" > «concat('#',@id)» } } otherwise { xsl:copy apply "@* | node()", mode="unlink_clone" { with "seed","$seed"; } } } } const "result_svg" apply "/", mode="inline_svg"; const "result_svg_ns", "exsl:node-set($result_svg)"; emit "preamble:inline-svg" { | let id = document.getElementById.bind(document); | var svg_root = id("«$svg/@id»"); } emit "debug:clone-unlinking" { | | Unlinked : foreach "$to_unlink"{ | «@id» } | Not to unlink : foreach "$targets_not_to_unlink"{ | «@id» } }