2012年11月27日火曜日

Image Quality using Itext


Is possible to generate a PDF document using Itext which contains imgages with a resolution of 150 dpi.

(Seems to be that Itext is reducing the quality of images to 72 dpi.)

=>Answer

See this thread, specifically this post which explains that PDFs don't have a concept of DPI. This threadis a good read, too.

Essentially (and this isn't technically accurate but close enough) by default, when you place an image using iText assume for every 72 pixels that 1 inch of printed space will be used. If you want to change this rule then you need to change the image's matrix. You can either do this directly (which is hard) or use the scaling methods (much easier) such as scaleAbsolute(), scaleAbsoluteWidth(),scaleAbsoluteHeight(), scalePercentage() and scaleToFit().

Changing the matrix does not change the image in any way, it only (essentially) changes the definition of how many pixels to cram into an inch, which when printed you would call DPI. (You're actually changing the relationship between image pixels and the document's user units but you don't really need to know that.) So by default, a 200x200 pixel image placed by iText should print out at about 2 3/4". If you callscaleAbsolute(100,100) it will cram those 200 pixels into 100 "user units" giving you an effective DPI of 144 and print at about 1 3/8".

All of this can really make your head hurt, especially if you're used to imaging programs such as Adobe Photoshop that allow you to set a "resolution" for some image types. Unfortunately this is more of a "printing hint" and doesn't really change the file that much. A 100x100 pixel image at 72DPI is the exact same as 100x100 pixel at 300DPI, except when printed. iText completely ignores this printing hint, either because that's all it is or because not all image formats support it. (Not directly related, but one common myth is that "Save for Web" makes images 72DPI when reality it completely strips the resolution information from the file.)

Ignoring what's actually happening, to get the image to be what you think of as 150 DPI, take the source image's width and height in pixels and multiple those by 0.48 (72 divided by 150) and pass those new numbers to scaleAbsolute().

There is a method on the Image class called setDPI but as far as I can tell it doesn't actually have any effect on the placement of the image and is more of an informational thing.

 

=>Answer

>Well basically I have an image that has a scale bar of 2.91
         At what resolution?   72dpi or 96dpi?      >but when it is inserted into the pdf document it is being displayed as 3.4cm,             Are you providing a transform when you place it into the PDF?      >  if the pdf does not have a dpi then where has this default of   > 72dpi arisen from?             Because the default "transformation matrix" in a PDF is 72   USER UNITS to an inch, but that's not an actual "dots per inch" (dpi)   since vector and textual data have no "dots".             When placing an image, unless you specify a transform, the   pixels in the image are assumed to be in user units (so there would   be 72 of them to an inch, which would be logically equivalent to   72dpi).  If you wish to adjust the relationship of pixels to user   units for this particular image, then you apply a transform when you   place the image.
 

2012年11月26日月曜日

iText で PDF ファイルの情報 (プロパティ) の指定と暗号化

PDFのプロパティ指定と暗号化のサンプル。


import java.io.*;
import java.util.*;

import com.lowagie.text.*;
import com.lowagie.text.pdf.*;

public class PdfMoreInfoSample {

public static void main(String[] args) {

try {
// 適当にPDFデータを作る
byte[] pdfdata = createPdfData("hello");

// PDFデータのプロパティを表示
HashMap moreinfo = getMoreInfo(pdfdata);
for (Iterator it = moreinfo.keySet().iterator(); it.hasNext();) {
Object key = it.next();
Object val = moreinfo.get(key);
System.out.println(key + "=" + val);
// 以下のようなデータが出力される
// ModDate=D:20070401010203+09'00'
// Producer=iText 2.0.4 (by lowagie.com)
// CreationDate=D:20070401010203+09'00'
}

// PDFのプロパティ情報を構築
// 値には文字列しか入れられない(ClassCastExceptionが発生したり)
HashMap addedMoreInfo = new HashMap();

// 文書のタイトル
addedMoreInfo.put(new String("Title"), "the Title");

// 著者
addedMoreInfo.put("Author", "the Author");

// PDF文書の主題
addedMoreInfo.put("Subject", "the Subject");

// キーワード
addedMoreInfo.put("Keywords", "the Keywords");

// リソースを作成したツール名称
addedMoreInfo.put("Creator", "the Creator");

// PDF文書を作成したツール名称(iTextのドキュメントには変更しないことが推奨されていた)
// PdfDocument クラスのソースコード(version 2.0.4)には
// "you can not change the name of the producer" とあるが……
addedMoreInfo.put("Producer", "the Producer");

// PDF文書の作成日
// PdfDocument クラスのソースコード(version 2.0.4)には
// "you can not set the creation date, only reset it" とあるが……
addedMoreInfo.put("CreationDate", "2011/11/11 11:11:11");
// PdfDate クラスを使う必要はない(むしろイマイチよくない)
// Calendar cdcal = Calendar.getInstance();
// cdcal.set(2011, Calendar.APRIL, 5, 6, 7, 8);
// PdfDate cd = new PdfDate(cdcal);
// addedMoreInfo.put("CreationDate", cd.getW3CDate());
// 実は、日時以外も設定可能
// addedMoreInfo.put("CreationDate", "abcde");

// PDF文書の最終修正日
addedMoreInfo.put("ModDate", "2012/12/12 12:12:12");

// PDFデータにプロパティとPDFのバージョンを設定
pdfdata = setMoreInfo(pdfdata, addedMoreInfo, PdfWriter.VERSION_1_2);

// パスワード制限は最後に設定すること
// setMoreInfo で制限が解除されてしまう
pdfdata = setEncryption(pdfdata, "hogehoge");

// PDFをファイルへ出力
out(pdfdata, "20070802_moreinfo.pdf");

} catch (Exception e) {
e.printStackTrace();
}

}

private static byte[] createPdfData(String text) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, baos);
document.open();
Paragraph p = new Paragraph(text);
document.add(p);
writer.close();
document.close();
byte[] pdf = baos.toByteArray();
return pdf;
}

static HashMap getMoreInfo(byte[] pdfdata) throws Exception {
PdfReader reader = new PdfReader(pdfdata);
HashMap baseInfo = reader.getInfo();
reader.close();
return baseInfo;
}

// PDF文書プロパティ指定
static byte[] setMoreInfo(byte[] pdfdata, HashMap info, char version)
throws Exception {
PdfReader reader = new PdfReader(pdfdata);
HashMap baseInfo = reader.getInfo();
baseInfo.putAll(info);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// PdfStamper には setThumbnail とか setTransition とかおもしろそうなメソッドが用意されている
PdfStamper pdfStamper = new PdfStamper(reader, baos, version);
pdfStamper.setMoreInfo(baseInfo);
pdfStamper.close();
reader.close();
return baos.toByteArray();
}

// 暗号化
static byte[] setEncryption(byte[] pdfdata, String password)
throws Exception {
PdfReader reader = new PdfReader(pdfdata);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper pdfStamper = new PdfStamper(reader, baos);
// 印刷だけ許可&パスワード設定
pdfStamper.setEncryption(true, null, password, PdfWriter.AllowPrinting);
pdfStamper.close();
reader.close();
return baos.toByteArray();
}

private static void out(byte[] pdfdata, String file) throws Exception {
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
os.write(pdfdata);
os.flush();
}

}