Java Create <a> element in SVG DOM - parsing

I create a Java application to modify some SVG files.
Here is my issue :
I would like to add an elements to the DOM of my SVG files to create link on a rectangle.
The SVG file is generated from another application.
I need to do it in Java.
So here is what I did :
I used JASXP to try to parse and modify my SVG file.
I tried many configurations, using SAX, DOM API to parse it and here is the only which I success to create a document from my SVG file.
Here is my SVGFile :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg ... xmlns:xlink="http://www.w3.org/1999/xlink" ....xmlns="http://www.w3.org/2000/svg" ...>
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>
<g>
<defs id="defs1">
....define the clipath...
</defs>
HERE I WOULD LIKE TO INSERT OPEN A ELEMENT : <a ....>
<g font-size="11" transform="translate(2,212)" fill-opacity="1"fill="rgb(192,255,255)" text-rendering="geometricPrecision" font-family="sans-serif" stroke="rgb(192,255,255)" font-weight="bold" stroke-opacity="1">
<rect x="0" width="80" height="40" y="0" clip-path="url(#clipPath1)" stroke="none"/>
</g>
<g font-size="11" stroke-linecap="butt" transform="translate(2,212)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" font-family="sans-serif" stroke-linejoin="round" stroke="black" font-weight="bold" stroke-opacity="1" stroke-miterlimit="0">
<rect fill="none" x="0" width="80" height="40" y="0" clip-path="url(#clipPath1)"/>
</g>
<g font-size="11" transform="translate(2,212)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1">
<text x="28" xml:space="preserve" y="12" clip-path="url(#clipPath4)"stroke="none" >Titre</text>
<line y2="12" fill="none" x1="28" clip-path="url(#clipPath4)" x2="52" y1="12"/>
</g>
</g>
</svg>
AND I WOULD LIKE TO CLOSE IT here : </a>
The code to obtain a DOM document from my SVG :
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
File file = new File ("D:/mySVGFile.svg");
String uri = file.toURI().toString();
Document doc = f.createDocument(uri);
To parse the document obtain I did that :
Element racine = doc.getDocumentElement();
String tag ="text"; // Because it's the only element I have store in an Array
NodeList liste = doc.getElementsByTagName(tag);
Element e = (Element) liste.item(0); // element text = Titre
Node target = e.getParentNode().getParentNode().getParentNode();
Element link = doc.createElement("a");
link.setAttribute("xlink:href", "DestinationFile.svg#67");
target.insertBefore(link, target);
Here is the console error :
org.w3c.dom.DOMException: The child node (type: 1, name: svg) is missing.
at org.apache.batik.dom.AbstractNode.createDOMException(AbstractNode.java:408)
at org.apache.batik.dom.AbstractParentNode.insertBefore(AbstractParentNode.java:78)
at SAXTagCount.main(SAXTagCount.java:60)
In fact I really don't know how can I create this element in Java for an SVG files.
Thanks by advance for your help !
Jim

The line
Element link = doc.createElement("a");
is incorrect. To create an SVG <a> element, or indeed any svg element you must use createElementNS i.e.
Element link = doc.createElementNS("http://www.w3.org/2000/svg", "a");
One of your other problems is that you have too many getParentNode() calls. The parent of the text element is its enclosing <g>. Its parent is another <g> and its parent is the <svg> root element so your insertBefore is trying to insert the <a> before the <svg> root element and that's not allowed. Looks to me like you need to remove 2 getParentNode() calls to put it at the right level.
You've various other issues like inserting at the right place - somehow you need to find that <g> element and ideally it would have an id or some way to identify it otherwise you'd have to search for it. Also you'd need to collect all the elements you want to make into children of the <a>. You'd to that by reparenting each existing element using link.appendChild so that its a child of the <a> element. That's a lot to cover in one answer though.
target.insertBefore(link, target);
isn't valid either as you can't use target twice. The argument should be a child of target, not target itself.

/**
* Use the SAXSVGDocumentFactory to parse the given URI into a DOM.
*
* #param uri
* The path to the SVG file to read.
* #return A Document instance that represents the SVG file.
* #throws Exception
* The file could not be read.
*/
private Document createSVGDocument(String uri) throws IOException {
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);
return factory.createDocument(uri);
}

Related

Trouble with XUL overlay, stringbundleset, getString is not a function, but createBundle method works

