Guides
创建、移动、删除文本、图像和路径
ComPDF 提供了完整丰富的创建、移动、删除文本,图像和路径的方法。
通过 CPDFReaderView操作
CPDFReaderView默认提供基本的交互能力,允许用户创建和删除文本,图像和路径,拖拽移动图片,文字块和路径位置,调整图片,文字块和路径大小等,实现类似在常见文字处理软件中的操作。
设置上下文菜单
如果需要复制、粘贴、剪切或删除文本,图片或路径,可以通过 CPDFReaderView的 setContextMenuShowListener事件在上下文菜单中添加这些操作方法。
以下是如何在上下文菜单中添加复制、粘贴、删除等操作的示例代码:
edit_text_area_menu_layout.xml:
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/edit_copy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:gravity="center"
android:text="copy" />
<TextView
android:id="@+id/edit_paste"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:gravity="center"
android:text="paste" />
<TextView
android:id="@+id/edit_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:gravity="center"
android:text="delete" />
</LinearLayout>java
public class DemoContextMenuHelper extends CPDFContextMenuShowHelper {
public DemoContextMenuHelper(CPDFReaderView cpdfReaderView) {
super(cpdfReaderView);
}
@Override
public View getEditTextAreaContentView(CPDFPageView cpdfPageView, LayoutInflater layoutInflater, CPDFEditSelections cpdfEditSelections) {
View contentView = layoutInflater.inflate(R.layout.edit_text_area_menu_layout, null);
invokeOnClickListener(contentView, v -> {
try {
int id = v.getId();
if (id == R.id.edit_copy) {
pageView.operateEditTextArea(CPDFPageView.EditTextAreaFuncType.COPY);
} else if (id == R.id.edit_paste) {
pageView.operateEditText(CPDFPageView.EditTextFuncType.PASTE);
} else if (id == R.id.edit_delete) {
pageView.operateEditTextArea(CPDFPageView.EditTextAreaFuncType.DELETE);
}
} finally {
dismissContextMenu();
}
}, R.id.edit_copy, R.id.edit_paste, R.id.edit_delete);
return contentView;
}
}
...
// 初始化CPDFReaderView
CPDFReaderView readerView = findViewById(R.id.readerView);
CPDFDocument document = new CPDFDocument(context);
document.open(pdfPath);
readerView.setPDFDocument(document);
// 设置自定义的上下文菜单
readerView.setContextMenuShowListener(new DemoContextMenuHelper(readerView));kotlin
class DemoContextMenuHelper(cpdfReaderView: CPDFReaderView) :
CPDFContextMenuShowHelper(cpdfReaderView) {
override fun getEditTextAreaContentView(
cpdfPageView: CPDFPageView,
layoutInflater: LayoutInflater,
cpdfEditSelections: CPDFEditSelections
): View {
val contentView: View = layoutInflater.inflate(R.layout.edit_text_area_menu_layout, null)
invokeOnClickListener(contentView, { v: View ->
try {
val id = v.id
if (id == R.id.edit_copy) {
pageView.operateEditTextArea(CPDFPageView.EditTextAreaFuncType.COPY)
} else if (id == R.id.edit_paste) {
pageView.operateEditText(CPDFPageView.EditTextFuncType.PASTE)
} else if (id == R.id.edit_delete) {
pageView.operateEditTextArea(CPDFPageView.EditTextAreaFuncType.DELETE)
}
} finally {
dismissContextMenu()
}
}, R.id.edit_copy, R.id.edit_paste, R.id.edit_delete)
return contentView
}
}
...
// 初始化CPDFReaderView
val readerView = findViewById<CPDFReaderView>(R.id.readerView)
val document = CPDFDocument(context)
document.open(pdfPath)
readerView.pdfDocument = document
// 设置自定义的上下文菜单
readerView.contextMenuShowListener = DemoContextMenuHelper(readerView)插入文字和图片
交互创建
您可以通过 CPDFEditManager的 beginEdit方法来指定是否可以插入文字和图片块。下面的代码将向您展示如何执行此操作:
java
CPDFEditManager editManager = cpdfReaderView.getEditManager();
// 允许插入图片。
editManager.beginEdit(CPDFEditPage.LoadImage);
// 允许插入文字。
editManager.beginEdit(CPDFEditPage.LoadText);
// 允许取消插入内容状态
editManager.beginEdit(CPDFEditPage.LoadNone);同时您也可以通过点击PDF页面区域来插入图片
java
private CImageResultLauncher imageResultLauncher = new CImageResultLauncher(this);
// 当点击PDF页面区域时会触发该回调
cpdfReaderView.setSelectImageCallback(() -> {
// 自行实现选择图片功能
imageResultLauncher.launch(RequestType.PHOTO_ALBUM,
result -> cpdfReaderView.addEditImage(result));
});编程创建
您也可以通过编程的方式插入文本或图片
- 插入文本
java
CPDFPage page = document.pageAtIndex(pageIndex);
Point point = new Point((int) pointX, (int) pointY);
RectF area = new RectF(point.x, point.y, point.x, point.y);
// Get the edit page object
CPDFEditPage cpdfEditPage = page.getEditPage(false);
cpdfEditPage.beginEdit(CPDFEditPage.LoadTextImage);
if (cpdfEditPage == null || !cpdfEditPage.isValid()) {
return null;
}
PDFEditAlignType alignType = CPDFEnumConvertUtil.stringToEditAlignType(alignment);
// Create a new text area
CPDFEditTextArea editTextArea = cpdfEditPage.createNewTextArea(area, psName,
(float) fontSize, Color.parseColor(fontColor), (int) alpha, false, false, alignType);
if (editTextArea != null && editTextArea.isValid()) {
// Get the start and end positions for text insertion
CPDFEditCharItem begin = editTextArea.getBeginCharPlace();
CPDFEditCharItem end = editTextArea.getEndCharPlace();
// Insert the content into the text area
CPDFEditCharItem charItem = editTextArea.insertTextRange(begin.getPlace(),
end.getPlace(), content);
RectF currentRect = editTextArea.getFrame(true);
if (maxWidth != 0) {
currentRect.right = area.left + (float) maxWidth;
editTextArea.setFrame(currentRect, true);
}
// 如果使用了CPDFReaderView展示当前文档,没有进入setViewMode(ViewMode.PDFEdit)模式,请调用结束编辑方法。
if (!isEditMode) {
page.endEdit();
}
return editTextArea;
}kotlin
val page: CPDFPage = document.pageAtIndex(pageIndex)
val point = Point(pointX.toInt(), pointY.toInt())
val area = RectF(point.x.toFloat(), point.y.toFloat(), point.x.toFloat(), point.y.toFloat())
// 获取编辑页对象
val editPage: CPDFEditPage? = page.getEditPage(false)
// 开始编辑(加载文本和图片资源)
editPage?.beginEdit(CPDFEditPage.LoadTextImage)
if (editPage == null || !editPage.isValid) {
return null
}
val alignType: PDFEditAlignType =
CPDFEnumConvertUtil.stringToEditAlignType(alignment)
// 创建新的文本区域
val editTextArea: CPDFEditTextArea? = editPage.createNewTextArea(
area,
psName,
fontSize.toFloat(),
Color.parseColor(fontColor),
alpha,
false,
false,
alignType
)
if (editTextArea != null && editTextArea.isValid) {
// 获取文本插入的起始和结束位置
val begin: CPDFEditCharItem = editTextArea.beginCharPlace
val end: CPDFEditCharItem = editTextArea.endCharPlace
// 在文本区域中插入内容
editTextArea.insertTextRange(
begin.place,
end.place,
content
)
val currentRect = editTextArea.getFrame(true)
if (maxWidth != 0) {
currentRect.right = area.left + maxWidth.toFloat()
editTextArea.setFrame(currentRect, true)
}
// 如果使用 CPDFReaderView 显示当前文档,且未进入 ViewMode.PDFEdit 模式
// 则需要主动结束编辑
if (!isEditMode) {
page.endEdit()
}
return editTextArea
}
return null- 插入图片
java
int pageIndex = 0;
double pointX = 100;
double pointY = 100;
double width = 200;
CPDFPage page = document.pageAtIndex(pageIndex);
// Get the edit page object
CPDFEditPage cpdfEditPage = page.getEditPage(false);
if (cpdfEditPage == null || !cpdfEditPage.isValid()) {
return;
}
// Start edit mode
cpdfEditPage.beginEdit(CPDFEditPage.LoadImage);
String imagePath = "xxx.jpg"
if (imagePath == null) {
Log.e("CPDFEditAreaUtil", "Failed to get image path");
callback.callback(null);
return;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int bitmapWidth = options.outWidth;
int bitmapHeight = options.outHeight;
if (bitmapWidth <= 0 || bitmapHeight <= 0) {
Log.e("CPDFEditAreaUtil", "Invalid bitmap dimensions");
return;
}
RectF insertRect = calculateInsertRect(
page,
pointX,
pointY,
bitmapWidth,
bitmapHeight,
width);
CPDFEditArea editArea = cpdfEditPage.createNewImageArea(
insertRect,
imagePath,
null);
// 如果使用了CPDFReaderView展示当前文档,没有进入setViewMode(ViewMode.PDFEdit)模式,请调用结束编辑方法。
if (!isEditMode) {
page.endEdit();
}
// ---------------------------------------------------
// 计算图片插入区域
private static RectF calculateInsertRect(
CPDFPage page,
double pointX,
double pointY,
int bitmapWidth,
int bitmapHeight,
@Nullable Double targetWidth) {
// 1. 获取页面尺寸(处理旋转)
RectF pageSize = page.getSize();
int pageWidth = (int) pageSize.width();
int pageHeight = (int) pageSize.height();
int rotation = page.getRotation();
if (rotation == 90 || rotation == 270) {
int temp = pageWidth;
pageWidth = pageHeight;
pageHeight = temp;
}
// 2. 计算图片宽高比
float aspectRatio = (float) bitmapWidth / bitmapHeight;
// 3. 计算目标尺寸
int width = (targetWidth != null && targetWidth > 0)
? targetWidth.intValue()
: bitmapWidth;
int height = (int) (width / aspectRatio);
// 4. 限制尺寸不超过页面大小(等比缩放)
if (width > pageWidth) {
width = pageWidth;
height = (int) (width / aspectRatio);
}
if (height > pageHeight) {
height = pageHeight;
width = (int) (height * aspectRatio);
}
// 5. 计算左上角坐标并进行边界修正
float left = clamp((float) pointX, 0, pageWidth - width);
float top = clamp((float) pointY, height, pageHeight);
// 6. 返回插入区域(PDF 坐标系,y 轴向上)
return new RectF(
left,
top,
left + width,
top - height
);
}
/**
* 将值限制在指定区间内
*/
private static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(value, max));
}kotlin
val pageIndex = 0
val pointX = 100.0
val pointY = 100.0
val width = 200.0
val page: CPDFPage = document.pageAtIndex(pageIndex)
// 获取编辑页对象
val editPage: CPDFEditPage? = page.getEditPage(false)
if (editPage == null || !editPage.isValid) return
// 开始编辑模式(加载图片资源)
editPage.beginEdit(CPDFEditPage.LoadImage)
val imagePath = "xxx.jpg"
if (imagePath.isEmpty()) {
Log.e("CPDFEditAreaUtil", "Failed to get image path")
callback.callback(null)
return
}
// 获取图片尺寸
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeFile(imagePath, options)
val bitmapWidth = options.outWidth
val bitmapHeight = options.outHeight
if (bitmapWidth <= 0 || bitmapHeight <= 0) {
Log.e("CPDFEditAreaUtil", "Invalid bitmap dimensions")
return
}
// 计算插入区域
val insertRect = calculateInsertRect(
page,
pointX,
pointY,
bitmapWidth,
bitmapHeight,
width
)
// 创建新的图片区域
val editArea: CPDFEditArea? = editPage.createNewImageArea(
insertRect,
imagePath,
null
)
// 如果使用了 CPDFReaderView 展示文档,且未进入 PDFEdit 模式,需要结束编辑
if (!isEditMode) {
page.endEdit()
}
// ---------------------------------------------------
// 计算图片插入区域
fun calculateInsertRect(
page: CPDFPage,
pointX: Double,
pointY: Double,
bitmapWidth: Int,
bitmapHeight: Int,
targetWidth: Double?
): RectF {
// 1. 获取页面尺寸(处理旋转)
var pageWidth = page.getSize().width().toInt()
var pageHeight = page.getSize().height().toInt()
val rotation = page.getRotation()
if (rotation == 90 || rotation == 270) {
val temp = pageWidth
pageWidth = pageHeight
pageHeight = temp
}
// 2. 图片宽高比
val aspectRatio = bitmapWidth.toFloat() / bitmapHeight
// 3. 计算目标尺寸
var width = if (targetWidth != null && targetWidth > 0) targetWidth.toInt() else bitmapWidth
var height = (width / aspectRatio).toInt()
// 4. 限制尺寸不超过页面大小(等比缩放)
if (width > pageWidth) {
width = pageWidth
height = (width / aspectRatio).toInt()
}
if (height > pageHeight) {
height = pageHeight
width = (height * aspectRatio).toInt()
}
// 5. 计算左上角坐标并边界修正
val left = clamp(pointX.toFloat(), 0f, (pageWidth - width).toFloat())
val top = clamp(pointY.toFloat(), height.toFloat(), pageHeight.toFloat())
// 6. 返回插入区域(PDF 坐标系,y 轴向上)
return RectF(left, top, left + width, top - height)
}
// 限制值在指定区间
fun clamp(value: Float, min: Float, max: Float): Float {
return max(min, min(value, max))
}