How to switch different iText document renderers in the same page - c#

The case is that at the beginning of Page, Elements should be drawn in one column, and after that, elements in the same page should be drawn in two columns.
So far, according to the iText example "c02e10_jekyllhydev6", I just can switch different renderers between pages, which means first applying DocumentRenderer, then add AreaBreak of Next Page, and applying ColumnDocumentRenderer in the new page.
The code:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc, PageSize.A4);
doc.SetMargins(36, 36, 36, 36);
Paragraph p = new Paragraph();
p.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p.Add(new Text(i + " "));
}
doc.Add(p);
**doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));**
PageSize ps = PageSize.A4;
var effectiveArea = doc.GetPageEffectiveArea(PageSize.A4);
float columnHeight = effectiveArea.GetHeight();
//Define column areas
Rectangle[] columns = new Rectangle[] {
new Rectangle(36, 36, 200, columnHeight),
new Rectangle(36 + 200 + 20, 36, effectiveArea.GetWidth()- 200 - 20, columnHeight)
};
ColumnDocumentRenderer renderer1 = new ColumnDocumentRenderer(doc, new Rectangle[] { columns[0] });
doc.SetRenderer(renderer1);
**doc.Add(new AreaBreak(AreaBreakType.LAST_PAGE));**
Paragraph p1 = new Paragraph();
p1.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p1.Add(new Text(i + " "));
}
doc.Add(p1);
ColumnDocumentRenderer renderer2 = new ColumnDocumentRenderer(doc, new Rectangle[] { columns[1] });
doc.SetRenderer(renderer2);
Paragraph p2 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p2.Add(new Text(i + " "));
}
doc.Add(p2);
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
DocumentRenderer renderer3 = new DocumentRenderer(doc);
doc.SetRenderer(renderer3);
doc.Add(new AreaBreak(AreaBreakType.LAST_PAGE));
Paragraph p3 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p3.Add(new Text(i + " "));
}
doc.Add(p3);
doc.Close();
If no AreaBreak added to the document, the contents with different renderers will be overlapped.
From Alexey's comment in this case, It seems possible that switching different renderers in the same page without content overlapping.
To handle this appropriately, you would have to update currentArea of the renderer you are going to switch to with the currentArea of the previous renderer you have just finished working with. You can do that by extending the standard provided renderers, or calling renderer.getCurrentArea() and modifying the bBox.
But I don't know how to achieve it according to above guides.

