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