IText 7 multiline field not auto breaking lines - c#

We recently switched from ITextSharp to IText 7, and it all seemed to work fine.
But then one of our customer send us back the pdf he got, and it didn't seem that the multiline field is auto breaking the lines, like it did on the ITextSharp.
Our fix right now is to split our string every 130 characters, and put in a \n on the closest space. For anyone having the same problem, here is the code we use:
public static void SetFormatted(this PdfFormField field, string txt)
{
var rect = field.GetPdfObject().GetAsRectangle(new PdfName("Rect"));
var fontSize = field.GetFontSize();
var div = fontSize / 2.2;
int length = (int)(rect.GetWidth() / div);
field.SetValue(Format(txt, length));
}
private static string Format(string text, int length)
{
if (text.Length > length)
{
if (text.IndexOf(' ') == 0)
text = text.Substring(1);
string temp = text.Substring(0, length);
int indexOfN = temp.IndexOf("\n");
if (indexOfN > 0)
{
return temp.Substring(0, indexOfN) + Format(text.Substring(indexOfN), length);
}
int index = temp.LastIndexOf(' ');
temp = temp.Substring(0, index) + "\n";
return temp + Format(text.Substring(index), length);
}
return text;
}
But this is more of a temporary fix, and not a solution.
(I know I said 130 characters, but it is what it split with in our field. It calculates what it should be. I don't gguarantee that it would work 100% for you)
We have tried to use field.SetRichText(ourTxt) but then it just didn't show, and the field is RTF enabled.
This is what it used to look like:
But this is what it looks like with IText 7:

I've been having this same problem and it's driving me crazy. My fields would not wrap... until I set a background color:
using iText.Forms;
using iText.Forms.Fields;
using iText.IO.Font.Constants;
using iText.Kernel.Colors;
using iText.Kernel.Font;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
// Create a new PDF document
PdfDocument pdf = new PdfDocument(new PdfWriter("my_document.pdf"));
// Create a page and add it to the document
PdfPage page = pdf.AddNewPage();
// Create an AcroForm and add it to the document
PdfAcroForm form = PdfAcroForm.GetAcroForm(pdf, true);
// Create a multiline text field that should wrap but does not.
PdfTextFormField nonWrapping = PdfFormField.CreateText
(pdf,
new Rectangle(25, 727, 100, 100),
"multiline-field-without-wrapping",
String.Concat(Enumerable.Repeat("Hello World ", 10)),
PdfFontFactory.CreateFont(StandardFonts.HELVETICA),
9.0F);
nonWrapping.SetMultiline(true);
form.AddField(nonWrapping);
// Create a multiline text field that DOES wrap but only because we set a background color.
PdfTextFormField wrapping = PdfFormField.CreateText
(pdf,
new Rectangle(150, 727, 100, 100),
"multiline-field-without-wrapping",
String.Concat(Enumerable.Repeat("Hello World ", 10)),
PdfFontFactory.CreateFont(StandardFonts.HELVETICA),
9.0F);
wrapping.SetMultiline(true);
wrapping.SetBackgroundColor(new DeviceRgb(0.9F, 0.9F, 0.99F));
form.AddField(wrapping);
// Close the document
pdf.Close();
I'm using .NET 7.0.0, iText7 7.2.4 and viewing the PDFs in Preview on Mac with Montery 12.6.1.
In both cases, the field sort of wraps when you click into it but only the field with the background color remains wrapped after the field loses focus.

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.

Remove page numbers from PDFsharp page merge when merging with another document

I used PDFsharp to merge 2 PDF files. My code adds page numbering to the bottom of each page. Now I need to merge the created document with another PDF the same way. The issue I get is the page numbers on the part created from the last document are fine. The document created from the first merge has the new page number added over the top of the first set so there are now two page numbers on top of each other in the first set of pages. So in one spot I have both of these "Page 1 of 40" and Page "1 of 110" on top of each other.
Here is the code I used to merge the PDFs
using PdfSharp.Drawing;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
namespace SomeProject.Helpers
{
public static class PDFHelper
{
public static void MergePDFFiles(string[] pdfFiles, string outputFilePath)
{
PdfDocument document = new PdfDocument();
foreach (string pdfFile in pdfFiles)
{
PdfDocument inputPDFDocument = PdfReader.Open(pdfFile, PdfDocumentOpenMode.Import);
document.Version = inputPDFDocument.Version;
foreach (PdfPage page in inputPDFDocument.Pages)
{
document.AddPage(page);
}
}
// Set font for paging
XFont font = new XFont("Verdana", 9);
XBrush brush = XBrushes.Black;
// Create variable that store page count
string noPages = document.Pages.Count.ToString();
// Set for loop of document page count and set page number using DrawString function of PdfSharp
for (int i = 0; i < document.Pages.Count; ++i)
{
PdfPage page = document.Pages[i];
// Make a layout rectangle.
XRect layoutRectangle = new XRect(240 /*X*/ , page.Height - font.Height - 10 /*Y*/ , page.Width /*Width*/ , font.Height /*Height*/ );
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
gfx.DrawString("Page " + (i + 1).ToString() + " of " + noPages, font, brush, layoutRectangle, XStringFormats.Center);
}
}
document.Save(outputFilePath);
}
}
}
A clean solution: only add the page numbers to the final documents. Keep copies of intermediate files without page numbers or create the files twice as needed.
A hack: draw a white rectangle below the new page numbers to hide anything that is already in that area. The PDF will have both page numbers, but only the most recent and current page number will be visible.
Removing page numbers is a bit complicated.

