Watermark does not print correctly - pdf

I'm using iTextSharp to add a watermark to existing PDF files. When viewing these files on screen, the watermark and PDF appears correctly. However, when printing on certain printers (2 out of 3 that have been tested by the client), the watermark seems to interfere with the content that is above it causing it to appear malformed when printed.
The PDF's are of CAD drawings (i.e. electrical circuit drawings etc.)
The code being used to apply the watermark is below. The PdfContentByte is retrieved by a call to GetOverContent from the class PdfStamper
private void AddWaterMark(PdfContentByte dc, string text, BaseFont font, float fontSize, float angle, BaseColor color, Rectangle realPageSize, Rectangle rect = null)
{
var gstate = new PdfGState { FillOpacity = 0.1f, StrokeOpacity = 0.3f };
dc.SaveState();
dc.SetGState(gstate);
dc.SetColorFill(color);
dc.BeginText();
dc.SetFontAndSize(font, fontSize);
var ps = rect ?? realPageSize; /*dc.PdfDocument.PageSize is not always correct*/
var x = (ps.Right + ps.Left) / 2;
var y = (ps.Bottom + ps.Top) / 2;
dc.ShowTextAligned(Element.ALIGN_CENTER, text, x, y, angle);
dc.EndText();
dc.RestoreState();
}
Here is an example of what it looks like on screen:
Here is what it looks like when printed on some of the printers:
Can anyone give me any idea on why certain printers will not print the PDF correctly and if I can change the way I apply the watermarks to avoid this issue?

Related

itext5 sign PDF containing only image

I have a code that successfully add visible signature block into a "normal" PDF.
<...>
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(c[0], c[1], c[2], c[3]), 1, field);
createVisigbleSignature(stamper, appearance, signFont, signTxt, img);
<...>
public static void createVisigbleSignature(PdfStamper stamper, PdfSignatureAppearance appearance, Font font, String text, byte[] img) throws Exception {
PdfTemplate layer2 = appearance.getLayer(2);
float size = -1;
final float MARGIN = 2;
Rectangle dataRect = new Rectangle(MARGIN, MARGIN, appearance.getRect().getWidth() - MARGIN, appearance.getRect().getHeight() - MARGIN);
Rectangle sr = new Rectangle(dataRect.getWidth(), dataRect.getHeight());
size = ColumnText.fitText(font, text, sr, 12, appearance.getRunDirection());
ColumnText ct = new ColumnText(layer2);
ct.setRunDirection(appearance.getRunDirection());
ct.setSimpleColumn(new Phrase(text, font), dataRect.getLeft(), dataRect.getBottom(), dataRect.getRight(), dataRect.getTop(), size, Element.ALIGN_LEFT);
ct.go();
//image
Image image = Image.getInstance(img);
layer2.addImage(image, appearance.getRect().getWidth(), 0, 0, appearance.getRect().getHeight(), 0, 0);
}
But if I try to sign PDF that contains only image (basically it is image exported as pdf), my visible signature block is no longer visible.
Acrobate Reader sees the signature container, but user can't see or click the "visible" block.
What can be the reason for that and how to make sure that signature information is visible no matter what?
Here the examples:
https://drive.google.com/drive/folders/1hnROu5UVXECi-hy9FY5ZXJLDK_jdwjch?usp=sharing
normal.pdf and photo.pdf are the files before signing.
normal_pre.pdf and photo.pre.pdf are pre-signed.
The sign will be seen as "broken". It is normal as the pdfs contain only container and not the signature itself.
The problem with your example PDF with images only is that you positioned the signature off-page.
In detail: That PDF has a single page with this crop box:
/CropBox [ 0.0 0.0 841.5 594.75 ]
I.e. its lower left corner is the coordinate system origin (0, 0) and its upper right corner is (841.5, 594.75).
Your signature on that page, though, is at
/Rect[10 810 130 840]
I.e. between (10, 810) and (130, 840). These coordinates clearly are above the crop box.
If you set Adobe Reader to show the pages continuously and zoom out a bit, you can actually see the annotation:
To fix this, therefore, simply use coordinates new Rectangle(c[0], c[1], c[2], c[3]) for your signature widget that are on-screen.

How to convert PDF to image with same resolution as original PDF

