I am creating a PDF and writing the stream in response. Before writing in the stream, I want to add a background image as watermark in all the pages so that PDF document flushed through response is the final one with watermark.
Hi this is my code sample. Any help would be much appriciated
private static String generatePDF(HttpServletRequest request, HttpServletResponse response, String fileName) throws Exception
{
Document document = null;
PdfWriter writer = null;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(fileName);
Document document = new Document(PageSize.A4);
writer = PdfWriter.getInstance(document, fos);
document.open();
/**
* Adding tables and cells and other stuff required
**/
return pdfFileName;
} catch (Exception e) {
FileUtil.deleteFile(fileName);
throw e
} finally {
if (document != null) {
document.close();
}
fos.flush();
}
}
I now would like to add a background image using the below code and write the output PDF to the same stream
PdfReader sourcePDFReader = null;
try
{
sourcePDFReader = new PdfReader(sourcePdfFileName);
int noOfPages = sourcePDFReader.getNumberOfPages();
PdfStamper stamp = new PdfStamper(sourcePDFReader, new FileOutputStream(destPdfFileName));
int i = 0;
Image templateImage = Image.getInstance(templateImageFile);
templateImage.setAbsolutePosition(0, 0);
PdfContentByte tempalteBytes;
while (i < noOfPages) {
i++;
tempalteBytes = stamp.getUnderContent(i);
tempalteBytes.addImage(templateImage);
}
stamp.close();
return destPdfFileName;
} catch (Exception ex) {
LOGGER.log(Level.INFO, "Error when applying tempalte image as watermark");
} finally {
if (sourcePDFReader != null) {
sourcePDFReader.close();
}
}
I solved this using Bruno's first (recommended) approach.
1) Create a page event helper with an onEndPage event:
class PDFBackground extends PdfPageEventHelper {
#Override
void onEndPage(PdfWriter writer, Document document) {
Image background = Image.getInstance("myimage.png");
// This scales the image to the page,
// use the image's width & height if you don't want to scale.
float width = document.getPageSize().getWidth();
float height = document.getPageSize().getHeight();
writer.getDirectContentUnder()
.addImage(background, width, 0, 0, height, 0, 0);
}
}
2) When creating your writer, register your page event helper:
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
writer.setPageEvent(new PDFBackground());
I have solved this with Bruno's second option. Here is the code.
public static String addBackgroundImageToPDF(ByteArrayOutputStream bos, String destPdfFileName, String templateImageFile)
{
PdfReader sourcePDFReader = null;
try
{
sourcePDFReader = new PdfReader(bos.toByteArray());
int noOfPages = sourcePDFReader.getNumberOfPages();
PdfStamper stamp = new PdfStamper(sourcePDFReader, new FileOutputStream(destPdfFileName));
int i = 0;
Image templateImage = Image.getInstance(templateImageFile);
templateImage.setAbsolutePosition(0, 0);
PdfContentByte tempalteBytes;
while (i < noOfPages)
{
i++;
tempalteBytes = stamp.getUnderContent(i);
tempalteBytes.addImage(templateImage);
}
stamp.close();
return destPdfFileName;
}
catch (Exception ex)
{
LOGGER.log(Level.INFO, "Error when applying template image as watermark");
}
finally
{
if (sourcePDFReader != null)
{
sourcePDFReader.close();
}
}
}
You can choose between two options:
Use the background image in a page event (to the 'under' content in the onEndPage() method)/
Create the first PDF in memory, then add the background image in a second pass using the code you posted.
I prefer option 1.
Related
I am creating a PDF document in tDEST by itext library. Then I am cleaning trailing spaces in resize functions writing a new document to DEST. If I want to print tDest document, it works fine but if I try to print a DEST file, it does not print anything and does not throw any exception.
Here is my main class:
String DEST = System.getProperty("user.home") + "\\Documents\\invoice.pdf";
String tDEST = System.getProperty("user.home") + "\\Documents\\temp.pdf";
public static void main(String... args) {
....
resize();
PDDocument document = null;
try {
document = PDDocument.load(new File(DEST));
} catch (IOException e) {
e.printStackTrace();
}
PrinterJob job = PrinterJob.getPrinterJob();
job.setPageable(new PDFPageable(document));
try {
PrintService myPrintService = findPrintService(Commons.printer.getName());
job.setPrintService(myPrintService);
job.print();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Printer ishlamayapti");
}
....
}
public void resize() {
try {
FileOutputStream fileOutputStream = new FileOutputStream(DEST);
PdfReader reader = new PdfReader(tDEST);
com.itextpdf.text.Rectangle pageSize = reader.getPageSize(1);
PdfStamper stamper = new PdfStamper(reader, fileOutputStream);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
TextMarginFinder finder = parser.processContent(1, new TextMarginFinder());
PdfDictionary page = reader.getPageN(1);
page.put(PdfName.CROPBOX, new PdfArray(new float[]{pageSize.getLeft(), finder.getLly(), pageSize.getRight(), pageSize.getTop()}));
stamper.markUsed(page);
stamper.flush();
stamper.close();
reader.close();
fileOutputStream.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
So, I am downloading the profile picture from the Google SIgn-in api and I save it to a hidden file. The problem is that when I try to retrieve it, it throws me: D/skia: --- Failed to create image decoder with message 'unimplemented'. However when I retrieve an image from FireBaseStorage and save that one to the hidden file I can retrieve it whithout any problems.
I tried BitmapFactory.decodeByteArray(), but then I had a message telling me skia wasn't able to decode the file and it returned null.
The method I use to retrieve the profile picture and call the method that will save the file
private void getUsersPic() {
Bitmap profilePic;
try {
InputStream in = new URL(AppData.getUser().getPicture()).openConnection().getInputStream();
profilePic = BitmapFactory.decodeStream(in);
int size = profilePic.getRowBytes()*profilePic.getHeight();
ByteBuffer b = ByteBuffer.allocate(size);
byte[] bytes = new byte[size];
profilePic.copyPixelsToBuffer(b);
b.position(0);
b.get(bytes, 0, bytes.length);
SaveBitmapToFile.saveBitmap(bytes , AppData.getUser().getName()+AppData.getUser().getLastName());
} catch(Exception e) {
System.out.println("Get profile pic: "+e.toString());
}
}
Save the file
public static void saveBitmap(byte[] bitmap, String key) {
String path = AppData.getAppContext().getFilesDir()+"/.image"+"/";
File fileDir = new File(path);
if(!fileDir.isDirectory())
fileDir.mkdirs();
try {
File bitmapDir = new File(fileDir+"/"+key);
bitmapDir.createNewFile();
FileOutputStream stream = new FileOutputStream(bitmapDir);
stream.write(bitmap);
stream.close();
} catch (IOException e) {
System.out.println("Problem creating file "+e.toString()+ " Directory: "+fileDir);
}
}
Retrieve and return a bitmap
public static Bitmap getBitmap(String key) {
File file = new File(AppData.getAppContext().getFilesDir()+"/.image/"+key);
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
return BitmapFactory.decodeStream(buf);//BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} catch(Exception e) {
System.out.println("Exception getting bitmap: "+e.toString());
return null;
}
}
The last method should return a Bitmap and it is doing it. It is just not working when the image comes from the Google Sign-in api.
As pskink said in the comment of the post, I had to use compress() instead of copyPixelToBuffer(). Here is my updated method:
private void getUsersPic() {
Bitmap profilePic;
try {
InputStream in = new URL(AppData.getUser().getPicture()).openConnection().getInputStream();
profilePic = BitmapFactory.decodeStream(in);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
profilePic.compress(Bitmap.CompressFormat.PNG, 100, stream);
SaveBitmapToFile.saveBitmap(stream.toByteArray() , AppData.getUser().getName()+AppData.getUser().getLastName());
} catch(Exception e) {
System.out.println("Get profile pic: "+e.toString());
}
}
So far I have tried,
document.open();
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("sampleImage/idcard.jpeg").getFile());
String str = "<b>bold</b><img src='"+file.getAbsolutePath()+"'>";
InputStream is = new ByteArrayInputStream(str.getBytes());
XMLWorkerHelper.getInstance().parseXHtml(writer, document, is);
document.close();
Pdf that generated by this code is only bold, image is not displayed in pdf.
I have to convert HTML String to pdf.
I did search before asking question but no luck.
Have you tried Image object from itext (com.itextpdf.text.Image), it has scale percent that you can use to adjust sizing.
Image image = Image.getInstance(url));
image.scalePercent(Integer(15).toFloat())
document.add(image)
After so much of R&D, I have decided to use itextpdf custom tag process, glad I was successful. My solution was to pass the unique id of the entity in a tag and retrieve bytes, then create the image then add it to the document.
ByteArrayOutputStream byteArrayOutputStreaml = new ByteArrayOutputStream();
Document document = new Document(PageSize.A4, 36, 36, 120, 36);
PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStreaml); // Do this BEFORE
HeaderFooterPageEvent event = new HeaderFooterPageEvent("", "", "");
writer.setPageEvent(event);
document.open();
CSSResolver cssResolver =
XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
//custom tag processor
TagProcessorFactory factory = Tags.getHtmlTagProcessorFactory();
factory.addProcessor(
new Span(){
#Override
public List<Element> end(WorkerContext ctx, Tag tag, List<Element> l) {
List<Element> list = new ArrayList<Element>(1);
try {
list.add(getImageChunk(ctx, tag.getAttributes()));
} catch (BadElementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
},
"ean");
htmlContext.setTagFactory(factory);
htmlContext.autoBookmark(false);
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
//here employee is my entity's object which has byte[] property.
String imgStr = "<b>bold</b><ean value="+employee.getId()+"/>";
p.parse(new ByteArrayInputStream(imgStr.getBytes()));
document.close();
public Chunk getImageChunk(WorkerContext ctx, Map<String, String> attributes) throws
BadElementException, MalformedURLException, IOException {
MapContext mc;
try {
mc = (MapContext)ctx.get("com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline");
} catch (NoCustomContextException ex) {
throw new ExceptionConverter(ex);
}
PdfWriter writer = (PdfWriter) mc.get("WRITER");
//employeeService is service layer of three tier architecture
Image img = Image.getInstance(employeeService.getEmployeeById(Integer.parseInt(attributes.get("value"))).getEmployeePhoto());
img.scaleAbsolute(80, 80);
return new Chunk(img, 0, 0, true);
}
Please let me know if any issues, any suggestions heartly welcome. Thanks.
Idea has taken from https://developers.itextpdf.com/ja/examples/xml-worker/custom-tag-html
I am trying to append a text and screenshot to the existing word file. But every time I execute the below code I am getting error as :
org.apache.poi.EmptyFileException: The supplied file was empty (zero bytes long) at
org.apache.poi.util.IOUtils.peekFirstNBytes(IOUtils.java:74) at
org.apache.poi.util.IOUtils.peekFirst8Bytes(IOUtils.java:57) at
org.apache.poi.poifs.filesystem.FileMagic.valueOf(FileMagic.java:135)
at
org.apache.poi.openxml4j.opc.internal.ZipHelper.verifyZipHeader(ZipHelper.java:175)
at
org.apache.poi.openxml4j.opc.internal.ZipHelper.openZipStream(ZipHelper.java:209)
at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:98)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:324)
at org.apache.poi.util.PackageHelper.open(PackageHelper.java:37) at
org.apache.poi.xwpf.usermodel.XWPFDocument.(XWPFDocument.java:116)
at test.tester.(tester.java:44) at
test.tester.main(tester.java:100)
failed to create file Taking first ss
java.lang.NullPointerException at test.tester.setText(tester.java:62)
at test.tester.main(tester.java:103)
Here is the code:
public class tester{
FileOutputStream fos = null;
XWPFDocument doc = null;
// create para and run
XWPFParagraph para = null;
XWPFRun run = null;
File file = null;
public tester() {
try {
file = new File("WordDocWithImage.docx");
writeToWord();
//doc = new XWPFDocument();
doc = new XWPFDocument(OPCPackage.open(file));
//doc = new XWPFDocument(new FileInputStream("WordDocWithImage.docx"));
para = doc.createParagraph();
run = para.createRun();
para.setAlignment(ParagraphAlignment.CENTER);
} catch (Exception e) {
e.printStackTrace();
System.out.println("failed to create file");
}
}
public FileOutputStream writeToWord() throws FileNotFoundException {
fos = new FileOutputStream("WordDocWithImage.docx");
return fos;
}
public void setText(String text) {
run.setText(text);
}
public void takeScreenshot() throws IOException, AWTException, InvalidFormatException {
// Take screenshot
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage screenFullImage = robot.createScreenCapture(screenRect);
// convert buffered image to Input Stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(screenFullImage, "jpeg", baos);
baos.flush();
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
baos.close();
// add image to word doc
run.addBreak();
run.addPicture(bis, XWPFDocument.PICTURE_TYPE_JPEG, "image file", Units.toEMU(450), Units.toEMU(250)); // 200x200
// pixels
bis.close();
}
public void writeToFile() {
try {
// write word doc to file
doc.write(fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
tester t = new tester();
try {
System.out.println("Taking first ss");
t.setText("First Text");
t.takeScreenshot();
System.out.println("Taking second ss");
t.setText("Second Text");
t.takeScreenshot();
t.writeToFile();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Please assist.
The supplied file was empty (zero bytes long)
The problem is about getting the WordDocWithImage file in this line.
file = new File("WordDocWithImage.docx");
It could not find the docx file. You should check the location of the file and give the true path in there.
EDIT: You need to change the outputStream to different location. You can create a subfolder. I got ss trying this.
public FileOutputStream writeToWord() throws FileNotFoundException {
fos = new FileOutputStream("path/subFolder/WordDocWithImage.docx");
return fos;
}
Note: I have tried the code.
My use case is this: when the client clicks download on a pdf, I want to edit/write some text on to the pdf using Itext pdf editor, then zip the pdf then let it download, All during the stream. I am aware of memory issue if the pdf is large etc. which won't be an issue since its like 20-50kb. I have the zipping during the stream before downloading working using byte array, now have to make the pdfeditor method also run before zipping, add some text then let the download happen.
Here is my code so far:
public class zipfolder {
public static void main(String[] args) {
try {
System.out.println("opening connection");
URL url = new URL("http://gitlab.itextsupport.com/itext/sandbox/raw/master/resources/pdfs/form.pdf");
InputStream in = url.openStream();
// FileOutputStream fos = new FileOutputStream(new
// File("enwiki.png"));
PdfEditor writepdf = new PdfEditor();
writepdf.manipulatePdf(url, dest, "field"); /// where i belive i
/// should execute the
/// editor function ?
File f = new File("test.zip");
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f));
ZipEntry entry = new ZipEntry("newform.pdf");
zos.putNextEntry(entry);
System.out.println("reading from resource and writing to file...");
int length = -1;
byte[] buffer = new byte[1024];// buffer for portion of data from
// connection
while ((length = in.read(buffer)) > -1) {
zos.write(buffer, 0, length);
}
zos.close();
in.close();
System.out.println("File downloaded");
} catch (Exception e) {
System.out.println("Error");
e.printStackTrace();
}
}
}
public class PdfEditor {
public String insertFields (String field, String value) {
return field + " " + value;
// System.out.println("does this work :" + field);
}
// public static final String SRC = "src/resources/source.pdf";
// public static final String DEST = "src/resources/Destination.pdf";
//
// public static void main(String[] args) throws DocumentException,
// IOException {
// File file = new File(DEST);
// file.getParentFile().mkdirs();
// }
public String manipulatePdf(URL src, String dest, String field) throws Exception {
System.out.println("test");
try {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
Item item = form.getFieldItem("Name");
PdfDictionary widget = item.getWidget(0);
PdfArray rect = widget.getAsArray(PdfName.RECT);
rect.set(2, new PdfNumber(rect.getAsNumber(2).floatValue() + 20f));
String value = field;
form.setField("Name", value);
form.setField("Company", value);
stamper.close();
} catch (Exception e) {
System.out.println("Error in manipulate");
System.out.println(e.getMessage());
throw e;
}
return field;
}
}
So playing with ByteArrayOutputStream, finally got it work. passing the input stream to 'manipulatepdf' and returning 'bytedata'.
public ByteArrayOutputStream manipulatePdf(InputStream in, String field) throws Exception {
System.out.println("pdfediter got hit");
ByteArrayOutputStream bytedata = new ByteArrayOutputStream();
try {
PdfReader reader = new PdfReader(in);
PdfStamper stamper = new PdfStamper(reader, bytedata);
AcroFields form = stamper.getAcroFields();
Item item = form.getFieldItem("Name");
PdfDictionary widget = item.getWidget(0);
PdfArray rect = widget.getAsArray(PdfName.RECT);
rect.set(2, new PdfNumber(rect.getAsNumber(2).floatValue() + 20f));
String value = field;
form.setField("Name", value);
form.setField("Company", value);
stamper.close();
} catch (Exception e) {
System.out.println("Error in manipulate");
System.out.println(e.getMessage());
throw e;
}
return bytedata;
}
public String editandzip (String data, String Link) {
try {
System.out.println("opening connection");
URL url = new URL(Link);
InputStream in = url.openStream();
System.out.println("in : "+ url);
//String data = "working ok with main";
PdfEditor writetopdf = new PdfEditor();
ByteArrayOutputStream bao = writetopdf.manipulatePdf(in, data);
byte[] ba = bao.toByteArray();
File f = new File("C:/Users/JayAcer/workspace/test/test.zip");
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f));
ZipEntry entry = new ZipEntry("newform.pdf");
entry.setSize(ba.length);
zos.putNextEntry(entry);
zos.write(ba);
zos.close();
in.close();
System.out.println("File downloaded");
} catch (Exception e) {
System.out.println("Error");
e.printStackTrace();
}
return data;
}
}