Centered, multiline text using iTextSharp ColumnText - pdf-generation

I am trying to center a block of multiline text using iTextSharp. I thought that using the ColumnText would do the trick, but I'm having some trouble getting both center alignment, as well as proper wrapping working at the same time.
Here is some code which shows the 2 methods I'm trying:
private void PrintLocationAddress(PdfContentByte Canvas, string Address)
{
//define the regions for our ColumnText objects
Rectangle rect1 = new Rectangle(150f, 300, 350f, 450f);
Rectangle rect2 = new Rectangle(150f, 50f, 350f, 200f);
//outline the rectangles so we can visualize placement of the ColumnText
Canvas.Rectangle(rect1.Left, rect1.Bottom, rect1.Width, rect1.Height);
Canvas.Rectangle(rect2.Left, rect2.Bottom, rect2.Width, rect2.Height);
Canvas.SetColorStroke(BaseColor.CYAN);
Canvas.Stroke();
//define the text and style
Chunk c = new Chunk(Address, new Font(Font.FontFamily.COURIER, 12, Font.NORMAL, BaseColor.MAGENTA));
c.SetTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL, 0, BaseColor.PINK);
Phrase LongText = new Phrase(c);
//this text is centered, but will wrap onto itself on the same line
ColumnText column1 = new ColumnText(Canvas);
column1.SetSimpleColumn(LongText, rect1.Left, rect1.Bottom, rect1.Right, rect1.Top, 0, PdfContentByte.ALIGN_CENTER);
column1.Go();
//this text will wrap, but how to center it?!
ColumnText column2 = new ColumnText(Canvas);
column2.SetSimpleColumn(rect2);
column2.SetText(LongText);
column2.Go();
}
You can view the output for the above code:
http://imgur.com/Ty3oD7w

I missed the ColumnText.Alignment property:
column2.Alignment = Element.ALIGN_CENTER;
So, to get Multiline text, centered, in a box of specified dimensions using ColumnText:
ColumnText column2 = new ColumnText(Canvas);
column2.SetSimpleColumn(rect2);
column2.SetText(LongText);
column2.Alignment = Element.ALIGN_CENTER;
column2.Go();

Related

iText 7 - How to fill a canvas rectangle with a transparent color

In iText 7.1.9 I am taking a pdf created programmatically (not via iText) and need to apply a transparent rectangle along the left side and bottom to ensure the no content exists within a predefined clear zone (for print).
The below code places the yellow rectangles correctly but the desired result is the for the yellow fill to be semi-transparent or not 100% opaque so that visual inspection will show the content that that intersects with the rectangle instead of the rectangle clipping the content.
var page = pdf.GetPage(1);
PdfCanvas canvas = new PdfCanvas(page);
canvas.SaveState();
canvas.SetFillColor(iText.Kernel.Colors.ColorConstants.YELLOW);
var pageHeight = page.GetPageSize().GetHeight();
var pageWidth = page.GetPageSize().GetWidth();
// left side
canvas.Rectangle(0, 0, 15, pageHeight);
// bottom
canvas.Rectangle(0, 0, pageWidth, 15);
canvas.Fill();
canvas.RestoreState();
I attempted to use a TransparentColor but canvas.SetFillColor won't accept a TransparentColor, are there any other options?
When we speak about low-level content stream instructions, color itself and transparency levels are specified separately in PDF syntax. The TransparentColor class that you speak about was designed to simplify lives of users who are less familiar with nuances of PDF syntax, but it it a higher-level class that you can use e.g. in layout module, and in your case you operate with the document on quite low level.
Long story short, to set color transparency you only need one additional line next to setting the color itself:
canvas.SetExtGState(new PdfExtGState().SetFillOpacity(0.5f));
So the code becomes:
var page = pdf.GetPage(1);
PdfCanvas canvas = new PdfCanvas(page);
canvas.SaveState();
canvas.SetFillColor(iText.Kernel.Colors.ColorConstants.YELLOW);
canvas.SetExtGState(new PdfExtGState().SetFillOpacity(0.5f));
var pageHeight = page.GetPageSize().GetHeight();
var pageWidth = page.GetPageSize().GetWidth();
// left side
canvas.Rectangle(0, 0, 15, pageHeight);
// bottom
canvas.Rectangle(0, 0, pageWidth, 15);
canvas.Fill();
canvas.RestoreState();

How to dynamically and automatically set FontSize based on view.Width?