I am using Aspose.Pdf to convert PDF to Image and latter convert Image back to Pdf. I would like to keep resolution, width and height of the new Pdf same as original Pdf.
using (var newDocument = new Document())
{
using (var originalDocument = new Document("c:\\source.pdf"))
{
foreach (Aspose.Pdf.Page page in originalDocument.Pages)
{
if (page.Rotate != Aspose.Pdf.Rotation.None)
{
using (var ms = new MemoryStream())
{
// Create Jpegdevice with specified attributes
// Width, Height, Resolution, Quality
// Quality [0-100], 100 is Maximum
// Create Resolution object
Resolution resolution = new Resolution(Convert.ToInt32(page.ArtBox.Width), Convert.ToInt32(page.ArtBox.Height));
JpegDevice device = new JpegDevice(resolution, 100);
// Convert a particular page and save the image to stream
device.Process(page, ms);
var newPage = newDocument.Pages.Add();
// Set margins so image will fit, etc.
newPage.PageInfo.Margin.Bottom = 0;
newPage.PageInfo.Margin.Top = 0;
newPage.PageInfo.Margin.Left = 0;
newPage.PageInfo.Margin.Right = 0;
// Create an image object
Aspose.Pdf.Image image1 = new Aspose.Pdf.Image();
// Set the image file stream
image1.ImageStream = ms;
// Add the image into paragraphs collection of the section
newPage.Paragraphs.Add(image1);
// YES, save after every page otherwise we get out of memory exception
newDocument.Save(txtFolder.Text + "\\out.pdf");
}
}
else
{
newDocument.Pages.Add(page);
newDocument.Save(txtFolder.Text + "\\out.pdf");
}
}
}
}
Qustions
1> The original PDF size is 18MB but the above code creates large file of size 163MB. I would like keep the size as it is or least somewhat near to original file size by keeping the same resolution.
2>Resolution class also takes int valueX and int valueY parameters. And JpegDevice class takes int width and int height parameters. However it does not specify unit, weathers its in points, pixels, inches etc?
As per Aspose.Pdf documentation
The conversion from point to pixel depends on an image's DPI (dots per
inch) property. For example, if an image's DPI is 96 (96 pixels for
each inch), and it is 100 points high, its height in pixels is (100 /
72) * 96 = 133.3. The general formula is: pixels = ( points / 72 ) *
DPI.
However that does not answer the question what unit the API is asking?
Original Issue
We get PDF from our clients. Some of the PDF we receive has rotation of 90 or 270 degree. However when you open them in Adobe or any other PDF viewer, the page's orientation is correct (it is not landscape or horizontal). All this mess i have to do becuase Aspose.Pdf cannot keep the same orientation when change the rotation to None. So suggested approach is convert PDF to Image and latter convert Image back to Pdf
https://forum.aspose.com/t/cannot-set-cropbox-on-rotated-page/201659/3
https://forum.aspose.com/t/remove-rotation-from-a-document-and-set-it-upright/29170

Line Drawing In landscape mode using iText

This is the code I use to draw a line.
double[] lineArray = annotation.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
cs.moveTo(x1, y1);
cs.lineTo(x2, y2);
Where cs is PdfAppearance, annotation is PdfAnnotation and rect is
PdfArray rect = annotation.getAsArray(PdfName.RECT);
This works ok in portrait. but come, landscape mode e.g. 270 rotation, the coordinates get misplaced. I also did a rotate via cs.transform() so my 0,0 would be rotated but it does nothing.
Any idea what could be lacking?
The source
This answer covers the updated source code provided by the OP via a google drive link in comments:
public static void main(String[] args) throws Exception {
PdfReader reader = new PdfReader("src");
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("dest"));
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
stamper.close();
reader.close();
}
private static void addAppearance(PdfStamper stamper, PdfAnnotation stamp, Rectangle location) {
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(location.getWidth(), location.getHeight());
PdfArray rect = stamp.getAsArray(PdfName.RECT);
Rectangle bbox = app.getBoundingBox();
double[] lineArray = stamp.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
app.moveTo(x1, y1);
app.lineTo(x2, y2);
app.stroke();
stamp.setAppearance(PdfName.N, app);
}
No appearance
The first observation when viewing the resulting PDF in Chrome is, as the OP put it in a comment:
nothing shows up
Inspecting the PDF the cause is clear: The annotation has no appearance stream. Thus, limited PDF viewers which only can show annotations by their appearance stream, not by their descriptive values, like the integrated viewer in Chrome don't show it.
This is due to the order in which the OP calls iText functionalities in his code:
[... create annotation object stamp ...]
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
So he first adds the annotation to the PDF by means of stamper.addAnnotation and thereafter creates an appearance and attaches it to the stamp object.
This order is wrong. In context with iText one has to be aware that the library attempts to write additions as early as possible to the output stream to reduce its memory footprint. (This by the way is one of the important features of iText in the context of server applications in which multiple PDFs may have to be processed in parallel.)
So already during stamper.addAnnotation(stamp, 1) the annotation is written to the output stream, and as it has no appearance yet, the annotation in the output stream is without appearance. The later addAppearance call only adds an appearance to the in-memory representation of the annotation which won't be serialized anymore.
Changing the order to
[... create annotation object stamp ...]
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
results in a PDF with a line drawn. Unfortunately not at the desired position, but that is another problem.
Wrong position
The reason why the line is both in the wrong location and has the wrong direction, is based in a feature of iText which has already been a topic in this answer and in this answer:
For rotated pages iText attempts to lift the burden of adding the rotation and translation to page content required to draw upright text and have the coordinate system origin in the lower left of the page of the users' shoulders, so that the users don't have to deal with page rotation at all. Consequently, it also does so for annotations.
As you already have the actual coordinates to use, this "help" by iText damages your annotation. As discussed in those other answers, there unfortunately is no explicit switch to turn off that mechanism; there is an easy work-around, though: before your manipulation simply remove the page rotation entry, and afterwards add it back again:
PdfReader reader = ...;
PdfStamper stamper = ...;
// hide the page rotation
PdfDictionary pageDict = reader.getPageN(1);
PdfNumber rotation = pageDict.getAsNumber(PdfName.ROTATE);
pageDict.remove(PdfName.ROTATE);
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
// add page rotation again if required
if (rotation != null)
pageDict.put(PdfName.ROTATE, rotation);
stamper.close();
reader.close();
This appears to create the annotation appearance as required.

