Skip to content
ComPDF
Guides

Create, Move, and Delete Text, Images and Path

ComPDF provides comprehensive methods for creating, moving, and deleting text, images and path.

Performing Actions Through CPDFReaderView

CPDFReaderView provides basic interactive capabilities by default, allowing the user to create and delete text, images and path, drag and drop to move the position of images, text blocks and path, resize images, text blocks and path, etc. by using the mouse and keyboard.

Configure the Context Menu.

If you need to copy, paste, cut, or delete text, images and path, you can add these methods to the context menu through the setContextMenuShowListener event of CPDFReaderView.

This example shows how to configure the context menu:

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;
  }
}
...
// Initialize CPDFReaderView.
CPDFReaderView readerView = findViewById(R.id.readerView);
CPDFDocument document = new CPDFDocument(context);
document.open(pdfPath);
readerView.setPDFDocument(document);
// Set a custom context menu.
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
  }
}
...
// Initialize CPDFReaderView
val readerView = findViewById<CPDFReaderView>(R.id.readerView)
val document = CPDFDocument(context)
document.open(pdfPath)
readerView.pdfDocument = document
// Set a custom context menu.
readerView.contextMenuShowListener = DemoContextMenuHelper(readerView)

Inserting Text and Images

You can specify the ability to insert text and image blocks through the beginEdit method of CPDFEditManager. The following code shows how to achieve this:

java
CPDFEditManager editManager = cpdfReaderView.getEditManager();
// Insert Image
editManager.beginEdit(CPDFEditPage.LoadImage);
// Insert Text
editManager.beginEdit(CPDFEditPage.LoadText);
// Cancel
editManager.beginEdit(CPDFEditPage.LoadNone);

You can also insert an image by tapping the PDF page area.

java
private CImageResultLauncher imageResultLauncher = new CImageResultLauncher(this);

// This callback is triggered when the PDF page area is tapped
cpdfReaderView.setSelectImageCallback(() -> {
  // Implement your own image selection functionality
  imageResultLauncher.launch(RequestType.PHOTO_ALBUM,
                             result -> cpdfReaderView.addEditImage(result));
});

Programmatically Creating Text or Images

You can programmatically insert text or images into a PDF page. When the document is bound to CPDFReaderView and displayed, if you are not in ViewMode.PDFEdit, you need to call page.endEdit() after the operation to properly finish editing.

Insert Text

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 editPage = page.getEditPage(false);
editPage.beginEdit(CPDFEditPage.LoadTextImage);

if (editPage == null || !editPage.isValid()) return null;

PDFEditAlignType alignType = CPDFEnumConvertUtil.stringToEditAlignType(alignment);

// Create a new text area
CPDFEditTextArea editTextArea = editPage.createNewTextArea(
        area, psName, (float) fontSize, Color.parseColor(fontColor),
        alpha, false, false, alignType
);

if (editTextArea != null && editTextArea.isValid()) {
    CPDFEditCharItem begin = editTextArea.getBeginCharPlace();
    CPDFEditCharItem end = editTextArea.getEndCharPlace();

    // Insert text
    editTextArea.insertTextRange(begin.getPlace(), end.getPlace(), content);

    // Set maximum width
    RectF currentRect = editTextArea.getFrame(true);
    if (maxWidth != 0) {
        currentRect.right = area.left + (float) maxWidth;
        editTextArea.setFrame(currentRect, true);
    }

    // If not in PDFEdit mode, end editing
    if (!isEditMode) page.endEdit();

    return editTextArea;
}

return null;
kotlin
val page = 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 = page.getEditPage(false)
editPage?.beginEdit(CPDFEditPage.LoadTextImage)

if (editPage == null || !editPage.isValid) return null

val alignType = CPDFEnumConvertUtil.stringToEditAlignType(alignment)
val editTextArea = editPage.createNewTextArea(
    area, psName, fontSize.toFloat(), Color.parseColor(fontColor),
    alpha, false, false, alignType
)

editTextArea?.let {
    val begin = it.beginCharPlace
    val end = it.endCharPlace
    it.insertTextRange(begin.place, end.place, content)

    val currentRect = it.getFrame(true)
    if (maxWidth != 0) {
        currentRect.right = area.left + maxWidth.toFloat()
        it.setFrame(currentRect, true)
    }

    if (!isEditMode) page.endEdit()
    return it
}

return null

Insert Image

java
CPDFPage page = document.pageAtIndex(pageIndex);
CPDFEditPage editPage = page.getEditPage(false);
if (editPage == null || !editPage.isValid()) return;

// Start edit mode (load image resource)
editPage.beginEdit(CPDFEditPage.LoadImage);

// Get image dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int bitmapWidth = options.outWidth;
int bitmapHeight = options.outHeight;

// Calculate insert area
RectF insertRect = calculateInsertRect(page, pointX, pointY, bitmapWidth, bitmapHeight, width);

// Create image area
CPDFEditArea editArea = editPage.createNewImageArea(insertRect, imagePath, null);

// If not in PDFEdit mode, end editing
if (!isEditMode) page.endEdit();
kotlin
val page = document.pageAtIndex(pageIndex)
val editPage = page.getEditPage(false)
if (editPage == null || !editPage.isValid) return

editPage.beginEdit(CPDFEditPage.LoadImage)

// Get image dimensions
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeFile(imagePath, options)
val bitmapWidth = options.outWidth
val bitmapHeight = options.outHeight

val insertRect = calculateInsertRect(page, pointX, pointY, bitmapWidth, bitmapHeight, width)
val editArea = editPage.createNewImageArea(insertRect, imagePath, null)

if (!isEditMode) page.endEdit()

Helper Method: Calculate the Image Insertion Area

java
private static RectF calculateInsertRect(CPDFPage page, double pointX, double pointY,
                                         int bitmapWidth, int bitmapHeight, @Nullable Double targetWidth) {
    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;
    }

    float aspectRatio = (float) bitmapWidth / bitmapHeight;
    int width = (targetWidth != null && targetWidth > 0) ? targetWidth.intValue() : bitmapWidth;
    int height = (int) (width / aspectRatio);

    if (width > pageWidth) { width = pageWidth; height = (int) (width / aspectRatio); }
    if (height > pageHeight) { height = pageHeight; width = (int) (height * aspectRatio); }

    float left = clamp((float) pointX, 0, pageWidth - width);
    float top = clamp((float) pointY, height, pageHeight);

    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
fun calculateInsertRect(page: CPDFPage, pointX: Double, pointY: Double,
                        bitmapWidth: Int, bitmapHeight: Int,
                        targetWidth: Double?): RectF {

    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
    }

    val aspectRatio = bitmapWidth.toFloat() / bitmapHeight
    var width = if (targetWidth != null && targetWidth > 0) targetWidth.toInt() else bitmapWidth
    var height = (width / aspectRatio).toInt()

    if (width > pageWidth) { width = pageWidth; height = (width / aspectRatio).toInt() }
    if (height > pageHeight) { height = pageHeight; width = (height * aspectRatio).toInt() }

    val left = clamp(pointX.toFloat(), 0f, (pageWidth - width).toFloat())
    val top = clamp(pointY.toFloat(), height.toFloat(), pageHeight.toFloat())
    return RectF(left, top, left + width, top - height)
}

fun clamp(value: Float, min: Float, max: Float): Float = max(min, min(value, max))