"TRYING OUT AUTO SIZING"
Font size too large, so much so part of the string is cut off (when text is too large, a portion is cut off and replaced with ellipsis).
"test text"
Font size too small. The fontSize is a third of its respective view (estimating).
How to set the fontSize of an Element to the size of the Element (and or containing Views) width? Use the examples above as reference - "test text" would take up much more of the view and "TRYING OUT AUTO SIZING" would take up far less.
NOTE: Black boxes denote other elements.
The desired goal is an element (label and or containing view) that when text is changed at run-time, the fontSize is set according to the text assigned and width available to it (this remains constant after build). So that all text is viewable in the string and it uses the width available to it.
Purpose is to support multiple platforms on a variety of devices with widely different scaling.
Whats been tried? NamedSizes based on the idiom (phone/tablet/etc) and manipulating these with multiplication and division based on OS (platform, i.e., IOS, Android, etC). This can't be best practice and there must be a way to go about accomplish this.
Following Xamarin.Forms guide for "fitting text to available size" or "empirically fitting text" yields results that are not as expected.. "CH5: Dealing with sizes"
Please advise on best practice and/or next steps.
Struct
struct FontCalc
{
public FontCalc(Label label, double fontSize, double containerWidth)
: this()
{
// Save the font size.
FontSize = fontSize;
// Recalculate the Label height.
label.FontSize = fontSize;
SizeRequest sizeRequest =
label.Measure(containerWidth, Double.PositiveInfinity);
// Save that height.
TextHeight = sizeRequest.Request.Height;
}
public double FontSize { private set; get; }
public double TextHeight { private set; get; }
}
Implementation
Label label;
public EmpiricalFontSizePage()
{
label = new Label();
Padding = new Thickness(0, Device.RuntimePlatform == Device.iOS ? 30 : 0, 0, 0);
ContentView contentView = new ContentView
{
Content = label
};
contentView.SizeChanged += OnContentViewSizeChanged;
Content = contentView;
}
void OnContentViewSizeChanged(object sender, EventArgs args)
{
// Get View whose size is changing.
View view = (View)sender;
if (view.Width <= 0 || view.Height <= 0)
return;
label.Text =
"This is text displayed. Does it work?";
// Calculate the height of the rendered text.
FontCalc lowerFontCalc = new FontCalc(label, 10, view.Width);
FontCalc upperFontCalc = new FontCalc(label, 100, view.Width);
while (upperFontCalc.FontSize - lowerFontCalc.FontSize > 1)
{
// Get the average font size of the upper and lower bounds.
double fontSize = (lowerFontCalc.FontSize + upperFontCalc.FontSize) / 2;
// Check the new text height against the container height.
FontCalc newFontCalc = new FontCalc(label, fontSize, view.Width);
if (newFontCalc.TextHeight > view.Height)
{
upperFontCalc = newFontCalc;
}
else
{
lowerFontCalc = newFontCalc;
}
}
// Set the final font size and the text with the embedded value.
label.FontSize = lowerFontCalc.FontSize;
label.Text = label.Text.Replace("??", label.FontSize.ToString("F0"));
}
(implementation code from XF docs linked above)
One solution is to use the NuGet package: Forms9Patch
Using its Lines and AutoFit properties we can achieve our desired result. In our case, we want one line and we want our FontSize set to what is required to make our text fit within one line. Lines = 1, AutoFit = Forms9Patch.AutoFit.Width, and FontSize set to a large value (does not matter if it is well over our desired max as setting AutoFit to Width shrinks font till to what is required to make text fit within our specified number of lines) results in a Label that automatically adjusts its FontSize to the maximum space available to it given the text length.

Sub 10 height request layouts/views not allowed

I'm trying to add line dividers between views. However, there is a forced margin on every element I try (Image, BoxView, Frame, Label). I set the margin to be 0, the HeightRequest is always 3, but as you can see the view bounds expand past the actual view. Is there a specific view I'm supposed to be using? I just want the gray line and nothing more.
var line2 = new Frame
{
WidthRequest = (App.ScreenDpWidth / 2),
MinimumHeightRequest = 3,
HeightRequest = 3,
BackgroundColor = Color.FromHex("#229EBB"),
Margin = new Thickness(0, 0)
};
I assume you are layouting your elements either in a Grid or a StackLayout.
By default, the StackLayout.Spacing, Grid.RowSpacing and Grid.ColumnSpacing properties are set to 6d.
Without more information, I think that's what you're seeing in your code. Change those values to 0d.
Also, if you only want a gray line, you can use a BoxView which will draw a gray box, and set it's Height to 1d.

iTextSharp: align image and text

