PDFsharp & MigraDoc Foundation
http://forum.pdfsharp.de/

Line aligment using PDF Sharp
http://forum.pdfsharp.de/viewtopic.php?f=2&t=1479
Page 1 of 1

Author:  galahadba [ Wed Dec 15, 2010 7:17 pm ]
Post subject:  Line aligment using PDF Sharp

Hi, i was having dificult to align the text into a retangle. i loked at the class and it was only implemented for aligment TopLeft, so i changed it to accept the center, right, etc... i don't know if it will be of any help, or if you alread corrected it, but i am posting the code, hope it helps. this is the code of the XtextFormatter.cs.


#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)
//
// Copyright (c) 2005-2009 empira Software GmbH, Cologne (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections;
using System.Text;
using PdfSharp.Drawing;
using PdfSharp.Pdf.IO;

namespace PdfSharp.Drawing.Layout
{
/// <summary>
/// Represents a very simple text formatter.
/// If this class does not satisfy your needs on formatting paragraphs I recommend to take a look
/// at MigraDoc Foundation. Alternatively you should copy this class in your own source code and modify it.
/// </summary>
public class XTextFormatter
{
/// <summary>
/// Initializes a new instance of the <see cref="XTextFormatter"/> class.
/// </summary>
public XTextFormatter(XGraphics gfx)
{
if (gfx == null)
throw new ArgumentNullException("gfx");
this.gfx = gfx;
}
XGraphics gfx;

/// <summary>
/// Gets or sets the text.
/// </summary>
/// <value>The text.</value>
public string Text
{
get { return this.text; }
set { this.text = value; }
}
string text;

/// <summary>
/// Gets or sets the font.
/// </summary>
public XFont Font
{
get { return this.font; }
set
{
if (value == null)
throw new ArgumentNullException("font");
this.font = value;

this.lineSpace = font.GetHeight(this.gfx);
this.cyAscent = lineSpace * font.cellAscent / font.cellSpace;
this.cyDescent = lineSpace * font.cellDescent / font.cellSpace;

// HACK in XTextFormatter
this.spaceWidth = gfx.MeasureString("x x", value).width;
this.spaceWidth -= gfx.MeasureString("xx", value).width;
}
}
XFont font;
double lineSpace;
double cyAscent;
double cyDescent;
double spaceWidth;

/// <summary>
/// Gets or sets the bounding box of the layout.
/// </summary>
public XRect LayoutRectangle
{
get { return this.layoutRectangle; }
set { this.layoutRectangle = value; }
}
XRect layoutRectangle;

/// <summary>
/// Gets or sets the alignment of the text.
/// </summary>
public XParagraphAlignment Alignment
{
get { return this.alignment; }
set { this.alignment = value; }
}
XParagraphAlignment alignment = XParagraphAlignment.Left;

/// <summary>
/// Draws the text.
/// </summary>
/// <param name="text">The text to be drawn.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The text brush.</param>
/// <param name="layoutRectangle">The layout rectangle.</param>
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle)
{
DrawString(text, font, brush, layoutRectangle, XStringFormats.TopLeft);
}

/// <summary>
/// Draws the text.
/// </summary>
/// <param name="text">The text to be drawn.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The text brush.</param>
/// <param name="layoutRectangle">The layout rectangle.</param>
/// <param name="format">The format. Must be <c>XStringFormat.TopLeft</c></param>
public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format)
{
if (text == null)
throw new ArgumentNullException("text");
if (font == null)
throw new ArgumentNullException("font");
if (brush == null)
throw new ArgumentNullException("brush");
/*if (format.Alignment != XStringAlignment.Near || format.LineAlignment!= XLineAlignment.Near)
throw new ArgumentException("Only TopLeft alignment is currently implemented.");*/

Text = text;
Font = font;
LayoutRectangle = layoutRectangle;

if (text.Length == 0)
return;

CreateBlocks();

CreateLayout();





double dx = layoutRectangle.Location.x;
double dy = layoutRectangle.Location.y + cyAscent;



/* Alterado por Ângelo Cossa */
List<double> Larr_lineWidth = new List<double>();
List<double> Larr_lineStart = new List<double>();

//If the text fills all the retangle, there is no sense in alignment
bool ends_before_end = false;

//Se if the last block comes before the end,
for (int idx = 0; idx < this.blocks.Count; idx++)
{
Block block = (Block)this.blocks[idx];

if (block.Type == BlockType.LineBreak)
{
Larr_lineStart.Add(0);

if ((idx - 1) < 0)
{
Larr_lineWidth.Add(0);
}
else
{
Larr_lineWidth.Add(this.blocks[idx - 1].Location.X + this.blocks[idx - 1].Width);
}
}

if (block.Stop)
{
ends_before_end = true;
break;
}
}

//Add the last line
Larr_lineStart.Add(0);

if ((this.blocks.Count - 1) < 0)
{
Larr_lineWidth.Add(0);
}
else
{
Larr_lineWidth.Add(this.blocks[this.blocks.Count - 1].Location.X + this.blocks[this.blocks.Count - 1].Width);
}

//Adjust the linestart
if (format.Alignment != XStringAlignment.Near)
{

for (int n = 0; n < Larr_lineWidth.Count; n++)
{
//gets the diference between the width of the retangle and the width of the line
double rest = layoutRectangle.width - Larr_lineWidth[n];


if (format.Alignment == XStringAlignment.Center)
{

Larr_lineStart[n] = rest / 2D;
}
else if (format.Alignment == XStringAlignment.Far)
{
Larr_lineStart[n] = rest;
}
}

}


if (format.LineAlignment != XLineAlignment.Near && !ends_before_end) {

Block last_block = (Block)this.blocks[this.blocks.Count - 1];

//gets the height of the text
double text_height = (last_block.Location.y + last_block.Height);

//the diference between the size of the block and the size fo the text
double rest = layoutRectangle.Height - text_height;


if (format.LineAlignment == XLineAlignment.BaseLine)
{
//if the text is in the botton, the rest is in the top
dy += rest;
}
else if (format.LineAlignment == XLineAlignment.Center)
{
//If the text is in the middle half the rest in the top
dy += (rest / 2D);
}


}




int count = this.blocks.Count;
int lineCount = 0;

for (int idx = 0; idx < count; idx++)
{
Block block = (Block)this.blocks[idx];
if (block.Stop)
break;
if (block.Type == BlockType.LineBreak)
{
lineCount++;
continue;
}

//Now that the text is correctly aligned vertically align it horinzontally
gfx.DrawString(block.Text, font, brush, dx + Larr_lineStart[lineCount] + block.Location.x, dy + block.Location.y);
}
}