Winforms Itext Ghost Script Rectangular coordinates selection

Using C# and Winforms, I want to display a PDF, select a rectangular region, and then extract that area of text from a number of PDFs. For displaying the PDF, I have a number of options...
Use an "Adobe PDF Reader" control to display the PDF - However, I cant use mouseover events and according to https://forums.adobe.com/thread/1640606 its just not possible to select a region.
Use a "WebBrowser" control to display the PDF, but it appears I have the same issue with mouseover events and cannot select a region.
Convert the PDF to an image (using ghostscript in my case) and displaying it in a picturebox. I'm finding the most success here, as I can now generate and record the coordinates of a rectangular region. When I take these coordinates and apply them to the PDF using Itext, I don't think my rectangular region translates correctly.
My question is, How do I render the GhostScripted image in a picture box maintaining the same ratios so that my coordinates will line up with the PDF?
Thank you in advance for the down votes!!
Here is the current state of my code... Everything works with the exception that my units are off in space somewhere. The action DOES return text, but it's never the text I selected. Im sure its a combination of the coordinate system / units and I will continue to try to understand this.
---- update
With a PDF at 0 deg rotation (portrait), I think the following holds true, or is at least working for me right now... User Units having not been changed, the coordinates taken from selecting in the picturebox need adjusting. The Y coordinates need to be subtracted from the overall height while the X coordinate remains the same.
iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(first.X, 3024-first.Y, last.X, 3024-last.Y);
This is picking text up exactly as expected on 0 deg rotated PDFs. On 90 deg rotated PDFs, the X and Y coordinates just need to be swapped.I am updating the code snippet below to show my working example.
using System;
using System.Drawing;
using System.Windows.Forms;
using Ghostscript.NET.Rasterizer;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
namespace formPdf
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileName; // The filename of the pdf
float width; // The width of the PDF in pixels
float hight; // the Height of the PDF in pixels
float rotation; // the Rotation of the PDF 0 or 90
float llx = 0; // The Lower Left X value for applying to the PDF
float lly = 0; // the Lower Left Y value for applying to the PDF
float urx = 0; // the Upper Right X value for applying to the PDF
float ury = 0; // the Upper Right Y value for applying to the PDF
// OnCLick event to open the file browser and select a file... The Width, Height and rotation values are set and the program
// is directed to render the First page of the pdf by calling the setPicture function
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
fileName = openFileDialog1.FileName;
PdfReader reader = new PdfReader(fileName);
iTextSharp.text.Rectangle dim = reader.GetPageSizeWithRotation(1);
width = dim.Width;
hight = dim.Height;
rotation = dim.Rotation;
setPicture(openFileDialog1.FileName);
} catch
{
// do nothing for now
}
}
}
// Using Ghostscript, the image is rendered to a picturebox. DPIs are set assuming the PDF default value is used
private void setPicture(string fileName)
{
GhostscriptRasterizer rasterizer = new GhostscriptRasterizer();
rasterizer.Open(fileName);
Image img = rasterizer.GetPage(72, 72, 1);
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
pictureBox1.Image = img;
}
// Declare point variables for the user defined rectangle indicating the locatoin of the PDF to be searched...
Point first = new Point();
Point last = new Point();
// The first point is collected on the MouseDown event
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
first = e.Location;
}
// The second point is collected on the mouse down event. Points to be applied to the PDF are adjusted based on the rotation of the PDF.
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
last = e.Location;
if (rotation == 0)
{
llx = first.X;
lly = hight - first.Y;
urx = last.X;
ury = hight - last.Y;
} else if(rotation == 90) {
llx = first.Y;
lly = first.X;
urx = last.Y;
ury = last.X;
}
gettext();
}
// the original PDF is opened with Itext and the text is extracted from t he defined location...
private void gettext()
{
PdfReader reader = new PdfReader(fileName);
iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(llx, lly, urx, ury);
RenderFilter[] renderfilter = new RenderFilter[1];
renderfilter[0] = new RegionTextRenderFilter(rect);
ITextExtractionStrategy textExtractionStrategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), renderfilter);
string text = PdfTextExtractor.GetTextFromPage(reader, 1, textExtractionStrategy);
iTextSharp.text.Rectangle mediabox = reader.GetPageSizeWithRotation(1);
MessageBox.Show("", text+" "+mediabox+" "+first+" "+last);
}
// Image Controls....
}
}