I am using iTextSharp to export reports in PDF format. Reports' headers should have following format:
The problem is to align report header by center of the page, while there is an image on the left of the page. When I use a table, report header is aligned by center of its cell, not by center of the page. Is there a better approach?
EDIT:
Code for adding header is following:
var doc = new Document(pageSize, margins.Width, margins.Width, margins.Height, margins.Height);
using (PdfWriter.GetInstance(doc, destination))
{
doc.Open();
var headerTable = new PdfPTable(1){ WidthPercentage = 100 };
RenderHeader(headerTable); // adds several lines to headerTable
doc.Add(headerTable);
}
Do you really need a PdfPTable?
Why not add a Paragraph with the data that is right-aligned, followed by a couple of Paragraphs that are centered.
Then add the image at an absolute position, for instance:
Image img = Image.GetInstance(path_to_image);
img.SetAbsolutePosition(36, PdfWriter.getVerticalPosition(true));
document.Add(Image);
Another option is to add the Image in a table or a cell event instead of adding it straight to the table. Suppose that your PdfPTable consists of a single row and a single column, then you could define a cell event like this:
public class ImageCell : IPdfPCellEvent {
public void CellLayout(
PdfPCell cell, Rectangle position, PdfContentByte[] canvases
) {
float x1 = position.Left + 2;
float y1 = position.Top - 2;
float y2 = position.Bottom + 2;
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
Image img = Image.GetInstance(path_to_image);
img.scaleToFit(10000, y1 - y2);
img.SetAbsolutePosition(x1, y2);
canvas.AddImage(img);
}
}
Suppose that cell is the PdfPCell with the date and the title, then you can do this:
cell.CellEvent = new ImageCell();
Note that in this case, the image will be scaled to the height of the cell.
Forgive me if the code doesn't compile right away, I'm writing this by heart (based on experience), I didn't test the actual code.

Rotate paragraphs or cells some arbitrary number of degrees -- Itext

I have a web site where the users upload photos and create photobooks. Also, they can add text at absolute positions, rotations, and alignments. The text can have new lines.
I've been using the Itext Library to automatize the creation of the Photobooks High Quality Pdfs that are printed latter on.
Adding the user uploaded images to the PDFs was really simple, the problem comes when I try to add the text.
In theory what I would need to do, is to define a paragraph of some defined width and height, set the users text, font, font style, alignment (center, left, right, justify), and finally set the rotation.
For what i've read about Itext, i could create a paragraph set the user properties, and use a ColumnText Object to set the absolute position, width and height. However it's not possibly to set the rotation of anything bigger than single line.
I can't use table cells either, because the rotation method only allow degrees that are multiples of 90.
Is there a way to add a paragraph with some rotation (say 20 degrees) without having to add the text line by line using the ColumnText.showTextAligned() method and all math that involves?
---- Edit: 08-Ago-2013 ----
If it helps anyone, this is the code I used to solve this problem (thanks to Bruno):
//Create the template that will contain the text
PdfContentByte canvas = pdfWriter.getDirectContent();
PdfTemplate textTemplate = canvas.createTemplate(imgWidth, imgHeight); //The width and height of the text to be inserted
ColumnText columnText = new ColumnText(textTemplate);
columnText.setSimpleColumn(0, 0, imgWidth, imgHeight);
columnText.addElement(paragraph);
columnText.go();
//Create de image wraper for the template
Image textImg = Image.getInstance(textTemplate);
//Asign the dimentions of the image, in this case, the text
textImg.setInterpolation(true);
textImg.scaleAbsolute(imgWidth, imgHeight);
textImg.setRotationDegrees((float) -textComp.getRotation()); //Arbitrary number of degress
textImg.setAbsolutePosition(imgXPos, imgYPos);
//Add the text to the pdf
pdfDocument.add(textImg);
Create a PdfTemplate object; just a rectangle.
Draw your ColumnText on this PdfTemplate; don't worry about the rotation, just fill the rectangle with whatever content you want to add to the column.
Wrap the PdfTemplate inside an Image object; this is just for convenience, to avoid the math. This doesn't mean your text will be rasterized.
Now apply a rotation and an absolute position to the Image and add it to your document.
Your problem is now solved ;-)
PS: I'm the author of the iText in Action books.
thanks to both our friends (Bruno & BernalCarlos)
my final code for users that use "RTL" in their projects is here :
// step 1
Document document = new Document();
document.setPageSize(PageSize.A4);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(destination_file));
CreateBorder event = new CreateBorder();
writer.setPageEvent(event);
// step 3
document.open();
// step 4
int imgWidth=400;
int imgHeight=50;
//Create the template that will contain the text
PdfContentByte canvas = writer.getDirectContent();
PdfTemplate textTemplate = canvas.createTemplate(imgWidth, imgHeight);
//The width and height of the text to be inserted
ColumnText columnText = new ColumnText(textTemplate);
columnText.setSimpleColumn(0, 0, imgWidth, imgHeight);
columnText.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
columnText.addElement(new Paragraph("محاسبه بار غیر متعادل", font_IranSemiBold));
columnText.go();
//Create de image wraper for the template
Image textImg = Image.getInstance(textTemplate);
//Asign the dimentions of the image, in this case, the text
textImg.setInterpolation(true);
textImg.scaleAbsolute(imgWidth, imgHeight);
textImg.setRotationDegrees(90); //Arbitrary number of degress
textImg.setAbsolutePosition(50, 200);
//Add the text to the pdf
document.add(textImg);
// step 5
document.close();

Resources