void CreateBlocks()
{
this.blocks.Clear();
int length = this.text.Length;
bool inNonWhiteSpace = false;
int startIndex = 0, blockLength = 0;
for (int idx = 0; idx < length; idx++)
{
char ch = text[idx];

// Treat CR and CRLF as LF
if (ch == Chars.CR)
{
if (idx < length - 1 && text[idx + 1] == Chars.LF)
idx++;
ch = Chars.LF;
}
if (ch == Chars.LF)
{
if (blockLength != 0)
{
string token = text.Substring(startIndex, blockLength);
this.blocks.Add(new Block(token, BlockType.Text,
this.gfx.MeasureString(token, this.font).Width, this.gfx.MeasureString(token, this.font).Height));
}
startIndex = idx + 1;
blockLength = 0;
this.blocks.Add(new Block(BlockType.LineBreak));
}
else if (Char.IsWhiteSpace(ch))
{
if (inNonWhiteSpace)
{
string token = text.Substring(startIndex, blockLength);
this.blocks.Add(new Block(token, BlockType.Text,
this.gfx.MeasureString(token, this.font).Width, this.gfx.MeasureString(token, this.font).Height));
startIndex = idx + 1;
blockLength = 0;
}
else
{
blockLength++;
}
}
else
{
inNonWhiteSpace = true;
blockLength++;
}
}
if (blockLength != 0)
{
string token = text.Substring(startIndex, blockLength);
this.blocks.Add(new Block(token, BlockType.Text,
this.gfx.MeasureString(token, this.font).Width, this.gfx.MeasureString(token, this.font).Height));
}
}

