SVGHMI: Filter unseen geometry from inkscape CSV output.
When inkscape exports geometry form all objects, then it also includes objects from svg:defs. This makes problems when deciding if an object is part of a page, since coordinate of objects in svg:defs can eventualy be contained in a page. In the end, those objects where getting detached when leaving pages where they where found, leading for exemple to non working text on clipping when the clipped text was cloned in multiple page.
// 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_elements[not(@id = $hmi_pages/@id)]/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_elements/@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»
}
}