crop Left side of pdf using Itextsharp

I am trying to crop left side of pdf to 10 mm. i used below code
public void TrimLeft(string sourceFilePath, string outputFilePath)
{
PdfReader pdfReader = new PdfReader(sourceFilePath);
float width =(float) GetPDFwidth(sourceFilePath);
float height = (float)GetPDFHeight(sourceFilePath);
float widthTo_Trim = iTextSharp.text.Utilities.MillimetersToPoints(10);
// Set which part of the source document will be copied.
// PdfRectangel(bottom-left-x, bottom-left-y, upper-right-x, upper-right-y)
PdfRectangle rect = new PdfRectangle(0, 0, width - widthTo_Trim, height);
PdfRectangle rectLeftside = new PdfRectangle(0,0,width - widthTo_Trim, height);
using (var output = new FileStream(outputFilePath, FileMode.CreateNew, FileAccess.Write))
{
// Create a new document
Document doc = new Document();
// Make a copy of the document
PdfSmartCopy smartCopy = new PdfSmartCopy(doc, output);
// Open the newly created document
doc.Open();
// Loop through all pages of the source document
for (int i = 1; i <= pdfReader.NumberOfPages; i++)
{
// Get a page
var page = pdfReader.GetPageN(i);
// Apply the rectangle filter we created
page.Put(PdfName.CROPBOX, rectLeftside);
page.Put(PdfName.MEDIABOX, rectLeftside);
// Copy the content and insert into the new document
var copiedPage = smartCopy.GetImportedPage(pdfReader, i);
smartCopy.AddPage(copiedPage);
}
// Close the output document
doc.Close();
}
}
Its croping RHS of pdf. i tried with changing the coordinates
PdfRectangle rectLeftside = new PdfRectangle(0,0,width - widthTo_Trim, height);
but unable to get desired result.
How can i crop X mm left side
Making the hint in the comments an actual answer...
You create the new crop box rectangle like this:
PdfRectangle rectLeftside = new PdfRectangle(0,0,width - widthTo_Trim, height);
The constructor in question is:
/**
* Constructs a <CODE>PdfRectangle</CODE>-object.
*
* #param llx lower left x
* #param lly lower left y
* #param urx upper right x
* #param ury upper right y
*/
...
public PdfRectangle(float llx, float lly, float urx, float ury)
Thus, assuming your original PDF has a crop box with lower left coordinates (0,0), your code manipulates the upper right x, i.e. the right side of the box. You, on the other hand, actually want to manipulate the left side. Thus, you should use something like:
PdfRectangle rectLeftside = new PdfRectangle(widthTo_Trim, 0, width, height);
After the hint in comments, this also was the OP's solution.
Additional improvements
Using a PdfStamper
The OP uses a PdfSmartCopy instance and its method GetImportedPage to crop left side of pdf. While this already is better than the use of a plain PdfWriter for this task, the best choice for manipulating a single PDF usually is a PdfStamper: You don't have to copy anything anymore, you merely apply the changes. Furthermore the result internally is more like the original.
Determining boxes page by page
The OP in his code assumes
a constant size of all pages in the PDF (which he determines in his methods GetPDFwidth and GetPDFHeight) and
constant lower left coordinates (0,0) of the current crop box on all pages.
Neither of these assumptions is true for all PDFs. Thus, one should retrieve and manipulate the crop box of each page separately.
The code
public void TrimLeftImproved(string sourceFilePath, string outputFilePath)
{
PdfReader pdfReader = new PdfReader(sourceFilePath);
float widthTo_Trim = iTextSharp.text.Utilities.MillimetersToPoints(10);
using (FileStream output = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, output))
{
for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
Rectangle cropBox = pdfReader.GetCropBox(page);
cropBox.Left += widthTo_Trim;
pdfReader.GetPageN(page).Put(PdfName.CROPBOX, new PdfRectangle(cropBox));
}
}
}