Batik copy element from one document to another - java

I'm trying to compose an svg with batik from elements from a source document. This works as long as the elements don't reference things defined in the defs section (like gradients or filters). But when a filter is referenced I get an exception. I tried to also copy over the defs section but that didn't help.
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);
Document source = factory.createDocument("source", getClass().getResourceAsStream("/artwork/source.svg"));
SVGOMDocument target = (SVGOMDocument) domImpl.createDocument(svgNS, "svg", null);
Node defs = source.getElementsByTagName("defs").item(0).cloneNode(true);
target.adoptNode(defs);
target.getRootElement().appendChild(defs);
BridgeContext ctx = new BridgeContext(new UserAgentAdapter());
GVTBuilder builder = new GVTBuilder();
builder.build(ctx, target);
Element sourceEl = getElementByXPath(source,
"//*[#id='IMAGES']/*[#id='" + selection.getImageSet() + "']/*[#id='"
+ suit.abbreviation() + value + "-" + selection.getImageSet() + "']");
SVGOMElement complete = (SVGOMElement) sourceEl.cloneNode(true);
target.adoptNode(complete);
target.getDocumentElement().appendChild(complete);
Rectangle2D completeBBox = builder.build(ctx, complete).getSensitiveBounds();
The last line gives the exception. Any help would be appreciated.
EDIT:
If I save the target svg after adding the defs section and then reload it again it works. But I would like to avoid this aditional step.

I found a solution to avoid the extra save/load. Use importNode instead of clone/adopt for the defs.
Node defs = target.importNode(source.getElementsByTagName("defs").item(0), true);
target.getRootElement().appendChild(defs);

Related

Apache POI can not add CustomProperty to Doc

I'm trying to add some Custom Properties to an existing document:
HWPFDocument document = new HWPFDocument(new FileInputStream(sourceFile));
DocumentSummaryInformation docSumInf = document.getDocumentSummaryInformation();
CustomProperties customProperties = docSumInf.getCustomProperties();
CustomProperty singleProp = null;
//...
singleProp = new CustomProperty();
singleProp.setName(entry.getKey());
singleProp.setType(Variant.VT_LPWSTR);
singleProp.setValue((String) entry.getValue());
//..
customProperties.put(entry.getKey(), singleProp);
docSumInf.setCustomProperties(customProperties);
return document;
However, the properties never make it to the file. I tried to
document.getDocumentSummaryInformation().getCustomProperties().putAll(customProperties);
I also tried
document.getDocumentSummaryInformation().getCustomProperties().put(entry.getKey(), singleProp);
System.out.println(document.getDocumentSummaryInformation().getCustomProperties().size() + " Elemente in Map");
in a loop. The printed size was allways one.
With the first attemp (docSumInf.setCustomProperties(customProperties);) I printed out customProperties before setting it to docSumInf. There where all new Properties I miss, as soon as I set them to the document summary.
I don't see what I am missing...
entry.getKey() = null
or entry.getKey() has common value for all CustomProperties in the map.
and because of that you have only one element in the map of CustomProperties.
You need to set non null values here
singleProp.setName(entry.getKey());
CustomProperty class represents custom properties in the document summary information stream. The difference to normal properties is that custom properties have an optional name. If the name is not null it will be maintained in the section's dictionary.

How to dynamically create a document in the GAE Search API?

I am trying to understand how i can dynamically add fields in my document.
I am following the doc . However i'd like to use the addField method inside a "for" statement and thus programmatically generate fields.
I've tried this :
Builder document = Document.newBuilder();
document.setId(id);
// for statement start
com.google.appengine.api.search.Field.Builder field = Field.newBuilder().setName(key).setAtom(value);
document.addField(field);
// for statement end
document.build();
But it doesn't seems to work. When i'm trying i got this printed out
com.google.appengine.api.search.Document$Builder#2604ed54
Whereas when i use the original code :
String myDocId = "PA6-5000";
Document doc = Document.newBuilder()
// Setting the document identifer is optional.
// If omitted, the search service will create an identifier.
.setId(myDocId)
.addField(Field.newBuilder().setName("content").setText("the rain in spain"))
.addField(Field.newBuilder().setName("email").setText(userEmail))
.addField(Field.newBuilder().setName("domain").setAtom(userDomain))
.addField(Field.newBuilder().setName("published").setDate(new Date()))
.build();
I get the expected result with the code above.
How i can manipulate this in order to put addfield() into for statement ?
thanks for helping.
The docs imply the Document.Builder is mutable, but typically you should reassign the builder after each invocation in case it isn't.
Builder document = Document.newBuilder();
document.setId(id);
com.google.appengine.api.search.Field.Builder field = Field.newBuilder().setName(key).setAtom(value);
document = document.addField(field);
document.build();
In your case what you're seeing is the toString() method on the builder, not the document itself. The problem is the last line.
Try this:
Builder builder = Document.newBuilder();
builder.setId(id);
com.google.appengine.api.search.Field.Builder field = Field.newBuilder().setName(key).setAtom(value);
builder = builder.addField(field);
Document document = builder.build();
Then you can just drop in your for loop and away you go.

