0001: /*
0002: * $Id: ColumnText.java 2742 2007-05-08 13:04:56Z blowagie $
0003: * $Name$
0004: *
0005: * Copyright 2001, 2002 by Paulo Soares.
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.util.ArrayList;
0054: import java.util.Iterator;
0055: import java.util.LinkedList;
0056: import java.util.Stack;
0057:
0058: import com.lowagie.text.Chunk;
0059: import com.lowagie.text.DocumentException;
0060: import com.lowagie.text.Element;
0061: import com.lowagie.text.ExceptionConverter;
0062: import com.lowagie.text.Image;
0063: import com.lowagie.text.ListItem;
0064: import com.lowagie.text.Paragraph;
0065: import com.lowagie.text.Phrase;
0066: import com.lowagie.text.SimpleTable;
0067:
0068: /**
0069: * Formats text in a columnwise form. The text is bound
0070: * on the left and on the right by a sequence of lines. This allows the column
0071: * to have any shape, not only rectangular.
0072: * <P>
0073: * Several parameters can be set like the first paragraph line indent and
0074: * extra space between paragraphs.
0075: * <P>
0076: * A call to the method <CODE>go</CODE> will return one of the following
0077: * situations: the column ended or the text ended.
0078: * <P>
0079: * I the column ended, a new column definition can be loaded with the method
0080: * <CODE>setColumns</CODE> and the method <CODE>go</CODE> can be called again.
0081: * <P>
0082: * If the text ended, more text can be loaded with <CODE>addText</CODE>
0083: * and the method <CODE>go</CODE> can be called again.<BR>
0084: * The only limitation is that one or more complete paragraphs must be loaded
0085: * each time.
0086: * <P>
0087: * Full bidirectional reordering is supported. If the run direction is
0088: * <CODE>PdfWriter.RUN_DIRECTION_RTL</CODE> the meaning of the horizontal
0089: * alignments and margins is mirrored.
0090: * @author Paulo Soares (psoares@consiste.pt)
0091: */
0092:
0093: public class ColumnText {
0094: /** Eliminate the arabic vowels */
0095: public static final int AR_NOVOWEL = ArabicLigaturizer.ar_novowel;
0096: /** Compose the tashkeel in the ligatures. */
0097: public static final int AR_COMPOSEDTASHKEEL = ArabicLigaturizer.ar_composedtashkeel;
0098: /** Do some extra double ligatures. */
0099: public static final int AR_LIG = ArabicLigaturizer.ar_lig;
0100: /**
0101: * Digit shaping option: Replace European digits (U+0030...U+0039) by Arabic-Indic digits.
0102: */
0103: public static final int DIGITS_EN2AN = ArabicLigaturizer.DIGITS_EN2AN;
0104:
0105: /**
0106: * Digit shaping option: Replace Arabic-Indic digits by European digits (U+0030...U+0039).
0107: */
0108: public static final int DIGITS_AN2EN = ArabicLigaturizer.DIGITS_AN2EN;
0109:
0110: /**
0111: * Digit shaping option:
0112: * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
0113: * if the most recent strongly directional character
0114: * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
0115: * The initial state at the start of the text is assumed to be not an Arabic,
0116: * letter, so European digits at the start of the text will not change.
0117: * Compare to DIGITS_ALEN2AN_INIT_AL.
0118: */
0119: public static final int DIGITS_EN2AN_INIT_LR = ArabicLigaturizer.DIGITS_EN2AN_INIT_LR;
0120:
0121: /**
0122: * Digit shaping option:
0123: * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
0124: * if the most recent strongly directional character
0125: * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
0126: * The initial state at the start of the text is assumed to be an Arabic,
0127: * letter, so European digits at the start of the text will change.
0128: * Compare to DIGITS_ALEN2AN_INT_LR.
0129: */
0130: public static final int DIGITS_EN2AN_INIT_AL = ArabicLigaturizer.DIGITS_EN2AN_INIT_AL;
0131:
0132: /**
0133: * Digit type option: Use Arabic-Indic digits (U+0660...U+0669).
0134: */
0135: public static final int DIGIT_TYPE_AN = ArabicLigaturizer.DIGIT_TYPE_AN;
0136:
0137: /**
0138: * Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9).
0139: */
0140: public static final int DIGIT_TYPE_AN_EXTENDED = ArabicLigaturizer.DIGIT_TYPE_AN_EXTENDED;
0141:
0142: protected int runDirection = PdfWriter.RUN_DIRECTION_DEFAULT;
0143:
0144: /** the space char ratio */
0145: public static final float GLOBAL_SPACE_CHAR_RATIO = 0;
0146:
0147: /** Initial value of the status. */
0148: public static final int START_COLUMN = 0;
0149:
0150: /** Signals that there is no more text available. */
0151: public static final int NO_MORE_TEXT = 1;
0152:
0153: /** Signals that there is no more column. */
0154: public static final int NO_MORE_COLUMN = 2;
0155:
0156: /** The column is valid. */
0157: protected static final int LINE_STATUS_OK = 0;
0158:
0159: /** The line is out the column limits. */
0160: protected static final int LINE_STATUS_OFFLIMITS = 1;
0161:
0162: /** The line cannot fit this column position. */
0163: protected static final int LINE_STATUS_NOLINE = 2;
0164:
0165: /** Upper bound of the column. */
0166: protected float maxY;
0167:
0168: /** Lower bound of the column. */
0169: protected float minY;
0170:
0171: protected float leftX;
0172:
0173: protected float rightX;
0174:
0175: /** The column alignment. Default is left alignment. */
0176: protected int alignment = Element.ALIGN_LEFT;
0177:
0178: /** The left column bound. */
0179: protected ArrayList leftWall;
0180:
0181: /** The right column bound. */
0182: protected ArrayList rightWall;
0183:
0184: /** The chunks that form the text. */
0185: // protected ArrayList chunks = new ArrayList();
0186: protected BidiLine bidiLine;
0187:
0188: /** The current y line location. Text will be written at this line minus the leading. */
0189: protected float yLine;
0190:
0191: /** The leading for the current line. */
0192: protected float currentLeading = 16;
0193:
0194: /** The fixed text leading. */
0195: protected float fixedLeading = 16;
0196:
0197: /** The text leading that is multiplied by the biggest font size in the line. */
0198: protected float multipliedLeading = 0;
0199:
0200: /** The <CODE>PdfContent</CODE> where the text will be written to. */
0201: protected PdfContentByte canvas;
0202:
0203: protected PdfContentByte[] canvases;
0204:
0205: /** The line status when trying to fit a line to a column. */
0206: protected int lineStatus;
0207:
0208: /** The first paragraph line indent. */
0209: protected float indent = 0;
0210:
0211: /** The following paragraph lines indent. */
0212: protected float followingIndent = 0;
0213:
0214: /** The right paragraph lines indent. */
0215: protected float rightIndent = 0;
0216:
0217: /** The extra space between paragraphs. */
0218: protected float extraParagraphSpace = 0;
0219:
0220: /** The width of the line when the column is defined as a simple rectangle. */
0221: protected float rectangularWidth = -1;
0222:
0223: protected boolean rectangularMode = false;
0224: /** Holds value of property spaceCharRatio. */
0225: private float spaceCharRatio = GLOBAL_SPACE_CHAR_RATIO;
0226:
0227: private boolean lastWasNewline = true;
0228:
0229: /** Holds value of property linesWritten. */
0230: private int linesWritten;
0231:
0232: private float firstLineY;
0233: private boolean firstLineYDone = false;
0234:
0235: /** Holds value of property arabicOptions. */
0236: private int arabicOptions = 0;
0237:
0238: protected float descender;
0239:
0240: protected boolean composite = false;
0241:
0242: protected ColumnText compositeColumn;
0243:
0244: protected LinkedList compositeElements;
0245:
0246: protected int listIdx = 0;
0247:
0248: private boolean splittedRow;
0249:
0250: protected Phrase waitPhrase;
0251:
0252: /** if true, first line height is adjusted so that the max ascender touches the top */
0253: private boolean useAscender = false;
0254:
0255: /**
0256: * Creates a <CODE>ColumnText</CODE>.
0257: * @param canvas the place where the text will be written to. Can
0258: * be a template.
0259: */
0260: public ColumnText(PdfContentByte canvas) {
0261: this .canvas = canvas;
0262: }
0263:
0264: /** Creates an independent duplicated of the instance <CODE>org</CODE>.
0265: * @param org the original <CODE>ColumnText</CODE>
0266: * @return the duplicated
0267: */
0268: public static ColumnText duplicate(ColumnText org) {
0269: ColumnText ct = new ColumnText(null);
0270: ct.setACopy(org);
0271: return ct;
0272: }
0273:
0274: /** Makes this instance an independent copy of <CODE>org</CODE>.
0275: * @param org the original <CODE>ColumnText</CODE>
0276: * @return itself
0277: */
0278: public ColumnText setACopy(ColumnText org) {
0279: setSimpleVars(org);
0280: if (org.bidiLine != null)
0281: bidiLine = new BidiLine(org.bidiLine);
0282: return this ;
0283: }
0284:
0285: protected void setSimpleVars(ColumnText org) {
0286: maxY = org.maxY;
0287: minY = org.minY;
0288: alignment = org.alignment;
0289: leftWall = null;
0290: if (org.leftWall != null)
0291: leftWall = new ArrayList(org.leftWall);
0292: rightWall = null;
0293: if (org.rightWall != null)
0294: rightWall = new ArrayList(org.rightWall);
0295: yLine = org.yLine;
0296: currentLeading = org.currentLeading;
0297: fixedLeading = org.fixedLeading;
0298: multipliedLeading = org.multipliedLeading;
0299: canvas = org.canvas;
0300: canvases = org.canvases;
0301: lineStatus = org.lineStatus;
0302: indent = org.indent;
0303: followingIndent = org.followingIndent;
0304: rightIndent = org.rightIndent;
0305: extraParagraphSpace = org.extraParagraphSpace;
0306: rectangularWidth = org.rectangularWidth;
0307: rectangularMode = org.rectangularMode;
0308: spaceCharRatio = org.spaceCharRatio;
0309: lastWasNewline = org.lastWasNewline;
0310: linesWritten = org.linesWritten;
0311: arabicOptions = org.arabicOptions;
0312: runDirection = org.runDirection;
0313: descender = org.descender;
0314: composite = org.composite;
0315: splittedRow = org.splittedRow;
0316: if (org.composite) {
0317: compositeElements = new LinkedList(org.compositeElements);
0318: if (splittedRow) {
0319: PdfPTable table = (PdfPTable) compositeElements
0320: .getFirst();
0321: compositeElements.set(0, new PdfPTable(table));
0322: }
0323: if (org.compositeColumn != null)
0324: compositeColumn = duplicate(org.compositeColumn);
0325: }
0326: listIdx = org.listIdx;
0327: firstLineY = org.firstLineY;
0328: leftX = org.leftX;
0329: rightX = org.rightX;
0330: firstLineYDone = org.firstLineYDone;
0331: waitPhrase = org.waitPhrase;
0332: useAscender = org.useAscender;
0333: filledWidth = org.filledWidth;
0334: }
0335:
0336: private void addWaitingPhrase() {
0337: if (bidiLine == null && waitPhrase != null) {
0338: bidiLine = new BidiLine();
0339: for (Iterator j = waitPhrase.getChunks().iterator(); j
0340: .hasNext();) {
0341: bidiLine.addChunk(new PdfChunk((Chunk) j.next(), null));
0342: }
0343: waitPhrase = null;
0344: }
0345: }
0346:
0347: /**
0348: * Adds a <CODE>Phrase</CODE> to the current text array.
0349: * Will not have any effect if addElement() was called before.
0350: * @param phrase the text
0351: */
0352: public void addText(Phrase phrase) {
0353: if (phrase == null || composite)
0354: return;
0355: addWaitingPhrase();
0356: if (bidiLine == null) {
0357: waitPhrase = phrase;
0358: return;
0359: }
0360: for (Iterator j = phrase.getChunks().iterator(); j.hasNext();) {
0361: bidiLine.addChunk(new PdfChunk((Chunk) j.next(), null));
0362: }
0363: }
0364:
0365: /**
0366: * Replaces the current text array with this <CODE>Phrase</CODE>.
0367: * Anything added previously with addElement() is lost.
0368: * @param phrase the text
0369: */
0370: public void setText(Phrase phrase) {
0371: bidiLine = null;
0372: composite = false;
0373: compositeColumn = null;
0374: compositeElements = null;
0375: listIdx = 0;
0376: splittedRow = false;
0377: waitPhrase = phrase;
0378: }
0379:
0380: /**
0381: * Adds a <CODE>Chunk</CODE> to the current text array.
0382: * Will not have any effect if addElement() was called before.
0383: * @param chunk the text
0384: */
0385: public void addText(Chunk chunk) {
0386: if (chunk == null || composite)
0387: return;
0388: addText(new Phrase(chunk));
0389: }
0390:
0391: /**
0392: * Adds an element. Elements supported are <CODE>Paragraph</CODE>,
0393: * <CODE>List</CODE>, <CODE>PdfPTable</CODE>, <CODE>Image</CODE> and
0394: * <CODE>Graphic</CODE>.
0395: * <p>
0396: * It removes all the text placed with <CODE>addText()</CODE>.
0397: * @param element the <CODE>Element</CODE>
0398: */
0399: public void addElement(Element element) {
0400: if (element == null)
0401: return;
0402: if (element instanceof Image) {
0403: Image img = (Image) element;
0404: PdfPTable t = new PdfPTable(1);
0405: float w = img.getWidthPercentage();
0406: if (w == 0) {
0407: t.setTotalWidth(img.getScaledWidth());
0408: t.setLockedWidth(true);
0409: } else
0410: t.setWidthPercentage(w);
0411: t.setSpacingAfter(img.getSpacingAfter());
0412: t.setSpacingBefore(img.getSpacingBefore());
0413: switch (img.getAlignment()) {
0414: case Image.LEFT:
0415: t.setHorizontalAlignment(Element.ALIGN_LEFT);
0416: break;
0417: case Image.RIGHT:
0418: t.setHorizontalAlignment(Element.ALIGN_RIGHT);
0419: break;
0420: default:
0421: t.setHorizontalAlignment(Element.ALIGN_CENTER);
0422: break;
0423: }
0424: PdfPCell c = new PdfPCell(img, true);
0425: c.setPadding(0);
0426: c.setBorder(img.getBorder());
0427: c.setBorderColor(img.getBorderColor());
0428: c.setBorderWidth(img.getBorderWidth());
0429: c.setBackgroundColor(img.getBackgroundColor());
0430: t.addCell(c);
0431: element = t;
0432: }
0433: if (element.type() == Element.CHUNK) {
0434: element = new Paragraph((Chunk) element);
0435: } else if (element.type() == Element.PHRASE) {
0436: element = new Paragraph((Phrase) element);
0437: }
0438: if (element instanceof SimpleTable) {
0439: try {
0440: element = ((SimpleTable) element).createPdfPTable();
0441: } catch (DocumentException e) {
0442: throw new IllegalArgumentException(
0443: "Element not allowed.");
0444: }
0445: } else if (element.type() != Element.PARAGRAPH
0446: && element.type() != Element.LIST
0447: && element.type() != Element.PTABLE)
0448: throw new IllegalArgumentException("Element not allowed.");
0449: if (!composite) {
0450: composite = true;
0451: compositeElements = new LinkedList();
0452: bidiLine = null;
0453: waitPhrase = null;
0454: }
0455: compositeElements.add(element);
0456: }
0457:
0458: /**
0459: * Converts a sequence of lines representing one of the column bounds into
0460: * an internal format.
0461: * <p>
0462: * Each array element will contain a <CODE>float[4]</CODE> representing
0463: * the line x = ax + b.
0464: * @param cLine the column array
0465: * @return the converted array
0466: */
0467: protected ArrayList convertColumn(float cLine[]) {
0468: if (cLine.length < 4)
0469: throw new RuntimeException("No valid column line found.");
0470: ArrayList cc = new ArrayList();
0471: for (int k = 0; k < cLine.length - 2; k += 2) {
0472: float x1 = cLine[k];
0473: float y1 = cLine[k + 1];
0474: float x2 = cLine[k + 2];
0475: float y2 = cLine[k + 3];
0476: if (y1 == y2)
0477: continue;
0478: // x = ay + b
0479: float a = (x1 - x2) / (y1 - y2);
0480: float b = x1 - a * y1;
0481: float r[] = new float[4];
0482: r[0] = Math.min(y1, y2);
0483: r[1] = Math.max(y1, y2);
0484: r[2] = a;
0485: r[3] = b;
0486: cc.add(r);
0487: maxY = Math.max(maxY, r[1]);
0488: minY = Math.min(minY, r[0]);
0489: }
0490: if (cc.isEmpty())
0491: throw new RuntimeException("No valid column line found.");
0492: return cc;
0493: }
0494:
0495: /**
0496: * Finds the intersection between the <CODE>yLine</CODE> and the column. It will
0497: * set the <CODE>lineStatus</CODE> apropriatly.
0498: * @param wall the column to intersect
0499: * @return the x coordinate of the intersection
0500: */
0501: protected float findLimitsPoint(ArrayList wall) {
0502: lineStatus = LINE_STATUS_OK;
0503: if (yLine < minY || yLine > maxY) {
0504: lineStatus = LINE_STATUS_OFFLIMITS;
0505: return 0;
0506: }
0507: for (int k = 0; k < wall.size(); ++k) {
0508: float r[] = (float[]) wall.get(k);
0509: if (yLine < r[0] || yLine > r[1])
0510: continue;
0511: return r[2] * yLine + r[3];
0512: }
0513: lineStatus = LINE_STATUS_NOLINE;
0514: return 0;
0515: }
0516:
0517: /**
0518: * Finds the intersection between the <CODE>yLine</CODE> and the two
0519: * column bounds. It will set the <CODE>lineStatus</CODE> apropriatly.
0520: * @return a <CODE>float[2]</CODE>with the x coordinates of the intersection
0521: */
0522: protected float[] findLimitsOneLine() {
0523: float x1 = findLimitsPoint(leftWall);
0524: if (lineStatus == LINE_STATUS_OFFLIMITS
0525: || lineStatus == LINE_STATUS_NOLINE)
0526: return null;
0527: float x2 = findLimitsPoint(rightWall);
0528: if (lineStatus == LINE_STATUS_NOLINE)
0529: return null;
0530: return new float[] { x1, x2 };
0531: }
0532:
0533: /**
0534: * Finds the intersection between the <CODE>yLine</CODE>,
0535: * the <CODE>yLine-leading</CODE>and the two
0536: * column bounds. It will set the <CODE>lineStatus</CODE> apropriatly.
0537: * @return a <CODE>float[4]</CODE>with the x coordinates of the intersection
0538: */
0539: protected float[] findLimitsTwoLines() {
0540: boolean repeat = false;
0541: for (;;) {
0542: if (repeat && currentLeading == 0)
0543: return null;
0544: repeat = true;
0545: float x1[] = findLimitsOneLine();
0546: if (lineStatus == LINE_STATUS_OFFLIMITS)
0547: return null;
0548: yLine -= currentLeading;
0549: if (lineStatus == LINE_STATUS_NOLINE) {
0550: continue;
0551: }
0552: float x2[] = findLimitsOneLine();
0553: if (lineStatus == LINE_STATUS_OFFLIMITS)
0554: return null;
0555: if (lineStatus == LINE_STATUS_NOLINE) {
0556: yLine -= currentLeading;
0557: continue;
0558: }
0559: if (x1[0] >= x2[1] || x2[0] >= x1[1])
0560: continue;
0561: return new float[] { x1[0], x1[1], x2[0], x2[1] };
0562: }
0563: }
0564:
0565: /**
0566: * Sets the columns bounds. Each column bound is described by a
0567: * <CODE>float[]</CODE> with the line points [x1,y1,x2,y2,...].
0568: * The array must have at least 4 elements.
0569: * @param leftLine the left column bound
0570: * @param rightLine the right column bound
0571: */
0572: public void setColumns(float leftLine[], float rightLine[]) {
0573: maxY = -10e20f;
0574: minY = 10e20f;
0575: rightWall = convertColumn(rightLine);
0576: leftWall = convertColumn(leftLine);
0577: rectangularWidth = -1;
0578: rectangularMode = false;
0579: }
0580:
0581: /**
0582: * Simplified method for rectangular columns.
0583: * @param phrase a <CODE>Phrase</CODE>
0584: * @param llx the lower left x corner
0585: * @param lly the lower left y corner
0586: * @param urx the upper right x corner
0587: * @param ury the upper right y corner
0588: * @param leading the leading
0589: * @param alignment the column alignment
0590: */
0591: public void setSimpleColumn(Phrase phrase, float llx, float lly,
0592: float urx, float ury, float leading, int alignment) {
0593: addText(phrase);
0594: setSimpleColumn(llx, lly, urx, ury, leading, alignment);
0595: }
0596:
0597: /**
0598: * Simplified method for rectangular columns.
0599: * @param llx the lower left x corner
0600: * @param lly the lower left y corner
0601: * @param urx the upper right x corner
0602: * @param ury the upper right y corner
0603: * @param leading the leading
0604: * @param alignment the column alignment
0605: */
0606: public void setSimpleColumn(float llx, float lly, float urx,
0607: float ury, float leading, int alignment) {
0608: setLeading(leading);
0609: this .alignment = alignment;
0610: setSimpleColumn(llx, lly, urx, ury);
0611: }
0612:
0613: /**
0614: * Simplified method for rectangular columns.
0615: * @param llx
0616: * @param lly
0617: * @param urx
0618: * @param ury
0619: */
0620: public void setSimpleColumn(float llx, float lly, float urx,
0621: float ury) {
0622: leftX = Math.min(llx, urx);
0623: maxY = Math.max(lly, ury);
0624: minY = Math.min(lly, ury);
0625: rightX = Math.max(llx, urx);
0626: yLine = maxY;
0627: rectangularWidth = rightX - leftX;
0628: if (rectangularWidth < 0)
0629: rectangularWidth = 0;
0630: rectangularMode = true;
0631: }
0632:
0633: /**
0634: * Sets the leading to fixed
0635: * @param leading the leading
0636: */
0637: public void setLeading(float leading) {
0638: fixedLeading = leading;
0639: multipliedLeading = 0;
0640: }
0641:
0642: /**
0643: * Sets the leading fixed and variable. The resultant leading will be
0644: * fixedLeading+multipliedLeading*maxFontSize where maxFontSize is the
0645: * size of the bigest font in the line.
0646: * @param fixedLeading the fixed leading
0647: * @param multipliedLeading the variable leading
0648: */
0649: public void setLeading(float fixedLeading, float multipliedLeading) {
0650: this .fixedLeading = fixedLeading;
0651: this .multipliedLeading = multipliedLeading;
0652: }
0653:
0654: /**
0655: * Gets the fixed leading
0656: * @return the leading
0657: */
0658: public float getLeading() {
0659: return fixedLeading;
0660: }
0661:
0662: /**
0663: * Gets the variable leading
0664: * @return the leading
0665: */
0666: public float getMultipliedLeading() {
0667: return multipliedLeading;
0668: }
0669:
0670: /**
0671: * Sets the yLine. The line will be written to yLine-leading.
0672: * @param yLine the yLine
0673: */
0674: public void setYLine(float yLine) {
0675: this .yLine = yLine;
0676: }
0677:
0678: /**
0679: * Gets the yLine.
0680: * @return the yLine
0681: */
0682: public float getYLine() {
0683: return yLine;
0684: }
0685:
0686: /**
0687: * Sets the alignment.
0688: * @param alignment the alignment
0689: */
0690: public void setAlignment(int alignment) {
0691: this .alignment = alignment;
0692: }
0693:
0694: /**
0695: * Gets the alignment.
0696: * @return the alignment
0697: */
0698: public int getAlignment() {
0699: return alignment;
0700: }
0701:
0702: /**
0703: * Sets the first paragraph line indent.
0704: * @param indent the indent
0705: */
0706: public void setIndent(float indent) {
0707: this .indent = indent;
0708: lastWasNewline = true;
0709: }
0710:
0711: /**
0712: * Gets the first paragraph line indent.
0713: * @return the indent
0714: */
0715: public float getIndent() {
0716: return indent;
0717: }
0718:
0719: /**
0720: * Sets the following paragraph lines indent.
0721: * @param indent the indent
0722: */
0723: public void setFollowingIndent(float indent) {
0724: this .followingIndent = indent;
0725: lastWasNewline = true;
0726: }
0727:
0728: /**
0729: * Gets the following paragraph lines indent.
0730: * @return the indent
0731: */
0732: public float getFollowingIndent() {
0733: return followingIndent;
0734: }
0735:
0736: /**
0737: * Sets the right paragraph lines indent.
0738: * @param indent the indent
0739: */
0740: public void setRightIndent(float indent) {
0741: this .rightIndent = indent;
0742: lastWasNewline = true;
0743: }
0744:
0745: /**
0746: * Gets the right paragraph lines indent.
0747: * @return the indent
0748: */
0749: public float getRightIndent() {
0750: return rightIndent;
0751: }
0752:
0753: /**
0754: * Outputs the lines to the document. It is equivalent to <CODE>go(false)</CODE>.
0755: * @return returns the result of the operation. It can be <CODE>NO_MORE_TEXT</CODE>
0756: * and/or <CODE>NO_MORE_COLUMN</CODE>
0757: * @throws DocumentException on error
0758: */
0759: public int go() throws DocumentException {
0760: return go(false);
0761: }
0762:
0763: /**
0764: * Outputs the lines to the document. The output can be simulated.
0765: * @param simulate <CODE>true</CODE> to simulate the writting to the document
0766: * @return returns the result of the operation. It can be <CODE>NO_MORE_TEXT</CODE>
0767: * and/or <CODE>NO_MORE_COLUMN</CODE>
0768: * @throws DocumentException on error
0769: */
0770: public int go(boolean simulate) throws DocumentException {
0771: if (composite)
0772: return goComposite(simulate);
0773: addWaitingPhrase();
0774: if (bidiLine == null)
0775: return NO_MORE_TEXT;
0776: descender = 0;
0777: linesWritten = 0;
0778: boolean dirty = false;
0779: float ratio = spaceCharRatio;
0780: Object currentValues[] = new Object[2];
0781: PdfFont currentFont = null;
0782: Float lastBaseFactor = new Float(0);
0783: currentValues[1] = lastBaseFactor;
0784: PdfDocument pdf = null;
0785: PdfContentByte graphics = null;
0786: PdfContentByte text = null;
0787: firstLineY = Float.NaN;
0788: int localRunDirection = PdfWriter.RUN_DIRECTION_NO_BIDI;
0789: if (runDirection != PdfWriter.RUN_DIRECTION_DEFAULT)
0790: localRunDirection = runDirection;
0791: if (canvas != null) {
0792: graphics = canvas;
0793: pdf = canvas.getPdfDocument();
0794: text = canvas.getDuplicate();
0795: } else if (!simulate)
0796: throw new NullPointerException(
0797: "ColumnText.go with simulate==false and text==null.");
0798: if (!simulate) {
0799: if (ratio == GLOBAL_SPACE_CHAR_RATIO)
0800: ratio = text.getPdfWriter().getSpaceCharRatio();
0801: else if (ratio < 0.001f)
0802: ratio = 0.001f;
0803: }
0804: float firstIndent = 0;
0805:
0806: int status = 0;
0807: if (rectangularMode) {
0808: for (;;) {
0809: firstIndent = (lastWasNewline ? indent
0810: : followingIndent);
0811: if (rectangularWidth <= firstIndent + rightIndent) {
0812: status = NO_MORE_COLUMN;
0813: if (bidiLine.isEmpty())
0814: status |= NO_MORE_TEXT;
0815: break;
0816: }
0817: if (bidiLine.isEmpty()) {
0818: status = NO_MORE_TEXT;
0819: break;
0820: }
0821: PdfLine line = bidiLine.processLine(rectangularWidth
0822: - firstIndent - rightIndent, alignment,
0823: localRunDirection, arabicOptions);
0824: if (line == null) {
0825: status = NO_MORE_TEXT;
0826: break;
0827: }
0828: float maxSize = line.getMaxSizeSimple();
0829: if (isUseAscender() && Float.isNaN(firstLineY)) {
0830: currentLeading = line.getAscender();
0831: } else {
0832: currentLeading = fixedLeading + maxSize
0833: * multipliedLeading;
0834: }
0835: if (yLine > maxY || yLine - currentLeading < minY) {
0836: status = NO_MORE_COLUMN;
0837: bidiLine.restore();
0838: break;
0839: }
0840: yLine -= currentLeading;
0841: if (!simulate && !dirty) {
0842: text.beginText();
0843: dirty = true;
0844: }
0845: if (Float.isNaN(firstLineY)) {
0846: firstLineY = yLine;
0847: }
0848: updateFilledWidth(rectangularWidth - line.widthLeft());
0849: if (!simulate) {
0850: currentValues[0] = currentFont;
0851: text.setTextMatrix(
0852: leftX
0853: + (line.isRTL() ? rightIndent
0854: : firstIndent)
0855: + line.indentLeft(), yLine);
0856: pdf.writeLineToContent(line, text, graphics,
0857: currentValues, ratio);
0858: currentFont = (PdfFont) currentValues[0];
0859: }
0860: lastWasNewline = line.isNewlineSplit();
0861: yLine -= line.isNewlineSplit() ? extraParagraphSpace
0862: : 0;
0863: ++linesWritten;
0864: descender = line.getDescender();
0865: }
0866: } else {
0867: currentLeading = fixedLeading;
0868: for (;;) {
0869: firstIndent = (lastWasNewline ? indent
0870: : followingIndent);
0871: float yTemp = yLine;
0872: float xx[] = findLimitsTwoLines();
0873: if (xx == null) {
0874: status = NO_MORE_COLUMN;
0875: if (bidiLine.isEmpty())
0876: status |= NO_MORE_TEXT;
0877: yLine = yTemp;
0878: break;
0879: }
0880: if (bidiLine.isEmpty()) {
0881: status = NO_MORE_TEXT;
0882: yLine = yTemp;
0883: break;
0884: }
0885: float x1 = Math.max(xx[0], xx[2]);
0886: float x2 = Math.min(xx[1], xx[3]);
0887: if (x2 - x1 <= firstIndent + rightIndent)
0888: continue;
0889: if (!simulate && !dirty) {
0890: text.beginText();
0891: dirty = true;
0892: }
0893: PdfLine line = bidiLine.processLine(x2 - x1
0894: - firstIndent - rightIndent, alignment,
0895: localRunDirection, arabicOptions);
0896: if (line == null) {
0897: status = NO_MORE_TEXT;
0898: yLine = yTemp;
0899: break;
0900: }
0901: if (!simulate) {
0902: currentValues[0] = currentFont;
0903: text.setTextMatrix(
0904: x1
0905: + (line.isRTL() ? rightIndent
0906: : firstIndent)
0907: + line.indentLeft(), yLine);
0908: pdf.writeLineToContent(line, text, graphics,
0909: currentValues, ratio);
0910: currentFont = (PdfFont) currentValues[0];
0911: }
0912: lastWasNewline = line.isNewlineSplit();
0913: yLine -= line.isNewlineSplit() ? extraParagraphSpace
0914: : 0;
0915: ++linesWritten;
0916: descender = line.getDescender();
0917: }
0918: }
0919: if (dirty) {
0920: text.endText();
0921: canvas.add(text);
0922: }
0923: return status;
0924: }
0925:
0926: /**
0927: * Sets the extra space between paragraphs.
0928: * @return the extra space between paragraphs
0929: */
0930: public float getExtraParagraphSpace() {
0931: return extraParagraphSpace;
0932: }
0933:
0934: /**
0935: * Sets the extra space between paragraphs.
0936: * @param extraParagraphSpace the extra space between paragraphs
0937: */
0938: public void setExtraParagraphSpace(float extraParagraphSpace) {
0939: this .extraParagraphSpace = extraParagraphSpace;
0940: }
0941:
0942: /**
0943: * Clears the chunk array. A call to <CODE>go()</CODE> will always return
0944: * NO_MORE_TEXT.
0945: */
0946: public void clearChunks() {
0947: if (bidiLine != null)
0948: bidiLine.clearChunks();
0949: }
0950:
0951: /** Gets the space/character extra spacing ratio for
0952: * fully justified text.
0953: * @return the space/character extra spacing ratio
0954: */
0955: public float getSpaceCharRatio() {
0956: return spaceCharRatio;
0957: }
0958:
0959: /** Sets the ratio between the extra word spacing and the extra character spacing
0960: * when the text is fully justified.
0961: * Extra word spacing will grow <CODE>spaceCharRatio</CODE> times more than extra character spacing.
0962: * If the ratio is <CODE>PdfWriter.NO_SPACE_CHAR_RATIO</CODE> then the extra character spacing
0963: * will be zero.
0964: * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
0965: */
0966: public void setSpaceCharRatio(float spaceCharRatio) {
0967: this .spaceCharRatio = spaceCharRatio;
0968: }
0969:
0970: /** Sets the run direction.
0971: * @param runDirection the run direction
0972: */
0973: public void setRunDirection(int runDirection) {
0974: if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT
0975: || runDirection > PdfWriter.RUN_DIRECTION_RTL)
0976: throw new RuntimeException("Invalid run direction: "
0977: + runDirection);
0978: this .runDirection = runDirection;
0979: }
0980:
0981: /** Gets the run direction.
0982: * @return the run direction
0983: */
0984: public int getRunDirection() {
0985: return runDirection;
0986: }
0987:
0988: /** Gets the number of lines written.
0989: * @return the number of lines written
0990: */
0991: public int getLinesWritten() {
0992: return this .linesWritten;
0993: }
0994:
0995: /** Gets the arabic shaping options.
0996: * @return the arabic shaping options
0997: */
0998: public int getArabicOptions() {
0999: return this .arabicOptions;
1000: }
1001:
1002: /** Sets the arabic shaping options. The option can be AR_NOVOWEL,
1003: * AR_COMPOSEDTASHKEEL and AR_LIG.
1004: * @param arabicOptions the arabic shaping options
1005: */
1006: public void setArabicOptions(int arabicOptions) {
1007: this .arabicOptions = arabicOptions;
1008: }
1009:
1010: /** Gets the biggest descender value of the last line written.
1011: * @return the biggest descender value of the last line written
1012: */
1013: public float getDescender() {
1014: return descender;
1015: }
1016:
1017: /** Gets the width that the line will occupy after writing.
1018: * Only the width of the first line is returned.
1019: * @param phrase the <CODE>Phrase</CODE> containing the line
1020: * @param runDirection the run direction
1021: * @param arabicOptions the options for the arabic shaping
1022: * @return the width of the line
1023: */
1024: public static float getWidth(Phrase phrase, int runDirection,
1025: int arabicOptions) {
1026: ColumnText ct = new ColumnText(null);
1027: ct.addText(phrase);
1028: ct.addWaitingPhrase();
1029: PdfLine line = ct.bidiLine.processLine(20000,
1030: Element.ALIGN_LEFT, runDirection, arabicOptions);
1031: if (line == null)
1032: return 0;
1033: else
1034: return 20000 - line.widthLeft();
1035: }
1036:
1037: /** Gets the width that the line will occupy after writing.
1038: * Only the width of the first line is returned.
1039: * @param phrase the <CODE>Phrase</CODE> containing the line
1040: * @return the width of the line
1041: */
1042: public static float getWidth(Phrase phrase) {
1043: return getWidth(phrase, PdfWriter.RUN_DIRECTION_NO_BIDI, 0);
1044: }
1045:
1046: /** Shows a line of text. Only the first line is written.
1047: * @param canvas where the text is to be written to
1048: * @param alignment the alignment. It is not influenced by the run direction
1049: * @param phrase the <CODE>Phrase</CODE> with the text
1050: * @param x the x reference position
1051: * @param y the y reference position
1052: * @param rotation the rotation to be applied in degrees counterclockwise
1053: * @param runDirection the run direction
1054: * @param arabicOptions the options for the arabic shaping
1055: */
1056: public static void showTextAligned(PdfContentByte canvas,
1057: int alignment, Phrase phrase, float x, float y,
1058: float rotation, int runDirection, int arabicOptions) {
1059: if (alignment != Element.ALIGN_LEFT
1060: && alignment != Element.ALIGN_CENTER
1061: && alignment != Element.ALIGN_RIGHT)
1062: alignment = Element.ALIGN_LEFT;
1063: canvas.saveState();
1064: ColumnText ct = new ColumnText(canvas);
1065: if (rotation == 0) {
1066: if (alignment == Element.ALIGN_LEFT)
1067: ct.setSimpleColumn(phrase, x, y - 1, 20000 + x, y + 2,
1068: 2, alignment);
1069: else if (alignment == Element.ALIGN_RIGHT)
1070: ct.setSimpleColumn(phrase, x - 20000, y - 1, x, y + 2,
1071: 2, alignment);
1072: else
1073: ct.setSimpleColumn(phrase, x - 20000, y - 1, x + 20000,
1074: y + 2, 2, alignment);
1075: } else {
1076: double alpha = rotation * Math.PI / 180.0;
1077: float cos = (float) Math.cos(alpha);
1078: float sin = (float) Math.sin(alpha);
1079: canvas.concatCTM(cos, sin, -sin, cos, x, y);
1080: if (alignment == Element.ALIGN_LEFT)
1081: ct.setSimpleColumn(phrase, 0, -1, 20000, 2, 2,
1082: alignment);
1083: else if (alignment == Element.ALIGN_RIGHT)
1084: ct.setSimpleColumn(phrase, -20000, -1, 0, 2, 2,
1085: alignment);
1086: else
1087: ct.setSimpleColumn(phrase, -20000, -1, 20000, 2, 2,
1088: alignment);
1089: }
1090: if (runDirection == PdfWriter.RUN_DIRECTION_RTL) {
1091: if (alignment == Element.ALIGN_LEFT)
1092: alignment = Element.ALIGN_RIGHT;
1093: else if (alignment == Element.ALIGN_RIGHT)
1094: alignment = Element.ALIGN_LEFT;
1095: }
1096: ct.setAlignment(alignment);
1097: ct.setArabicOptions(arabicOptions);
1098: ct.setRunDirection(runDirection);
1099: try {
1100: ct.go();
1101: } catch (DocumentException e) {
1102: throw new ExceptionConverter(e);
1103: }
1104: canvas.restoreState();
1105: }
1106:
1107: /** Shows a line of text. Only the first line is written.
1108: * @param canvas where the text is to be written to
1109: * @param alignment the alignment
1110: * @param phrase the <CODE>Phrase</CODE> with the text
1111: * @param x the x reference position
1112: * @param y the y reference position
1113: * @param rotation the rotation to be applied in degrees counterclockwise
1114: */
1115: public static void showTextAligned(PdfContentByte canvas,
1116: int alignment, Phrase phrase, float x, float y,
1117: float rotation) {
1118: showTextAligned(canvas, alignment, phrase, x, y, rotation,
1119: PdfWriter.RUN_DIRECTION_NO_BIDI, 0);
1120: }
1121:
1122: protected int goComposite(boolean simulate)
1123: throws DocumentException {
1124: if (!rectangularMode)
1125: throw new DocumentException(
1126: "Irregular columns are not supported in composite mode.");
1127: linesWritten = 0;
1128: descender = 0;
1129: boolean firstPass = true;
1130: main_loop: while (true) {
1131: if (compositeElements.isEmpty())
1132: return NO_MORE_TEXT;
1133: Element element = (Element) compositeElements.getFirst();
1134: if (element.type() == Element.PARAGRAPH) {
1135: Paragraph para = (Paragraph) element;
1136: int status = 0;
1137: for (int keep = 0; keep < 2; ++keep) {
1138: float lastY = yLine;
1139: boolean createHere = false;
1140: if (compositeColumn == null) {
1141: compositeColumn = new ColumnText(canvas);
1142: compositeColumn
1143: .setUseAscender(firstPass ? useAscender
1144: : false);
1145: compositeColumn.setAlignment(para
1146: .getAlignment());
1147: compositeColumn.setIndent(para
1148: .getIndentationLeft()
1149: + para.getFirstLineIndent());
1150: compositeColumn.setExtraParagraphSpace(para
1151: .getExtraParagraphSpace());
1152: compositeColumn.setFollowingIndent(para
1153: .getIndentationLeft());
1154: compositeColumn.setRightIndent(para
1155: .getIndentationRight());
1156: compositeColumn.setLeading(para.getLeading(),
1157: para.getMultipliedLeading());
1158: compositeColumn.setRunDirection(runDirection);
1159: compositeColumn.setArabicOptions(arabicOptions);
1160: compositeColumn
1161: .setSpaceCharRatio(spaceCharRatio);
1162: compositeColumn.addText(para);
1163: if (!firstPass) {
1164: yLine -= para.spacingBefore();
1165: }
1166: createHere = true;
1167: }
1168: compositeColumn.leftX = leftX;
1169: compositeColumn.rightX = rightX;
1170: compositeColumn.yLine = yLine;
1171: compositeColumn.rectangularWidth = rectangularWidth;
1172: compositeColumn.rectangularMode = rectangularMode;
1173: compositeColumn.minY = minY;
1174: compositeColumn.maxY = maxY;
1175: boolean keepCandidate = (para.getKeepTogether()
1176: && createHere && !firstPass);
1177: status = compositeColumn.go(simulate
1178: || (keepCandidate && keep == 0));
1179: updateFilledWidth(compositeColumn.filledWidth);
1180: if ((status & NO_MORE_TEXT) == 0 && keepCandidate) {
1181: compositeColumn = null;
1182: yLine = lastY;
1183: return NO_MORE_COLUMN;
1184: }
1185: if (simulate || !keepCandidate)
1186: break;
1187: if (keep == 0) {
1188: compositeColumn = null;
1189: yLine = lastY;
1190: }
1191: }
1192: firstPass = false;
1193: yLine = compositeColumn.yLine;
1194: linesWritten += compositeColumn.linesWritten;
1195: descender = compositeColumn.descender;
1196: if ((status & NO_MORE_TEXT) != 0) {
1197: compositeColumn = null;
1198: compositeElements.removeFirst();
1199: yLine -= para.spacingAfter();
1200: }
1201: if ((status & NO_MORE_COLUMN) != 0) {
1202: return NO_MORE_COLUMN;
1203: }
1204: } else if (element.type() == Element.LIST) {
1205: com.lowagie.text.List list = (com.lowagie.text.List) element;
1206: ArrayList items = list.getItems();
1207: ListItem item = null;
1208: float listIndentation = list.getIndentationLeft();
1209: int count = 0;
1210: Stack stack = new Stack();
1211: for (int k = 0; k < items.size(); ++k) {
1212: Object obj = items.get(k);
1213: if (obj instanceof ListItem) {
1214: if (count == listIdx) {
1215: item = (ListItem) obj;
1216: break;
1217: } else
1218: ++count;
1219: } else if (obj instanceof com.lowagie.text.List) {
1220: stack.push(new Object[] { list, new Integer(k),
1221: new Float(listIndentation) });
1222: list = (com.lowagie.text.List) obj;
1223: items = list.getItems();
1224: listIndentation += list.getIndentationLeft();
1225: k = -1;
1226: continue;
1227: }
1228: if (k == items.size() - 1) {
1229: if (!stack.isEmpty()) {
1230: Object objs[] = (Object[]) stack.pop();
1231: list = (com.lowagie.text.List) objs[0];
1232: items = list.getItems();
1233: k = ((Integer) objs[1]).intValue();
1234: listIndentation = ((Float) objs[2])
1235: .floatValue();
1236: }
1237: }
1238: }
1239: int status = 0;
1240: for (int keep = 0; keep < 2; ++keep) {
1241: float lastY = yLine;
1242: boolean createHere = false;
1243: if (compositeColumn == null) {
1244: if (item == null) {
1245: listIdx = 0;
1246: compositeElements.removeFirst();
1247: continue main_loop;
1248: }
1249: compositeColumn = new ColumnText(canvas);
1250: compositeColumn
1251: .setUseAscender(firstPass ? useAscender
1252: : false);
1253: compositeColumn.setAlignment(item
1254: .getAlignment());
1255: compositeColumn.setIndent(item
1256: .getIndentationLeft()
1257: + listIndentation
1258: + item.getFirstLineIndent());
1259: compositeColumn.setExtraParagraphSpace(item
1260: .getExtraParagraphSpace());
1261: compositeColumn
1262: .setFollowingIndent(compositeColumn
1263: .getIndent());
1264: compositeColumn.setRightIndent(item
1265: .getIndentationRight()
1266: + list.getIndentationRight());
1267: compositeColumn.setLeading(item.getLeading(),
1268: item.getMultipliedLeading());
1269: compositeColumn.setRunDirection(runDirection);
1270: compositeColumn.setArabicOptions(arabicOptions);
1271: compositeColumn
1272: .setSpaceCharRatio(spaceCharRatio);
1273: compositeColumn.addText(item);
1274: if (!firstPass) {
1275: yLine -= item.spacingBefore();
1276: }
1277: createHere = true;
1278: }
1279: compositeColumn.leftX = leftX;
1280: compositeColumn.rightX = rightX;
1281: compositeColumn.yLine = yLine;
1282: compositeColumn.rectangularWidth = rectangularWidth;
1283: compositeColumn.rectangularMode = rectangularMode;
1284: compositeColumn.minY = minY;
1285: compositeColumn.maxY = maxY;
1286: boolean keepCandidate = (item.getKeepTogether()
1287: && createHere && !firstPass);
1288: status = compositeColumn.go(simulate
1289: || (keepCandidate && keep == 0));
1290: updateFilledWidth(compositeColumn.filledWidth);
1291: if ((status & NO_MORE_TEXT) == 0 && keepCandidate) {
1292: compositeColumn = null;
1293: yLine = lastY;
1294: return NO_MORE_COLUMN;
1295: }
1296: if (simulate || !keepCandidate)
1297: break;
1298: if (keep == 0) {
1299: compositeColumn = null;
1300: yLine = lastY;
1301: }
1302: }
1303: firstPass = false;
1304: yLine = compositeColumn.yLine;
1305: linesWritten += compositeColumn.linesWritten;
1306: descender = compositeColumn.descender;
1307: if (!Float.isNaN(compositeColumn.firstLineY)
1308: && !compositeColumn.firstLineYDone) {
1309: if (!simulate)
1310: showTextAligned(
1311: canvas,
1312: Element.ALIGN_LEFT,
1313: new Phrase(item.getListSymbol()),
1314: compositeColumn.leftX + listIndentation,
1315: compositeColumn.firstLineY, 0);
1316: compositeColumn.firstLineYDone = true;
1317: }
1318: if ((status & NO_MORE_TEXT) != 0) {
1319: compositeColumn = null;
1320: ++listIdx;
1321: yLine -= item.spacingAfter();
1322: }
1323: if ((status & NO_MORE_COLUMN) != 0) {
1324: return NO_MORE_COLUMN;
1325: }
1326: } else if (element.type() == Element.PTABLE) {
1327: if (yLine < minY || yLine > maxY)
1328: return NO_MORE_COLUMN;
1329: PdfPTable table = (PdfPTable) element;
1330: if (table.size() <= table.getHeaderRows()) {
1331: compositeElements.removeFirst();
1332: continue;
1333: }
1334: float yTemp = yLine;
1335: float yLineWrite = yLine;
1336: if (!firstPass && listIdx == 0) {
1337: yTemp -= table.spacingBefore();
1338: yLineWrite = yTemp;
1339: }
1340: currentLeading = 0;
1341: if (yTemp < minY || yTemp > maxY)
1342: return NO_MORE_COLUMN;
1343: float x1 = leftX;
1344: float tableWidth;
1345: if (table.isLockedWidth()) {
1346: tableWidth = table.getTotalWidth();
1347: updateFilledWidth(tableWidth);
1348: } else {
1349: tableWidth = rectangularWidth
1350: * table.getWidthPercentage() / 100f;
1351: table.setTotalWidth(tableWidth);
1352: }
1353: int k;
1354: boolean skipHeader = (!firstPass
1355: && table.isSkipFirstHeader() && listIdx <= table
1356: .getHeaderRows());
1357: if (!skipHeader) {
1358: yTemp -= table.getHeaderHeight();
1359: if (yTemp < minY || yTemp > maxY) {
1360: if (firstPass) {
1361: compositeElements.removeFirst();
1362: continue;
1363: }
1364: return NO_MORE_COLUMN;
1365: }
1366: }
1367: if (listIdx < table.getHeaderRows())
1368: listIdx = table.getHeaderRows();
1369: for (k = listIdx; k < table.size(); ++k) {
1370: float rowHeight = table.getRowHeight(k);
1371: if (yTemp - rowHeight < minY)
1372: break;
1373: yTemp -= rowHeight;
1374: }
1375: if (k < table.size()) {
1376: if (table.isSplitRows()
1377: && (!table.isSplitLate() || (k == listIdx && firstPass))) {
1378: if (!splittedRow) {
1379: splittedRow = true;
1380: table = new PdfPTable(table);
1381: compositeElements.set(0, table);
1382: ArrayList rows = table.getRows();
1383: for (int i = table.getHeaderRows(); i < listIdx; ++i)
1384: rows.set(i, null);
1385: }
1386: float h = yTemp - minY;
1387: PdfPRow newRow = table.getRow(k).splitRow(h);
1388: if (newRow == null) {
1389: if (k == listIdx)
1390: return NO_MORE_COLUMN;
1391: } else {
1392: yTemp = minY;
1393: table.getRows().add(++k, newRow);
1394: }
1395: } else if (!table.isSplitRows() && k == listIdx
1396: && firstPass) {
1397: compositeElements.removeFirst();
1398: splittedRow = false;
1399: continue;
1400: } else if (k == listIdx
1401: && !firstPass
1402: && (!table.isSplitRows() || table
1403: .isSplitLate())) {
1404: return NO_MORE_COLUMN;
1405: }
1406: }
1407: firstPass = false;
1408: if (!simulate) {
1409: switch (table.getHorizontalAlignment()) {
1410: case Element.ALIGN_LEFT:
1411: break;
1412: case Element.ALIGN_RIGHT:
1413: x1 += rectangularWidth - tableWidth;
1414: break;
1415: default:
1416: x1 += (rectangularWidth - tableWidth) / 2f;
1417: }
1418: int realHeaderRows = table.getHeaderRows();
1419: int footerRows = table.getFooterRows();
1420: if (footerRows > realHeaderRows)
1421: footerRows = realHeaderRows;
1422: realHeaderRows -= footerRows;
1423: PdfPTable nt = PdfPTable.shallowCopy(table);
1424: ArrayList rows = table.getRows();
1425: ArrayList sub = nt.getRows();
1426: if (!skipHeader) {
1427: for (int j = 0; j < realHeaderRows; ++j)
1428: sub.add(rows.get(j));
1429: } else
1430: nt.setHeaderRows(footerRows);
1431: for (int j = listIdx; j < k; ++j)
1432: sub.add(rows.get(j));
1433: for (int j = 0; j < footerRows; ++j)
1434: sub.add(rows.get(j + realHeaderRows));
1435: float rowHeight = 0;
1436: if (table.isExtendLastRow()) {
1437: PdfPRow last = (PdfPRow) sub.get(sub.size() - 1
1438: - footerRows);
1439: rowHeight = last.getMaxHeights();
1440: last.setMaxHeights(yTemp - minY + rowHeight);
1441: yTemp = minY;
1442: }
1443: if (canvases != null)
1444: nt.writeSelectedRows(0, -1, x1, yLineWrite,
1445: canvases);
1446: else
1447: nt.writeSelectedRows(0, -1, x1, yLineWrite,
1448: canvas);
1449: if (table.isExtendLastRow()) {
1450: PdfPRow last = (PdfPRow) sub.get(sub.size() - 1
1451: - footerRows);
1452: last.setMaxHeights(rowHeight);
1453: }
1454: } else if (table.isExtendLastRow()
1455: && minY > PdfPRow.BOTTOM_LIMIT)
1456: yTemp = minY;
1457: yLine = yTemp;
1458: if (k >= table.size()) {
1459: yLine -= table.spacingAfter();
1460: compositeElements.removeFirst();
1461: splittedRow = false;
1462: listIdx = 0;
1463: } else {
1464: if (splittedRow) {
1465: ArrayList rows = table.getRows();
1466: for (int i = listIdx; i < k; ++i)
1467: rows.set(i, null);
1468: }
1469: listIdx = k;
1470: return NO_MORE_COLUMN;
1471: }
1472: } else
1473: compositeElements.removeFirst();
1474: }
1475: }
1476:
1477: /**
1478: * Gets the canvas.
1479: * @return a PdfContentByte.
1480: */
1481: public PdfContentByte getCanvas() {
1482: return canvas;
1483: }
1484:
1485: /**
1486: * Sets the canvas.
1487: * @param canvas
1488: */
1489: public void setCanvas(PdfContentByte canvas) {
1490: this .canvas = canvas;
1491: this .canvases = null;
1492: if (compositeColumn != null)
1493: compositeColumn.setCanvas(canvas);
1494: }
1495:
1496: /**
1497: * Sets the canvases.
1498: * @param canvases
1499: */
1500: public void setCanvases(PdfContentByte[] canvases) {
1501: this .canvases = canvases;
1502: this .canvas = canvases[PdfPTable.TEXTCANVAS];
1503: if (compositeColumn != null)
1504: compositeColumn.setCanvases(canvases);
1505: }
1506:
1507: /**
1508: * Gets the canvases.
1509: * @return an array of PdfContentByte
1510: */
1511: public PdfContentByte[] getCanvases() {
1512: return canvases;
1513: }
1514:
1515: /**
1516: * Checks if UseAscender is enabled/disabled.
1517: * @return true is the adjustment of the first line height is based on max ascender.
1518: */
1519: public boolean isUseAscender() {
1520: return useAscender;
1521: }
1522:
1523: /**
1524: * Enables/Disables adjustment of first line height based on max ascender.
1525: * @param use enable adjustment if true
1526: */
1527: public void setUseAscender(boolean use) {
1528: useAscender = use;
1529: }
1530:
1531: /**
1532: * Checks the status variable and looks if there's still some text.
1533: */
1534: public static boolean hasMoreText(int status) {
1535: return (status & ColumnText.NO_MORE_TEXT) == 0;
1536: }
1537:
1538: /**
1539: * Holds value of property filledWidth.
1540: */
1541: private float filledWidth;
1542:
1543: /**
1544: * Gets the real width used by the largest line.
1545: * @return the real width used by the largest line
1546: */
1547: public float getFilledWidth() {
1548:
1549: return this .filledWidth;
1550: }
1551:
1552: /**
1553: * Sets the real width used by the largest line. Only used to set it
1554: * to zero to start another measurement.
1555: * @param filledWidth the real width used by the largest line
1556: */
1557: public void setFilledWidth(float filledWidth) {
1558:
1559: this .filledWidth = filledWidth;
1560: }
1561:
1562: /**
1563: * Replaces the <CODE>filledWidth</CODE> if greater than the existing one.
1564: * @param w the new <CODE>filledWidth</CODE> if greater than the existing one
1565: */
1566: public void updateFilledWidth(float w) {
1567: if (w > filledWidth)
1568: filledWidth = w;
1569: }
1570: }
|