Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stamp on certain location of pdf using iText

I was trying to get the location using javascript and passed the coordinates to apply stamp on the stamp but it does not work properly. Below is the function I used to capture the coordinates of the mouse pointer.

function divMove(e){
  var div = document.getElementById('stamp');
  div.style.position = 'absolute';
  //div.style.top = e.clientY + 'px';
  //div.style.left = e.clientX + 'px';
  var box = div.getBoundingClientRect();
  mouse_top = e.clientY;
  mouse_left = e.clientX;
  var diff_x = mouse_left - box.left;
  var diff_y = mouse_top - box.top;
  div.style.top = ((Number(div.style.top.replace("px", "")) - 1) + diff_y) +"px";
  div.style.left = ((Number(div.style.left.replace("px", "")) - 1) + diff_x) +"px";
  document.getElementById("data").innerHTML =
    "mouse_top:" + mouse_top + "<br>mouse_left:" + mouse_left
}

Below is the back-end code to handle the stamping part using iText:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  Properties p = new Properties();
  p.load(new FileInputStream("config.properties"));

  String src = p.getProperty("src");
  String dest = p.getProperty("dest");
  String imgSrc = p.getProperty("stamp");

  PdfDocument doc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));

  ImageData image = ImageDataFactory.create(imgSrc);
  float w = image.getWidth();
  float h = image.getHeight();
  System.out.println("w: " + w + ", h: " + h);

  float mouseX = Float.valueOf(request.getParameter("mouseTop"));
  float mouseY = Float.valueOf(request.getParameter("mouseLeft"));
  System.out.println("top: " + mouseX + ", left: " + mouseY);

  //Rectangle rect = new Rectangle(Math.abs(mouseX-600)+w,Math.abs(mouseY-300)+h,w,h);
  Rectangle rect = new Rectangle(mouseX,mouseY,w,h);
  PdfStampAnnotation stamp = new PdfStampAnnotation(rect).setStampName(new PdfName("Approved"));
  PdfFormXObject xObj = new PdfFormXObject(new Rectangle(w,h));
  PdfCanvas canvas = new PdfCanvas(xObj,doc);
  canvas.addImage(image,0,0,false);
  //canvas.getGraphicsState();

  stamp.setNormalAppearance(xObj.getPdfObject());
  stamp.setFlags(PdfAnnotation.PRINT);
  stamp.setFlags(PdfAnnotation.LOCKED);

  for(int i=1;i<=doc.getNumberOfPages();i++) {
    doc.getPage(i).addAnnotation(stamp);
  }
  //doc.getFirstPage().addAnnotation(stamp);
  FileOutputStream out = new FileOutputStream("config.properties");
  p.setProperty("src", dest);
  p.setProperty("dest", src);
  p.store(out, null);
  out.close();
  doc.close();

  //first read the file to byte array
  try {
    File file = new File(dest);

    if(file.canRead()) {
      String base64File;

      //define the byte array to store the file
      byte[] byteFile = new byte[(int)file.length()];

      //define the stream to read the pdf
      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
      FileInputStream fis = new FileInputStream(file);

      //convert the read file's stream byte to base64
      //important for streaming the pdf bytes back to the front
      Base64OutputStream baos = new Base64OutputStream(bytes);

      int len;

      //read the byte from file then write it through stream to byteFile variable
      //read is reading one by one
      while((len = fis.read(byteFile)) > 0){
        baos.write(byteFile,0,len);
      }
      baos.flush();

      // turn the read byte into string
      base64File = bytes.toString("UTF-8");

      bytes.close();
      baos.close();
      fis.close();

      response.setContentType("application/pdf");
      response.setHeader("Content-Disposition","inline");
      response.setCharacterEncoding("UTF-8");
      response.setContentLength(base64File.length());
      //write the base64 string to the response message body
      response.getWriter().write(base64File,0,base64File.length());
              //response.getOutputStream().write(base64File,0,base64File.length());

    } else {
      response.setCharacterEncoding("UTF-8");
      response.getWriter().write("File is unreadable!");
    }

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

This is the position I am going to stamp:

This is the position I am going to stamp

The output is not what I expected:

The output is not what I expected

like image 267
Tan Chee Hong Avatar asked Aug 14 '18 04:08

Tan Chee Hong


People also ask

What is PdfStamper in iText?

PdfStamper(PdfReader reader, OutputStream os, char pdfVersion) Starts the process of adding extra content to an existing PDF document. PdfStamper(PdfReader reader, OutputStream os, char pdfVersion, boolean append) Starts the process of adding extra content to an existing PDF document, possibly as a new revision.

Is iText PDF free?

iText 7 Community is a PDF library where free/open source software (F/OSS) allows you to read through the full-code API.

What is iText jar used for?

iText is a library for creating and manipulating PDF files in Java and.NET. iText was written by Bruno Lowagie. The source code was initially distributed as open source under the Mozilla Public License or the GNU Library General Public License open source licenses. However, as of version 5.0.


2 Answers

The whole point of the problem is to be able to determine the position and size of the stamp you want to include in the PDF relative to the PDF page, in percentages. This is exactly what @rossfrank tries to explain in his answer and comments.

If you have control above where the PDF preview you are using to compute where the stamp should be placed this can be very easy, for instance, just take into account the width and height of a certain frame or even a div if you are using some kind of custom element to preview the different pages of your pdf, or very complex, if it depends on the actual browser size, the level of zoom applied, etcetera. The best advice is try controlling the place in which you are previewing your PDF.

Probably you will need to pass this reference width and height to your servlet as well because it seems you only know the dimensions of your image in the backend. If should be necessary to prevent overflows, etcetera.

Once obtained these values, the code should be straightforward:

Properties p = new Properties();
p.load(new FileInputStream("config.properties"));

String src = p.getProperty("src");
String dest = p.getProperty("dest");
String imgSrc = p.getProperty("stamp");

PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer);