Insert Element between two elements with iTextSharp

I want to add an extra Table between two tables in a pdf file.
Things to consider:
The Blue and the Red one always have a fixed height.
The Green table height between different file may change and after adding the Red element in the middle, we may need two pages.
What I know? I know how to make a table and add it to a pdf file.
My Question: Is it possible to move the Green Element and add the Red one between? How?
As you say that you know how to make a table and add it to a pdf file and that the Blue and the Red one always have a fixed height, let's assume
you have already created the table for the red part in a separate PDF,
you have determined the y coordinate at which to split the input document to insert the red part, and
you also have determined between which y coordinates in the separate PDF the table to insert is located.
Thus, the problem is reduced to
"cutting" the input document page into three stripes,
the stripe above the splitting y coordinate containing the blue box,
the stripe below the splitting y coordinate containing as much of the green box as will still fit on that page after insertion of the red box, and
the stripe at the bottom of the page containing the remainder of the green box that will overflow to a new second page,
"cutting" a stripe from the separate PDF containing the red box to insert, and
"pasting" these stripes together in the desired new order.
For this you can use a tool like this:
public class CutAndPasteTool
{
readonly Document document;
readonly PdfWriter pdfWriter;
readonly Dictionary<string, PdfTemplate> templates = new Dictionary<string, PdfTemplate>();
public CutAndPasteTool(Rectangle pageSize, Stream os)
{
document = new Document(pageSize);
pdfWriter = PdfWriter.GetInstance(document, os);
document.Open();
}
public ICutter CreateCutter(PdfReader pdfReader, int pageNumber)
{
return new SimpleCutter(pdfReader, pageNumber, pdfWriter, templates);
}
public void Paste(string name, float x, float y)
{
pdfWriter.DirectContent.AddTemplate(templates[name], x, y);
}
public void NewPage()
{
document.NewPage();
}
public void Close()
{
document.Close();
}
class SimpleCutter : ICutter
{
readonly PdfImportedPage importedPage;
readonly Dictionary<string, PdfTemplate> templates;
internal SimpleCutter(PdfReader pdfReader, int pageNumber, PdfWriter pdfWriter, Dictionary<string, PdfTemplate> templates)
{
this.importedPage = pdfWriter.GetImportedPage(pdfReader, pageNumber);
this.templates = templates;
}
public void Cut(string name, Rectangle rectangle)
{
PdfTemplate template = importedPage.CreateTemplate(rectangle.Width, rectangle.Height);
template.AddTemplate(importedPage, importedPage.BoundingBox.Left - rectangle.Left, importedPage.BoundingBox.Bottom - rectangle.Bottom);
templates.Add(name, template);
}
}
}
public interface ICutter
{
void Cut(string name, Rectangle rectangle);
}
Using this tool you can cut the stripes and paste them like this:
Rectangle pageSize = PageSize.A4; // The page size of the result file
int doc1page = 1; // The number of the page in the input PDF
float doc1split = 600; // The splitting y coordinate in the input PDF
int doc2page = 1; // The number of the page in the separate PDF
float doc2from = 700; // The top y coordinate of the table in the separate PDF
float doc2to = 600; // The bottom y coordinate of the table in the separate PDF
using (PdfReader reader1 = new PdfReader(...)) // The input PDF file
using (PdfReader reader2 = new PdfReader(...)) // The separate PDF file
using (Stream os = new FileStream(..., FileMode.Create)) // The stream to write the result to
{
Rectangle doc1box = reader1.GetPageSize(doc1page);
Rectangle doc1upper = new Rectangle(doc1box);
doc1upper.Bottom = doc1split;
Rectangle doc1lower = new Rectangle(doc1box);
doc1lower.Top = doc1split;
Rectangle doc2box = reader2.GetPageSize(doc2page);
Rectangle doc2inset = new Rectangle(doc2box);
doc2inset.Top = doc2from;
doc2inset.Bottom = doc2to;
float doc1lowerRemainHeight = pageSize.Height - doc1upper.Height - doc2inset.Height;
float doc1lowerOverflowHeight = doc1lower.Height - doc1lowerRemainHeight;
Rectangle doc1lowerRemain = new Rectangle(doc1lower);
doc1lowerRemain.Bottom = doc1lowerRemain.Top - doc1lowerRemainHeight;
Rectangle doc1lowerOverflow = new Rectangle(doc1lower);
doc1lowerOverflow.Top = doc1lowerRemain.Bottom;
CutAndPasteTool tool = new CutAndPasteTool(pageSize, os);
ICutter cutterDoc1 = tool.CreateCutter(reader1, doc1page);
cutterDoc1.Cut("Top1", doc1upper);
cutterDoc1.Cut("Bottom1Remain", doc1lowerRemain);
cutterDoc1.Cut("Bottom1Overflow", doc1lowerOverflow);
ICutter cutterDoc2 = tool.CreateCutter(reader2, doc2page);
cutterDoc2.Cut("Inset", doc2inset);
float y = pageSize.Top;
tool.Paste("Top1", 0, y-= doc1upper.Height);
tool.Paste("Inset", 0, y -= doc2inset.Height);
tool.Paste("Bottom1Remain", 0, y -= doc1lowerRemain.Height);
tool.NewPage();
y = pageSize.Top;
tool.Paste("Bottom1Overflow", 0, y -= doc1lowerOverflow.Height);
tool.Close();
}
Beware: This tool only works for content in the page content streams or content streams referenced from there, in particular not for content in annotations or XFA forms.
In case of annotations one can extend the solution to associate each annotation with one of those stripes (which might be difficult for border cases) and copy stripes and associated annotations together.
In case of XFA form tables this approach won't work unless you flatten the form first.
Furthermore, there now is some hidden text which nonetheless can be copied and pasted elsewhere. If that is not acceptable, one can replace the SimpleCutter by a more advanced ICutter implementation which removes (and not only hides) content outside of stripes, e.g. using the iText PdfCleanUpProcessor.