There is no need in ColumnDocumentRenderer in your case - that renderer was created for cases when you want to lay out your content in columns in scope of one page and then move on to the next page etc. In your case you are laying out the content in columns that span many pages and that is equivalent to just setting proper margins to the DocumentRenderer instead of passing the columns to ColumnDocumentRenderer.
To switch the renderers ad-hoc you indeed need to tweak their currentArea field and currentPageNumber as well. Please note that the solution below is not guaranteed to work in all complex cases and in all iText versions. This is merely a guideline on how to implement what you need and not a complete solution to all cases.
The helper renderer class we need is very simple - it allows customizing current area:
private class ExtendedDocumentRenderer : DocumentRenderer {
public ExtendedDocumentRenderer(Document document, RootLayoutArea currentArea) : base(document) {
this.currentArea = new RootLayoutArea(currentArea.GetPageNumber(), currentArea.GetBBox().Clone());
this.currentPageNumber = this.currentArea.GetPageNumber();
}
}
Now I've adapted your code to get rid of the ColumnDocumentRenderer and use plain DocumentRenderer instead. You just need to tweak document margins and recalculate current area (the space that is left on the page) properly:
Document doc = new Document(pdfDocument);
Paragraph p = new Paragraph();
p.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p.Add(new Text(i + " "));
}
doc.Add(p);
RootLayoutArea endOfFullWidthContentArea = (RootLayoutArea) doc.GetRenderer().GetCurrentArea();
ExtendedDocumentRenderer renderer1 = new ExtendedDocumentRenderer(doc,
new RootLayoutArea(endOfFullWidthContentArea.GetPageNumber(), endOfFullWidthContentArea.GetBBox().Clone().SetWidth(200)));
doc.SetRightMargin(doc.GetRightMargin() + doc.GetPageEffectiveArea(PageSize.A4).GetWidth() - 200);
doc.SetRenderer(renderer1);
Paragraph p1 = new Paragraph();
p1.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p1.Add(new Text(i + " "));
}
doc.Add(p1);
ExtendedDocumentRenderer renderer2 = new ExtendedDocumentRenderer(doc,
new RootLayoutArea(endOfFullWidthContentArea.GetPageNumber(),
endOfFullWidthContentArea.GetBBox().Clone().MoveRight(200)
.SetWidth(endOfFullWidthContentArea.GetBBox().GetWidth() - 200)));
doc.SetRightMargin(36);
doc.SetLeftMargin(200 + 36);
doc.SetRenderer(renderer2);
Paragraph p2 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p2.Add(new Text(i + " "));
}
doc.Add(p2);
// Compute which free area is lower in the document
RootLayoutArea areaColumn1 = (RootLayoutArea) renderer1.GetCurrentArea();
RootLayoutArea areaColumn2 = (RootLayoutArea) renderer2.GetCurrentArea();
RootLayoutArea downArea = areaColumn1.GetPageNumber() > areaColumn2.GetPageNumber() ? areaColumn1 :
(areaColumn1.GetPageNumber() < areaColumn2.GetPageNumber() ? areaColumn2 :
(areaColumn1.GetBBox().GetTop() < areaColumn2.GetBBox().GetTop() ? areaColumn1 : areaColumn2));
doc.SetMargins(36, 36, 36, 36);
DocumentRenderer renderer3 = new ExtendedDocumentRenderer(doc,
new RootLayoutArea(downArea.GetPageNumber(), downArea.GetBBox().Clone().SetX(36).SetWidth(doc.GetPageEffectiveArea(PageSize.A4).GetWidth())));
doc.SetRenderer(renderer3);
Paragraph p3 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p3.Add(new Text(i + " "));
}
doc.Add(p3);
doc.Close();
Result looks as follows:

Related

Creating a grid like structure based on the area of an room

so in unity im creating randomly generating rooms. inside of these rooms i will "randomly" place objects, enemy,objects,items etc. based on nodes. nodes are transform objects that are separated from each other by 1 unit. the idea is that i can now pick a node and instantiate something at that node. The code works great when the room is 10x10(x,z since its a 3d game) i get a nice grid pattern it also works as expected when x is any multiple of 2. however when z changes from 10 it doesn't create the grid properly. there is also a problem if the room changes locations other then 0,0.
floor is a child object of the room.
void RoomSetup()
{
bool first = true;
int collCount = -1;
GameObject placeHolder = new GameObject("temp");
float colls;
float offX, offZ;
colls = floor.transform.localScale.x - 1;
print(colls);
for (int i = 0; i < ((floor.transform.localScale.x-1)
*(floor.transform.localScale.z-1)); i++)
{
//creating a for loop that stops when its greater then the area of the rectangle, subtract one from x,z so theres a one unit of padding from the walls.
offX = floor.transform.lossyPosition.x;
offZ = floor.transform.lossyPosition.z;
//getting the offset of the room in world space this is where i believe the issues arise
collCount++;
GameObject temp = new GameObject(i + " Node");
temp.transform.SetParent(floor.transform);
temp.AddComponent<BoxCollider>();
temp.GetComponent<BoxCollider>().isTrigger = true;
if(!first)
{
temp.transform.position = new Vector3((placeHolder.transform.position.x + 1), 6, placeHolder.transform.position.z);
placeHolder = temp;
if (collCount >= colls)
{
print("new line on " + temp.name + " coll " + collCount);
collCount = 0;
placeHolder.transform.position = new Vector3((-(floor.transform.localScale.x / 2) + 1)+offX, 6, (placeHolder.transform.position.z - 1)-offZ);
}
}
if (first)
{
// print(colls);
temp.transform.position = new Vector3((-(floor.transform.localScale.x / 2) + 1) + offX, 6, floor.transform.localScale.z - offZ);
placeHolder = temp;
first = false;
}
nodes.Add(temp);
}
}
Here are some pictures to help illustrate the issue
the first image is when the room is at 0,0 and it creates a nice grid pattern
when the room is offest it creates the grid still at 0,0
i figured it out, it happend to do with with the local and world scale/pos of the floor and the room
void RoomSetup()
{
bool first = true;
int collCount = -1;
GameObject placeHolder = new GameObject("temp");
float colls;
colls = floor.transform.localScale.x - 1;
offX = floor.transform.localScale.x;
offZ = floor.transform.localScale.z;
offX = (offX / 2) - offX + 1;
offZ = (offZ / 2) - 1;
offX = offX + floor.transform.position.x;
offZ = (offZ + floor.transform.position.z);
print(colls);
for (int i = 0; i < ((floor.transform.localScale.x-1) * (floor.transform.localScale.z-1)); i++)
{
collCount++;
//print(collCount);
GameObject temp = new GameObject(i + " Node");
temp.transform.SetParent(floor.transform);
temp.AddComponent<BoxCollider>();
temp.GetComponent<BoxCollider>().isTrigger = true;
if(!first)
{
temp.transform.position = new Vector3((placeHolder.transform.position.x + 1), 6, placeHolder.transform.position.z);
placeHolder = temp;
if (collCount >= colls)
{
print("new line on " + temp.name + " coll " + collCount);
collCount = 0;
temp.transform.position = new Vector3(floor.transform.localPosition.x + offX, 6, placeHolder.transform.position.z - 1);
placeHolder = temp;
}
}
if (first)
{
// print(colls);
temp.transform.position = new Vector3(floor.transform.localPosition.x+offX, 6, floor.transform.localPosition.z+offZ);
placeHolder = temp;
first = false;
}
nodes.Add(temp);
}
}
works as intended, creates a grid of nodes on the floor of anyroom square room

