I am using Jxls to write data into an excel file.
For that I have a xls template R.raw.object_collection_xmlbuilder_template_products_list which is in */res>raw folder and xml builder file R.xml.excel_template_products_list is in */res>xml folder .
try (InputStream is = mContext.getResources().openRawResource(R.raw.object_collection_xmlbuilder_template_products_list)) {
try (OutputStream os = new FileOutputStream(excel_file)) {
Transformer transformer = TransformerFactory.createTransformer(is, os);
try (InputStream configInputStream = mContext.getResources().openRawResource(+R.xml.excel_template_products_list)) {
AreaBuilder areaBuilder = new XmlAreaBuilder(configInputStream, transformer);
List<Area> xlsAreaList = areaBuilder.build();
Area xlsArea = xlsAreaList.get(0);
org.jxls.common.Context context = new org.jxls.common.Context();
context.putVar("products", hashmap_list);
xlsArea.applyAt(new CellRef("Result!A1"), context);
transformer.write();
}
}
} catch (Exception ex) {
aLog.error(ex);
}
But Transformer Object is returning as null.
And for line List<Area> xlsAreaList = areaBuilder.build();, it throws :
Method threw 'java.lang.NoClassDefFoundError'
exception.org.jxls.builder.xml.AreaAction
These are the jar files I had added:
'libs/jxls-2.4.0.jar'
'libs/jxls-poi-1.0.12.jar'
'libs/slf4j.api-1.6.1.jar'
One solution suggested that I add the maven dependency, like so:
maven {
url "https://repo1.maven.org/maven2/"
}
But I still get the exception.
So, any idea why could the transformer be null and how can I fix the exception ? Thank you.
Edit:
This is xml content:
<?xml version="1.0" encoding="utf-8"?>
<xls>
<area ref="Template!A1:T31">
<each items="products" var="products" ref="Template!A4:T31">
<area ref="Template!A4:T31"/>
</each>
</area>
</xls>
This is the xls template:
Check the jxls-demo project repo to see how the Jxls dependencies are configured for Maven project.
Related
When creating a PDF using Apache FOP it is possible to embed a font with configuration file. The problem emerges when the app is a web application and it is necessary to embed a font that is inside WAR file (so treated as resource).
It is not acceptable to use particular container's folder structure to determine where exactly the war is located (when in configuration xml file we set tag to ./, it is set to the base folder of running container like C:\Tomcat\bin).
So the question is: Do anyone know the way to embed a font programatically?
After going through lots of FOP java code I managed to get it to work.
Descriptive version
Main idea is to force FOP to use custom PDFRendererConfigurator that will return desired font list when getCustomFontCollection() is executed.
In order to do it we need to create custom PDFDocumentHandlerMaker that will return custom PDFDocumentHandler (form method makeIFDocumentHandler()) which will in turn return our custom PDFRendererConfigurator (from getConfigurator() method) that, as above, will set out custom font list.
Then just add custom PDFDocumentHandlerMaker to RendererFactory and it will work.
FopFactory > RendererFactory > PDFDocumentHandlerMaker > PDFDocumentHandler > PDFRendererConfigurator
Full code
FopTest.java
public class FopTest {
public static void main(String[] args) throws Exception {
// the XSL FO file
StreamSource xsltFile = new StreamSource(
Thread.currentThread().getContextClassLoader().getResourceAsStream("template.xsl"));
// the XML file which provides the input
StreamSource xmlSource = new StreamSource(
Thread.currentThread().getContextClassLoader().getResourceAsStream("employees.xml"));
// create an instance of fop factory
FopFactory fopFactory = new FopFactoryBuilder(new File(".").toURI()).build();
RendererFactory rendererFactory = fopFactory.getRendererFactory();
rendererFactory.addDocumentHandlerMaker(new CustomPDFDocumentHandlerMaker());
// a user agent is needed for transformation
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// Setup output
OutputStream out;
out = new java.io.FileOutputStream("employee.pdf");
try {
// Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
// Setup XSLT
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xsltFile);
// Resulting SAX events (the generated FO) must be piped through to
// FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Start XSLT transformation and FOP processing
// That's where the XML is first transformed to XSL-FO and then
// PDF is created
transformer.transform(xmlSource, res);
} finally {
out.close();
}
}
}
CustomPDFDocumentHandlerMaker.java
public class CustomPDFDocumentHandlerMaker extends PDFDocumentHandlerMaker {
#Override
public IFDocumentHandler makeIFDocumentHandler(IFContext ifContext) {
CustomPDFDocumentHandler handler = new CustomPDFDocumentHandler(ifContext);
FOUserAgent ua = ifContext.getUserAgent();
if (ua.isAccessibilityEnabled()) {
ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler());
}
return handler;
}
}
CustomPDFDocumentHandler.java
public class CustomPDFDocumentHandler extends PDFDocumentHandler {
public CustomPDFDocumentHandler(IFContext context) {
super(context);
}
#Override
public IFDocumentHandlerConfigurator getConfigurator() {
return new CustomPDFRendererConfigurator(getUserAgent(), new PDFRendererConfigParser());
}
}
CustomPDFRendererConfigurator.java
public class CustomPDFRendererConfigurator extends PDFRendererConfigurator {
public CustomPDFRendererConfigurator(FOUserAgent userAgent, RendererConfigParser rendererConfigParser) {
super(userAgent, rendererConfigParser);
}
#Override
protected FontCollection getCustomFontCollection(InternalResourceResolver resolver, String mimeType)
throws FOPException {
List<EmbedFontInfo> fontList = new ArrayList<EmbedFontInfo>();
try {
FontUris fontUris = new FontUris(Thread.currentThread().getContextClassLoader().getResource("UbuntuMono-Bold.ttf").toURI(), null);
List<FontTriplet> triplets = new ArrayList<FontTriplet>();
triplets.add(new FontTriplet("UbuntuMono", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL));
EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, false, false, triplets, null, EncodingMode.AUTO, EmbeddingMode.AUTO);
fontList.add(fontInfo);
} catch (Exception e) {
e.printStackTrace();
}
return createCollectionFromFontList(resolver, fontList);
}
}
Yes you can do this. You need to set FOP's first base directory programmatically.
fopFactory = FopFactory.newInstance();
// for image base URL : images from Resource path of project
String serverPath = request.getSession().getServletContext().getRealPath("/");
fopFactory.setBaseURL(serverPath);
// for fonts base URL : .ttf from Resource path of project
fopFactory.getFontManager().setFontBaseURL(serverPath);
Then use FOB font config file.It will use above base path.
Just put your font files in web applications resource folder and refer that path in FOP's font config file.
After Comment : Reading font config programmatically (not preferred & clean way still as requested)
//This is NON tested and PSEUDO code to get understanding of logic
FontUris fontUris = new FontUris(new URI("<font.ttf relative path>"), null);
EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, "is kerning enabled boolean", "is aldvaned enabled boolean", null, "subFontName");
List<EmbedFontInfo> fontInfoList = new ArrayList<>();
fontInfoList.add(fontInfo);
//set base URL for Font Manager to use relative path of ttf file.
fopFactory.getFontManager().updateReferencedFonts(fontInfoList);
You can get more info for FOP's relative path https://xmlgraphics.apache.org/fop/2.2/configuration.html
The following approach may be useful for those who use PDFTranscoder.
Put the following xml template in the resources:
<?xml version="1.0" encoding="UTF-8"?>
<fop version="1.0">
<fonts>
<font kerning="no" embed-url="IBM_PLEX_MONO_PATH" embedding-mode="subset">
<font-triplet name="IBM Plex Mono" style="normal" weight="normal"/>
</font>
</fonts>
</fop>
Then one can load this xml and replace the line with font (IBM_PLEX_MONO_PATH) with the actual URI of the font from the resource bundle at runtime:
private val fopConfig = DefaultConfigurationBuilder()
.buildFromFile(javaClass.getResourceAsStream("/fonts/fopconf.xml")?.use {
val xml = BufferedReader(InputStreamReader(it)).use { bf ->
bf.readLines()
.joinToString("")
.replace(
"IBM_PLEX_MONO_PATH",
javaClass.getResource("/fonts/IBM_Plex_Mono/IBMPlexMono-Text.ttf")!!.toURI().toString()
)
}
val file = Files.createTempFile("fopconf", "xml")
file.writeText(xml)
file.toFile()
})
Now one can use this config with PDFTranscoder and your custom fonts will be probably rendered and embedded in PDF:
val pdfTranscoder = if (type == PDF) PDFTranscoder() else EPSTranscoder()
ContainerUtil.configure(pdfTranscoder, fopConfig)
val input = TranscoderInput(ByteArrayInputStream(svg.toByteArray()))
ByteArrayOutputStream().use { byteArrayOutputStream ->
val output = TranscoderOutput(byteArrayOutputStream)
pdfTranscoder.transcode(input, output)
byteArrayOutputStream.toByteArray()
}
I've created a report with JasperReports 6.4.3 which is normally exported to PDF. Now, I'm trying to export this report to HTML as well.
I don't want to create a HTML file via JasperExportManager, so I'm using JasperReport's HtmlExporter to wirte the report directly into an outputstream.
Her is my code:
public void exportToHtml(OutputStream outputStream, JRDataSource jrDataSource, Map<String, Object> parameter, File reportFile)
throws IOException {
try {
JasperPrint jasperprint = JasperFillManager.fillReport(reportFile.getAbsolutePath(), parameter, jrDataSource);
HtmlExporter exporter = new HtmlExporter();
SimpleHtmlExporterOutput exporterOutput = new SimpleHtmlExporterOutput(outputStream);
Map<String, String> images = Maps.newHashMap();
exporterOutput.setImageHandler(new HtmlResourceHandler() {
#Override
public void handleResource(String id, byte[] data) {
System.err.println("id" + id);
images.put(id, "data:image/jpg;base64," + Base64.encodeBytes(data));
}
#Override
public String getResourcePath(String id) {
return images.get(id);
}
});
exporter.setExporterOutput(exporterOutput);
exporter.setExporterInput(new SimpleExporterInput(jasperprint));
SimpleHtmlExporterConfiguration exporterConfiguration = new SimpleHtmlExporterConfiguration();
exporterConfiguration.setBetweenPagesHtml("<div style='page-break-after:always'></div>");
exporter.setConfiguration(exporterConfiguration);
exporter.exportReport();
} catch (JRException jrException) {
throw new IOException(jrException);
}
}
The output looks good, but I need to add some styles to the HTML output. So I wonder if it is possible to add a reference to a css file to the exported html report.
Similarly to how you are setting the between pages HTML, you could do the same for the header/footer with:
exporterConfiguration.setHtmlHeader("...");
exporterConfiguration.setHtmlFooter("...");
The default HTML code for header is set here and the one for footer is set here.
You need to match the opening/closing tags when modifying any of them.
In a xsl transformation I have a xslt file that includes some other xslt. The problem is that the URI for these xslt contains illegal characters, in particular '##'. The xslt looks like this:
<xsl:include href="/appdm/tomcat/webapps/sentys##1.0.0/WEB-INF/classes/xslt/release_java/xslt/gen.xslt" />
and when I try to instantiate a java Transformer I get the error:
javax.xml.transform.TransformerConfigurationException: javax.xml.transform.TransformerConfigurationException: javax.xml.transform.TransformerException: org.xml.sax.SAXException: org.apache.xml.utils.URI$MalformedURIException: Fragment contains invalid character:#
This is the java code:
public String xslTransform2String(String sXml, String sXslt) throws Exception {
String sResult = null;
try {
Source oStrSource = createStringSource(sXml);
DocumentBuilderFactory oDocFactory = DocumentBuilderFactory.newInstance();
oDocFactory.setNamespaceAware(true);
//sXslt is the xslt content with the inclusions
//<xsl:include href="/appdm/tomcat/webapps/sentys##1.0.0/WEB-INF/classes/xslt/release_java/xslt/gen.xslt" />"
Document oDocXslt = oDocFactory.newDocumentBuilder().parse(new InputSource(new StringReader(sXslt)));
Source oXsltSource = new DOMSource(oDocXslt);
StringWriter oStrOut = new StringWriter();
Result oTransRes = createStringResult(oStrOut);
Transformer oTrans = createXsltTransformer(oXsltSource);
oTrans.transform(oStrSource, oTransRes);
sResult = oStrOut.toString();
} catch (Exception oEx) {
throw new BddException(oEx, XmlProvider.ERR_XSLT, null);
}
return sResult;
}
private Transformer createXsltTransformer(Source oXsltSource) throws Exception {
Transformer transformer = getXsltTransformerFactory().newTransformer(
oXsltSource);
ErrorListener errorListener = new DefaultErrorListener();
transformer.setErrorListener(errorListener);
return transformer;
}
is there a way I can go with relative paths instead of absolute path?
Thank you
To avoid the MalformedURIException, replace the second or both # with %23.
See https://stackoverflow.com/a/5007362/4092205
This is my xml document:
<definitions>
<task name="TASK1"
class="CLASS"
group="GROUP">
<trigger count="3" interval="400"/>
<property xmlns:task="URI"
name="PROPERTY2"
value="VALUE1"/>
<property xmlns:task="URI"
name="PROPERTY2"
value="VALUE2"/>
</task>
<task name="TASK1"
class="CLASS"
group="GROUP">
<trigger count="1" interval="600"/>
<property xmlns:task="URI"
name="PROPERTY2"
value="VALUE1"/>
<property xmlns:task="URI"
name="PROPERTY2"
value="VALUE2"/>
</task>
<another_tag name="a_name"/>
<another_tag2 name="a_name2"/>
<another_tag3> something in the middle </another_tag3>
</definitions>
I have to delete all <task> tags and what inside them. I used this java code:
Document esb = new Document();
SAXBuilder saxBuilder = new SAXBuilder();
try {
esb = saxBuilder.build(new File("C:\\...path\\file.xml"));
}
catch (JDOMException ex) {
System.err.println(ex);
}
catch (IOException ex) {
System.err.println(ex);
}
Element root = esb.getRootElement();
boolean b = root.removeChild("task");
System.out.println(b);
I can't understand how to obtain an xml file without <task> tag and containing only <another_tag> tag. I've looked for other solutions but nothing useful. I also used removeContent() method, but nothing. I imported jdom2 libraries, I need to use recent libraries, because there are bad interactions among jdom and jdom2, so I would prefer to use recent libraries only. Any suggest about how to remove some element from this xml CODE?
The api says this for the function 'removeChild':
... This removes the first child element (one level deep) with the given local name and belonging to no namespace...
The removeChild function removes only one child. So if you want remove all childs with a specific name you need to use a loop. If the function call can't find a node with the desired name it returns with false.
If I work with your sample xml the third call of removeChild returns with false. So the following code will delete all task childs
...
boolean b = root.removeChild("task");
while (b)
b = root.removeChild("task");
...
In addition to what OkieOth has said, you are only removing the first task.
The other issue is that you are not writing the JDOM out again to file. You need to save over the existing file in order to see the changes.
If you want to apply the changes back to the file, consider this:
SAXBuilder saxBuilder = new SAXBuilder();
try {
Document esb = saxBuilder.build(new File("C:\\...path\\file.xml"));
Element root = esb.getRootElement();
boolean b = root.removeChild("task");
System.out.println(b);
XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
try (OutputStream os = new FileOutputStream("C:\\...path\\file.xml")) {
xout.output(os, esb)
}
}
catch (JDOMException ex) {
System.err.println(ex);
}
catch (IOException ex) {
System.err.println(ex);
}
I solved this problem by managing namespace. There is the code:`
Document doc = new Document();
SAXBuilder saxBuilder = new SAXBuilder();
try {
doc = saxBuilder.build(new File("....path\MyXMLfile.xml"));
}
catch (JDOMException ex) {
System.err.println(ex);
}
catch (IOException ex) {
System.err.println(ex);
}
Element root = esb.getRootElement();
System.out.println(root.getName()); // it prints "definitions"
Namespace namespace = Namespace.getNamespace("task","http://....myDefinitionsNamespace....");
boolean b = root.removeChildren("task", namespace);
System.out.println(b);
XMLOutputter xmlOutputter = new XMLOutputter();
xmlOutputter.setFormat(Format.getPrettyFormat());
System.out.println(xmlOutputter.outputString(doc)); //so we can see new XML FILE
In order to understand this code, we need starting xml too:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://....myDefinitionsNamespace....">
<task name="MyTask"
class="....myClass...."
group="....myGroup....">
<taskChild/>
</task>
<anotherTag1/>
<anotherTag2>
<task/>
.
.
.
</definitions>
The result is an XML file without every task tag, it will contain anotherTags only. Then you need to define the output (for example into a file with a FileOutputStream instance).
Thanks to #OkieOth and #rolfl.
Here is the code to use XPath to remove all task nodes incrementally using vtd-xml
import com.ximpleware.*;
import java.io.*;
public class removeElement {
public static void main(String s[]) throws VTDException,IOException{
VTDGen vg = new VTDGen();
if (!vg.parseFile("input.xml", false))
return;
VTDNav vn = vg.getNav();
AutoPilot ap = new AutoPilot(vn);
ap.selectXPath("/definition/task");
XMLModifier xm = new XMLModifier(vn);
int i=0;
while((i=ap.evalXPath())!=-1){
xm.remove();
}
xm.output("output.xml");
}
}
I am using jaxb for my application configurations
I feel like I am doing something really crooked and I am looking for a way to not need an actual file or this transaction.
As you can see in code I:
1.create a schema into a file from my JaxbContext (from my class annotation actually)
2.set this schema file in order to allow true validation when I unmarshal
JAXBContext context = JAXBContext.newInstance(clazz);
Schema mySchema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(schemaFile);
jaxbContext.generateSchema(new MySchemaOutputResolver()); // ultimately creates schemaFile
Unmarshaller u = m_context.createUnmarshaller();
u.setSchema(mySchema);
u.unmarshal(...);
do any of you know how I can validate jaxb without needing to create a schema file that sits in my computer?
Do I need to create a schema for validation, it looks redundant when I get it by JaxbContect.generateSchema ?
How do you do this?
Regarding ekeren's solution above, it's not a good idea to use PipedOutputStream/PipedInputStream in a single thread, lest you overflow the buffer and cause a deadlock. ByteArrayOutputStream/ByteArrayInputStream works, but if your JAXB classes generate multiple schemas (in different namespaces) you need multiple StreamSources.
I ended up with this:
JAXBContext jc = JAXBContext.newInstance(Something.class);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
jc.generateSchema(new SchemaOutputResolver(){
#Override
public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
outs.add(out);
StreamResult streamResult = new StreamResult(out);
streamResult.setSystemId("");
return streamResult;
}});
StreamSource[] sources = new StreamSource[outs.size()];
for (int i=0; i<outs.size(); i++) {
ByteArrayOutputStream out = outs.get(i);
// to examine schema: System.out.append(new String(out.toByteArray()));
sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()),"");
}
SchemaFactory sf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
m.setSchema(sf.newSchema(sources));
m.marshal(docs, new DefaultHandler()); // performs the schema validation
I had the exact issue and found a solution in the Apache Axis 2 source code:
protected List<DOMResult> generateJaxbSchemas(JAXBContext context) throws IOException {
final List<DOMResult> results = new ArrayList<DOMResult>();
context.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String ns, String file) throws IOException {
DOMResult result = new DOMResult();
result.setSystemId(file);
results.add(result);
return result;
}
});
return results;
}
and after you've acquired your list of DOMResults that represent the schemas, you will need to transform them into DOMSource objects before you can feed them into a schema generator. This second step might look something like this:
Unmarshaller u = myJAXBContext.createUnmarshaller();
List<DOMSource> dsList = new ArrayList<DOMSource>();
for(DOMResult domresult : myDomList){
dsList.add(new DOMSource(domresult.getNode()));
}
String schemaLang = "http://www.w3.org/2001/XMLSchema";
SchemaFactory sFactory = SchemaFactory.newInstance(schemaLang);
Schema schema = sFactory.newSchema((DOMSource[]) dsList.toArray(new DOMSource[0]));
u.setSchema(schema);
I believe you just need to set a ValidationEventHandler on your unmarshaller. Something like this:
public class JAXBValidator extends ValidationEventCollector {
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == event.ERROR ||
event.getSeverity() == event.FATAL_ERROR)
{
ValidationEventLocator locator = event.getLocator();
// change RuntimeException to something more appropriate
throw new RuntimeException("XML Validation Exception: " +
event.getMessage() + " at row: " + locator.getLineNumber() +
" column: " + locator.getColumnNumber());
}
return true;
}
}
And in your code:
Unmarshaller u = m_context.createUnmarshaller();
u.setEventHandler(new JAXBValidator());
u.unmarshal(...);
If you use maven using jaxb2-maven-plugin can help you. It generates schemas in generate-resources phase.