/** * Creates a line from the chunk array. * @param width the width of the line * @return the line or null if no more chunks */ protected PdfLine CreateLine(float width) { if (chunks.Count == 0) { return(null); } splittedChunkText = null; currentStandbyChunk = null; PdfLine line = new PdfLine(0, width, alignment, 0); string total; for (currentChunkMarker = 0; currentChunkMarker < chunks.Count; ++currentChunkMarker) { PdfChunk original = (PdfChunk)(chunks[currentChunkMarker]); total = original.ToString(); currentStandbyChunk = line.Add(original); if (currentStandbyChunk != null) { splittedChunkText = original.ToString(); original.Value = total; return(line); } } return(line); }
// 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; } }
public float GetWidthCorrected(float charSpacing, float wordSpacing) { float total = 0; for (int k = 0; k < line.Count; ++k) { PdfChunk ck = (PdfChunk)line[k]; total += ck.GetWidthCorrected(charSpacing, wordSpacing); } return(total); }
private void AddToLine(PdfChunk chunk) { if (chunk.ChangeLeading && chunk.IsImage()) { float f = chunk.Image.ScaledHeight + chunk.ImageOffsetY + chunk.Image.BorderWidthTop; if (f > height) { height = f; } } line.Add(chunk); }
/** * Normalizes the list of chunks when the line is accepted. */ protected void ShortenChunkArray() { if (currentChunkMarker < 0) { return; } if (currentChunkMarker >= chunks.Count) { chunks.Clear(); return; } PdfChunk split = (PdfChunk)(chunks[currentChunkMarker]); split.Value = splittedChunkText; chunks[currentChunkMarker] = currentStandbyChunk; for (int j = currentChunkMarker - 1; j >= 0; --j) { chunks.RemoveAt(j); } }
private void AddList(List list, float left, float right, int alignment) { PdfChunk chunk; PdfChunk overflow; ArrayList allActions = new ArrayList(); ProcessActions(list, null, allActions); int aCounter = 0; foreach (IElement ele in list.Items) { switch (ele.Type) { case Element.LISTITEM: ListItem item = (ListItem)ele; line = new PdfLine(left + item.IndentationLeft, right, alignment, item.Leading); line.ListItem = item; foreach (Chunk c in item.Chunks) { chunk = new PdfChunk(c, (PdfAction)(allActions[aCounter++])); while ((overflow = line.Add(chunk)) != null) { AddLine(line); line = new PdfLine(left + item.IndentationLeft, right, alignment, item.Leading); chunk = overflow; } line.ResetAlignment(); AddLine(line); line = new PdfLine(left + item.IndentationLeft, right, alignment, leading); } break; case Element.LIST: List sublist = (List)ele; AddList(sublist, left + sublist.IndentationLeft, right, alignment); break; } } }
// constructors /** * Constructs a <CODE>PdfCell</CODE>-object. * * @param cell the original <CODE>Cell</CODE> * @param rownumber the number of the <CODE>Row</CODE> the <CODE>Cell</CODE> was in. * @param left the left border of the <CODE>PdfCell</CODE> * @param right the right border of the <CODE>PdfCell</CODE> * @param top the top border of the <CODE>PdfCell</CODE> * @param cellspacing the cellspacing of the <CODE>Table</CODE> * @param cellpadding the cellpadding of the <CODE>Table</CODE> */ public PdfCell(Cell cell, int rownumber, float left, float right, float top, float cellspacing, float cellpadding) : base(left, top, right, top) { // copying the other Rectangle attributes from class Cell CloneNonPositionParameters(cell); this.cellpadding = cellpadding; this.cellspacing = cellspacing; this.verticalAlignment = cell.VerticalAlignment; this.useAscender = cell.UseAscender; this.useDescender = cell.UseDescender; this.useBorderPadding = cell.UseBorderPadding; // initialisation of some parameters PdfChunk chunk; PdfChunk overflow; lines = new ArrayList(); images = new ArrayList(); leading = cell.Leading; int alignment = cell.HorizontalAlignment; left += cellspacing + cellpadding; right -= cellspacing + cellpadding; left += GetBorderWidthInside(LEFT_BORDER); right -= GetBorderWidthInside(RIGHT_BORDER); contentHeight = 0; rowspan = cell.Rowspan; ArrayList allActions; int aCounter; // we loop over all the elements of the cell foreach (IElement ele in cell.Elements) { switch (ele.Type) { case Element.JPEG: case Element.JPEG2000: case Element.JBIG2: case Element.IMGRAW: case Element.IMGTEMPLATE: AddImage((Image)ele, left, right, 0.4f * leading, alignment); break; // if the element is a list case Element.LIST: if (line != null && line.Size > 0) { line.ResetAlignment(); AddLine(line); } // we loop over all the listitems AddList((List)ele, left, right, alignment); line = new PdfLine(left, right, alignment, leading); break; // if the element is something else default: allActions = new ArrayList(); ProcessActions(ele, null, allActions); aCounter = 0; float currentLineLeading = leading; float currentLeft = left; float currentRight = right; if (ele is Phrase) { currentLineLeading = ((Phrase)ele).Leading; } if (ele is Paragraph) { Paragraph p = (Paragraph)ele; currentLeft += p.IndentationLeft; currentRight -= p.IndentationRight; } if (line == null) { line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading); } // we loop over the chunks ArrayList chunks = ele.Chunks; if (chunks.Count == 0) { AddLine(line); // add empty line - all cells need some lines even if they are empty line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading); } else { foreach (Chunk c in chunks) { chunk = new PdfChunk(c, (PdfAction)allActions[aCounter++]); while ((overflow = line.Add(chunk)) != null) { AddLine(line); line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading); chunk = overflow; } } } // if the element is a paragraph, section or chapter, we reset the alignment and add the line switch (ele.Type) { case Element.PARAGRAPH: case Element.SECTION: case Element.CHAPTER: line.ResetAlignment(); FlushCurrentLine(); break; } break; } } FlushCurrentLine(); if (lines.Count > cell.MaxLines) { while (lines.Count > cell.MaxLines) { RemoveLine(lines.Count - 1); } if (cell.MaxLines > 0) { String more = cell.ShowTruncation; if (more != null && more.Length > 0) { // Denote that the content has been truncated lastLine = (PdfLine)lines[lines.Count - 1]; if (lastLine.Size >= 0) { PdfChunk lastChunk = lastLine.GetChunk(lastLine.Size - 1); float moreWidth = new PdfChunk(more, lastChunk).Width; while (lastChunk.ToString().Length > 0 && lastChunk.Width + moreWidth > right - left) { // Remove characters to leave room for the 'more' indicator lastChunk.Value = lastChunk.ToString().Substring(0, lastChunk.Length - 1); } lastChunk.Value = lastChunk.ToString() + more; } else { lastLine.Add(new PdfChunk(new Chunk(more), null)); } } } } // we set some additional parameters if (useDescender && lastLine != null) { contentHeight -= lastLine.Descender; } // adjust first line height so that it touches the top if (lines.Count > 0) { firstLine = (PdfLine)lines[0]; float firstLineRealHeight = FirstLineRealHeight; contentHeight -= firstLine.Height; firstLine.height = firstLineRealHeight; contentHeight += firstLineRealHeight; } float newBottom = top - contentHeight - (2f * Cellpadding) - (2f * Cellspacing); newBottom -= GetBorderWidthInside(TOP_BORDER) + GetBorderWidthInside(BOTTOM_BORDER); Bottom = newBottom; this.rownumber = rownumber; }
/** * 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); }
// constructors /** * Constructs a <CODE>PdfCell</CODE>-object. * * @param cell the original <CODE>Cell</CODE> * @param rownumber the number of the <CODE>Row</CODE> the <CODE>Cell</CODE> was in. * @param left the left border of the <CODE>PdfCell</CODE> * @param right the right border of the <CODE>PdfCell</CODE> * @param top the top border of the <CODE>PdfCell</CODE> * @param cellspacing the cellspacing of the <CODE>Table</CODE> * @param cellpadding the cellpadding of the <CODE>Table</CODE> */ public PdfCell(Cell cell, int rownumber, float left, float right, float top, float cellspacing, float cellpadding) : base(left, top, right, top) { // copying the other Rectangle attributes from class Cell CloneNonPositionParameters(cell); this.cellpadding = cellpadding; this.cellspacing = cellspacing; this.verticalAlignment = cell.VerticalAlignment; this.useAscender = cell.UseAscender; this.useDescender = cell.UseDescender; this.useBorderPadding = cell.UseBorderPadding; // initialisation of some parameters PdfChunk chunk; PdfChunk overflow; lines = new ArrayList(); images = new ArrayList(); leading = cell.Leading; int alignment = cell.HorizontalAlignment; left += cellspacing + cellpadding; right -= cellspacing + cellpadding; left += GetBorderWidthInside(LEFT_BORDER); right -= GetBorderWidthInside(RIGHT_BORDER); contentHeight = 0; rowspan = cell.Rowspan; ArrayList allActions; int aCounter; // we loop over all the elements of the cell foreach (IElement ele in cell.Elements) { switch (ele.Type) { case Element.JPEG: case Element.JPEG2000: case Element.JBIG2: case Element.IMGRAW: case Element.IMGTEMPLATE: AddImage((Image)ele, left, right, 0.4f * leading, alignment); break; // if the element is a list case Element.LIST: if (line != null && line.Size > 0) { line.ResetAlignment(); AddLine(line); } // we loop over all the listitems AddList((List)ele, left, right, alignment); line = new PdfLine(left, right, alignment, leading); break; // if the element is something else default: allActions = new ArrayList(); ProcessActions(ele, null, allActions); aCounter = 0; float currentLineLeading = leading; float currentLeft = left; float currentRight = right; if (ele is Phrase) { currentLineLeading = ((Phrase) ele).Leading; } if (ele is Paragraph) { Paragraph p = (Paragraph) ele; currentLeft += p.IndentationLeft; currentRight -= p.IndentationRight; } if (line == null) { line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading); } // we loop over the chunks ArrayList chunks = ele.Chunks; if (chunks.Count == 0) { AddLine(line); // add empty line - all cells need some lines even if they are empty line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading); } else { foreach (Chunk c in chunks) { chunk = new PdfChunk(c, (PdfAction)allActions[aCounter++]); while ((overflow = line.Add(chunk)) != null) { AddLine(line); line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading); chunk = overflow; } } } // if the element is a paragraph, section or chapter, we reset the alignment and add the line switch (ele.Type) { case Element.PARAGRAPH: case Element.SECTION: case Element.CHAPTER: line.ResetAlignment(); FlushCurrentLine(); break; } break; } } FlushCurrentLine(); if (lines.Count > cell.MaxLines) { while (lines.Count > cell.MaxLines) { RemoveLine(lines.Count - 1); } if (cell.MaxLines > 0) { String more = cell.ShowTruncation; if (more != null && more.Length > 0) { // Denote that the content has been truncated lastLine = (PdfLine) lines[lines.Count - 1]; if (lastLine.Size >= 0) { PdfChunk lastChunk = lastLine.GetChunk(lastLine.Size - 1); float moreWidth = new PdfChunk(more, lastChunk).Width; while (lastChunk.ToString().Length > 0 && lastChunk.Width + moreWidth > right - left) { // Remove characters to leave room for the 'more' indicator lastChunk.Value = lastChunk.ToString().Substring(0, lastChunk.Length - 1); } lastChunk.Value = lastChunk.ToString() + more; } else { lastLine.Add(new PdfChunk(new Chunk(more), null)); } } } } // we set some additional parameters if (useDescender && lastLine != null) { contentHeight -= lastLine.Descender; } // adjust first line height so that it touches the top if (lines.Count > 0) { firstLine = (PdfLine) lines[0]; float firstLineRealHeight = FirstLineRealHeight; contentHeight -= firstLine.Height; firstLine.height = firstLineRealHeight; contentHeight += firstLineRealHeight; } float newBottom = top - contentHeight - (2f * Cellpadding) - (2f * Cellspacing); newBottom -= GetBorderWidthInside(TOP_BORDER) + GetBorderWidthInside(BOTTOM_BORDER); Bottom = newBottom; this.rownumber = rownumber; }
// methods /** * Adds a <CODE>PdfChunk</CODE> to the <CODE>PdfLine</CODE>. * * @param chunk the <CODE>PdfChunk</CODE> to add * @return <CODE>null</CODE> if the chunk could be added completely; if not * a <CODE>PdfChunk</CODE> containing the part of the chunk that could * not be added is returned */ internal PdfChunk Add(PdfChunk chunk) { // nothing happens if the chunk is null. if (chunk == null || chunk.ToString().Equals("")) { return null; } // we split the chunk to be added PdfChunk overflow = chunk.Split(width); newlineSplit = (chunk.IsNewlineSplit() || overflow == null); // if (chunk.IsNewlineSplit() && alignment == Element.ALIGN_JUSTIFIED) // alignment = Element.ALIGN_LEFT; if (chunk.IsTab()) { Object[] tab = (Object[])chunk.GetAttribute(Chunk.TAB); float tabPosition = (float)tab[1]; bool newline = (bool)tab[2]; if (newline && tabPosition < originalWidth - width) { return chunk; } width = originalWidth - tabPosition; chunk.AdjustLeft(left); AddToLine(chunk); } // if the length of the chunk > 0 we add it to the line else if (chunk.Length > 0 || chunk.IsImage()) { if (overflow != null) chunk.TrimLastSpace(); width -= chunk.Width; AddToLine(chunk); } // if the length == 0 and there were no other chunks added to the line yet, // we risk to end up in an endless loop trying endlessly to add the same chunk else if (line.Count < 1) { chunk = overflow; overflow = chunk.Truncate(width); width -= chunk.Width; if (chunk.Length > 0) { AddToLine(chunk); return overflow; } // if the chunck couldn't even be truncated, we add everything, so be it else { if (overflow != null) AddToLine(chunk); return null; } } else { width += ((PdfChunk)(line[line.Count - 1])).TrimLastSpace(); } return overflow; }
/** * 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; }
// 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; }
/** * Returns the current character * @param current current position in the array * @param cc the character array that has to be checked * @param ck chunk array * @return the current character */ protected char GetCurrentCharacter(int current, char[] cc, PdfChunk[] ck) { if (ck == null) { return (char)cc[current]; } return (char)ck[Math.Min(current, ck.Length - 1)].GetUnicodeEquivalent(cc[current]); }
public void AddChunk(PdfChunk chunk) { chunks.Add(chunk); }
// methods /** * Adds a <CODE>PdfChunk</CODE> to the <CODE>PdfLine</CODE>. * * @param chunk the <CODE>PdfChunk</CODE> to add * @return <CODE>null</CODE> if the chunk could be added completely; if not * a <CODE>PdfChunk</CODE> containing the part of the chunk that could * not be added is returned */ internal PdfChunk Add(PdfChunk chunk) { // nothing happens if the chunk is null. if (chunk == null || chunk.ToString().Equals("")) { return(null); } // we split the chunk to be added PdfChunk overflow = chunk.Split(width); newlineSplit = (chunk.IsNewlineSplit() || overflow == null); // if (chunk.IsNewlineSplit() && alignment == Element.ALIGN_JUSTIFIED) // alignment = Element.ALIGN_LEFT; if (chunk.IsTab()) { Object[] tab = (Object[])chunk.GetAttribute(Chunk.TAB); float tabPosition = (float)tab[1]; bool newline = (bool)tab[2]; if (newline && tabPosition < originalWidth - width) { return(chunk); } width = originalWidth - tabPosition; chunk.AdjustLeft(left); AddToLine(chunk); } // if the length of the chunk > 0 we add it to the line else if (chunk.Length > 0 || chunk.IsImage()) { if (overflow != null) { chunk.TrimLastSpace(); } width -= chunk.Width; AddToLine(chunk); } // if the length == 0 and there were no other chunks added to the line yet, // we risk to end up in an endless loop trying endlessly to add the same chunk else if (line.Count < 1) { chunk = overflow; overflow = chunk.Truncate(width); width -= chunk.Width; if (chunk.Length > 0) { AddToLine(chunk); return(overflow); } // if the chunck couldn't even be truncated, we add everything, so be it else { if (overflow != null) { AddToLine(chunk); } return(null); } } else { width += ((PdfChunk)(line[line.Count - 1])).TrimLastSpace(); } return(overflow); }
/** * Creates a line from the chunk array. * @param width the width of the line * @return the line or null if no more chunks */ protected PdfLine CreateLine(float width) { if (chunks.Count == 0) return null; splittedChunkText = null; currentStandbyChunk = null; PdfLine line = new PdfLine(0, width, alignment, 0); string total; for (currentChunkMarker = 0; currentChunkMarker < chunks.Count; ++currentChunkMarker) { PdfChunk original = (PdfChunk)(chunks[currentChunkMarker]); total = original.ToString(); currentStandbyChunk = line.Add(original); if (currentStandbyChunk != null) { splittedChunkText = original.ToString(); original.Value = total; return line; } } return line; }
public void AddPiece(char c, PdfChunk chunk) { if (totalTextLength >= pieceSize) { char[] tempText = text; PdfChunk[] tempDetailChunks = detailChunks; pieceSize *= 2; text = new char[pieceSize]; detailChunks = new PdfChunk[pieceSize]; Array.Copy(tempText, 0, text, 0, totalTextLength); Array.Copy(tempDetailChunks, 0, detailChunks, 0, totalTextLength); } text[totalTextLength] = c; detailChunks[totalTextLength++] = chunk; }
/** * Checks if a character can be used to split a <CODE>PdfString</CODE>. * <P> * for the moment every character less than or equal to SPACE, the character '-' * and some specific unicode ranges 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 <CODE>true</CODE> if the character can be used to split a string, <CODE>false</CODE> otherwise */ public bool IsSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) { char c = GetCurrentCharacter(current, cc, ck); if (c <= ' ' || c == '-' || c == '\u2010') { return true; } if (c < 0x2002) return false; return ((c >= 0x2002 && c <= 0x200b) || (c >= 0x2e80 && c < 0xd7a0) || (c >= 0xf900 && c < 0xfb00) || (c >= 0xfe30 && c < 0xfe50) || (c >= 0xff61 && c < 0xffa0)); }
public ArrayList CreateArrayOfPdfChunks(int startIdx, int endIdx, PdfChunk extraPdfChunk) { bool bidi = (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL); if (bidi) Reorder(startIdx, endIdx); ArrayList ar = new ArrayList(); PdfChunk refCk = detailChunks[startIdx]; PdfChunk ck = null; StringBuilder buf = new StringBuilder(); char c; int idx = 0; for (; startIdx <= endIdx; ++startIdx) { idx = bidi ? indexChars[startIdx] : startIdx; c = text[idx]; ck = detailChunks[idx]; if (PdfChunk.NoPrint(ck.GetUnicodeEquivalent(c))) continue; if (ck.IsImage() || ck.IsSeparator() || ck.IsTab()) { if (buf.Length > 0) { ar.Add(new PdfChunk(buf.ToString(), refCk)); buf = new StringBuilder(); } ar.Add(ck); } else if (ck == refCk) { buf.Append(c); } else { if (buf.Length > 0) { ar.Add(new PdfChunk(buf.ToString(), refCk)); buf = new StringBuilder(); } if (!ck.IsImage() && !ck.IsSeparator() && !ck.IsTab()) buf.Append(c); refCk = ck; } } if (buf.Length > 0) { ar.Add(new PdfChunk(buf.ToString(), refCk)); } if (extraPdfChunk != null) ar.Add(extraPdfChunk); return ar; }
internal bool IsExtSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) { return splitCharacter.IsSplitCharacter(start, current, end, cc, ck); }
public PdfLine ProcessLine(float leftX, float width, int alignment, int runDirection, int arabicOptions) { this.arabicOptions = arabicOptions; Save(); bool isRTL = (runDirection == PdfWriter.RUN_DIRECTION_RTL); if (currentChar >= totalTextLength) { bool hasText = GetParagraph(runDirection); if (!hasText) return null; if (totalTextLength == 0) { ArrayList ar = new ArrayList(); PdfChunk ckx = new PdfChunk("", detailChunks[0]); ar.Add(ckx); return new PdfLine(0, 0, 0, alignment, true, ar, isRTL); } } float originalWidth = width; int lastSplit = -1; if (currentChar != 0) currentChar = TrimLeftEx(currentChar, totalTextLength - 1); int oldCurrentChar = currentChar; int uniC = 0; PdfChunk ck = null; float charWidth = 0; PdfChunk lastValidChunk = null; bool splitChar = false; bool surrogate = false; for (; currentChar < totalTextLength; ++currentChar) { ck = detailChunks[currentChar]; surrogate = Utilities.IsSurrogatePair(text, currentChar); if (surrogate) uniC = ck.GetUnicodeEquivalent(Utilities.ConvertToUtf32(text, currentChar)); else uniC = ck.GetUnicodeEquivalent(text[currentChar]); if (PdfChunk.NoPrint(uniC)) continue; if (surrogate) charWidth = ck.GetCharWidth(uniC); else charWidth = ck.GetCharWidth(text[currentChar]); splitChar = ck.IsExtSplitCharacter(oldCurrentChar, currentChar, totalTextLength, text, detailChunks); if (splitChar && Char.IsWhiteSpace((char)uniC)) lastSplit = currentChar; if (width - charWidth < 0) break; if (splitChar) lastSplit = currentChar; width -= charWidth; lastValidChunk = ck; if (surrogate) ++currentChar; if (ck.IsTab()) { Object[] tab = (Object[])ck.GetAttribute(Chunk.TAB); float tabPosition = (float)tab[1]; bool newLine = (bool)tab[2]; if (newLine && tabPosition < originalWidth - width) { return new PdfLine(0, originalWidth, width, alignment, true, CreateArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL); } detailChunks[currentChar].AdjustLeft(leftX); width = originalWidth - tabPosition; } } if (lastValidChunk == null) { // not even a single char fit; must output the first char ++currentChar; if (surrogate) ++currentChar; return new PdfLine(0, originalWidth, 0, alignment, false, CreateArrayOfPdfChunks(currentChar - 1, currentChar - 1), isRTL); } if (currentChar >= totalTextLength) { // there was more line than text return new PdfLine(0, originalWidth, width, alignment, true, CreateArrayOfPdfChunks(oldCurrentChar, totalTextLength - 1), isRTL); } int newCurrentChar = TrimRightEx(oldCurrentChar, currentChar - 1); if (newCurrentChar < oldCurrentChar) { // only WS return new PdfLine(0, originalWidth, width, alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL); } if (newCurrentChar == currentChar - 1) { // middle of word IHyphenationEvent he = (IHyphenationEvent)lastValidChunk.GetAttribute(Chunk.HYPHENATION); if (he != null) { int[] word = GetWord(oldCurrentChar, newCurrentChar); if (word != null) { float testWidth = width + GetWidth(word[0], currentChar - 1); String pre = he.GetHyphenatedWordPre(new String(text, word[0], word[1] - word[0]), lastValidChunk.Font.Font, lastValidChunk.Font.Size, testWidth); String post = he.HyphenatedWordPost; if (pre.Length > 0) { PdfChunk extra = new PdfChunk(pre, lastValidChunk); currentChar = word[1] - post.Length; return new PdfLine(0, originalWidth, testWidth - lastValidChunk.Font.Width(pre), alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, word[0] - 1, extra), isRTL); } } } } if (lastSplit == -1 || lastSplit >= newCurrentChar) { // no split point or split point ahead of end return new PdfLine(0, originalWidth, width + GetWidth(newCurrentChar + 1, currentChar - 1), alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL); } // standard split currentChar = lastSplit + 1; newCurrentChar = TrimRightEx(oldCurrentChar, lastSplit); if (newCurrentChar < oldCurrentChar) { // only WS again newCurrentChar = currentChar - 1; } return new PdfLine(0, originalWidth, originalWidth - GetWidth(oldCurrentChar, newCurrentChar), alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL); }
/** * 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; }
/** * 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); }
private void AddToLine(PdfChunk chunk) { if (chunk.ChangeLeading && chunk.IsImage()) { float f = chunk.Image.ScaledHeight + chunk.ImageOffsetY + chunk.Image.BorderWidthTop; if (f > height) height = f; } line.Add(chunk); }
/** * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>. * * @param element the element to add * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not. * @throws DocumentException when a document isn't open yet, or has been closed */ public override bool Add(IElement element) { if (writer != null && writer.IsPaused()) { return false; } switch (element.Type) { // Information (headers) case Element.HEADER: info.Addkey(((Meta)element).Name, ((Meta)element).Content); break; case Element.TITLE: info.AddTitle(((Meta)element).Content); break; case Element.SUBJECT: info.AddSubject(((Meta)element).Content); break; case Element.KEYWORDS: info.AddKeywords(((Meta)element).Content); break; case Element.AUTHOR: info.AddAuthor(((Meta)element).Content); break; case Element.CREATOR: info.AddCreator(((Meta)element).Content); break; case Element.PRODUCER: // you can not change the name of the producer info.AddProducer(); break; case Element.CREATIONDATE: // you can not set the creation date, only reset it info.AddCreationDate(); break; // content (text) case Element.CHUNK: { // if there isn't a current line available, we make one if (line == null) { CarriageReturn(); } // we cast the element to a chunk PdfChunk chunk = new PdfChunk((Chunk) element, anchorAction); // we try to add the chunk to the line, until we succeed { PdfChunk overflow; while ((overflow = line.Add(chunk)) != null) { CarriageReturn(); chunk = overflow; chunk.TrimFirstSpace(); } } pageEmpty = false; if (chunk.IsAttribute(Chunk.NEWPAGE)) { NewPage(); } break; } case Element.ANCHOR: { leadingCount++; Anchor anchor = (Anchor) element; String url = anchor.Reference; leading = anchor.Leading; if (url != null) { anchorAction = new PdfAction(url); } // we process the element element.Process(this); anchorAction = null; leadingCount--; break; } case Element.ANNOTATION: { if (line == null) { CarriageReturn(); } Annotation annot = (Annotation) element; Rectangle rect = new Rectangle(0, 0); if (line != null) rect = new Rectangle(annot.GetLlx(IndentRight - line.WidthLeft), annot.GetLly(IndentTop - currentHeight), annot.GetUrx(IndentRight - line.WidthLeft + 20), annot.GetUry(IndentTop - currentHeight - 20)); PdfAnnotation an = PdfAnnotationsImp.ConvertAnnotation(writer, annot, rect); annotationsImp.AddPlainAnnotation(an); pageEmpty = false; break; } case Element.PHRASE: { leadingCount++; // we cast the element to a phrase and set the leading of the document leading = ((Phrase) element).Leading; // we process the element element.Process(this); leadingCount--; break; } case Element.PARAGRAPH: { leadingCount++; // we cast the element to a paragraph Paragraph paragraph = (Paragraph) element; AddSpacing(paragraph.SpacingBefore, leading, paragraph.Font); // we adjust the parameters of the document alignment = paragraph.Alignment; leading = paragraph.TotalLeading; CarriageReturn(); // we don't want to make orphans/widows if (currentHeight + line.Height + leading > IndentTop - IndentBottom) { NewPage(); } indentation.indentLeft += paragraph.IndentationLeft; indentation.indentRight += paragraph.IndentationRight; CarriageReturn(); IPdfPageEvent pageEvent = writer.PageEvent; if (pageEvent != null && !isSectionTitle) pageEvent.OnParagraph(writer, this, IndentTop - currentHeight); // if a paragraph has to be kept together, we wrap it in a table object if (paragraph.KeepTogether) { CarriageReturn(); PdfPTable table = new PdfPTable(1); table.WidthPercentage = 100f; PdfPCell cell = new PdfPCell(); cell.AddElement(paragraph); cell.Border = Rectangle.NO_BORDER; cell.Padding = 0; table.AddCell(cell); indentation.indentLeft -= paragraph.IndentationLeft; indentation.indentRight -= paragraph.IndentationRight; this.Add(table); indentation.indentLeft += paragraph.IndentationLeft; indentation.indentRight += paragraph.IndentationRight; } else { line.SetExtraIndent(paragraph.FirstLineIndent); element.Process(this); CarriageReturn(); AddSpacing(paragraph.SpacingAfter, paragraph.TotalLeading, paragraph.Font); } if (pageEvent != null && !isSectionTitle) pageEvent.OnParagraphEnd(writer, this, IndentTop - currentHeight); alignment = Element.ALIGN_LEFT; indentation.indentLeft -= paragraph.IndentationLeft; indentation.indentRight -= paragraph.IndentationRight; CarriageReturn(); leadingCount--; break; } case Element.SECTION: case Element.CHAPTER: { // Chapters and Sections only differ in their constructor // so we cast both to a Section Section section = (Section) element; IPdfPageEvent pageEvent = writer.PageEvent; bool hasTitle = section.NotAddedYet && section.Title != null; // if the section is a chapter, we begin a new page if (section.TriggerNewPage) { NewPage(); } if (hasTitle) { float fith = IndentTop - currentHeight; int rotation = pageSize.Rotation; if (rotation == 90 || rotation == 180) fith = pageSize.Height - fith; PdfDestination destination = new PdfDestination(PdfDestination.FITH, fith); while (currentOutline.Level >= section.Depth) { currentOutline = currentOutline.Parent; } PdfOutline outline = new PdfOutline(currentOutline, destination, section.GetBookmarkTitle(), section.BookmarkOpen); currentOutline = outline; } // some values are set CarriageReturn(); indentation.sectionIndentLeft += section.IndentationLeft; indentation.sectionIndentRight += section.IndentationRight; if (section.NotAddedYet && pageEvent != null) if (element.Type == Element.CHAPTER) pageEvent.OnChapter(writer, this, IndentTop - currentHeight, section.Title); else pageEvent.OnSection(writer, this, IndentTop - currentHeight, section.Depth, section.Title); // the title of the section (if any has to be printed) if (hasTitle) { isSectionTitle = true; Add(section.Title); isSectionTitle = false; } indentation.sectionIndentLeft += section.Indentation; // we process the section element.Process(this); // some parameters are set back to normal again indentation.sectionIndentLeft -= (section.IndentationLeft + section.Indentation); indentation.sectionIndentRight -= section.IndentationRight; if (section.ElementComplete && pageEvent != null) if (element.Type == Element.CHAPTER) pageEvent.OnChapterEnd(writer, this, IndentTop - currentHeight); else pageEvent.OnSectionEnd(writer, this, IndentTop - currentHeight); break; } case Element.LIST: { // we cast the element to a List List list = (List) element; if (list.Alignindent) { list.NormalizeIndentation(); } // we adjust the document indentation.listIndentLeft += list.IndentationLeft; indentation.indentRight += list.IndentationRight; // we process the items in the list element.Process(this); // some parameters are set back to normal again indentation.listIndentLeft -= list.IndentationLeft; indentation.indentRight -= list.IndentationRight; CarriageReturn(); break; } case Element.LISTITEM: { leadingCount++; // we cast the element to a ListItem ListItem listItem = (ListItem) element; AddSpacing(listItem.SpacingBefore, leading, listItem.Font); // we adjust the document alignment = listItem.Alignment; indentation.listIndentLeft += listItem.IndentationLeft; indentation.indentRight += listItem.IndentationRight; leading = listItem.TotalLeading; CarriageReturn(); // we prepare the current line to be able to show us the listsymbol line.ListItem = listItem; // we process the item element.Process(this); AddSpacing(listItem.SpacingAfter, listItem.TotalLeading, listItem.Font); // if the last line is justified, it should be aligned to the left if (line.HasToBeJustified()) { line.ResetAlignment(); } // some parameters are set back to normal again CarriageReturn(); indentation.listIndentLeft -= listItem.IndentationLeft; indentation.indentRight -= listItem.IndentationRight; leadingCount--; break; } case Element.RECTANGLE: { Rectangle rectangle = (Rectangle) element; graphics.Rectangle(rectangle); pageEmpty = false; break; } case Element.PTABLE: { PdfPTable ptable = (PdfPTable)element; if (ptable.Size <= ptable.HeaderRows) break; //nothing to do // before every table, we add a new line and flush all lines EnsureNewLine(); FlushLines(); AddPTable(ptable); pageEmpty = false; NewLine(); break; } case Element.MULTI_COLUMN_TEXT: { EnsureNewLine(); FlushLines(); MultiColumnText multiText = (MultiColumnText) element; float height = multiText.Write(writer.DirectContent, this, IndentTop - currentHeight); currentHeight += height; text.MoveText(0, -1f* height); pageEmpty = false; break; } case Element.TABLE : { if (element is SimpleTable) { PdfPTable ptable = ((SimpleTable)element).CreatePdfPTable(); if (ptable.Size <= ptable.HeaderRows) break; //nothing to do // before every table, we add a new line and flush all lines EnsureNewLine(); FlushLines(); AddPTable(ptable); pageEmpty = false; break; } else if (element is Table) { try { PdfPTable ptable = ((Table)element).CreatePdfPTable(); if (ptable.Size <= ptable.HeaderRows) break; //nothing to do // before every table, we add a new line and flush all lines EnsureNewLine(); FlushLines(); AddPTable(ptable); pageEmpty = false; break; } catch (BadElementException) { // constructing the PdfTable // Before the table, add a blank line using offset or default leading float offset = ((Table)element).Offset; if (float.IsNaN(offset)) offset = leading; CarriageReturn(); lines.Add(new PdfLine(IndentLeft, IndentRight, alignment, offset)); currentHeight += offset; AddPdfTable((Table)element); } } else { return false; } break; } case Element.JPEG: case Element.JPEG2000: case Element.JBIG2: case Element.IMGRAW: case Element.IMGTEMPLATE: { //carriageReturn(); suggestion by Marc Campforts Add((Image) element); break; } case Element.YMARK: { IDrawInterface zh = (IDrawInterface)element; zh.Draw(graphics, IndentLeft, IndentBottom, IndentRight, IndentTop, IndentTop - currentHeight - (leadingCount > 0 ? leading : 0)); pageEmpty = false; break; } case Element.MARKED: { MarkedObject mo; if (element is MarkedSection) { mo = ((MarkedSection)element).Title; if (mo != null) { mo.Process(this); } } mo = (MarkedObject)element; mo.Process(this); break; } default: return false; } lastElementType = element.Type; return true; }