001: /*
002: * $Id: PdfLine.java 2742 2007-05-08 13:04:56Z blowagie $
003: * $Name$
004: *
005: * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
006: *
007: * The contents of this file are subject to the Mozilla Public License Version 1.1
008: * (the "License"); you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the License.
014: *
015: * The Original Code is 'iText, a free JAVA-PDF library'.
016: *
017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
019: * All Rights Reserved.
020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
022: *
023: * Contributor(s): all the names of the contributors are added in the source code
024: * where applicable.
025: *
026: * Alternatively, the contents of this file may be used under the terms of the
027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
028: * provisions of LGPL are applicable instead of those above. If you wish to
029: * allow use of your version of this file only under the terms of the LGPL
030: * License and not to allow others to use your version of this file under
031: * the MPL, indicate your decision by deleting the provisions above and
032: * replace them with the notice and other provisions required by the LGPL.
033: * If you do not delete the provisions above, a recipient may use your version
034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
035: *
036: * This library is free software; you can redistribute it and/or modify it
037: * under the terms of the MPL as stated above or under the terms of the GNU
038: * Library General Public License as published by the Free Software Foundation;
039: * either version 2 of the License, or any later version.
040: *
041: * This library is distributed in the hope that it will be useful, but WITHOUT
042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
044: * details.
045: *
046: * If you didn't download this code from the following link, you should check if
047: * you aren't using an obsolete version:
048: * http://www.lowagie.com/iText/
049: */
050:
051: package com.lowagie.text.pdf;
052:
053: import java.util.ArrayList;
054: import java.util.Iterator;
055:
056: import com.lowagie.text.Chunk;
057: import com.lowagie.text.Element;
058: import com.lowagie.text.ListItem;
059:
060: /**
061: * <CODE>PdfLine</CODE> defines an array with <CODE>PdfChunk</CODE>-objects
062: * that fit into 1 line.
063: */
064:
065: public class PdfLine {
066:
067: // membervariables
068:
069: /** The arraylist containing the chunks. */
070: protected ArrayList line;
071:
072: /** The left indentation of the line. */
073: protected float left;
074:
075: /** The width of the line. */
076: protected float width;
077:
078: /** The alignment of the line. */
079: protected int alignment;
080:
081: /** The heigth of the line. */
082: protected float height;
083:
084: /** The listsymbol (if necessary). */
085: protected Chunk listSymbol = null;
086:
087: /** The listsymbol (if necessary). */
088: protected float symbolIndent;
089:
090: /** <CODE>true</CODE> if the chunk splitting was caused by a newline. */
091: protected boolean newlineSplit = false;
092:
093: /** The original width. */
094: protected float originalWidth;
095:
096: protected boolean isRTL = false;
097:
098: // constructors
099:
100: /**
101: * Constructs a new <CODE>PdfLine</CODE>-object.
102: *
103: * @param left the limit of the line at the left
104: * @param right the limit of the line at the right
105: * @param alignment the alignment of the line
106: * @param height the height of the line
107: */
108:
109: PdfLine(float left, float right, int alignment, float height) {
110: this .left = left;
111: this .width = right - left;
112: this .originalWidth = this .width;
113: this .alignment = alignment;
114: this .height = height;
115: this .line = new ArrayList();
116: }
117:
118: PdfLine(float left, float remainingWidth, int alignment,
119: boolean newlineSplit, ArrayList line, boolean isRTL) {
120: this .left = left;
121: this .width = remainingWidth;
122: this .alignment = alignment;
123: this .line = line;
124: this .newlineSplit = newlineSplit;
125: this .isRTL = isRTL;
126: }
127:
128: // methods
129:
130: /**
131: * Adds a <CODE>PdfChunk</CODE> to the <CODE>PdfLine</CODE>.
132: *
133: * @param chunk the <CODE>PdfChunk</CODE> to add
134: * @return <CODE>null</CODE> if the chunk could be added completely; if not
135: * a <CODE>PdfChunk</CODE> containing the part of the chunk that could
136: * not be added is returned
137: */
138:
139: PdfChunk add(PdfChunk chunk) {
140: // nothing happens if the chunk is null.
141: if (chunk == null || chunk.toString().equals("")) {
142: return null;
143: }
144:
145: // we split the chunk to be added
146: PdfChunk overflow = chunk.split(width);
147: newlineSplit = (chunk.isNewlineSplit() || overflow == null);
148: // if (chunk.isNewlineSplit() && alignment == Element.ALIGN_JUSTIFIED)
149: // alignment = Element.ALIGN_LEFT;
150:
151: // if the length of the chunk > 0 we add it to the line
152: if (chunk.length() > 0) {
153: if (overflow != null)
154: chunk.trimLastSpace();
155: width -= chunk.width();
156: addToLine(chunk);
157: }
158:
159: // if the length == 0 and there were no other chunks added to the line yet,
160: // we risk to end up in an endless loop trying endlessly to add the same chunk
161: else if (line.size() < 1) {
162: chunk = overflow;
163: overflow = chunk.truncate(width);
164: width -= chunk.width();
165: if (chunk.length() > 0) {
166: addToLine(chunk);
167: return overflow;
168: }
169: // if the chunck couldn't even be truncated, we add everything, so be it
170: else {
171: if (overflow != null)
172: addToLine(overflow);
173: return null;
174: }
175: } else {
176: width += ((PdfChunk) (line.get(line.size() - 1)))
177: .trimLastSpace();
178: }
179: return overflow;
180: }
181:
182: private void addToLine(PdfChunk chunk) {
183: if (chunk.changeLeading && chunk.isImage()) {
184: float f = chunk.getImage().getScaledHeight()
185: + chunk.getImageOffsetY();
186: if (f > height)
187: height = f;
188: }
189: line.add(chunk);
190: }
191:
192: // methods to retrieve information
193:
194: /**
195: * Returns the number of chunks in the line.
196: *
197: * @return a value
198: */
199:
200: public int size() {
201: return line.size();
202: }
203:
204: /**
205: * Returns an iterator of <CODE>PdfChunk</CODE>s.
206: *
207: * @return an <CODE>Iterator</CODE>
208: */
209:
210: public Iterator iterator() {
211: return line.iterator();
212: }
213:
214: /**
215: * Returns the height of the line.
216: *
217: * @return a value
218: */
219:
220: float height() {
221: return height;
222: }
223:
224: /**
225: * Returns the left indentation of the line taking the alignment of the line into account.
226: *
227: * @return a value
228: */
229:
230: float indentLeft() {
231: if (isRTL) {
232: switch (alignment) {
233: case Element.ALIGN_LEFT:
234: return left + width;
235: case Element.ALIGN_CENTER:
236: return left + (width / 2f);
237: default:
238: return left;
239: }
240: } else {
241: switch (alignment) {
242: case Element.ALIGN_RIGHT:
243: return left + width;
244: case Element.ALIGN_CENTER:
245: return left + (width / 2f);
246: default:
247: return left;
248: }
249: }
250: }
251:
252: /**
253: * Checks if this line has to be justified.
254: *
255: * @return <CODE>true</CODE> if the alignment equals <VAR>ALIGN_JUSTIFIED</VAR> and there is some width left.
256: */
257:
258: public boolean hasToBeJustified() {
259: return ((alignment == Element.ALIGN_JUSTIFIED || alignment == Element.ALIGN_JUSTIFIED_ALL) && width != 0);
260: }
261:
262: /**
263: * Resets the alignment of this line.
264: * <P>
265: * The alignment of the last line of for instance a <CODE>Paragraph</CODE>
266: * that has to be justified, has to be reset to <VAR>ALIGN_LEFT</VAR>.
267: */
268:
269: public void resetAlignment() {
270: if (alignment == Element.ALIGN_JUSTIFIED) {
271: alignment = Element.ALIGN_LEFT;
272: }
273: }
274:
275: /** Adds extra indentation to the left (for Paragraph.setFirstLineIndent). */
276: void setExtraIndent(float extra) {
277: left += extra;
278: width -= extra;
279: }
280:
281: /**
282: * Returns the width that is left, after a maximum of characters is added to the line.
283: *
284: * @return a value
285: */
286:
287: float widthLeft() {
288: return width;
289: }
290:
291: /**
292: * Returns the number of space-characters in this line.
293: *
294: * @return a value
295: */
296:
297: int numberOfSpaces() {
298: String string = toString();
299: int length = string.length();
300: int numberOfSpaces = 0;
301: for (int i = 0; i < length; i++) {
302: if (string.charAt(i) == ' ') {
303: numberOfSpaces++;
304: }
305: }
306: return numberOfSpaces;
307: }
308:
309: /**
310: * Sets the listsymbol of this line.
311: * <P>
312: * This is only necessary for the first line of a <CODE>ListItem</CODE>.
313: *
314: * @param listItem the list symbol
315: */
316:
317: public void setListItem(ListItem listItem) {
318: this .listSymbol = listItem.getListSymbol();
319: this .symbolIndent = listItem.getIndentationLeft();
320: }
321:
322: /**
323: * Returns the listsymbol of this line.
324: *
325: * @return a <CODE>PdfChunk</CODE> if the line has a listsymbol; <CODE>null</CODE> otherwise
326: */
327:
328: public Chunk listSymbol() {
329: return listSymbol;
330: }
331:
332: /**
333: * Return the indentation needed to show the listsymbol.
334: *
335: * @return a value
336: */
337:
338: public float listIndent() {
339: return symbolIndent;
340: }
341:
342: /**
343: * Get the string representation of what is in this line.
344: *
345: * @return a <CODE>String</CODE>
346: */
347:
348: public String toString() {
349: StringBuffer tmp = new StringBuffer();
350: for (Iterator i = line.iterator(); i.hasNext();) {
351: tmp.append(((PdfChunk) i.next()).toString());
352: }
353: return tmp.toString();
354: }
355:
356: /**
357: * Checks if a newline caused the line split.
358: * @return <CODE>true</CODE> if a newline caused the line split
359: */
360: public boolean isNewlineSplit() {
361: return newlineSplit
362: && (alignment != Element.ALIGN_JUSTIFIED_ALL);
363: }
364:
365: /**
366: * Gets the index of the last <CODE>PdfChunk</CODE> with metric attributes
367: * @return the last <CODE>PdfChunk</CODE> with metric attributes
368: */
369: public int getLastStrokeChunk() {
370: int lastIdx = line.size() - 1;
371: for (; lastIdx >= 0; --lastIdx) {
372: PdfChunk chunk = (PdfChunk) line.get(lastIdx);
373: if (chunk.isStroked())
374: break;
375: }
376: return lastIdx;
377: }
378:
379: /**
380: * Gets a <CODE>PdfChunk</CODE> by index.
381: * @param idx the index
382: * @return the <CODE>PdfChunk</CODE> or null if beyond the array
383: */
384: public PdfChunk getChunk(int idx) {
385: if (idx < 0 || idx >= line.size())
386: return null;
387: return (PdfChunk) line.get(idx);
388: }
389:
390: /**
391: * Gets the original width of the line.
392: * @return the original width of the line
393: */
394: public float getOriginalWidth() {
395: return originalWidth;
396: }
397:
398: /**
399: * Gets the maximum size of all the fonts used in this line
400: * including images.
401: * @return maximum size of all the fonts used in this line
402: */
403: float getMaxSizeSimple() {
404: float maxSize = 0;
405: for (int k = 0; k < line.size(); ++k) {
406: PdfChunk chunk = (PdfChunk) line.get(k);
407: if (!chunk.isImage()) {
408: maxSize = Math.max(chunk.font().size(), maxSize);
409: } else {
410: maxSize = Math.max(chunk.getImage().getScaledHeight()
411: + chunk.getImageOffsetY(), maxSize);
412: }
413: }
414: return maxSize;
415: }
416:
417: boolean isRTL() {
418: return isRTL;
419: }
420:
421: /**
422: * Gets a width corrected with a charSpacing and wordSpacing.
423: * @param charSpacing
424: * @param wordSpacing
425: * @return a corrected width
426: */
427: public float getWidthCorrected(float charSpacing, float wordSpacing) {
428: float total = 0;
429: for (int k = 0; k < line.size(); ++k) {
430: PdfChunk ck = (PdfChunk) line.get(k);
431: total += ck.getWidthCorrected(charSpacing, wordSpacing);
432: }
433: return total;
434: }
435:
436: /**
437: * Gets the maximum size of the ascender for all the fonts used
438: * in this line.
439: * @return maximum size of all the ascenders used in this line
440: */
441: public float getAscender() {
442: float ascender = 0;
443: for (int k = 0; k < line.size(); ++k) {
444: PdfChunk ck = (PdfChunk) line.get(k);
445: if (ck.isImage())
446: ascender = Math.max(ascender, ck.getImage()
447: .getScaledHeight()
448: + ck.getImageOffsetY());
449: else {
450: PdfFont font = ck.font();
451: ascender = Math.max(ascender,
452: font.getFont().getFontDescriptor(
453: BaseFont.ASCENT, font.size()));
454: }
455: }
456: return ascender;
457: }
458:
459: /**
460: * Gets the biggest descender for all the fonts used
461: * in this line. Note that this is a negative number.
462: * @return maximum size of all the ascenders used in this line
463: */
464: public float getDescender() {
465: float descender = 0;
466: for (int k = 0; k < line.size(); ++k) {
467: PdfChunk ck = (PdfChunk) line.get(k);
468: if (ck.isImage())
469: descender = Math.min(descender, ck.getImageOffsetY());
470: else {
471: PdfFont font = ck.font();
472: descender = Math.min(descender, font.getFont()
473: .getFontDescriptor(BaseFont.DESCENT,
474: font.size()));
475: }
476: }
477: return descender;
478: }
479: }
|