Create multiline text in PDF document Xamarin Forms Syncfusion - c#

I have struggled a bit with the official Syncfusion Xamarin Forms PDF Documentation. I have managed to create a table, bind it to values and append it to a PdfDocument.
However odd enough, I cannot seem to draw text strings on separate lines. Each text that I try to draw using graphics.DrawString() or TextElement gets drawn on top of each other in the first line of the document.
All the string are AppResources, concatenating each other to form a report from resx files and class objects that represent my data. Many of them need to be rendered as separate phrases and paragraphs.
Is there a working sample on how to append text line after line and in between them to have tables?
Thank you!

Drawing text one after another is possible by using Essential PDF. We can draw text by one after another by using PdfTextElement with PdfLayoutResult or specifying its position in PdfGraphics.DrawString method. Please refer the below code snippet and sample for more details.
//Creates a new PDF document.
PdfDocument doc = new PdfDocument();
//Adds a page.
PdfPage page = doc.Pages.Add();
//create a new PDF string format
PdfStringFormat drawFormat = new PdfStringFormat();
drawFormat.WordWrap = PdfWordWrapType.Word;
drawFormat.Alignment = PdfTextAlignment.Justify;
drawFormat.LineAlignment = PdfVerticalAlignment.Top;
//Set the font.
PdfFont font = new PdfStandardFont(PdfFontFamily.Helvetica, 10f);
//Create a brush.
PdfBrush brush = PdfBrushes.Red;
//bounds
RectangleF bounds = new RectangleF(new PointF(10, 10), new SizeF(page.Graphics.ClientSize.Width - 30, page.Graphics.ClientSize.Height - 20));
//Create a new text elememt
PdfTextElement element = new PdfTextElement(text, font, brush);
//Set the string format
element.StringFormat = drawFormat;
//Draw the text element
PdfLayoutResult result = element.Draw(page, bounds);
// Draw the string one after another.
result = element.Draw(result.Page, new RectangleF(result.Bounds.X, result.Bounds.Bottom + 10, result.Bounds.Width, result.Bounds.Height));
// Creates a PdfLightTable.
PdfLightTable pdfLightTable = new PdfLightTable();
//Add colums to light table
pdfLightTable.Columns.Add(new PdfColumn("Name"));
pdfLightTable.Columns.Add(new PdfColumn("Age"));
pdfLightTable.Columns.Add(new PdfColumn("Sex"));
//Add row
pdfLightTable.Rows.Add(new string[] { "abc", "21", "Male" });
//Includes the style to display the header of the light table.
pdfLightTable.Style.ShowHeader = true;
//Draws PdfLightTable and returns the rendered bounds.
result = pdfLightTable.Draw(page, new PointF(result.Bounds.Left,result.Bounds.Bottom+20));
//draw string with returned bounds from table
result = element.Draw(result.Page, result.Bounds.X, result.Bounds.Bottom + 10);
//draw string with returned bounds from table
element.Draw(result.Page, result.Bounds.X, result.Bounds.Bottom + 10);
MemoryStream stream = new MemoryStream();
//Saves the document.
doc.Save(stream);
doc.Close(true);
Sample link: http://www.syncfusion.com/downloads/support/directtrac/171058/ze/App2938608856
Please let us know if you need any further assistance.

Related

iText 7 C# creating a pdf from a template and adding text to it

