0001: /*
0002: * $Name$
0003: * $Id: PdfDocument.java 2903 2007-08-29 14:29:07Z blowagie $
0004: *
0005: * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
0006: *
0007: * The contents of this file are subject to the Mozilla Public License Version 1.1
0008: * (the "License"); you may not use this file except in compliance with the License.
0009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the License.
0014: *
0015: * The Original Code is 'iText, a free JAVA-PDF library'.
0016: *
0017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0019: * All Rights Reserved.
0020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0022: *
0023: * Contributor(s): all the names of the contributors are added in the source code
0024: * where applicable.
0025: *
0026: * Alternatively, the contents of this file may be used under the terms of the
0027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0028: * provisions of LGPL are applicable instead of those above. If you wish to
0029: * allow use of your version of this file only under the terms of the LGPL
0030: * License and not to allow others to use your version of this file under
0031: * the MPL, indicate your decision by deleting the provisions above and
0032: * replace them with the notice and other provisions required by the LGPL.
0033: * If you do not delete the provisions above, a recipient may use your version
0034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
0035: *
0036: * This library is free software; you can redistribute it and/or modify it
0037: * under the terms of the MPL as stated above or under the terms of the GNU
0038: * Library General Public License as published by the Free Software Foundation;
0039: * either version 2 of the License, or any later version.
0040: *
0041: * This library is distributed in the hope that it will be useful, but WITHOUT
0042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
0044: * details.
0045: *
0046: * If you didn't download this code from the following link, you should check if
0047: * you aren't using an obsolete version:
0048: * http://www.lowagie.com/iText/
0049: */
0050:
0051: package com.lowagie.text.pdf;
0052:
0053: import java.awt.Color;
0054: import java.io.IOException;
0055: import java.util.ArrayList;
0056: import java.util.HashMap;
0057: import java.util.HashSet;
0058: import java.util.Iterator;
0059: import java.util.LinkedHashMap;
0060: import java.util.Map;
0061: import java.util.Set;
0062: import java.util.TreeMap;
0063:
0064: import com.lowagie.text.Anchor;
0065: import com.lowagie.text.Annotation;
0066: import com.lowagie.text.BadElementException;
0067: import com.lowagie.text.Chunk;
0068: import com.lowagie.text.Document;
0069: import com.lowagie.text.DocumentException;
0070: import com.lowagie.text.Element;
0071: import com.lowagie.text.ExceptionConverter;
0072: import com.lowagie.text.Font;
0073: import com.lowagie.text.HeaderFooter;
0074: import com.lowagie.text.Image;
0075: import com.lowagie.text.List;
0076: import com.lowagie.text.ListItem;
0077: import com.lowagie.text.MarkedObject;
0078: import com.lowagie.text.MarkedSection;
0079: import com.lowagie.text.Meta;
0080: import com.lowagie.text.Paragraph;
0081: import com.lowagie.text.Phrase;
0082: import com.lowagie.text.Rectangle;
0083: import com.lowagie.text.Section;
0084: import com.lowagie.text.SimpleTable;
0085: import com.lowagie.text.Table;
0086: import com.lowagie.text.pdf.collection.PdfCollection;
0087: import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
0088: import com.lowagie.text.pdf.internal.PdfViewerPreferencesImp;
0089:
0090: /**
0091: * <CODE>PdfDocument</CODE> is the class that is used by <CODE>PdfWriter</CODE>
0092: * to translate a <CODE>Document</CODE> into a PDF with different pages.
0093: * <P>
0094: * A <CODE>PdfDocument</CODE> always listens to a <CODE>Document</CODE>
0095: * and adds the Pdf representation of every <CODE>Element</CODE> that is
0096: * added to the <CODE>Document</CODE>.
0097: *
0098: * @see com.lowagie.text.Document
0099: * @see com.lowagie.text.DocListener
0100: * @see PdfWriter
0101: */
0102:
0103: class PdfDocument extends Document {
0104:
0105: /**
0106: * <CODE>PdfInfo</CODE> is the PDF InfoDictionary.
0107: * <P>
0108: * A document's trailer may contain a reference to an Info dictionary that provides information
0109: * about the document. This optional dictionary may contain one or more keys, whose values
0110: * should be strings.<BR>
0111: * This object is described in the 'Portable Document Format Reference Manual version 1.3'
0112: * section 6.10 (page 120-121)
0113: */
0114:
0115: public static class PdfInfo extends PdfDictionary {
0116:
0117: /**
0118: * Construct a <CODE>PdfInfo</CODE>-object.
0119: */
0120:
0121: PdfInfo() {
0122: super ();
0123: addProducer();
0124: addCreationDate();
0125: }
0126:
0127: /**
0128: * Constructs a <CODE>PdfInfo</CODE>-object.
0129: *
0130: * @param author name of the author of the document
0131: * @param title title of the document
0132: * @param subject subject of the document
0133: */
0134:
0135: PdfInfo(String author, String title, String subject) {
0136: this ();
0137: addTitle(title);
0138: addSubject(subject);
0139: addAuthor(author);
0140: }
0141:
0142: /**
0143: * Adds the title of the document.
0144: *
0145: * @param title the title of the document
0146: */
0147:
0148: void addTitle(String title) {
0149: put(PdfName.TITLE, new PdfString(title,
0150: PdfObject.TEXT_UNICODE));
0151: }
0152:
0153: /**
0154: * Adds the subject to the document.
0155: *
0156: * @param subject the subject of the document
0157: */
0158:
0159: void addSubject(String subject) {
0160: put(PdfName.SUBJECT, new PdfString(subject,
0161: PdfObject.TEXT_UNICODE));
0162: }
0163:
0164: /**
0165: * Adds some keywords to the document.
0166: *
0167: * @param keywords the keywords of the document
0168: */
0169:
0170: void addKeywords(String keywords) {
0171: put(PdfName.KEYWORDS, new PdfString(keywords,
0172: PdfObject.TEXT_UNICODE));
0173: }
0174:
0175: /**
0176: * Adds the name of the author to the document.
0177: *
0178: * @param author the name of the author
0179: */
0180:
0181: void addAuthor(String author) {
0182: put(PdfName.AUTHOR, new PdfString(author,
0183: PdfObject.TEXT_UNICODE));
0184: }
0185:
0186: /**
0187: * Adds the name of the creator to the document.
0188: *
0189: * @param creator the name of the creator
0190: */
0191:
0192: void addCreator(String creator) {
0193: put(PdfName.CREATOR, new PdfString(creator,
0194: PdfObject.TEXT_UNICODE));
0195: }
0196:
0197: /**
0198: * Adds the name of the producer to the document.
0199: */
0200:
0201: void addProducer() {
0202: put(PdfName.PRODUCER, new PdfString(getVersion()));
0203: }
0204:
0205: /**
0206: * Adds the date of creation to the document.
0207: */
0208:
0209: void addCreationDate() {
0210: PdfString date = new PdfDate();
0211: put(PdfName.CREATIONDATE, date);
0212: put(PdfName.MODDATE, date);
0213: }
0214:
0215: void addkey(String key, String value) {
0216: if (key.equals("Producer") || key.equals("CreationDate"))
0217: return;
0218: put(new PdfName(key), new PdfString(value,
0219: PdfObject.TEXT_UNICODE));
0220: }
0221: }
0222:
0223: /**
0224: * <CODE>PdfCatalog</CODE> is the PDF Catalog-object.
0225: * <P>
0226: * The Catalog is a dictionary that is the root node of the document. It contains a reference
0227: * to the tree of pages contained in the document, a reference to the tree of objects representing
0228: * the document's outline, a reference to the document's article threads, and the list of named
0229: * destinations. In addition, the Catalog indicates whether the document's outline or thumbnail
0230: * page images should be displayed automatically when the document is viewed and wether some location
0231: * other than the first page should be shown when the document is opened.<BR>
0232: * In this class however, only the reference to the tree of pages is implemented.<BR>
0233: * This object is described in the 'Portable Document Format Reference Manual version 1.3'
0234: * section 6.2 (page 67-71)
0235: */
0236:
0237: static class PdfCatalog extends PdfDictionary {
0238:
0239: /** The writer writing the PDF for which we are creating this catalog object. */
0240: PdfWriter writer;
0241:
0242: /**
0243: * Constructs a <CODE>PdfCatalog</CODE>.
0244: *
0245: * @param pages an indirect reference to the root of the document's Pages tree.
0246: * @param writer the writer the catalog applies to
0247: */
0248:
0249: PdfCatalog(PdfIndirectReference pages, PdfWriter writer) {
0250: super (CATALOG);
0251: this .writer = writer;
0252: put(PdfName.PAGES, pages);
0253: }
0254:
0255: /**
0256: * Adds the names of the named destinations to the catalog.
0257: * @param localDestinations the local destinations
0258: * @param documentLevelJS the javascript used in the document
0259: * @param documentFileAttachment the attached files
0260: * @param writer the writer the catalog applies to
0261: */
0262: void addNames(TreeMap localDestinations,
0263: LinkedHashMap documentLevelJS,
0264: HashMap documentFileAttachment, PdfWriter writer) {
0265: if (localDestinations.isEmpty()
0266: && documentLevelJS.isEmpty()
0267: && documentFileAttachment.isEmpty())
0268: return;
0269: try {
0270: PdfDictionary names = new PdfDictionary();
0271: if (!localDestinations.isEmpty()) {
0272: PdfArray ar = new PdfArray();
0273: for (Iterator i = localDestinations.entrySet()
0274: .iterator(); i.hasNext();) {
0275: Map.Entry entry = (Map.Entry) i.next();
0276: String name = (String) entry.getKey();
0277: Object obj[] = (Object[]) entry.getValue();
0278: PdfIndirectReference ref = (PdfIndirectReference) obj[1];
0279: ar.add(new PdfString(name, null));
0280: ar.add(ref);
0281: }
0282: PdfDictionary dests = new PdfDictionary();
0283: dests.put(PdfName.NAMES, ar);
0284: names.put(PdfName.DESTS, writer.addToBody(dests)
0285: .getIndirectReference());
0286: }
0287: if (!documentLevelJS.isEmpty()) {
0288: PdfDictionary tree = PdfNameTree.writeTree(
0289: documentLevelJS, writer);
0290: names.put(PdfName.JAVASCRIPT, writer
0291: .addToBody(tree).getIndirectReference());
0292: }
0293: if (!documentFileAttachment.isEmpty()) {
0294: names.put(PdfName.EMBEDDEDFILES, writer.addToBody(
0295: PdfNameTree.writeTree(
0296: documentFileAttachment, writer))
0297: .getIndirectReference());
0298: }
0299: put(PdfName.NAMES, writer.addToBody(names)
0300: .getIndirectReference());
0301: } catch (IOException e) {
0302: throw new ExceptionConverter(e);
0303: }
0304: }
0305:
0306: /**
0307: * Adds an open action to the catalog.
0308: * @param action the action that will be triggered upon opening the document
0309: */
0310: void setOpenAction(PdfAction action) {
0311: put(PdfName.OPENACTION, action);
0312: }
0313:
0314: /**
0315: * Sets the document level additional actions.
0316: * @param actions dictionary of actions
0317: */
0318: void setAdditionalActions(PdfDictionary actions) {
0319: try {
0320: put(PdfName.AA, writer.addToBody(actions)
0321: .getIndirectReference());
0322: } catch (Exception e) {
0323: throw new ExceptionConverter(e);
0324: }
0325: }
0326: }
0327:
0328: // CONSTRUCTING A PdfDocument/PdfWriter INSTANCE
0329:
0330: /**
0331: * Constructs a new PDF document.
0332: * @throws DocumentException on error
0333: */
0334: public PdfDocument() {
0335: super ();
0336: addProducer();
0337: addCreationDate();
0338: }
0339:
0340: /** The <CODE>PdfWriter</CODE>. */
0341: private PdfWriter writer;
0342:
0343: /**
0344: * Adds a <CODE>PdfWriter</CODE> to the <CODE>PdfDocument</CODE>.
0345: *
0346: * @param writer the <CODE>PdfWriter</CODE> that writes everything
0347: * what is added to this document to an outputstream.
0348: * @throws DocumentException on error
0349: */
0350: public void addWriter(PdfWriter writer) throws DocumentException {
0351: if (this .writer == null) {
0352: this .writer = writer;
0353: annotationsImp = new PdfAnnotationsImp(writer);
0354: return;
0355: }
0356: throw new DocumentException(
0357: "You can only add a writer to a PdfDocument once.");
0358: }
0359:
0360: // LISTENER METHODS START
0361:
0362: // [L0] ElementListener interface
0363:
0364: /** This is the PdfContentByte object, containing the text. */
0365: private PdfContentByte text;
0366:
0367: /** This is the PdfContentByte object, containing the borders and other Graphics. */
0368: private PdfContentByte graphics;
0369:
0370: /** This represents the leading of the lines. */
0371: private float leading = 0;
0372:
0373: /** This represents the current alignment of the PDF Elements. */
0374: private int alignment = Element.ALIGN_LEFT;
0375:
0376: /** This is the current height of the document. */
0377: private float currentHeight = 0;
0378:
0379: /** Signals that onParagraph is valid (to avoid that a Chapter/Section title is treated as a Paragraph). */
0380: private boolean isParagraph = true;
0381:
0382: /** The current active <CODE>PdfAction</CODE> when processing an <CODE>Anchor</CODE>. */
0383: private PdfAction anchorAction = null;
0384:
0385: /**
0386: * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>.
0387: *
0388: * @param element the element to add
0389: * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not.
0390: * @throws DocumentException when a document isn't open yet, or has been closed
0391: */
0392: public boolean add(Element element) throws DocumentException {
0393: if (writer != null && writer.isPaused()) {
0394: return false;
0395: }
0396: try {
0397: switch (element.type()) {
0398: // Information (headers)
0399: case Element.HEADER:
0400: info.addkey(((Meta) element).getName(),
0401: ((Meta) element).getContent());
0402: break;
0403: case Element.TITLE:
0404: info.addTitle(((Meta) element).getContent());
0405: break;
0406: case Element.SUBJECT:
0407: info.addSubject(((Meta) element).getContent());
0408: break;
0409: case Element.KEYWORDS:
0410: info.addKeywords(((Meta) element).getContent());
0411: break;
0412: case Element.AUTHOR:
0413: info.addAuthor(((Meta) element).getContent());
0414: break;
0415: case Element.CREATOR:
0416: info.addCreator(((Meta) element).getContent());
0417: break;
0418: case Element.PRODUCER:
0419: // you can not change the name of the producer
0420: info.addProducer();
0421: break;
0422: case Element.CREATIONDATE:
0423: // you can not set the creation date, only reset it
0424: info.addCreationDate();
0425: break;
0426:
0427: // content (text)
0428: case Element.CHUNK: {
0429: // if there isn't a current line available, we make one
0430: if (line == null) {
0431: carriageReturn();
0432: }
0433:
0434: // we cast the element to a chunk
0435: PdfChunk chunk = new PdfChunk((Chunk) element,
0436: anchorAction);
0437: // we try to add the chunk to the line, until we succeed
0438: {
0439: PdfChunk overflow;
0440: while ((overflow = line.add(chunk)) != null) {
0441: carriageReturn();
0442: chunk = overflow;
0443: chunk.trimFirstSpace();
0444: }
0445: }
0446: pageEmpty = false;
0447: if (chunk.isAttribute(Chunk.NEWPAGE)) {
0448: newPage();
0449: }
0450: break;
0451: }
0452: case Element.ANCHOR: {
0453: Anchor anchor = (Anchor) element;
0454: String url = anchor.getReference();
0455: leading = anchor.getLeading();
0456: if (url != null) {
0457: anchorAction = new PdfAction(url);
0458: }
0459: // we process the element
0460: element.process(this );
0461: anchorAction = null;
0462: break;
0463: }
0464: case Element.ANNOTATION: {
0465: if (line == null) {
0466: carriageReturn();
0467: }
0468: Annotation annot = (Annotation) element;
0469: Rectangle rect = new Rectangle(0, 0);
0470: if (line != null)
0471: rect = new Rectangle(annot.llx(indentRight()
0472: - line.widthLeft()), annot.lly(indentTop()
0473: - currentHeight), annot.urx(indentRight()
0474: - line.widthLeft() + 20), annot
0475: .ury(indentTop() - currentHeight - 20));
0476: PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(
0477: writer, annot, rect);
0478: annotationsImp.addPlainAnnotation(an);
0479: pageEmpty = false;
0480: break;
0481: }
0482: case Element.PHRASE: {
0483: // we cast the element to a phrase and set the leading of the document
0484: leading = ((Phrase) element).getLeading();
0485: // we process the element
0486: element.process(this );
0487: break;
0488: }
0489: case Element.PARAGRAPH: {
0490: // we cast the element to a paragraph
0491: Paragraph paragraph = (Paragraph) element;
0492:
0493: addSpacing(paragraph.spacingBefore(), leading,
0494: paragraph.getFont());
0495:
0496: // we adjust the parameters of the document
0497: alignment = paragraph.getAlignment();
0498: leading = paragraph.getTotalLeading();
0499: carriageReturn();
0500:
0501: // we don't want to make orphans/widows
0502: if (currentHeight + line.height() + leading > indentTop()
0503: - indentBottom()) {
0504: newPage();
0505: }
0506:
0507: indentation.indentLeft += paragraph
0508: .getIndentationLeft();
0509: indentation.indentRight += paragraph
0510: .getIndentationRight();
0511: carriageReturn();
0512:
0513: PdfPageEvent pageEvent = writer.getPageEvent();
0514: if (pageEvent != null && isParagraph)
0515: pageEvent.onParagraph(writer, this , indentTop()
0516: - currentHeight);
0517:
0518: // if a paragraph has to be kept together, we wrap it in a table object
0519: if (paragraph.getKeepTogether()) {
0520: carriageReturn();
0521: PdfPTable table = new PdfPTable(1);
0522: table.setWidthPercentage(100f);
0523: PdfPCell cell = new PdfPCell();
0524: cell.addElement(paragraph);
0525: cell.setBorder(Table.NO_BORDER);
0526: cell.setPadding(0);
0527: table.addCell(cell);
0528: indentation.indentLeft -= paragraph
0529: .getIndentationLeft();
0530: indentation.indentRight -= paragraph
0531: .getIndentationRight();
0532: this .add(table);
0533: indentation.indentLeft += paragraph
0534: .getIndentationLeft();
0535: indentation.indentRight += paragraph
0536: .getIndentationRight();
0537: } else {
0538: line.setExtraIndent(paragraph.getFirstLineIndent());
0539: element.process(this );
0540: carriageReturn();
0541: addSpacing(paragraph.spacingAfter(), paragraph
0542: .getTotalLeading(), paragraph.getFont());
0543: }
0544:
0545: if (pageEvent != null && isParagraph)
0546: pageEvent.onParagraphEnd(writer, this , indentTop()
0547: - currentHeight);
0548:
0549: alignment = Element.ALIGN_LEFT;
0550: indentation.indentLeft -= paragraph
0551: .getIndentationLeft();
0552: indentation.indentRight -= paragraph
0553: .getIndentationRight();
0554: carriageReturn();
0555: break;
0556: }
0557: case Element.SECTION:
0558: case Element.CHAPTER: {
0559: // Chapters and Sections only differ in their constructor
0560: // so we cast both to a Section
0561: Section section = (Section) element;
0562:
0563: boolean hasTitle = section.getTitle() != null;
0564:
0565: // if the section is a chapter, we begin a new page
0566: if (section.isTriggerNewPage()) {
0567: newPage();
0568: }
0569: // otherwise, we begin a new line
0570: else {
0571: newLine();
0572: }
0573:
0574: if (hasTitle) {
0575: float fith = indentTop() - currentHeight;
0576: int rotation = pageSize.getRotation();
0577: if (rotation == 90 || rotation == 180)
0578: fith = pageSize.getHeight() - fith;
0579: PdfDestination destination = new PdfDestination(
0580: PdfDestination.FITH, fith);
0581: while (currentOutline.level() >= section.getDepth()) {
0582: currentOutline = currentOutline.parent();
0583: }
0584: PdfOutline outline = new PdfOutline(currentOutline,
0585: destination, section.getBookmarkTitle(),
0586: section.isBookmarkOpen());
0587: currentOutline = outline;
0588: }
0589:
0590: // some values are set
0591: carriageReturn();
0592: indentation.sectionIndentLeft += section
0593: .getIndentationLeft();
0594: indentation.sectionIndentRight += section
0595: .getIndentationRight();
0596:
0597: PdfPageEvent pageEvent = writer.getPageEvent();
0598: if (pageEvent != null)
0599: if (element.type() == Element.CHAPTER)
0600: pageEvent.onChapter(writer, this , indentTop()
0601: - currentHeight, section.getTitle());
0602: else
0603: pageEvent.onSection(writer, this , indentTop()
0604: - currentHeight, section.getDepth(),
0605: section.getTitle());
0606:
0607: // the title of the section (if any has to be printed)
0608: if (hasTitle) {
0609: isParagraph = false;
0610: add(section.getTitle());
0611: isParagraph = true;
0612: }
0613: indentation.sectionIndentLeft += section
0614: .getIndentation();
0615: // we process the section
0616: element.process(this );
0617: flushLines();
0618: // some parameters are set back to normal again
0619: indentation.sectionIndentLeft -= (section
0620: .getIndentationLeft() + section
0621: .getIndentation());
0622: indentation.sectionIndentRight -= section
0623: .getIndentationRight();
0624:
0625: if (pageEvent != null)
0626: if (element.type() == Element.CHAPTER)
0627: pageEvent.onChapterEnd(writer, this ,
0628: indentTop() - currentHeight);
0629: else
0630: pageEvent.onSectionEnd(writer, this ,
0631: indentTop() - currentHeight);
0632:
0633: break;
0634: }
0635: case Element.LIST: {
0636: // we cast the element to a List
0637: List list = (List) element;
0638: if (list.isAlignindent()) {
0639: list.normalizeIndentation();
0640: }
0641: // we adjust the document
0642: indentation.listIndentLeft += list.getIndentationLeft();
0643: indentation.indentRight += list.getIndentationRight();
0644: // we process the items in the list
0645: element.process(this );
0646: // some parameters are set back to normal again
0647: indentation.listIndentLeft -= list.getIndentationLeft();
0648: indentation.indentRight -= list.getIndentationRight();
0649: carriageReturn();
0650: break;
0651: }
0652: case Element.LISTITEM: {
0653: // we cast the element to a ListItem
0654: ListItem listItem = (ListItem) element;
0655:
0656: addSpacing(listItem.spacingBefore(), leading, listItem
0657: .getFont());
0658:
0659: // we adjust the document
0660: alignment = listItem.getAlignment();
0661: indentation.listIndentLeft += listItem
0662: .getIndentationLeft();
0663: indentation.indentRight += listItem
0664: .getIndentationRight();
0665: leading = listItem.getTotalLeading();
0666: carriageReturn();
0667:
0668: // we prepare the current line to be able to show us the listsymbol
0669: line.setListItem(listItem);
0670: // we process the item
0671: element.process(this );
0672:
0673: addSpacing(listItem.spacingAfter(), listItem
0674: .getTotalLeading(), listItem.getFont());
0675:
0676: // if the last line is justified, it should be aligned to the left
0677: if (line.hasToBeJustified()) {
0678: line.resetAlignment();
0679: }
0680: // some parameters are set back to normal again
0681: carriageReturn();
0682: indentation.listIndentLeft -= listItem
0683: .getIndentationLeft();
0684: indentation.indentRight -= listItem
0685: .getIndentationRight();
0686: break;
0687: }
0688: case Element.RECTANGLE: {
0689: Rectangle rectangle = (Rectangle) element;
0690: graphics.rectangle(rectangle);
0691: pageEmpty = false;
0692: break;
0693: }
0694: case Element.PTABLE: {
0695: PdfPTable ptable = (PdfPTable) element;
0696: if (ptable.size() <= ptable.getHeaderRows())
0697: break; //nothing to do
0698:
0699: // before every table, we add a new line and flush all lines
0700: ensureNewLine();
0701: flushLines();
0702:
0703: addPTable(ptable);
0704: pageEmpty = false;
0705: newLine();
0706: break;
0707: }
0708: case Element.MULTI_COLUMN_TEXT: {
0709: ensureNewLine();
0710: flushLines();
0711: MultiColumnText multiText = (MultiColumnText) element;
0712: float height = multiText.write(writer
0713: .getDirectContent(), this , indentTop()
0714: - currentHeight);
0715: currentHeight += height;
0716: text.moveText(0, -1f * height);
0717: pageEmpty = false;
0718: break;
0719: }
0720: case Element.TABLE: {
0721: PdfTable table;
0722: if (element instanceof PdfTable) {
0723: // Already pre-rendered
0724: table = (PdfTable) element;
0725: table.updateRowAdditions();
0726: } else if (element instanceof SimpleTable) {
0727: PdfPTable ptable = ((SimpleTable) element)
0728: .createPdfPTable();
0729: if (ptable.size() <= ptable.getHeaderRows())
0730: break; //nothing to do
0731:
0732: // before every table, we add a new line and flush all lines
0733: ensureNewLine();
0734: flushLines();
0735: addPTable(ptable);
0736: pageEmpty = false;
0737: break;
0738: } else if (element instanceof Table) {
0739: try {
0740: PdfPTable ptable = ((Table) element)
0741: .createPdfPTable();
0742: if (ptable.size() <= ptable.getHeaderRows())
0743: break; //nothing to do
0744:
0745: // before every table, we add a new line and flush all lines
0746: ensureNewLine();
0747: flushLines();
0748: addPTable(ptable);
0749: pageEmpty = false;
0750: break;
0751: } catch (BadElementException bee) {
0752: // constructing the PdfTable
0753: // Before the table, add a blank line using offset or default leading
0754: float offset = ((Table) element).getOffset();
0755: if (Float.isNaN(offset))
0756: offset = leading;
0757: carriageReturn();
0758: lines.add(new PdfLine(indentLeft(),
0759: indentRight(), alignment, offset));
0760: currentHeight += offset;
0761: table = getPdfTable((Table) element, false);
0762: }
0763: } else {
0764: return false;
0765: }
0766: add(table, false);
0767: break;
0768: }
0769: case Element.JPEG:
0770: case Element.IMGRAW:
0771: case Element.IMGTEMPLATE: {
0772: //carriageReturn(); suggestion by Marc Campforts
0773: add((Image) element);
0774: break;
0775: }
0776: case Element.MARKED: {
0777: MarkedObject mo;
0778: if (element instanceof MarkedSection) {
0779: mo = ((MarkedSection) element).title();
0780: if (mo != null) {
0781: mo.process(this );
0782: }
0783: }
0784: mo = (MarkedObject) element;
0785: mo.process(this );
0786: break;
0787: }
0788: default:
0789: return false;
0790: }
0791: lastElementType = element.type();
0792: return true;
0793: } catch (Exception e) {
0794: throw new DocumentException(e);
0795: }
0796: }
0797:
0798: // [L1] DocListener interface
0799:
0800: /**
0801: * Opens the document.
0802: * <P>
0803: * You have to open the document before you can begin to add content
0804: * to the body of the document.
0805: */
0806: public void open() {
0807: if (!open) {
0808: super .open();
0809: writer.open();
0810: rootOutline = new PdfOutline(writer);
0811: currentOutline = rootOutline;
0812: }
0813: try {
0814: initPage();
0815: } catch (DocumentException de) {
0816: throw new ExceptionConverter(de);
0817: }
0818: }
0819:
0820: // [L2] DocListener interface
0821:
0822: /**
0823: * Closes the document.
0824: * <B>
0825: * Once all the content has been written in the body, you have to close
0826: * the body. After that nothing can be written to the body anymore.
0827: */
0828: public void close() {
0829: if (close) {
0830: return;
0831: }
0832: try {
0833: boolean wasImage = (imageWait != null);
0834: newPage();
0835: if (imageWait != null || wasImage)
0836: newPage();
0837: if (annotationsImp.hasUnusedAnnotations())
0838: throw new RuntimeException(
0839: "Not all annotations could be added to the document (the document doesn't have enough pages).");
0840: PdfPageEvent pageEvent = writer.getPageEvent();
0841: if (pageEvent != null)
0842: pageEvent.onCloseDocument(writer, this );
0843: super .close();
0844:
0845: writer.addLocalDestinations(localDestinations);
0846: calculateOutlineCount();
0847: writeOutlines();
0848: } catch (Exception e) {
0849: throw new ExceptionConverter(e);
0850: }
0851:
0852: writer.close();
0853: }
0854:
0855: // [L3] DocListener interface
0856: private int textEmptySize;
0857:
0858: // [C9] Metadata for the page
0859: /** XMP Metadata for the page. */
0860: protected byte[] xmpMetadata = null;
0861:
0862: /**
0863: * Use this method to set the XMP Metadata.
0864: * @param xmpMetadata The xmpMetadata to set.
0865: */
0866: public void setXmpMetadata(byte[] xmpMetadata) {
0867: this .xmpMetadata = xmpMetadata;
0868: }
0869:
0870: /**
0871: * Makes a new page and sends it to the <CODE>PdfWriter</CODE>.
0872: *
0873: * @return a <CODE>boolean</CODE>
0874: * @throws DocumentException on error
0875: */
0876: public boolean newPage() {
0877: lastElementType = -1;
0878: if (writer == null
0879: || (writer.getDirectContent().size() == 0
0880: && writer.getDirectContentUnder().size() == 0 && (pageEmpty || writer
0881: .isPaused()))) {
0882: setNewPageSizeAndMargins();
0883: return false;
0884: }
0885: if (!open || close) {
0886: throw new RuntimeException("The document isn't open.");
0887: }
0888: PdfPageEvent pageEvent = writer.getPageEvent();
0889: if (pageEvent != null)
0890: pageEvent.onEndPage(writer, this );
0891:
0892: //Added to inform any listeners that we are moving to a new page (added by David Freels)
0893: super .newPage();
0894:
0895: // the following 2 lines were added by Pelikan Stephan
0896: indentation.imageIndentLeft = 0;
0897: indentation.imageIndentRight = 0;
0898:
0899: try {
0900: // we flush the arraylist with recently written lines
0901: flushLines();
0902:
0903: // we prepare the elements of the page dictionary
0904:
0905: // [U1] page size and rotation
0906: int rotation = pageSize.getRotation();
0907:
0908: // [C10]
0909: if (writer.isPdfX()) {
0910: if (this BoxSize.containsKey("art")
0911: && this BoxSize.containsKey("trim"))
0912: throw new PdfXConformanceException(
0913: "Only one of ArtBox or TrimBox can exist in the page.");
0914: if (!this BoxSize.containsKey("art")
0915: && !this BoxSize.containsKey("trim")) {
0916: if (this BoxSize.containsKey("crop"))
0917: this BoxSize
0918: .put("trim", this BoxSize.get("crop"));
0919: else
0920: this BoxSize.put("trim", new PdfRectangle(
0921: pageSize, pageSize.getRotation()));
0922: }
0923: }
0924:
0925: // [M1]
0926: pageResources.addDefaultColorDiff(writer
0927: .getDefaultColorspace());
0928: PdfDictionary resources = pageResources.getResources();
0929:
0930: // we create the page dictionary
0931:
0932: PdfPage page = new PdfPage(new PdfRectangle(pageSize,
0933: rotation), this BoxSize, resources, rotation);
0934:
0935: // we complete the page dictionary
0936:
0937: // [C9] if there is XMP data to add: add it
0938: if (xmpMetadata != null) {
0939: PdfStream xmp = new PdfStream(xmpMetadata);
0940: xmp.put(PdfName.TYPE, PdfName.METADATA);
0941: xmp.put(PdfName.SUBTYPE, PdfName.XML);
0942: PdfEncryption crypto = writer.getEncryption();
0943: if (crypto != null && !crypto.isMetadataEncrypted()) {
0944: PdfArray ar = new PdfArray();
0945: ar.add(PdfName.CRYPT);
0946: xmp.put(PdfName.FILTER, ar);
0947: }
0948: page.put(PdfName.METADATA, writer.addToBody(xmp)
0949: .getIndirectReference());
0950: }
0951:
0952: // [U3] page actions: transition, duration, additional actions
0953: if (this .transition != null) {
0954: page.put(PdfName.TRANS, this .transition
0955: .getTransitionDictionary());
0956: transition = null;
0957: }
0958: if (this .duration > 0) {
0959: page.put(PdfName.DUR, new PdfNumber(this .duration));
0960: duration = 0;
0961: }
0962: if (pageAA != null) {
0963: page.put(PdfName.AA, writer.addToBody(pageAA)
0964: .getIndirectReference());
0965: pageAA = null;
0966: }
0967:
0968: // [U4] we add the thumbs
0969: if (thumb != null) {
0970: page.put(PdfName.THUMB, thumb);
0971: thumb = null;
0972: }
0973:
0974: // [U8] we check if the userunit is defined
0975: if (writer.getUserunit() > 0f) {
0976: page.put(PdfName.USERUNIT, new PdfNumber(writer
0977: .getUserunit()));
0978: }
0979:
0980: // [C5] and [C8] we add the annotations
0981: if (annotationsImp.hasUnusedAnnotations()) {
0982: PdfArray array = annotationsImp.rotateAnnotations(
0983: writer, pageSize);
0984: if (array.size() != 0)
0985: page.put(PdfName.ANNOTS, array);
0986: }
0987:
0988: // [F12] we add tag info
0989: if (writer.isTagged())
0990: page.put(PdfName.STRUCTPARENTS, new PdfNumber(writer
0991: .getCurrentPageNumber() - 1));
0992:
0993: if (text.size() > textEmptySize)
0994: text.endText();
0995: else
0996: text = null;
0997: writer.add(page, new PdfContents(writer
0998: .getDirectContentUnder(), graphics, text, writer
0999: .getDirectContent(), pageSize));
1000: // we initialize the new page
1001: initPage();
1002: } catch (DocumentException de) {
1003: // maybe this never happens, but it's better to check.
1004: throw new ExceptionConverter(de);
1005: } catch (IOException ioe) {
1006: throw new ExceptionConverter(ioe);
1007: }
1008: return true;
1009: }
1010:
1011: // [L4] DocListener interface
1012:
1013: /**
1014: * Sets the pagesize.
1015: *
1016: * @param pageSize the new pagesize
1017: * @return <CODE>true</CODE> if the page size was set
1018: */
1019: public boolean setPageSize(Rectangle pageSize) {
1020: if (writer != null && writer.isPaused()) {
1021: return false;
1022: }
1023: nextPageSize = new Rectangle(pageSize);
1024: return true;
1025: }
1026:
1027: // [L5] DocListener interface
1028:
1029: /** margin in x direction starting from the left. Will be valid in the next page */
1030: protected float nextMarginLeft;
1031:
1032: /** margin in x direction starting from the right. Will be valid in the next page */
1033: protected float nextMarginRight;
1034:
1035: /** margin in y direction starting from the top. Will be valid in the next page */
1036: protected float nextMarginTop;
1037:
1038: /** margin in y direction starting from the bottom. Will be valid in the next page */
1039: protected float nextMarginBottom;
1040:
1041: /**
1042: * Sets the margins.
1043: *
1044: * @param marginLeft the margin on the left
1045: * @param marginRight the margin on the right
1046: * @param marginTop the margin on the top
1047: * @param marginBottom the margin on the bottom
1048: * @return a <CODE>boolean</CODE>
1049: */
1050: public boolean setMargins(float marginLeft, float marginRight,
1051: float marginTop, float marginBottom) {
1052: if (writer != null && writer.isPaused()) {
1053: return false;
1054: }
1055: nextMarginLeft = marginLeft;
1056: nextMarginRight = marginRight;
1057: nextMarginTop = marginTop;
1058: nextMarginBottom = marginBottom;
1059: return true;
1060: }
1061:
1062: // [L6] DocListener interface
1063:
1064: /**
1065: * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
1066: */
1067: public boolean setMarginMirroring(boolean MarginMirroring) {
1068: if (writer != null && writer.isPaused()) {
1069: return false;
1070: }
1071: return super .setMarginMirroring(MarginMirroring);
1072: }
1073:
1074: // [L7] DocListener interface
1075:
1076: /**
1077: * Sets the page number.
1078: *
1079: * @param pageN the new page number
1080: */
1081: public void setPageCount(int pageN) {
1082: if (writer != null && writer.isPaused()) {
1083: return;
1084: }
1085: super .setPageCount(pageN);
1086: }
1087:
1088: // [L8] DocListener interface
1089:
1090: /**
1091: * Sets the page number to 0.
1092: */
1093: public void resetPageCount() {
1094: if (writer != null && writer.isPaused()) {
1095: return;
1096: }
1097: super .resetPageCount();
1098: }
1099:
1100: // [L9] DocListener interface
1101:
1102: /**
1103: * Changes the header of this document.
1104: *
1105: * @param header the new header
1106: */
1107: public void setHeader(HeaderFooter header) {
1108: if (writer != null && writer.isPaused()) {
1109: return;
1110: }
1111: super .setHeader(header);
1112: }
1113:
1114: // [L10] DocListener interface
1115:
1116: /**
1117: * Resets the header of this document.
1118: */
1119: public void resetHeader() {
1120: if (writer != null && writer.isPaused()) {
1121: return;
1122: }
1123: super .resetHeader();
1124: }
1125:
1126: // [L11] DocListener interface
1127:
1128: /**
1129: * Changes the footer of this document.
1130: *
1131: * @param footer the new footer
1132: */
1133: public void setFooter(HeaderFooter footer) {
1134: if (writer != null && writer.isPaused()) {
1135: return;
1136: }
1137: super .setFooter(footer);
1138: }
1139:
1140: // [L12] DocListener interface
1141:
1142: /**
1143: * Resets the footer of this document.
1144: */
1145: public void resetFooter() {
1146: if (writer != null && writer.isPaused()) {
1147: return;
1148: }
1149: super .resetFooter();
1150: }
1151:
1152: // DOCLISTENER METHODS END
1153:
1154: /** Signals that OnOpenDocument should be called. */
1155: private boolean firstPageEvent = true;
1156:
1157: /**
1158: * Initializes a page.
1159: * <P>
1160: * If the footer/header is set, it is printed.
1161: * @throws DocumentException on error
1162: */
1163: private void initPage() throws DocumentException {
1164: // the pagenumber is incremented
1165: pageN++;
1166:
1167: // initialisation of some page objects
1168: annotationsImp.resetAnnotations();
1169: pageResources = new PageResources();
1170:
1171: writer.resetContent();
1172: graphics = new PdfContentByte(writer);
1173: text = new PdfContentByte(writer);
1174: text.reset();
1175: text.beginText();
1176: textEmptySize = text.size();
1177:
1178: markPoint = 0;
1179: setNewPageSizeAndMargins();
1180: imageEnd = -1;
1181: indentation.imageIndentRight = 0;
1182: indentation.imageIndentLeft = 0;
1183: indentation.indentBottom = 0;
1184: indentation.indentTop = 0;
1185: currentHeight = 0;
1186:
1187: // backgroundcolors, etc...
1188: this BoxSize = new HashMap(boxSize);
1189: if (pageSize.getBackgroundColor() != null
1190: || pageSize.hasBorders()
1191: || pageSize.getBorderColor() != null) {
1192: add(pageSize);
1193: }
1194:
1195: float oldleading = leading;
1196: int oldAlignment = alignment;
1197: // if there is a footer, the footer is added
1198: doFooter();
1199: // we move to the left/top position of the page
1200: text.moveText(left(), top());
1201: doHeader();
1202: pageEmpty = true;
1203: // if there is an image waiting to be drawn, draw it
1204: try {
1205: if (imageWait != null) {
1206: add(imageWait);
1207: imageWait = null;
1208: }
1209: } catch (Exception e) {
1210: throw new ExceptionConverter(e);
1211: }
1212: leading = oldleading;
1213: alignment = oldAlignment;
1214: carriageReturn();
1215:
1216: PdfPageEvent pageEvent = writer.getPageEvent();
1217: if (pageEvent != null) {
1218: if (firstPageEvent) {
1219: pageEvent.onOpenDocument(writer, this );
1220: }
1221: pageEvent.onStartPage(writer, this );
1222: }
1223: firstPageEvent = false;
1224: }
1225:
1226: /** The line that is currently being written. */
1227: private PdfLine line = null;
1228: /** The lines that are written until now. */
1229: private ArrayList lines = new ArrayList();
1230:
1231: /**
1232: * Adds the current line to the list of lines and also adds an empty line.
1233: * @throws DocumentException on error
1234: */
1235: private void newLine() throws DocumentException {
1236: lastElementType = -1;
1237: carriageReturn();
1238: if (lines != null && !lines.isEmpty()) {
1239: lines.add(line);
1240: currentHeight += line.height();
1241: }
1242: line = new PdfLine(indentLeft(), indentRight(), alignment,
1243: leading);
1244: }
1245:
1246: /**
1247: * If the current line is not empty or null, it is added to the arraylist
1248: * of lines and a new empty line is added.
1249: * @throws DocumentException on error
1250: */
1251: private void carriageReturn() {
1252: // the arraylist with lines may not be null
1253: if (lines == null) {
1254: lines = new ArrayList();
1255: }
1256: // If the current line is not null
1257: if (line != null) {
1258: // we check if the end of the page is reached (bugfix by Francois Gravel)
1259: if (currentHeight + line.height() + leading < indentTop()
1260: - indentBottom()) {
1261: // if so nonempty lines are added and the heigt is augmented
1262: if (line.size() > 0) {
1263: currentHeight += line.height();
1264: lines.add(line);
1265: pageEmpty = false;
1266: }
1267: }
1268: // if the end of the line is reached, we start a new page
1269: else {
1270: newPage();
1271: }
1272: }
1273: if (imageEnd > -1 && currentHeight > imageEnd) {
1274: imageEnd = -1;
1275: indentation.imageIndentRight = 0;
1276: indentation.imageIndentLeft = 0;
1277: }
1278: // a new current line is constructed
1279: line = new PdfLine(indentLeft(), indentRight(), alignment,
1280: leading);
1281: }
1282:
1283: /**
1284: * Gets the current vertical page position.
1285: * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
1286: * for elements that do not terminate the lines they've started because those lines will get
1287: * terminated.
1288: * @return The current vertical page position.
1289: */
1290: public float getVerticalPosition(boolean ensureNewLine) {
1291: // ensuring that a new line has been started.
1292: if (ensureNewLine) {
1293: ensureNewLine();
1294: }
1295: return top() - currentHeight - indentation.indentTop;
1296: }
1297:
1298: /** Holds the type of the last element, that has been added to the document. */
1299: private int lastElementType = -1;
1300:
1301: /**
1302: * Ensures that a new line has been started.
1303: */
1304: private void ensureNewLine() {
1305: try {
1306: if ((lastElementType == Element.PHRASE)
1307: || (lastElementType == Element.CHUNK)) {
1308: newLine();
1309: flushLines();
1310: }
1311: } catch (DocumentException ex) {
1312: throw new ExceptionConverter(ex);
1313: }
1314: }
1315:
1316: /**
1317: * Writes all the lines to the text-object.
1318: *
1319: * @return the displacement that was caused
1320: * @throws DocumentException on error
1321: */
1322: private float flushLines() throws DocumentException {
1323: // checks if the ArrayList with the lines is not null
1324: if (lines == null) {
1325: return 0;
1326: }
1327: // checks if a new Line has to be made.
1328: if (line != null && line.size() > 0) {
1329: lines.add(line);
1330: line = new PdfLine(indentLeft(), indentRight(), alignment,
1331: leading);
1332: }
1333:
1334: // checks if the ArrayList with the lines is empty
1335: if (lines.isEmpty()) {
1336: return 0;
1337: }
1338:
1339: // initialisation of some parameters
1340: Object currentValues[] = new Object[2];
1341: PdfFont currentFont = null;
1342: float displacement = 0;
1343: PdfLine l;
1344: Float lastBaseFactor = new Float(0);
1345: currentValues[1] = lastBaseFactor;
1346: // looping over all the lines
1347: for (Iterator i = lines.iterator(); i.hasNext();) {
1348:
1349: // this is a line in the loop
1350: l = (PdfLine) i.next();
1351:
1352: float moveTextX = l.indentLeft() - indentLeft()
1353: + indentation.listIndentLeft
1354: + indentation.sectionIndentLeft;
1355: text.moveText(moveTextX, -l.height());
1356:
1357: // is the line preceeded by a symbol?
1358: if (l.listSymbol() != null) {
1359: ColumnText.showTextAligned(graphics,
1360: Element.ALIGN_LEFT, new Phrase(l.listSymbol()),
1361: text.getXTLM() - l.listIndent(),
1362: text.getYTLM(), 0);
1363: }
1364:
1365: currentValues[0] = currentFont;
1366:
1367: writeLineToContent(l, text, graphics, currentValues, writer
1368: .getSpaceCharRatio());
1369:
1370: currentFont = (PdfFont) currentValues[0];
1371:
1372: displacement += l.height();
1373: text.moveText(-moveTextX, 0);
1374:
1375: }
1376: lines = new ArrayList();
1377: return displacement;
1378: }
1379:
1380: /** The characters to be applied the hanging punctuation. */
1381: static final String hangingPunctuation = ".,;:'";
1382:
1383: /**
1384: * Writes a text line to the document. It takes care of all the attributes.
1385: * <P>
1386: * Before entering the line position must have been established and the
1387: * <CODE>text</CODE> argument must be in text object scope (<CODE>beginText()</CODE>).
1388: * @param line the line to be written
1389: * @param text the <CODE>PdfContentByte</CODE> where the text will be written to
1390: * @param graphics the <CODE>PdfContentByte</CODE> where the graphics will be written to
1391: * @param currentValues the current font and extra spacing values
1392: * @param ratio
1393: * @throws DocumentException on error
1394: */
1395: void writeLineToContent(PdfLine line, PdfContentByte text,
1396: PdfContentByte graphics, Object currentValues[], float ratio)
1397: throws DocumentException {
1398: PdfFont currentFont = (PdfFont) (currentValues[0]);
1399: float lastBaseFactor = ((Float) (currentValues[1]))
1400: .floatValue();
1401: PdfChunk chunk;
1402: int numberOfSpaces;
1403: int lineLen;
1404: boolean isJustified;
1405: float hangingCorrection = 0;
1406: float hScale = 1;
1407: float lastHScale = Float.NaN;
1408: float baseWordSpacing = 0;
1409: float baseCharacterSpacing = 0;
1410:
1411: numberOfSpaces = line.numberOfSpaces();
1412: lineLen = line.toString().length();
1413: // does the line need to be justified?
1414: isJustified = line.hasToBeJustified()
1415: && (numberOfSpaces != 0 || lineLen > 1);
1416: if (isJustified) {
1417: if (line.isNewlineSplit()
1418: && line.widthLeft() >= (lastBaseFactor * (ratio
1419: * numberOfSpaces + lineLen - 1))) {
1420: if (line.isRTL()) {
1421: text
1422: .moveText(
1423: line.widthLeft()
1424: - lastBaseFactor
1425: * (ratio * numberOfSpaces
1426: + lineLen - 1), 0);
1427: }
1428: baseWordSpacing = ratio * lastBaseFactor;
1429: baseCharacterSpacing = lastBaseFactor;
1430: } else {
1431: float width = line.widthLeft();
1432: PdfChunk last = line.getChunk(line.size() - 1);
1433: if (last != null) {
1434: String s = last.toString();
1435: char c;
1436: if (s.length() > 0
1437: && hangingPunctuation.indexOf((c = s
1438: .charAt(s.length() - 1))) >= 0) {
1439: float oldWidth = width;
1440: width += last.font().width(c) * 0.4f;
1441: hangingCorrection = width - oldWidth;
1442: }
1443: }
1444: float baseFactor = width
1445: / (ratio * numberOfSpaces + lineLen - 1);
1446: baseWordSpacing = ratio * baseFactor;
1447: baseCharacterSpacing = baseFactor;
1448: lastBaseFactor = baseFactor;
1449: }
1450: }
1451:
1452: int lastChunkStroke = line.getLastStrokeChunk();
1453: int chunkStrokeIdx = 0;
1454: float xMarker = text.getXTLM();
1455: float baseXMarker = xMarker;
1456: float yMarker = text.getYTLM();
1457: boolean adjustMatrix = false;
1458:
1459: // looping over all the chunks in 1 line
1460: for (Iterator j = line.iterator(); j.hasNext();) {
1461: chunk = (PdfChunk) j.next();
1462: Color color = chunk.color();
1463: hScale = 1;
1464:
1465: if (chunkStrokeIdx <= lastChunkStroke) {
1466: float width;
1467: if (isJustified) {
1468: width = chunk.getWidthCorrected(
1469: baseCharacterSpacing, baseWordSpacing);
1470: } else
1471: width = chunk.width();
1472: if (chunk.isStroked()) {
1473: PdfChunk nextChunk = line
1474: .getChunk(chunkStrokeIdx + 1);
1475: if (chunk.isAttribute(Chunk.BACKGROUND)) {
1476: float subtract = lastBaseFactor;
1477: if (nextChunk != null
1478: && nextChunk
1479: .isAttribute(Chunk.BACKGROUND))
1480: subtract = 0;
1481: if (nextChunk == null)
1482: subtract += hangingCorrection;
1483: float fontSize = chunk.font().size();
1484: float ascender = chunk.font().getFont()
1485: .getFontDescriptor(BaseFont.ASCENT,
1486: fontSize);
1487: float descender = chunk.font().getFont()
1488: .getFontDescriptor(BaseFont.DESCENT,
1489: fontSize);
1490: Object bgr[] = (Object[]) chunk
1491: .getAttribute(Chunk.BACKGROUND);
1492: graphics.setColorFill((Color) bgr[0]);
1493: float extra[] = (float[]) bgr[1];
1494: graphics.rectangle(xMarker - extra[0], yMarker
1495: + descender - extra[1]
1496: + chunk.getTextRise(), width - subtract
1497: + extra[0] + extra[2], ascender
1498: - descender + extra[1] + extra[3]);
1499: graphics.fill();
1500: graphics.setGrayFill(0);
1501: }
1502: if (chunk.isAttribute(Chunk.UNDERLINE)) {
1503: float subtract = lastBaseFactor;
1504: if (nextChunk != null
1505: && nextChunk
1506: .isAttribute(Chunk.UNDERLINE))
1507: subtract = 0;
1508: if (nextChunk == null)
1509: subtract += hangingCorrection;
1510: Object unders[][] = (Object[][]) chunk
1511: .getAttribute(Chunk.UNDERLINE);
1512: Color scolor = null;
1513: for (int k = 0; k < unders.length; ++k) {
1514: Object obj[] = unders[k];
1515: scolor = (Color) obj[0];
1516: float ps[] = (float[]) obj[1];
1517: if (scolor == null)
1518: scolor = color;
1519: if (scolor != null)
1520: graphics.setColorStroke(scolor);
1521: float fsize = chunk.font().size();
1522: graphics
1523: .setLineWidth(ps[0] + fsize * ps[1]);
1524: float shift = ps[2] + fsize * ps[3];
1525: int cap2 = (int) ps[4];
1526: if (cap2 != 0)
1527: graphics.setLineCap(cap2);
1528: graphics.moveTo(xMarker, yMarker + shift);
1529: graphics.lineTo(xMarker + width - subtract,
1530: yMarker + shift);
1531: graphics.stroke();
1532: if (scolor != null)
1533: graphics.resetGrayStroke();
1534: if (cap2 != 0)
1535: graphics.setLineCap(0);
1536: }
1537: graphics.setLineWidth(1);
1538: }
1539: if (chunk.isAttribute(Chunk.ACTION)) {
1540: float subtract = lastBaseFactor;
1541: if (nextChunk != null
1542: && nextChunk.isAttribute(Chunk.ACTION))
1543: subtract = 0;
1544: if (nextChunk == null)
1545: subtract += hangingCorrection;
1546: text.addAnnotation(new PdfAnnotation(writer,
1547: xMarker, yMarker, xMarker + width
1548: - subtract, yMarker
1549: + chunk.font().size(),
1550: (PdfAction) chunk
1551: .getAttribute(Chunk.ACTION)));
1552: }
1553: if (chunk.isAttribute(Chunk.REMOTEGOTO)) {
1554: float subtract = lastBaseFactor;
1555: if (nextChunk != null
1556: && nextChunk
1557: .isAttribute(Chunk.REMOTEGOTO))
1558: subtract = 0;
1559: if (nextChunk == null)
1560: subtract += hangingCorrection;
1561: Object obj[] = (Object[]) chunk
1562: .getAttribute(Chunk.REMOTEGOTO);
1563: String filename = (String) obj[0];
1564: if (obj[1] instanceof String)
1565: remoteGoto(filename, (String) obj[1],
1566: xMarker, yMarker, xMarker + width
1567: - subtract, yMarker
1568: + chunk.font().size());
1569: else
1570: remoteGoto(filename, ((Integer) obj[1])
1571: .intValue(), xMarker, yMarker,
1572: xMarker + width - subtract, yMarker
1573: + chunk.font().size());
1574: }
1575: if (chunk.isAttribute(Chunk.LOCALGOTO)) {
1576: float subtract = lastBaseFactor;
1577: if (nextChunk != null
1578: && nextChunk
1579: .isAttribute(Chunk.LOCALGOTO))
1580: subtract = 0;
1581: if (nextChunk == null)
1582: subtract += hangingCorrection;
1583: localGoto((String) chunk
1584: .getAttribute(Chunk.LOCALGOTO),
1585: xMarker, yMarker, xMarker + width
1586: - subtract, yMarker
1587: + chunk.font().size());
1588: }
1589: if (chunk.isAttribute(Chunk.LOCALDESTINATION)) {
1590: float subtract = lastBaseFactor;
1591: if (nextChunk != null
1592: && nextChunk
1593: .isAttribute(Chunk.LOCALDESTINATION))
1594: subtract = 0;
1595: if (nextChunk == null)
1596: subtract += hangingCorrection;
1597: localDestination((String) chunk
1598: .getAttribute(Chunk.LOCALDESTINATION),
1599: new PdfDestination(PdfDestination.XYZ,
1600: xMarker, yMarker
1601: + chunk.font().size(),
1602: 0));
1603: }
1604: if (chunk.isAttribute(Chunk.GENERICTAG)) {
1605: float subtract = lastBaseFactor;
1606: if (nextChunk != null
1607: && nextChunk
1608: .isAttribute(Chunk.GENERICTAG))
1609: subtract = 0;
1610: if (nextChunk == null)
1611: subtract += hangingCorrection;
1612: Rectangle rect = new Rectangle(xMarker,
1613: yMarker, xMarker + width - subtract,
1614: yMarker + chunk.font().size());
1615: PdfPageEvent pev = writer.getPageEvent();
1616: if (pev != null)
1617: pev
1618: .onGenericTag(
1619: writer,
1620: this ,
1621: rect,
1622: (String) chunk
1623: .getAttribute(Chunk.GENERICTAG));
1624: }
1625: if (chunk.isAttribute(Chunk.PDFANNOTATION)) {
1626: float subtract = lastBaseFactor;
1627: if (nextChunk != null
1628: && nextChunk
1629: .isAttribute(Chunk.PDFANNOTATION))
1630: subtract = 0;
1631: if (nextChunk == null)
1632: subtract += hangingCorrection;
1633: float fontSize = chunk.font().size();
1634: float ascender = chunk.font().getFont()
1635: .getFontDescriptor(BaseFont.ASCENT,
1636: fontSize);
1637: float descender = chunk.font().getFont()
1638: .getFontDescriptor(BaseFont.DESCENT,
1639: fontSize);
1640: PdfAnnotation annot = PdfFormField
1641: .shallowDuplicate((PdfAnnotation) chunk
1642: .getAttribute(Chunk.PDFANNOTATION));
1643: annot.put(PdfName.RECT, new PdfRectangle(
1644: xMarker, yMarker + descender, xMarker
1645: + width - subtract, yMarker
1646: + ascender));
1647: text.addAnnotation(annot);
1648: }
1649: float params[] = (float[]) chunk
1650: .getAttribute(Chunk.SKEW);
1651: Float hs = (Float) chunk.getAttribute(Chunk.HSCALE);
1652: if (params != null || hs != null) {
1653: float b = 0, c = 0;
1654: if (params != null) {
1655: b = params[0];
1656: c = params[1];
1657: }
1658: if (hs != null)
1659: hScale = hs.floatValue();
1660: text.setTextMatrix(hScale, b, c, 1, xMarker,
1661: yMarker);
1662: }
1663: if (chunk.isImage()) {
1664: Image image = chunk.getImage();
1665: float matrix[] = image.matrix();
1666: matrix[Image.CX] = xMarker
1667: + chunk.getImageOffsetX()
1668: - matrix[Image.CX];
1669: matrix[Image.CY] = yMarker
1670: + chunk.getImageOffsetY()
1671: - matrix[Image.CY];
1672: graphics.addImage(image, matrix[0], matrix[1],
1673: matrix[2], matrix[3], matrix[4],
1674: matrix[5]);
1675: text.moveText(xMarker + lastBaseFactor
1676: + image.getScaledWidth()
1677: - text.getXTLM(), 0);
1678: }
1679: }
1680: xMarker += width;
1681: ++chunkStrokeIdx;
1682: }
1683:
1684: if (chunk.font().compareTo(currentFont) != 0) {
1685: currentFont = chunk.font();
1686: text.setFontAndSize(currentFont.getFont(), currentFont
1687: .size());
1688: }
1689: float rise = 0;
1690: Object textRender[] = (Object[]) chunk
1691: .getAttribute(Chunk.TEXTRENDERMODE);
1692: int tr = 0;
1693: float strokeWidth = 1;
1694: Color strokeColor = null;
1695: Float fr = (Float) chunk.getAttribute(Chunk.SUBSUPSCRIPT);
1696: if (textRender != null) {
1697: tr = ((Integer) textRender[0]).intValue() & 3;
1698: if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
1699: text.setTextRenderingMode(tr);
1700: if (tr == PdfContentByte.TEXT_RENDER_MODE_STROKE
1701: || tr == PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE) {
1702: strokeWidth = ((Float) textRender[1]).floatValue();
1703: if (strokeWidth != 1)
1704: text.setLineWidth(strokeWidth);
1705: strokeColor = (Color) textRender[2];
1706: if (strokeColor == null)
1707: strokeColor = color;
1708: if (strokeColor != null)
1709: text.setColorStroke(strokeColor);
1710: }
1711: }
1712: if (fr != null)
1713: rise = fr.floatValue();
1714: if (color != null)
1715: text.setColorFill(color);
1716: if (rise != 0)
1717: text.setTextRise(rise);
1718: if (chunk.isImage()) {
1719: adjustMatrix = true;
1720: }
1721: // If it is a CJK chunk or Unicode TTF we will have to simulate the
1722: // space adjustment.
1723: else if (isJustified && numberOfSpaces > 0
1724: && chunk.isSpecialEncoding()) {
1725: if (hScale != lastHScale) {
1726: lastHScale = hScale;
1727: text.setWordSpacing(baseWordSpacing / hScale);
1728: text.setCharacterSpacing(baseCharacterSpacing
1729: / hScale);
1730: }
1731: String s = chunk.toString();
1732: int idx = s.indexOf(' ');
1733: if (idx < 0)
1734: text.showText(chunk.toString());
1735: else {
1736: float spaceCorrection = -baseWordSpacing * 1000f
1737: / chunk.font.size() / hScale;
1738: PdfTextArray textArray = new PdfTextArray(s
1739: .substring(0, idx));
1740: int lastIdx = idx;
1741: while ((idx = s.indexOf(' ', lastIdx + 1)) >= 0) {
1742: textArray.add(spaceCorrection);
1743: textArray.add(s.substring(lastIdx, idx));
1744: lastIdx = idx;
1745: }
1746: textArray.add(spaceCorrection);
1747: textArray.add(s.substring(lastIdx));
1748: text.showText(textArray);
1749: }
1750: } else {
1751: if (isJustified && hScale != lastHScale) {
1752: lastHScale = hScale;
1753: text.setWordSpacing(baseWordSpacing / hScale);
1754: text.setCharacterSpacing(baseCharacterSpacing
1755: / hScale);
1756: }
1757: text.showText(chunk.toString());
1758: }
1759:
1760: if (rise != 0)
1761: text.setTextRise(0);
1762: if (color != null)
1763: text.resetRGBColorFill();
1764: if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
1765: text
1766: .setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
1767: if (strokeColor != null)
1768: text.resetRGBColorStroke();
1769: if (strokeWidth != 1)
1770: text.setLineWidth(1);
1771: if (chunk.isAttribute(Chunk.SKEW)
1772: || chunk.isAttribute(Chunk.HSCALE)) {
1773: adjustMatrix = true;
1774: text.setTextMatrix(xMarker, yMarker);
1775: }
1776: }
1777: if (isJustified) {
1778: text.setWordSpacing(0);
1779: text.setCharacterSpacing(0);
1780: if (line.isNewlineSplit())
1781: lastBaseFactor = 0;
1782: }
1783: if (adjustMatrix)
1784: text.moveText(baseXMarker - text.getXTLM(), 0);
1785: currentValues[0] = currentFont;
1786: currentValues[1] = new Float(lastBaseFactor);
1787: }
1788:
1789: Indentation indentation = new Indentation();
1790:
1791: public static class Indentation {
1792:
1793: /** This represents the current indentation of the PDF Elements on the left side. */
1794: private float indentLeft = 0;
1795:
1796: /** Indentation to the left caused by a section. */
1797: private float sectionIndentLeft = 0;
1798:
1799: /** This represents the current indentation of the PDF Elements on the left side. */
1800: private float listIndentLeft = 0;
1801:
1802: /** This is the indentation caused by an image on the left. */
1803: private float imageIndentLeft = 0;
1804:
1805: /** This represents the current indentation of the PDF Elements on the right side. */
1806: private float indentRight = 0;
1807:
1808: /** Indentation to the right caused by a section. */
1809: private float sectionIndentRight = 0;
1810:
1811: /** This is the indentation caused by an image on the right. */
1812: private float imageIndentRight = 0;
1813:
1814: /** This represents the current indentation of the PDF Elements on the top side. */
1815: private float indentTop = 0;
1816:
1817: /** This represents the current indentation of the PDF Elements on the bottom side. */
1818: private float indentBottom = 0;
1819: }
1820:
1821: /**
1822: * Gets the indentation on the left side.
1823: *
1824: * @return a margin
1825: */
1826:
1827: private float indentLeft() {
1828: return left(indentation.indentLeft + indentation.listIndentLeft
1829: + indentation.imageIndentLeft
1830: + indentation.sectionIndentLeft);
1831: }
1832:
1833: /**
1834: * Gets the indentation on the right side.
1835: *
1836: * @return a margin
1837: */
1838:
1839: private float indentRight() {
1840: return right(indentation.indentRight
1841: + indentation.sectionIndentRight);
1842: }
1843:
1844: /**
1845: * Gets the indentation on the top side.
1846: *
1847: * @return a margin
1848: */
1849:
1850: private float indentTop() {
1851: return top(indentation.indentTop);
1852: }
1853:
1854: /**
1855: * Gets the indentation on the bottom side.
1856: *
1857: * @return a margin
1858: */
1859:
1860: float indentBottom() {
1861: return bottom(indentation.indentBottom);
1862: }
1863:
1864: /**
1865: * Adds extra space.
1866: * This method should probably be rewritten.
1867: */
1868: private void addSpacing(float extraspace, float oldleading, Font f) {
1869: if (extraspace == 0)
1870: return;
1871: if (pageEmpty)
1872: return;
1873: if (currentHeight + line.height() + leading > indentTop()
1874: - indentBottom())
1875: return;
1876: leading = extraspace;
1877: carriageReturn();
1878: Chunk space = new Chunk(" ", f);
1879: space.process(this );
1880: carriageReturn();
1881: leading = oldleading;
1882: }
1883:
1884: // Info Dictionary and Catalog
1885:
1886: /** some meta information about the Document. */
1887: private PdfInfo info = new PdfInfo();
1888:
1889: /**
1890: * Gets the <CODE>PdfInfo</CODE>-object.
1891: *
1892: * @return <CODE>PdfInfo</COPE>
1893: */
1894:
1895: PdfInfo getInfo() {
1896: return info;
1897: }
1898:
1899: /**
1900: * Gets the <CODE>PdfCatalog</CODE>-object.
1901: *
1902: * @param pages an indirect reference to this document pages
1903: * @return <CODE>PdfCatalog</CODE>
1904: */
1905:
1906: PdfCatalog getCatalog(PdfIndirectReference pages) {
1907: PdfCatalog catalog = new PdfCatalog(pages, writer);
1908:
1909: // [C1] outlines
1910: if (rootOutline.getKids().size() > 0) {
1911: catalog.put(PdfName.PAGEMODE, PdfName.USEOUTLINES);
1912: catalog.put(PdfName.OUTLINES, rootOutline
1913: .indirectReference());
1914: }
1915:
1916: // [C2] version
1917: writer.getPdfVersion().addToCatalog(catalog);
1918:
1919: // [C3] preferences
1920: viewerPreferences.addToCatalog(catalog);
1921:
1922: // [C4] pagelabels
1923: if (pageLabels != null) {
1924: catalog.put(PdfName.PAGELABELS, pageLabels
1925: .getDictionary(writer));
1926: }
1927:
1928: // [C5] named objects
1929: catalog.addNames(localDestinations, getDocumentLevelJS(),
1930: documentFileAttachment, writer);
1931:
1932: // [C6] actions
1933: if (openActionName != null) {
1934: PdfAction action = getLocalGotoAction(openActionName);
1935: catalog.setOpenAction(action);
1936: } else if (openActionAction != null)
1937: catalog.setOpenAction(openActionAction);
1938: if (additionalActions != null) {
1939: catalog.setAdditionalActions(additionalActions);
1940: }
1941:
1942: // [C7] portable collections
1943: if (collection != null) {
1944: catalog.put(PdfName.COLLECTION, collection);
1945: }
1946:
1947: // [C8] AcroForm
1948: if (annotationsImp.hasValidAcroForm()) {
1949: try {
1950: catalog.put(PdfName.ACROFORM, writer.addToBody(
1951: annotationsImp.getAcroForm())
1952: .getIndirectReference());
1953: } catch (IOException e) {
1954: throw new ExceptionConverter(e);
1955: }
1956: }
1957:
1958: return catalog;
1959: }
1960:
1961: // [C1] outlines
1962:
1963: /** This is the root outline of the document. */
1964: private PdfOutline rootOutline;
1965:
1966: /** This is the current <CODE>PdfOutline</CODE> in the hierarchy of outlines. */
1967: private PdfOutline currentOutline;
1968:
1969: /**
1970: * Adds a named outline to the document .
1971: * @param outline the outline to be added
1972: * @param name the name of this local destination
1973: */
1974: void addOutline(PdfOutline outline, String name) {
1975: localDestination(name, outline.getPdfDestination());
1976: }
1977:
1978: /**
1979: * Gets the root outline. All the outlines must be created with a parent.
1980: * The first level is created with this outline.
1981: * @return the root outline
1982: */
1983: public PdfOutline getRootOutline() {
1984: return rootOutline;
1985: }
1986:
1987: /**
1988: * Updates the count in the outlines.
1989: */
1990: void calculateOutlineCount() {
1991: if (rootOutline.getKids().size() == 0)
1992: return;
1993: traverseOutlineCount(rootOutline);
1994: }
1995:
1996: /**
1997: * Recursive method to update the count in the outlines.
1998: */
1999: void traverseOutlineCount(PdfOutline outline) {
2000: ArrayList kids = outline.getKids();
2001: PdfOutline parent = outline.parent();
2002: if (kids.isEmpty()) {
2003: if (parent != null) {
2004: parent.setCount(parent.getCount() + 1);
2005: }
2006: } else {
2007: for (int k = 0; k < kids.size(); ++k) {
2008: traverseOutlineCount((PdfOutline) kids.get(k));
2009: }
2010: if (parent != null) {
2011: if (outline.isOpen()) {
2012: parent.setCount(outline.getCount()
2013: + parent.getCount() + 1);
2014: } else {
2015: parent.setCount(parent.getCount() + 1);
2016: outline.setCount(-outline.getCount());
2017: }
2018: }
2019: }
2020: }
2021:
2022: /**
2023: * Writes the outline tree to the body of the PDF document.
2024: */
2025: void writeOutlines() throws IOException {
2026: if (rootOutline.getKids().size() == 0)
2027: return;
2028: outlineTree(rootOutline);
2029: writer.addToBody(rootOutline, rootOutline.indirectReference());
2030: }
2031:
2032: /**
2033: * Recursive method used to write outlines.
2034: */
2035: void outlineTree(PdfOutline outline) throws IOException {
2036: outline.setIndirectReference(writer.getPdfIndirectReference());
2037: if (outline.parent() != null)
2038: outline.put(PdfName.PARENT, outline.parent()
2039: .indirectReference());
2040: ArrayList kids = outline.getKids();
2041: int size = kids.size();
2042: for (int k = 0; k < size; ++k)
2043: outlineTree((PdfOutline) kids.get(k));
2044: for (int k = 0; k < size; ++k) {
2045: if (k > 0)
2046: ((PdfOutline) kids.get(k)).put(PdfName.PREV,
2047: ((PdfOutline) kids.get(k - 1))
2048: .indirectReference());
2049: if (k < size - 1)
2050: ((PdfOutline) kids.get(k)).put(PdfName.NEXT,
2051: ((PdfOutline) kids.get(k + 1))
2052: .indirectReference());
2053: }
2054: if (size > 0) {
2055: outline.put(PdfName.FIRST, ((PdfOutline) kids.get(0))
2056: .indirectReference());
2057: outline.put(PdfName.LAST, ((PdfOutline) kids.get(size - 1))
2058: .indirectReference());
2059: }
2060: for (int k = 0; k < size; ++k) {
2061: PdfOutline kid = (PdfOutline) kids.get(k);
2062: writer.addToBody(kid, kid.indirectReference());
2063: }
2064: }
2065:
2066: // [C3] PdfViewerPreferences interface
2067:
2068: /** Contains the Viewer preferences of this PDF document. */
2069: protected PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp();
2070:
2071: /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */
2072: void setViewerPreferences(int preferences) {
2073: this .viewerPreferences.setViewerPreferences(preferences);
2074: }
2075:
2076: /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */
2077: void addViewerPreference(PdfName key, PdfObject value) {
2078: this .viewerPreferences.addViewerPreference(key, value);
2079: }
2080:
2081: // [C4] Page labels
2082:
2083: protected PdfPageLabels pageLabels;
2084:
2085: /**
2086: * Sets the page labels
2087: * @param pageLabels the page labels
2088: */
2089: void setPageLabels(PdfPageLabels pageLabels) {
2090: this .pageLabels = pageLabels;
2091: }
2092:
2093: // [C5] named objects: local destinations, javascript, embedded files
2094:
2095: /**
2096: * Implements a link to other part of the document. The jump will
2097: * be made to a local destination with the same name, that must exist.
2098: * @param name the name for this link
2099: * @param llx the lower left x corner of the activation area
2100: * @param lly the lower left y corner of the activation area
2101: * @param urx the upper right x corner of the activation area
2102: * @param ury the upper right y corner of the activation area
2103: */
2104: void localGoto(String name, float llx, float lly, float urx,
2105: float ury) {
2106: PdfAction action = getLocalGotoAction(name);
2107: annotationsImp.addPlainAnnotation(new PdfAnnotation(writer,
2108: llx, lly, urx, ury, action));
2109: }
2110:
2111: /**
2112: * Implements a link to another document.
2113: * @param filename the filename for the remote document
2114: * @param name the name to jump to
2115: * @param llx the lower left x corner of the activation area
2116: * @param lly the lower left y corner of the activation area
2117: * @param urx the upper right x corner of the activation area
2118: * @param ury the upper right y corner of the activation area
2119: */
2120: void remoteGoto(String filename, String name, float llx, float lly,
2121: float urx, float ury) {
2122: annotationsImp.addPlainAnnotation(new PdfAnnotation(writer,
2123: llx, lly, urx, ury, new PdfAction(filename, name)));
2124: }
2125:
2126: /**
2127: * Implements a link to another document.
2128: * @param filename the filename for the remote document
2129: * @param page the page to jump to
2130: * @param llx the lower left x corner of the activation area
2131: * @param lly the lower left y corner of the activation area
2132: * @param urx the upper right x corner of the activation area
2133: * @param ury the upper right y corner of the activation area
2134: */
2135: void remoteGoto(String filename, int page, float llx, float lly,
2136: float urx, float ury) {
2137: addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury,
2138: new PdfAction(filename, page)));
2139: }
2140:
2141: /** Implements an action in an area.
2142: * @param action the <CODE>PdfAction</CODE>
2143: * @param llx the lower left x corner of the activation area
2144: * @param lly the lower left y corner of the activation area
2145: * @param urx the upper right x corner of the activation area
2146: * @param ury the upper right y corner of the activation area
2147: */
2148: void setAction(PdfAction action, float llx, float lly, float urx,
2149: float ury) {
2150: addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury,
2151: action));
2152: }
2153:
2154: /**
2155: * Stores the destinations keyed by name. Value is
2156: * <CODE>Object[]{PdfAction,PdfIndirectReference,PdfDestintion}</CODE>.
2157: */
2158: private TreeMap localDestinations = new TreeMap();
2159:
2160: PdfAction getLocalGotoAction(String name) {
2161: PdfAction action;
2162: Object obj[] = (Object[]) localDestinations.get(name);
2163: if (obj == null)
2164: obj = new Object[3];
2165: if (obj[0] == null) {
2166: if (obj[1] == null) {
2167: obj[1] = writer.getPdfIndirectReference();
2168: }
2169: action = new PdfAction((PdfIndirectReference) obj[1]);
2170: obj[0] = action;
2171: localDestinations.put(name, obj);
2172: } else {
2173: action = (PdfAction) obj[0];
2174: }
2175: return action;
2176: }
2177:
2178: /**
2179: * The local destination to where a local goto with the same
2180: * name will jump to.
2181: * @param name the name of this local destination
2182: * @param destination the <CODE>PdfDestination</CODE> with the jump coordinates
2183: * @return <CODE>true</CODE> if the local destination was added,
2184: * <CODE>false</CODE> if a local destination with the same name
2185: * already existed
2186: */
2187: boolean localDestination(String name, PdfDestination destination) {
2188: Object obj[] = (Object[]) localDestinations.get(name);
2189: if (obj == null)
2190: obj = new Object[3];
2191: if (obj[2] != null)
2192: return false;
2193: obj[2] = destination;
2194: localDestinations.put(name, obj);
2195: destination.addPage(writer.getCurrentPage());
2196: return true;
2197: }
2198:
2199: /**
2200: * Stores a list of document level JavaScript actions.
2201: */
2202: int jsCounter;
2203: private LinkedHashMap documentLevelJS = new LinkedHashMap();
2204:
2205: void addJavaScript(PdfAction js) {
2206: if (js.get(PdfName.JS) == null)
2207: throw new RuntimeException(
2208: "Only JavaScript actions are allowed.");
2209: try {
2210: documentLevelJS.put("iTextJS_" + jsCounter, writer
2211: .addToBody(js).getIndirectReference());
2212: jsCounter++;
2213: } catch (IOException e) {
2214: throw new ExceptionConverter(e);
2215: }
2216: }
2217:
2218: void addJavaScript(String name, PdfAction js) {
2219: if (js.get(PdfName.JS) == null)
2220: throw new RuntimeException(
2221: "Only JavaScript actions are allowed.");
2222: try {
2223: documentLevelJS.put(name, writer.addToBody(js)
2224: .getIndirectReference());
2225: } catch (IOException e) {
2226: throw new ExceptionConverter(e);
2227: }
2228: }
2229:
2230: LinkedHashMap getDocumentLevelJS() {
2231: return documentLevelJS;
2232: }
2233:
2234: private HashMap documentFileAttachment = new HashMap();
2235:
2236: void addFileAttachment(String description, PdfFileSpecification fs)
2237: throws IOException {
2238: if (description == null) {
2239: PdfString desc = (PdfString) fs.get(PdfName.DESC);
2240: if (desc == null) {
2241: description = "";
2242: } else {
2243: description = PdfEncodings.convertToString(desc
2244: .getBytes(), null);
2245: }
2246: }
2247: fs.addDescription(description, true);
2248: if (description.length() == 0)
2249: description = "Unnamed";
2250: String fn = PdfEncodings.convertToString(new PdfString(
2251: description, PdfObject.TEXT_UNICODE).getBytes(), null);
2252: int k = 0;
2253: while (documentFileAttachment.containsKey(fn)) {
2254: ++k;
2255: fn = PdfEncodings
2256: .convertToString(new PdfString(description + " "
2257: + k, PdfObject.TEXT_UNICODE).getBytes(),
2258: null);
2259: }
2260: documentFileAttachment.put(fn, fs.getReference());
2261: }
2262:
2263: HashMap getDocumentFileAttachment() {
2264: return documentFileAttachment;
2265: }
2266:
2267: // [C6] document level actions
2268:
2269: private String openActionName;
2270:
2271: void setOpenAction(String name) {
2272: openActionName = name;
2273: openActionAction = null;
2274: }
2275:
2276: private PdfAction openActionAction;
2277:
2278: void setOpenAction(PdfAction action) {
2279: openActionAction = action;
2280: openActionName = null;
2281: }
2282:
2283: private PdfDictionary additionalActions;
2284:
2285: void addAdditionalAction(PdfName actionType, PdfAction action) {
2286: if (additionalActions == null) {
2287: additionalActions = new PdfDictionary();
2288: }
2289: if (action == null)
2290: additionalActions.remove(actionType);
2291: else
2292: additionalActions.put(actionType, action);
2293: if (additionalActions.size() == 0)
2294: additionalActions = null;
2295: }
2296:
2297: // [C7] portable collections
2298:
2299: private PdfCollection collection;
2300:
2301: /**
2302: * Sets the collection dictionary.
2303: * @param collection a dictionary of type PdfCollection
2304: */
2305: public void setCollection(PdfCollection collection) {
2306: this .collection = collection;
2307: }
2308:
2309: // [C8] AcroForm
2310:
2311: PdfAnnotationsImp annotationsImp;
2312:
2313: /**
2314: * Gets the AcroForm object.
2315: * @return the PdfAcroform object of the PdfDocument
2316: */
2317: PdfAcroForm getAcroForm() {
2318: return annotationsImp.getAcroForm();
2319: }
2320:
2321: void setSigFlags(int f) {
2322: annotationsImp.setSigFlags(f);
2323: }
2324:
2325: void addCalculationOrder(PdfFormField formField) {
2326: annotationsImp.addCalculationOrder(formField);
2327: }
2328:
2329: void addAnnotation(PdfAnnotation annot) {
2330: pageEmpty = false;
2331: annotationsImp.addAnnotation(annot);
2332: }
2333:
2334: // [F12] tagged PDF
2335:
2336: protected int markPoint;
2337:
2338: int getMarkPoint() {
2339: return markPoint;
2340: }
2341:
2342: void incMarkPoint() {
2343: ++markPoint;
2344: }
2345:
2346: // [U1] page sizes
2347:
2348: /** This is the size of the next page. */
2349: protected Rectangle nextPageSize = null;
2350:
2351: /** This is the size of the several boxes of the current Page. */
2352: protected HashMap this BoxSize = new HashMap();
2353:
2354: /** This is the size of the several boxes that will be used in
2355: * the next page. */
2356: protected HashMap boxSize = new HashMap();
2357:
2358: void setCropBoxSize(Rectangle crop) {
2359: setBoxSize("crop", crop);
2360: }
2361:
2362: void setBoxSize(String boxName, Rectangle size) {
2363: if (size == null)
2364: boxSize.remove(boxName);
2365: else
2366: boxSize.put(boxName, new PdfRectangle(size));
2367: }
2368:
2369: private void setNewPageSizeAndMargins() {
2370: pageSize = nextPageSize;
2371: if (marginMirroring && (getPageNumber() & 1) == 0) {
2372: marginRight = nextMarginLeft;
2373: marginLeft = nextMarginRight;
2374: } else {
2375: marginLeft = nextMarginLeft;
2376: marginRight = nextMarginRight;
2377: }
2378: marginTop = nextMarginTop;
2379: marginBottom = nextMarginBottom;
2380: }
2381:
2382: /**
2383: * Gives the size of a trim, art, crop or bleed box, or null if not defined.
2384: * @param boxName crop, trim, art or bleed
2385: */
2386: Rectangle getBoxSize(String boxName) {
2387: PdfRectangle r = (PdfRectangle) this BoxSize.get(boxName);
2388: if (r != null)
2389: return r.getRectangle();
2390: return null;
2391: }
2392:
2393: // [U2] empty pages
2394:
2395: /** This checks if the page is empty. */
2396: private boolean pageEmpty = true;
2397:
2398: void setPageEmpty(boolean pageEmpty) {
2399: this .pageEmpty = pageEmpty;
2400: }
2401:
2402: // [U3] page actions
2403:
2404: /** The duration of the page */
2405: protected int duration = -1; // negative values will indicate no duration
2406:
2407: /** The page transition */
2408: protected PdfTransition transition = null;
2409:
2410: /**
2411: * Sets the display duration for the page (for presentations)
2412: * @param seconds the number of seconds to display the page
2413: */
2414: void setDuration(int seconds) {
2415: if (seconds > 0)
2416: this .duration = seconds;
2417: else
2418: this .duration = -1;
2419: }
2420:
2421: /**
2422: * Sets the transition for the page
2423: * @param transition the PdfTransition object
2424: */
2425: void setTransition(PdfTransition transition) {
2426: this .transition = transition;
2427: }
2428:
2429: protected PdfDictionary pageAA = null;
2430:
2431: void setPageAction(PdfName actionType, PdfAction action) {
2432: if (pageAA == null) {
2433: pageAA = new PdfDictionary();
2434: }
2435: pageAA.put(actionType, action);
2436: }
2437:
2438: // [U8] thumbnail images
2439:
2440: private PdfIndirectReference thumb;
2441:
2442: void setThumbnail(Image image) throws PdfException,
2443: DocumentException {
2444: thumb = writer.getImageReference(writer
2445: .addDirectImageSimple(image));
2446: }
2447:
2448: // [M0] Page resources contain references to fonts, extgstate, images,...
2449:
2450: /** This are the page resources of the current Page. */
2451: protected PageResources pageResources;
2452:
2453: PageResources getPageResources() {
2454: return pageResources;
2455: }
2456:
2457: // [M3] Images
2458:
2459: /** Holds value of property strictImageSequence. */
2460: private boolean strictImageSequence = false;
2461:
2462: /** Getter for property strictImageSequence.
2463: * @return Value of property strictImageSequence.
2464: *
2465: */
2466: boolean isStrictImageSequence() {
2467: return this .strictImageSequence;
2468: }
2469:
2470: /** Setter for property strictImageSequence.
2471: * @param strictImageSequence New value of property strictImageSequence.
2472: *
2473: */
2474: void setStrictImageSequence(boolean strictImageSequence) {
2475: this .strictImageSequence = strictImageSequence;
2476: }
2477:
2478: /** This is the position where the image ends. */
2479: private float imageEnd = -1;
2480:
2481: /**
2482: * Method added by Pelikan Stephan
2483: */
2484: public void clearTextWrap() {
2485: float tmpHeight = imageEnd - currentHeight;
2486: if (line != null) {
2487: tmpHeight += line.height();
2488: }
2489: if ((imageEnd > -1) && (tmpHeight > 0)) {
2490: carriageReturn();
2491: currentHeight += tmpHeight;
2492: }
2493: }
2494:
2495: /** This is the image that could not be shown on a previous page. */
2496: private Image imageWait = null;
2497:
2498: /**
2499: * Adds an image to the document.
2500: * @param image the <CODE>Image</CODE> to add
2501: * @throws PdfException on error
2502: * @throws DocumentException on error
2503: */
2504:
2505: private void add(Image image) throws PdfException,
2506: DocumentException {
2507:
2508: if (image.hasAbsoluteY()) {
2509: graphics.addImage(image);
2510: pageEmpty = false;
2511: return;
2512: }
2513:
2514: // if there isn't enough room for the image on this page, save it for the next page
2515: if (currentHeight != 0
2516: && indentTop() - currentHeight
2517: - image.getScaledHeight() < indentBottom()) {
2518: if (!strictImageSequence && imageWait == null) {
2519: imageWait = image;
2520: return;
2521: }
2522: newPage();
2523: if (currentHeight != 0
2524: && indentTop() - currentHeight
2525: - image.getScaledHeight() < indentBottom()) {
2526: imageWait = image;
2527: return;
2528: }
2529: }
2530: pageEmpty = false;
2531: // avoid endless loops
2532: if (image == imageWait)
2533: imageWait = null;
2534: boolean textwrap = (image.getAlignment() & Image.TEXTWRAP) == Image.TEXTWRAP
2535: && !((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE);
2536: boolean underlying = (image.getAlignment() & Image.UNDERLYING) == Image.UNDERLYING;
2537: float diff = leading / 2;
2538: if (textwrap) {
2539: diff += leading;
2540: }
2541: float lowerleft = indentTop() - currentHeight
2542: - image.getScaledHeight() - diff;
2543: float mt[] = image.matrix();
2544: float startPosition = indentLeft() - mt[4];
2545: if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT)
2546: startPosition = indentRight() - image.getScaledWidth()
2547: - mt[4];
2548: if ((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE)
2549: startPosition = indentLeft()
2550: + ((indentRight() - indentLeft() - image
2551: .getScaledWidth()) / 2) - mt[4];
2552: if (image.hasAbsoluteX())
2553: startPosition = image.getAbsoluteX();
2554: if (textwrap) {
2555: if (imageEnd < 0
2556: || imageEnd < currentHeight
2557: + image.getScaledHeight() + diff) {
2558: imageEnd = currentHeight + image.getScaledHeight()
2559: + diff;
2560: }
2561: if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT) {
2562: // indentation suggested by Pelikan Stephan
2563: indentation.imageIndentRight += image.getScaledWidth()
2564: + image.getIndentationLeft();
2565: } else {
2566: // indentation suggested by Pelikan Stephan
2567: indentation.imageIndentLeft += image.getScaledWidth()
2568: + image.getIndentationRight();
2569: }
2570: } else {
2571: if ((image.getAlignment() & Image.RIGHT) == Image.RIGHT)
2572: startPosition -= image.getIndentationRight();
2573: else if ((image.getAlignment() & Image.MIDDLE) == Image.MIDDLE)
2574: startPosition += image.getIndentationLeft()
2575: - image.getIndentationRight();
2576: else
2577: startPosition += image.getIndentationLeft();
2578: }
2579: graphics.addImage(image, mt[0], mt[1], mt[2], mt[3],
2580: startPosition, lowerleft - mt[5]);
2581: if (!(textwrap || underlying)) {
2582: currentHeight += image.getScaledHeight() + diff;
2583: flushLines();
2584: text.moveText(0, -(image.getScaledHeight() + diff));
2585: newLine();
2586: }
2587: }
2588:
2589: // [M4] Adding a PdfPTable
2590:
2591: /** Adds a <CODE>PdfPTable</CODE> to the document.
2592: * @param ptable the <CODE>PdfPTable</CODE> to be added to the document.
2593: * @throws DocumentException on error
2594: */
2595: void addPTable(PdfPTable ptable) throws DocumentException {
2596: ColumnText ct = new ColumnText(writer.getDirectContent());
2597: if (currentHeight > 0) {
2598: Paragraph p = new Paragraph();
2599: p.setLeading(0);
2600: ct.addElement(p);
2601: // if the table prefers to be on a single page, and it wouldn't
2602: //fit on the current page, start a new page.
2603: if (ptable.getKeepTogether() && !fitsPage(ptable, 0f)) {
2604: newPage();
2605: }
2606: }
2607: ct.addElement(ptable);
2608: boolean he = ptable.isHeadersInEvent();
2609: ptable.setHeadersInEvent(true);
2610: int loop = 0;
2611: while (true) {
2612: ct.setSimpleColumn(indentLeft(), indentBottom(),
2613: indentRight(), indentTop() - currentHeight);
2614: int status = ct.go();
2615: if ((status & ColumnText.NO_MORE_TEXT) != 0) {
2616: text.moveText(0, ct.getYLine() - indentTop()
2617: + currentHeight);
2618: currentHeight = indentTop() - ct.getYLine();
2619: break;
2620: }
2621: if (indentTop() - currentHeight == ct.getYLine())
2622: ++loop;
2623: else
2624: loop = 0;
2625: if (loop == 3) {
2626: add(new Paragraph("ERROR: Infinite table loop"));
2627: break;
2628: }
2629: newPage();
2630: }
2631: ptable.setHeadersInEvent(he);
2632: }
2633:
2634: /**
2635: * Checks if a <CODE>PdfPTable</CODE> fits the current page of the <CODE>PdfDocument</CODE>.
2636: *
2637: * @param table the table that has to be checked
2638: * @param margin a certain margin
2639: * @return <CODE>true</CODE> if the <CODE>PdfPTable</CODE> fits the page, <CODE>false</CODE> otherwise.
2640: */
2641:
2642: boolean fitsPage(PdfPTable table, float margin) {
2643: if (!table.isLockedWidth()) {
2644: float totalWidth = (indentRight() - indentLeft())
2645: * table.getWidthPercentage() / 100;
2646: table.setTotalWidth(totalWidth);
2647: }
2648: // ensuring that a new line has been started.
2649: ensureNewLine();
2650: return table.getTotalHeight() <= indentTop() - currentHeight
2651: - indentBottom() - margin;
2652: }
2653:
2654: // [M4'] Adding a Table
2655:
2656: /**
2657: * Gets a PdfTable object
2658: * (contributed by dperezcar@fcc.es)
2659: * @param table a high level table object
2660: * @param supportRowAdditions
2661: * @return returns a PdfTable object
2662: * @see PdfWriter#getPdfTable(Table)
2663: */
2664:
2665: PdfTable getPdfTable(Table table, boolean supportRowAdditions) {
2666: return new PdfTable(table, indentLeft(), indentRight(),
2667: indentTop() - currentHeight, supportRowAdditions);
2668: }
2669:
2670: /**
2671: * This is a helper class for adding a Table to a document.
2672: */
2673: private static class RenderingContext {
2674: float pagetop = -1;
2675: float oldHeight = -1;
2676:
2677: PdfContentByte cellGraphics = null;
2678:
2679: float lostTableBottom;
2680:
2681: float maxCellBottom;
2682: float maxCellHeight;
2683:
2684: Map rowspanMap;
2685: Map pageMap = new HashMap();
2686: /**
2687: * A PdfPTable
2688: */
2689: public PdfTable table;
2690:
2691: /**
2692: * Consumes the rowspan
2693: * @param c
2694: * @return a rowspan.
2695: */
2696: public int consumeRowspan(PdfCell c) {
2697: if (c.rowspan() == 1) {
2698: return 1;
2699: }
2700:
2701: Integer i = (Integer) rowspanMap.get(c);
2702: if (i == null) {
2703: i = new Integer(c.rowspan());
2704: }
2705:
2706: i = new Integer(i.intValue() - 1);
2707: rowspanMap.put(c, i);
2708:
2709: if (i.intValue() < 1) {
2710: return 1;
2711: }
2712: return i.intValue();
2713: }
2714:
2715: /**
2716: * Looks at the current rowspan.
2717: * @param c
2718: * @return the current rowspan
2719: */
2720: public int currentRowspan(PdfCell c) {
2721: Integer i = (Integer) rowspanMap.get(c);
2722: if (i == null) {
2723: return c.rowspan();
2724: } else {
2725: return i.intValue();
2726: }
2727: }
2728:
2729: public int cellRendered(PdfCell cell, int pageNumber) {
2730: Integer i = (Integer) pageMap.get(cell);
2731: if (i == null) {
2732: i = new Integer(1);
2733: } else {
2734: i = new Integer(i.intValue() + 1);
2735: }
2736: pageMap.put(cell, i);
2737:
2738: Integer pageInteger = new Integer(pageNumber);
2739: Set set = (Set) pageMap.get(pageInteger);
2740:
2741: if (set == null) {
2742: set = new HashSet();
2743: pageMap.put(pageInteger, set);
2744: }
2745:
2746: set.add(cell);
2747:
2748: return i.intValue();
2749: }
2750:
2751: public int numCellRendered(PdfCell cell) {
2752: Integer i = (Integer) pageMap.get(cell);
2753: if (i == null) {
2754: i = new Integer(0);
2755: }
2756: return i.intValue();
2757: }
2758:
2759: public boolean isCellRenderedOnPage(PdfCell cell, int pageNumber) {
2760: Integer pageInteger = new Integer(pageNumber);
2761: Set set = (Set) pageMap.get(pageInteger);
2762:
2763: if (set != null) {
2764: return set.contains(cell);
2765: }
2766:
2767: return false;
2768: }
2769: };
2770:
2771: /**
2772: * Adds a new table to the document.
2773: * @param table Table to add. Rendered rows will be deleted after processing.
2774: * @param onlyFirstPage Render only the first full page
2775: * @throws DocumentException
2776: */
2777: private void add(PdfTable table, boolean onlyFirstPage)
2778: throws DocumentException {
2779: // before every table, we flush all lines
2780: flushLines();
2781:
2782: RenderingContext ctx = new RenderingContext();
2783: ctx.pagetop = indentTop();
2784: ctx.oldHeight = currentHeight;
2785: ctx.cellGraphics = new PdfContentByte(writer);
2786: ctx.rowspanMap = new HashMap();
2787: ctx.table = table;
2788:
2789: // initialisation of parameters
2790: PdfCell cell;
2791:
2792: // drawing the table
2793: ArrayList dataCells = table.getCells();
2794:
2795: ArrayList headercells = table.getHeaderCells();
2796: // Check if we have removed header cells in a previous call
2797: if (!headercells.isEmpty()
2798: && (dataCells.isEmpty() || dataCells.get(0) != headercells
2799: .get(0))) {
2800: ArrayList allCells = new ArrayList(dataCells.size()
2801: + headercells.size());
2802: allCells.addAll(headercells);
2803: allCells.addAll(dataCells);
2804: dataCells = allCells;
2805: }
2806:
2807: ArrayList cells = dataCells;
2808: ArrayList rows = extractRows(cells, ctx);
2809: boolean isContinue = false;
2810: while (!cells.isEmpty()) {
2811: // initialisation of some extra parameters;
2812: ctx.lostTableBottom = 0;
2813:
2814: // loop over the cells
2815: boolean cellsShown = false;
2816:
2817: // draw the cells (line by line)
2818: Iterator iterator = rows.iterator();
2819:
2820: boolean atLeastOneFits = false;
2821: while (iterator.hasNext()) {
2822: ArrayList row = (ArrayList) iterator.next();
2823: analyzeRow(rows, ctx);
2824: renderCells(ctx, row, table.hasToFitPageCells()
2825: & atLeastOneFits);
2826:
2827: if (!mayBeRemoved(row)) {
2828: break;
2829: }
2830: consumeRowspan(row, ctx);
2831: iterator.remove();
2832: atLeastOneFits = true;
2833: }
2834:
2835: // compose cells array list for subsequent code
2836: cells.clear();
2837: Set opt = new HashSet();
2838: iterator = rows.iterator();
2839: while (iterator.hasNext()) {
2840: ArrayList row = (ArrayList) iterator.next();
2841:
2842: Iterator cellIterator = row.iterator();
2843: while (cellIterator.hasNext()) {
2844: cell = (PdfCell) cellIterator.next();
2845:
2846: if (!opt.contains(cell)) {
2847: cells.add(cell);
2848: opt.add(cell);
2849: }
2850: }
2851: }
2852:
2853: // we paint the graphics of the table after looping through all the cells
2854: Rectangle tablerec = new Rectangle(table);
2855: tablerec.setBorder(table.getBorder());
2856: tablerec.setBorderWidth(table.getBorderWidth());
2857: tablerec.setBorderColor(table.getBorderColor());
2858: tablerec.setBackgroundColor(table.getBackgroundColor());
2859: PdfContentByte under = writer.getDirectContentUnder();
2860: under.rectangle(tablerec.rectangle(top(), indentBottom()));
2861: under.add(ctx.cellGraphics);
2862: // bugfix by Gerald Fehringer: now again add the border for the table
2863: // since it might have been covered by cell backgrounds
2864: tablerec.setBackgroundColor(null);
2865: tablerec = tablerec.rectangle(top(), indentBottom());
2866: tablerec.setBorder(table.getBorder());
2867: under.rectangle(tablerec);
2868: // end bugfix
2869:
2870: ctx.cellGraphics = new PdfContentByte(null);
2871: // if the table continues on the next page
2872:
2873: if (!rows.isEmpty()) {
2874: isContinue = true;
2875: graphics.setLineWidth(table.getBorderWidth());
2876: if (cellsShown
2877: && (table.getBorder() & Rectangle.BOTTOM) == Rectangle.BOTTOM) {
2878: // Draw the bottom line
2879:
2880: // the color is set to the color of the element
2881: Color tColor = table.getBorderColor();
2882: if (tColor != null) {
2883: graphics.setColorStroke(tColor);
2884: }
2885: graphics.moveTo(table.getLeft(), Math.max(table
2886: .getBottom(), indentBottom()));
2887: graphics.lineTo(table.getRight(), Math.max(table
2888: .getBottom(), indentBottom()));
2889: graphics.stroke();
2890: if (tColor != null) {
2891: graphics.resetRGBColorStroke();
2892: }
2893: }
2894:
2895: // old page
2896: pageEmpty = false;
2897: float difference = ctx.lostTableBottom;
2898:
2899: // new page
2900: newPage();
2901:
2902: // G.F.: if something added in page event i.e. currentHeight > 0
2903: float heightCorrection = 0;
2904: boolean somethingAdded = false;
2905: if (currentHeight > 0) {
2906: heightCorrection = 6;
2907: currentHeight += heightCorrection;
2908: somethingAdded = true;
2909: newLine();
2910: flushLines();
2911: indentation.indentTop = currentHeight - leading;
2912: currentHeight = 0;
2913: } else {
2914: flushLines();
2915: }
2916:
2917: // this part repeats the table headers (if any)
2918: int size = headercells.size();
2919: if (size > 0) {
2920: // this is the top of the headersection
2921: cell = (PdfCell) headercells.get(0);
2922: float oldTop = cell.getTop(0);
2923: // loop over all the cells of the table header
2924: for (int i = 0; i < size; i++) {
2925: cell = (PdfCell) headercells.get(i);
2926: // calculation of the new cellpositions
2927: cell.setTop(indentTop() - oldTop
2928: + cell.getTop(0));
2929: cell.setBottom(indentTop() - oldTop
2930: + cell.getBottom(0));
2931: ctx.pagetop = cell.getBottom();
2932: // we paint the borders of the cell
2933: ctx.cellGraphics.rectangle(cell.rectangle(
2934: indentTop(), indentBottom()));
2935: // we write the text of the cell
2936: ArrayList images = cell.getImages(indentTop(),
2937: indentBottom());
2938: for (Iterator im = images.iterator(); im
2939: .hasNext();) {
2940: cellsShown = true;
2941: Image image = (Image) im.next();
2942: graphics.addImage(image);
2943: }
2944: lines = cell.getLines(indentTop(),
2945: indentBottom());
2946: float cellTop = cell.getTop(indentTop());
2947: text.moveText(0, cellTop - heightCorrection);
2948: float cellDisplacement = flushLines() - cellTop
2949: + heightCorrection;
2950: text.moveText(0, cellDisplacement);
2951: }
2952:
2953: currentHeight = indentTop() - ctx.pagetop
2954: + table.cellspacing();
2955: text.moveText(0, ctx.pagetop - indentTop()
2956: - currentHeight);
2957: } else {
2958: if (somethingAdded) {
2959: ctx.pagetop = indentTop();
2960: text.moveText(0, -table.cellspacing());
2961: }
2962: }
2963: ctx.oldHeight = currentHeight - heightCorrection;
2964:
2965: // calculating the new positions of the table and the cells
2966: size = Math.min(cells.size(), table.columns());
2967: int i = 0;
2968: while (i < size) {
2969: cell = (PdfCell) cells.get(i);
2970: if (cell.getTop(-table.cellspacing()) > ctx.lostTableBottom) {
2971: float newBottom = ctx.pagetop - difference
2972: + cell.getBottom();
2973: float neededHeight = cell.remainingHeight();
2974: if (newBottom > ctx.pagetop - neededHeight) {
2975: difference += newBottom
2976: - (ctx.pagetop - neededHeight);
2977: }
2978: }
2979: i++;
2980: }
2981: size = cells.size();
2982: table.setTop(indentTop());
2983: table.setBottom(ctx.pagetop - difference
2984: + table.getBottom(table.cellspacing()));
2985: for (i = 0; i < size; i++) {
2986: cell = (PdfCell) cells.get(i);
2987: float newBottom = ctx.pagetop - difference
2988: + cell.getBottom();
2989: float newTop = ctx.pagetop - difference
2990: + cell.getTop(-table.cellspacing());
2991: if (newTop > indentTop() - currentHeight) {
2992: newTop = indentTop() - currentHeight;
2993: }
2994:
2995: cell.setTop(newTop);
2996: cell.setBottom(newBottom);
2997: }
2998: if (onlyFirstPage) {
2999: break;
3000: }
3001: }
3002: }
3003:
3004: float tableHeight = table.getTop() - table.getBottom();
3005: // bugfix by Adauto Martins when have more than two tables and more than one page
3006: // If continuation of table in other page (bug report #1460051)
3007: if (isContinue) {
3008: currentHeight = tableHeight;
3009: text.moveText(0, -(tableHeight - (ctx.oldHeight * 2)));
3010: } else {
3011: currentHeight = ctx.oldHeight + tableHeight;
3012: text.moveText(0, -tableHeight);
3013: }
3014: // end bugfix
3015: pageEmpty = false;
3016: }
3017:
3018: private void analyzeRow(ArrayList rows, RenderingContext ctx) {
3019: ctx.maxCellBottom = indentBottom();
3020:
3021: // determine whether row(index) is in a rowspan
3022: int rowIndex = 0;
3023:
3024: ArrayList row = (ArrayList) rows.get(rowIndex);
3025: int maxRowspan = 1;
3026: Iterator iterator = row.iterator();
3027: while (iterator.hasNext()) {
3028: PdfCell cell = (PdfCell) iterator.next();
3029: maxRowspan = Math.max(ctx.currentRowspan(cell), maxRowspan);
3030: }
3031: rowIndex += maxRowspan;
3032:
3033: boolean useTop = true;
3034: if (rowIndex == rows.size()) {
3035: rowIndex = rows.size() - 1;
3036: useTop = false;
3037: }
3038:
3039: if (rowIndex < 0 || rowIndex >= rows.size())
3040: return;
3041:
3042: row = (ArrayList) rows.get(rowIndex);
3043: iterator = row.iterator();
3044: while (iterator.hasNext()) {
3045: PdfCell cell = (PdfCell) iterator.next();
3046: Rectangle cellRect = cell.rectangle(ctx.pagetop,
3047: indentBottom());
3048: if (useTop) {
3049: ctx.maxCellBottom = Math.max(ctx.maxCellBottom,
3050: cellRect.getTop());
3051: } else {
3052: if (ctx.currentRowspan(cell) == 1) {
3053: ctx.maxCellBottom = Math.max(ctx.maxCellBottom,
3054: cellRect.getBottom());
3055: }
3056: }
3057: }
3058: }
3059:
3060: private boolean mayBeRemoved(ArrayList row) {
3061: Iterator iterator = row.iterator();
3062: boolean mayBeRemoved = true;
3063: while (iterator.hasNext()) {
3064: PdfCell cell = (PdfCell) iterator.next();
3065:
3066: mayBeRemoved &= cell.mayBeRemoved();
3067: }
3068: return mayBeRemoved;
3069: }
3070:
3071: private void consumeRowspan(ArrayList row, RenderingContext ctx) {
3072: Iterator iterator = row.iterator();
3073: while (iterator.hasNext()) {
3074: PdfCell c = (PdfCell) iterator.next();
3075: ctx.consumeRowspan(c);
3076: }
3077: }
3078:
3079: private ArrayList extractRows(ArrayList cells, RenderingContext ctx) {
3080: PdfCell cell;
3081: PdfCell previousCell = null;
3082: ArrayList rows = new ArrayList();
3083: java.util.List rowCells = new ArrayList();
3084:
3085: Iterator iterator = cells.iterator();
3086: while (iterator.hasNext()) {
3087: cell = (PdfCell) iterator.next();
3088:
3089: boolean isAdded = false;
3090:
3091: boolean isEndOfRow = !iterator.hasNext();
3092: boolean isCurrentCellPartOfRow = !iterator.hasNext();
3093:
3094: if (previousCell != null) {
3095: if (cell.getLeft() <= previousCell.getLeft()) {
3096: isEndOfRow = true;
3097: isCurrentCellPartOfRow = false;
3098: }
3099: }
3100:
3101: if (isCurrentCellPartOfRow) {
3102: rowCells.add(cell);
3103: isAdded = true;
3104: }
3105:
3106: if (isEndOfRow) {
3107: if (!rowCells.isEmpty()) {
3108: // add to rowlist
3109: rows.add(rowCells);
3110: }
3111:
3112: // start a new list for next line
3113: rowCells = new ArrayList();
3114: }
3115:
3116: if (!isAdded) {
3117: rowCells.add(cell);
3118: }
3119:
3120: previousCell = cell;
3121: }
3122:
3123: if (!rowCells.isEmpty()) {
3124: rows.add(rowCells);
3125: }
3126:
3127: // fill row information with rowspan cells to get complete "scan lines"
3128: for (int i = rows.size() - 1; i >= 0; i--) {
3129: ArrayList row = (ArrayList) rows.get(i);
3130: // iterator through row
3131: for (int j = 0; j < row.size(); j++) {
3132: PdfCell c = (PdfCell) row.get(j);
3133: int rowspan = c.rowspan();
3134: // fill in missing rowspan cells to complete "scan line"
3135: for (int k = 1; k < rowspan && rows.size() < i + k; k++) {
3136: ArrayList spannedRow = ((ArrayList) rows.get(i + k));
3137: if (spannedRow.size() > j)
3138: spannedRow.add(j, c);
3139: }
3140: }
3141: }
3142:
3143: return rows;
3144: }
3145:
3146: private void renderCells(RenderingContext ctx,
3147: java.util.List cells, boolean hasToFit)
3148: throws DocumentException {
3149: PdfCell cell;
3150: Iterator iterator;
3151: if (hasToFit) {
3152: iterator = cells.iterator();
3153: while (iterator.hasNext()) {
3154: cell = (PdfCell) iterator.next();
3155: if (!cell.isHeader()) {
3156: if (cell.getBottom() < indentBottom())
3157: return;
3158: }
3159: }
3160: }
3161: iterator = cells.iterator();
3162:
3163: while (iterator.hasNext()) {
3164: cell = (PdfCell) iterator.next();
3165: if (!ctx.isCellRenderedOnPage(cell, getPageNumber())) {
3166:
3167: float correction = 0;
3168: if (ctx.numCellRendered(cell) >= 1) {
3169: correction = 1.0f;
3170: }
3171:
3172: lines = cell.getLines(ctx.pagetop, indentBottom()
3173: - correction);
3174:
3175: // if there is still text to render we render it
3176: if (lines != null && !lines.isEmpty()) {
3177:
3178: // we write the text
3179: float cellTop = cell.getTop(ctx.pagetop
3180: - ctx.oldHeight);
3181: text.moveText(0, cellTop);
3182: float cellDisplacement = flushLines() - cellTop;
3183:
3184: text.moveText(0, cellDisplacement);
3185: if (ctx.oldHeight + cellDisplacement > currentHeight) {
3186: currentHeight = ctx.oldHeight
3187: + cellDisplacement;
3188: }
3189:
3190: ctx.cellRendered(cell, getPageNumber());
3191: }
3192: float indentBottom = Math.max(cell.getBottom(),
3193: indentBottom());
3194: Rectangle tableRect = ctx.table.rectangle(ctx.pagetop,
3195: indentBottom());
3196: indentBottom = Math.max(tableRect.getBottom(),
3197: indentBottom);
3198:
3199: // we paint the borders of the cells
3200: Rectangle cellRect = cell.rectangle(tableRect.getTop(),
3201: indentBottom);
3202: //cellRect.setBottom(cellRect.bottom());
3203: if (cellRect.getHeight() > 0) {
3204: ctx.lostTableBottom = indentBottom;
3205: ctx.cellGraphics.rectangle(cellRect);
3206: }
3207:
3208: // and additional graphics
3209: ArrayList images = cell.getImages(ctx.pagetop,
3210: indentBottom());
3211: for (Iterator i = images.iterator(); i.hasNext();) {
3212: Image image = (Image) i.next();
3213: graphics.addImage(image);
3214: }
3215:
3216: }
3217: }
3218: }
3219:
3220: /**
3221: * @see PdfWriter#breakTableIfDoesntFit(PdfTable)
3222: * (contributed by dperezcar@fcc.es)
3223: * @param table Table to add
3224: * @return true if the table will be broken
3225: * @throws DocumentException
3226: * @deprecated
3227: */
3228:
3229: boolean breakTableIfDoesntFit(PdfTable table)
3230: throws DocumentException {
3231: table.updateRowAdditions();
3232: // Do we have any full page available?
3233: if (!table.hasToFitPageTable()
3234: && table.getBottom() <= indentation.indentBottom) {
3235: // Then output that page
3236: add(table, true);
3237: return true;
3238: }
3239: return false;
3240: }
3241:
3242: /**
3243: * Returns the bottomvalue of a <CODE>Table</CODE> if it were added to this document.
3244: *
3245: * @param table the table that may or may not be added to this document
3246: * @return a bottom value
3247: * @deprecated
3248: */
3249:
3250: float bottom(Table table) {
3251: // constructing a PdfTable
3252: PdfTable tmp = getPdfTable(table, false);
3253: return tmp.getBottom();
3254: }
3255:
3256: // [M5] header/footer
3257: private void doFooter() throws DocumentException {
3258: if (footer == null)
3259: return;
3260: // Begin added by Edgar Leonardo Prieto Perilla
3261: // Avoid footer identation
3262: float tmpIndentLeft = indentation.indentLeft;
3263: float tmpIndentRight = indentation.indentRight;
3264: // Begin added: Bonf (Marc Schneider) 2003-07-29
3265: float tmpListIndentLeft = indentation.listIndentLeft;
3266: float tmpImageIndentLeft = indentation.imageIndentLeft;
3267: float tmpImageIndentRight = indentation.imageIndentRight;
3268: // End added: Bonf (Marc Schneider) 2003-07-29
3269:
3270: indentation.indentLeft = indentation.indentRight = 0;
3271: // Begin added: Bonf (Marc Schneider) 2003-07-29
3272: indentation.listIndentLeft = 0;
3273: indentation.imageIndentLeft = 0;
3274: indentation.imageIndentRight = 0;
3275: // End added: Bonf (Marc Schneider) 2003-07-29
3276: // End Added by Edgar Leonardo Prieto Perilla
3277: footer.setPageNumber(pageN);
3278: leading = footer.paragraph().getTotalLeading();
3279: add(footer.paragraph());
3280: // adding the footer limits the height
3281: indentation.indentBottom = currentHeight;
3282: text.moveText(left(), indentBottom());
3283: flushLines();
3284: text.moveText(-left(), -bottom());
3285: footer.setTop(bottom(currentHeight));
3286: footer.setBottom(bottom() - (0.75f * leading));
3287: footer.setLeft(left());
3288: footer.setRight(right());
3289: graphics.rectangle(footer);
3290: indentation.indentBottom = currentHeight + leading * 2;
3291: currentHeight = 0;
3292: // Begin added by Edgar Leonardo Prieto Perilla
3293: indentation.indentLeft = tmpIndentLeft;
3294: indentation.indentRight = tmpIndentRight;
3295: // Begin added: Bonf (Marc Schneider) 2003-07-29
3296: indentation.listIndentLeft = tmpListIndentLeft;
3297: indentation.imageIndentLeft = tmpImageIndentLeft;
3298: indentation.imageIndentRight = tmpImageIndentRight;
3299: // End added: Bonf (Marc Schneider) 2003-07-29
3300: // End added by Edgar Leonardo Prieto Perilla
3301: }
3302:
3303: private void doHeader() throws DocumentException {
3304: // if there is a header, the header = added
3305: if (header == null)
3306: return;
3307: // Begin added by Edgar Leonardo Prieto Perilla
3308: // Avoid header identation
3309: float tmpIndentLeft = indentation.indentLeft;
3310: float tmpIndentRight = indentation.indentRight;
3311: // Begin added: Bonf (Marc Schneider) 2003-07-29
3312: float tmpListIndentLeft = indentation.listIndentLeft;
3313: float tmpImageIndentLeft = indentation.imageIndentLeft;
3314: float tmpImageIndentRight = indentation.imageIndentRight;
3315: // End added: Bonf (Marc Schneider) 2003-07-29
3316: indentation.indentLeft = indentation.indentRight = 0;
3317: // Added: Bonf
3318: indentation.listIndentLeft = 0;
3319: indentation.imageIndentLeft = 0;
3320: indentation.imageIndentRight = 0;
3321: // End added: Bonf
3322: // Begin added by Edgar Leonardo Prieto Perilla
3323: header.setPageNumber(pageN);
3324: leading = header.paragraph().getTotalLeading();
3325: text.moveText(0, leading);
3326: add(header.paragraph());
3327: newLine();
3328: indentation.indentTop = currentHeight - leading;
3329: header.setTop(top() + leading);
3330: header.setBottom(indentTop() + leading * 2 / 3);
3331: header.setLeft(left());
3332: header.setRight(right());
3333: graphics.rectangle(header);
3334: flushLines();
3335: currentHeight = 0;
3336: // Begin added by Edgar Leonardo Prieto Perilla
3337: // Restore identation
3338: indentation.indentLeft = tmpIndentLeft;
3339: indentation.indentRight = tmpIndentRight;
3340: // Begin added: Bonf (Marc Schneider) 2003-07-29
3341: indentation.listIndentLeft = tmpListIndentLeft;
3342: indentation.imageIndentLeft = tmpImageIndentLeft;
3343: indentation.imageIndentRight = tmpImageIndentRight;
3344: // End added: Bonf (Marc Schneider) 2003-07-29
3345: // End Added by Edgar Leonardo Prieto Perilla
3346: }
3347: }
|