1 /*
2 * $Id: PdfDocument.java 3373 2008-05-12 16:21:24Z xlv $
3 *
4 * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * (the "License"); you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the License.
13 *
14 * The Original Code is 'iText, a free JAVA-PDF library'.
15 *
16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18 * All Rights Reserved.
19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21 *
22 * Contributor(s): all the names of the contributors are added in the source code
23 * where applicable.
24 *
25 * Alternatively, the contents of this file may be used under the terms of the
26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27 * provisions of LGPL are applicable instead of those above. If you wish to
28 * allow use of your version of this file only under the terms of the LGPL
29 * License and not to allow others to use your version of this file under
30 * the MPL, indicate your decision by deleting the provisions above and
31 * replace them with the notice and other provisions required by the LGPL.
32 * If you do not delete the provisions above, a recipient may use your version
33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34 *
35 * This library is free software; you can redistribute it and/or modify it
36 * under the terms of the MPL as stated above or under the terms of the GNU
37 * Library General Public License as published by the Free Software Foundation;
38 * either version 2 of the License, or any later version.
39 *
40 * This library is distributed in the hope that it will be useful, but WITHOUT
41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43 * details.
44 *
45 * If you didn't download this code from the following link, you should check if
46 * you aren't using an obsolete version:
47 * http://www.lowagie.com/iText/
48 */
49
50 package com.lowagie.text.pdf;
51
52 import java.awt.Color;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.HashMap;
56 import java.util.HashSet;
57 import java.util.Iterator;
58 import java.util.Map;
59 import java.util.Set;
60 import java.util.TreeMap;
61
62 import com.lowagie.text.Anchor;
63 import com.lowagie.text.Annotation;
64 import com.lowagie.text.BadElementException;
65 import com.lowagie.text.Chunk;
66 import com.lowagie.text.Document;
67 import com.lowagie.text.DocumentException;
68 import com.lowagie.text.Element;
69 import com.lowagie.text.ExceptionConverter;
70 import com.lowagie.text.Font;
71 import com.lowagie.text.HeaderFooter;
72 import com.lowagie.text.Image;
73 import com.lowagie.text.List;
74 import com.lowagie.text.ListItem;
75 import com.lowagie.text.MarkedObject;
76 import com.lowagie.text.MarkedSection;
77 import com.lowagie.text.Meta;
78 import com.lowagie.text.Paragraph;
79 import com.lowagie.text.Phrase;
80 import com.lowagie.text.Rectangle;
81 import com.lowagie.text.Section;
82 import com.lowagie.text.SimpleTable;
83 import com.lowagie.text.Table;
84 import com.lowagie.text.pdf.collection.PdfCollection;
85 import com.lowagie.text.pdf.draw.DrawInterface;
86 import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
87 import com.lowagie.text.pdf.internal.PdfViewerPreferencesImp;
88 import java.text.DecimalFormat;
89
90 /**
91 * <CODE>PdfDocument</CODE> is the class that is used by <CODE>PdfWriter</CODE>
92 * to translate a <CODE>Document</CODE> into a PDF with different pages.
93 * <P>
94 * A <CODE>PdfDocument</CODE> always listens to a <CODE>Document</CODE>
95 * and adds the Pdf representation of every <CODE>Element</CODE> that is
96 * added to the <CODE>Document</CODE>.
97 *
98 * @see com.lowagie.text.Document
99 * @see com.lowagie.text.DocListener
100 * @see PdfWriter
101 * @since 2.0.8 (class was package-private before)
102 */
103
104 public class PdfDocument extends Document {
105
106 /**
107 * <CODE>PdfInfo</CODE> is the PDF InfoDictionary.
108 * <P>
109 * A document's trailer may contain a reference to an Info dictionary that provides information
110 * about the document. This optional dictionary may contain one or more keys, whose values
111 * should be strings.<BR>
112 * This object is described in the 'Portable Document Format Reference Manual version 1.3'
113 * section 6.10 (page 120-121)
114 * @since 2.0.8 (PdfDocument was package-private before)
115 */
116
117 public static class PdfInfo extends PdfDictionary {
118
119 /**
120 * Construct a <CODE>PdfInfo</CODE>-object.
121 */
122
123 PdfInfo() {
124 super();
125 addProducer();
126 addCreationDate();
127 }
128
129 /**
130 * Constructs a <CODE>PdfInfo</CODE>-object.
131 *
132 * @param author name of the author of the document
133 * @param title title of the document
134 * @param subject subject of the document
135 */
136
137 PdfInfo(String author, String title, String subject) {
138 this();
139 addTitle(title);
140 addSubject(subject);
141 addAuthor(author);
142 }
143
144 /**
145 * Adds the title of the document.
146 *
147 * @param title the title of the document
148 */
149
150 void addTitle(String title) {
151 put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE));
152 }
153
154 /**
155 * Adds the subject to the document.
156 *
157 * @param subject the subject of the document
158 */
159
160 void addSubject(String subject) {
161 put(PdfName.SUBJECT, new PdfString(subject, PdfObject.TEXT_UNICODE));
162 }
163
164 /**
165 * Adds some keywords to the document.
166 *
167 * @param keywords the keywords of the document
168 */
169
170 void addKeywords(String keywords) {
171 put(PdfName.KEYWORDS, new PdfString(keywords, PdfObject.TEXT_UNICODE));
172 }
173
174 /**
175 * Adds the name of the author to the document.
176 *
177 * @param author the name of the author
178 */
179
180 void addAuthor(String author) {
181 put(PdfName.AUTHOR, new PdfString(author, PdfObject.TEXT_UNICODE));
182 }
183
184 /**
185 * Adds the name of the creator to the document.
186 *
187 * @param creator the name of the creator
188 */
189
190 void addCreator(String creator) {
191 put(PdfName.CREATOR, new PdfString(creator, PdfObject.TEXT_UNICODE));
192 }
193
194 /**
195 * Adds the name of the producer to the document.
196 */
197
198 void addProducer() {
199 put(PdfName.PRODUCER, new PdfString(getVersion()));
200 }
201
202 /**
203 * Adds the date of creation to the document.
204 */
205
206 void addCreationDate() {
207 PdfString date = new PdfDate();
208 put(PdfName.CREATIONDATE, date);
209 put(PdfName.MODDATE, date);
210 }
211
212 void addkey(String key, String value) {
213 if (key.equals("Producer") || key.equals("CreationDate"))
214 return;
215 put(new PdfName(key), new PdfString(value, PdfObject.TEXT_UNICODE));
216 }
217 }
218
219 /**
220 * <CODE>PdfCatalog</CODE> is the PDF Catalog-object.
221 * <P>
222 * The Catalog is a dictionary that is the root node of the document. It contains a reference
223 * to the tree of pages contained in the document, a reference to the tree of objects representing
224 * the document's outline, a reference to the document's article threads, and the list of named
225 * destinations. In addition, the Catalog indicates whether the document's outline or thumbnail
226 * page images should be displayed automatically when the document is viewed and whether some location
227 * other than the first page should be shown when the document is opened.<BR>
228 * In this class however, only the reference to the tree of pages is implemented.<BR>
229 * This object is described in the 'Portable Document Format Reference Manual version 1.3'
230 * section 6.2 (page 67-71)
231 */
232
233 static class PdfCatalog extends PdfDictionary {
234
235 /** The writer writing the PDF for which we are creating this catalog object. */
236 PdfWriter writer;
237
238 /**
239 * Constructs a <CODE>PdfCatalog</CODE>.
240 *
241 * @param pages an indirect reference to the root of the document's Pages tree.
242 * @param writer the writer the catalog applies to
243 */
244
245 PdfCatalog(PdfIndirectReference pages, PdfWriter writer) {
246 super(CATALOG);
247 this.writer = writer;
248 put(PdfName.PAGES, pages);
249 }
250
251 /**
252 * Adds the names of the named destinations to the catalog.
253 * @param localDestinations the local destinations
254 * @param documentLevelJS the javascript used in the document
255 * @param documentFileAttachment the attached files
256 * @param writer the writer the catalog applies to
257 */
258 void addNames(TreeMap localDestinations, HashMap documentLevelJS, HashMap documentFileAttachment, PdfWriter writer) {
259 if (localDestinations.isEmpty() && documentLevelJS.isEmpty() && documentFileAttachment.isEmpty())
260 return;
261 try {
262 PdfDictionary names = new PdfDictionary();
263 if (!localDestinations.isEmpty()) {
264 PdfArray ar = new PdfArray();
265 for (Iterator i = localDestinations.entrySet().iterator(); i.hasNext();) {
266 Map.Entry entry = (Map.Entry) i.next();
267 String name = (String) entry.getKey();
268 Object obj[] = (Object[]) entry.getValue();
269 PdfIndirectReference ref = (PdfIndirectReference)obj[1];
270 ar.add(new PdfString(name, null));
271 ar.add(ref);
272 }
273 PdfDictionary dests = new PdfDictionary();
274 dests.put(PdfName.NAMES, ar);
275 names.put(PdfName.DESTS, writer.addToBody(dests).getIndirectReference());
276 }
277 if (!documentLevelJS.isEmpty()) {
278 PdfDictionary tree = PdfNameTree.writeTree(documentLevelJS, writer);
279 names.put(PdfName.JAVASCRIPT, writer.addToBody(tree).getIndirectReference());
280 }
281 if (!documentFileAttachment.isEmpty()) {
282 names.put(PdfName.EMBEDDEDFILES, writer.addToBody(PdfNameTree.writeTree(documentFileAttachment, writer)).getIndirectReference());
283 }
284 put(PdfName.NAMES, writer.addToBody(names).getIndirectReference());
285 }
286 catch (IOException e) {
287 throw new ExceptionConverter(e);
288 }
289 }
290
291 /**
292 * Adds an open action to the catalog.
293 * @param action the action that will be triggered upon opening the document
294 */
295 void setOpenAction(PdfAction action) {
296 put(PdfName.OPENACTION, action);
297 }
298
299
300 /**
301 * Sets the document level additional actions.
302 * @param actions dictionary of actions
303 */
304 void setAdditionalActions(PdfDictionary actions) {
305 try {
306 put(PdfName.AA, writer.addToBody(actions).getIndirectReference());
307 } catch (Exception e) {
308 throw new ExceptionConverter(e);
309 }
310 }
311 }
312
313 // CONSTRUCTING A PdfDocument/PdfWriter INSTANCE
314
315 /**
316 * Constructs a new PDF document.
317 * @throws DocumentException on error
318 */
319 public PdfDocument() {
320 super();
321 addProducer();
322 addCreationDate();
323 }
324
325 /** The <CODE>PdfWriter</CODE>. */
326 protected PdfWriter writer;
327
328 /**
329 * Adds a <CODE>PdfWriter</CODE> to the <CODE>PdfDocument</CODE>.
330 *
331 * @param writer the <CODE>PdfWriter</CODE> that writes everything
332 * what is added to this document to an outputstream.
333 * @throws DocumentException on error
334 */
335 public void addWriter(PdfWriter writer) throws DocumentException {
336 if (this.writer == null) {
337 this.writer = writer;
338 annotationsImp = new PdfAnnotationsImp(writer);
339 return;
340 }
341 throw new DocumentException("You can only add a writer to a PdfDocument once.");
342 }
343
344 // LISTENER METHODS START
345
346 // [L0] ElementListener interface
347
348 /** This is the PdfContentByte object, containing the text. */
349 protected PdfContentByte text;
350
351 /** This is the PdfContentByte object, containing the borders and other Graphics. */
352 protected PdfContentByte graphics;
353
354 /** This represents the leading of the lines. */
355 protected float leading = 0;
356
357 /**
358 * Getter for the current leading.
359 * @return the current leading
360 * @since 2.1.2
361 */
362 public float getLeading() {
363 return leading;
364 }
365
366 /** This represents the current alignment of the PDF Elements. */
367 protected int alignment = Element.ALIGN_LEFT;
368
369 /** This is the current height of the document. */
370 protected float currentHeight = 0;
371
372 /**
373 * Signals that onParagraph is valid (to avoid that a Chapter/Section title is treated as a Paragraph).
374 * @since 2.1.2
375 */
376 protected boolean isSectionTitle = false;
377
378 /**
379 * Signals that the current leading has to be subtracted from a YMark object when positive.
380 * @since 2.1.2
381 */
382 protected int leadingCount = 0;
383
384 /** The current active <CODE>PdfAction</CODE> when processing an <CODE>Anchor</CODE>. */
385 protected PdfAction anchorAction = null;
386
387 /**
388 * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>.
389 *
390 * @param element the element to add
391 * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not.
392 * @throws DocumentException when a document isn't open yet, or has been closed
393 */
394 public boolean add(Element element) throws DocumentException {
395 if (writer != null && writer.isPaused()) {
396 return false;
397 }
398 try {
399 switch(element.type()) {
400 // Information (headers)
401 case Element.HEADER:
402 info.addkey(((Meta)element).getName(), ((Meta)element).getContent());
403 break;
404 case Element.TITLE:
405 info.addTitle(((Meta)element).getContent());
406 break;
407 case Element.SUBJECT:
408 info.addSubject(((Meta)element).getContent());
409 break;
410 case Element.KEYWORDS:
411 info.addKeywords(((Meta)element).getContent());
412 break;
413 case Element.AUTHOR:
414 info.addAuthor(((Meta)element).getContent());
415 break;
416 case Element.CREATOR:
417 info.addCreator(((Meta)element).getContent());
418 break;
419 case Element.PRODUCER:
420 // you can not change the name of the producer
421 info.addProducer();
422 break;
423 case Element.CREATIONDATE:
424 // you can not set the creation date, only reset it
425 info.addCreationDate();
426 break;
427
428 // content (text)
429 case Element.CHUNK: {
430 // if there isn't a current line available, we make one
431 if (line == null) {
432 carriageReturn();
433 }
434
435 // we cast the element to a chunk
436 PdfChunk chunk = new PdfChunk((Chunk) element, anchorAction);
437 // we try to add the chunk to the line, until we succeed
438 {
439 PdfChunk overflow;
440 while ((overflow = line.add(chunk)) != null) {
441 carriageReturn();
442 chunk = overflow;
443 chunk.trimFirstSpace();
444 }
445 }
446 pageEmpty = false;
447 if (chunk.isAttribute(Chunk.NEWPAGE)) {
448 newPage();
449 }
450 break;
451 }
452 case Element.ANCHOR: {
453 leadingCount++;
454 Anchor anchor = (Anchor) element;
455 String url = anchor.getReference();
456 leading = anchor.getLeading();
457 if (url != null) {
458 anchorAction = new PdfAction(url);
459 }
460 // we process the element
461 element.process(this);
462 anchorAction = null;
463 leadingCount--;
464 break;
465 }
466 case Element.ANNOTATION: {
467 if (line == null) {
468 carriageReturn();
469 }
470 Annotation annot = (Annotation) element;
471 Rectangle rect = new Rectangle(0, 0);
472 if (line != null)
473 rect = new Rectangle(annot.llx(indentRight() - line.widthLeft()), annot.lly(indentTop() - currentHeight), annot.urx(indentRight() - line.widthLeft() + 20), annot.ury(indentTop() - currentHeight - 20));
474 PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(writer, annot, rect);
475 annotationsImp.addPlainAnnotation(an);
476 pageEmpty = false;
477 break;
478 }
479 case Element.PHRASE: {
480 leadingCount++;
481 // we cast the element to a phrase and set the leading of the document
482 leading = ((Phrase) element).getLeading();
483 // we process the element
484 element.process(this);
485 leadingCount--;
486 break;
487 }
488 case Element.PARAGRAPH: {
489 leadingCount++;
490 // we cast the element to a paragraph
491 Paragraph paragraph = (Paragraph) element;
492 addSpacing(paragraph.spacingBefore(), leading, paragraph.getFont());
493
494 // we adjust the parameters of the document
495 alignment = paragraph.getAlignment();
496 leading = paragraph.getTotalLeading();
497 carriageReturn();
498
499 // we don't want to make orphans/widows
500 if (currentHeight + line.height() + leading > indentTop() - indentBottom()) {
501 newPage();
502 }
503 indentation.indentLeft += paragraph.getIndentationLeft();
504 indentation.indentRight += paragraph.getIndentationRight();
505 carriageReturn();
506
507 PdfPageEvent pageEvent = writer.getPageEvent();
508 if (pageEvent != null && !isSectionTitle)
509 pageEvent.onParagraph(writer, this, indentTop() - currentHeight);
510
511 // if a paragraph has to be kept together, we wrap it in a table object
512 if (paragraph.getKeepTogether()) {
513 carriageReturn();
514 PdfPTable table = new PdfPTable(1);
515 table.setWidthPercentage(100f);
516 PdfPCell cell = new PdfPCell();
517 cell.addElement(paragraph);
518 cell.setBorder(Table.NO_BORDER);
519 cell.setPadding(0);
520 table.addCell(cell);
521 indentation.indentLeft -= paragraph.getIndentationLeft();
522 indentation.indentRight -= paragraph.getIndentationRight();
523 this.add(table);
524 indentation.indentLeft += paragraph.getIndentationLeft();
525 indentation.indentRight += paragraph.getIndentationRight();
526 }
527 else {
528 line.setExtraIndent(paragraph.getFirstLineIndent());
529 element.process(this);
530 carriageReturn();
531 addSpacing(paragraph.spacingAfter(), paragraph.getTotalLeading(), paragraph.getFont());
532 }
533
534 if (pageEvent != null && !isSectionTitle)
535 pageEvent.onParagraphEnd(writer, this, indentTop() - currentHeight);
536
537 alignment = Element.ALIGN_LEFT;
538 indentation.indentLeft -= paragraph.getIndentationLeft();
539 indentation.indentRight -= paragraph.getIndentationRight();
540 carriageReturn();
541 leadingCount--;
542 break;
543 }
544 case Element.SECTION:
545 case Element.CHAPTER: {
546 // Chapters and Sections only differ in their constructor
547 // so we cast both to a Section
548 Section section = (Section) element;
549 PdfPageEvent pageEvent = writer.getPageEvent();
550
551 boolean hasTitle = section.isNotAddedYet()
552 && section.getTitle() != null;
553
554 // if the section is a chapter, we begin a new page
555 if (section.isTriggerNewPage()) {
556 newPage();
557 }
558
559 if (hasTitle) {
560 float fith = indentTop() - currentHeight;
561 int rotation = pageSize.getRotation();
562 if (rotation == 90 || rotation == 180)
563 fith = pageSize.getHeight() - fith;
564 PdfDestination destination = new PdfDestination(PdfDestination.FITH, fith);
565 while (currentOutline.level() >= section.getDepth()) {
566 currentOutline = currentOutline.parent();
567 }
568 PdfOutline outline = new PdfOutline(currentOutline, destination, section.getBookmarkTitle(), section.isBookmarkOpen());
569 currentOutline = outline;
570 }
571
572 // some values are set
573 carriageReturn();
574 indentation.sectionIndentLeft += section.getIndentationLeft();
575 indentation.sectionIndentRight += section.getIndentationRight();
576
577 if (section.isNotAddedYet() && pageEvent != null)
578 if (element.type() == Element.CHAPTER)
579 pageEvent.onChapter(writer, this, indentTop() - currentHeight, section.getTitle());
580 else
581 pageEvent.onSection(writer, this, indentTop() - currentHeight, section.getDepth(), section.getTitle());
582
583 // the title of the section (if any has to be printed)
584 if (hasTitle) {
585 isSectionTitle = true;
586 add(section.getTitle());
587 isSectionTitle = false;
588 }
589 indentation.sectionIndentLeft += section.getIndentation();
590 // we process the section
591 element.process(this);
592 flushLines();
593 // some parameters are set back to normal again
594 indentation.sectionIndentLeft -= (section.getIndentationLeft() + section.getIndentation());
595 indentation.sectionIndentRight -= section.getIndentationRight();
596
597 if (section.isComplete() && pageEvent != null)
598 if (element.type() == Element.CHAPTER)
599 pageEvent.onChapterEnd(writer, this, indentTop() - currentHeight);
600 else
601 pageEvent.onSectionEnd(writer, this, indentTop() - currentHeight);
602
603 break;
604 }
605 case Element.LIST: {
606 // we cast the element to a List
607 List list = (List) element;
608 if (list.isAlignindent()) {
609 list.normalizeIndentation();
610 }
611 // we adjust the document
612 indentation.listIndentLeft += list.getIndentationLeft();
613 indentation.indentRight += list.getIndentationRight();
614 // we process the items in the list
615 element.process(this);
616 // some parameters are set back to normal again
617 indentation.listIndentLeft -= list.getIndentationLeft();
618 indentation.indentRight -= list.getIndentationRight();
619 carriageReturn();
620 break;
621 }
622 case Element.LISTITEM: {
623 leadingCount++;
624 // we cast the element to a ListItem
625 ListItem listItem = (ListItem) element;
626
627 addSpacing(listItem.spacingBefore(), leading, listItem.getFont());
628
629 // we adjust the document
630 alignment = listItem.getAlignment();
631 indentation.listIndentLeft += listItem.getIndentationLeft();
632 indentation.indentRight += listItem.getIndentationRight();
633 leading = listItem.getTotalLeading();
634 carriageReturn();
635
636 // we prepare the current line to be able to show us the listsymbol
637 line.setListItem(listItem);
638 // we process the item
639 element.process(this);
640
641 addSpacing(listItem.spacingAfter(), listItem.getTotalLeading(), listItem.getFont());
642
643 // if the last line is justified, it should be aligned to the left
644 if (line.hasToBeJustified()) {
645 line.resetAlignment();
646 }
647 // some parameters are set back to normal again
648 carriageReturn();
649 indentation.listIndentLeft -= listItem.getIndentationLeft();
650 indentation.indentRight -= listItem.getIndentationRight();
651 leadingCount--;
652 break;
653 }
654 case Element.RECTANGLE: {
655 Rectangle rectangle = (Rectangle) element;
656 graphics.rectangle(rectangle);
657 pageEmpty = false;
658 break;
659 }
660 case Element.PTABLE: {
661 PdfPTable ptable = (PdfPTable)element;
662 if (ptable.size() <= ptable.getHeaderRows())
663 break; //nothing to do
664
665 // before every table, we add a new line and flush all lines
666 ensureNewLine();
667 flushLines();
668
669 addPTable(ptable);
670 pageEmpty = false;
671 newLine();
672 break;
673 }
674 case Element.MULTI_COLUMN_TEXT: {
675 ensureNewLine();
676 flushLines();
677 MultiColumnText multiText = (MultiColumnText) element;
678 float height = multiText.write(writer.getDirectContent(), this, indentTop() - currentHeight);
679 currentHeight += height;
680 text.moveText(0, -1f* height);
681 pageEmpty = false;
682 break;
683 }
684 case Element.TABLE : {
685 if (element instanceof SimpleTable) {
686 PdfPTable ptable = ((SimpleTable)element).createPdfPTable();
687 if (ptable.size() <= ptable.getHeaderRows())
688 break; //nothing to do
689
690 // before every table, we add a new line and flush all lines
691 ensureNewLine();
692 flushLines();
693 addPTable(ptable);
694 pageEmpty = false;
695 break;
696 } else if (element instanceof Table) {
697 try {
698 PdfPTable ptable = ((Table)element).createPdfPTable();
699 if (ptable.size() <= ptable.getHeaderRows())
700 break; //nothing to do
701 // before every table, we add a new line and flush all lines
702 ensureNewLine();
703 flushLines();
704 addPTable(ptable);
705 pageEmpty = false;
706 break;
707 }
708 catch(BadElementException bee) {
709 // constructing the PdfTable
710 // Before the table, add a blank line using offset or default leading
711 float offset = ((Table)element).getOffset();
712 if (Float.isNaN(offset))
713 offset = leading;
714 carriageReturn();
715 lines.add(new PdfLine(indentLeft(), indentRight(), alignment, offset));
716 currentHeight += offset;
717 addPdfTable((Table)element);
718 }
719 } else {
720 return false;
721 }
722 break;
723 }
724 case Element.JPEG:
725 case Element.JPEG2000:
726 case Element.IMGRAW:
727 case Element.IMGTEMPLATE: {
728 //carriageReturn(); suggestion by Marc Campforts
729 add((Image) element);
730 break;
731 }
732 case Element.YMARK: {
733 DrawInterface zh = (DrawInterface)element;
734 zh.draw(graphics, indentLeft(), indentBottom(), indentRight(), indentTop(), indentTop() - currentHeight - (leadingCount > 0 ? leading : 0));
735 pageEmpty = false;
736 break;
737 }
738 case Element.MARKED: {
739 MarkedObject mo;
740 if (element instanceof MarkedSection) {
741 mo = ((MarkedSection)element).getTitle();
742 if (mo != null) {
743 mo.process(this);
744 }
745 }
746 mo = (MarkedObject)element;
747 mo.process(this);
748 break;
749 }
750 default:
751 return false;
752 }
753 lastElementType = element.type();
754 return true;
755 }
756 catch(Exception e) {
757 throw new DocumentException(e);
758 }
759 }
760
761 // [L1] DocListener interface
762
763 /**
764 * Opens the document.
765 * <P>
766 * You have to open the document before you can begin to add content
767 * to the body of the document.
768 */
769 public void open() {
770 if (!open) {
771 super.open();
772 writer.open();
773 rootOutline = new PdfOutline(writer);
774 currentOutline = rootOutline;
775 }
776 try {
777 initPage();
778 }
779 catch(DocumentException de) {
780 throw new ExceptionConverter(de);
781 }
782 }
783
784 // [L2] DocListener interface
785
786 /**
787 * Closes the document.
788 * <B>
789 * Once all the content has been written in the body, you have to close
790 * the body. After that nothing can be written to the body anymore.
791 */
792 public void close() {
793 if (close) {
794 return;
795 }
796 try {
797 boolean wasImage = (imageWait != null);
798 newPage();
799 if (imageWait != null || wasImage) newPage();
800 if (annotationsImp.hasUnusedAnnotations())
801 throw new RuntimeException("Not all annotations could be added to the document (the document doesn't have enough pages).");
802 PdfPageEvent pageEvent = writer.getPageEvent();
803 if (pageEvent != null)
804 pageEvent.onCloseDocument(writer, this);
805 super.close();
806
807 writer.addLocalDestinations(localDestinations);
808 calculateOutlineCount();
809 writeOutlines();
810 }
811 catch(Exception e) {
812 throw new ExceptionConverter(e);
813 }
814
815 writer.close();
816 }
817
818 // [L3] DocListener interface
819 protected int textEmptySize;
820
821 // [C9] Metadata for the page
822 /** XMP Metadata for the page. */
823 protected byte[] xmpMetadata = null;
824 /**
825 * Use this method to set the XMP Metadata.
826 * @param xmpMetadata The xmpMetadata to set.
827 */
828 public void setXmpMetadata(byte[] xmpMetadata) {
829 this.xmpMetadata = xmpMetadata;
830 }
831
832 /**
833 * Makes a new page and sends it to the <CODE>PdfWriter</CODE>.
834 *
835 * @return a <CODE>boolean</CODE>
836 * @throws DocumentException on error
837 */
838 public boolean newPage() {
839 lastElementType = -1;
840 if (writer == null || (writer.getDirectContent().size() == 0 && writer.getDirectContentUnder().size() == 0 && (pageEmpty || writer.isPaused()))) {
841 setNewPageSizeAndMargins();
842 return false;
843 }
844 if (!open || close) {
845 throw new RuntimeException("The document isn't open.");
846 }
847 PdfPageEvent pageEvent = writer.getPageEvent();
848 if (pageEvent != null)
849 pageEvent.onEndPage(writer, this);
850
851 //Added to inform any listeners that we are moving to a new page (added by David Freels)
852 super.newPage();
853
854 // the following 2 lines were added by Pelikan Stephan
855 indentation.imageIndentLeft = 0;
856 indentation.imageIndentRight = 0;
857
858 try {
859 // we flush the arraylist with recently written lines
860 flushLines();
861
862 // we prepare the elements of the page dictionary
863
864 // [U1] page size and rotation
865 int rotation = pageSize.getRotation();
866
867 // [C10]
868 if (writer.isPdfX()) {
869 if (thisBoxSize.containsKey("art") && thisBoxSize.containsKey("trim"))
870 throw new PdfXConformanceException("Only one of ArtBox or TrimBox can exist in the page.");
871 if (!thisBoxSize.containsKey("art") && !thisBoxSize.containsKey("trim")) {
872 if (thisBoxSize.containsKey("crop"))
873 thisBoxSize.put("trim", thisBoxSize.get("crop"));
874 else
875 thisBoxSize.put("trim", new PdfRectangle(pageSize, pageSize.getRotation()));
876 }
877 }
878
879 // [M1]
880 pageResources.addDefaultColorDiff(writer.getDefaultColorspace());
881 if (writer.isRgbTransparencyBlending()) {
882 PdfDictionary dcs = new PdfDictionary();
883 dcs.put(PdfName.CS, PdfName.DEVICERGB);
884 pageResources.addDefaultColorDiff(dcs);
885 }
886 PdfDictionary resources = pageResources.getResources();
887
888 // we create the page dictionary
889
890 PdfPage page = new PdfPage(new PdfRectangle(pageSize, rotation), thisBoxSize, resources, rotation);
891
892 // we complete the page dictionary
893
894 // [C9] if there is XMP data to add: add it
895 if (xmpMetadata != null) {
896 PdfStream xmp = new PdfStream(xmpMetadata);
897 xmp.put(PdfName.TYPE, PdfName.METADATA);
898 xmp.put(PdfName.SUBTYPE, PdfName.XML);
899 PdfEncryption crypto = writer.getEncryption();
900 if (crypto != null && !crypto.isMetadataEncrypted()) {
901 PdfArray ar = new PdfArray();
902 ar.add(PdfName.CRYPT);
903 xmp.put(PdfName.FILTER, ar);
904 }
905 page.put(PdfName.METADATA, writer.addToBody(xmp).getIndirectReference());
906 }
907
908 // [U3] page actions: transition, duration, additional actions
909 if (this.transition!=null) {
910 page.put(PdfName.TRANS, this.transition.getTransitionDictionary());
911 transition = null;
912 }
913 if (this.duration>0) {
914 page.put(PdfName.DUR,new PdfNumber(this.duration));
915 duration = 0;
916 }
917 if (pageAA != null) {
918 page.put(PdfName.AA, writer.addToBody(pageAA).getIndirectReference());
919 pageAA = null;
920 }
921
922 // [U4] we add the thumbs
923 if (thumb != null) {
924 page.put(PdfName.THUMB, thumb);
925 thumb = null;
926 }
927
928 // [U8] we check if the userunit is defined
929 if (writer.getUserunit() > 0f) {
930 page.put(PdfName.USERUNIT, new PdfNumber(writer.getUserunit()));
931 }
932
933 // [C5] and [C8] we add the annotations
934 if (annotationsImp.hasUnusedAnnotations()) {
935 PdfArray array = annotationsImp.rotateAnnotations(writer, pageSize);
936 if (array.size() != 0)
937 page.put(PdfName.ANNOTS, array);
938 }
939
940 // [F12] we add tag info
941 if (writer.isTagged())
942 page.put(PdfName.STRUCTPARENTS, new PdfNumber(writer.getCurrentPageNumber() - 1));
943
944 if (text.size() > textEmptySize)
945 text.endText();
946 else
947 text = null;
948 writer.add(page, new PdfContents(writer.getDirectContentUnder(), graphics, text, writer.getDirectContent(), pageSize));
949 // we initialize the new page
950 initPage();
951 }
952 catch(DocumentException de) {
953 // maybe this never happens, but it's better to check.
954 throw new ExceptionConverter(de);
955 }
956 catch (IOException ioe) {
957 throw new ExceptionConverter(ioe);
958 }
959 return true;
960 }
961
962 // [L4] DocListener interface
963
964 /**
965 * Sets the pagesize.
966 *
967 * @param pageSize the new pagesize
968 * @return <CODE>true</CODE> if the page size was set
969 */
970 public boolean setPageSize(Rectangle pageSize) {
971 if (writer != null && writer.isPaused()) {
972 return false;
973 }
974 nextPageSize = new Rectangle(pageSize);
975 return true;
976 }
977
978 // [L5] DocListener interface
979
980 /** margin in x direction starting from the left. Will be valid in the next page */
981 protected float nextMarginLeft;
982
983 /** margin in x direction starting from the right. Will be valid in the next page */
984 protected float nextMarginRight;
985
986 /** margin in y direction starting from the top. Will be valid in the next page */
987 protected float nextMarginTop;
988
989 /** margin in y direction starting from the bottom. Will be valid in the next page */
990 protected float nextMarginBottom;
991
992 /**
993 * Sets the margins.
994 *
995 * @param marginLeft the margin on the left
996 * @param marginRight the margin on the right
997 * @param marginTop the margin on the top
998 * @param marginBottom the margin on the bottom
999 * @return a <CODE>boolean</CODE>
1000 */
1001 public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
1002 if (writer != null && writer.isPaused()) {
1003 return false;
1004 }
1005 nextMarginLeft = marginLeft;
1006 nextMarginRight = marginRight;
1007 nextMarginTop = marginTop;
1008 nextMarginBottom = marginBottom;
1009 return true;
1010 }
1011
1012 // [L6] DocListener interface
1013
1014 /**
1015 * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
1016 */
1017 public boolean setMarginMirroring(boolean MarginMirroring) {
1018 if (writer != null && writer.isPaused()) {
1019 return false;
1020 }
1021 return super.setMarginMirroring(MarginMirroring);
1022 }
1023
1024 // [L7] DocListener interface
1025
1026 /**
1027 * Sets the page number.
1028 *
1029 * @param pageN the new page number
1030 */
1031 public void setPageCount(int pageN) {
1032 if (writer != null && writer.isPaused()) {
1033 return;
1034 }
1035 super.setPageCount(pageN);
1036 }
1037
1038 // [L8] DocListener interface
1039
1040 /**
1041 * Sets the page number to 0.
1042 */
1043 public void resetPageCount() {
1044 if (writer != null && writer.isPaused()) {
1045 return;
1046 }
1047 super.resetPageCount();
1048 }
1049
1050 // [L9] DocListener interface
1051
1052 /**
1053 * Changes the header of this document.
1054 *
1055 * @param header the new header
1056 */
1057 public void setHeader(HeaderFooter header) {
1058 if (writer != null && writer.isPaused()) {
1059 return;
1060 }
1061 super.setHeader(header);
1062 }
1063
1064 // [L10] DocListener interface
1065
1066 /**
1067 * Resets the header of this document.
1068 */
1069 public void resetHeader() {
1070 if (writer != null && writer.isPaused()) {
1071 return;
1072 }
1073 super.resetHeader();
1074 }
1075
1076 // [L11] DocListener interface
1077
1078 /**
1079 * Changes the footer of this document.
1080 *
1081 * @param footer the new footer
1082 */
1083 public void setFooter(HeaderFooter footer) {
1084 if (writer != null && writer.isPaused()) {
1085 return;
1086 }
1087 super.setFooter(footer);
1088 }
1089
1090 // [L12] DocListener interface
1091
1092 /**
1093 * Resets the footer of this document.
1094 */
1095 public void resetFooter() {
1096 if (writer != null && writer.isPaused()) {
1097 return;
1098 }
1099 super.resetFooter();
1100 }
1101
1102 // DOCLISTENER METHODS END
1103
1104 /** Signals that OnOpenDocument should be called. */
1105 protected boolean firstPageEvent = true;
1106
1107 /**
1108 * Initializes a page.
1109 * <P>
1110 * If the footer/header is set, it is printed.
1111 * @throws DocumentException on error
1112 */
1113 protected void initPage() throws DocumentException {
1114 // the pagenumber is incremented
1115 pageN++;
1116
1117 // initialization of some page objects
1118 annotationsImp.resetAnnotations();
1119 pageResources = new PageResources();
1120
1121 writer.resetContent();
1122 graphics = new PdfContentByte(writer);
1123 text = new PdfContentByte(writer);
1124 text.reset();
1125 text.beginText();
1126 textEmptySize = text.size();
1127
1128 markPoint = 0;
1129 setNewPageSizeAndMargins();
1130 imageEnd = -1;
1131 indentation.imageIndentRight = 0;
1132 indentation.imageIndentLeft = 0;
1133 indentation.indentBottom = 0;
1134 indentation.indentTop = 0;
1135 currentHeight = 0;
1136
1137 // backgroundcolors, etc...
1138 thisBoxSize = new HashMap(boxSize);
1139 if (pageSize.getBackgroundColor() != null
1140 || pageSize.hasBorders()
1141 || pageSize.getBorderColor() != null) {
1142 add(pageSize);
1143 }
1144
1145 float oldleading = leading;
1146 int oldAlignment = alignment;
1147 // if there is a footer, the footer is added
1148 doFooter();
1149 // we move to the left/top position of the page
1150 text.moveText(left(), top());
1151 doHeader();
1152 pageEmpty = true;
1153 // if there is an image waiting to be drawn, draw it
1154 try {
1155 if (imageWait != null) {
1156 add(imageWait);
1157 imageWait = null;
1158 }
1159 }
1160 catch(Exception e) {
1161 throw new ExceptionConverter(e);
1162 }
1163 leading = oldleading;
1164 alignment = oldAlignment;
1165 carriageReturn();
1166
1167 PdfPageEvent pageEvent = writer.getPageEvent();
1168 if (pageEvent != null) {
1169 if (firstPageEvent) {
1170 pageEvent.onOpenDocument(writer, this);
1171 }
1172 pageEvent.onStartPage(writer, this);
1173 }
1174 firstPageEvent = false;
1175 }
1176
1177 /** The line that is currently being written. */
1178 protected PdfLine line = null;
1179 /** The lines that are written until now. */
1180 protected ArrayList lines = new ArrayList();
1181
1182 /**
1183 * Adds the current line to the list of lines and also adds an empty line.
1184 * @throws DocumentException on error
1185 */
1186 protected void newLine() throws DocumentException {
1187 lastElementType = -1;
1188 carriageReturn();
1189 if (lines != null && !lines.isEmpty()) {
1190 lines.add(line);
1191 currentHeight += line.height();
1192 }
1193 line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
1194 }
1195
1196 /**
1197 * If the current line is not empty or null, it is added to the arraylist
1198 * of lines and a new empty line is added.
1199 * @throws DocumentException on error
1200 */
1201 protected void carriageReturn() {
1202 // the arraylist with lines may not be null
1203 if (lines == null) {
1204 lines = new ArrayList();
1205 }
1206 // If the current line is not null
1207 if (line != null) {
1208 // we check if the end of the page is reached (bugfix by Francois Gravel)
1209 if (currentHeight + line.height() + leading < indentTop() - indentBottom()) {
1210 // if so nonempty lines are added and the height is augmented
1211 if (line.size() > 0) {
1212 currentHeight += line.height();
1213 lines.add(line);
1214 pageEmpty = false;
1215 }
1216 }
1217 // if the end of the line is reached, we start a new page
1218 else {
1219 newPage();
1220 }
1221 }
1222 if (imageEnd > -1 && currentHeight > imageEnd) {
1223 imageEnd = -1;
1224 indentation.imageIndentRight = 0;
1225 indentation.imageIndentLeft = 0;
1226 }
1227 // a new current line is constructed
1228 line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
1229 }
1230
1231 /**
1232 * Gets the current vertical page position.
1233 * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
1234 * for elements that do not terminate the lines they've started because those lines will get
1235 * terminated.
1236 * @return The current vertical page position.
1237 */
1238 public float getVerticalPosition(boolean ensureNewLine) {
1239 // ensuring that a new line has been started.
1240 if (ensureNewLine) {
1241 ensureNewLine();
1242 }
1243 return top() - currentHeight - indentation.indentTop;
1244 }
1245
1246 /** Holds the type of the last element, that has been added to the document. */
1247 protected int lastElementType = -1;
1248
1249 /**
1250 * Ensures that a new line has been started.
1251 */
1252 protected void ensureNewLine() {
1253 try {
1254 if ((lastElementType == Element.PHRASE) ||
1255 (lastElementType == Element.CHUNK)) {
1256 newLine();
1257 flushLines();
1258 }
1259 } catch (DocumentException ex) {
1260 throw new ExceptionConverter(ex);
1261 }
1262 }
1263
1264 /**
1265 * Writes all the lines to the text-object.
1266 *
1267 * @return the displacement that was caused
1268 * @throws DocumentException on error
1269 */
1270 protected float flushLines() throws DocumentException {
1271 // checks if the ArrayList with the lines is not null
1272 if (lines == null) {
1273 return 0;
1274 }
1275 // checks if a new Line has to be made.
1276 if (line != null && line.size() > 0) {
1277 lines.add(line);
1278 line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
1279 }
1280
1281 // checks if the ArrayList with the lines is empty
1282 if (lines.isEmpty()) {
1283 return 0;
1284 }
1285
1286 // initialization of some parameters
1287 Object currentValues[] = new Object[2];
1288 PdfFont currentFont = null;
1289 float displacement = 0;
1290 PdfLine l;
1291 Float lastBaseFactor = new Float(0);
1292 currentValues[1] = lastBaseFactor;
1293 // looping over all the lines
1294 for (Iterator i = lines.iterator(); i.hasNext(); ) {
1295
1296 // this is a line in the loop
1297 l = (PdfLine) i.next();
1298
1299 float moveTextX = l.indentLeft() - indentLeft() + indentation.indentLeft + indentation.listIndentLeft + indentation.sectionIndentLeft;
1300 text.moveText(moveTextX, -l.height());
1301 // is the line preceded by a symbol?
1302 if (l.listSymbol() != null) {
1303 ColumnText.showTextAligned(graphics, Element.ALIGN_LEFT, new Phrase(l.listSymbol()), text.getXTLM() - l.listIndent(), text.getYTLM(), 0);
1304 }
1305
1306 currentValues[0] = currentFont;
1307
1308 writeLineToContent(l, text, graphics, currentValues, writer.getSpaceCharRatio());
1309
1310 currentFont = (PdfFont)currentValues[0];
1311 displacement += l.height();
1312 text.moveText(-moveTextX, 0);
1313
1314 }
1315 lines = new ArrayList();
1316 return displacement;
1317 }
1318
1319 /** The characters to be applied the hanging punctuation. */
1320 static final String hangingPunctuation = ".,;:'";
1321
1322 /**
1323 * Writes a text line to the document. It takes care of all the attributes.
1324 * <P>
1325 * Before entering the line position must have been established and the
1326 * <CODE>text</CODE> argument must be in text object scope (<CODE>beginText()</CODE>).
1327 * @param line the line to be written
1328 * @param text the <CODE>PdfContentByte</CODE> where the text will be written to
1329 * @param graphics the <CODE>PdfContentByte</CODE> where the graphics will be written to
1330 * @param currentValues the current font and extra spacing values
1331 * @param ratio
1332 * @throws DocumentException on error
1333 */
1334 void writeLineToContent(PdfLine line, PdfContentByte text, PdfContentByte graphics, Object currentValues[], float ratio) throws DocumentException {
1335 PdfFont currentFont = (PdfFont)(currentValues[0]);
1336 float lastBaseFactor = ((Float)(currentValues[1])).floatValue();
1337 PdfChunk chunk;
1338 int numberOfSpaces;
1339 int lineLen;
1340 boolean isJustified;
1341 float hangingCorrection = 0;
1342 float hScale = 1;
1343 float lastHScale = Float.NaN;
1344 float baseWordSpacing = 0;
1345 float baseCharacterSpacing = 0;
1346 float glueWidth = 0;
1347
1348 numberOfSpaces = line.numberOfSpaces();
1349 lineLen = line.GetLineLengthUtf32();
1350 // does the line need to be justified?
1351 isJustified = line.hasToBeJustified() && (numberOfSpaces != 0 || lineLen > 1);
1352 int separatorCount = line.getSeparatorCount();
1353 if (separatorCount > 0) {
1354 glueWidth = line.widthLeft() / separatorCount;
1355 }
1356 else if (isJustified) {
1357 if (line.isNewlineSplit() && line.widthLeft() >= (lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1))) {
1358 if (line.isRTL()) {
1359 text.moveText(line.widthLeft() - lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1), 0);
1360 }
1361 baseWordSpacing = ratio * lastBaseFactor;
1362 baseCharacterSpacing = lastBaseFactor;
1363 }
1364 else {
1365 float width = line.widthLeft();
1366 PdfChunk last = line.getChunk(line.size() - 1);
1367 if (last != null) {
1368 String s = last.toString();
1369 char c;
1370 if (s.length() > 0 && hangingPunctuation.indexOf((c = s.charAt(s.length() - 1))) >= 0) {
1371 float oldWidth = width;
1372 width += last.font().width(c) * 0.4f;
1373 hangingCorrection = width - oldWidth;
1374 }
1375 }
1376 float baseFactor = width / (ratio * numberOfSpaces + lineLen - 1);
1377 baseWordSpacing = ratio * baseFactor;
1378 baseCharacterSpacing = baseFactor;
1379 lastBaseFactor = baseFactor;
1380 }
1381 }
1382
1383 int lastChunkStroke = line.getLastStrokeChunk();
1384 int chunkStrokeIdx = 0;
1385 float xMarker = text.getXTLM();
1386 float baseXMarker = xMarker;
1387 float yMarker = text.getYTLM();
1388 boolean adjustMatrix = false;
1389 float tabPosition = 0;
1390
1391 // looping over all the chunks in 1 line
1392 for (Iterator j = line.iterator(); j.hasNext(); ) {
1393 chunk = (PdfChunk) j.next();
1394 Color color = chunk.color();
1395 hScale = 1;
1396
1397 if (chunkStrokeIdx <= lastChunkStroke) {
1398 float width;
1399 if (isJustified) {
1400 width = chunk.getWidthCorrected(baseCharacterSpacing, baseWordSpacing);
1401 }
1402 else {
1403 width = chunk.width();
1404 }
1405 if (chunk.isStroked()) {
1406 PdfChunk nextChunk = line.getChunk(chunkStrokeIdx + 1);
1407 if (chunk.isSeparator()) {
1408 width = glueWidth;
1409 Object[] sep = (Object[])chunk.getAttribute(Chunk.SEPARATOR);
1410 DrawInterface di = (DrawInterface)sep[0];
1411 Boolean vertical = (Boolean)sep[1];
1412 float fontSize = chunk.font().size();
1413 float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
1414 float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
1415 if (vertical.booleanValue()) {
1416 di.draw(graphics, baseXMarker, yMarker + descender, baseXMarker + line.getOriginalWidth(), ascender - descender, yMarker);
1417 }
1418 else {
1419 di.draw(graphics, xMarker, yMarker + descender, xMarker + width, ascender - descender, yMarker);
1420 }
1421 }
1422 if (chunk.isTab()) {
1423 Object[] tab = (Object[])chunk.getAttribute(Chunk.TAB);
1424 DrawInterface di = (DrawInterface)tab[0];
1425 tabPosition = ((Float)tab[1]).floatValue() + ((Float)tab[3]).floatValue();
1426 float fontSize = chunk.font().size();
1427 float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
1428 float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
1429 if (tabPosition > xMarker) {
1430 di.draw(graphics, xMarker, yMarker + descender, tabPosition, ascender - descender, yMarker);
1431 }
1432 float tmp = xMarker;
1433 xMarker = tabPosition;
1434 tabPosition = tmp;
1435 }
1436 if (chunk.isAttribute(Chunk.BACKGROUND)) {
1437 float subtract = lastBaseFactor;
1438 if (nextChunk != null && nextChunk.isAttribute(Chunk.BACKGROUND))
1439 subtract = 0;
1440 if (nextChunk == null)
1441 subtract += hangingCorrection;
1442 float fontSize = chunk.font().size();
1443 float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
1444 float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
1445 Object bgr[] = (Object[])chunk.getAttribute(Chunk.BACKGROUND);
1446 graphics.setColorFill((Color)bgr[0]);
1447 float extra[] = (float[])bgr[1];
1448 graphics.rectangle(xMarker - extra[0],
1449 yMarker + descender - extra[1] + chunk.getTextRise(),
1450 width - subtract + extra[0] + extra[2],
1451 ascender - descender + extra[1] + extra[3]);
1452 graphics.fill();
1453 graphics.setGrayFill(0);
1454 }
1455 if (chunk.isAttribute(Chunk.UNDERLINE)) {
1456 float subtract = lastBaseFactor;
1457 if (nextChunk != null && nextChunk.isAttribute(Chunk.UNDERLINE))
1458 subtract = 0;
1459 if (nextChunk == null)
1460 subtract += hangingCorrection;
1461 Object unders[][] = (Object[][])chunk.getAttribute(Chunk.UNDERLINE);
1462 Color scolor = null;
1463 for (int k = 0; k < unders.length; ++k) {
1464 Object obj[] = unders[k];
1465 scolor = (Color)obj[0];
1466 float ps[] = (float[])obj[1];
1467 if (scolor == null)
1468 scolor = color;
1469 if (scolor != null)
1470 graphics.setColorStroke(scolor);
1471 float fsize = chunk.font().size();
1472 graphics.setLineWidth(ps[0] + fsize * ps[1]);
1473 float shift = ps[2] + fsize * ps[3];
1474 int cap2 = (int)ps[4];
1475 if (cap2 != 0)
1476 graphics.setLineCap(cap2);
1477 graphics.moveTo(xMarker, yMarker + shift);
1478 graphics.lineTo(xMarker + width - subtract, yMarker + shift);
1479 graphics.stroke();
1480 if (scolor != null)
1481 graphics.resetGrayStroke();
1482 if (cap2 != 0)
1483 graphics.setLineCap(0);
1484 }
1485 graphics.setLineWidth(1);
1486 }
1487 if (chunk.isAttribute(Chunk.ACTION)) {
1488 float subtract = lastBaseFactor;
1489 if (nextChunk != null && nextChunk.isAttribute(Chunk.ACTION))
1490 subtract = 0;
1491 if (nextChunk == null)
1492 subtract += hangingCorrection;
1493 text.addAnnotation(new PdfAnnotation(writer, xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size(), (PdfAction)chunk.getAttribute(Chunk.ACTION)));
1494 }
1495 if (chunk.isAttribute(Chunk.REMOTEGOTO)) {
1496 float subtract = lastBaseFactor;
1497 if (nextChunk != null && nextChunk.isAttribute(Chunk.REMOTEGOTO))
1498 subtract = 0;
1499 if (nextChunk == null)
1500 subtract += hangingCorrection;
1501 Object obj[] = (Object[])chunk.getAttribute(Chunk.REMOTEGOTO);
1502 String filename = (String)obj[0];
1503 if (obj[1] instanceof String)
1504 remoteGoto(filename, (String)obj[1], xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
1505 else
1506 remoteGoto(filename, ((Integer)obj[1]).intValue(), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
1507 }
1508 if (chunk.isAttribute(Chunk.LOCALGOTO)) {
1509 float subtract = lastBaseFactor;
1510 if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALGOTO))
1511 subtract = 0;
1512 if (nextChunk == null)
1513 subtract += hangingCorrection;
1514 localGoto((String)chunk.getAttribute(Chunk.LOCALGOTO), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
1515 }
1516 if (chunk.isAttribute(Chunk.LOCALDESTINATION)) {
1517 float subtract = lastBaseFactor;
1518 if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALDESTINATION))
1519 subtract = 0;
1520 if (nextChunk == null)
1521 subtract += hangingCorrection;
1522 localDestination((String)chunk.getAttribute(Chunk.LOCALDESTINATION), new PdfDestination(PdfDestination.XYZ, xMarker, yMarker + chunk.font().size(), 0));
1523 }
1524 if (chunk.isAttribute(Chunk.GENERICTAG)) {
1525 float subtract = lastBaseFactor;
1526 if (nextChunk != null && nextChunk.isAttribute(Chunk.GENERICTAG))
1527 subtract = 0;
1528 if (nextChunk == null)
1529 subtract += hangingCorrection;
1530 Rectangle rect = new Rectangle(xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
1531 PdfPageEvent pev = writer.getPageEvent();
1532 if (pev != null)
1533 pev.onGenericTag(writer, this, rect, (String)chunk.getAttribute(Chunk.GENERICTAG));
1534 }
1535 if (chunk.isAttribute(Chunk.PDFANNOTATION)) {
1536 float subtract = lastBaseFactor;
1537 if (nextChunk != null && nextChunk.isAttribute(Chunk.PDFANNOTATION))
1538 subtract = 0;
1539 if (nextChunk == null)
1540 subtract += hangingCorrection;
1541 float fontSize = chunk.font().size();
1542 float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
1543 float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
1544 PdfAnnotation annot = PdfFormField.shallowDuplicate((PdfAnnotation)chunk.getAttribute(Chunk.PDFANNOTATION));
1545 annot.put(PdfName.RECT, new PdfRectangle(xMarker, yMarker + descender, xMarker + width - subtract, yMarker + ascender));
1546 text.addAnnotation(annot);
1547 }
1548 float params[] = (float[])chunk.getAttribute(Chunk.SKEW);
1549 Float hs = (Float)chunk.getAttribute(Chunk.HSCALE);
1550 if (params != null || hs != null) {
1551 float b = 0, c = 0;
1552 if (params != null) {
1553 b = params[0];
1554 c = params[1];
1555 }
1556 if (hs != null)
1557 hScale = hs.floatValue();
1558 text.setTextMatrix(hScale, b, c, 1, xMarker, yMarker);
1559 }
1560 if (chunk.isImage()) {
1561 Image image = chunk.getImage();
1562 float matrix[] = image.matrix();
1563 matrix[Image.CX] = xMarker + chunk.getImageOffsetX() - matrix[Image.CX];
1564 matrix[Image.CY] = yMarker + chunk.getImageOffsetY() - matrix[Image.CY];
1565 graphics.addImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
1566 text.moveText(xMarker + lastBaseFactor + image.getScaledWidth() - text.getXTLM(), 0);
1567 }
1568 }
1569 xMarker += width;
1570 ++chunkStrokeIdx;
1571 }
1572
1573 if (chunk.font().compareTo(currentFont) != 0) {
1574 currentFont = chunk.font();
1575 text.setFontAndSize(currentFont.getFont(), currentFont.size());
1576 }
1577 float rise = 0;
1578 Object textRender[] = (Object[])chunk.getAttribute(Chunk.TEXTRENDERMODE);
1579 int tr = 0;
1580 float strokeWidth = 1;
1581 Color strokeColor = null;
1582 Float fr = (Float)chunk.getAttribute(Chunk.SUBSUPSCRIPT);
1583 if (textRender != null) {
1584 tr = ((Integer)textRender[0]).intValue() & 3;
1585 if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
1586 text.setTextRenderingMode(tr);
1587 if (tr == PdfContentByte.TEXT_RENDER_MODE_STROKE || tr == PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE) {
1588 strokeWidth = ((Float)textRender[1]).floatValue();
1589 if (strokeWidth != 1)
1590 text.setLineWidth(strokeWidth);
1591 strokeColor = (Color)textRender[2];
1592 if (strokeColor == null)
1593 strokeColor = color;
1594 if (strokeColor != null)
1595 text.setColorStroke(strokeColor);
1596 }
1597 }
1598 if (fr != null)
1599 rise = fr.floatValue();
1600 if (color != null)
1601 text.setColorFill(color);
1602 if (rise != 0)
1603 text.setTextRise(rise);
1604 if (chunk.isImage()) {
1605 adjustMatrix = true;
1606 }
1607 else if (chunk.isHorizontalSeparator()) {
1608 PdfTextArray array = new PdfTextArray();
1609 array.add(-glueWidth * 1000f / chunk.font.size() / hScale);
1610 text.showText(array);
1611 }
1612 else if (chunk.isTab()) {
1613 PdfTextArray array = new PdfTextArray();
1614 array.add((tabPosition - xMarker) * 1000f / chunk.font.size() / hScale);
1615 text.showText(array);
1616 }
1617 // If it is a CJK chunk or Unicode TTF we will have to simulate the
1618 // space adjustment.
1619 else if (isJustified && numberOfSpaces > 0 && chunk.isSpecialEncoding()) {
1620 if (hScale != lastHScale) {
1621 lastHScale = hScale;
1622 text.setWordSpacing(baseWordSpacing / hScale);
1623 text.setCharacterSpacing(baseCharacterSpacing / hScale);
1624 }
1625 String s = chunk.toString();
1626 int idx = s.indexOf(' ');
1627 if (idx < 0)
1628 text.showText(s);
1629 else {
1630 float spaceCorrection = - baseWordSpacing * 1000f / chunk.font.size() / hScale;
1631 PdfTextArray textArray = new PdfTextArray(s.substring(0, idx));
1632 int lastIdx = idx;
1633 while ((idx = s.indexOf(' ', lastIdx + 1)) >= 0) {
1634 textArray.add(spaceCorrection);
1635 textArray.add(s.substring(lastIdx, idx));
1636 lastIdx = idx;
1637 }
1638 textArray.add(spaceCorrection);
1639 textArray.add(s.substring(lastIdx));
1640 text.showText(textArray);
1641 }
1642 }
1643 else {
1644 if (isJustified && hScale != lastHScale) {
1645 lastHScale = hScale;
1646 text.setWordSpacing(baseWordSpacing / hScale);
1647 text.setCharacterSpacing(baseCharacterSpacing / hScale);
1648 }
1649 text.showText(chunk.toString());
1650 }
1651
1652 if (rise != 0)
1653 text.setTextRise(0);
1654 if (color != null)
1655 text.resetRGBColorFill();
1656 if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
1657 text.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
1658 if (strokeColor != null)
1659 text.resetRGBColorStroke();
1660 if (strokeWidth != 1)
1661 text.setLineWidth(1);
1662 if (chunk.isAttribute(Chunk.SKEW) || chunk.isAttribute(Chunk.HSCALE)) {
1663 adjustMatrix = true;
1664 text.setTextMatrix(xMarker, yMarker);
1665 }
1666 }
1667 if (isJustified) {
1668 text.setWordSpacing(0);
1669 text.setCharacterSpacing(0);
1670 if (line.isNewlineSplit())
1671 lastBaseFactor = 0;
1672 }
1673 if (adjustMatrix)
1674 text.moveText(baseXMarker - text.getXTLM(), 0);
1675 currentValues[0] = currentFont;
1676 currentValues[1] = new Float(lastBaseFactor);
1677 }
1678
1679 protected Indentation indentation = new Indentation();
1680
1681 /**
1682 * @since 2.0.8 (PdfDocument was package-private before)
1683 */
1684 public static class Indentation {
1685
1686 /** This represents the current indentation of the PDF Elements on the left side. */
1687 float indentLeft = 0;
1688
1689 /** Indentation to the left caused by a section. */
1690 float sectionIndentLeft = 0;
1691
1692 /** This represents the current indentation of the PDF Elements on the left side. */
1693 float listIndentLeft = 0;
1694
1695 /** This is the indentation caused by an image on the left. */
1696 float imageIndentLeft = 0;
1697
1698 /** This represents the current indentation of the PDF Elements on the right side. */
1699 float indentRight = 0;
1700
1701 /** Indentation to the right caused by a section. */
1702 float sectionIndentRight = 0;
1703
1704 /** This is the indentation caused by an image on the right. */
1705 float imageIndentRight = 0;
1706
1707 /** This represents the current indentation of the PDF Elements on the top side. */
1708 float indentTop = 0;
1709
1710 /** This represents the current indentation of the PDF Elements on the bottom side. */
1711 float indentBottom = 0;
1712 }
1713
1714 /**
1715 * Gets the indentation on the left side.
1716 *
1717 * @return a margin
1718 */
1719
1720 protected float indentLeft() {
1721 return left(indentation.indentLeft + indentation.listIndentLeft + indentation.imageIndentLeft + indentation.sectionIndentLeft);
1722 }
1723
1724 /**
1725 * Gets the indentation on the right side.
1726 *
1727 * @return a margin
1728 */
1729
1730 protected float indentRight() {
1731 return right(indentation.indentRight + indentation.sectionIndentRight + indentation.imageIndentRight);
1732 }
1733
1734 /**
1735 * Gets the indentation on the top side.
1736 *
1737 * @return a margin
1738 */
1739
1740 protected float indentTop() {
1741 return top(indentation.indentTop);
1742 }
1743
1744 /**
1745 * Gets the indentation on the bottom side.
1746 *
1747 * @return a margin
1748 */
1749
1750 float indentBottom() {
1751 return bottom(indentation.indentBottom);
1752 }
1753
1754 /**
1755 * Adds extra space.
1756 * This method should probably be rewritten.
1757 */
1758 protected void addSpacing(float extraspace, float oldleading, Font f) {
1759 if (extraspace == 0) return;
1760 if (pageEmpty) return;
1761 if (currentHeight + line.height() + leading > indentTop() - indentBottom()) return;
1762 leading = extraspace;
1763 carriageReturn();
1764 if (f.isUnderlined() || f.isStrikethru()) {
1765 f = new Font(f);
1766 int style = f.getStyle();
1767 style &= ~Font.UNDERLINE;
1768 style &= ~Font.STRIKETHRU;
1769 f.setStyle(Font.UNDEFINED);
1770 f.setStyle(style);
1771 }
1772 Chunk space = new Chunk(" ", f);
1773 space.process(this);
1774 carriageReturn();
1775 leading = oldleading;
1776 }
1777
1778 // Info Dictionary and Catalog
1779
1780 /** some meta information about the Document. */
1781 protected PdfInfo info = new PdfInfo();
1782
1783 /**
1784 * Gets the <CODE>PdfInfo</CODE>-object.
1785 *
1786 * @return <CODE>PdfInfo</COPE>
1787 */
1788
1789 PdfInfo getInfo() {
1790 return info;
1791 }
1792
1793 /**
1794 * Gets the <CODE>PdfCatalog</CODE>-object.
1795 *
1796 * @param pages an indirect reference to this document pages
1797 * @return <CODE>PdfCatalog</CODE>
1798 */
1799
1800 PdfCatalog getCatalog(PdfIndirectReference pages) {
1801 PdfCatalog catalog = new PdfCatalog(pages, writer);
1802
1803 // [C1] outlines
1804 if (rootOutline.getKids().size() > 0) {
1805 catalog.put(PdfName.PAGEMODE, PdfName.USEOUTLINES);
1806 catalog.put(PdfName.OUTLINES, rootOutline.indirectReference());
1807 }
1808
1809 // [C2] version
1810 writer.getPdfVersion().addToCatalog(catalog);
1811
1812 // [C3] preferences
1813 viewerPreferences.addToCatalog(catalog);
1814
1815 // [C4] pagelabels
1816 if (pageLabels != null) {
1817 catalog.put(PdfName.PAGELABELS, pageLabels.getDictionary(writer));
1818 }
1819
1820 // [C5] named objects
1821 catalog.addNames(localDestinations, getDocumentLevelJS(), documentFileAttachment, writer);
1822
1823 // [C6] actions
1824 if (openActionName != null) {
1825 PdfAction action = getLocalGotoAction(openActionName);
1826 catalog.setOpenAction(action);
1827 }
1828 else if (openActionAction != null)
1829 catalog.setOpenAction(openActionAction);
1830 if (additionalActions != null) {
1831 catalog.setAdditionalActions(additionalActions);
1832 }
1833
1834 // [C7] portable collections
1835 if (collection != null) {
1836 catalog.put(PdfName.COLLECTION, collection);
1837 }
1838
1839 // [C8] AcroForm
1840 if (annotationsImp.hasValidAcroForm()) {
1841 try {
1842 catalog.put(PdfName.ACROFORM, writer.addToBody(annotationsImp.getAcroForm()).getIndirectReference());
1843 }
1844 catch (IOException e) {
1845 throw new ExceptionConverter(e);
1846 }
1847 }
1848
1849 return catalog;
1850 }
1851
1852 // [C1] outlines
1853
1854 /** This is the root outline of the document. */
1855 protected PdfOutline rootOutline;
1856
1857 /** This is the current <CODE>PdfOutline</CODE> in the hierarchy of outlines. */
1858 protected PdfOutline currentOutline;
1859
1860 /**
1861 * Adds a named outline to the document .
1862 * @param outline the outline to be added
1863 * @param name the name of this local destination
1864 */
1865 void addOutline(PdfOutline outline, String name) {
1866 localDestination(name, outline.getPdfDestination());
1867 }
1868
1869 /**
1870 * Gets the root outline. All the outlines must be created with a parent.
1871 * The first level is created with this outline.
1872 * @return the root outline
1873 */
1874 public PdfOutline getRootOutline() {
1875 return rootOutline;
1876 }
1877
1878
1879 /**
1880 * Updates the count in the outlines.
1881 */
1882 void calculateOutlineCount() {
1883 if (rootOutline.getKids().size() == 0)
1884 return;
1885 traverseOutlineCount(rootOutline);
1886 }
1887
1888 /**
1889 * Recursive method to update the count in the outlines.
1890 */
1891 void traverseOutlineCount(PdfOutline outline) {
1892 ArrayList kids = outline.getKids();
1893 PdfOutline parent = outline.parent();
1894 if (kids.isEmpty()) {
1895 if (parent != null) {
1896 parent.setCount(parent.getCount() + 1);
1897 }
1898 }
1899 else {
1900 for (int k = 0; k < kids.size(); ++k) {
1901 traverseOutlineCount((PdfOutline)kids.get(k));
1902 }
1903 if (parent != null) {
1904 if (outline.isOpen()) {
1905 parent.setCount(outline.getCount() + parent.getCount() + 1);
1906 }
1907 else {
1908 parent.setCount(parent.getCount() + 1);
1909 outline.setCount(-outline.getCount());
1910 }
1911 }
1912 }
1913 }
1914
1915 /**
1916 * Writes the outline tree to the body of the PDF document.
1917 */
1918 void writeOutlines() throws IOException {
1919 if (rootOutline.getKids().size() == 0)
1920 return;
1921 outlineTree(rootOutline);
1922 writer.addToBody(rootOutline, rootOutline.indirectReference());
1923 }
1924
1925 /**
1926 * Recursive method used to write outlines.
1927 */
1928 void outlineTree(PdfOutline outline) throws IOException {
1929 outline.setIndirectReference(writer.getPdfIndirectReference());
1930 if (outline.parent() != null)
1931 outline.put(PdfName.PARENT, outline.parent().indirectReference());
1932 ArrayList kids = outline.getKids();
1933 int size = kids.size();
1934 for (int k = 0; k < size; ++k)
1935 outlineTree((PdfOutline)kids.get(k));
1936 for (int k = 0; k < size; ++k) {
1937 if (k > 0)
1938 ((PdfOutline)kids.get(k)).put(PdfName.PREV, ((PdfOutline)kids.get(k - 1)).indirectReference());
1939 if (k < size - 1)
1940 ((PdfOutline)kids.get(k)).put(PdfName.NEXT, ((PdfOutline)kids.get(k + 1)).indirectReference());
1941 }
1942 if (size > 0) {
1943 outline.put(PdfName.FIRST, ((PdfOutline)kids.get(0)).indirectReference());
1944 outline.put(PdfName.LAST, ((PdfOutline)kids.get(size - 1)).indirectReference());
1945 }
1946 for (int k = 0; k < size; ++k) {
1947 PdfOutline kid = (PdfOutline)kids.get(k);
1948 writer.addToBody(kid, kid.indirectReference());
1949 }
1950 }
1951
1952 // [C3] PdfViewerPreferences interface
1953
1954 /** Contains the Viewer preferences of this PDF document. */
1955 protected PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp();
1956 /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */
1957 void setViewerPreferences(int preferences) {
1958 this.viewerPreferences.setViewerPreferences(preferences);
1959 }
1960
1961 /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */
1962 void addViewerPreference(PdfName key, PdfObject value) {
1963 this.viewerPreferences.addViewerPreference(key, value);
1964 }
1965
1966 // [C4] Page labels
1967
1968 protected PdfPageLabels pageLabels;
1969 /**
1970 * Sets the page labels
1971 * @param pageLabels the page labels
1972 */
1973 void setPageLabels(PdfPageLabels pageLabels) {
1974 this.pageLabels = pageLabels;
1975 }
1976
1977 // [C5] named objects: local destinations, javascript, embedded files
1978
1979 /**
1980 * Implements a link to other part of the document. The jump will
1981 * be made to a local destination with the same name, that must exist.
1982 * @param name the name for this link
1983 * @param llx the lower left x corner of the activation area
1984 * @param lly the lower left y corner of the activation area
1985 * @param urx the upper right x corner of the activation area
1986 * @param ury the upper right y corner of the activation area
1987 */
1988 void localGoto(String name, float llx, float lly, float urx, float ury) {
1989 PdfAction action = getLocalGotoAction(name);
1990 annotationsImp.addPlainAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action));
1991 }
1992
1993 /**
1994 * Implements a link to another document.
1995 * @param filename the filename for the remote document
1996 * @param name the name to jump to
1997 * @param llx the lower left x corner of the activation area
1998 * @param lly the lower left y corner of the activation area
1999 * @param urx the upper right x corner of the activation area
2000 * @param ury the upper right y corner of the activation area
2001 */
2002 void remoteGoto(String filename, String name, float llx, float lly, float urx, float ury) {
2003 annotationsImp.addPlainAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, name)));
2004 }
2005
2006 /**
2007 * Implements a link to another document.
2008 * @param filename the filename for the remote document
2009 * @param page the page to jump to
2010 * @param llx the lower left x corner of the activation area
2011 * @param lly the lower left y corner of the activation area
2012 * @param urx the upper right x corner of the activation area
2013 * @param ury the upper right y corner of the activation area
2014 */
2015 void remoteGoto(String filename, int page, float llx, float lly, float urx, float ury) {
2016 addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, page)));
2017 }
2018
2019 /** Implements an action in an area.
2020 * @param action the <CODE>PdfAction</CODE>
2021 * @param llx the lower left x corner of the activation area
2022 * @param lly the lower left y corner of the activation area
2023 * @param urx the upper right x corner of the activation area
2024 * @param ury the upper right y corner of the activation area
2025 */
2026 void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
2027 addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action));
2028 }
2029
2030 /**
2031 * Stores the destinations keyed by name. Value is
2032 * <CODE>Object[]{PdfAction,PdfIndirectReference,PdfDestintion}</CODE>.
2033 */
2034 protected TreeMap localDestinations = new TreeMap();
2035
2036 PdfAction getLocalGotoAction(String name) {
2037 PdfAction action;
2038 Object obj[] = (Object[])localDestinations.get(name);
2039 if (obj == null)
2040 obj = new Object[3];
2041 if (obj[0] == null) {
2042 if (obj[1] == null) {
2043 obj[1] = writer.getPdfIndirectReference();
2044 }
2045 action = new PdfAction((PdfIndirectReference)obj[1]);
2046 obj[0] = action;
2047 localDestinations.put(name, obj);
2048 }
2049 else {
2050 action = (PdfAction)obj[0];
2051 }
2052 return action;
2053 }
2054
2055 /**
2056 * The local destination to where a local goto with the same
2057 * name will jump to.
2058 * @param name the name of this local destination
2059 * @param destination the <CODE>PdfDestination</CODE> with the jump coordinates
2060 * @return <CODE>true</CODE> if the local destination was added,
2061 * <CODE>false</CODE> if a local destination with the same name
2062 * already existed
2063 */
2064 boolean localDestination(String name, PdfDestination destination) {
2065 Object obj[] = (Object[])localDestinations.get(name);
2066 if (obj == null)
2067 obj = new Object[3];
2068 if (obj[2] != null)
2069 return false;
2070 obj[2] = destination;
2071 localDestinations.put(name, obj);
2072 destination.addPage(writer.getCurrentPage());
2073 return true;
2074 }
2075
2076 /**
2077 * Stores a list of document level JavaScript actions.
2078 */
2079 int jsCounter;
2080 protected HashMap documentLevelJS = new HashMap();
2081 protected static final DecimalFormat SIXTEEN_DIGITS = new DecimalFormat("0000000000000000");
2082 void addJavaScript(PdfAction js) {
2083 if (js.get(PdfName.JS) == null)
2084 throw new RuntimeException("Only JavaScript actions are allowed.");
2085 try {
2086 documentLevelJS.put(SIXTEEN_DIGITS.format(jsCounter++), writer.addToBody(js).getIndirectReference());
2087 }
2088 catch (IOException e) {
2089 throw new ExceptionConverter(e);
2090 }
2091 }
2092 void addJavaScript(String name, PdfAction js) {
2093 if (js.get(PdfName.JS) == null)
2094 throw new RuntimeException("Only JavaScript actions are allowed.");
2095 try {
2096 documentLevelJS.put(name, writer.addToBody(js).getIndirectReference());
2097 }
2098 catch (IOException e) {
2099 throw new ExceptionConverter(e);
2100 }
2101 }
2102
2103 HashMap getDocumentLevelJS() {
2104 return documentLevelJS;
2105 }
2106
2107 protected HashMap documentFileAttachment = new HashMap();
2108
2109 void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
2110 if (description == null) {
2111 PdfString desc = (PdfString)fs.get(PdfName.DESC);
2112 if (desc == null) {
2113 description = "";
2114 }
2115 else {
2116 description = PdfEncodings.convertToString(desc.getBytes(), null);
2117 }
2118 }
2119 fs.addDescription(description, true);
2120 if (description.length() == 0)
2121 description = "Unnamed";
2122 String fn = PdfEncodings.convertToString(new PdfString(description, PdfObject.TEXT_UNICODE).getBytes(), null);
2123 int k = 0;
2124 while (documentFileAttachment.containsKey(fn)) {
2125 ++k;
2126 fn = PdfEncodings.convertToString(new PdfString(description + " " + k, PdfObject.TEXT_UNICODE).getBytes(), null);
2127 }
2128 documentFileAttachment.put(fn, fs.getReference());
2129 }
2130
2131 HashMap getDocumentFileAttachment() {
2132 return documentFileAttachment;
2133 }
2134
2135 // [C6] document level actions
2136
2137 protected String openActionName;
2138
2139 void setOpenAction(String name) {
2140 openActionName = name;
2141 openActionAction = null;
2142 }
2143
2144 protected PdfAction openActionAction;
2145 void setOpenAction(PdfAction action) {
2146 openActionAction = action;
2147 openActionName = null;
2148 }
2149
2150 protected PdfDictionary additionalActions;
2151 void addAdditionalAction(PdfName actionType, PdfAction action) {
2152 if (additionalActions == null) {
2153 additionalActions = new PdfDictionary();
2154 }
2155 if (action == null)
2156 additionalActions.remove(actionType);
2157 else
2158 additionalActions.put(actionType, action);
2159 if (additionalActions.size() == 0)
2160 additionalActions = null;
2161 }
2162
2163 // [C7] portable collections
2164
2165 protected PdfCollection collection;
2166
2167 /**
2168 * Sets the collection dictionary.
2169 * @param collection a dictionary of type PdfCollection
2170 */
2171 public void setCollection(PdfCollection collection) {
2172 this.collection = collection;
2173 }
2174
2175 // [C8] AcroForm
2176
2177 PdfAnnotationsImp annotationsImp;
2178
2179 /**
2180 * Gets the AcroForm object.
2181 * @return the PdfAcroform object of the PdfDocument
2182 */
2183 PdfAcroForm getAcroForm() {
2184 return annotationsImp.getAcroForm();
2185 }
2186
2187 void setSigFlags(int f) {
2188 annotationsImp.setSigFlags(f);
2189 }
2190
2191 void addCalculationOrder(PdfFormField formField) {
2192 annotationsImp.addCalculationOrder(formField);
2193 }
2194
2195 void addAnnotation(PdfAnnotation annot) {
2196 pageEmpty = false;
2197 annotationsImp.addAnnotation(annot);
2198 }
2199
2200 // [F12] tagged PDF
2201
2202 protected int markPoint;
2203
2204 int getMarkPoint() {
2205 return markPoint;
2206 }
2207
2208 void incMarkPoint() {
2209 ++markPoint;
2210 }
2211
2212 // [U1] page sizes
2213
2214 /** This is the size of the next page. */
2215 protected Rectangle nextPageSize = null;
2216
2217 /** This is the size of the several boxes of the current Page. */
2218 protected HashMap thisBoxSize = new HashMap();
2219
2220 /** This is the size of the several boxes that will be used in
2221 * the next page. */
2222 protected HashMap boxSize = new HashMap();
2223
2224 void setCropBoxSize(Rectangle crop) {
2225 setBoxSize("crop", crop);
2226 }
2227
2228 void setBoxSize(String boxName, Rectangle size) {
2229 if (size == null)
2230 boxSize.remove(boxName);
2231 else
2232 boxSize.put(boxName, new PdfRectangle(size));
2233 }
2234
2235 protected void setNewPageSizeAndMargins() {
2236 pageSize = nextPageSize;
2237 if (marginMirroring && (getPageNumber() & 1) == 0) {
2238 marginRight = nextMarginLeft;
2239 marginLeft = nextMarginRight;
2240 }
2241 else {
2242 marginLeft = nextMarginLeft;
2243 marginRight = nextMarginRight;
2244 }
2245 marginTop = nextMarginTop;
2246 marginBottom = nextMarginBottom;
2247 }
2248
2249 /**
2250 * Gives the size of a trim, art, crop or bleed box, or null if not defined.
2251 * @param boxName crop, trim, art or bleed
2252 */
2253 Rectangle getBoxSize(String boxName) {
2254 PdfRectangle r = (PdfRectangle)thisBoxSize.get(boxName);
2255 if (r != null) return r.getRectangle();
2256 return null;
2257 }
2258
2259 // [U2] empty pages
2260
2261 /** This checks if the page is empty. */
2262 protected boolean pageEmpty = true;
2263
2264 void setPageEmpty(boolean pageEmpty) {
2265 this.pageEmpty = pageEmpty;
2266 }
2267
2268 // [U3] page actions
2269
2270 /** The duration of the page */
2271 protected int duration=-1; // negative values will indicate no duration
2272
2273 /** The page transition */
2274 protected PdfTransition transition=null;
2275
2276 /**
2277 * Sets the display duration for the page (for presentations)
2278 * @param seconds the number of seconds to display the page
2279 */
2280 void setDuration(int seconds) {
2281 if (seconds > 0)
2282 this.duration=seconds;
2283 else
2284 this.duration=-1;
2285 }
2286
2287 /**
2288 * Sets the transition for the page
2289 * @param transition the PdfTransition object
2290 */
2291 void setTransition(PdfTransition transition) {
2292 this.transition=transition;
2293 }
2294
2295 protected PdfDictionary pageAA = null;
2296 void setPageAction(PdfName actionType, PdfAction action) {
2297 if (pageAA == null) {
2298 pageAA = new PdfDictionary();
2299 }
2300 pageAA.put(actionType, action);
2301 }
2302
2303 // [U8] thumbnail images
2304
2305 protected PdfIndirectReference thumb;
2306 void setThumbnail(Image image) throws PdfException, DocumentException {
2307 thumb = writer.getImageReference(writer.addDirectImageSimple(image));
2308 }
2309
2310 // [M0] Page resources contain references to fonts, extgstate, images,...
2311
2312 /** This are the page resources of the current Page. */
2313 protected PageResources pageResources;
2314
2315 PageResources getPageResources() {
2316 return pageResources;
2317 }
2318
2319 // [M3] Images
2320
2321 /** Holds value of property strictImageSequence. */
2322 protected boolean strictImageSequence = false;
2323
2324 /** Getter for property strictImageSequence.
2325 * @return Value of property strictImageSequence.
2326 *
2327 */
2328 boolean isStrictImageSequence() {
2329 return this.strictImageSequence;
2330 }
2331
2332 /** Setter for property strictImageSequence.
2333 * @param strictImageSequence New value of property strictImageSequence.
2334 *
2335 */
2336 void setStrictImageSequence(boolean strictImageSequence) {
2337 this.strictImageSequence = strictImageSequence;
2338 }
2339
2340 /** This is the position where the image ends. */
2341 protected float imageEnd = -1;
2342
2343 /**
2344 * Method added by Pelikan Stephan
2345 */
2346 public void clearTextWrap() {
2347 float tmpHeight = imageEnd - currentHeight;
2348 if (line != null) {
2349 tmpHeight += line.height();
2350 }
2351 if ((imageEnd > -1) && (tmpHeight > 0)) {
2352 carriageReturn();
2353 currentHeight += tmpHeight;
2354 }
2355 }
2356
2357 /** This is the image that could not be shown on a previous page. */
2358 protected Image imageWait = null;
2359
2360 /**
2361 * Adds an image to the document.
2362 * @param image the <CODE>Image</CODE> to add
2363 * @throws PdfException on error
2364 * @throws DocumentException on error
2365 */
2366
2367 protected void add(Image image) throws PdfException, DocumentException {
2368
2369 if (image.hasAbsoluteY()) {
2370 graphics.addImage(image);
2371 pageEmpty = false;
2372 return;
2373 }
2374
2375 // if there isn't enough room for the image on this page, save it for the next page
2376 if (currentHeight != 0 && indentTop() - currentHeight - image.getScaledHeight() < indentBottom()) {
2377 if (!strictImageSequence && imageWait == null) {
2378 imageWait = image;
2379 return;
2380 }
2381 newPage();
2382 if (currentHeight != 0 && indentTop() - currentHeight - image.getScaledHeight() < indentBottom()) {
2383 imageWait = image;
2384 return;
2385 }
2386 }
2387 pageEmpty = false;
2388 // avoid endless loops
2389 if (image == imageWait)
2390 imageWait = null;
2391 boolean textwrap = (image.getAlignment() & Image.TEXTWRAP) == Image.TEXTWRAP
2392 && !((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE);
2393 boolean underlying = (image.getAlignment() & Image.UNDERLYING) == Image.UNDERLYING;
2394 float diff = leading / 2;
2395 if (textwrap) {
2396 diff += leading;
2397 }
2398 float lowerleft = indentTop() - currentHeight - image.getScaledHeight() -diff;
2399 float mt[] = image.matrix();
2400 float startPosition = indentLeft() - mt[4];
2401 if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) startPosition = indentRight() - image.getScaledWidth() - mt[4];
2402 if ((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE) startPosition = indentLeft() + ((indentRight() - indentLeft() - image.getScaledWidth()) / 2) - mt[4];
2403 if (image.hasAbsoluteX()) startPosition = image.getAbsoluteX();
2404 if (textwrap) {
2405 if (imageEnd < 0 || imageEnd < currentHeight + image.getScaledHeight() + diff) {
2406 imageEnd = currentHeight + image.getScaledHeight() + diff;
2407 }
2408 if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) {
2409 // indentation suggested by Pelikan Stephan
2410 indentation.imageIndentRight += image.getScaledWidth() + image.getIndentationLeft();
2411 }
2412 else {
2413 // indentation suggested by Pelikan Stephan
2414 indentation.imageIndentLeft += image.getScaledWidth() + image.getIndentationRight();
2415 }
2416 }
2417 else {
2418 if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) startPosition -= image.getIndentationRight();
2419 else if ((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE) startPosition += image.getIndentationLeft() - image.getIndentationRight();
2420 else startPosition += image.getIndentationLeft();
2421 }
2422 graphics.addImage(image, mt[0], mt[1], mt[2], mt[3], startPosition, lowerleft - mt[5]);
2423 if (!(textwrap || underlying)) {
2424 currentHeight += image.getScaledHeight() + diff;
2425 flushLines();
2426 text.moveText(0, - (image.getScaledHeight() + diff));
2427 newLine();
2428 }
2429 }
2430
2431 // [M4] Adding a PdfPTable
2432
2433 /** Adds a <CODE>PdfPTable</CODE> to the document.
2434 * @param ptable the <CODE>PdfPTable</CODE> to be added to the document.
2435 * @throws DocumentException on error
2436 */
2437 void addPTable(PdfPTable ptable) throws DocumentException {
2438 ColumnText ct = new ColumnText(writer.getDirectContent());
2439 if (currentHeight > 0) {
2440 Paragraph p = new Paragraph();
2441 p.setLeading(0);
2442 ct.addElement(p);
2443 // if the table prefers to be on a single page, and it wouldn't
2444 //fit on the current page, start a new page.
2445 if (ptable.getKeepTogether() && !fitsPage(ptable, 0f)) {
2446 newPage();
2447 }
2448 }
2449 ct.addElement(ptable);
2450 boolean he = ptable.isHeadersInEvent();
2451 ptable.setHeadersInEvent(true);
2452 int loop = 0;
2453 while (true) {
2454 ct.setSimpleColumn(indentLeft(), indentBottom(), indentRight(), indentTop() - currentHeight);
2455 int status = ct.go();
2456 if ((status & ColumnText.NO_MORE_TEXT) != 0) {
2457 text.moveText(0, ct.getYLine() - indentTop() + currentHeight);
2458 currentHeight = indentTop() - ct.getYLine();
2459 break;
2460 }
2461 if (indentTop() - currentHeight == ct.getYLine())
2462 ++loop;
2463 else
2464 loop = 0;
2465 if (loop == 3) {
2466 add(new Paragraph("ERROR: Infinite table loop"));
2467 break;
2468 }
2469 newPage();
2470 }
2471 ptable.setHeadersInEvent(he);
2472 }
2473
2474 /**
2475 * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
2476 *
2477 * @param table the table that has to be checked
2478 * @param margin a certain margin
2479 * @return <CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
2480 */
2481
2482 boolean fitsPage(PdfPTable table, float margin) {
2483 if (!table.isLockedWidth()) {
2484 float totalWidth = (indentRight() - indentLeft()) * table.getWidthPercentage() / 100;
2485 table.setTotalWidth(totalWidth);
2486 }
2487 // ensuring that a new line has been started.
2488 ensureNewLine();
2489 return table.getTotalHeight() <= indentTop() - currentHeight - indentBottom() - margin;
2490 }
2491
2492 // [M4'] Adding a Table
2493
2494 /**
2495 * This is a helper class for adding a Table to a document.
2496 * @since 2.0.8 (PdfDocument was package-private before)
2497 */
2498 protected static class RenderingContext {
2499 float pagetop = -1;
2500 float oldHeight = -1;
2501
2502 PdfContentByte cellGraphics = null;
2503
2504 float lostTableBottom;
2505
2506 float maxCellBottom;
2507 float maxCellHeight;
2508
2509 Map rowspanMap;
2510 Map pageMap = new HashMap();
2511 /**
2512 * A PdfPTable
2513 */
2514 public PdfTable table;
2515
2516 /**
2517 * Consumes the rowspan
2518 * @param c
2519 * @return a rowspan.
2520 */
2521 public int consumeRowspan(PdfCell c) {
2522 if (c.rowspan() == 1) {
2523 return 1;
2524 }
2525
2526 Integer i = (Integer) rowspanMap.get(c);
2527 if (i == null) {
2528 i = new Integer(c.rowspan());
2529 }
2530
2531 i = new Integer(i.intValue() - 1);
2532 rowspanMap.put(c, i);
2533
2534 if (i.intValue() < 1) {
2535 return 1;
2536 }
2537 return i.intValue();
2538 }
2539
2540 /**
2541 * Looks at the current rowspan.
2542 * @param c
2543 * @return the current rowspan
2544 */
2545 public int currentRowspan(PdfCell c) {
2546 Integer i = (Integer) rowspanMap.get(c);
2547 if (i == null) {
2548 return c.rowspan();
2549 } else {
2550 return i.intValue();
2551 }
2552 }
2553
2554 public int cellRendered(PdfCell cell, int pageNumber) {
2555 Integer i = (Integer) pageMap.get(cell);
2556 if (i == null) {
2557 i = new Integer(1);
2558 } else {
2559 i = new Integer(i.intValue() + 1);
2560 }
2561 pageMap.put(cell, i);
2562
2563 Integer pageInteger = new Integer(pageNumber);
2564 Set set = (Set) pageMap.get(pageInteger);
2565
2566 if (set == null) {
2567 set = new HashSet();
2568 pageMap.put(pageInteger, set);
2569 }
2570
2571 set.add(cell);
2572
2573 return i.intValue();
2574 }
2575
2576 public int numCellRendered(PdfCell cell) {
2577 Integer i = (Integer) pageMap.get(cell);
2578 if (i == null) {
2579 i = new Integer(0);
2580 }
2581 return i.intValue();
2582 }
2583
2584 public boolean isCellRenderedOnPage(PdfCell cell, int pageNumber) {
2585 Integer pageInteger = new Integer(pageNumber);
2586 Set set = (Set) pageMap.get(pageInteger);
2587
2588 if (set != null) {
2589 return set.contains(cell);
2590 }
2591
2592 return false;
2593 }
2594 };
2595
2596 /**
2597 * Adds a new table to the document.
2598 * @param t Table to add. Rendered rows will be deleted after processing.
2599 * @throws DocumentException
2600 * @since iText 2.0.8
2601 */
2602 private void addPdfTable(Table t) throws DocumentException {
2603 // before every table, we flush all lines
2604 flushLines();
2605
2606 PdfTable table = new PdfTable(t, indentLeft(), indentRight(), indentTop() - currentHeight);
2607 RenderingContext ctx = new RenderingContext();
2608 ctx.pagetop = indentTop();
2609 ctx.oldHeight = currentHeight;
2610 ctx.cellGraphics = new PdfContentByte(writer);
2611 ctx.rowspanMap = new HashMap();
2612 ctx.table = table;
2613
2614 // initialization of parameters
2615 PdfCell cell;
2616
2617 // drawing the table
2618 ArrayList headercells = table.getHeaderCells();
2619 ArrayList cells = table.getCells();
2620 ArrayList rows = extractRows(cells, ctx);
2621 boolean isContinue = false;
2622 while (!cells.isEmpty()) {
2623 // initialization of some extra parameters;
2624 ctx.lostTableBottom = 0;
2625
2626 // loop over the cells
2627 boolean cellsShown = false;
2628
2629 // draw the cells (line by line)
2630 Iterator iterator = rows.iterator();
2631
2632 boolean atLeastOneFits = false;
2633 while (iterator.hasNext()) {
2634 ArrayList row = (ArrayList) iterator.next();
2635 analyzeRow(rows, ctx);
2636 renderCells(ctx, row, table.hasToFitPageCells() & atLeastOneFits);
2637
2638 if (!mayBeRemoved(row)) {
2639 break;
2640 }
2641 consumeRowspan(row, ctx);
2642 iterator.remove();
2643 atLeastOneFits = true;
2644 }
2645
2646 // compose cells array list for subsequent code
2647 cells.clear();
2648 Set opt = new HashSet();
2649 iterator = rows.iterator();
2650 while (iterator.hasNext()) {
2651 ArrayList row = (ArrayList) iterator.next();
2652
2653 Iterator cellIterator = row.iterator();
2654 while (cellIterator.hasNext()) {
2655 cell = (PdfCell) cellIterator.next();
2656
2657 if (!opt.contains(cell)) {
2658 cells.add(cell);
2659 opt.add(cell);
2660 }
2661 }
2662 }
2663
2664 // we paint the graphics of the table after looping through all the cells
2665 Rectangle tablerec = new Rectangle(table);
2666 tablerec.setBorder(table.getBorder());
2667 tablerec.setBorderWidth(table.getBorderWidth());
2668 tablerec.setBorderColor(table.getBorderColor());
2669 tablerec.setBackgroundColor(table.getBackgroundColor());
2670 PdfContentByte under = writer.getDirectContentUnder();
2671 under.rectangle(tablerec.rectangle(top(), indentBottom()));
2672 under.add(ctx.cellGraphics);
2673 // bugfix by Gerald Fehringer: now again add the border for the table
2674 // since it might have been covered by cell backgrounds
2675 tablerec.setBackgroundColor(null);
2676 tablerec = tablerec.rectangle(top(), indentBottom());
2677 tablerec.setBorder(table.getBorder());
2678 under.rectangle(tablerec);
2679 // end bugfix
2680
2681 ctx.cellGraphics = new PdfContentByte(null);
2682 // if the table continues on the next page
2683
2684 if (!rows.isEmpty()) {
2685 isContinue = true;
2686 graphics.setLineWidth(table.getBorderWidth());
2687 if (cellsShown && (table.getBorder() & Rectangle.BOTTOM) == Rectangle.BOTTOM) {
2688 // Draw the bottom line
2689
2690 // the color is set to the color of the element
2691 Color tColor = table.getBorderColor();
2692 if (tColor != null) {
2693 graphics.setColorStroke(tColor);
2694 }
2695 graphics.moveTo(table.getLeft(), Math.max(table.getBottom(), indentBottom()));
2696 graphics.lineTo(table.getRight(), Math.max(table.getBottom(), indentBottom()));
2697 graphics.stroke();
2698 if (tColor != null) {
2699 graphics.resetRGBColorStroke();
2700 }
2701 }
2702
2703 // old page
2704 pageEmpty = false;
2705 float difference = ctx.lostTableBottom;
2706
2707 // new page
2708 newPage();
2709
2710 // G.F.: if something added in page event i.e. currentHeight > 0
2711 float heightCorrection = 0;
2712 boolean somethingAdded = false;
2713 if (currentHeight > 0) {
2714 heightCorrection = 6;
2715 currentHeight += heightCorrection;
2716 somethingAdded = true;
2717 newLine();
2718 flushLines();
2719 indentation.indentTop = currentHeight - leading;
2720 currentHeight = 0;
2721 }
2722 else {
2723 flushLines();
2724 }
2725
2726 // this part repeats the table headers (if any)
2727 int size = headercells.size();
2728 if (size > 0) {
2729 // this is the top of the headersection
2730 cell = (PdfCell) headercells.get(0);
2731 float oldTop = cell.getTop(0);
2732 // loop over all the cells of the table header
2733 for (int i = 0; i < size; i++) {
2734 cell = (PdfCell) headercells.get(i);
2735 // calculation of the new cellpositions
2736 cell.setTop(indentTop() - oldTop + cell.getTop(0));
2737 cell.setBottom(indentTop() - oldTop + cell.getBottom(0));
2738 ctx.pagetop = cell.getBottom();
2739 // we paint the borders of the cell
2740 ctx.cellGraphics.rectangle(cell.rectangle(indentTop(), indentBottom()));
2741 // we write the text of the cell
2742 ArrayList images = cell.getImages(indentTop(), indentBottom());
2743 for (Iterator im = images.iterator(); im.hasNext();) {
2744 cellsShown = true;
2745 Image image = (Image) im.next();
2746 graphics.addImage(image);
2747 }
2748 lines = cell.getLines(indentTop(), indentBottom());
2749 float cellTop = cell.getTop(indentTop());
2750 text.moveText(0, cellTop-heightCorrection);
2751 float cellDisplacement = flushLines() - cellTop+heightCorrection;
2752 text.moveText(0, cellDisplacement);
2753 }
2754
2755 currentHeight = indentTop() - ctx.pagetop + table.cellspacing();
2756 text.moveText(0, ctx.pagetop - indentTop() - currentHeight);
2757 }
2758 else {
2759 if (somethingAdded) {
2760 ctx.pagetop = indentTop();
2761 text.moveText(0, -table.cellspacing());
2762 }
2763 }
2764 ctx.oldHeight = currentHeight - heightCorrection;
2765
2766 // calculating the new positions of the table and the cells
2767 size = Math.min(cells.size(), table.columns());
2768 int i = 0;
2769 while (i < size) {
2770 cell = (PdfCell) cells.get(i);
2771 if (cell.getTop(-table.cellspacing()) > ctx.lostTableBottom) {
2772 float newBottom = ctx.pagetop - difference + cell.getBottom();
2773 float neededHeight = cell.remainingHeight();
2774 if (newBottom > ctx.pagetop - neededHeight) {
2775 difference += newBottom - (ctx.pagetop - neededHeight);
2776 }
2777 }
2778 i++;
2779 }
2780 size = cells.size();
2781 table.setTop(indentTop());
2782 table.setBottom(ctx.pagetop - difference + table.getBottom(table.cellspacing()));
2783 for (i = 0; i < size; i++) {
2784 cell = (PdfCell) cells.get(i);
2785 float newBottom = ctx.pagetop - difference + cell.getBottom();
2786 float newTop = ctx.pagetop - difference + cell.getTop(-table.cellspacing());
2787 if (newTop > indentTop() - currentHeight) {
2788 newTop = indentTop() - currentHeight;
2789 }
2790
2791 cell.setTop(newTop );
2792 cell.setBottom(newBottom );
2793 }
2794 }
2795 }
2796
2797 float tableHeight = table.getTop() - table.getBottom();
2798 // bugfix by Adauto Martins when have more than two tables and more than one page
2799 // If continuation of table in other page (bug report #1460051)
2800 if (isContinue) {
2801 currentHeight = tableHeight;
2802 text.moveText(0, -(tableHeight - (ctx.oldHeight * 2)));
2803 } else {
2804 currentHeight = ctx.oldHeight + tableHeight;
2805 text.moveText(0, -tableHeight);
2806 }
2807 // end bugfix
2808 pageEmpty = false;
2809 }
2810
2811 protected void analyzeRow(ArrayList rows, RenderingContext ctx) {
2812 ctx.maxCellBottom = indentBottom();
2813
2814 // determine whether row(index) is in a rowspan
2815 int rowIndex = 0;
2816
2817 ArrayList row = (ArrayList) rows.get(rowIndex);
2818 int maxRowspan = 1;
2819 Iterator iterator = row.iterator();
2820 while (iterator.hasNext()) {
2821 PdfCell cell = (PdfCell) iterator.next();
2822 maxRowspan = Math.max(ctx.currentRowspan(cell), maxRowspan);
2823 }
2824 rowIndex += maxRowspan;
2825
2826 boolean useTop = true;
2827 if (rowIndex == rows.size()) {
2828 rowIndex = rows.size() - 1;
2829 useTop = false;
2830 }
2831
2832 if (rowIndex < 0 || rowIndex >= rows.size()) return;
2833
2834 row = (ArrayList) rows.get(rowIndex);
2835 iterator = row.iterator();
2836 while (iterator.hasNext()) {
2837 PdfCell cell = (PdfCell) iterator.next();
2838 Rectangle cellRect = cell.rectangle(ctx.pagetop, indentBottom());
2839 if (useTop) {
2840 ctx.maxCellBottom = Math.max(ctx.maxCellBottom, cellRect.getTop());
2841 } else {
2842 if (ctx.currentRowspan(cell) == 1) {
2843 ctx.maxCellBottom = Math.max(ctx.maxCellBottom, cellRect.getBottom());
2844 }
2845 }
2846 }
2847 }
2848
2849 protected boolean mayBeRemoved(ArrayList row) {
2850 Iterator iterator = row.iterator();
2851 boolean mayBeRemoved = true;
2852 while (iterator.hasNext()) {
2853 PdfCell cell = (PdfCell) iterator.next();
2854
2855 mayBeRemoved &= cell.mayBeRemoved();
2856 }
2857 return mayBeRemoved;
2858 }
2859
2860 protected void consumeRowspan(ArrayList row, RenderingContext ctx) {
2861 Iterator iterator = row.iterator();
2862 while (iterator.hasNext()) {
2863 PdfCell c = (PdfCell) iterator.next();
2864 ctx.consumeRowspan(c);
2865 }
2866 }
2867
2868 protected ArrayList extractRows(ArrayList cells, RenderingContext ctx) {
2869 PdfCell cell;
2870 PdfCell previousCell = null;
2871 ArrayList rows = new ArrayList();
2872 java.util.List rowCells = new ArrayList();
2873
2874 Iterator iterator = cells.iterator();
2875 while (iterator.hasNext()) {
2876 cell = (PdfCell) iterator.next();
2877
2878 boolean isAdded = false;
2879
2880 boolean isEndOfRow = !iterator.hasNext();
2881 boolean isCurrentCellPartOfRow = !iterator.hasNext();
2882
2883 if (previousCell != null) {
2884 if (cell.getLeft() <= previousCell.getLeft()) {
2885 isEndOfRow = true;
2886 isCurrentCellPartOfRow = false;
2887 }
2888 }
2889
2890 if (isCurrentCellPartOfRow) {
2891 rowCells.add(cell);
2892 isAdded = true;
2893 }
2894
2895 if (isEndOfRow) {
2896 if (!rowCells.isEmpty()) {
2897 // add to rowlist
2898 rows.add(rowCells);
2899 }
2900
2901 // start a new list for next line
2902 rowCells = new ArrayList();
2903 }
2904
2905 if (!isAdded) {
2906 rowCells.add(cell);
2907 }
2908
2909 previousCell = cell;
2910 }
2911
2912 if (!rowCells.isEmpty()) {
2913 rows.add(rowCells);
2914 }
2915
2916 // fill row information with rowspan cells to get complete "scan lines"
2917 for (int i = rows.size() - 1; i >= 0; i--) {
2918 ArrayList row = (ArrayList) rows.get(i);
2919 // iterator through row
2920 for (int j = 0; j < row.size(); j++) {
2921 PdfCell c = (PdfCell) row.get(j);
2922 int rowspan = c.rowspan();
2923 // fill in missing rowspan cells to complete "scan line"
2924 for (int k = 1; k < rowspan && rows.size() < i+k; k++) {
2925 ArrayList spannedRow = ((ArrayList) rows.get(i + k));
2926 if (spannedRow.size() > j)
2927 spannedRow.add(j, c);
2928 }
2929 }
2930 }
2931
2932 return rows;
2933 }
2934
2935 protected void renderCells(RenderingContext ctx, java.util.List cells, boolean hasToFit) throws DocumentException {
2936 PdfCell cell;
2937 Iterator iterator;
2938 if (hasToFit) {
2939 iterator = cells.iterator();
2940 while (iterator.hasNext()) {
2941 cell = (PdfCell) iterator.next();
2942 if (!cell.isHeader()) {
2943 if (cell.getBottom() < indentBottom()) return;
2944 }
2945 }
2946 }
2947 iterator = cells.iterator();
2948
2949 while (iterator.hasNext()) {
2950 cell = (PdfCell) iterator.next();
2951 if (!ctx.isCellRenderedOnPage(cell, getPageNumber())) {
2952
2953 float correction = 0;
2954 if (ctx.numCellRendered(cell) >= 1) {
2955 correction = 1.0f;
2956 }
2957
2958 lines = cell.getLines(ctx.pagetop, indentBottom() - correction);
2959
2960 // if there is still text to render we render it
2961 if (lines != null && !lines.isEmpty()) {
2962 // we write the text
2963 float cellTop = cell.getTop(ctx.pagetop - ctx.oldHeight);
2964 text.moveText(0, cellTop);
2965 float cellDisplacement = flushLines() - cellTop;
2966
2967 text.moveText(0, cellDisplacement);
2968 if (ctx.oldHeight + cellDisplacement > currentHeight) {
2969 currentHeight = ctx.oldHeight + cellDisplacement;
2970 }
2971
2972 ctx.cellRendered(cell, getPageNumber());
2973 }
2974 float indentBottom = Math.max(cell.getBottom(), indentBottom());
2975 Rectangle tableRect = ctx.table.rectangle(ctx.pagetop, indentBottom());
2976 indentBottom = Math.max(tableRect.getBottom(), indentBottom);
2977
2978 // we paint the borders of the cells
2979 Rectangle cellRect = cell.rectangle(tableRect.getTop(), indentBottom);
2980 //cellRect.setBottom(cellRect.bottom());
2981 if (cellRect.getHeight() > 0) {
2982 ctx.lostTableBottom = indentBottom;
2983 ctx.cellGraphics.rectangle(cellRect);
2984 }
2985
2986 // and additional graphics
2987 ArrayList images = cell.getImages(ctx.pagetop, indentBottom());
2988 for (Iterator i = images.iterator(); i.hasNext();) {
2989 Image image = (Image) i.next();
2990 graphics.addImage(image);
2991 }
2992
2993 }
2994 }
2995 }
2996
2997 /**
2998 * Returns the bottomvalue of a <CODE>Table</CODE> if it were added to this document.
2999 *
3000 * @param table the table that may or may not be added to this document
3001 * @return a bottom value
3002 */
3003 float bottom(Table table) {
3004 // constructing a PdfTable
3005 PdfTable tmp = new PdfTable(table, indentLeft(), indentRight(), indentTop() - currentHeight);
3006 return tmp.getBottom();
3007 }
3008
3009 // [M5] header/footer
3010 protected void doFooter() throws DocumentException {
3011 if (footer == null) return;
3012 // Begin added by Edgar Leonardo Prieto Perilla
3013 // Avoid footer indentation
3014 float tmpIndentLeft = indentation.indentLeft;
3015 float tmpIndentRight = indentation.indentRight;
3016 // Begin added: Bonf (Marc Schneider) 2003-07-29
3017 float tmpListIndentLeft = indentation.listIndentLeft;
3018 float tmpImageIndentLeft = indentation.imageIndentLeft;
3019 float tmpImageIndentRight = indentation.imageIndentRight;
3020 // End added: Bonf (Marc Schneider) 2003-07-29
3021
3022 indentation.indentLeft = indentation.indentRight = 0;
3023 // Begin added: Bonf (Marc Schneider) 2003-07-29
3024 indentation.listIndentLeft = 0;
3025 indentation.imageIndentLeft = 0;
3026 indentation.imageIndentRight = 0;
3027 // End added: Bonf (Marc Schneider) 2003-07-29
3028 // End Added by Edgar Leonardo Prieto Perilla
3029 footer.setPageNumber(pageN);
3030 leading = footer.paragraph().getTotalLeading();
3031 add(footer.paragraph());
3032 // adding the footer limits the height
3033 indentation.indentBottom = currentHeight;
3034 text.moveText(left(), indentBottom());
3035 flushLines();
3036 text.moveText(-left(), -bottom());
3037 footer.setTop(bottom(currentHeight));
3038 footer.setBottom(bottom() - (0.75f * leading));
3039 footer.setLeft(left());
3040 footer.setRight(right());
3041 graphics.rectangle(footer);
3042 indentation.indentBottom = currentHeight + leading * 2;
3043 currentHeight = 0;
3044 // Begin added by Edgar Leonardo Prieto Perilla
3045 indentation.indentLeft = tmpIndentLeft;
3046 indentation.indentRight = tmpIndentRight;
3047 // Begin added: Bonf (Marc Schneider) 2003-07-29
3048 indentation.listIndentLeft = tmpListIndentLeft;
3049 indentation.imageIndentLeft = tmpImageIndentLeft;
3050 indentation.imageIndentRight = tmpImageIndentRight;
3051 // End added: Bonf (Marc Schneider) 2003-07-29
3052 // End added by Edgar Leonardo Prieto Perilla
3053 }
3054
3055 protected void doHeader() throws DocumentException {
3056 // if there is a header, the header = added
3057 if (header == null) return;
3058 // Begin added by Edgar Leonardo Prieto Perilla
3059 // Avoid header indentation
3060 float tmpIndentLeft = indentation.indentLeft;
3061 float tmpIndentRight = indentation.indentRight;
3062 // Begin added: Bonf (Marc Schneider) 2003-07-29
3063 float tmpListIndentLeft = indentation.listIndentLeft;
3064 float tmpImageIndentLeft = indentation.imageIndentLeft;
3065 float tmpImageIndentRight = indentation.imageIndentRight;
3066 // End added: Bonf (Marc Schneider) 2003-07-29
3067 indentation.indentLeft = indentation.indentRight = 0;
3068 // Added: Bonf
3069 indentation.listIndentLeft = 0;
3070 indentation.imageIndentLeft = 0;
3071 indentation.imageIndentRight = 0;
3072 // End added: Bonf
3073 // Begin added by Edgar Leonardo Prieto Perilla
3074 header.setPageNumber(pageN);
3075 leading = header.paragraph().getTotalLeading();
3076 text.moveText(0, leading);
3077 add(header.paragraph());
3078 newLine();
3079 indentation.indentTop = currentHeight - leading;
3080 header.setTop(top() + leading);
3081 header.setBottom(indentTop() + leading * 2 / 3);
3082 header.setLeft(left());
3083 header.setRight(right());
3084 graphics.rectangle(header);
3085 flushLines();
3086 currentHeight = 0;
3087 // Begin added by Edgar Leonardo Prieto Perilla
3088 // Restore indentation
3089 indentation.indentLeft = tmpIndentLeft;
3090 indentation.indentRight = tmpIndentRight;
3091 // Begin added: Bonf (Marc Schneider) 2003-07-29
3092 indentation.listIndentLeft = tmpListIndentLeft;
3093 indentation.imageIndentLeft = tmpImageIndentLeft;
3094 indentation.imageIndentRight = tmpImageIndentRight;
3095 // End added: Bonf (Marc Schneider) 2003-07-29
3096 // End Added by Edgar Leonardo Prieto Perilla
3097 }
3098 }