/* * $Id$ * * Authors: * Jeff Buchbinder <jeff@freemedsoftware.org> * * REMITT Electronic Medical Information Translation and Transmission * Copyright (C) 1999-2014 FreeMED Software Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.remitt.plugin.translation; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.remitt.prototype.PluginInterface; import org.remitt.server.Configuration; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.PageSize; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfWriter; public class FixedFormPdf implements PluginInterface { static final Logger log = Logger.getLogger(FixedFormPdf.class); public static final int LETTER_HEIGHT = 792; protected String defaultUsername = ""; protected XPath xpath = null; @Override public String getInputFormat() { return "fixedformxml"; } @Override public HashMap<String, String> getOptions() { return null; } @Override public String getOutputFormat() { return "pdf"; } @Override public String getPluginName() { return "FixedFormPdf"; } @Override public Double getPluginVersion() { return 0.1; } @Override public byte[] render(Integer jobId, byte[] input, String option) throws Exception { log.info("Entered Translate for job #" + jobId.toString()); Document newDocument = null; // Attempt to load input stream DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbFactory.newDocumentBuilder(); log.debug("Loading input into XmlDocument"); ByteArrayInputStream inputStream = new ByteArrayInputStream(input); org.w3c.dom.Document xmlInput = builder.parse(inputStream); XPathFactory xpFactory = XPathFactory.newInstance(); xpath = xpFactory.newXPath(); Node root = xmlInput.getDocumentElement(); NodeList nodeList = (NodeList) xpath.evaluate("/fixedform/page", root, XPathConstants.NODESET); // Create PDF document. If template, then let's use first page. String pdfTemplate = ""; int pdfPageNumber = 1; try { pdfTemplate = xpath.evaluate( "/fixedform/page/format/pdf/@template", root); pdfPageNumber = Integer.parseInt(xpath.evaluate( "/fixedform/page/format/pdf/@page", root)); } catch (Exception ex) { log.trace(ex.toString()); pdfTemplate = ""; pdfPageNumber = 1; } if (pdfTemplate.length() == 0) { log .debug("No PDF template found in XML stream, defaulting to LETTER size"); newDocument = new Document(PageSize.LETTER); } else { String templateFqdn = Configuration.getServletContext() .getServletContext().getRealPath( "/WEB-INF/pdf/" + pdfTemplate + ".pdf"); log.debug("Found template path = " + templateFqdn); PdfReader srcDocument = new PdfReader(templateFqdn); Rectangle srcPageRectangle = srcDocument .getPageSizeWithRotation(pdfPageNumber); newDocument = new Document(srcPageRectangle, 0, 0, 0, 0); } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PdfWriter writer = PdfWriter.getInstance(newDocument, outputStream); newDocument.open(); // Loop through all page elements int pageCount = 0; for (int iter = 0; iter < nodeList.getLength(); iter++) { log.debug("Iterating through page node #" + iter + ", pageCount = " + pageCount); Node page = nodeList.item(iter); newDocument.newPage(); // Append page translation translatePageFromNode(newDocument, writer, page); // Increment page counter pageCount += 1; } log.info("Leaving Translate for job #" + jobId.toString()); // Close document newDocument.close(); outputStream.flush(); outputStream.close(); return outputStream.toByteArray(); } private void translatePageFromNode(Document doc, PdfWriter writer, Node page) { // Grab template if necessary String pdfTemplate = ""; int pdfPageNumber = 1; try { pdfTemplate = xpath.evaluate("./format/pdf/@template", page); pdfPageNumber = Integer.parseInt(xpath.evaluate( "./format/pdf/@page", page)); } catch (Exception ex) { log.trace(ex.toString()); pdfTemplate = ""; pdfPageNumber = 1; } // Get font size int fontSize = 10; try { fontSize = Integer.parseInt(xpath.evaluate( "./format/pdf/font/@size", page)); } catch (Exception ex) { log.trace(ex.toString()); log.debug("Defaulting to 10pt font"); fontSize = 10; } // -------- Calculate offsets -------- int vOffset; try { vOffset = Integer.parseInt(xpath.evaluate( "./format/pdf/offset/@vertical", page)); } catch (Exception ex) { log.trace(ex.toString()); vOffset = 0; } int hOffset; try { hOffset = Integer.parseInt(xpath.evaluate( "./format/pdf/offset/@horizontal", page)); } catch (Exception ex) { log.trace(ex.toString()); hOffset = 0; } double vScaling; try { vScaling = Double.parseDouble(xpath.evaluate( "./format/pdf/scaling/@vertical", page)); } catch (Exception ex) { log.trace(ex.toString()); vScaling = 0; } double hScaling; try { hScaling = Double.parseDouble(xpath.evaluate( "./format/pdf/scaling/@horizontal", page)); } catch (Exception ex) { log.trace(ex.toString()); hScaling = 0; } // Prep ContentByte PdfContentByte cb = writer.getDirectContent(); int vSize = LETTER_HEIGHT; if (pdfTemplate.length() == 0) { vSize = LETTER_HEIGHT; } else { // Use template try { String templateFqdn = Configuration.getServletContext() .getServletContext().getRealPath( "/WEB-INF/pdf/" + pdfTemplate + ".pdf"); log.info("Using template " + templateFqdn); PdfReader srcDocument = new PdfReader(templateFqdn); Rectangle srcPageRectangle = srcDocument .getPageSizeWithRotation(pdfPageNumber); vSize = (int) srcPageRectangle.getHeight(); log.debug("vertical size = " + vSize); PdfImportedPage tp = writer.getImportedPage(srcDocument, pdfPageNumber); cb.addTemplate(tp, 0, 0); } catch (Exception ex) { log.trace(ex.toString()); vSize = LETTER_HEIGHT; } } // Begin text BaseFont bf = null; try { bf = BaseFont.createFont(BaseFont.COURIER, BaseFont.CP1252, BaseFont.EMBEDDED); } catch (DocumentException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } // Get elements ... NodeList elementNodes = null; try { elementNodes = (NodeList) xpath.evaluate("./element", page, XPathConstants.NODESET); } catch (XPathExpressionException e) { log.error("Found no elements " + e.toString()); } for (int jIter = 0; jIter < elementNodes.getLength(); jIter++) { Node element = elementNodes.item(jIter); int elementRow; int elementColumn; try { elementRow = Integer.parseInt(xpath.evaluate("./row", element)); } catch (Exception ex) { log.trace(ex.toString()); elementRow = 0; } try { elementColumn = Integer.parseInt(xpath.evaluate("./column", element)); } catch (Exception ex) { log.trace(ex.toString()); elementColumn = 0; } if ((elementRow > 0) && (elementColumn > 0)) { cb.beginText(); cb.setFontAndSize(bf, fontSize); // Calculate positions int colPos = (int) ((elementColumn * hScaling) + hOffset); int rowPos = (int) ((vSize - (elementRow * vScaling)) - vOffset); // Calculate effective element contents String elementContents = ProcessElement(element); // Reposition and display log.trace("Render '" + elementContents + "' @ " + colPos + "," + rowPos); // cb.setTextMatrix(colPos, rowPos); // cb.showText(elementContents); cb.showTextAligned(PdfContentByte.ALIGN_LEFT, elementContents, colPos, rowPos, 0); cb.endText(); } else { log.debug("Found null element, skipping."); } } } private String ProcessElement(Node element) { String elementContent; Integer elementLength; try { elementLength = Integer.parseInt(((Element) element) .getElementsByTagName("length").item(0).getTextContent()); } catch (Exception ex) { log.trace(ex.toString()); elementLength = 0; } // Make sure null is sent back if there's no length to be had here if (elementLength == 0) { return ""; } try { elementContent = ((Element) element) .getElementsByTagName("content").item(0).getTextContent(); } catch (Exception ex) { log.trace(ex.toString()); elementContent = ""; } // Chop string if it's too long if (elementContent.length() > elementLength) { return elementContent.substring(0, elementLength); } // Block to check for right alignment try { Integer alignment = Integer.parseInt(((Element) ((Element) element) .getElementsByTagName("format").item(0)) .getAttribute("right")); if (alignment == 1) { return StringUtils.leftPad(elementContent, elementLength); } } catch (Exception ex) { log.trace(ex.toString()); } // If formatting doesn't dictate otherwise, return padded output return StringUtils.rightPad(elementContent, elementLength); } @Override public String[] getPluginConfigurationOptions() { return null; } @Override public void setDefaultUsername(String username) { defaultUsername = username; } }