842 lines
30 KiB
C#
842 lines
30 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.util;
|
|
|
|
using iTextSharp.text;
|
|
|
|
/*
|
|
* $Id: PdfChunk.cs,v 1.11 2008/05/22 22:11:10 psoares33 Exp $
|
|
*
|
|
*
|
|
* 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/
|
|
*/
|
|
|
|
namespace iTextSharp.text.pdf {
|
|
|
|
/**
|
|
* A <CODE>PdfChunk</CODE> is the PDF translation of a <CODE>Chunk</CODE>.
|
|
* <P>
|
|
* A <CODE>PdfChunk</CODE> is a <CODE>PdfString</CODE> in a certain
|
|
* <CODE>PdfFont</CODE> and <CODE>Color</CODE>.
|
|
*
|
|
* @see PdfString
|
|
* @see PdfFont
|
|
* @see iTextSharp.text.Chunk
|
|
* @see iTextSharp.text.Font
|
|
*/
|
|
|
|
public class PdfChunk {
|
|
|
|
|
|
private static char[] singleSpace = {' '};
|
|
private static PdfChunk[] thisChunk = new PdfChunk[1];
|
|
private const float ITALIC_ANGLE = 0.21256f;
|
|
|
|
/** The allowed attributes in variable <CODE>attributes</CODE>. */
|
|
private static Hashtable keysAttributes = new Hashtable();
|
|
|
|
/** The allowed attributes in variable <CODE>noStroke</CODE>. */
|
|
private static Hashtable keysNoStroke = new Hashtable();
|
|
|
|
static PdfChunk() {
|
|
keysAttributes.Add(Chunk.ACTION, null);
|
|
keysAttributes.Add(Chunk.UNDERLINE, null);
|
|
keysAttributes.Add(Chunk.REMOTEGOTO, null);
|
|
keysAttributes.Add(Chunk.LOCALGOTO, null);
|
|
keysAttributes.Add(Chunk.LOCALDESTINATION, null);
|
|
keysAttributes.Add(Chunk.GENERICTAG, null);
|
|
keysAttributes.Add(Chunk.NEWPAGE, null);
|
|
keysAttributes.Add(Chunk.IMAGE, null);
|
|
keysAttributes.Add(Chunk.BACKGROUND, null);
|
|
keysAttributes.Add(Chunk.PDFANNOTATION, null);
|
|
keysAttributes.Add(Chunk.SKEW, null);
|
|
keysAttributes.Add(Chunk.HSCALE, null);
|
|
keysAttributes.Add(Chunk.SEPARATOR, null);
|
|
keysAttributes.Add(Chunk.TAB, null);
|
|
keysNoStroke.Add(Chunk.SUBSUPSCRIPT, null);
|
|
keysNoStroke.Add(Chunk.SPLITCHARACTER, null);
|
|
keysNoStroke.Add(Chunk.HYPHENATION, null);
|
|
keysNoStroke.Add(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 <CODE>PdfChunk</CODE>. */
|
|
protected PdfFont font;
|
|
|
|
protected BaseFont baseFont;
|
|
|
|
protected ISplitCharacter splitCharacter;
|
|
/**
|
|
* Metric attributes.
|
|
* <P>
|
|
* This attributes require the mesurement of characters widths when rendering
|
|
* such as underline.
|
|
*/
|
|
protected Hashtable attributes = new Hashtable();
|
|
|
|
/**
|
|
* Non metric attributes.
|
|
* <P>
|
|
* This attributes do not require the mesurement of characters widths when rendering
|
|
* such as Color.
|
|
*/
|
|
protected Hashtable noStroke = new Hashtable();
|
|
|
|
/** <CODE>true</CODE> if the chunk split was cause by a newline. */
|
|
protected bool newlineSplit;
|
|
|
|
/** The image in this <CODE>PdfChunk</CODE>, 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 bool changeLeading = false;
|
|
|
|
// constructors
|
|
|
|
/**
|
|
* Constructs a <CODE>PdfChunk</CODE>-object.
|
|
*
|
|
* @param string the content of the <CODE>PdfChunk</CODE>-object
|
|
* @param font the <CODE>PdfFont</CODE>
|
|
* @param attributes the metrics attributes
|
|
* @param noStroke the non metric attributes
|
|
*/
|
|
|
|
internal PdfChunk(string str, PdfChunk other) {
|
|
thisChunk[0] = this;
|
|
value = str;
|
|
this.font = other.font;
|
|
this.attributes = other.attributes;
|
|
this.noStroke = other.noStroke;
|
|
this.baseFont = other.baseFont;
|
|
Object[] obj = (Object[])attributes[Chunk.IMAGE];
|
|
if (obj == null)
|
|
image = null;
|
|
else {
|
|
image = (Image)obj[0];
|
|
offsetX = (float)obj[1];
|
|
offsetY = (float)obj[2];
|
|
changeLeading = (bool)obj[3];
|
|
}
|
|
encoding = font.Font.Encoding;
|
|
splitCharacter = (ISplitCharacter)noStroke[Chunk.SPLITCHARACTER];
|
|
if (splitCharacter == null)
|
|
splitCharacter = DefaultSplitCharacter.DEFAULT;
|
|
}
|
|
|
|
/**
|
|
* Constructs a <CODE>PdfChunk</CODE>-object.
|
|
*
|
|
* @param chunk the original <CODE>Chunk</CODE>-object
|
|
* @param action the <CODE>PdfAction</CODE> if the <CODE>Chunk</CODE> comes from an <CODE>Anchor</CODE>
|
|
*/
|
|
|
|
internal PdfChunk(Chunk chunk, PdfAction action) {
|
|
thisChunk[0] = this;
|
|
value = chunk.Content;
|
|
|
|
Font f = chunk.Font;
|
|
float size = f.Size;
|
|
if (size == iTextSharp.text.Font.UNDEFINED)
|
|
size = 12;
|
|
baseFont = f.BaseFont;
|
|
BaseFont bf = f.BaseFont;
|
|
int style = f.Style;
|
|
if (style == iTextSharp.text.Font.UNDEFINED) {
|
|
style = iTextSharp.text.Font.NORMAL;
|
|
}
|
|
if (baseFont == null) {
|
|
// translation of the font-family to a PDF font-family
|
|
baseFont = f.GetCalculatedBaseFont(false);
|
|
}
|
|
else{
|
|
// bold simulation
|
|
if ((style & iTextSharp.text.Font.BOLD) != 0)
|
|
attributes[Chunk.TEXTRENDERMODE] = new Object[]{PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, size / 30f, null};
|
|
// italic simulation
|
|
if ((style & iTextSharp.text.Font.ITALIC) != 0)
|
|
attributes[Chunk.SKEW] = new float[]{0, ITALIC_ANGLE};
|
|
}
|
|
font = new PdfFont(baseFont, size);
|
|
// other style possibilities
|
|
Hashtable attr = chunk.Attributes;
|
|
if (attr != null) {
|
|
foreach (DictionaryEntry entry in attr) {
|
|
string name = (string)entry.Key;
|
|
if (keysAttributes.ContainsKey(name)) {
|
|
attributes[name] = entry.Value;
|
|
}
|
|
else if (keysNoStroke.ContainsKey(name)) {
|
|
noStroke[name] = entry.Value;
|
|
}
|
|
}
|
|
if ("".Equals(attr[Chunk.GENERICTAG])) {
|
|
attributes[Chunk.GENERICTAG] = chunk.Content;
|
|
}
|
|
}
|
|
if (f.IsUnderlined()) {
|
|
Object[] obj = {null, new float[]{0, 1f / 15, 0, -1f / 3, 0}};
|
|
Object[][] unders = Utilities.AddToArray((Object[][])attributes[Chunk.UNDERLINE], obj);
|
|
attributes[Chunk.UNDERLINE] = unders;
|
|
}
|
|
if (f.IsStrikethru()) {
|
|
Object[] obj = {null, new float[]{0, 1f / 15, 0, 1f / 3, 0}};
|
|
Object[][] unders = Utilities.AddToArray((Object[][])attributes[Chunk.UNDERLINE], obj);
|
|
attributes[Chunk.UNDERLINE] = unders;
|
|
}
|
|
if (action != null)
|
|
attributes[Chunk.ACTION] = action;
|
|
// the color can't be stored in a PdfFont
|
|
noStroke[Chunk.COLOR] = f.Color;
|
|
noStroke[Chunk.ENCODING] = font.Font.Encoding;
|
|
Object[] obj2 = (Object[])attributes[Chunk.IMAGE];
|
|
if (obj2 == null)
|
|
image = null;
|
|
else {
|
|
attributes.Remove(Chunk.HSCALE); // images are scaled in other ways
|
|
image = (Image)obj2[0];
|
|
offsetX = ((float)obj2[1]);
|
|
offsetY = ((float)obj2[2]);
|
|
changeLeading = (bool)obj2[3];
|
|
}
|
|
font.Image = image;
|
|
object hs = attributes[Chunk.HSCALE];
|
|
if (hs != null)
|
|
font.HorizontalScaling = (float)hs;
|
|
encoding = font.Font.Encoding;
|
|
splitCharacter = (ISplitCharacter)noStroke[Chunk.SPLITCHARACTER];
|
|
if (splitCharacter == null)
|
|
splitCharacter = DefaultSplitCharacter.DEFAULT;
|
|
}
|
|
|
|
// methods
|
|
|
|
/** Gets the Unicode equivalent to a CID.
|
|
* The (inexistent) CID <FF00> is translated as '\n'.
|
|
* It has only meaning with CJK fonts with Identity encoding.
|
|
* @param c the CID code
|
|
* @return the Unicode equivalent
|
|
*/
|
|
public int GetUnicodeEquivalent(int c) {
|
|
return baseFont.GetUnicodeEquivalent(c);
|
|
}
|
|
|
|
protected int GetWord(string text, int start) {
|
|
int len = text.Length;
|
|
while (start < len) {
|
|
if (!char.IsLetter(text[start]))
|
|
break;
|
|
++start;
|
|
}
|
|
return start;
|
|
}
|
|
|
|
/**
|
|
* Splits this <CODE>PdfChunk</CODE> if it's too long for the given width.
|
|
* <P>
|
|
* Returns <VAR>null</VAR> if the <CODE>PdfChunk</CODE> wasn't truncated.
|
|
*
|
|
* @param width a given width
|
|
* @return the <CODE>PdfChunk</CODE> that doesn't fit into the width.
|
|
*/
|
|
|
|
internal 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 Hashtable();
|
|
image = null;
|
|
font = PdfFont.DefaultFont;
|
|
return pc;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
IHyphenationEvent hyphenationEvent = (IHyphenationEvent)noStroke[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 = (char)0;
|
|
BaseFont ft = font.Font;
|
|
bool surrogate = false;
|
|
if (ft.FontType == BaseFont.FONT_TYPE_CJK && ft.GetUnicodeEquivalent(' ') != ' ') {
|
|
while (currentPosition < length) {
|
|
// the width of every character is added to the currentWidth
|
|
char cidChar = valueArray[currentPosition];
|
|
character = (char)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;
|
|
}
|
|
surrogate = Utilities.IsSurrogatePair(valueArray, currentPosition);
|
|
if (surrogate)
|
|
currentWidth += font.Width(Utilities.ConvertToUtf32(valueArray[currentPosition], valueArray[currentPosition + 1]));
|
|
else
|
|
currentWidth += font.Width(character);
|
|
if (character == ' ') {
|
|
lastSpace = currentPosition + 1;
|
|
lastSpaceWidth = currentWidth;
|
|
}
|
|
if (surrogate)
|
|
currentPosition++;
|
|
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 >= 0 && lastSpace < currentPosition) {
|
|
int wordIdx = GetWord(value, lastSpace);
|
|
if (wordIdx > lastSpace) {
|
|
string pre = hyphenationEvent.GetHyphenatedWordPre(value.Substring(lastSpace, wordIdx - lastSpace), font.Font, font.Size, width - lastSpaceWidth);
|
|
string post = hyphenationEvent.HyphenatedWordPost;
|
|
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 retVal = value.Substring(splitPosition);
|
|
value = Trim(value.Substring(0, splitPosition));
|
|
PdfChunk tmp = new PdfChunk(retVal, this);
|
|
return tmp;
|
|
}
|
|
|
|
/**
|
|
* Truncates this <CODE>PdfChunk</CODE> if it's too long for the given width.
|
|
* <P>
|
|
* Returns <VAR>null</VAR> if the <CODE>PdfChunk</CODE> wasn't truncated.
|
|
*
|
|
* @param width a given width
|
|
* @return the <CODE>PdfChunk</CODE> that doesn't fit into the width.
|
|
*/
|
|
|
|
internal 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.DefaultFont;
|
|
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;
|
|
bool surrogate = false;
|
|
while (currentPosition < length) {
|
|
// the width of every character is added to the currentWidth
|
|
surrogate = Utilities.IsSurrogatePair(value, currentPosition);
|
|
if (surrogate)
|
|
currentWidth += font.Width(Utilities.ConvertToUtf32(value, currentPosition));
|
|
else
|
|
currentWidth += font.Width(value[currentPosition]);
|
|
if (currentWidth > width)
|
|
break;
|
|
if (surrogate)
|
|
currentPosition++;
|
|
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;
|
|
if (surrogate)
|
|
++currentPosition;
|
|
}
|
|
string retVal = value.Substring(currentPosition);
|
|
value = value.Substring(0, currentPosition);
|
|
PdfChunk tmp = new PdfChunk(retVal, this);
|
|
return tmp;
|
|
}
|
|
|
|
// methods to retrieve the membervariables
|
|
|
|
/**
|
|
* Returns the font of this <CODE>Chunk</CODE>.
|
|
*
|
|
* @return a <CODE>PdfFont</CODE>
|
|
*/
|
|
|
|
internal PdfFont Font {
|
|
get {
|
|
return font;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the color of this <CODE>Chunk</CODE>.
|
|
*
|
|
* @return a <CODE>Color</CODE>
|
|
*/
|
|
|
|
internal Color Color {
|
|
get {
|
|
return (Color)noStroke[Chunk.COLOR];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the width of this <CODE>PdfChunk</CODE>.
|
|
*
|
|
* @return a width
|
|
*/
|
|
|
|
internal float Width {
|
|
get {
|
|
return font.Width(this.value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the <CODE>PdfChunk</CODE> split was caused by a newline.
|
|
* @return <CODE>true</CODE> if the <CODE>PdfChunk</CODE> split was caused by a newline.
|
|
*/
|
|
|
|
public bool IsNewlineSplit() {
|
|
return newlineSplit;
|
|
}
|
|
|
|
/**
|
|
* Gets the width of the <CODE>PdfChunk</CODE> 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 TextRise {
|
|
get {
|
|
object f = GetAttribute(Chunk.SUBSUPSCRIPT);
|
|
if (f != null) {
|
|
return (float)f;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trims the last space.
|
|
* @return the width of the space trimmed, otherwise 0
|
|
*/
|
|
|
|
public float TrimLastSpace() {
|
|
BaseFont ft = font.Font;
|
|
if (ft.FontType == 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;
|
|
}
|
|
|
|
public float TrimFirstSpace()
|
|
{
|
|
BaseFont ft = font.Font;
|
|
if (ft.FontType == BaseFont.FONT_TYPE_CJK && ft.GetUnicodeEquivalent(' ') != ' ') {
|
|
if (value.Length > 1 && value.StartsWith("\u0001")) {
|
|
value = value.Substring(1);
|
|
return font.Width('\u0001');
|
|
}
|
|
}
|
|
else {
|
|
if (value.Length > 1 && value.StartsWith(" ")) {
|
|
value = value.Substring(1);
|
|
return font.Width(' ');
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Gets an attribute. The search is made in <CODE>attributes</CODE>
|
|
* and <CODE>noStroke</CODE>.
|
|
* @param name the attribute key
|
|
* @return the attribute value or null if not found
|
|
*/
|
|
|
|
internal Object GetAttribute(string name) {
|
|
if (attributes.ContainsKey(name))
|
|
return attributes[name];
|
|
return noStroke[name];
|
|
}
|
|
|
|
/**
|
|
*Checks if the attribute exists.
|
|
* @param name the attribute key
|
|
* @return <CODE>true</CODE> if the attribute exists
|
|
*/
|
|
|
|
internal bool IsAttribute(string name) {
|
|
if (attributes.ContainsKey(name))
|
|
return true;
|
|
return noStroke.ContainsKey(name);
|
|
}
|
|
|
|
/**
|
|
* Checks if this <CODE>PdfChunk</CODE> needs some special metrics handling.
|
|
* @return <CODE>true</CODE> if this <CODE>PdfChunk</CODE> needs some special metrics handling.
|
|
*/
|
|
|
|
internal bool IsStroked() {
|
|
return (attributes.Count > 0);
|
|
}
|
|
|
|
/**
|
|
* Checks if this <CODE>PdfChunk</CODE> is a Separator Chunk.
|
|
* @return true if this chunk is a separator.
|
|
* @since 2.1.2
|
|
*/
|
|
internal bool IsSeparator() {
|
|
return IsAttribute(Chunk.SEPARATOR);
|
|
}
|
|
|
|
/**
|
|
* Checks if this <CODE>PdfChunk</CODE> is a horizontal Separator Chunk.
|
|
* @return true if this chunk is a horizontal separator.
|
|
* @since 2.1.2
|
|
*/
|
|
internal bool IsHorizontalSeparator() {
|
|
if (IsAttribute(Chunk.SEPARATOR)) {
|
|
Object[] o = (Object[])GetAttribute(Chunk.SEPARATOR);
|
|
return !(bool)o[1];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if this <CODE>PdfChunk</CODE> is a tab Chunk.
|
|
* @return true if this chunk is a separator.
|
|
* @since 2.1.2
|
|
*/
|
|
internal bool IsTab() {
|
|
return IsAttribute(Chunk.TAB);
|
|
}
|
|
|
|
/**
|
|
* Correction for the tab position based on the left starting position.
|
|
* @param newValue the new value for the left X.
|
|
* @since 2.1.2
|
|
*/
|
|
internal void AdjustLeft(float newValue) {
|
|
Object[] o = (Object[])attributes[Chunk.TAB];
|
|
if (o != null) {
|
|
attributes[Chunk.TAB] = new Object[]{o[0], o[1], o[2], newValue};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if there is an image in the <CODE>PdfChunk</CODE>.
|
|
* @return <CODE>true</CODE> if an image is present
|
|
*/
|
|
|
|
internal bool IsImage() {
|
|
return image != null;
|
|
}
|
|
|
|
/**
|
|
* Gets the image in the <CODE>PdfChunk</CODE>.
|
|
* @return the image or <CODE>null</CODE>
|
|
*/
|
|
|
|
internal Image Image {
|
|
get {
|
|
return image;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the image offset in the x direction
|
|
* @return the image offset in the x direction
|
|
*/
|
|
|
|
internal float ImageOffsetX {
|
|
get {
|
|
return offsetX;
|
|
}
|
|
|
|
set {
|
|
this.offsetX = value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the image offset in the y direction
|
|
* @return Gets the image offset in the y direction
|
|
*/
|
|
|
|
internal float ImageOffsetY {
|
|
get {
|
|
return offsetY;
|
|
}
|
|
|
|
set {
|
|
this.offsetY = value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* sets the value.
|
|
*/
|
|
|
|
internal string Value {
|
|
set {
|
|
this.value = value;
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Tells you if this string is in Chinese, Japanese, Korean or Identity-H.
|
|
*/
|
|
|
|
internal bool IsSpecialEncoding() {
|
|
return encoding.Equals(CJKFont.CJK_ENCODING) || encoding.Equals(BaseFont.IDENTITY_H);
|
|
}
|
|
|
|
/**
|
|
* Gets the encoding of this string.
|
|
*
|
|
* @return a <CODE>string</CODE>
|
|
*/
|
|
|
|
internal string Encoding {
|
|
get {
|
|
return encoding;
|
|
}
|
|
}
|
|
|
|
internal int Length {
|
|
get {
|
|
return value.Length;
|
|
}
|
|
}
|
|
|
|
internal int LengthUtf32 {
|
|
get {
|
|
if (!BaseFont.IDENTITY_H.Equals(encoding))
|
|
return value.Length;
|
|
int total = 0;
|
|
int len = value.Length;
|
|
for (int k = 0; k < len; ++k) {
|
|
if (Utilities.IsSurrogateHigh(value[k]))
|
|
++k;
|
|
++total;
|
|
}
|
|
return total;
|
|
}
|
|
}
|
|
|
|
internal bool IsExtSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) {
|
|
return splitCharacter.IsSplitCharacter(start, current, end, cc, ck);
|
|
}
|
|
|
|
/**
|
|
* Removes all the <VAR>' '</VAR> and <VAR>'-'</VAR>-characters on the right of a <CODE>string</CODE>.
|
|
* <P>
|
|
* @param string the <CODE>string<CODE> that has to be trimmed.
|
|
* @return the trimmed <CODE>string</CODE>
|
|
*/
|
|
internal string Trim(string str) {
|
|
BaseFont ft = font.Font;
|
|
if (ft.FontType == BaseFont.FONT_TYPE_CJK && ft.GetUnicodeEquivalent(' ') != ' ') {
|
|
while (str.EndsWith("\u0001")) {
|
|
str = str.Substring(0, str.Length - 1);
|
|
}
|
|
}
|
|
else {
|
|
while (str.EndsWith(" ") || str.EndsWith("\t")) {
|
|
str = str.Substring(0, str.Length - 1);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
public bool ChangeLeading {
|
|
get {
|
|
return changeLeading;
|
|
}
|
|
}
|
|
|
|
internal float GetCharWidth(int c) {
|
|
if (NoPrint(c))
|
|
return 0;
|
|
return font.Width(c);
|
|
}
|
|
|
|
public static bool NoPrint(int c) {
|
|
return ((c >= 0x200b && c <= 0x200f) || (c >= 0x202a && c <= 0x202e));
|
|
}
|
|
}
|
|
}
|