So I am working on an old XUL overlay add-on and encountered this problem, and simplified it to a minimal add-on. Was wondering if someone could explain to me if I am doing something wrong, if I misunderstood something, or how to get this to work as expected. Complete code below. Testing in both FF 29.0b1 and 49.0a1, same results.
In the main XUL file, I define a stringbundleset and a stringbundle, both with (very likely) unique IDs. In the JavaScript file, I want to simply get the bundle (not the set) by element ID, and use the getString (or getFormattedString) method. In the main JavaScript file, I have an event listener call a load function which can call other functions, including an init function, where I put the string grabbing code. All the string variables are defined globally with var (outside the init function). The problem is, I get getString is not a function. Further, console.log() DOES NOT EVEN WORK ANYWHERE IN THE FUNCTION, so I can't even check the value of variable that's supposed to hold the returned strings bundle.
In my add-on, I use the exact same pattern in Options and About XUL files, and it works, as the MDN documentation says it should. So I excluded this from the example code.
If attempting to build and test, note that in the example JavaScript code, I have the broken block that tries to use the XUL stringbundle and fails, and the working pure javascript-only technique which uses createBundle() is commented out. This is easily modified with the //* code /**/ comment pattern, just delete the 1st / and add a / on the other block and rebuild the xpi.
Here's an example screenshot of what the working code should create. The add-on relies on the old status bar. I used "The Addon Bar (Restored)" add-on. Note "TSBS" on the status bar. Right click "TSBS", a menu pops up with Options at the top. These labels are pulled in from the .dtd file. The Menu is a sub menu that has labels set by the tsbs.js script, pulling values from the .properties file. The example shows them pulled from the JS-only method. I am trying to get it to work from the XUL/DOM method.
I realize that XUL add-ons will die soon anyways. I realize that I shouldn't be relying solely upon the status bar. I realize I should just use the JS-only method as it seems more reliable. But I am asking this because I noticed it and wanted to learn from it. Any idea how to correct or improve the code pattern, or is this a known problem or common pitfall?
Directory tree:
test_string_bundle_set-1.0.0-fx
+-- chrome
| +-- content
| | +-- tsbs.js
| | +-- tsbs.xul
| +-- locale
| | +-- en-US
| | +-- contents.rdf
| | +-- tsbs.dtd
| | +-- tsbs.properties
+-- chrome.manifest
+-- install.rdf
chrome.manifest:
overlay chrome://browser/content/browser.xul chrome://{00735700-7357-7357-7357-073570073570}/content/tsbs.xul
content {00735700-7357-7357-7357-073570073570} chrome/content/
locale {00735700-7357-7357-7357-073570073570} en-US chrome/locale/en-US/
install.rdf:
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>{00735700-7357-7357-7357-073570073570}</em:id>
<em:version>1.0.0</em:version>
<em:type>2</em:type>
<em:name>Test String Bundle Sets</em:name>
<em:description>Test String Bundle Sets</em:description>
<em:creator>Quite A. Character</em:creator>
<!--Firefox-->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>29.0</em:minVersion>
<em:maxVersion>49.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>
chrome/content/tsbs.js:
var tsbs_strings;
var tsbs_string_4;
var tsbs_string_5;
var tsbs_string_6;
function tsbs_load( ) {
tsbs_init( );
return true;
}
function tsbs_close( ) {
return true;
}
window.addEventListener( 'load', tsbs_load, false );
window.addEventListener( 'close', tsbs_close, false );
function tsbs_init( ) {
//* XUL/DOM method
tsbs_strings = document.getElementById( 'tsbs-strings' );
console.log( 'TSBS->tsbs_init()->tsbs_strings: ' + typeof tsbs_strings );
tsbs_string_4 = tsbs_strings.getString( 'tsbs.properties.string.4' );
tsbs_string_5 = tsbs_strings.getString( 'tsbs.properties.string.5' );
tsbs_string_6 = tsbs_strings.getString( 'tsbs.properties.string.6' );
/**/
/* JS-only method
tsbs_strings = Services.strings.createBundle( 'chrome://{00735700-7357-7357-7357-073570073570}/locale/tsbs.properties' );
console.log( 'TSBS->tsbs_init()->tsbs_strings: ' + typeof tsbs_strings );
tsbs_string_4 = tsbs_strings.GetStringFromName( 'tsbs.properties.string.4' );
tsbs_string_5 = tsbs_strings.GetStringFromName( 'tsbs.properties.string.5' );
tsbs_string_6 = tsbs_strings.GetStringFromName( 'tsbs.properties.string.6' );
/**/
document.getElementById( 'tsbs-item-4' ).label = tsbs_string_4;
document.getElementById( 'tsbs-item-5' ).label = tsbs_string_5;
document.getElementById( 'tsbs-item-6' ).label = tsbs_string_6;
}
chrome/content/tsbs.xul:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://{00735700-7357-7357-7357-073570073570}/locale/tsbs.dtd">
<overlay id="tsbs-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://{00735700-7357-7357-7357-073570073570}/content/tsbs.js"/>
<stringbundleset id="tsbs-stringset">
<stringbundle id="tsbs-strings" src="chrome://{00735700-7357-7357-7357-073570073570}/locale/tsbs.properties"/>
</stringbundleset>
<statusbar id="status-bar">
<statusbarpanel
id="tsbs-display"
label="&tsbs.dtd.statusbarpanel.display;"
pack="end"
context="tsbs-contextmenu"
/>
</statusbar>
<window id="main-window">
<vbox id="tsbs-notifier">
<popupset id="mainPopupSet">
<menupopup id="tsbs-contextmenu" position="before_end">
<menuitem id="tsbs-options" label="&tsbs.dtd.options;"/>
<menuitem label="&tsbs.dtd.string.1;"/>
<menuitem label="&tsbs.dtd.string.2;"/>
<menuitem label="&tsbs.dtd.string.3;"/>
<menu id="tsbs-menu" label="&tsbs.dtd.menu;">
<menupopup id="tsbs-submenu">
<menuitem id="tsbs-item-4"/>
<menuitem id="tsbs-item-5"/>
<menuitem id="tsbs-item-6"/>
</menupopup>
</menu>
</menupopup>
</popupset>
</vbox>
</window>
</overlay>
chrome/locale/en-US/contents.rdf:
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<Seq about="urn:mozilla:locale:root">
<li resource="urn:mozilla:locale:en-US"/>
</Seq>
<Description about="urn:mozilla:locale:en-US">
<chrome:packages>
<Seq about="urn:mozilla:locale:en-US:packages">
<li resource="urn:mozilla:locale:en-US:{00735700-7357-7357-7357-073570073570}"/>
</Seq>
</chrome:packages>
</Description>
</RDF>
chrome/locale/en-US/tsbs.dtd:
<!ENTITY tsbs.dtd.statusbarpanel.display "TSBS" >
<!ENTITY tsbs.dtd.options "Options" >
<!ENTITY tsbs.dtd.menu "Menu" >
<!ENTITY tsbs.dtd.string.1 "One" >
<!ENTITY tsbs.dtd.string.2 "Two" >
<!ENTITY tsbs.dtd.string.3 "Three" >
chrome/locale/en-US/tsbs.properties:
extensions.{00735700-7357-7357-7357-073570073570}.description=Test String Bundle Sets
tsbs.properties.string.4=four
tsbs.properties.string.5=five
tsbs.properties.string.6=six
I believe this is your problem:
<stringbundleset id="tsbs-stringset">
In XUL overlays if a direct child of <overlay> has an ID, it's supposed to match an element in the base document. If it does, its contents is inserted under the matching element. If it doesn't this part of an overlay is ignored.
The existing stringbundleset in browser.xul can be matched using:
<stringbundleset id="stringbundleset">
Also I don't believe there's a 'close' event (use 'unload'), and you don't need the contents.rdf since Firefox 2 or so. Regarding console.log() not working - you checked the Browser Console, not the Web Console, right?

