Check if "Continued" or "New" page - c#

I would like to know if there is a way to check if the "New Page" happened because of exceeded table or programmatically (by using doc.NewPage();)?
If the new page caused because of exceeded table or text, I need to hide the header table and show a text instead, else if the new page caused programmatically I need to display the header table normally.
I tried to find a flag or something like this in the "OnStartPage" event that show me if the page exceeded or not, but I found nothing.
I hope that some one can help me here.
Thanks!

I would look at the IPdfPTableEventSplit interface that you can implement and assign to a PdfPTable.TableEvent property. It has two methods, SplitTable and TableLayout. The first method is called whenever a table split happens, the second is called whenever the table actually gets written to the canvas. In the first method you could set a flag and disable the header rows if a split happened and in the second method you could write your content out.
The SplitTable method is fired before the new page is added so you need to keep track of a trinary state, "no split", "draw on next page" and "draw on this page". I've packaged these up as an enum:
[Flags]
public enum SplitState {
None = 0,
DrawOnNextPage = 1,
DrawOnThisPage = 2
}
The implemented interface would look like this:
public class SplitTableWatcher : IPdfPTableEventSplit {
/// <summary>
/// The current table split state
/// </summary>
private SplitState currentSplitState = SplitState.None;
public void SplitTable(PdfPTable table) {
//Disable header rows for automatic splitting (per OP's request)
table.HeaderRows = 0;
//We now need to split on the next page, so append the flag
this.currentSplitState |= SplitState.DrawOnNextPage;
}
public void TableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) {
//If a split happened and we're on the next page
if (this.currentSplitState.HasFlag(SplitState.DrawOnThisPage)) {
//Draw something, nothing too special here
var cb = canvases[PdfPTable.TEXTCANVAS];
cb.BeginText();
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, false), 18);
//Use the table's widths and heights to find a spot, this probably could use some tweaking
cb.SetTextMatrix(widths[0][0], heights[0]);
cb.ShowText("A Split Happened!");
cb.EndText();
//Unset the draw on this page flag, it will be reset below if needed
this.currentSplitState ^= SplitState.DrawOnThisPage;
}
//If we previously had the next page flag set change it to this page
if (currentSplitState.HasFlag(SplitState.DrawOnNextPage)) {
this.currentSplitState = SplitState.DrawOnThisPage;
}
}
}
And finally the actual implementation of that class with some simple test data:
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
using (var fs = new FileStream(testFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
var t = new PdfPTable(1);
//Implement our class
t.TableEvent = new SplitTableWatcher();
//Add a single header row
t.HeaderRows = 1;
t.AddCell("Header");
//Create 100 test cells
for (var i = 1; i < 100; i++) {
t.AddCell(i.ToString());
}
doc.Add(t);
doc.Close();
}
}
}

Related

Adding header to pages using iText7