I have a one page pdf template and need to create a new document with several pages. Each page needs to be as the first page of the template. Then i need to add text to each page. The pages are copied but the text is not added.
This is my code:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(stream));
PdfDocument cover = new PdfDocument(new PdfReader(templatePath));
//First copy the pages
var totalPages=5;
var coverPage = cover.GetPage(1);
for (int i = 0; i < totalPages; i++)
{
//If i do it to a blank page the text is visible
//pdfDoc.AddNewPage();
//I have tried both methods:
pdfDoc.AddPage(coverPage.CopyTo(pdfDoc));
//cover.CopyPagesTo(1, 1, pdfDoc);
}
//Now i try to add text
Document doc = new Document(pdfDoc);
var font = PdfFontFactory.CreateFont(fontPath);
for (int i = 1; i <= totalPages; i++)
{
//Edited
Rectangle pagesize = pdfDoc.GetPage(i).GetPageSize();
doc.ShowTextAligned(new Paragraph("HEADER").SetFont(font).SetFontSize(22), pagesize.GetLeft(), pagesize.GetBottom(), i, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
//doc.ShowTextAligned(new Paragraph("HEADER").SetFont(font), 100, 700, i, TextAlignment.CENTER, VerticalAlignment.TOP, 0);
}
doc.Close();
cover.Close();
pdfDoc.Close();
I have tried this options:
Canvas instead of document with no result (see code below)
If i use the AddNewPage() and not the cover page, then the text is added to the blank page (both document and canvas methods).
If i open and write directly to the template document the text is visible but the size is very small and position of the text is different compared to 2)
This is the canvas code inside the for instruction:
var canvas = new PdfCanvas(pdfDoc.GetPage(i));
canvas.BeginText()
.SetFontAndSize(font, 22) //Edited
.MoveText(100, 700)
.ShowText("HEADER")
.EndText();
//UPDATED
Following the solution contributed by #mkl, i have changed the way i add the pages:
var coverPage = cover.GetPage(1);
Rectangle coverSize = coverPage.GetPageSize();
for (int i = 0; i < totalPaginas; i++)
{
//Taken from this example: https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java/chapter-6-reusing-existing-pdf-documents
PdfPage page = pdfDoc.AddNewPage(PageSize.A4);
PdfCanvas canvas = new PdfCanvas(page);
AffineTransform transformationMatrix = AffineTransform.GetScaleInstance(
page.GetPageSize().GetWidth() / coverSize.GetWidth(),
page.GetPageSize().GetHeight() / coverSize.GetHeight());
canvas.ConcatMatrix(transformationMatrix);
var pageCopy = coverPage.CopyAsFormXObject(pdfDoc);
canvas.AddXObjectAt(pageCopy, 0, 0);
//pdfDoc.AddNewPage();
//pdfDoc.AddPage(coverPage.CopyTo(pdfDoc));
//cover.CopyPagesTo(1, 1, pdfDoc);
}
Now i can see the text added, but the font size is much smaller than if instead of copying i do "pdfDoc.AddNewPage()", why is it? i would like it to be the correct font size.
Your code works in my tests. Maybe (100,700) is outside of the visual page area, which would typically be the case if your template page does not have its lower left corner at (0,0).
This should put the text in the lower left corner:
Rectangle pagesize = pdfDoc.GetPage(i).GetPageSize();
doc.ShowTextAligned(new Paragraph("HEADER"), pagesize.GetLeft(),
pagesize.GetBottom(), i, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
If that works, you can work from the pagesize rectangle to calculate the appropriate position for the text.
The solution to why the text dont appear is updated in my question at the bottom.
The reason why the coordinates dont match and the size is so small is the source template pdf that was exported with a very high px/inch for high level printing. Reducing it to 72ppp was the fix.

How to correct obsolete code for creating Canvas

This code using iText7 runs. It copies a PDF file and, on the copy, prints a rectangle on top of page 1 with a red border around a text id or message:
public static void InsertIdPdf(string sourceFilename, string targetFilename, string idText)
{
if (idText.Length > 0)
{
PdfDocument sourcePdf = new PdfDocument(new PdfReader(sourceFilename));
PdfDocument targetPdf = new PdfDocument(new PdfWriter(targetFilename));
sourcePdf.CopyPagesTo(1, sourcePdf.GetNumberOfPages(), targetPdf);
Document document = new Document(targetPdf, new PageSize(PageSize.A4));
PdfPage firstPage = targetPdf.GetFirstPage();
iText.Kernel.Geom.Rectangle pageSize = firstPage.GetCropBox().MoveUp(4);
Canvas canvas = new Canvas(new PdfCanvas(firstPage, true), targetPdf, pageSize);
PdfFont idFont = PdfFontFactory.CreateFont(StandardFonts.HELVETICA);
Style idStyle = new Style()
.SetFont(idFont)
.SetFontSize(8)
.SetPaddingRight(3)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.RIGHT)
.SetBackgroundColor(ColorConstants.WHITE);
Paragraph paragraph = new Paragraph(idText)
.SetBorder(new SolidBorder(ColorConstants.RED, (float)0.7))
.AddStyle(idStyle);
IRenderer renderer = paragraph.CreateRendererSubTree();
renderer.SetParent(document.GetRenderer()).Layout(new LayoutContext(new LayoutArea(1, pageSize)));
canvas.Add(paragraph);
document.Close();
sourcePdf.Close();
targetPdf.Close();
}
}
However, iText7 claims this line to be obsolete because the last argument, the Rectangle, may be removed in a future version:
Canvas canvas = new Canvas(new PdfCanvas(firstPage, true), targetPdf, pageSize);
I've tried to adjust the line, removing pageSize to satisfy iText7, but then the rectangle will print below the normal text on the page and flipped (mirrored) vertically.
So, how to adjust the line to not be obsolete and still print the rectangle at the top?
Also, notice the .MoveUp(4) for pageSize. The value 4 is an empiric value that moves the rectangle to the absolute top of the page. If omitted, the rectangle will be positioned with a small gab to the top of the page. This gab is independent of the font size.
So, why 4? I dislike magic numbers and would prefer a calculated value. What expression could be used to calculate this value?
Reference is the two questions here at SO:
Adding page number text to pdf copy gets flipped/mirrored with iText7
iText7: How to get the real width of a Paragraph
I wasn't able to reproduce this but I am now. I think it was something about the printable area of the document I was trying.
That's aside, the adjustment of the obsolete part is pretty simple. Just change
Canvas canvas = new Canvas(new PdfCanvas(firstPage, true), targetPdf, pageSize);
to
Canvas canvas = new Canvas(new PdfCanvas(firstPage, true), pageSize);
The obsoletion is the pdfDocument parameter.
About the magic number 4. I think it is about the DPI, I really don't know what it is. But the most relevant resource I could find is already in the community: Points -> pixels iText (im)precision