Fallback for SVG fill image source

I am looking into using an image as an SVG fill. I am currently using the following SVG markup:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="logo" x="0px" y="0px" viewBox="0 0 200 25" enable-background="new 0 0 0 0" xml:space="preserve">
<defs>
<pattern id="bg" patternUnits="userSpaceOnUse" width="300" height="25">
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="..." preserveAspectRatio="xMidYMid slice"></image>
</pattern>
</defs>
<g class="graphic">
<path d="..."></path>
</g>
(I have left some details out ("...") of which I am sure are not relevant to my issue.)
In CSS, I use the following code to append the pattern ID. Pls see below:
.default-header .site-name svg.logo .graphic {
fill:url(#bg);
}
I've already done some digging and found out that this technique (images as SVG background) isn't supported in FireFox and iOS Safari. Therefore I used the following "fall-back" method in CSS:
.default-header .site-name svg.logo .graphic {
fill:#ddd;
}
#supports (-webkit-appearance:none) {
.default-header .site-name svg.logo .graphic {
fill:url(#bg);
}
}
The above code works, EXCEPT for iOS 8/9's Safari. So my issue is mainly that I don't know how to target those non-supporting browsers, which I'm sure there are more of than I am currently testing. SO... I decided to use Modernizr to look for support, but I can't seeem to find an appropriate, representational way of checking if this SVG technique is supported. In other words, on this page: https://modernizr.com/download?setclasses, I can't find the browser feature appropriate for my issue.
I hope somebody knows what direction I could best look into or better yet, has some experience with this technique, which I still think is so awesome (if it works).
Thanks in advance guys!
I found a solution by using a different approach in markup. Instead of using defs and gs, I used the following markup to achieve the same effect:
<svg id="graphic" width="300" height="40">
<!-- Graphic: Styling (inline as FF fix) -->
<style> svg image { clip-path:url(#clipping); } </style>
<!-- Clip Path -->
<clipPath id="clipping">
<!-- this might as well be any type of path -->
<text id="graphicText" x="0" y="37">My Text</text>
</clipPath>
<!-- Image -->
<image id="graphicImage" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="{img_url}" width="300" height="300" x="0" y="-120px"></image>
</svg>
I used CSS styling for the text itself.
Hope this helps someone!

how to translate sign under appbar buttom

Hello I want to localize text below standard AddAppBarButton
<Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="AddAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Add"/>
<Setter Property="Content" Value=""/>
</Style>
I've tried in Resource filead add something like
ButtonId.AutomationProperties.Name = value
But it doesn not work. I get errors when app starts. How can I trasnlate this property ?
Why not put the translation strings in resource files and let XAML parser do the work?
You need to create a folder in your project matching the locale name and put a resw file inside it, e.g sl-SI\Resources.resw.
Add x:Uid attribute to XAML elements to name them:
<Button x:Uid="AppBarButton" Style="{StaticResource AddAppBarButtonStyle}" />
Now just name the resource strings appropriately so that the XAML parser will find them. The pattern is UidName.PropertyName, e.g. Button.Content. In the case of AppBar buttons the syntax is a little more complex because of attached properties:
AppBarButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name
You could try AutomationProperties.SetName(ButtonId, value);

How can i embed SVG in JSF 2 Facelets (XHTML)?

I have a JSF 2 application which creates some SVG content. How can i embed it in the output HTML?
The generated SVG looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" style="fill-opacity:1; color-rendering:auto; color-interpolation:auto; stroke:black; text-rendering:auto; stroke-linecap:square; stroke-miterlimit:10; stroke-opacity:1; shape-rendering:auto; fill:black; stroke-dasharray:none; font-weight:normal; stroke-width:1; font-family:&apos;Dialog&apos;; font-style:normal; stroke-linejoin:miter; font-size:12; stroke-dashoffset:0; image-rendering:auto;" xmlns="http://www.w3.org/2000/svg">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>
<g/>
</svg>
I want this output directly in the rendered HTML page. I don't want to use the <object> tag, because i want to be able to manipulate the svg content on the client via javascript.
The result should look like this:
<div id="svgcontent">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 50" id="myDIVBG">
<defs>
[...]
</svg>
</div>
The above is correctly displayed in my browser, I just cant get the XML into the HTML without the XML string being escaped.
What I have always done is edit my SVG files with a tool like inkscape and include them at the appropriate spot with a ui:include tag. Like this:
<ui:include src="images/somedrawing.svg" />
The included file starts with an xml tag followed by an svg tag and the rest of the drawing. The size of the block on the page will be drawn according to the viewPort attributes of the svg tag.
This only works with HTML 5, so make sure your file starts with
<!DOCTYPE html>
I've never used JSF, but searching for "JSF disable escaping" turned up this:
<h:outputText value="???" escape="false" />

How to embed hyperlink in the entire trecell of treeitem of treecol in XUL Tree

I have a XUL tree formed using XML template. I have treecol for URL's and I want to embed those url's as a hyperlink.
<tree id="myTodoListTree" flex="1" seltype="multiple"
datasources="file://C:/mercredi.xml" ref="*" querytype="xml" >
<treecol id="url" label="Facebook" flex="1" />
<template>
<query expr="CONTACT">
<assign var="?facebook" expr="./URL/text()"/>
</query>
<action>
<treechildren id="myTodoListTreeChildren">
<treeitem uri="?">
<treerow>
<treecell value="true" editable="false" label="?facebook"/>
</treerow>
</treeitem>
</treechildren>
</action>
</template>
</tree>
This thread has similar problem but no proper solution.
How to embedded a link in a XUL treecell?
Even in the Mozilla website referred by the above link has no proper solution.
I couldn't find any refrence in XUL School mozilla website.
For emaple, this is one of the url item in the tree-cell: It's a duplicate value
http://www.facebook.com/profile.php?id=101112487211054
Using CSS I can assign something to the entire treecolumn like this: it works.
treechildren:-moz-tree-column {
border-right:1px solid rgb(220,220,220) !important;
}
When i tried to assign to the specific column, it's not working.
#url>treechildren::-moz-tree-column(hover){
cursor: pointer;
text-decoration: underline;
}
This selector is wrong: #url>treechildren::-moz-tree-column(hover). The cell with id url does not have <treechildren> in it, the <treechildren> has a cell in it. Try something like this instead: treechildren .url::-moz-tree-column(hover), which selects any cell with the classs url.

Resources