For the past week or so this exception is causing me a headache, I can't for the life of me fix it. I'm using iTextSharp to merge PDF files and add a watermark on them if the user chooses to do so.
Here's the code for merging :
private void CreateMergedPdf(object sender, DoWorkEventArgs e)
{
using (FileStream stream = new FileStream(pdfname, FileMode.Create)) {
Document pdfDoc = new Document(PageSize.A4);
PdfCopy pdf = new PdfCopy(pdfDoc, stream);
pdfDoc.Open();
int i = 0;
foreach (File_class newpdf in AddedPDFs)
{
(sender as BackgroundWorker).ReportProgress(i++);
if (newpdf.toMerge)
{
PdfReader reader = new PdfReader(newpdf.file_path);
pdf.AddDocument(reader); //<!> Exception here
this.Dispatcher.Invoke(() => progBtxt.Text = "Merging file #" + newpdf.file_id + "..."); //Dispatcher.Invoke since UI is on seperate thread
if (add_wtrmk)//This is called for every FILE
{
AddWatermark(reader, stream);
}
}
}
}
}
And here's the code for the watermark:
private void AddWatermark(PdfReader reader, FileStream stream)
{
using (PdfStamper pdfStamper = new PdfStamper(reader, stream))//This is called for every PAGE of the file
{
for (int pgIndex = 1; pgIndex <= reader.NumberOfPages; pgIndex++)
{
Rectangle pageRectangle = reader.GetPageSizeWithRotation(pgIndex);
PdfContentByte pdfData; //Contains graphics and text content of page returned by pdfstamper
if (this.Dispatcher.Invoke(() => dropdown.Text == "Under Content"))
{
pdfData = pdfStamper.GetUnderContent(pgIndex);
}
else if (this.Dispatcher.Invoke(() => dropdown.Text == "Over Content"))
{
pdfData = pdfStamper.GetOverContent(pgIndex);
}
else//Just in case
{
MessageBox.Show("Something went wrong when adding the watermark");
return;
}
//Set font
pdfData.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 40);
//Create new graphics state and assign opacity
PdfGState graphicsState = new PdfGState();
graphicsState.FillOpacity = 0.25F;
//Set graphics state to pdfcontentbyte
pdfData.SetGState(graphicsState);
//Color of watermark
pdfData.SetColorFill(BaseColor.GRAY);
pdfData.BeginText();
//Show text as per position and rotation
this.Dispatcher.Invoke(() => pdfData.ShowTextAligned(Element.ALIGN_CENTER, WtrmkTextbox.Text, pageRectangle.Width / 2, pageRectangle.Height / 2, 45));
pdfData.EndText();
}
}
}
The error appears on the code for merging, specifically the line " pdf.AddDocument(reader);" BUT I get this error only if I try to add watermarks on more than one files (with just one file it works perfectly).
I'm thinking either I am closing something too early, or addWatermark() does - I've tried changing our the using statemets to no avail. I must be missing something
Okay, it seems PdfStamper was the culprit, i passed the necessary arguements to AddWatermark() and added a simple if statement. Now everything works perfectly.
BIG thanks to Mark Rucker
Related
I have the following code but this code add only the last image into pdf.
try {
filePath = (filePath != null && filePath.endsWith(".pdf")) ? filePath
: filePath + ".pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream(filePath));
document.open();
// document.add(new Paragraph("Image Example"));
for (String imageIpath : imagePathsList) {
// Add Image
Image image1 = Image.getInstance(imageIpath);
// Fixed Positioning
image1.setAbsolutePosition(10f, 10f);
// Scale to new height and new width of image
image1.scaleAbsolute(600, 800);
// image1.scalePercent(0.5f);
// Add to document
document.add(image1);
//document.bottom();
}
writer.close();
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
Would you give me a hint about how to update the code in order to add all the images into the exported pdf? imagePathsList contains all the paths of images that that I want to add into a single pdf.
Best Regards,
Aurelian
Take a look at the MultipleImages example and you'll discover that there are two errors in your code:
You create a page with size 595 x 842 user units, and you add every image to that page regardless of the dimensions of the image.
You claim that only one image is added, but that's not true. You are adding all the images on top of each other on the same page. The last image covers all the preceding images.
Take a look at my code:
public void createPdf(String dest) throws IOException, DocumentException {
Image img = Image.getInstance(IMAGES[0]);
Document document = new Document(img);
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
for (String image : IMAGES) {
img = Image.getInstance(image);
document.setPageSize(img);
document.newPage();
img.setAbsolutePosition(0, 0);
document.add(img);
}
document.close();
}
I create a Document instance using the size of the first image. I then loop over an array of images, setting the page size of the next page to the size of each image before I trigger a newPage() [*]. Then I add the image at coordinate 0, 0 because now the size of the image will match the size of each page.
[*] The newPage() method only has effect if something was added to the current page. The first time you go through the loop, nothing has been added yet, so nothing happens. This is why you need set the page size to the size of the first image when you create the Document instance.
Android has the feature "PdfDocument" to achieve this,
class Main2Activity : AppCompatActivity() {
private var imgFiles: Array<File?>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
imgFiles= arrayOfNulls(2)
imgFiles!![0] = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() + "/doc1.png")
imgFiles!![1] = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() + "/doc3.png")
val file = getOutputFile(File(Environment.getExternalStorageDirectory().absolutePath)
, "/output.pdf")
val fOut = FileOutputStream(file)
val document = PdfDocument()
var i = 0
imgFiles?.forEach {
i++
val bitmap = BitmapFactory.decodeFile(it?.path)
val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, i).create()
val page = document.startPage(pageInfo)
val canvas = page?.canvas
val paint = Paint()
canvas?.drawPaint(paint)
paint.color = Color.BLUE;
canvas?.drawBitmap(bitmap, 0f, 0f, null)
document.finishPage(page)
bitmap.recycle()
}
document.writeTo(fOut)
document.close()
}
private fun getOutputFile(path: File, fileName: String): File? {
if (!path.exists()) {
path.mkdirs()
}
val file = File(path, fileName)
try {
if (file.exists()) {
file.delete()
}
file.createNewFile()
} catch (e: Exception) {
e.printStackTrace()
}
return file
}
}
finally enable the storage permission in manifest, this should works
I made some file merging using PDFsharp before, and now I'm trying to change several files (insert or remove some pages) and I faced with problem, that the library does not see pages. It says that PageCount == 0 and I cannot find pages in object (while debugging). And sure, I cannot do my current work. I use this very simple code:
var destinationPdf = new PdfDocument(destinationFilePath);
Int32 count = destinationPdf.PageCount;
And also, here is the code, that I used to merge files to one PDF before:
public class PdfCreator
{
private PdfDocument document;
public PdfCreator()
{
this.document = new PdfDocument();
}
public void AddImage(String imageFilePath)
{
PdfPage newPage = this.document.AddPage();
XGraphics xGraphics = XGraphics.FromPdfPage(newPage);
XImage image = XImage.FromFile(imageFilePath);
xGraphics.DrawImage(image, 0, 0);
}
public void AddPdfFile(String pdfFilePath)
{
PdfDocument inputDocument = PdfReader.Open(pdfFilePath, PdfDocumentOpenMode.Import);
Int32 count = inputDocument.PageCount;
for (Int32 currentPage = 0; currentPage < count; currentPage++)
{
PdfPage page = inputDocument.Pages[currentPage];
this.document.AddPage(page);
}
}
public void AddTextFile(String txtFilePath)
{
PdfPage newPage = this.document.AddPage();
XGraphics xGraphics = XGraphics.FromPdfPage(newPage);
var xFont = new XFont("Times New Roman", 12, XFontStyle.Bold);
var xTextFormatter = new XTextFormatter(xGraphics);
var rect = new XRect(30, 30, 540, 740);
xGraphics.DrawRectangle(XBrushes.Transparent, rect);
xTextFormatter.Alignment = XParagraphAlignment.Left;
xTextFormatter.DrawString(File.ReadAllText(txtFilePath), xFont, XBrushes.Black, rect, XStringFormats.TopLeft);
}
public void Save(String destinationFilePath)
{
if (this.document.Pages.Count > 0)
{
this.document.Save(destinationFilePath);
this.document.Close();
}
}
}
Your code
var destinationPdf = new PdfDocument(destinationFilePath);
Int32 count = destinationPdf.PageCount;
creates a new document in memory - and surely this document is empty.
Use PdfReader.Open to create a document in memory from an existing file.
When I place the mouse cursor over PdfDocument in your code I get this tooltip:
Creates a new PDF document with the specified file name. The file is
immediately created and keeps locked until the document is closed, at
that time the document is saved automatically. Do not call Save() for
documents created with this constructor, just call Close(). To open an
existing PDF file and import it, use the PdfReader class.
m having a grid: companysnapshot.. that grid is exported to disk then its again taken from disk and exported to the pdf.
Below code is working fine and export with the image is done. bt issues is that..
-->image is saved from UI is black background..when its exported is changing in white background( may be its getting converted to png)
--> i want to align the coordinates of the image in the pdf page
is there any way to either increase the width of the image or pdf page.
m a newbie to this ...it would be helpful if someone code me out for this a little.
private void PrepareDocument(RadDocument document)
{
document.SectionDefaultPageOrientation = PageOrientation.Landscape;
document.LayoutMode = DocumentLayoutMode.Paged;
document.Measure(RadDocument.MAX_DOCUMENT_SIZE);
document.Arrange(new RectangleF(PointF.Empty, document.DesiredSize));
}
chart document part:
private void CreateChartDocumentPart(RadDocument document, Grid whGrid, Grid companysnapshot, Grid chartgridimage)
{
Telerik.Windows.Documents.Model.Section section = new Telerik.Windows.Documents.Model.Section();
Telerik.Windows.Documents.Model.Paragraph paragraph = new Telerik.Windows.Documents.Model.Paragraph();
Telerik.Windows.Documents.Model.Span span1;
using (MemoryStream ms = new MemoryStream())
{
companysnapshot.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
int companywidth = (int)Math.Round(companysnapshot.ActualWidth);
int companyheight = (int)Math.Round(companysnapshot.ActualHeight);
companywidth = companywidth == 0 ? 1 : companywidth;
companyheight = companyheight == 0 ? 1 : companyheight;
RenderTargetBitmap rtbmp = new RenderTargetBitmap(companywidth, companyheight, 96d, 96d, PixelFormats.Default);
rtbmp.Render(companysnapshot);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtbmp));
FileStream fs1 = File.Create(#"C:\Users\Admin\Desktop\Chart12xx.bmp");
encoder.Save(fs1);
fs1.Close();
// this.ExportPNGToImage(chart, ms);
paragraph.LeftIndent = 0;
paragraph.RightIndent = 0.0;
FileStream ss = File.Open(#"C:\Users\Admin\Desktop\Chart12xx.bmp", FileMode.Open);
ImageInline image = new ImageInline(ss, new Size(companywidth, companyheight), "bmp");
paragraph.FlowDirection = FlowDirection.LeftToRight;
paragraph.Inlines.Add(image);
ss.Close();
//Span spacespan = new Span(" ");
//paragraph.Inlines.Add(spacespan);
}
try
{
section1.Blocks.Add(paragraph);
document.Sections.Add(section1);
}
catch (Exception)
{
}
// paragraph.Inlines.Add(new Span(FormattingSymbolLayoutBox.LINE_BREAK));
}
issue is solved.. by switching from telerik export to itextsharp for pdf export.
I'm trying to generate a pdf from image with iTextSharp, but I'm getting the following errors: iTextSharp.Image does not contain a definition for 'getInstance' and 'iTextSharp.text.Document does not contain a definition for 'add' and 'iTextSharp.text.Document does not contain a definition for 'newPage' and iTextSharp.text.Image does not contain a definition for 'scalePercent'**
I have already add the iText Library (itextsharp, itextsharp.pdfa and itextshar.xtra). here is my code:
private void button3_Click_1(object sender, EventArgs e)
{
saveFileDialog1.FileName = "name.pdf";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
using (Bitmap bitmap = new Bitmap(panel1.ClientSize.Width,
panel1.ClientSize.Height))
{
panel1.DrawToBitmap(bitmap, panel1.ClientRectangle);
bitmap.Save("C:\\" + (nPaginasPDF + 1) + ".bmp", ImageFormat.Bmp);
}
Document doc = new Document();
PdfWriter.GetInstance(doc, new FileOutputStream(yourOutFile));
doc.Open();
for (int iCnt = 0; iCnt < nPaginasPDF; iCnt++)
{
iTextSharp.text.Image image1 = iTextSharp.text.Image.GetInstance("C:\\" + (iCnt + 1) + ".bmp");
image1.ScalePercent(23f);
doc.NewPage();
doc.Add(image1);
}
using (var Stream = saveFileDialog1.OpenFile())
{
doc.Save(Stream);
}
doc.Close();
}
Both #Nenad and #MaxStoun are correct, you just need to adapt the Java conventions to .Net. Additionally, you'll also need to swap the Java FileOutputStream for the .Net System.IO.FileStream object.
EDIT
You have some "magic variables" in there that I need to work around. For instance, I'm not 100% sure what you're doing with your for loop so I just removed it for this sample. Also, I don't have write permissions to my c:\ directory to I'm saving to the desktop. Otherwise, this code should hopefully get you on the correct path.
//I don't know what you're doing with this variable so I'm just setting it to something
int nPaginasPDF = 10;
//I can't write to my C: drive so I'm saving to the desktop
string saveFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
//Set the default file name
saveFileDialog1.FileName = "name.pdf";
//If the user presses "OK"
if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
//Create a bitmap and save it to disk
using (Bitmap bitmap = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Height)) {
panel1.DrawToBitmap(bitmap, panel1.ClientRectangle);
//Path.Combine is a safer way to build file pathes
bitmap.Save(System.IO.Path.Combine(saveFolder, nPaginasPDF + ".bmp"), ImageFormat.Bmp);
}
//Create a new file stream instance with some locks for safety
using (var fs = new System.IO.FileStream(saveFileDialog1.FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) {
//Create our iTextSharp document
using (var doc = new Document()) {
//Bind a PdfWriter to the Document and FileStream
using (var writer = PdfWriter.GetInstance(doc, fs)) {
//Open the document for writing
doc.Open();
//Get an instance of our image
iTextSharp.text.Image image1 = iTextSharp.text.Image.GetInstance(System.IO.Path.Combine(saveFolder, nPaginasPDF + ".bmp"));
//Sacle it
image1.ScalePercent(23f);
//Add a new page
doc.NewPage();
//Add our image to the document
doc.Add(image1);
//Close our document for writing
doc.Close();
}
}
}
}
If you use iText documentation or books for Java, you need to adapt things a bit for .NET. In your example, since .NET implicit getters and setters for properties, this:
var instance = iTextSharp.Image.getInstance();
becomes this:
var instance = iTextSharp.Image.Instance;
Second issue: method names in Java are camel case, vs .NET pascal case, so this (camelCase):
image1.scalePercent(23f);
doc.newPage();
doc.add(image1);
becomes this (PascalCase):
image1.ScalePercent(23f);
doc.NewPage();
doc.Add(image1);
And so on. Just apply .NET code naming conventions instead of Java's.
You'll upper first letter in method name (I just downloaded it from Nuget)
Image.getInstance(); => Image.GetInstance();
doc.add(image1); => doc.Add(image1);
Anyone can help with how to get a text coordinates? can this be possible? because I just wanted a windows form app where the user types a word in a text box, and the app reads existing PDF using iTextSharp, highlights the matched words if found, and saves the PDF with highlighted text. so far i have almost everything done, including the drawing of a yellow rectangle, but what is lacking is how to get the text coordinates of the matched patterns to highlight them, thanks in advance: (by the way: sb is the search text box, tb is a rich text box where the PDF text is exhibited)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using iTextSharp.text;
using System.Text.RegularExpressions;
namespace manipulatePDF
{
public partial class Form1 : Form
{
string oldFile;
Document document = new Document();
StringBuilder text = new StringBuilder();
public Form1()
{
InitializeComponent();
}
private void open_Click(object sender, EventArgs e)
{
reset_Click(sender, e);
openFileDialog1.Filter = "PDF Files (.pdf)|*.pdf";
openFileDialog1.FilterIndex = 1;
if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
label1.Text = "File Location: " + openFileDialog1.FileName;
oldFile = openFileDialog1.FileName;
// open the reader
PdfReader reader = new PdfReader(oldFile);
iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
document.SetPageSize(size);
for (int cPage = 1; cPage <= reader.NumberOfPages; cPage++)
{
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
string currentText = PdfTextExtractor.GetTextFromPage(reader, cPage, strategy);
currentText = Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText)));
text.Append(currentText);
reader.Close();
}
tb.Text = text.ToString();
}
}
private void save_Click(object sender, EventArgs e)
{
saveFileDialog1.InitialDirectory = "C: ";
saveFileDialog1.Title = "Save the PDF File";
saveFileDialog1.Filter = "PDF files (*.pdf)|*.pdf";
if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
PdfReader reader = new PdfReader(oldFile);
string newFile = saveFileDialog1.FileName;
// open the writer
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
// the pdf content
PdfContentByte cb = writer.DirectContent;
// select the font properties
PdfGState graphicsState = new PdfGState();
graphicsState.FillOpacity = 10;
cb.SetGState(graphicsState);
int index = 0;
while (index < text.ToString().LastIndexOf(sb.Text))
{
if (contain.Checked == true)
{
tb.Find(sb.Text, index, tb.TextLength, RichTextBoxFinds.MatchCase);
tb.SelectionBackColor = Color.Gold;
index = tb.Text.IndexOf(sb.Text, index) + 1;
}
else if (exact.Checked == true)
{
tb.Find(sb.Text, index, tb.TextLength, RichTextBoxFinds.WholeWord);
tb.SelectionBackColor = Color.Gold;
index = tb.Text.IndexOf(sb.Text, index) + 1;
}
}
int count = 0; //counts the pattern occurance
for (int cPage = 1; cPage <= reader.NumberOfPages; cPage++)
{
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
string currentText = PdfTextExtractor.GetTextFromPage(reader, cPage, strategy);
currentText = Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText)));
string textToSearch = sb.Text;
int lastStartIndex = currentText.IndexOf(textToSearch, 0, StringComparison.CurrentCulture);
while (lastStartIndex != -1)//if the pattern was found
{
count++;
lastStartIndex = currentText.IndexOf(textToSearch, lastStartIndex + 1, StringComparison.CurrentCulture);
BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
cb.SetFontAndSize(bf, 10);
cb.SetColorFill(new CMYKColor(0f, 0f, 1f, 0f));
cb.Rectangle(document.PageSize.Width - 500f, 600f, 100f, 100f);
cb.Fill();
}
if (count != 0)
{
if (contain.Checked == true)
{
label2.Text = "Number of pages: " + cPage + " - " + textToSearch + " found " + count + " times. \n";
}
else if (exact.Checked == true)
{
//finds the words that are bounded by a space or a dot and store in cCount
//returns the count of matched pattern = count - cCount
}
}
text.Append(currentText);
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, cPage);
cb.AddTemplate(page, 0, 0);
document.NewPage();
//PdfStamper stamper = new PdfStamper(reader, fs);
////Create a rectangle for the highlight. NOTE: Technically this isn't used but it helps with the quadpoint calculation
//iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(60.6755f, 749.172f, 94.0195f, 735.3f);
////Create an array of quad points based on that rectangle. NOTE: The order below doesn't appear to match the actual spec but is what Acrobat produces
//float[] quad = { rect.Left, rect.Bottom, rect.Right, rect.Bottom, rect.Left, rect.Top, rect.Right, rect.Top };
////Create our hightlight
//PdfAnnotation highlight = PdfAnnotation.CreateMarkup(stamper.Writer, rect, null, PdfAnnotation.MARKUP_HIGHLIGHT, quad);
////Set the color
//highlight.Color = BaseColor.YELLOW;
////Add the annotation
//stamper.AddAnnotation(highlight, 1);
}
// close the streams
document.Close();
fs.Close();
writer.Close();
reader.Close();
}
}
private void reset_Click(object sender, EventArgs e)
{
tb.Text = "";
}
}
Well, i had added a downloable example made using Vb.NET 2010 that does exactly what you need, and it's available in another post in the same thread Chris referenced. That code will work for every font type, font size and will return all matches for the word/sentence you search for, returning each match as a rectangle with x/y locations to the UI, and finally hightlighting them all and saving to a new PDF, you just need to give some initial parameters like, search term, comparison type by culture, source PDF path and destination PDF Path. The only thing not implemented is that particular case when the search word/sentence falls into multiple lines, but it should be an easy change in code since you can use SameLine() method in TextChunk Class.