void CreateLayout()
{
double rectWidth = this.layoutRectangle.width;
double rectHeight = this.layoutRectangle.height - this.cyAscent - this.cyDescent;
int firstIndex = 0;
double x = 0, y = 0;
int count = this.blocks.Count;
for (int idx = 0; idx < count; idx++)
{
Block block = (Block)this.blocks[idx];
if (block.Type == BlockType.LineBreak)
{
if (Alignment == XParagraphAlignment.Justify)
((Block)this.blocks[firstIndex]).Alignment = XParagraphAlignment.Left;
AlignLine(firstIndex, idx - 1, rectWidth);
firstIndex = idx + 1;
x = 0;
y += this.lineSpace;
}
else
{
double width = block.Width; //!!!modTHHO 19.11.09 don't add this.spaceWidth here
if ((x + width <= rectWidth || x == 0) && block.Type != BlockType.LineBreak)
{
block.Location = new XPoint(x, y);
x += width + spaceWidth; //!!!modTHHO 19.11.09 add this.spaceWidth here
}
else
{
AlignLine(firstIndex, idx - 1, rectWidth);
firstIndex = idx;
y += lineSpace;
if (y > rectHeight)
{
block.Stop = true;
break;
}
block.Location = new XPoint(0, y);
x = width + spaceWidth; //!!!modTHHO 19.11.09 add this.spaceWidth here
}
}
}
if (firstIndex < count && Alignment != XParagraphAlignment.Justify)
AlignLine(firstIndex, count - 1, rectWidth);
}

/// <summary>
/// Align center, right or justify.
/// </summary>
void AlignLine(int firstIndex, int lastIndex, double layoutWidth)
{
XParagraphAlignment blockAlignment = ((Block)(this.blocks[firstIndex])).Alignment;
if (this.alignment == XParagraphAlignment.Left || blockAlignment == XParagraphAlignment.Left)
return;

int count = lastIndex - firstIndex + 1;
if (count == 0)
return;

double totalWidth = -this.spaceWidth;
for (int idx = firstIndex; idx <= lastIndex; idx++)
totalWidth += ((Block)(this.blocks[idx])).Width + this.spaceWidth;

double dx = Math.Max(layoutWidth - totalWidth, 0);
//Debug.Assert(dx >= 0);
if (this.alignment != XParagraphAlignment.Justify)
{
if (this.alignment == XParagraphAlignment.Center)
dx /= 2;
for (int idx = firstIndex; idx <= lastIndex; idx++)
{
Block block = (Block)this.blocks[idx];
block.Location += new XSize(dx, 0);
}
}
else if (count > 1) // case: justify
{
dx /= count - 1;
for (int idx = firstIndex + 1, i = 1; idx <= lastIndex; idx++, i++)
{
Block block = (Block)this.blocks[idx];
block.Location += new XSize(dx * i, 0);
}
}
}

readonly List<Block> blocks = new List<Block>();

enum BlockType
{
Text, Space, Hyphen, LineBreak,
}

/// <summary>
/// Represents a single word.
/// </summary>
class Block
{
/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// </summary>
/// <param name="text">The text of the block.</param>
/// <param name="type">The type of the block.</param>
/// <param name="width">The width of the text.</param>
/// <param name="height">the height of the text</param>
public Block(string text, BlockType type, double width,double height)
{
Text = text;
Type = type;
Width = width;
Height = height;
}

/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// </summary>
/// <param name="type">The type.</param>
public Block(BlockType type)
{
Type = type;
}

/// <summary>
/// The text represented by this block.
/// </summary>
public string Text;

/// <summary>
/// The type of the block.
/// </summary>
public BlockType Type;

/// <summary>
/// The width of the text.
/// </summary>
public double Width;

/// <summary>
/// The Heigth of the text.
/// </summary>
public double Height;


/// <summary>
/// The location relative to the upper left corner of the layout rectangle.
/// </summary>
public XPoint Location;

/// <summary>
/// The alignment of this line.
/// </summary>
public XParagraphAlignment Alignment;

/// <summary>
/// A flag indicating that this is the last bock that fits in the layout rectangle.
/// </summary>
public bool Stop;
}
// TODO:
// - more XStringFormat variations
// - calculate bounding box
// - left and right indent
// - first line indent
// - margins and paddings
// - background color
// - text background color
// - border style
// - hyphens, soft hyphens, hyphenation
// - kerning
// - change font, size, text color etc.
// - line spacing
// - underine and strike-out variation
// - super- and sub-script
// - ...
}
}

Author:  garhbod [ Mon Jan 24, 2011 1:10 pm ]
Post subject:  Re: Line aligment using PDF Sharp

I have updated the XTextFormatter.cs in the code folder of PDFSharp but i'm still gettin the "only TopLeft..." throw when I select XStringFormats.Center

Page 1 of 1 All times are UTC
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/