xades4j: Sign only one element

I'm using this code to sign a xml document:
Document doc = getDocument(xml_to_sign);
Element elemToSign = doc.getDocumentElement();
String file_uri_path = elemToSign.getBaseURI();
DataObjectDesc obj1 = new DataObjectReference(file_uri_path).withType("http://www.gzs.si/shemas/eslog/racun/1.5#Racun");
SignedDataObjects dataObjs = new SignedDataObjects(obj1);
signer.sign(dataObjs, elemToSign);
xml_to_sign is the full path to the xml file.
The problem is, that I would like to sign only the node with the id "data" (#data), but append the signature to the node elemToSign.
Is it possible to do this with xades4j?
Yes, it is. The sign method's argument is the parent node, not the element to sign (it could be the same node, depending on the configured references). In your example you should add a reference for "#data":
Document doc = getDocument(xml_to_sign);
Element parent = doc.getDocumentElement();
DataObjectDesc obj1 = new DataObjectReference("#data").withType("http://www.gzs.si/shemas/eslog/racun/1.5#Racun");
SignedDataObjects dataObjs = new SignedDataObjects(obj1);
signer.sign(dataObjs, parent);
Another option is to add a reference for the whole XML document (empty URI) and use a XPath transform.
You should specify that the attribute named "Id" in your xml document is the XML ID attribute that Apache Santuario (used internally by Xades4j) will use in the getElementById() (as lgoncalves has pointed out in his commments to his own answer).
Element parent = doc.getDocumentElement();
parent.setIdAttribute("Id", true);
//or parent.setIdAttributeNS("http://your.name.space", "Id", true);
I had the same problem, and this additional line of code solved it.

Parser JSoup change the tags to lower case letter

I did some research and it seems that is standard Jsoup make this change. I wonder if there is a way to configure this or is there some other Parser I can be converted to a document of Jsoup, or some way to fix this?
Unfortunately not, the constructor of Tag class changes the name to lower case:
private Tag(String tagName) {
this.tagName = tagName.toLowerCase();
}
But there are two ways to change this behavour:
If you want a clean solution, you can clone / download the JSoup Git and change this line.
If you want a dirty solution, you can use reflection.
Example for #2:
Field tagName = Tag.class.getDeclaredField("tagName"); // Get the field which contains the tagname
tagName.setAccessible(true); // Set accessible to allow changes
for( Element element : doc.select("*") ) // Iterate over all tags
{
Tag tag = element.tag(); // Get the tag of the element
String value = tagName.get(tag).toString(); // Get the value (= name) of the tag
if( !value.startsWith("#") ) // You can ignore all tags starting with a '#'
{
tagName.set(tag, value.toUpperCase()); // Set the tagname to the uppercase
}
}
tagName.setAccessible(false); // Revert to false
Here is a code sample (version >= 1.11.x):
Parser parser = Parser.htmlParser();
parser.settings(new ParseSettings(true, true));
Document doc = parser.parseInput(html, baseUrl);
There is ParseSettings class introduced in version 1.9.3.
It comes with options to preserve case for tags and attributes.
You must use xmlParser instead of htmlParser and the tags will remain unchanged. One line does the trick:
String html = "<camelCaseTag>some text</camelCaseTag>";
Document doc = Jsoup.parse(html, "", Parser.xmlParser());
I am using 1.11.1-SNAPSHOT version which does not have this piece of code.
private Tag(String tagName) {
this.tagName = tagName.toLowerCase();
}
So I checked ParseSettings as suggested above and changed this piece of code from:
static {
htmlDefault = new ParseSettings(false, false);
preserveCase = new ParseSettings(true, true);
}
to:
static {
htmlDefault = new ParseSettings(true, true);
preserveCase = new ParseSettings(true, true);
}
and skipped test cases while building JAR.

Java ME: How to flip svg image horizontally/vertically?

The most helpfull result from google is this, but there are no scale() with two arguments in class SVGMatrix in JavaME that could be an analogous for this:
TinyMatrix transform = (TinyMatrix) node.getAttribute(SVG.ATT_TRANSFORM);
transform.scale(-(1<<Tiny2D.DFIX_BITS), 1<<Tiny2D.DFIX_BITS );
May be it is possible to do something with defining my own matrix for flipping and then use mMultiply() method, but I also could not find examples in net.
Ok, I solved it myself, flip is possible to do with embeded SVG-properties:
SVGImage svgImage = (SVGImage) this.frames.elementAt(i);
Document doc = svgImage.getDocument();
SVGSVGElement svg = (SVGSVGElement) doc.getDocumentElement();
SVGElement image = (SVGElement) doc.getElementById("image");
SVGElement group = (SVGElement) doc.createElementNS(SVG_NAMESPACE_URI, "g");
group.appendChild(image);
group.setTrait("transform", "translate(200,200) scale(-1,1)");
svg.appendChild(group);

Categories

Resources