Create multiline text in PDF document Xamarin Forms Syncfusion

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.

Where does bottom and right border come from in abcPdf

Why I am getting border on bottom and right when rendering html using abcAdf.
PS I got professional license using (ABCpdf9-64.dll)
My process is to create page:
I use master / child layout template in mvc (works)
I point to this html using abcPdf to convert into pdf
Html rendered
When my html gets rendered (plain) format : each originating from its respective location.
<h1>Layout</h1>
<p>
content
</p>
C# code to render pdf
My code is as follows:
using (var pdf = new Doc())
{
pdf.HtmlOptions.Timeout = 600000;
pdf.HtmlOptions.AddTags = true;
pdf.Page = pdf.AddPage();
var id = pdf.AddImageUrl(url, true, 1024, true);
if (allowPaging)
{
while (true)
{
if (!pdf.Chainable(id))
{
break;
}
pdf.Page = pdf.AddPage();
id = pdf.AddImageToChain(id);
}
for (int i = 1; i <= pdf.PageCount; i++)
{
pdf.PageNumber = i;
pdf.Flatten();
}
////reset back to page 1 so the pdf starts displaying there
if (pdf.PageCount > 0)
{
pdf.PageNumber = 1;
}
}
return store(pdf);
}
Output
My text/html gets rendered ok but I get borders that I have not asked for.
Rendered output:
Please note the hairline in bottom of the image.
After hours of googling about setting margin in abcpdf, I found nothing and took it as a challenge to find it myself.
I tried experimenting with everything I found relevant in the abcpdf documentation and finally made a chart myself
for setting the margins. I have successfully implemented this in many situations. Hope this helps others.
Here is a code snippet that shows how to set margins-
string html; // my html content that should be shown in the pdf page
Doc pdf = new Doc();
// adjust the default rotation and save
double w = pdf.MediaBox.Width;
double h = pdf.MediaBox.Height;
double l = pdf.MediaBox.Left;
double b = pdf.MediaBox.Bottom;
// explicitly giving page size
pdf.MediaBox.String = "A4";
pdf.Transform.Rotate(90, l, b);
pdf.Transform.Translate(w, 0);
pdf.Rect.Width = h;
pdf.Rect.Height = w;
int theID1 = pdf.GetInfoInt(pdf.Root, "Pages");
pdf.SetInfo(theID1, "/Rotate", "90");
int theID;
pdf.Rect.String = "17 55 823 423";
theID = pdf.AddImageHtml(html.ToString()); //Writes the HTML image to PDF
Here is a picture that describes margin layout for some junk values. Here
20 suggests that your content starts 20 pixels away from left of your pdf page
770 suggests that your content ends 770 pixels away from left of your pdf page
75 suggests that your content starts 55 pixels above the bottom of your pdf page
600 suggests that your content ends 600 pixels above the bottom of your pdf page
In your case you have to add
pdf.Rect.String = "20 75 770 600"; // giving junk values
right before
var id = pdf.AddImageUrl(url, true, 1024, true);
NOTE: In this example I explicitly set landscape mode instead of portrait mode. But orientation doesn't matter for setting
margins.

Categories