iTextSharp not working in loop

I have 2 pieces of code. The first one is working, second one is inside a loop and it doesn't show the value.
Anyone have any idea? Could it be looping is too fast for memorystream to read?
I am writing everything to a memorystream and response to download the file.
If I do it one by one as below, everything works fine.
var phraseinvoice = new Phrase();
phraseinvoice.Add(new Chunk("Invoice to:", FontFactory.GetFont(FontFactory.TIMES, 12)));
Invoicetable.AddCell(phraseinvoice);
phraseinvoice = new Phrase();
phraseinvoice.Add(new Chunk("BCD Meetings & Events Asia Pacific", FontFactory.GetFont(FontFactory.TIMES_BOLD, 12)));
PdfPCell inheader = new PdfPCell(phraseinvoice);
inheader.PaddingBottom = 4;
inheader.Border = Rectangle.NO_BORDER;
inheader.FixedHeight=20f;
Invoicetable.AddCell(inheader);
If I put them inside a array and read from a for loop the PDF will not show any text.
string[] tbText = {" ","Pte.Ltd"," ", "20 Anson Road, #06-01"," ", "Twenty Anson 079912","",
"Singapore"," "," ","Tel", "1234567", "Fax","123"," "," ","Delivery to:", "BCD Meetings & Events Asia Pacific"," ",
"Pte,Ltd"," ","20 Anson Road, #06-01"," ", "Twenty Anson 079912"," ","Singapore"};
Invoicetable.AddCell(inheader);
for (int i = 0; i < 25; i++)
{
var inputstring = tbText[i];
phraseinvoice = new Phrase();
phraseinvoice.Add(new Chunk(inputstring, FontFactory.GetFont(FontFactory.TIMES_BOLD, 12)));
PdfPCell cellbox = new PdfPCell(phraseinvoice);
cellbox = new PdfPCell(phraseinvoice);
cellbox.Border = Rectangle.NO_BORDER;
cellbox.Padding= -4;
Invoicetable.AddCell(cellbox);
}
You can see the differences between first image after -4 padding and second image without padding
You are creating a Phrase with text in a 12pt font, but you are limiting the height of the cell to 10pt. That explains why nothing is shown.
Change your code like this:
for (int i = 0; i < 25; i++)
{
var inputstring = tbText[i];
phraseinvoice = new Phrase(inputstring,
FontFactory.GetFont(FontFactory.TIMES_BOLD, 12)));
PdfPCell cellbox = new PdfPCell(phraseinvoice);
cellbox.Border = Rectangle.NO_BORDER;
cellbox.FixedHeight = 20f;
Invoicetable.AddCell(cellbox);
}
20 user units should be sufficient to show text in a 12pt font.
Update:
Another option would be to reduce the font size, for instance:
for (int i = 0; i < 25; i++)
{
var inputstring = tbText[i];
phraseinvoice = new Phrase(inputstring,
FontFactory.GetFont(FontFactory.TIMES_BOLD, 8)));
PdfPCell cellbox = new PdfPCell(phraseinvoice);
cellbox.Border = Rectangle.NO_BORDER;
cellbox.FixedHeight = 15f;
Invoicetable.AddCell(cellbox);
}
But there's more: the height needed by a Phrase in a PdfPCell depends on:
The font size,
The leading,
The ascender and descender of the font.
For instance:
for (int i = 0; i < 25; i++)
{
var inputstring = tbText[i];
phraseinvoice = new Phrase(inputstring,
FontFactory.GetFont(FontFactory.TIMES_BOLD, 12)));
PdfPCell cellbox = new PdfPCell(phraseinvoice);
cellbox.Leading = 14;
cellbox.UseAscender = true;
cellbox.UseDescender = true;
cellbox.Border = Rectangle.NO_BORDER;
cellbox.FixedHeight = 18f;
Invoicetable.AddCell(cellbox);
}
Note how we reduced the Leading from the default (1.5 times the font size) to 14, and how we told the cellBox to take the ascender and descender into account.
Looks like you are trying to add a string as a pdfpcell
var inputstring = tbText[i];
Invoicetable.AddCell(inputstring);
tbText is a string array

