From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- src/main/java/com/lowagie/text/pdf/PdfChunk.java | 781 +++++++++++++++++++++++ 1 file changed, 781 insertions(+) create mode 100644 src/main/java/com/lowagie/text/pdf/PdfChunk.java (limited to 'src/main/java/com/lowagie/text/pdf/PdfChunk.java') diff --git a/src/main/java/com/lowagie/text/pdf/PdfChunk.java b/src/main/java/com/lowagie/text/pdf/PdfChunk.java new file mode 100644 index 0000000..2906552 --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/PdfChunk.java @@ -0,0 +1,781 @@ +/* + * $Id: PdfChunk.java,v 1.71 2006/02/16 16:17:52 psoares33 Exp $ + * $Name: $ + * + * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + */ + +package com.lowagie.text.pdf; + +import java.awt.Color; + +import com.lowagie.text.Chunk; +import com.lowagie.text.Font; +import com.lowagie.text.Image; +import com.lowagie.text.SplitCharacter; +import java.util.HashMap; +import java.util.Iterator; + +/** + * A PdfChunk is the PDF translation of a Chunk. + *

+ * A PdfChunk is a PdfString in a certain + * PdfFont and Color. + * + * @see PdfString + * @see com.lowagie.text.Chunk + * @see com.lowagie.text.Font + */ + +public class PdfChunk implements SplitCharacter{ + + private static final char singleSpace[] = {' '}; + private static final PdfChunk thisChunk[] = new PdfChunk[1]; + private static final float ITALIC_ANGLE = 0.21256f; +/** The allowed attributes in variable attributes. */ + private static final HashMap keysAttributes = new HashMap(); + +/** The allowed attributes in variable noStroke. */ + private static final HashMap keysNoStroke = new HashMap(); + + static { + keysAttributes.put(Chunk.ACTION, null); + keysAttributes.put(Chunk.UNDERLINE, null); + keysAttributes.put(Chunk.REMOTEGOTO, null); + keysAttributes.put(Chunk.LOCALGOTO, null); + keysAttributes.put(Chunk.LOCALDESTINATION, null); + keysAttributes.put(Chunk.GENERICTAG, null); + keysAttributes.put(Chunk.NEWPAGE, null); + keysAttributes.put(Chunk.IMAGE, null); + keysAttributes.put(Chunk.BACKGROUND, null); + keysAttributes.put(Chunk.PDFANNOTATION, null); + keysAttributes.put(Chunk.SKEW, null); + keysAttributes.put(Chunk.HSCALE, null); + keysNoStroke.put(Chunk.SUBSUPSCRIPT, null); + keysNoStroke.put(Chunk.SPLITCHARACTER, null); + keysNoStroke.put(Chunk.HYPHENATION, null); + keysNoStroke.put(Chunk.TEXTRENDERMODE, null); + } + + // membervariables + + /** The value of this object. */ + protected String value = PdfObject.NOTHING; + + /** The encoding. */ + protected String encoding = BaseFont.WINANSI; + + +/** The font for this PdfChunk. */ + protected PdfFont font; + + protected BaseFont baseFont; + + protected SplitCharacter splitCharacter; +/** + * Metric attributes. + *

+ * This attributes require the mesurement of characters widths when rendering + * such as underline. + */ + protected HashMap attributes = new HashMap(); + +/** + * Non metric attributes. + *

+ * This attributes do not require the mesurement of characters widths when rendering + * such as Color. + */ + protected HashMap noStroke = new HashMap(); + +/** true if the chunk split was cause by a newline. */ + protected boolean newlineSplit; + +/** The image in this PdfChunk, if it has one */ + protected Image image; + +/** The offset in the x direction for the image */ + protected float offsetX; + +/** The offset in the y direction for the image */ + protected float offsetY; + +/** Indicates if the height and offset of the Image has to be taken into account */ + protected boolean changeLeading = false; + + // constructors + +/** + * Constructs a PdfChunk-object. + * + * @param string the content of the PdfChunk-object + * @param other Chunk with the same style you want for the new Chunk + */ + + PdfChunk(String string, PdfChunk other) { + thisChunk[0] = this; + value = string; + this.font = other.font; + this.attributes = other.attributes; + this.noStroke = other.noStroke; + this.baseFont = other.baseFont; + Object obj[] = (Object[])attributes.get(Chunk.IMAGE); + if (obj == null) + image = null; + else { + image = (Image)obj[0]; + offsetX = ((Float)obj[1]).floatValue(); + offsetY = ((Float)obj[2]).floatValue(); + changeLeading = ((Boolean)obj[3]).booleanValue(); + } + encoding = font.getFont().getEncoding(); + splitCharacter = (SplitCharacter)noStroke.get(Chunk.SPLITCHARACTER); + if (splitCharacter == null) + splitCharacter = this; + } + +/** + * Constructs a PdfChunk-object. + * + * @param chunk the original Chunk-object + * @param action the PdfAction if the Chunk comes from an Anchor + */ + + PdfChunk(Chunk chunk, PdfAction action) { + thisChunk[0] = this; + value = chunk.content(); + + Font f = chunk.font(); + float size = f.size(); + if (size == Font.UNDEFINED) + size = 12; + baseFont = f.getBaseFont(); + int style = f.style(); + if (style == Font.UNDEFINED) { + style = Font.NORMAL; + } + if (baseFont == null) { + // translation of the font-family to a PDF font-family + baseFont = f.getCalculatedBaseFont(false); + } + else { + // bold simulation + if ((style & Font.BOLD) != 0) + attributes.put(Chunk.TEXTRENDERMODE, new Object[]{new Integer(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE), new Float(size / 30f), null}); + // italic simulation + if ((style & Font.ITALIC) != 0) + attributes.put(Chunk.SKEW, new float[]{0, ITALIC_ANGLE}); + } + font = new PdfFont(baseFont, size); + // other style possibilities + HashMap attr = chunk.getAttributes(); + if (attr != null) { + for (Iterator i = attr.keySet().iterator(); i.hasNext();) { + Object name = i.next(); + if (keysAttributes.containsKey(name)) { + attributes.put(name, attr.get(name)); + } + else if (keysNoStroke.containsKey(name)) { + noStroke.put(name, attr.get(name)); + } + } + if ("".equals(attr.get(Chunk.GENERICTAG))) { + attributes.put(Chunk.GENERICTAG, chunk.content()); + } + } + if (f.isUnderlined()) { + Object obj[] = {null, new float[]{0, 1f / 15, 0, -1f / 3, 0}}; + Object unders[][] = Chunk.addToArray((Object[][])attributes.get(Chunk.UNDERLINE), obj); + attributes.put(Chunk.UNDERLINE, unders); + } + if (f.isStrikethru()) { + Object obj[] = {null, new float[]{0, 1f / 15, 0, 1f / 3, 0}}; + Object unders[][] = Chunk.addToArray((Object[][])attributes.get(Chunk.UNDERLINE), obj); + attributes.put(Chunk.UNDERLINE, unders); + } + if (action != null) + attributes.put(Chunk.ACTION, action); + // the color can't be stored in a PdfFont + noStroke.put(Chunk.COLOR, f.color()); + noStroke.put(Chunk.ENCODING, font.getFont().getEncoding()); + Object obj[] = (Object[])attributes.get(Chunk.IMAGE); + if (obj == null) { + image = null; + } + else { + attributes.remove(Chunk.HSCALE); // images are scaled in other ways + image = (Image)obj[0]; + offsetX = ((Float)obj[1]).floatValue(); + offsetY = ((Float)obj[2]).floatValue(); + changeLeading = ((Boolean)obj[3]).booleanValue(); + } + font.setImage(image); + Float hs = (Float)attributes.get(Chunk.HSCALE); + if (hs != null) + font.setHorizontalScaling(hs.floatValue()); + encoding = font.getFont().getEncoding(); + splitCharacter = (SplitCharacter)noStroke.get(Chunk.SPLITCHARACTER); + if (splitCharacter == null) + splitCharacter = this; + } + + // methods + + /** Gets the Unicode equivalent to a CID. + * The (inexistent) CID is translated as '\n'. + * It has only meaning with CJK fonts with Identity encoding. + * @param c the CID code + * @return the Unicode equivalent + */ + public char getUnicodeEquivalent(char c) { + return baseFont.getUnicodeEquivalent(c); + } + + protected int getWord(String text, int start) { + int len = text.length(); + while (start < len) { + if (!Character.isLetter(text.charAt(start))) + break; + ++start; + } + return start; + } + +/** + * Splits this PdfChunk if it's too long for the given width. + *

+ * Returns null if the PdfChunk wasn't truncated. + * + * @param width a given width + * @return the PdfChunk that doesn't fit into the width. + */ + + PdfChunk split(float width) { + newlineSplit = false; + if (image != null) { + if (image.scaledWidth() > width) { + PdfChunk pc = new PdfChunk(Chunk.OBJECT_REPLACEMENT_CHARACTER, this); + value = ""; + attributes = new HashMap(); + image = null; + font = PdfFont.getDefaultFont(); + return pc; + } + else + return null; + } + HyphenationEvent hyphenationEvent = (HyphenationEvent)noStroke.get(Chunk.HYPHENATION); + int currentPosition = 0; + int splitPosition = -1; + float currentWidth = 0; + + // loop over all the characters of a string + // or until the totalWidth is reached + int lastSpace = -1; + float lastSpaceWidth = 0; + int length = value.length(); + char valueArray[] = value.toCharArray(); + char character = 0; + BaseFont ft = font.getFont(); + if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') { + while (currentPosition < length) { + // the width of every character is added to the currentWidth + char cidChar = valueArray[currentPosition]; + character = ft.getUnicodeEquivalent(cidChar); + // if a newLine or carriageReturn is encountered + if (character == '\n') { + newlineSplit = true; + String returnValue = value.substring(currentPosition + 1); + value = value.substring(0, currentPosition); + if (value.length() < 1) { + value = "\u0001"; + } + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + currentWidth += font.width(cidChar); + if (character == ' ') { + lastSpace = currentPosition + 1; + lastSpaceWidth = currentWidth; + } + if (currentWidth > width) + break; + // if a split-character is encountered, the splitPosition is altered + if (splitCharacter.isSplitCharacter(0, currentPosition, length, valueArray, thisChunk)) + splitPosition = currentPosition + 1; + currentPosition++; + } + } + else { + while (currentPosition < length) { + // the width of every character is added to the currentWidth + character = valueArray[currentPosition]; + // if a newLine or carriageReturn is encountered + if (character == '\r' || character == '\n') { + newlineSplit = true; + int inc = 1; + if (character == '\r' && currentPosition + 1 < length && valueArray[currentPosition + 1] == '\n') + inc = 2; + String returnValue = value.substring(currentPosition + inc); + value = value.substring(0, currentPosition); + if (value.length() < 1) { + value = " "; + } + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + currentWidth += font.width(character); + if (character == ' ') { + lastSpace = currentPosition + 1; + lastSpaceWidth = currentWidth; + } + if (currentWidth > width) + break; + // if a split-character is encountered, the splitPosition is altered + if (splitCharacter.isSplitCharacter(0, currentPosition, length, valueArray, null)) + splitPosition = currentPosition + 1; + currentPosition++; + } + } + + // if all the characters fit in the total width, null is returned (there is no overflow) + if (currentPosition == length) { + return null; + } + // otherwise, the string has to be truncated + if (splitPosition < 0) { + String returnValue = value; + value = ""; + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + if (lastSpace > splitPosition && splitCharacter.isSplitCharacter(0, 0, 1, singleSpace, null)) + splitPosition = lastSpace; + if (hyphenationEvent != null && lastSpace < currentPosition) { + int wordIdx = getWord(value, lastSpace); + if (wordIdx > lastSpace) { + String pre = hyphenationEvent.getHyphenatedWordPre(value.substring(lastSpace, wordIdx), font.getFont(), font.size(), width - lastSpaceWidth); + String post = hyphenationEvent.getHyphenatedWordPost(); + if (pre.length() > 0) { + String returnValue = post + value.substring(wordIdx); + value = trim(value.substring(0, lastSpace) + pre); + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + } + } + String returnValue = value.substring(splitPosition); + value = trim(value.substring(0, splitPosition)); + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + +/** + * Truncates this PdfChunk if it's too long for the given width. + *

+ * Returns null if the PdfChunk wasn't truncated. + * + * @param width a given width + * @return the PdfChunk that doesn't fit into the width. + */ + + PdfChunk truncate(float width) { + if (image != null) { + if (image.scaledWidth() > width) { + PdfChunk pc = new PdfChunk("", this); + value = ""; + attributes.remove(Chunk.IMAGE); + image = null; + font = PdfFont.getDefaultFont(); + return pc; + } + else + return null; + } + + int currentPosition = 0; + float currentWidth = 0; + + // it's no use trying to split if there isn't even enough place for a space + if (width < font.width()) { + String returnValue = value.substring(1); + value = value.substring(0, 1); + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + + // loop over all the characters of a string + // or until the totalWidth is reached + int length = value.length(); + char character; + while (currentPosition < length) { + // the width of every character is added to the currentWidth + character = value.charAt(currentPosition); + currentWidth += font.width(character); + if (currentWidth > width) + break; + currentPosition++; + } + + // if all the characters fit in the total width, null is returned (there is no overflow) + if (currentPosition == length) { + return null; + } + + // otherwise, the string has to be truncated + //currentPosition -= 2; + // we have to chop off minimum 1 character from the chunk + if (currentPosition == 0) { + currentPosition = 1; + } + String returnValue = value.substring(currentPosition); + value = value.substring(0, currentPosition); + PdfChunk pc = new PdfChunk(returnValue, this); + return pc; + } + + // methods to retrieve the membervariables + +/** + * Returns the font of this Chunk. + * + * @return a PdfFont + */ + + PdfFont font() { + return font; + } + +/** + * Returns the color of this Chunk. + * + * @return a Color + */ + + Color color() { + return (Color)noStroke.get(Chunk.COLOR); + } + +/** + * Returns the width of this PdfChunk. + * + * @return a width + */ + + float width() { + return font.width(value); + } + +/** + * Checks if the PdfChunk split was caused by a newline. + * @return true if the PdfChunk split was caused by a newline. + */ + + public boolean isNewlineSplit() + { + return newlineSplit; + } + +/** + * Gets the width of the PdfChunk taking into account the + * extra character and word spacing. + * @param charSpacing the extra character spacing + * @param wordSpacing the extra word spacing + * @return the calculated width + */ + + public float getWidthCorrected(float charSpacing, float wordSpacing) + { + if (image != null) { + return image.scaledWidth() + charSpacing; + } + int numberOfSpaces = 0; + int idx = -1; + while ((idx = value.indexOf(' ', idx + 1)) >= 0) + ++numberOfSpaces; + return width() + (value.length() * charSpacing + numberOfSpaces * wordSpacing); + } + + /** + * Gets the text displacement relatiev to the baseline. + * @return a displacement in points + */ + public float getTextRise() { + Float f = (Float) getAttribute(Chunk.SUBSUPSCRIPT); + if (f != null) { + return f.floatValue(); + } + return 0.0f; + } + +/** + * Trims the last space. + * @return the width of the space trimmed, otherwise 0 + */ + + public float trimLastSpace() + { + BaseFont ft = font.getFont(); + if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') { + if (value.length() > 1 && value.endsWith("\u0001")) { + value = value.substring(0, value.length() - 1); + return font.width('\u0001'); + } + } + else { + if (value.length() > 1 && value.endsWith(" ")) { + value = value.substring(0, value.length() - 1); + return font.width(' '); + } + } + return 0; + } + +/** + * Gets an attribute. The search is made in attributes + * and noStroke. + * @param name the attribute key + * @return the attribute value or null if not found + */ + + Object getAttribute(String name) + { + if (attributes.containsKey(name)) + return attributes.get(name); + return noStroke.get(name); + } + +/** + *Checks if the attribute exists. + * @param name the attribute key + * @return true if the attribute exists + */ + + boolean isAttribute(String name) + { + if (attributes.containsKey(name)) + return true; + return noStroke.containsKey(name); + } + +/** + * Checks if this PdfChunk needs some special metrics handling. + * @return true if this PdfChunk needs some special metrics handling. + */ + + boolean isStroked() + { + return (attributes.size() > 0); + } + +/** + * Checks if there is an image in the PdfChunk. + * @return true if an image is present + */ + + boolean isImage() + { + return image != null; + } + +/** + * Gets the image in the PdfChunk. + * @return the image or null + */ + + Image getImage() + { + return image; + } + +/** + * Sets the image offset in the x direction + * @param offsetX the image offset in the x direction + */ + + void setImageOffsetX(float offsetX) + { + this.offsetX = offsetX; + } + +/** + * Gets the image offset in the x direction + * @return the image offset in the x direction + */ + + float getImageOffsetX() + { + return offsetX; + } + +/** + * Sets the image offset in the y direction + * @param offsetY the image offset in the y direction + */ + + void setImageOffsetY(float offsetY) + { + this.offsetY = offsetY; + } + +/** + * Gets the image offset in the y direction + * @return Gets the image offset in the y direction + */ + + float getImageOffsetY() + { + return offsetY; + } + +/** + * sets the value. + * @param value content of the Chunk + */ + + void setValue(String value) + { + this.value = value; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return value; + } + + /** + * Tells you if this string is in Chinese, Japanese, Korean or Identity-H. + * @return true if the Chunk has a special encoding + */ + + boolean isSpecialEncoding() { + return encoding.equals(CJKFont.CJK_ENCODING) || encoding.equals(BaseFont.IDENTITY_H); + } + + /** + * Gets the encoding of this string. + * + * @return a String + */ + + String getEncoding() { + return encoding; + } + + int length() { + return value.length(); + } +/** + * Checks if a character can be used to split a PdfString. + *

+ * for the moment every character less than or equal to SPACE and the character '-' are 'splitCharacters'. + * + * @param start start position in the array + * @param current current position in the array + * @param end end position in the array + * @param cc the character array that has to be checked + * @param ck chunk array + * @return true if the character can be used to split a string, false otherwise + */ + public boolean isSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) { + char c; + if (ck == null) + c = cc[current]; + else + c = ck[Math.min(current, ck.length - 1)].getUnicodeEquivalent(cc[current]); + if (c <= ' ' || c == '-') { + return true; + } + if (c < 0x2e80) + return false; + return ((c >= 0x2e80 && c < 0xd7a0) + || (c >= 0xf900 && c < 0xfb00) + || (c >= 0xfe30 && c < 0xfe50) + || (c >= 0xff61 && c < 0xffa0)); + } + + boolean isExtSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) { + return splitCharacter.isSplitCharacter(start, current, end, cc, ck); + } + +/** + * Removes all the ' ' and '-'-characters on the right of a String. + *

+ * @param string the String that has to be trimmed. + * @return the trimmed String + */ + String trim(String string) { + BaseFont ft = font.getFont(); + if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') { + while (string.endsWith("\u0001")) { + string = string.substring(0, string.length() - 1); + } + } + else { + while (string.endsWith(" ") || string.endsWith("\t")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + public boolean changeLeading() { + return changeLeading; + } + + float getCharWidth(char c) { + if (noPrint(c)) + return 0; + return font.width(c); + } + + public static boolean noPrint(char c) { + return ((c >= 0x200b && c <= 0x200f) || (c >= 0x202a && c <= 0x202e)); + } + +} \ No newline at end of file -- cgit v1.2.3