I am generating a PDF in my application using itext. In the footer part, a phrase containing page X of Y is there. The total number of pages is not showing after PDF creation. Please refer the below code:
in MainActivity:
PdfPTable table = new PdfPTable(2);
table.setSpacingAfter(40.0f);
table.setTotalWidth(document.right()-document.left()-100);
try {
table.setWidths(new int[]{1, 2});
} catch (DocumentException e) {
e.printStackTrace();
}
PdfPCell cellOne = new PdfPCell(img);
Font f=new Font(Font.FontFamily.TIMES_ROMAN,20.0f,Font.BOLD, BaseColor.BLACK);
Font Para1_font=new Font(Font.FontFamily.TIMES_ROMAN,15.0f,Font.BOLD, BaseColor.BLACK);
Paragraph paragraph1_header = new Paragraph("QUOTE TO: 294087",f);
Paragraph paragraph = new Paragraph();
paragraph.add("AAAAAAAAAALLC * hhjkhhhhuhjbbnb" +
"jgjkll;, Sjklkjjjhh * AAHHGBJJ");
// Paragraph paragraph3 = new Paragraph();
// paragraph3.add("Page 1");
paragraph.setAlignment(Paragraph.ALIGN_LEFT);
PdfPCell cellTwo = new PdfPCell(paragraph);
// PdfPCell cellThree = new PdfPCell(paragraph3);
cellTwo.setPaddingLeft(10.0f);
cellOne.setPaddingBottom(20.0f);
cellTwo.setPaddingBottom(20.0f);
cellThree.setPaddingBottom(20.0f);
cellOne.setBorder(Rectangle.NO_BORDER);
cellTwo.setBorder(Rectangle.NO_BORDER);
cellThree.setBorder(Rectangle.NO_BORDER);
cellOne.setHorizontalAlignment(Element.ALIGN_LEFT);
cellTwo.setHorizontalAlignment(Element.ALIGN_LEFT);
cellThree.setHorizontalAlignment(Element.ALIGN_RIGHT);
table.addCell(cellOne);
table.addCell(cellTwo);
//table.addCell(cellThree);
try {
HeaderFooterPageEvent event = new HeaderFooterPageEvent(this, table);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(path));
//writer.setBoxSize("art", new Rectangle(36, 54, 559, 788));
writer.setPageEvent(event);
document.open();
inside HeaderFooterPageEvent class:
public void onOpenDocument(PdfWriter writer, Document document) {
total = writer.getDirectContent().createTemplate(30, 16);
try {
totalPages = Image.getInstance(total);
} catch (BadElementException e) {
e.printStackTrace();
}
totalPages.setRole(PdfName.ARTIFACT);
}
public void onEndPage(PdfWriter writer, Document document) {
footer.writeSelectedRows(0, -100, 36, 65, writer.getDirectContent());
try {
PdfPCell cell = new PdfPCell(Image.getInstance(total));
} catch (BadElementException e) {
e.printStackTrace();
}
Phrase footerPhrase = new Phrase("Page "+writer.getPageNumber()+
" of");
footerPhrase.add(new Chunk(totalPages,0,0,true));
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_CENTER, footerPhrase, 500, 65, 0);
}
Its just showing " Page x of" instead of " Page X of Y". Is there something I am missing? Please help.
You have implemented the onOpenDocument method to create a PdfTemplate of size 30, 16 (isn't that rather small?) and you are adding this empty placeholder on every page in the onEndPage method.
However, I don't see you adding content to the PdfTemplate anywhere. If you don't add the total number of pages, then you won't see the total number of pages anywhere in your document.
Since you can only know the total number of pages at the moment you close the document, you need to implement the onCloseDocument():
public void onCloseDocument(PdfWriter writer, Document document) {
ColumnText.showTextAligned(total, Element.ALIGN_LEFT,
new Phrase(String.valueOf(writer.getPageNumber() - 1)),
2, 2, 0);
}
See MovieCountries1 for a full example. This example was written in the context of the second edition of the book "iText in Action."
Related
I have Implemented an algorithm to draw text on pages using PDFBox library for Android. The problem is whenever I add a new page the text is overlapped like shown on the image below. I am sure i am using the PDPageContentStream.newLine() method but the result is not as expected.
Am I missing something else ?
Here is my code snippet
PDPage page1 = new PDPage();
getInstance().getAnnexe().addPage(page1);
PDPageContentStream contentStream1 = new
PDPageContentStream(getInstance().getAnnexe(), page1, true, true);
contentStream1.beginText();
contentStream1.newLineAtOffset(100F, 650F);
contentStream1.setFont(font, fontSize);
printMultipleLines(subSet, contentStream1);
contentStream1.endText();
contentStream1.close();
And this is the printMultipleLines() Method
private void printMultipleLines(ArrayList<String> lines, PDPageContentStream contentStream) {
try {
for (String line :
lines) {
if (line.length() > 110) {
// Print line as 2 lines
contentStream.showText(line.substring(0, 90));
contentStream.newLine();
contentStream.showText(line.substring(90, line.length()));
} else
// Print line as a whole
contentStream.showText(line);
// Print Carriage Return
contentStream.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Thank to #TilmanHausherr the problem was with the TL operator. Each newly created page had TL equal to zero amount of user default units. I just had to set the Text Leading offset.
Here is the updated code :
PDPage page1 = new PDPage();
getInstance().getAnnexe().addPage(page1);
PDPageContentStream contentStream1 = new
PDPageContentStream(getInstance().getAnnexe(), page1, true, true);
// Set the Text Leading (TL operator) here!!!!
contentStream1.setLeading(12);
contentStream1.beginText();
contentStream1.newLineAtOffset(100F, 650F);
contentStream1.setFont(font, fontSize);
printMultipleLines(subSet, contentStream1);
contentStream1.endText();
contentStream1.close();
All thanks and credits goes to # TilmanHausherr for his fast and accurate answer.
I have tried itextPdf_page_orientation but the problem is - only first page is proper rest all pages are printed in half area.
Here is an image which will help you understand the issue.
I have tried both setting the page size PdfPage.A4.rotate() and setting the event to PdfWritter.
Here is the code snipet.
#Override
protected String doInBackground(Void... params) {
final String PARENT_PATH =
Environment.getExternalStorageDirectory().getPath() + "/.GSTInvoice";
Document document = null;
try {
File file = new File(PARENT_PATH);
if (!file.exists()) {
file.mkdirs();
}
File pdfFile = new File(file, "last_sales_summary");
document = new Document();
document.setPageSize(PageSize.A4.rotate());
event = new RotateEvent();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
writer.setPageEvent(event);
document.open();
event.setOrientation(PdfPage.LANDSCAPE);
taxList = new ArrayList<>();
PdfContentByte cb = writer.getDirectContent();
printPage(document, writer, cb);
if (document != null && document.isOpen()) {
document.close();
}
return pdfFile.getPath();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document != null && document.isOpen()) {
document.close();
}
}
return null;
}
private void printPage(Document document, PdfWriter pdfWriter, PdfContentByte pdfContentByte) throws Exception{
int noOfPages = getNoOfPages();
BaseFont latoLight = BaseFont.createFont("assets/Lato-Light.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
Font light = new Font(latoLight, 8);
for (int i=1;i<=noOfPages;i++) {
if (i != 1) {
document.newPage();
pdfWriter.setPageSize(PageSize.A4.rotate());
event.setOrientation(PdfPage.LANDSCAPE);
document.setPageSize(PageSize.A4.rotate());
}
addTopPart(document);
addMiddleTable(document, i);
if (noOfPages>1) {
Paragraph paragraph = new Paragraph(new Phrase("Page " + i + "/" + noOfPages, light));
paragraph.setAlignment(Element.ALIGN_CENTER);
paragraph.setSpacingBefore(8f);
ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER,
paragraph,
(document.right() - document.left()) / 2 + document.leftMargin(),
document.bottom() - 10, 0);
}
event.setOrientation(PdfPage.LANDSCAPE);
}
}
Any input will be appreciated
The only peculiarity of the PDF is that the first page has a page rotation of 0 while the second one has a page rotation of 90.
This is due to what I hinted at in my first comment: By doing event.setOrientation(PdfPage.LANDSCAPE) after document.open() the first page is not rotated by the event listener, only all following ones. Other than that all pages have a mediabox of a portrait A4 page with rotated, page-filling content.
As you indicate that changing the order of event.setOrientation and document.open does not change the behavior, the print manager in question seems to be buggy.
You might try to remove all event.setOrientation(PdfPage.LANDSCAPE) calls; this should result in all pages to become like the first one.
Or you might want to remove all that code setting and changing rotation and instead instantiate the Document document using new Document(new RectangleReadOnly(842,595)), i.e. without any rotation at all; as the print manager appears not to handle rotation properly, this might also result in a desired behavior.
As the OP reported in a comment,
your suggestion to use RectangleReadOnly(842,595) worked.
I am using Itext library for android for converting html to pdf which is working fine but at certain things it is not parsing properly. I want to create a dotted line separator of red color but it is always gives me a solid line separator with dark gray color.
My html tag is
<hr noshade style="border: 0; width:100%;border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: red">
My conversion code
Document document = new Document(PageSize.A4);
//this sets the margin to the created pdf
document.setMargins(35, 35, 150, 100);
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream(fileWithinMyDir));
if (isPrescription) {
HeaderFooterPageEvent event = new HeaderFooterPageEvent();
writer.setPageEvent(event);
} else {
CertificateFooterPageEvent event = new CertificateFooterPageEvent();
writer.setPageEvent(event);
}
document.open();
HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
htmlContext.setImageProvider(new AbstractImageProvider() {
public String getImageRootPath() {
Uri uri = Uri.parse("file:///android_asset/");
return uri.toString();
}
});
CSSResolver cssResolver =
XMLWorkerHelper.getInstance().getDefaultCssResolver(false);
// Pipelines
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
InputStream is = new ByteArrayInputStream(htmlString.getBytes());
XMLWorkerHelper.getInstance().parseXHtml(writer, document, is);
p.parse(is);
document.close();
I'm a .NET developer, so the code is in C#. But you should be able to easily translate the following.
iText is a PDF-first library, and [X]HTML parsing is quite complex so it's not full featured in that regard. Whenever parsing [X]HTML and things aren't going the way you expect for specific tags, the basic steps you should follow are:
Verify XML Worker supports the tag: Tags class.
If the tag is supported, which in this case is true, take a look at the default implementation. Here it's handled by the the HorizontalRule class. However, we see there's no support for your use case, so one way to go is use that code as a blueprint. (follows below) You can also inherit from the specific tag class and override the End() method as done here. Either way, all you're doing is implementing a custom tag processor.
If the tag is not supported, you need to roll your own custom tag processor by inheriting from AbstractTagProcessor.
Anyway, here's a simple example to get you started. First, the custom tag processor:
public class CustomHorizontalRule : AbstractTagProcessor
{
public override IList<IElement> Start(IWorkerContext ctx, Tag tag)
{
IList<IElement> result;
LineSeparator lineSeparator;
var cssUtil = CssUtils.GetInstance();
try
{
IList<IElement> list = new List<IElement>();
HtmlPipelineContext htmlPipelineContext = this.GetHtmlPipelineContext(ctx);
Paragraph paragraph = new Paragraph();
IDictionary<string, string> css = tag.CSS;
float baseValue = 12f;
if (css.ContainsKey("font-size"))
{
baseValue = cssUtil.ParsePxInCmMmPcToPt(css["font-size"]);
}
string text;
css.TryGetValue("margin-top", out text);
if (text == null) text = "0.5em";
string text2;
css.TryGetValue("margin-bottom", out text2);
if (text2 == null) text2 = "0.5em";
string border;
css.TryGetValue(CSS.Property.BORDER_BOTTOM_STYLE, out border);
lineSeparator = border != null && border == "dotted"
? new DottedLineSeparator()
: new LineSeparator();
var element = (LineSeparator)this.GetCssAppliers().Apply(
lineSeparator, tag, htmlPipelineContext
);
string color;
css.TryGetValue(CSS.Property.BORDER_BOTTOM_COLOR, out color);
if (color != null)
{
// WebColors deprecated, but docs don't state replacement
element.LineColor = WebColors.GetRGBColor(color);
}
paragraph.SpacingBefore += cssUtil.ParseValueToPt(text, baseValue);
paragraph.SpacingAfter += cssUtil.ParseValueToPt(text2, baseValue);
paragraph.Leading = 0f;
paragraph.Add(element);
list.Add(paragraph);
result = list;
}
catch (NoCustomContextException cause)
{
throw new RuntimeWorkerException(
LocaleMessages.GetInstance().GetMessage("customcontext.404"),
cause
);
}
return result;
}
}
Most of the code is taken directly from the existing source, with the exception of the checks for CSS.Property.BORDER_BOTTOM_STYLE and CSS.Property.BORDER_BOTTOM_COLOR to set border style and color if they're inlined in the <hr> style attribute.
Then you add the custom tag processor above to the XML Worker TagProcessorFactory:
using (var stream = new FileStream(OUTPUT_FILE, FileMode.Create))
{
using (var document = new Document())
{
var writer = PdfWriter.GetInstance(document, stream);
document.Open();
var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory();
// custom tag processor above
tagProcessorFactory.AddProcessor(
new CustomHorizontalRule(),
new string[] { HTML.Tag.HR }
);
var htmlPipelineContext = new HtmlPipelineContext(null);
htmlPipelineContext.SetTagFactory(tagProcessorFactory);
var pdfWriterPipeline = new PdfWriterPipeline(document, writer);
var htmlPipeline = new HtmlPipeline(htmlPipelineContext, pdfWriterPipeline);
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true);
var cssResolverPipeline = new CssResolverPipeline(
cssResolver, htmlPipeline
);
var worker = new XMLWorker(cssResolverPipeline, true);
var parser = new XMLParser(worker);
var xHtml = "<hr style='border:1px dotted red' />";
using (var stringReader = new StringReader(xHtml))
{
parser.Parse(stringReader);
}
}
}
One thing to note is that even though we're using the shorthand border inline style, iText's CSS parser appears to set all the styles internally. I.e., you can use any of the four longhand styles to check - I just happened to use CSS.Property.BORDER_BOTTOM_STYLE and CSS.Property.BORDER_BOTTOM_COLOR.
The resulting PDF:
You could use a div without any or with any content you want instead of an hr and give border style to that div, I am sure it will work in your case.
Firstly, pardon me due to lack of info as I am very new to programming. Below are my codes for opening Physicaloid to connect my android device to Arduino through serial communication. Right now I am receiving analog signals coming from my arduino and appearing on tVread from the array "buf". However I failed and stucked trying to plot it the array "buf" using Android Plot. Please kindly advice. Thank you!
if (mPhysicaloid.open()) {
setEnabledUi(true);
if (cbAutoscroll.isChecked()) {
tvRead.setMovementMethod(new ScrollingMovementMethod());
}
mPhysicaloid.addReadListener(new ReadLisener() {
#Override
public void onRead(int size) {
byte[] buf = new byte[size];
Number[] numarray = new Number[size];
mPhysicaloid.read(buf,size);
//convert buf to int array
for(int i=0;i<size;i++) {
numarray[i]= buf[i];
}
tvAppend(tvRead, Html.fromHtml("<font color=blue>" + new String (buf) + "</font>"));
XYSeries series1 = new SimpleXYSeries(
Arrays.asList(numarray),SimpleXYSeries.ArrayFormat.Y_VALS_ONLY,"Series1");
LineAndPointFormatter series1Format = new LineAndPointFormatter(
Color.rgb(0, 200, 0), // line color
Color.rgb(0, 100, 0), // point color
null, // fill color (none)
new PointLabelFormatter(Color.WHITE));
mySimpleXYPlot.addSeries(series1, series1Format);
}
});
} else {
Toast.makeText(this, "Cannot open", Toast.LENGTH_LONG).show();
}
}
It's not clear from the code snippet at which point in the Activity lifecycle this gets called but it seems likely that it happens well after onCreate via a background thread.
You'll likely need to add a call to plot.redraw() at the end of your onRead() implementation. Also, depending on how many times this method gets called, what you currently have will result in adding an additional series to the plot on each call, which you probably do not want.
Instead you probably want create your XYSeries outside of the callback and simply update it from onRead().
I have created a PDF file dynamically using iText Library, Now I want to add Header and Footer in PDF's pages, for this one I have added given code:
document.addHeader("My Header Title", "My Header Details");
But in my PDF's pages this header couldn't set. What is issue that I don't know, If you have any idea related to it,please share your thoughts.
In case you use a current iText version (i.e. 5.4.x as of now) have a look at the sample MovieHistory2 from iText in Action — 2nd Edition which shows how to add headers (different ones for odd and even pages) to a PDF while creating it.
Most essential is a PdfPageEventHelper implementation
/** Inner class to add a header and a footer. */
class HeaderFooter extends PdfPageEventHelper {
/** Alternating phrase for the header. */
Phrase[] header = new Phrase[2];
/** Current page number (will be reset for every chapter). */
int pagenumber;
/**
* Initialize one of the headers.
* #see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(
* com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
*/
public void onOpenDocument(PdfWriter writer, Document document) {
header[0] = new Phrase("Movie history");
}
/**
* Initialize one of the headers, based on the chapter title;
* reset the page number.
* #see com.itextpdf.text.pdf.PdfPageEventHelper#onChapter(
* com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document, float,
* com.itextpdf.text.Paragraph)
*/
public void onChapter(PdfWriter writer, Document document,
float paragraphPosition, Paragraph title) {
header[1] = new Phrase(title.getContent());
pagenumber = 1;
}
/**
* Increase the page number.
* #see com.itextpdf.text.pdf.PdfPageEventHelper#onStartPage(
* com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
*/
public void onStartPage(PdfWriter writer, Document document) {
pagenumber++;
}
/**
* Adds the header and the footer.
* #see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(
* com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
*/
public void onEndPage(PdfWriter writer, Document document) {
Rectangle rect = writer.getBoxSize("art");
switch(writer.getPageNumber() % 2) {
case 0:
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_RIGHT, header[0],
rect.getRight(), rect.getTop(), 0);
break;
case 1:
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_LEFT, header[1],
rect.getLeft(), rect.getTop(), 0);
break;
}
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_CENTER, new Phrase(String.format("page %d", pagenumber)),
(rect.getLeft() + rect.getRight()) / 2, rect.getBottom() - 18, 0);
}
}
which is registered like this:
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
HeaderFooter event = new HeaderFooter();
writer.setBoxSize("art", new Rectangle(36, 54, 559, 788));
writer.setPageEvent(event);
EDIT: As requested in the comments, a simpler variant of the onEndPage method with a static header instead of the alternating one:
public void onEndPage(PdfWriter writer, Document document)
{
Rectangle rect = writer.getBoxSize("art");
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_RIGHT, new Phrase("My static header text"),
rect.getRight(), rect.getTop(), 0);
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_CENTER, new Phrase(String.format("page %d", pagenumber)),
(rect.getLeft() + rect.getRight()) / 2, rect.getBottom() - 18, 0);
}
please refer this site.......
https://www.coderanch.com/how-to/java/ItextExample
Please first refer to the accepted answer of this question.
That answer is very helpful (and It helped me to).
Just in case you are programming in C#, here is the SAME accepted answer but in C# version
/// <summary>
/// Inner class to add a header and a footer.
/// </summary>
internal class HeaderFooter : PdfPageEventHelper
{
private Phrase[] header = new Phrase[2];
private int pageNumber;
public override void OnOpenDocument(PdfWriter writer, Document document)
{
header[0] = new Phrase("Smares in Header");
}
public override void OnChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title)
{
header[1] = new Phrase(title.Content);
pageNumber = 1;
}
public override void OnStartPage(PdfWriter writer, Document document)
{
pageNumber++;
}
public override void OnEndPage(PdfWriter writer, Document document)
{
Rectangle rect = writer.GetBoxSize("art");
switch (writer.PageNumber % 2)
{
case 0:
ColumnText.ShowTextAligned(writer.DirectContent,
Element.ALIGN_RIGHT, header[0],
rect.Right, rect.Top, 0);
break;
case 1:
ColumnText.ShowTextAligned(writer.DirectContent,
Element.ALIGN_LEFT, header[1],
rect.Left, rect.Top, 0);
break;
}
ColumnText.ShowTextAligned(writer.DirectContent,
Element.ALIGN_CENTER, new Phrase(String.Format("page {0}", pageNumber)),
(rect.Left + rect.Right) / 2, rect.Bottom - 18, 0);
}
}
and the registration of the event will be :
using (MemoryStream ms = new MemoryStream())
{
using (Document doc = new Document(PageSize.A4, -30, -30, 45, 45))
{
using (PdfWriter writer = PdfWriter.GetInstance(doc, ms))
{
HeaderFooter ev = new HeaderFooter();
writer.SetBoxSize("art", new Rectangle(36, 54, 559, 788));
writer.PageEvent = ev;
// continue your code here
}
}
}
NOTE : this is just a conversion of the accepted answer from java to C#.
but you can customize this according to your needs, as I did with it.
You can add like that
HeaderFooter header = new HeaderFooter(new Phrase("Add Header Part Here"), false);
HeaderFooter footer = new HeaderFooter(new Phrase("Add Footer Here"), new Phrase("."));
document.setHeader(header);
document.setFooter(footer);