ImageData image = ImageDataFactory.create(imgSrc);
float w = image.getWidth();
float h = image.getHeight();
System.out.println("w: " + w + ", h: " + h);

float mouseX = Float.valueOf(request.getParameter("mouseTop"));
float mouseY = Float.valueOf(request.getParameter("mouseLeft"));

// As explained, get reference width and height values
// They can be a frame dimension, a div width and height, etcetera
float referenceWidth = Float.valueOf(request.getParameter("referenceWidth"));
float referenceHeight = Float.valueOf(request.getParameter("referenceHeight"));

// Normalize values: it can be done in the client side as well
float top    = mouseY / referenceHeight;
float left   = mouseX / referenceWidth;
float width  = w      / referenceWidth;
float height = h      / referenceHeight;

// Just in case, take into account page rotation
Rectangle pdfRectangle = reader.getPageSizeWithRotation(1);

float pdfWidth = pdfRectangle.getWidth();
float pdfHeight = pdfRectangle.getHeight();

// Please, pay attention to this code, it seems that changed in itext7
// Any way, any change should be easy
float llx = pdfWidth * left;
float lly = pdfHeight * (1 - top - height);
float urx = llx + (pdfWidth * width);
float ury = lly + (pdfHeight * height);

Rectangle rect = new Rectangle(llx, lly, urx, ury);

PdfStampAnnotation stamp = new PdfStampAnnotation(rect).setStampName(new PdfName("Approved"));
PdfFormXObject xObj = new PdfFormXObject(new Rectangle(width,height));
PdfCanvas canvas = new PdfCanvas(xObj,doc);
canvas.addImage(image,0,0,false);
//...

This related SO answer provides further insights into the problem.

like image 137
jccampanero Avatar answered Nov 01 '22 01:11

jccampanero


This code uses the percentage of the window to translate it into user units.

Update:

document.getElementById("data").innerHTML = "mouse_top:" + mouse_top
+ "<br>mouse_left:" + mouse_left

to

document.getElementById("data").innerHTML = "mouse_top:" +
(mouse_top\window.innerHeight) + "<br>mouse_left:" + (mouse_left\window.innerWidth)

On the backend:

Rectange size = doc.getPage(1).getPageSize();
Rectangle rect = new Rectangle(mouseX*size.getHeight(),mouseY*size.getWidth(),w,h);
like image 37
rossfrank Avatar answered Oct 31 '22 23:10

rossfrank