How to scale text within a fixed rectangle with itext7?

I'm trying to make a pdf document with itext7 in c# which should have fixed rectangles containing varying text that should scale within the boundaries of the (invisible) rectangles.
I have tried to find if there's automatic scaling, but so far only found auto-scaling for formfields. Since the pdf will be used for plotting text, formfields are of no use.
Code below is a snippet placing a 'box' with fixed dimensions, where all the text should be shown scaled (on one line)
float fontSize = 22f;
Text lineTxt = new Text("A VERY LONG TEXT SHOULD BE SCALED").SetFont(lineFont).SetFontSize(fontSize);
iText.Kernel.Geom.Rectangle lineTxtRect = new iText.Kernel.Geom.Rectangle(100, posHeight - 200, (float)plotline.producttype_plotmaxwidthpts, (float)plotline.producttype_plotmaxheightpts);
Div lineDiv = new Div();
lineDiv.SetMaxHeight((float)plotline.producttype_plotmaxheightpts);
lineDiv.SetWidth((float)plotline.producttype_plotmaxwidthpts);
lineDiv.SetHeight((float)plotline.producttype_plotmaxheightpts);
lineDiv.SetVerticalAlignment(VerticalAlignment.MIDDLE);
lineDiv.SetBorder(new DashedBorder(1));
Paragraph linePara = new Paragraph().Add(lineTxt).
SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER).
SetBorder(new DottedBorder(1)).
SetMultipliedLeading(0.7f).
SetMaxHeight((float)plotline.producttype_plotmaxheightpts).
SetHeight((float)plotline.producttype_plotmaxheightpts);
lineDiv.Add(linePara);
new Canvas(PageCanvas, pdf, lineTxtRect).Add(lineDiv).SetBorder(new SolidBorder(1f));
Layout module of iText 7 allows you to simulate rendering of an element (by creating the renderer tree from the element and then using Layout method) and check whether it fits the given area (by checking LayoutResult object). Thus what you can do is check whether the text fits into your fixed rectangle with the given font size. Then you can just do a binary search on the font size.
Here is a sample code:
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(outFileName));
Text lineTxt = new Text("A VERY LONG TEXT SHOULD BE SCALED");
iText.Kernel.Geom.Rectangle lineTxtRect =
new iText.Kernel.Geom.Rectangle(100,200,100,100);
Div lineDiv = new Div();
lineDiv.SetVerticalAlignment(VerticalAlignment.MIDDLE);
lineDiv.SetBorder(new DashedBorder(1));
Paragraph linePara =
new Paragraph()
.Add(lineTxt)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER)
.SetBorder(new DottedBorder(1))
.SetMultipliedLeading(0.7f);
lineDiv.Add(linePara);
// 1 is the font size that is definitely small enough to draw all the text
float fontSizeL = 1;
// 20 is the maximum value of the font size you want to use
float fontSizeR = 20;
Canvas canvas =
new Canvas(
new PdfCanvas(pdfDocument.AddNewPage()),
pdfDocument,
lineTxtRect);
// Binary search on the font size
while (Math.Abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
lineDiv.SetFontSize(curFontSize);
// It is important to set parent for the current element renderer
// to a root renderer.
IRenderer renderer =
lineDiv.CreateRendererSubTree()
.SetParent(canvas.GetRenderer());
LayoutContext context =
new LayoutContext(
new LayoutArea(1, lineTxtRect));
if (renderer.Layout(context).GetStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
}
// Use the biggest font size that is still small enough to fit all the
// text.
lineDiv.SetFontSize(fontSizeL);
canvas.Add(lineDiv);
pdfDocument.Close();

C# itextsharp table over multiple pages [duplicate]

I have two parts to my java project.
I need to populate the fields of a pdf
I need to add a table below the populated section on the blank area of the page (and this table needs to be able to rollover to the next page).
I am able to do these things separately (populate the pdf and create a table). But I cannot effectively merge them. I have tried doing a doc.add(table) which will result in the table being on the next page of the pdf, which I don't want.
I essentially just need to be able to specify where the table starts on the page (so it wouldn't overlap the existing content) and then stamp the table onto the existing pdf.
My other option if this doesn't work is trying to add fields to the original pdf that will be filled by the table contents (so it will instead be a field-based table).
Any suggestions?
EDIT:
I'm new to iText and have not used columntext before, but I'm trying to test it out in the following code but the table is not being displayed. I looked at other columntext examples and I have not seen exactly where the columntext is added back into the pdf.
//CREATE FILLED FORM PDF
PdfReader reader = new PdfReader(sourcePath);
PdfStamper pdfStamper = new PdfStamper(reader, new FileOutputStream(destPath));
pdfStamper.setFormFlattening(true);
AcroFields form = pdfStamper.getAcroFields();
form.setField("ID", "99999");
form.setField("ADDR1", "425 Test Street");
form.setField("ADDR2", "Test, WA 91334");
form.setField("PHNBR", "(999)999-9999");
form.setField("NAME", "John Smith");
//CREATE TABLE
PdfPTable table = new PdfPTable(3);
Font bfBold12 = new Font(FontFamily.HELVETICA, 12, Font.BOLD, new BaseColor(0, 0, 0));
insertCell(table, "Table", Element.ALIGN_CENTER, 1, bfBold12);
table.completeRow();
ColumnText column = new ColumnText(pdfStamper.getOverContent(1));
column.addElement(table);
pdfStamper.close();
reader.close();
Please take a look at the AddExtraTable example. It's a simplification of the AddExtraPage example written in answer to the question How to continue field output on a second page?
That question is almost an exact duplicate of your question, with as only difference the fact that your requirement is easier to achieve.
I simplified the code like this:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
Rectangle pagesize = reader.getPageSize(1);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
form.setField("Name", "Jennifer");
form.setField("Company", "iText's next customer");
form.setField("Country", "No Man's Land");
PdfPTable table = new PdfPTable(2);
table.addCell("#");
table.addCell("description");
table.setHeaderRows(1);
table.setWidths(new int[]{ 1, 15 });
for (int i = 1; i <= 150; i++) {
table.addCell(String.valueOf(i));
table.addCell("test " + i);
}
ColumnText column = new ColumnText(stamper.getOverContent(1));
Rectangle rectPage1 = new Rectangle(36, 36, 559, 540);
column.setSimpleColumn(rectPage1);
column.addElement(table);
int pagecount = 1;
Rectangle rectPage2 = new Rectangle(36, 36, 559, 806);
int status = column.go();
while (ColumnText.hasMoreText(status)) {
status = triggerNewPage(stamper, pagesize, column, rectPage2, ++pagecount);
}
stamper.setFormFlattening(true);
stamper.close();
reader.close();
}
public int triggerNewPage(PdfStamper stamper, Rectangle pagesize, ColumnText column, Rectangle rect, int pagecount) throws DocumentException {
stamper.insertPage(pagecount, pagesize);
PdfContentByte canvas = stamper.getOverContent(pagecount);
column.setCanvas(canvas);
column.setSimpleColumn(rect);
return column.go();
}
As you can see, the main differences are:
We create a rectPage1 for the first page and a rectPage2 for page 2 and all pages that follow. That's because we don't need a full page on the first page.
We don't need to load a PdfImportedPage, instead we're just adding blank pages of the same size as the first page.
Possible improvements: I hardcoded the Rectangle instances. It goes without saying that rect1Page depends on the location of your original form. I also hardcoded rect2Page. If I had more time, I would calculate rect2Page based on the pagesize value.
See the following questions and answers of the official FAQ:
How to add a table on a form (and maybe insert a new page)?
How to continue field output on a second page?

How to place images dynamically using Aspose.PDF in C#?

I have a C# script where I am using the Aspose.PDF library.
I am trying to place multiple images with a bit of a spacing vertically.
Here is what I am doing.
// Create pdf document
Aspose.Pdf.Document document = new Aspose.Pdf.Document();
Aspose.Pdf.Page page = document.Pages.Add();
Aspose.Pdf.Text.TextFragment text = new Aspose.Pdf.Text.TextFragment("Einstein Picture");
page.Paragraphs.Add(text);
Aspose.Pdf.Rectangle rectangle = new Aspose.Pdf.Rectangle(100, 600, 300, 800);
Aspose.Pdf.Rectangle rectangle1 = new Aspose.Pdf.Rectangle(100, 400, 300, 800);
page.AddImage("C:/Users/Alan/Desktop/image.gif", rectangle);
page.AddImage("C:/Users/Alan/Desktop/s.jpeg", rectangle1);
document.Save("C:/Users/Alan/Desktop/Testpdf.pdf", Aspose.Pdf.SaveFormat.Pdf);
How can I perfectly align pictures vertically with a bit of gap no matter how many pictures there are?
Currently the picture looks like this.
I request you to use below code snippet on your end and then share your kind feedback with us. This will enable you to place multiple images with a bit vertical spacing.
// Instantiate Document object
var pdf = new Aspose.Pdf.Document();
//Add a page to the document
var pdfImageSection = pdf.Pages.Add();
DirectoryInfo dir = new DirectoryInfo(#"D:\Aspose Files\images\");
FileInfo[] files = dir.GetFiles("*.jpg");
//Iterate through multiple images
foreach (var file in files)
{
FileStream stream = new FileStream(file.FullName, FileMode.Open);
System.Drawing.Image img = new System.Drawing.Bitmap(stream);
var image = new Aspose.Pdf.Image { ImageStream = stream };
//Set appearance properties
image.FixHeight = 300;
image.FixWidth = 300;
//Set margins for proper spacing and alignment
image.Margin = new MarginInfo(5, 5, 5, 5);
//Add the image to paragraphs of the document
pdfImageSection.Paragraphs.Add(image);
}
//Save resultant document
pdf.Save(#"D:\Aspose Files\Image2Pdf_out.pdf");
You were simply adding an image on a PDF page whereas this code snippet adds an image to Paragraphs collection and setting the margin property of image object fixes the alignment and spacing of the images.
Please let us know if you need any further assistance. We will be glad to help. For Aspose documentation to manipulate images is here.
I work with Aspose as Developer Evangelist.

Categories