【发布时间】:2020-05-18 14:40:54
【问题描述】:
我正在开发一个 Android 应用程序,我需要实现将用户签名(图像)放置在用户拖动的 pdf 上。我正在使用 pdf 查看器(com.github.barteksc.pdfviewer.PDFView)来显示 pdf,并且我正在使用拖放侦听器从视图中获取坐标。但它没有放在pdf上的正确位置。我花了很多时间在其他解决方案上,但没有任何帮助。 (或者请推荐任何 Android 中可用的免费 SDK 来实现同样的目的。)
请帮我解决这个问题。这是我的代码示例。
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();
}
}
}
【问题讨论】:
-
“但它没有放在 pdf 上的正确位置” - 它放在哪里?你有没有尝试在其中找到一种模式?只是在一个恒定的偏移量?还是镜像?还是x和y交换了? (作为第一印象:我看到您正在切换 x 和 y,这是设计使然吗?并且您从某个宽度/高度中减去两个坐标,通常只需减去一个。)
-
感谢mkl的回复,我会添加截图,让你有更多的想法。
-
我看到签名是您最可能不希望它出现的位置,但它打算去哪里?如果你改变了预期的位置,最终的位置会如何移动?平行、同向还是相反?在一个角度?
-
根据我对此链接的观察pspdfkit.com/guides/android/current/faq/coordinate-spaces 视图坐标和 pdf 坐标不同,因为我手动做了一些数学运算,我发现了这个。如果我错了,请告诉我。
-
我没有使用过
PDFView组件的经验。但是,考虑到类似情况,它的坐标系原点似乎在左上角。另一方面,PDF 随处可见,通常在左下角。因此,我理解您为什么要通过减去宽度或高度来反转 一个 坐标,但为什么两者都?此外,您可以切换坐标 (setAbsolutePosition(y, x))。可能是因为页面旋转?但你不为此测试。那么,为什么?
标签: android pdf itext pdf-generation electronic-signature