1
24 package net.sf.jasperreports.export.pdf.classic;
25
26 import java.awt.Graphics2D;
27 import java.awt.geom.Rectangle2D;
28 import java.awt.image.BufferedImage;
29 import java.io.IOException;
30 import java.io.OutputStream;
31 import java.text.AttributedCharacterIterator.Attribute;
32 import java.util.HashMap;
33 import java.util.Locale;
34 import java.util.Map;
35
36 import com.lowagie.text.BadElementException;
37 import com.lowagie.text.Chunk;
38 import com.lowagie.text.Document;
39 import com.lowagie.text.DocumentException;
40 import com.lowagie.text.Font;
41 import com.lowagie.text.Image;
42 import com.lowagie.text.Phrase;
43 import com.lowagie.text.Rectangle;
44 import com.lowagie.text.SplitCharacter;
45 import com.lowagie.text.pdf.PdfContentByte;
46 import com.lowagie.text.pdf.PdfFormField;
47 import com.lowagie.text.pdf.PdfOutline;
48 import com.lowagie.text.pdf.PdfTemplate;
49 import com.lowagie.text.pdf.PdfWriter;
50 import com.lowagie.text.pdf.RadioCheckField;
51 import com.lowagie.text.pdf.TextField;
52
53 import net.sf.jasperreports.engine.JRException;
54 import net.sf.jasperreports.engine.JRPrintImage;
55 import net.sf.jasperreports.engine.JRPrintText;
56 import net.sf.jasperreports.engine.JRRuntimeException;
57 import net.sf.jasperreports.engine.PrintPageFormat;
58 import net.sf.jasperreports.engine.export.AbstractPdfTextRenderer;
59 import net.sf.jasperreports.engine.export.PdfTextRenderer;
60 import net.sf.jasperreports.engine.export.SimplePdfTextRenderer;
61 import net.sf.jasperreports.engine.export.type.PdfFieldTypeEnum;
62 import net.sf.jasperreports.engine.type.ModeEnum;
63 import net.sf.jasperreports.engine.util.BreakIteratorSplitCharacter;
64 import net.sf.jasperreports.engine.util.JRStyledText;
65 import net.sf.jasperreports.engine.util.NullOutputStream;
66 import net.sf.jasperreports.export.pdf.PdfChunk;
67 import net.sf.jasperreports.export.pdf.PdfContent;
68 import net.sf.jasperreports.export.pdf.PdfDocument;
69 import net.sf.jasperreports.export.pdf.PdfDocumentWriter;
70 import net.sf.jasperreports.export.pdf.PdfImage;
71 import net.sf.jasperreports.export.pdf.PdfOutlineEntry;
72 import net.sf.jasperreports.export.pdf.PdfPhrase;
73 import net.sf.jasperreports.export.pdf.PdfProducer;
74 import net.sf.jasperreports.export.pdf.PdfProducerContext;
75 import net.sf.jasperreports.export.pdf.PdfRadioCheck;
76 import net.sf.jasperreports.export.pdf.PdfStructure;
77 import net.sf.jasperreports.export.pdf.PdfTextChunk;
78 import net.sf.jasperreports.export.pdf.PdfTextField;
79 import net.sf.jasperreports.renderers.Graphics2DRenderable;
80
81
85 public class ClassicPdfProducer implements PdfProducer
86 {
87
88 private PdfProducerContext context;
89
90 private ClassicPdfStructure pdfStructure;
91
92 private ClassicDocument document;
93 private ClassicPdfWriter writer;
94
95 private Document imageTesterDocument;
96 private PdfContentByte imageTesterPdfContentByte;
97
98 private SplitCharacter splitCharacter;
99 private GlyphRendering glyphRendering;
100
101 private ClassicPdfContent pdfContent;
102
103 private Map<String, RadioCheckField> radioFieldFactories;
104 private Map<String, PdfFormField> radioGroups;
105
106 public ClassicPdfProducer(PdfProducerContext context)
107 {
108 this.context = context;
109 this.glyphRendering = new GlyphRendering(this);
110 }
111
112 @Override
113 public PdfProducerContext getContext()
114 {
115 return context;
116 }
117
118 @Override
119 public PdfDocument createDocument(PrintPageFormat pageFormat)
120 {
121 Document pdfDocument =
122 new Document(
123 new Rectangle(
124 pageFormat.getPageWidth(),
125 pageFormat.getPageHeight()
126 )
127 );
128
129 imageTesterDocument =
130 new Document(
131 new Rectangle(
132 10,
133 10
134 )
135 );
136
137 document = new ClassicDocument(pdfDocument);
138 return document;
139 }
140
141 @Override
142 public PdfDocumentWriter createWriter(OutputStream os) throws JRException
143 {
144 try
145 {
146 PdfWriter pdfWriter = PdfWriter.getInstance(document.getDocument(), os);
147 pdfWriter.setCloseStream(false);
148
149 PdfWriter imageTesterPdfWriter =
150 PdfWriter.getInstance(
151 imageTesterDocument,
152 new NullOutputStream()
153 );
154 imageTesterDocument.open();
155 imageTesterDocument.newPage();
156 imageTesterPdfContentByte = imageTesterPdfWriter.getDirectContent();
157 imageTesterPdfContentByte.setLiteral("\n");
158
159 writer = new ClassicPdfWriter(this, pdfWriter);
160 return writer;
161 }
162 catch (DocumentException e)
163 {
164 throw context.handleDocumentException(e);
165 }
166 }
167
168 public PdfWriter getPdfWriter()
169 {
170 return writer.getPdfWriter();
171 }
172
173 @Override
174 public void setTagged()
175 {
176 writer.getPdfWriter().setTagged();
177 }
178
179 @Override
180 public PdfContent createPdfContent()
181 {
182 pdfContent = new ClassicPdfContent(writer.getPdfWriter());
183 return pdfContent;
184 }
185
186 @Override
187 public PdfContent getPdfContent()
188 {
189 return pdfContent;
190 }
191
192 public PdfContentByte getPdfContentByte()
193 {
194 return pdfContent.getPdfContentByte();
195 }
196
197 @Override
198 public void initReport()
199 {
200 glyphRendering.initGlyphRenderer();
201 }
202
203 @Override
204 public void setForceLineBreakPolicy(boolean forceLineBreakPolicy)
205 {
206 splitCharacter = forceLineBreakPolicy ? new BreakIteratorSplitCharacter() : null;
207 }
208
209 @Override
210 public void newPage()
211 {
212 document.getDocument().newPage();
213 pdfContent.refreshContent();
214 }
215
216 @Override
217 public void setPageSize(PrintPageFormat pageFormat, int pageWidth, int pageHeight)
218 {
219 Rectangle pageSize;
220 switch (pageFormat.getOrientation())
221 {
222 case LANDSCAPE:
223
224 pageSize = new Rectangle(pageHeight, pageWidth).rotate();
225 break;
226 default:
227 pageSize = new Rectangle(pageWidth, pageHeight);
228 break;
229 }
230 document.getDocument().setPageSize(pageSize);
231 }
232
233 @Override
234 public void endPage()
235 {
236 if (radioGroups != null)
237 {
238 for (PdfFormField radioGroup : radioGroups.values())
239 {
240 getPdfWriter().addAnnotation(radioGroup);
241 }
242 radioGroups = null;
243 radioFieldFactories = null;
244 }
245 }
246
247 @Override
248 public void close()
249 {
250 document.getDocument().close();
251 imageTesterDocument.close();
252 }
253
254 @Override
255 public AbstractPdfTextRenderer getTextRenderer(
256 JRPrintText text, JRStyledText styledText, Locale textLocale,
257 boolean awtIgnoreMissingFont, boolean defaultIndentFirstLine, boolean defaultJustifyLastLine)
258 {
259 AbstractPdfTextRenderer textRenderer = glyphRendering.getGlyphTextRenderer(text, styledText, textLocale,
260 awtIgnoreMissingFont, defaultIndentFirstLine, defaultJustifyLastLine);
261 if (textRenderer == null)
262 {
263 if (text.getLeadingOffset() == 0)
264 {
265
266 textRenderer =
267 new PdfTextRenderer(
268 context.getJasperReportsContext(),
269 awtIgnoreMissingFont,
270 defaultIndentFirstLine,
271 defaultJustifyLastLine
272 );
273 }
274 else
275 {
276 textRenderer =
277 new SimplePdfTextRenderer(
278 context.getJasperReportsContext(),
279 awtIgnoreMissingFont,
280 defaultIndentFirstLine,
281 defaultJustifyLastLine
282 );
283 }
284 }
285 return textRenderer;
286 }
287
288 @Override
289 public PdfImage createImage(byte[] data, boolean verify) throws IOException, JRException
290 {
291 try
292 {
293 Image image = Image.getInstance(data);
294
295 if (verify)
296 {
297 imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
298 }
299
300 return new ClassicImage(image);
301 }
302 catch (DocumentException e)
303 {
304 throw context.handleDocumentException(e);
305 }
306 }
307
308 @Override
309 public PdfImage createImage(BufferedImage bi, int angle) throws IOException
310 {
311 try
312 {
313 Image image = Image.getInstance(bi, null);
314 image.setRotationDegrees(angle);
315
316 return new ClassicImage(image);
317 }
318 catch (BadElementException e)
319 {
320
321 throw new JRRuntimeException(e);
322 }
323 }
324
325 @Override
326 public void drawImage(JRPrintImage image, Graphics2DRenderable renderer, boolean forceSvgShapes,
327 double templateWidth, double templateHeight,
328 int translateX, int translateY, double angle,
329 double renderWidth, double renderHeight,
330 float ratioX, float ratioY, float x, float y) throws JRException, IOException
331 {
332 PdfContentByte pdfContentByte = getPdfContentByte();
333 PdfTemplate template = pdfContentByte.createTemplate(
334 (float) templateWidth, (float) templateHeight);
335
336 Graphics2D g = forceSvgShapes
337 ? template.createGraphicsShapes((float) templateWidth, (float) templateHeight)
338 : template.createGraphics((float) templateWidth, (float) templateHeight,
339 new ClassicPdfFontMapper(this));
340
341 try
342 {
343 g.translate(translateX, translateY);
344
345 if (angle != 0)
346 {
347 g.rotate(angle);
348 }
349
350 if (image.getModeValue() == ModeEnum.OPAQUE)
351 {
352 g.setColor(image.getBackcolor());
353 g.fillRect(0, 0, (int) renderWidth, (int) renderHeight);
354 }
355
356 renderer.render(context.getJasperReportsContext(), g,
357 new Rectangle2D.Double(0, 0, renderWidth, renderHeight));
358 }
359 finally
360 {
361 g.dispose();
362 }
363
364 pdfContentByte.saveState();
365 pdfContentByte.addTemplate(
366 template,
367 ratioX, 0f, 0f, ratioY, x, y);
368 pdfContentByte.restoreState();
369
370 getPdfWriter().releaseTemplate(template);
371 }
372
373 public Font getFont(Map<Attribute,Object> attributes, Locale locale)
374 {
375 ClassicFontRecipient fontRecipient = new ClassicFontRecipient();
376 context.setFont(attributes, locale, false, fontRecipient);
377 Font font = fontRecipient.getFont();
378 return font;
379 }
380
381 @Override
382 public PdfTextChunk createChunk(String text, Map<Attribute,Object> attributes, Locale locale)
383 {
384 Font font = getFont(attributes, locale);
385 Chunk chunk = new Chunk(text, font);
386
387 if (splitCharacter != null)
388 {
389
390 chunk.setSplitCharacter(splitCharacter);
391 }
392
393 return new ClassicTextChunk(this, chunk, font);
394 }
395
396 @Override
397 public PdfChunk createChunk(PdfImage imageContainer)
398 {
399 Image image = ((ClassicImage) imageContainer).getImage();
400 Chunk chunk = new Chunk(image, 0, 0);
401 return new ClassicChunk(this, chunk);
402 }
403
404 @Override
405 public PdfPhrase createPhrase()
406 {
407 Phrase phrase = new Phrase();
408 return new ClassicPhrase(this, phrase);
409 }
410
411 @Override
412 public PdfPhrase createPhrase(PdfChunk chunk)
413 {
414 Phrase phrase = new Phrase(((ClassicChunk) chunk).getChunk());
415 return new ClassicPhrase(this, phrase);
416 }
417
418 @Override
419 public PdfTextField createTextField(float llx, float lly, float urx, float ury, String fieldName)
420 {
421 TextField textField = createTextFormField(llx, lly, urx, ury, fieldName);
422 return new ClassicPdfTextField(this, textField, PdfFieldTypeEnum.TEXT);
423 }
424
425 protected TextField createTextFormField(float llx, float lly, float urx, float ury, String fieldName)
426 {
427 Rectangle rectangle = new Rectangle(llx, lly, urx, ury);
428 TextField textField = new TextField(writer.getPdfWriter(), rectangle, fieldName);
429 return textField;
430 }
431
432 @Override
433 public PdfTextField createComboField(float llx, float lly, float urx, float ury, String fieldName,
434 String value, String[] choices)
435 {
436 TextField textField = createTextFormField(llx, lly, urx, ury, fieldName);
437 setFieldChoices(textField, value, choices);
438 return new ClassicPdfTextField(this, textField, PdfFieldTypeEnum.COMBO);
439 }
440
441 protected void setFieldChoices(TextField textField, String value, String[] choices)
442 {
443 if (choices != null)
444 {
445 textField.setChoices(choices);
446
447 if (value != null)
448 {
449 int i = 0;
450 for (String choice : choices)
451 {
452 if (value.equals(choice))
453 {
454 textField.setChoiceSelection(i);
455 break;
456 }
457 i++;
458 }
459 }
460 }
461 }
462
463 @Override
464 public PdfTextField createListField(float llx, float lly, float urx, float ury, String fieldName,
465 String value, String[] choices)
466 {
467 TextField textField = createTextFormField(llx, lly, urx, ury, fieldName);
468 setFieldChoices(textField, value, choices);
469 return new ClassicPdfTextField(this, textField, PdfFieldTypeEnum.LIST);
470 }
471
472 @Override
473 public PdfRadioCheck createCheckField(float llx, float lly, float urx, float ury, String fieldName,
474 String onValue)
475 {
476 Rectangle rectangle = new Rectangle(llx, lly, urx, ury);
477 RadioCheckField radioField = new RadioCheckField(writer.getPdfWriter(), rectangle, fieldName, onValue);
478 return new ClassicRadioCheck(this, radioField);
479 }
480
481 @Override
482 public PdfRadioCheck getRadioField(float llx, float lly, float urx, float ury, String fieldName,
483 String onValue)
484 {
485 Rectangle rectangle = new Rectangle(llx, lly, urx, ury);
486
487 RadioCheckField radioField = radioFieldFactories == null ? null : radioFieldFactories.get(fieldName);
488 if (radioField == null)
489 {
490 radioField = new RadioCheckField(writer.getPdfWriter(), rectangle, fieldName, onValue);
491 if (radioFieldFactories == null)
492 {
493 radioFieldFactories = new HashMap<>();
494 }
495 radioFieldFactories.put(fieldName, radioField);
496 }
497
498 radioField.setBox(rectangle);
499
500 return new ClassicRadioCheck(this, radioField);
501 }
502
503 protected PdfFormField getRadioGroup(RadioCheckField radioCheckField)
504 {
505 String fieldName = radioCheckField.getFieldName();
506 PdfFormField radioGroup = radioGroups == null ? null : radioGroups.get(fieldName);
507 if (radioGroup == null)
508 {
509 if (radioGroups == null)
510 {
511 radioGroups = new HashMap<>();
512 }
513
514 radioGroup = radioCheckField.getRadioGroup(true, false);
515 radioGroups.put(fieldName, radioGroup);
516 }
517 return radioGroup;
518 }
519
520 @Override
521 public PdfOutlineEntry getRootOutline()
522 {
523 PdfOutline rootOutline = pdfContent.getPdfContentByte().getRootOutline();
524 return new ClassicPdfOutline(rootOutline);
525 }
526
527 @Override
528 public PdfStructure getPdfStructure()
529 {
530 if (pdfStructure == null)
531 {
532 pdfStructure = new ClassicPdfStructure(this);
533 }
534 return pdfStructure;
535 }
536
537 }
538