Table in Word document with n columns to fit as per page size and allow truncated columns to break across page using Aspose.words for .Net

I'm generating a word document using Aspose.Words(Evaluation mode) for .Net in which I'm building a table as following
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
Table table = builder.StartTable();
for (int i = 0; i < 5; i++)
{
for(int j = 0; j < 20; j++)
{
builder.InsertCell();
builder.Write("Column : "+ j.toString());
}
builder.EndRow();
}
builder.EndTable();
doc.Save(ms, Aspose.Words.Saving.SaveOptions.CreateSaveOptions(SaveFormat.Doc));
FileStream file = new FileStream(#"c:\NewDoc.doc", FileMode.Create, FileAccess.Write);
ms.WriteTo(file);
file.Close();
ms.Close();
Now this code gives following word file with invisible columns, it should give 20 columns
.
Is there any way to break invisible columns to next page?
Rows can go to next page, not columns, it is the behavior of Microsoft Word. You can change the design and formatting of the document to make all columns visible. Below are few pointers.
Reduce the page margins (left and right)
Make the cells width fixed. This way, the text inside each cell will break downwards, if more characters found.
Change orientation to landscape, you will have a wider page.
Check the related articles and code sample on Aspose.Words documentation website.
Try the updated code sample below:
string dst = dataDir + "table.doc";
Aspose.Words.Document doc = new Aspose.Words.Document();
DocumentBuilder builder = new DocumentBuilder(doc);
// Set margins
doc.FirstSection.PageSetup.LeftMargin = 10;
//doc.FirstSection.PageSetup.TopMargin = 0;
doc.FirstSection.PageSetup.RightMargin = 10;
//doc.FirstSection.PageSetup.BottomMargin = 0;
// Set oriantation
doc.FirstSection.PageSetup.Orientation = Aspose.Words.Orientation.Landscape;
Aspose.Words.Tables.Table table = builder.StartTable();
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 20; j++)
{
builder.InsertCell();
// Fixed width
builder.CellFormat.Width = ConvertUtil.InchToPoint(0.5);
builder.Write("Column : " + j);
}
builder.EndRow();
}
builder.EndTable();
// Set table auto fit behavior to fixed width columns
table.AutoFit(AutoFitBehavior.FixedColumnWidths);
doc.Save(dst, Aspose.Words.Saving.SaveOptions.CreateSaveOptions(Aspose.Words.SaveFormat.Doc));
I work with Aspose as a Developer Evangelist.

ABCPDF Calculation of header size and position.

