Stamp on certain location of pdf using iText
Asked Answered
N

2

11

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

Nudnik answered 14/8, 2018 at 4:25 Comment(3)
You are making the wrong assumption that measurement in PDF is done in pixels (px). Please read the FAQ to find out more about the measurement unit in PDF: developers.itextpdf.com/content/…Etude
It's not only that, though, it also looks like a problem due to ignoring either a page rotation or a custom coordinates system origin.Consociate
do i need to get the dpi of the pdf?Nudnik
C
3

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);
Crownwork answered 21/8, 2018 at 13:34 Comment(10)
I just updated the img. Actually the pdf is displayed over the browser, unfortunately it still doesnt work after implemented the window.innerHeight and window.innerWidthNudnik
you can update innerWidth to the width in pixels of the displayed content. The goal would be to get a percentage that you can then use to place it dynamicallyCrownwork
@Crownwork What do you mean?Chronaxie
@CookieMonster The idea is to get the point as a percentage of the area so that it can easily be converted to a different size area and retain the same spot. For example, if you have a square that is 2 x 2 and you get the point in the middle(1, 1). The percentage would be 50%. If you then converted it to an area of 8 x 8, after you multiply it out you would get (4, 4) which is still the center of the larger area.Crownwork
@Crownwork I see. So in this case your answer above would not work right?Chronaxie
@CookieMonster yes it would still work. The only change would be how you get the percentage (which would change depending on your project)Crownwork
@Crownwork I tried your answer above but it doesn't work though. It runs the stamp on a web browser and I hit stamp, the location/position changes off.Chronaxie
@CookieMonster it is likely you are calculating the percentage in the browser incorrectly. It is likely that the PDF in the browser isn't taking up the whole screen. How you get the percentage would depend on the size of the content.Crownwork
@Crownwork That is correct, the PDF in the browser only takes up 1/4 of the page. How to determine the size of the content?Chronaxie
@CookieMonster This should work to get the size.Crownwork
A
2

The goal 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. That's exactly what @rossfrank is trying to explain in his answer and comments..

If you have control over PDF preview you are using to compute the position in which 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. It should be necessary to prevent overflows, etcetera.

Once these values are obtained, 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.

Altarpiece answered 3/8, 2021 at 21:49 Comment(5)
@jcccapanero Hello promote the question since I have the same problem I do not know if you can see my question #68582202Behistun
Hi @SebastianRuiz. Of course. I included a new related answer in your own question. I hope it helps.Altarpiece
Hello @Altarpiece if it helped me however for the positions of x and and they still do not come out correct, I have updated the question with the code that I mentionBehistun
Hi @SebastianRuiz. Thank you very much for the feedback. I replied you in your own answer. I hope it helps.Altarpiece
Hi @Altarpiece I have updated the question to show what I getBehistun

© 2022 - 2024 — McMap. All rights reserved.