I have a piece of code to put an image watermark into an existing pdf. I am looking for a way to calculate the scale percentage of the watermark image
public void MixFiles(String wmrk, String src, String dest)
{
string watermarkedFile = dest;
PdfReader pdfReader = new PdfReader(src);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(dest, FileMode.Create, FileAccess.Write, FileShare.None));
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(wmrk);
PdfContentByte waterMark;
for (int pageIndex = 1; pageIndex <= pdfReader.NumberOfPages; pageIndex++)
{
waterMark = pdfStamper.GetOverContent(pageIndex);
// the scale percent is found by trial and error how can I calculate it??
img.ScalePercent(24f);
img.SetAbsolutePosition(0f, 0f);
waterMark.AddImage(img);
}
pdfStamper.FormFlattening = true;
pdfStamper.Close();
}
My code works so far but what happens with other watermark image. What does the scale proportion depend on? The watermark image is a png with a size of 210x297mm the source pdf to bestamped has also pages with 210x297mm both have a resolution of 300 dpi.
I found out that itext uses internaly a 72 dpi resolution. The original resolution of the watermark png is 300 dpi. So 72/300 results with 0.24.
I tried with a 400 dpi watermark and get the expected result with 72/400= 0.18.
For unknown watermark resolution I now use
System.Drawing.Image newImage = System.Drawing.Image.FromFile(wmrk);
var reso=newImage.HorizontalResolution;
float scalepercent = (72/reso)*100;
img.ScalePercent(scalepercent);
Related
Surprisingly in Unity, for years the only way to simply scale an actual PNG is to use the very awesome library http://wiki.unity3d.com/index.php/TextureScale
Example below
How do you scale a PNG using Unity5 functions? There must be a way now with new UI and so on.
So, scaling actual pixels (such as in Color[]) or literally a PNG file, perhaps downloaded from the net.
(BTW if you're new to Unity, the Resize call is unrelated. It merely changes the size of an array.)
public WebCamTexture wct;
public void UseFamousLibraryToScale()
{
// take the photo. scale down to 256
// also crop to a central-square
WebCamTexture wct;
int oldW = wct.width; // NOTE example code assumes wider than high
int oldH = wct.height;
Texture2D photo = new Texture2D(oldW, oldH,
TextureFormat.ARGB32, false);
//consider WaitForEndOfFrame() before GetPixels
photo.SetPixels( 0,0,oldW,oldH, wct.GetPixels() );
photo.Apply();
int newH = 256;
int newW = Mathf.FloorToInt(
((float)newH/(float)oldH) * oldW );
// use a famous Unity library to scale
TextureScale.Bilinear(photo, newW,newH);
// crop to central square 256.256
int startAcross = (newW - 256)/2;
Color[] pix = photo.GetPixels(startAcross,0, 256,256);
photo = new Texture2D(256,256, TextureFormat.ARGB32, false);
photo.SetPixels(pix);
photo.Apply();
demoImage.texture = photo;
// consider WriteAllBytes(
// Application.persistentDataPath+"p.png",
// photo.EncodeToPNG()); etc
}
Just BTW it occurs to me I'm probably only talking about scaling down here (as you often have to do to post an image, create something on the fly or whatever.) I guess, there would not often be a need to scale up in size an image; it's pointless quality-wise.
If you're okay with stretch-scaling, actually there's simpler way by using a temporary RenderTexture and Graphics.Blit. If you need it to be Texture2D, swapping RenderTexture.active temporarily and read its pixels to Texture2D should do the trick. For example:
public Texture2D ScaleTexture(Texture src, int width, int height){
RenderTexture rt = RenderTexture.GetTemporary(width, height);
Graphics.Blit(src, rt);
RenderTexture currentActiveRT = RenderTexture.active;
RenderTexture.active = rt;
Texture2D tex = new Texture2D(rt.width,rt.height);
tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
tex.Apply();
RenderTexture.ReleaseTemporary(rt);
RenderTexture.active = currentActiveRT;
return tex;
}
I am using iTextSharp to convert & stitch single-page TIF files to multi-page PDF file. The single-page TIF files are of different bit depths and compressions.
Here is the code-
private void button1_Click(object sender, EventArgs e)
{
List<string> TIFfiles = new List<string>();
Document document;
PdfWriter pdfwriter;
Bitmap tifFile;
pdfFilename = <file path>.PDF;
TIFfiles = <load the path to each TIF file in this array>;
//Create document
document = new Document();
// creation of the different writers
pdfwriter = PdfWriter.GetInstance(document, new System.IO.FileStream(pdfFilename, FileMode.Create));
document.Open();
document.SetMargins(0, 0, 0, 0);
foreach (string file in TIFfiles)
{
//load the tiff image
tifFile = new Bitmap(file);
//Total number of pages
iTextSharp.text.Rectangle pgSize = new iTextSharp.text.Rectangle(tifFile.Width, tifFile.Height);
document.SetPageSize(pgSize);
document.NewPage();
PdfContentByte cb = pdfwriter.DirectContent;
tifFile.SelectActiveFrame(FrameDimension.Page, 0);
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(tifFile, ImageFormat.Tiff);
// scale the image to fit in the page
img.SetAbsolutePosition(0, 0);
cb.AddImage(img);
}
document.Close();
}
This code works well and stitches & converts tifs to PDF. Issue is with processing time and pdf file size that it creates when processing certain types of TIFs.
For e.g.
Original TIF --> B&W/Bit depth 1/Compression CCITT T.6 --> Faster processing, PDF file size is ~1.1x times the TIF file size.
Original TIF --> Color/Bit depth 8/Compression LZW --> Faster processing, PDF file size is ~1.1x times the TIF file size.
Original TIF --> Color/Bit depth 24/Compression JPEG--> Slow processing, PDF file size is ~12.5x times the TIF file size.
Why doesn't converting Color/Bit depth 24/Compression JPEG files gives similar result as other tif files?
Moreover, this issue is only with iTextSharp. I had a colleague test the same set of TIF samples using Java-iText and the resulting PDF was of smaller size (1.1x times) and had faster processing.
Unfortunately, I need to use .Net for this TIF to PDF conversion, so am stuck with using iTextSharp.
Any ideas/suggestions on how to get those Compression JPEG TIF files to create smaller size PDFs as it does for other TIF compressions?
Appreciate your help!
Regards,
AG
I was able to reproduce your problem with the code you supplied, but found that the problem went away once I used Image.GetInstance instead of the bitmap used in your sample. When using the code below, the file size and run time was the same between Java and C#. If you have any questions about the sample, don't hesitate to ask.
List<string> TIFfiles = new List<string>();
Document document;
PdfWriter pdfwriter;
iTextSharp.text.Image tifFile;
String pdfFilename = pdfFile;
TIFfiles = new List<string>();
TIFfiles.Add(tifFile1);
TIFfiles.Add(tifFile2);
TIFfiles.Add(tifFile3);
TIFfiles.Add(tifFile4);
TIFfiles.Add(tifFile5);
TIFfiles.Add(tifFile6);
TIFfiles.Add(tifFile7);
//Create document
document = new Document();
// creation of the different writers
pdfwriter = PdfWriter.GetInstance(document, new System.IO.FileStream(pdfFilename, FileMode.Create));
document.Open();
document.SetMargins(0, 0, 0, 0);
int i = 0;
while (i < 50)
{
foreach (string file in TIFfiles)
{
//load the tiff image
tifFile = iTextSharp.text.Image.GetInstance(file);
//Total number of pages
iTextSharp.text.Rectangle pgSize = new iTextSharp.text.Rectangle(tifFile.Width, tifFile.Height);
document.SetPageSize(pgSize);
document.NewPage();
PdfContentByte cb = pdfwriter.DirectContent;
// scale the image to fit in the page
tifFile.SetAbsolutePosition(0, 0);
cb.AddImage(tifFile);
}
i++;
}
document.Close();
Adding a time series bar chart for a large time span in PDF results in large file size like 50 MB or more depending on the data points. Here are the code samples:
Adding chart to PDF
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
document.open();
PdfContentByte cb = writer.getDirectContent();
float width = PageSize.A4.getWidth();
float height = PageSize.A4.getHeight() / 2;
PdfTemplate bar = cb.createTemplate(width, height);
Graphics2D g2d2 = new PdfGraphics2D(bar, width, height);
Rectangle2D r2d2 = new Rectangle2D.Double(0, 0, width, height);
getBarChart().draw(g2d2, r2d2);
g2d2.dispose();
cb.addTemplate(bar, 0, 0);
document.close();
Creating chart
JFreeChart getBarChart() {
TimeSeries series = new TimeSeries("Data");
GregorianCalendar cal = new GregorianCalendar();
for (int i=0; i<365*24; i++) {
cal.add(Calendar.HOUR, 1);
series.addOrUpdate(new Millisecond(cal.getTime()), Math.random());
}
XYPlot plot = new XYPlot();
plot.setDataset(new XYBarDataset(new TimeSeriesCollection(series), 10));
plot.setRenderer(new XYBarRenderer());
plot.setRangeAxis(new NumberAxis());
plot.setDomainAxis(new DateAxis());
return new JFreeChart(plot);
}
How can I reduce the file size?
Using itextpdf-5.4.4 and jfreechart-1.0.15.
While inspecting the PDF provided by the OP, it quickly becomes apparent that it is full of Pattern definitions and the like used for drawing pretty bars. To reduce the PDF size, therefore, simplifying the bar design is the way to go.
In the case at hand this can be done by setting a different default bar painter (using XYBarRenderer.setDefaultBarPainter()). The initial value of that attribute is a GradientXYBarPainter, but using gradients for so small bars makes the number of required drawing operations and operators explode while only making a difference at a gigantic zoom level, if at all.
As already worked out in the comments to the question, using the StandardXYBarPainter instead solves the size issues.
You can try to set full compression and compare the difference:
PdfReader reader = new PdfReader(new FileInputStream("in.pdf"));
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("out.pdf"));
int total = reader.getNumberOfPages() + 1;
for ( int i=1; i<total; i++) {
reader.setPageContent(i + 1, reader.getPageContent(i + 1));
}
stamper.setFullCompression();
stamper.close();
I'm lost at the moment.
What I try to accomplish is adding one PDF on another (like a watermark).
The problem is that I dont seems to understand the coordinate system that is used because
my watermark just behaves unexpected.
The two PDFs have different dimensions.
My target has the following dimensions:
595 height
842 width
The PDF that shall be added has this dimension:
41 height
552 width
In my code I do the following:
public bool AddPdf(ref PdfReader pdfSource, ref PdfReader pdfTarget, ref FileStream destination)
{
PdfStamper stamper = null;
try
{
stamper = new PdfStamper( pdfSource, destination );
PdfImportedPage importatedPage = stamper.GetImportedPage(pdfTarget, 1);
PdfContentByte background;
for (int iPage = 1; iPage <= pdfSource.NumberOfPages; iPage++)
{
background = stamper.GetOverContent(iPage);
background.AddTemplate(importatedPage, 0, 0 + importHeight);
}
}
When I do this I would expect my watermark to appear in the bottom left.
Instead it is somewhere of the page (I dont see it). Just for testing I hardcoded 600 as y position and then it is centered vertically on the page.
Can someone give me a tip please?
So i solved the issue.
The problem was that the sourcepdf had a cropbox - i only needed to correct my x and y position with that information:
PdfStamper stamper = null;
try
{
stamper = new PdfStamper(pdfSource, destination);
PdfImportedPage importatedPage = stamper.GetImportedPage(pdfTarget, 1);
PdfContentByte background;
for (int iPage = 1; iPage <= pdfSource.NumberOfPages; iPage++)
{
background = stamper.GetOverContent(iPage);
// here comes the important part
Rectangle cropBox = pdfSource.GetCropBox(iPage);
float xCorrected = 0 + cropBox.Left;
float yCorrected = 0 + cropBox.Bottom;
background.AddTemplate(importatedPage, xCorrected, yCorrected);
}
}
Take in mind that in case the pdf that you want to stamp on your original has also a cropbox, you need to reduce the x,y by x,y of that cropbox again.
I'm using iTextSharp 5.0.6 to read an existing PDF, iterate each page stamping text on each, and then writing out the newly stamped PDF. The issue I'm faced with is that this isn't working 100% of the time. For some PDFs every page is stamped as expected, for others most pages are stamped while some are not. Seems as if there's potentially an issue where the stamper's GetOverContent() is not returning the top-most layer, but that's just an assumption. Has anyone had a similar issue?
using iTextSharp.text;
using iTextSharp.text.pdf;
const string WATERMARK_TEXT = "John Doe";
static void Main(string[] args)
{
string masterPdf = "master.pdf";
string pdfToCreate = "watermark.pdf";
byte[] bytes = StampPDF(masterPdf);
using (FileStream stream = new FileStream(pdfToCreate, FileMode.Create))
{
stream.Write(bytes, 0, bytes.Length);
}
}
static byte[] StampPDF(string PdfPath)
{
using (MemoryStream memoryStream = new MemoryStream())
{
PdfReader reader = new PdfReader(PdfPath);
int pageCount = reader.NumberOfPages;
PdfStamper stamper = new PdfStamper(reader, memoryStream);
float fontSize = 9;
float textAngle = 0f;
BaseFont font = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.WINANSI, BaseFont.EMBEDDED);
BaseColor backgroundColor = new BaseColor(0, 0, 0);
BaseColor fontColor = new BaseColor(255, 255, 255);
float padding = 2f;
float fontWidth = font.GetWidthPoint(WATERMARK_TEXT, fontSize);
iTextSharp.text.Rectangle pageSize;
PdfContentByte pageContents;
for (int i = 1; i <= pageCount; i++)
{
pageSize = reader.GetPageSize(i);
pageContents = stamper.GetOverContent(i);
//draw a rectangle
pageContents.SetColorFill(backgroundColor);
pageContents.MoveTo(pageSize.Width - (fontWidth + padding), 0f);
pageContents.LineTo(pageSize.Width, 0f);
pageContents.LineTo(pageSize.Width, 14f);
pageContents.LineTo(pageSize.Width - (fontWidth + padding), 14f);
pageContents.Fill();
//drop our watermark on top of the rectangle we just created
pageContents.BeginText();
pageContents.SetColorFill(fontColor);
pageContents.SetFontAndSize(font, fontSize);
pageContents.ShowTextAligned(PdfContentByte.ALIGN_LEFT, WATERMARK_TEXT, pageSize.Width - fontWidth, 4, textAngle);
pageContents.EndText();
}
stamper.Close();
reader.Close();
return memoryStream.ToArray();
}
}
For those that may encounter the same problem the key is inspecting the CropBox. Since the dimensions of a PDF's CropBox may be less than that of its PageSize you need to conditionally use one or the other. So, based on the code sample above the for loop would be altered as so:
for (int i = 1; i <= pageCount; i++)
{
mediaBox = reader.GetPageSize(i);
cropBox = reader.GetCropBox(i);
overContent = stamper.GetOverContent(i);
if (cropBox != null && (cropBox.Width < mediaBox.Width || cropBox.Height < cropBox.Height))
mediaBox = cropBox;
//draw a rectangle
overContent.SetColorFill(backgroundColor);
overContent.MoveTo(mediaBox.Right - (fontWidth + fontPadding), mediaBox.Bottom);
overContent.LineTo(mediaBox.Right, mediaBox.Bottom);
overContent.LineTo(mediaBox.Right, mediaBox.Bottom + rectangleHeight);
overContent.LineTo(mediaBox.Right - (fontWidth + fontPadding), mediaBox.Bottom + rectangleHeight);
overContent.ClosePathFillStroke();
//drop our watermark on top of the rectangle we just created
overContent.BeginText();
overContent.SetColorFill(fontColor);
overContent.SetFontAndSize(font, fontSize);
overContent.ShowTextAligned(PdfContentByte.ALIGN_LEFT, WATERMARK_TEXT, mediaBox.Right - fontWidth, mediaBox.Bottom + (rectangleHeight - fontSize), textAngle);
overContent.EndText();
}
You've made two mistakes:
You're assuming that the pages aren't rotated, but they can be: 90, 180, 270. Note that I've never seen a 180 page, but its legal. When drawing to a rotated page, you have to take that rotation into account when drawing on it. Fun with transformation matrices.
You're assuming that the page's (unrotated) lower left corner is 0,0. You're basing your measurements on the page's width and height (close), but aren't adjusting for any offset in that bottom left corner.
There are three ways to do a landscape page:
11"x8.5"
8.5"x11" # 90 degrees rotation
8.5"x11" # 270 degrees rotation
Technically, a 4th way is to build an 11x8.5 # 180, but anyone writing such code should be Punished. A lot.
There are various SO questions floating about that give details on how to deal with page rotation. Going by your code, I'd say you'll figure out the llx,lly thing pretty quickly.