1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.coeus.s2sgen.impl.print;
17
18 import com.lowagie.text.*;
19 import com.lowagie.text.Font;
20 import com.lowagie.text.pdf.*;
21 import org.apache.commons.lang3.StringUtils;
22 import org.apache.fop.apps.*;
23 import org.kuali.coeus.propdev.api.s2s.S2SConfigurationService;
24 import org.kuali.coeus.s2sgen.api.core.S2SException;
25 import org.kuali.coeus.s2sgen.api.core.ConfigurationConstants;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.springframework.beans.factory.annotation.Autowired;
29 import org.springframework.beans.factory.annotation.Qualifier;
30 import org.springframework.stereotype.Component;
31
32 import javax.xml.transform.*;
33 import javax.xml.transform.sax.SAXResult;
34 import javax.xml.transform.stream.StreamSource;
35 import java.awt.*;
36 import java.io.*;
37 import java.sql.Timestamp;
38 import java.text.DateFormat;
39 import java.text.SimpleDateFormat;
40 import java.util.*;
41 import java.util.List;
42
43
44
45
46
47
48 @Component("s2SPrintingService")
49 public class S2SPrintingServiceImpl implements S2SPrintingService {
50
51 private static final Logger LOG = LoggerFactory.getLogger(S2SPrintingServiceImpl.class);
52 public static final String PDF_REPORT_CONTENT_TYPE = "application/pdf";
53 public static final String PDF_FILE_EXTENSION = ".pdf";
54 public char SPACE_SEPARATOR = 32;
55 public int WHITESPACE_LENGTH_76 = 76;
56 public int WHITESPACE_LENGTH_60 = 60;
57
58 @Autowired
59 @Qualifier("s2SConfigurationService")
60 private S2SConfigurationService s2SConfigurationService;
61
62
63
64
65
66
67
68
69 protected Map<String, byte[]> getPrintBytes(S2SPrintable printableArtifact) {
70 try {
71 Map<String, byte[]> streamMap = printableArtifact.renderXML();
72 try{
73 String loggingEnable = s2SConfigurationService.getValueAsString(ConfigurationConstants.PRINT_LOGGING_ENABLE);
74 if (loggingEnable != null && Boolean.parseBoolean(loggingEnable))
75 logPrintDetails(streamMap);
76 }catch(Exception ex){
77 LOG.error(ex.getMessage());
78 }
79
80 Map<String, byte[]> pdfByteMap = new LinkedHashMap<String, byte[]>();
81
82 FopFactory fopFactory = FopFactory.newInstance();
83
84 int xslCount = 0;
85
86
87 if (printableArtifact.getXSLTemplates() != null) {
88 for (Source source : printableArtifact.getXSLTemplates()) {
89 xslCount++;
90 StreamSource xslt = (StreamSource) source;
91 if(xslt.getInputStream()==null || xslt.getInputStream().available()<=0){
92 LOG.error("Stylesheet is not available");
93 }else{
94 createPdfWithFOP(streamMap, pdfByteMap, fopFactory, xslCount, xslt, printableArtifact);
95 }
96 }
97 }
98 else if (printableArtifact.getXSLTemplateWithBookmarks() != null) {
99 Map<String, Source> templatesWithBookmarks = printableArtifact.getXSLTemplateWithBookmarks();
100 for (Map.Entry<String, Source> templatesWithBookmark : templatesWithBookmarks.entrySet()) {
101 StreamSource xslt = (StreamSource) templatesWithBookmark.getValue();
102 createPdfWithFOP(streamMap, pdfByteMap, fopFactory, xslCount, xslt, templatesWithBookmark.getKey(),
103 printableArtifact);
104 }
105
106 }
107
108
109 if (printableArtifact.getAttachments() != null) {
110 pdfByteMap.putAll(printableArtifact.getAttachments());
111 }
112 return pdfByteMap;
113 }
114 catch (FOPException|TransformerException|IOException e) {
115 throw new S2SException(e.getMessage(), e);
116 }
117 }
118
119 protected void createPdfWithFOP(Map<String, byte[]> streamMap, Map<String, byte[]> pdfByteMap, FopFactory fopFactory,
120 int xslCount, StreamSource xslt, S2SPrintable printableArtifact) throws FOPException, TransformerException {
121 createPdfWithFOP(streamMap, pdfByteMap, fopFactory, xslCount, xslt, null, printableArtifact);
122 }
123
124 protected void createPdfWithFOP(Map<String, byte[]> streamMap, Map<String, byte[]> pdfByteMap, FopFactory fopFactory,
125 int xslCount, StreamSource xslt, String bookmark, S2SPrintable printableArtifact) throws FOPException,
126 TransformerException {
127 TransformerFactory factory = TransformerFactory.newInstance();
128 Transformer transformer = factory.newTransformer(xslt);
129 String applicationUrl = s2SConfigurationService.getValueAsString(ConfigurationConstants.APPLICATION_URL_KEY);
130 FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
131 foUserAgent.setBaseURL(applicationUrl);
132 for (Map.Entry<String, byte[]> xmlData : streamMap.entrySet()) {
133 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
134 ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlData.getValue());
135 Source src = new StreamSource(inputStream);
136 Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outputStream);
137 Result res = new SAXResult(fop.getDefaultHandler());
138 transformer.transform(src, res);
139 byte[] pdfBytes = outputStream.toByteArray();
140 if (pdfBytes != null && pdfBytes.length > 0) {
141 String pdfMapKey = bookmark == null ? createBookMark(xslCount, xmlData.getKey()) : bookmark;
142 pdfByteMap.put(pdfMapKey, pdfBytes);
143 }
144 }
145 }
146
147
148 protected String createBookMark(int xslCount, String bookmarkKey) {
149 String pdfMapKey = bookmarkKey + (xslCount == 1 ? "" : " " + xslCount);
150 return pdfMapKey;
151 }
152
153
154
155
156
157
158
159
160 public S2SFile print(S2SPrintable printableArtifacts) {
161 List<S2SPrintable> printables = new ArrayList<S2SPrintable>();
162 printables.add(printableArtifacts);
163 return print(printables);
164 }
165
166
167
168
169
170
171
172
173 public S2SFile print(List<S2SPrintable> printableArtifactList) {
174 return print(printableArtifactList, false);
175 }
176
177 public S2SFile print(List<S2SPrintable> printableArtifactList, boolean headerFooterRequired) {
178 S2SFile printablePdf = null;
179 List<String> bookmarksList = new ArrayList<String>();
180 List<byte[]> pdfBaosList = new ArrayList<byte[]>();
181 for (S2SPrintable printableArtifact : printableArtifactList) {
182 Map<String, byte[]> printBytes = getPrintBytes(printableArtifact);
183 for (String bookmark : printBytes.keySet()) {
184 byte[] pdfBytes = printBytes.get(bookmark);
185 if (isPdfGoodToMerge(pdfBytes)) {
186 bookmarksList.add(bookmark);
187 pdfBaosList.add(pdfBytes);
188 }
189 }
190 }
191
192 printablePdf = new S2SFile();
193 byte[] mergedPdfBytes = mergePdfBytes(pdfBaosList, bookmarksList, headerFooterRequired);
194
195
196
197 if (mergedPdfBytes == null) {
198 mergedPdfBytes = new byte[0];
199 }
200
201 printablePdf.setData(mergedPdfBytes);
202 StringBuilder fileName = new StringBuilder();
203 fileName.append(getReportName());
204 fileName.append(PDF_FILE_EXTENSION);
205 printablePdf.setName(fileName.toString());
206 printablePdf.setType(PDF_REPORT_CONTENT_TYPE);
207 return printablePdf;
208 }
209
210 protected boolean isPdfGoodToMerge(byte[] pdfBytes) {
211 try {
212 new PdfReader(pdfBytes);
213 return true;
214 }
215 catch (IOException e) {
216 return false;
217 }
218 }
219
220 protected String getReportName() {
221 String dateString = new Date().toString();
222 return StringUtils.deleteWhitespace(dateString);
223 }
224
225
226
227
228
229 protected byte[] mergePdfBytes(List<byte[]> pdfBytesList, List<String> bookmarksList, boolean headerFooterRequired) {
230 Document document = null;
231 PdfWriter writer = null;
232 ByteArrayOutputStream mergedPdfReport = new ByteArrayOutputStream();
233 int totalNumOfPages = 0;
234 PdfReader[] pdfReaderArr = new PdfReader[pdfBytesList.size()];
235 int pdfReaderCount = 0;
236 for (byte[] fileBytes : pdfBytesList) {
237 LOG.debug("File Size " + fileBytes.length + " For " + bookmarksList.get(pdfReaderCount));
238 PdfReader reader = null;
239 try {
240 reader = new PdfReader(fileBytes);
241 pdfReaderArr[pdfReaderCount] = reader;
242 pdfReaderCount = pdfReaderCount + 1;
243 totalNumOfPages += reader.getNumberOfPages();
244 }
245 catch (IOException e) {
246 LOG.error(e.getMessage(), e);
247 }
248 }
249 HeaderFooter footer = null;
250 if (headerFooterRequired) {
251 Calendar calendar = Calendar.getInstance();
252 String dateString = formateCalendar(calendar);
253 StringBuilder footerPhStr = new StringBuilder();
254 footerPhStr.append(" of ");
255 footerPhStr.append(totalNumOfPages);
256 footerPhStr.append(getWhitespaceString(WHITESPACE_LENGTH_76));
257 footerPhStr.append(getWhitespaceString(WHITESPACE_LENGTH_76));
258 footerPhStr.append(getWhitespaceString(WHITESPACE_LENGTH_60));
259 footerPhStr.append(dateString);
260 Font font = FontFactory.getFont(FontFactory.TIMES, 8, Font.NORMAL, Color.BLACK);
261 Phrase beforePhrase = new Phrase("Page ", font);
262 Phrase afterPhrase = new Phrase(footerPhStr.toString(), font);
263 footer = new HeaderFooter(beforePhrase, afterPhrase);
264 footer.setAlignment(Element.ALIGN_BASELINE);
265 footer.setBorderWidth(0f);
266 }
267 for (int count = 0; count < pdfReaderArr.length; count++) {
268 PdfReader reader = pdfReaderArr[count];
269 int nop;
270 if (reader == null) {
271 LOG.debug("Empty PDF byetes found for " + bookmarksList.get(count));
272 continue;
273 }
274 else {
275 nop = reader.getNumberOfPages();
276 }
277
278 if (count == 0) {
279 document = nop > 0 ? new Document(reader.getPageSizeWithRotation(1))
280 : new Document();
281 try {
282 writer = PdfWriter.getInstance(document, mergedPdfReport);
283 }
284 catch (DocumentException e) {
285 LOG.error(e.getMessage(), e);
286 throw new S2SException(e.getMessage(), e);
287 }
288 if (footer != null) {
289 document.setFooter(footer);
290 }
291 document.open();
292 }
293
294 PdfContentByte cb = writer.getDirectContent();
295 int pageCount = 0;
296 while (pageCount < nop) {
297 document.setPageSize(reader.getPageSize(++pageCount));
298 document.newPage();
299 if (footer != null) {
300 document.setFooter(footer);
301 }
302 PdfImportedPage page = writer.getImportedPage(reader, pageCount);
303
304 cb.addTemplate(page, 1, 0, 0, 1, 0, 0);
305
306
307 PdfOutline root = cb.getRootOutline();
308 if (pageCount == 1) {
309 String pageName = bookmarksList.get(count);
310 cb.addOutline(new PdfOutline(root, new PdfDestination(PdfDestination.FITH), pageName), pageName);
311 }
312 }
313 }
314 if (document != null) {
315 try {
316 document.close();
317 return mergedPdfReport.toByteArray();
318 }
319 catch (Exception e) {
320 LOG.error("Exception occured because the generated PDF document has no pages", e);
321 }
322 }
323 return null;
324 }
325
326
327 protected String formateCalendar(Calendar calendar) {
328 DateFormat dateFormat = new SimpleDateFormat("M/d/yy h:mm a");
329 return dateFormat.format(calendar.getTime());
330 }
331
332 protected String getWhitespaceString(int length) {
333 StringBuilder sb = new StringBuilder();
334 char[] whiteSpace = new char[length];
335 Arrays.fill(whiteSpace, SPACE_SEPARATOR);
336 sb.append(whiteSpace);
337 return sb.toString();
338 }
339
340 protected void logPrintDetails(Map<String, byte[]> xmlStreamMap) {
341 String loggingDirectory = s2SConfigurationService.getValueAsString(ConfigurationConstants.PRINT_LOGGING_DIRECTORY);
342 if (loggingDirectory != null) {
343
344 for (String key : xmlStreamMap.keySet()) {
345 byte[] xmlBytes = xmlStreamMap.get(key);
346 String xmlString = new String(xmlBytes);
347 String dateString = new Timestamp(new Date().getTime()).toString();
348 String reportName = StringUtils.deleteWhitespace(key);
349 String createdTime = StringUtils.replaceChars(StringUtils.deleteWhitespace(dateString), ":", "_");
350 File dir = new File(loggingDirectory);
351 if(!dir.exists() || !dir.isDirectory()){
352 dir.mkdirs();
353 }
354 File file = new File(dir , reportName + createdTime + ".xml");
355 try(BufferedWriter out = new BufferedWriter(new FileWriter(file))) {
356 out.write(xmlString);
357 } catch (IOException e) {
358 LOG.error(e.getMessage(), e);
359 }
360 }
361 }
362 }
363
364 public S2SConfigurationService getS2SConfigurationService() {
365 return s2SConfigurationService;
366 }
367
368 public void setS2SConfigurationService(S2SConfigurationService s2SConfigurationService) {
369 this.s2SConfigurationService = s2SConfigurationService;
370 }
371 }