4

I'm developing an Android application, I need to implement placing user signature(image) on pdf draging by user. I'm using pdf viewer(com.github.barteksc.pdfviewer.PDFView) to show pdf and I'm using drag and drop listener to get coordinates from view. but it is not placing in proper position on pdf. I spent lot of time on other solutions, but there is no help. (or please suggest any free SDK available in Android to achieve the same.)

Please help me resolve this. Here is my code sample.

public class MainActivity extends AppCompatActivity implements View.OnTouchListener, View.OnDragListener, OnPageChangeListener, OnPageScrollListener {
private PDFView pdfViewDemo;
private Button buttonGetPage;
private ImageView imageViewDemo;
private ImageView imageDrag;

private String pdfUrl = "/sdcard/sample.pdf";

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pdf_view);
    findViewById(R.id.rootView).setOnDragListener(this);
    pdfViewDemo = findViewById(R.id.pdfView);
    final File file = new File(pdfUrl);
    pdfViewDemo.setMaxZoom(0);
    pdfViewDemo.fromFile(file)
            .onPageChange(this)
            .onPageScroll(this)
            .spacing(10)
            .swipeHorizontal(true)
            .enableDoubletap(false)
            .load();

    //Implementation of Drag and Drop---------------------------------------------
    imageDrag = findViewById(R.id.imageDrag);
    imageDrag.setOnTouchListener(this);
    //Implementation of Drag and Drop---------------------------------------------

    imageViewDemo = findViewById(R.id.imageDemo);

    buttonGetPage = findViewById(R.id.buttonGetPage);
    buttonGetPage.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ArrayList<Bitmap> bitmaps = pdfToBitmap(file);
            pdfViewDemo.setVisibility(View.GONE);
            imageViewDemo.setVisibility(View.VISIBLE);
            imageViewDemo.setImageBitmap(bitmaps.get(0));
            Log.e("test", "Bitmaps: " + bitmaps.size());
        }
    });


}

private ArrayList<Bitmap> pdfToBitmap(File pdfFile) {
    ArrayList<Bitmap> bitmaps = new ArrayList<>();

    try {
        PdfRenderer renderer = new PdfRenderer(ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY));

        Bitmap bitmap;
        final int pageCount = renderer.getPageCount();
        for (int i = 0; i < pageCount; i++) {
            PdfRenderer.Page page = renderer.openPage(i);

            int width = getResources().getDisplayMetrics().densityDpi / 72 * page.getWidth();
            int height = getResources().getDisplayMetrics().densityDpi / 72 * page.getHeight();
            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

            page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

            bitmaps.add(bitmap);

            // close the page
            page.close();

        }

        // close the renderer
        renderer.close();
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return bitmaps;

}

@Override
public void onPageChanged(int page, int pageCount) {
    Log.e("test", "onPageChanged() called with: page = [" + page + "], pageCount = [" + pageCount + "]");
}

@Override
public void onPointerCaptureChanged(boolean hasCapture) {
    Log.e("test", "onPointerCaptureChanged() called with: hasCapture = [" + hasCapture + "]");
}

@Override
public void onPageScrolled(int page, float positionOffset) {
    Log.e("test", "onPageScrolled() called with: page = [" + page + "], positionOffset = [" + positionOffset + "]");
}

@Override
public boolean onTouch(View view, MotionEvent event) {
    Log.e("test", "onTouch() called with: v = [" + view + "], event = [" + event + "]");
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
        view.startDrag(null, shadowBuilder, view, 0);
        return true;
    } else {
        return false;
    }
}

@Override
public boolean onDrag(View v, DragEvent event) {
    int action = event.getAction();
    switch (action) {
        case DragEvent.ACTION_DRAG_STARTED:
            Log.e("test", "onDrag: ACTION_DRAG_STARTED");
            break;
        case DragEvent.ACTION_DRAG_ENTERED:
            Log.e("test", "onDrag: ACTION_DRAG_ENTERED");
            break;
        case DragEvent.ACTION_DRAG_EXITED:
            Log.e("test", "onDrag: ACTION_DRAG_EXITED");
            break;
        case DragEvent.ACTION_DROP:
            Log.e("test", "onDrag: ACTION_DROP");
            Log.e("test-", "X: " + (int) event.getX());
            Log.e("test-", "Y: " + (int) event.getY());
            getPdfCoordinates((int) event.getX(), (int) event.getY());
            /*View tvState = (View) event.getLocalState();
            val tvParent = tvState.parent as ViewGroup
            tvParent.removeView(tvState)
            val container = view as LinearLayout
            container.addView(tvState)
            tvParent.removeView(tvState)
            tvState.x = dragEvent.x
            tvState.y = dragEvent.y
            view.addView(tvState)
            view.setVisibility(View.VISIBLE)
            int x = (int) v.getX();
            int y = (int) v.getY();
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(x, y);
            textTitle.setLayoutParams(layoutParams);*/
            break;
        case DragEvent.ACTION_DRAG_ENDED:
            Log.e("test", "onDrag: ACTION_DRAG_ENDED" + v.getX() + ":" + v.getY());
            imageDrag.setVisibility(View.VISIBLE);
            break;
        default:
            break;
    }
    return true;
}