The problem is a header file, which I have to include on each page of the pdf file generated by abcpdf.
The header file contains more than one image file and several lines of text, which varies from case to case.
The problem is that I do not know how to calculate the size of the header. I need to have its size to allocate the rectangle positions to put the rest of html file on each page together with header. I am using C#.
First off you need to create your document with enough space at the top to allow a header to be added. The settings below are for a normal A4 document with a header of about 1/5 of the page. Remember the coordinates on a PDF are from the bottom right not the top left..
//Setting to create the document using ABCPdf 8
var theDoc = new Doc();
theDoc.MediaBox.String = "A4";
theDoc.HtmlOptions.PageCacheEnabled = false;
theDoc.HtmlOptions.ImageQuality = 101;
theDoc.Rect.Width = 719;
theDoc.Rect.Height = 590;
theDoc.Rect.Position(2, 70);
theDoc.HtmlOptions.Engine = EngineType.Gecko;
The code below out puts a header across each page on the document, with a header image then a colored box under the image with some custom text in.
The header image in this case is 1710 x 381 to keep the resolution of the image as high as possible to stop it looking fuzzy when printed.
private static Doc AddHeader(Doc theDoc)
{
int theCount = theDoc.PageCount;
int i = 0;
//Image header
for (i = 1; i <= theCount; i++)
{
theDoc.Rect.Width = 590;
theDoc.Rect.Height = 140;
theDoc.Rect.Position(0, 706);
theDoc.PageNumber = i;
string imagefilePath = HttpContext.Current.Server.MapPath("/images/pdf/pdf-header.png");
Bitmap myBmp = (Bitmap)Bitmap.FromFile(imagefilePath);
theDoc.AddImage(myBmp);
}
//Blue header box
for (i = 2; i <= theCount; i++)
{
theDoc.Rect.String = "20 15 590 50";
theDoc.Rect.Position(13, 672);
System.Drawing.Color c = System.Drawing.ColorTranslator.FromHtml("#468DCB");
theDoc.Color.Color = c;
theDoc.PageNumber = i;
theDoc.FillRect();
}
//Blue header text
for (i = 2; i <= theCount; i++)
{
theDoc.Rect.String = "20 15 586 50";
theDoc.Rect.Position(25, 660);
System.Drawing.Color cText = System.Drawing.ColorTranslator.FromHtml("#ffffff");
theDoc.Color.Color = cText;
string theFont = "Century Gothic";
theDoc.Font = theDoc.AddFont(theFont);
theDoc.FontSize = 14;
theDoc.PageNumber = i;
theDoc.AddText("Your Text Here");
}
return theDoc;
}

Cannot get SpacingAfter to work on image with iTextSharp

Hi I would like to add some extra space after a image in my iTextSharp generated pdf document. But for some reason whatever I try to do it will not prevent my text from wrapping
Example image
As you can see the "to read everyth..." does not indent like the rest
This is the codesnip that should do this:
var brevityBox = iTextSharp.text.Image.GetInstance("http://" + domain + "/ImageGen.ashx?Text=" + brevityScore + "&FontSize=120&&FontStyle=Bold&Font=Calibri&Align=Center&image=/media/images/PDF/BrevityBox.jpg");
brevityBox.ScaleToFit(80f, 220f);
brevityBox.Alignment = Image.TEXTWRAP;
brevityBox.SpacingAfter = 460f;
doc.Add(brevityBox);
Chunk c3 = new Chunk(brevityText, FontFactory.GetFont("Verdana", 12, Font.NORMAL)); ;
Paragraph p3 = new Paragraph();
p3.IndentationLeft = 20;
p3.IndentationRight = 20;
p3.Alignment = Element.ALIGN_LEFT;
p3.Add(c3);
doc.Add(p3);
Just to prove the point the SpacingAfter is 460 points.
IndentationRight
works fine
Any ideas?
I believe that iTextSharp is not even using the SpacingAfter property. I modified the method
iTextSharp.text.pdf.PdfDocument.Add(iTextSharp.text.Image image){}
to this:
if (imageEnd < 0 || imageEnd < currentHeight + image.ScaledHeight + diff + image.SpacingAfter)
{
imageEnd = currentHeight + image.ScaledHeight + diff + image.SpacingAfter;
}
From v5.2.1 it was line 2244 in PdfDocument.cs

Categories