I've built a method that tries to see if the resolution of all the embedded images in a given pdf is at least 300 PPI (print-worthy). What it does is cycle through each image on a page, and compare its width and height to the artbox width and height. It works successfully if there is only one image per page, but when there are multiple, the artbox size includes all of the images, throwing the numbers off.
I was hoping that somebody might have some idea of how to get the rectangle size the image is drawn in so I can compare correctly, or if there is an easier way to get the PPI of an image object (as it would be rendered in its rectangle, not in raw form).
this is the code for the aforementioned method
private static bool AreImages300PPI(PdfDictionary pg)
{
var res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
var xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
if (xobj == null) return true;
foreach (PdfName name in xobj.Keys)
{
PdfObject obj = xobj.Get(name);
if (!obj.IsIndirect()) continue;
var tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
var type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
var width = float.Parse(tg.Get(PdfName.WIDTH).ToString());
var height = float.Parse(tg.Get(PdfName.HEIGHT).ToString());
var artbox = (PdfArray) pg.Get(PdfName.ARTBOX);
var pdfRect = new PdfRectangle(float.Parse(artbox[0].ToString()), float.Parse(artbox[1].ToString()),
float.Parse(artbox[2].ToString()), float.Parse(artbox[3].ToString()));
if (PdfName.IMAGE.Equals(type) && (width < pdfRect.Width*300/72 || height < pdfRect.Height*300/72)
|| ((PdfName.FORM.Equals(type) || PdfName.GROUP.Equals(type)) && !AreImages300PPI(tg)))
{
return false;
}
}
return true;
}
for reference, here is the method that calls it:
internal static List<string> GetLowResWarnings(string MergedPDFPath)
{
var returnlist = new List<string>();
using (PdfReader pdf = new PdfReader(MergedPDFPath))
{
for (int pageNumber = 1; pageNumber <= pdf.NumberOfPages; pageNumber++)
{
var pg = pdf.GetPageN(pageNumber);
if (!AreImages300PPI(pg))
returnlist.Add(pageNumber.ToString());
}
}
return returnlist;
}
Thanks for any help you can provide.
Can I put you down a totally different path? You're looking at images that live in the global file but you're not seeing how they're used in a page.
iTextSharp has a class called
iTextSharp.text.pdf.parser.PdfReaderContentParser
that can walk aPdfReader
and tell you things about it. You can subscribe to information by implementing theiTextSharp.text.pdf.parser.IRenderListener
interface. For each image that it encounters theRenderImage
method of your class will be called with aniTextSharp.text.pdf.parser.ImageRenderInfo
object. From this object you can get both the actual image as well as the current transformation matrix which will tell you how the image is placed into the document.Using this information you could create a class like this:
It uses this helper class to store our information:
Using it is really simple then:
EDIT
From @BrunoLowagie's comments below I've updated the above to remove the "magic 72" and actually try querying the document to see if this has been overridden. Very unlikely to happen but someone in a year or two will find some obscure PDF and complain that this code doesn't work so better safe than sorry.