001/* 002 * $Id: VerticalText.java 4827 2011-05-02 22:34:16Z mstorer $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045import java.util.ArrayList; 046import java.util.Iterator; 047 048import com.itextpdf.text.BaseColor; 049import com.itextpdf.text.Chunk; 050import com.itextpdf.text.Element; 051import com.itextpdf.text.Phrase; 052import com.itextpdf.text.error_messages.MessageLocalization; 053 054/** Writes text vertically. Note that the naming is done according 055 * to horizontal text although it refers to vertical text. 056 * A line with the alignment Element.LEFT_ALIGN will actually 057 * be top aligned. 058 */ 059public class VerticalText { 060 061/** Signals that there are no more text available. */ 062 public static final int NO_MORE_TEXT = 1; 063 064/** Signals that there is no more column. */ 065 public static final int NO_MORE_COLUMN = 2; 066 067/** The chunks that form the text. */ 068 protected ArrayList<PdfChunk> chunks = new ArrayList<PdfChunk>(); 069 070 /** The <CODE>PdfContent</CODE> where the text will be written to. */ 071 protected PdfContentByte text; 072 073 /** The column alignment. Default is left alignment. */ 074 protected int alignment = Element.ALIGN_LEFT; 075 076 /** Marks the chunks to be eliminated when the line is written. */ 077 protected int currentChunkMarker = -1; 078 079 /** The chunk created by the splitting. */ 080 protected PdfChunk currentStandbyChunk; 081 082 /** The chunk created by the splitting. */ 083 protected String splittedChunkText; 084 085 /** The leading 086 */ 087 protected float leading; 088 089 /** The X coordinate. 090 */ 091 protected float startX; 092 093 /** The Y coordinate. 094 */ 095 protected float startY; 096 097 /** The maximum number of vertical lines. 098 */ 099 protected int maxLines; 100 101 /** The height of the text. 102 */ 103 protected float height; 104 105 /** Creates new VerticalText 106 * @param text the place where the text will be written to. Can 107 * be a template. 108 */ 109 public VerticalText(PdfContentByte text) { 110 this.text = text; 111 } 112 113 /** 114 * Adds a <CODE>Phrase</CODE> to the current text array. 115 * @param phrase the text 116 */ 117 public void addText(Phrase phrase) { 118 for (Chunk c: phrase.getChunks()) { 119 chunks.add(new PdfChunk(c, null)); 120 } 121 } 122 123 /** 124 * Adds a <CODE>Chunk</CODE> to the current text array. 125 * @param chunk the text 126 */ 127 public void addText(Chunk chunk) { 128 chunks.add(new PdfChunk(chunk, null)); 129 } 130 131 /** Sets the layout. 132 * @param startX the top right X line position 133 * @param startY the top right Y line position 134 * @param height the height of the lines 135 * @param maxLines the maximum number of lines 136 * @param leading the separation between the lines 137 */ 138 public void setVerticalLayout(float startX, float startY, float height, int maxLines, float leading) { 139 this.startX = startX; 140 this.startY = startY; 141 this.height = height; 142 this.maxLines = maxLines; 143 setLeading(leading); 144 } 145 146 /** Sets the separation between the vertical lines. 147 * @param leading the vertical line separation 148 */ 149 public void setLeading(float leading) { 150 this.leading = leading; 151 } 152 153 /** Gets the separation between the vertical lines. 154 * @return the vertical line separation 155 */ 156 public float getLeading() { 157 return leading; 158 } 159 160 /** 161 * Creates a line from the chunk array. 162 * @param width the width of the line 163 * @return the line or null if no more chunks 164 */ 165 protected PdfLine createLine(float width) { 166 if (chunks.isEmpty()) 167 return null; 168 splittedChunkText = null; 169 currentStandbyChunk = null; 170 PdfLine line = new PdfLine(0, width, alignment, 0); 171 String total; 172 for (currentChunkMarker = 0; currentChunkMarker < chunks.size(); ++currentChunkMarker) { 173 PdfChunk original = chunks.get(currentChunkMarker); 174 total = original.toString(); 175 currentStandbyChunk = line.add(original); 176 if (currentStandbyChunk != null) { 177 splittedChunkText = original.toString(); 178 original.setValue(total); 179 return line; 180 } 181 } 182 return line; 183 } 184 185 /** 186 * Normalizes the list of chunks when the line is accepted. 187 */ 188 protected void shortenChunkArray() { 189 if (currentChunkMarker < 0) 190 return; 191 if (currentChunkMarker >= chunks.size()) { 192 chunks.clear(); 193 return; 194 } 195 PdfChunk split = chunks.get(currentChunkMarker); 196 split.setValue(splittedChunkText); 197 chunks.set(currentChunkMarker, currentStandbyChunk); 198 for (int j = currentChunkMarker - 1; j >= 0; --j) 199 chunks.remove(j); 200 } 201 202 /** 203 * Outputs the lines to the document. It is equivalent to <CODE>go(false)</CODE>. 204 * @return returns the result of the operation. It can be <CODE>NO_MORE_TEXT</CODE> 205 * and/or <CODE>NO_MORE_COLUMN</CODE> 206 */ 207 public int go() { 208 return go(false); 209 } 210 211 /** 212 * Outputs the lines to the document. The output can be simulated. 213 * @param simulate <CODE>true</CODE> to simulate the writing to the document 214 * @return returns the result of the operation. It can be <CODE>NO_MORE_TEXT</CODE> 215 * and/or <CODE>NO_MORE_COLUMN</CODE> 216 */ 217 public int go(boolean simulate) { 218 boolean dirty = false; 219 PdfContentByte graphics = null; 220 if (text != null) { 221 graphics = text.getDuplicate(); 222 } 223 else if (!simulate) 224 throw new NullPointerException(MessageLocalization.getComposedMessage("verticaltext.go.with.simulate.eq.eq.false.and.text.eq.eq.null")); 225 int status = 0; 226 for (;;) { 227 if (maxLines <= 0) { 228 status = NO_MORE_COLUMN; 229 if (chunks.isEmpty()) 230 status |= NO_MORE_TEXT; 231 break; 232 } 233 if (chunks.isEmpty()) { 234 status = NO_MORE_TEXT; 235 break; 236 } 237 PdfLine line = createLine(height); 238 if (!simulate && !dirty) { 239 text.beginText(); 240 dirty = true; 241 } 242 shortenChunkArray(); 243 if (!simulate) { 244 text.setTextMatrix(startX, startY - line.indentLeft()); 245 writeLine(line, text, graphics); 246 } 247 --maxLines; 248 startX -= leading; 249 } 250 if (dirty) { 251 text.endText(); 252 text.add(graphics); 253 } 254 return status; 255 } 256 257 private Float curCharSpace = 0f; 258 259 void writeLine(PdfLine line, PdfContentByte text, PdfContentByte graphics) { 260 PdfFont currentFont = null; 261 PdfChunk chunk; 262 for (Iterator<PdfChunk> j = line.iterator(); j.hasNext(); ) { 263 chunk = j.next(); 264 265 if (chunk.font().compareTo(currentFont) != 0) { 266 currentFont = chunk.font(); 267 text.setFontAndSize(currentFont.getFont(), currentFont.size()); 268 } 269 BaseColor color = chunk.color(); 270 Float charSpace = (Float)chunk.getAttribute(Chunk.CHAR_SPACING); 271 // no char space setting means "leave it as is". 272 if (charSpace != null && !curCharSpace.equals(charSpace)) { 273 curCharSpace = charSpace.floatValue(); 274 text.setCharacterSpacing(curCharSpace); 275 } 276 if (color != null) 277 text.setColorFill(color); 278 279 text.showText(chunk.toString()); 280 281 if (color != null) 282 text.resetRGBColorFill(); 283 } 284 } 285 286 /** Sets the new text origin. 287 * @param startX the X coordinate 288 * @param startY the Y coordinate 289 */ 290 public void setOrigin(float startX, float startY) { 291 this.startX = startX; 292 this.startY = startY; 293 } 294 295 /** Gets the X coordinate where the next line will be written. This value will change 296 * after each call to <code>go()</code>. 297 * @return the X coordinate 298 */ 299 public float getOriginX() { 300 return startX; 301 } 302 303 /** Gets the Y coordinate where the next line will be written. 304 * @return the Y coordinate 305 */ 306 public float getOriginY() { 307 return startY; 308 } 309 310 /** Gets the maximum number of available lines. This value will change 311 * after each call to <code>go()</code>. 312 * @return Value of property maxLines. 313 */ 314 public int getMaxLines() { 315 return maxLines; 316 } 317 318 /** Sets the maximum number of lines. 319 * @param maxLines the maximum number of lines 320 */ 321 public void setMaxLines(int maxLines) { 322 this.maxLines = maxLines; 323 } 324 325 /** Gets the height of the line 326 * @return the height 327 */ 328 public float getHeight() { 329 return height; 330 } 331 332 /** Sets the height of the line 333 * @param height the new height 334 */ 335 public void setHeight(float height) { 336 this.height = height; 337 } 338 339 /** 340 * Sets the alignment. 341 * @param alignment the alignment 342 */ 343 public void setAlignment(int alignment) { 344 this.alignment = alignment; 345 } 346 347 /** 348 * Gets the alignment. 349 * @return the alignment 350 */ 351 public int getAlignment() { 352 return alignment; 353 } 354}