I need to add an HTML block as the header of all pages using iText7. The header contains an image logo and some text.
I have this at the moment, before closing the document object:
for (int i = 1; i <= n; i++)
{
float x = pdf.GetPage(i).GetPageSize().GetWidth() / 2; // 297.5f
float yFooter = 20;
if (headerBlock != null)
{
float yHeader = 600;// pdf.GetPage(i).GetPageSize().GetTop() - 20;
// Header
document.ShowTextAligned(headerBlock, 0, yHeader, page, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
}
// Footer
Paragraph footerBlock = new Paragraph(String.Format("Página {0} de {1}", i, n));
document.ShowTextAligned(footerBlock, x, yFooter, page, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
}
Footer works correctly but header.
Header was loaded this way:
Paragraph headerBlock = String.IsNullOrWhiteSpace(header) ? null : CreateHtmlParagraph(header);
where, CreateHtmlParagraph is defined this way:
private Paragraph CreateHtmlParagraph(string html)
{
Paragraph p = new Paragraph();
ConverterProperties properties = new ConverterProperties();
properties.SetBaseUri(HttpContext.Current.Server.MapPath("/"));
var elements = HtmlConverter.ConvertToElements(html, properties);
foreach (IElement e in elements)
p.Add((IBlockElement)e);
return p;
}
When I add the header using the document.Add method, it works well, but for the first page only. All other content follows it.
When I try to add it using ShowTextAligned method, only the image is rendered in all pages.
By the way, is there a way to get the actual height of the header paragraph? I think, once the header positioning is solved, I will have the problem that the other content blocks will be overlapped by the header.
I believe you need to use page events. This is well documented. Create a class that implements IEventHandler that will handle specific events.
Add a event handler for a specific event
pdf.AddEventHandler(PdfDocumentEvent.START_PAGE, new StartPageEventHandler());
StartPageEventHandler is a class you create, implementing IEventHandler. You'll likely need to take this approach for both header and footer.
See this link for more info

How to get the row number of the last row when you are printing an excel sheet (EPPlus)

I want to add one picture (displaying "DRAFT") by printable Excel worksheet in C# EPPlus.
I need to know if there is a way to find the last visible row of each page of a worksheet when you are printing it. I can't pretend that it will always be a fix number of row per page because it depends on the content of the cells.
Here is my current code that use a fix number of row per page (30) to insert image. This result in approximately one image per printable page except that in each new page the image is not at the same place. (Slightly off, depending on content of cells.)
public void InsertDraftImage(ExcelWorksheet worksheet, FileInfo draft_image)
{
int maxRowNumber = worksheet.Dimension.End.Row;
int rowByPage = 30;
int numberOfPage = (maxRowNumber / rowByPage) + 1;
ExcelPicture picture = null;
for(int i = 0; i < numberOfPage; i++)
{
if(draft_image != null)
{
picture = worksheet.Drawings.AddPicture(i.ToString(), draft_image);
picture.SetSize(609, 545); //original image size
picture.SetPosition(i * rowByPage, 0, 1, 0);
picture.EditAs = eEditAs.Absolute;
}
}
After trying to implement the missing code in 'ExcelHeaderFooter.cs' from the EPPlus with a workmate without success, we finally did it by following Ernie suggestion!!
There is my final code to insert a picture into each page of a printable excel file generate with EPPlus in C#.
It is done by adding the picture in the footer and setting the Boolean ScaleWithDoc to false (default = true).
public void InsertDraftImage(ExcelWorksheet worksheet, FileInfo draft_image)
{
ExcelHeaderFooterText footer = worksheet.HeaderFooter.OddFooter; //all page have same footer
footer.InsertPicture(draft_image, PictureAlignment.Centered);
}
Added this code in my method to create the ExcelWorksheet (all the other excel style, populate, settings).
XmlAttribute temp = worksheet.WorksheetXml.CreateAttribute("scaleWithDoc");
temp.Value = "0";
worksheet.WorksheetXml.GetElementsByTagName("headerFooter")[0].Attributes.Append(temp);
package.Save();

underline portion of text using iTextSharp

I have an application that uses itextsharp to fill PDF form fields.
One of these fields has some text with tags. For example:
<U>This text should be underlined</>.
I'd like that the text closed in .. has to be underlined.
How could I do that?
How could I approch it with HTMLWorker for example?
Here's the portion of code where I write my description:
for (int i = 0; i < linesDescription.Count; i++)
{
int count = linesDescription[i].Count();
int countTrim = linesDescription[i].Trim().Count();
Chunk cnk = new Chunk(linesDescription[i] + GeneralPurpose.ReturnChar, TextStyle);
if (firstOpe && i > MaxLinePerPage - 1)
LongDescWrapped_dt_extra.Add(cnk);
else
LongDescWrapped_dt.Add(cnk);
}
Ordinary text fields do not support rich text. If you want the fields to remain interactive, you will need RichText fields. These are fields that are flagged in a way that they accept an RV value. This is explained here: Set different parts of a form field to have different fonts using iTextSharp (Note that I didn't succeed in getting this to work, but you may have better luck.)
If it is OK for you to flatten the form (i.e. remove all interactivity), please take a look at the FillWithUnderline example:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setFormFlattening(true);
AcroFields form = stamper.getAcroFields();
FieldPosition pos = form.getFieldPositions("Name").get(0);
ColumnText ct = new ColumnText(stamper.getOverContent(pos.page));
ct.setSimpleColumn(pos.position);
ElementList elements = XMLWorkerHelper.parseToElementList("<div>Bruno <u>Lowagie</u></div>", null);
for (Element element : elements) {
ct.addElement(element);
}
ct.go();
stamper.close();
}
In this example, we don't fill out the field, but we get the fields position (a page number and a rectangle). We then use ColumnText to add content at this position. As we are inputting HTML, we use XML Worker to parse the HTML into iText objects that we can add to the ColumnText object.
This is a Java example, but it should be easy to port this to C# if you know how to code in C# (which I don't).
You can trythis
Chunk chunk = new Chunk("Underlined TExt", FontFactory.GetFont(FontFactory.TIMES_ROMAN, 12.0f, iTextSharp.text.Font.BOLD | iTextSharp.text.Font.UNDERLINE));
Paragraph reportHeadline = new Paragraph(chunk);
reportHeadline.SpacingBefore = 12.0f;
pdfDoc.Add(reportHeadline);

ITextsharp c# field calculations

Is there way creating automatically calculating fields with iTextsharp? I have tried doing this with javascript but the problem is that field values only get updated during certain events (ex. mouseover, mouseup). If I use events the field values only update when I move mouse cursor. They don't get updated if I write a value to field, then move mouse cursor somewhere else and then press enter. They get updated when I move cursor back to the field. Afaik there is no event like "field value changed" or something similiar?
There is no "on changed" event like there is in HTML, however there are "on focus" and "on blur" events so you can pretty easily write your own. The code below shows this off. It first creates a global JavaScript variable (which is not needed, you can discard that line, it just helps me think). Then it creates a standard text field and sets two actions, the Fo (focus) event and the Bl (blur) event. You can find these events and others in the PDF standard section 12.6.3 table 194.
In the focus event I'm just storing the current text field's value. In the blur event I'm comparing the store value with the new value and then just alerting if they are the same or different. If you have a bunch of fields you'll probably want to use a global array instead of individual variables, too. See the code comments for more information. This was tested against iTextSharp 5.4.2.0.
//Our test file
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test.pdf");
//Standard PDF creation, nothing special
using (var fs = new FileStream(testFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
//Add a global variable. This line is 100% not needed but it helps me think more clearly
writer.AddJavaScript(PdfAction.JavaScript("var first_name = '';", writer));
//Create a text field
var tf = new TextField(writer, new iTextSharp.text.Rectangle(50, 50, 300, 100), "first_name");
//Give it some style and default text
tf.BorderStyle = PdfBorderDictionary.STYLE_INSET;
tf.BorderColor = BaseColor.BLACK;
tf.Text = "First Name";
//Get the underlying form field object
var tfa = tf.GetTextField();
//On focus (Fo) store the value in our global variable
tfa.SetAdditionalActions(PdfName.FO, PdfAction.JavaScript("first_name = this.getField('first_name').value;", writer));
//On blur (Bl) compare the old value with the entered value and do something if they are the same/different
tfa.SetAdditionalActions(PdfName.BL, PdfAction.JavaScript("var old_value = first_name; var new_value = this.getField('first_name').value; if(old_value != new_value){app.alert('Different');}else{app.alert('Same');}", writer));
//Add our form field to the document
writer.AddAnnotation(tfa);
doc.Close();
}
}
}

OpenXML SDK - Setting excel page break to a certain number of columns

Does anyone know how to set the excel page break to include a certain number of columns using C# with the OpenXML SDK? What I want to do is make x columns appear on one page. I had originally thought setting the print area would do it but it doesn't. I can't find any references to do this.
This is done manually in an excel spreadsheet's "Page Break View" where you drag the vertical dotted line to include more columns.
Thanks
The OpenXML SDK distinguish between manual horizontal page breaks and manual vertical page breaks.
A manual horizontal page break allows you to specify a break above a given row Id (index).
A vertical page break allows you to specify a break to the left of the specified column Id (index).
To programmatically insert a horizontal page break use the RowBreaks and Break class.
The RowBreaks class represents a collection of all horizontal page breaks in a worksheet.
The ColumnBreaks and Break class allow you to insert a vertical page break. The
ColumnBreaks class holds all vertical page breaks for a worksheet.
The following example demonstrate the insertion of a vertical page break.
The function InsertVerticalPageBreak() takes a columnIndex (where the page break should be inserted)
and the WorksheetPart. This function first checks if the worksheet already contains a
ColumnBreaks collection. If not, one will be created. Then the function creates an instance
of the Break class and sets the Id property to the column index. I've also set the Max property
to the maximum number of rows Excel is able to handle to get a continuing vertical page break. By setting the property ManualPageBreak to true we specifing a manual page break.
I've also added a InsertHorizontalPageBreak() function to the sample to show how to
add a horizontal page break.
private void InsertPageBreaks()
{
uint columnIndex = 17U;
uint rowIndex = 51U;
using (SpreadsheetDocument sd = SpreadsheetDocument.Open("c:\\temp\\spreadsheet.xlsx", true))
{
WorkbookPart workbookPart = sd.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.Last();
// Uncomment the following line to insert row page breaks.
// InsertHorizontalPageBreak(rowIndex, worksheetPart);
InsertColumnVerticalBreak(columnIndex, worksheetPart);
}
}
private void InsertHorizontalPageBreak(uint rowIndex, WorksheetPart worksheetPart)
{
Break rowBreak =
new Break() { Id = (UInt32Value)rowIndex, Max = (UInt32Value)16383U, ManualPageBreak = true };
RowBreaks rb = worksheetPart.Worksheet.GetFirstChild<RowBreaks>();
if (rb == null)
{
rb = new RowBreaks();
rb.ManualBreakCount = (UInt32Value)0;
rb.Count = (UInt32Value)0;
worksheetPart.Worksheet.Append(rb);
}
rb.Append(rowBreak);
rb.ManualBreakCount++;
rb.Count++;
}
private void InsertVerticalPageBreak(uint columnIndex, WorksheetPart worksheetPart)
{
ColumnBreaks cb = worksheetPart.Worksheet.GetFirstChild<ColumnBreaks>();
if (cb == null)
{
cb = new ColumnBreaks();
cb.ManualBreakCount = (UInt32Value)0;
cb.Count = (UInt32Value)0;
worksheetPart.Worksheet.Append(cb);
}
Break br =
new Break() { Id = (UInt32Value)columnIndex, Max = (UInt32Value)1048575U, ManualPageBreak = true };
cb.Append(br);
cb.ManualBreakCount++;
cb.Count++;
}

Categories