private void getPdfCoordinates(int x, int y) {
    Log.e("test-", "PDF: getMeasuredWidth: " + pdfViewDemo.getMeasuredWidth());
    Log.e("test-", "PDF: getWidth: " + pdfViewDemo.getWidth());
    Log.e("test-", "PDF: getOptimalPageHeight: " + pdfViewDemo.getOptimalPageHeight());
    Log.e("test-", "PDF: getOptimalPageWidth: " + pdfViewDemo.getOptimalPageWidth());
    Log.e("test-", "PDF: getMeasuredWidthAndState: " + pdfViewDemo.getMeasuredWidthAndState());

    Log.e("test-", "PDF: getMinimumHeight: " + pdfViewDemo.getMinimumHeight());
    Log.e("test-", "PDF: getMinimumWidth: " + pdfViewDemo.getMinimumWidth());

    x= (int) pdfViewDemo.getOptimalPageHeight()/2-x;
    y=(int)pdfViewDemo.getOptimalPageWidth()/2-y;
    addImageToPdf(x, y);
}

private void addImageToPdf(int x, int y) {
    Log.e("test", "addImageToPdf() called with: x = [" + x + "], y = [" + y + "]" + pdfViewDemo.getCurrentPage());
    try {
        PdfReader reader = new PdfReader(pdfUrl);
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("/sdcard/test_pdf.pdf"));
        PdfContentByte content = stamper.getOverContent(pdfViewDemo.getCurrentPage()+1);

        com.itextpdf.text.Image image = Image.getInstance("/sdcard/sign_1.jpg");


        image.scaleAbsoluteHeight(50);
        image.scaleAbsoluteWidth((image.getWidth() * 50) / image.getHeight());

        image.setAbsolutePosition(y, x);

        content.addImage(image);

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

Here are the screenshots of input and output enter image description here enter image description here

Suresh
  • 427
  • 1
  • 6
  • 22
  • *"but it is not placing in proper position on pdf"* - where is it placed instead? Have you tried to find a pattern in that? Just at a constant offset? Or mirrored? Or x and y exchanged? (As a first impression: I see that you are switching x and y, is that by design? And you subtract both coordinates from some width/height, usually one needs only subtract one.) – mkl Feb 03 '20 at 10:54
  • Thanks for responding mkl, I'll add screenshots, so you will get more idea. – Suresh Feb 03 '20 at 12:37
  • I see that the signature is where you most likely did not want it to be, but where was it intended to go? And if you change the the intended position, how does the final position move? Parallel, same direction or opposite? At an angle? – mkl Feb 03 '20 at 13:15
  • as per my observation from this link https://pspdfkit.com/guides/android/current/faq/coordinate-spaces/ The view coordinates and pdf coordinates are different, because of that I did some math manually and I found this. let me know if I'm wrong. – Suresh Feb 03 '20 at 14:13
  • I have no experiences with that `PDFView` component. Considering the like, though, it appears like it has its coordinate system origin in the upper left. PDFs, on the other hand, have it anywhere, most often in the lower left. Thus, I understand why you invert *one* of the coordinates by subtracting from width or height, but *why both*? Furthermore, you switch the coordinates (`setAbsolutePosition(y, x)`). Probably because of page rotation? But you don't test for that. Thus, why? – mkl Feb 03 '20 at 15:33

1 Answers1

0

According to this answer, the coordinates of a given page can be anything and must be checked using the PdfReader.

There may also be a handful of other factors influencing the positioning.

  • PDF coordinate system may be scaled differently than androids.
  • PdfStamper may place stamp in an unexpected way (I could not find sufficient documentation) thus some testing is required to find out where exactly the stamp is placed for given coordinates.
  • The coordinates of the touch event which you are intercepting at the drop are absolute but should be relative to the position of the PdfView. This can be achieved getting the absolute position of the PdfView and subtracting it from the TouchEvent coordinates.

Just a few thoughts to help with your debugging.

B. Plüster
  • 564
  • 3
  • 11