diff options
Diffstat (limited to 'libs/hwui/jni/pdf/PdfEditor.cpp')
-rw-r--r-- | libs/hwui/jni/pdf/PdfEditor.cpp | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp new file mode 100644 index 000000000000..828d6e3992b6 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "PdfEditor" + +#include <sys/types.h> +#include <unistd.h> + +#include <vector> + +#include <log/log.h> +#include <utils/Log.h> + +#include "PdfUtils.h" + +#include "graphics_jni_helpers.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" +#include "fpdfview.h" +#include "fpdf_edit.h" +#include "fpdf_save.h" +#include "fpdf_transformpage.h" +#pragma GCC diagnostic pop + +#include "SkMatrix.h" + +namespace android { + +enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static struct { + jfieldID left; + jfieldID top; + jfieldID right; + jfieldID bottom; +} gRectClassInfo; + +static jint nativeRemovePage(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDFPage_Delete(document, pageIndex); + return FPDF_GetPageCount(document); +} + +struct PdfToFdWriter : FPDF_FILEWRITE { + int dstFd; +}; + +static bool writeAllBytes(const int fd, const void* buffer, const size_t byteCount) { + char* writeBuffer = static_cast<char*>(const_cast<void*>(buffer)); + size_t remainingBytes = byteCount; + while (remainingBytes > 0) { + ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); + if (writtenByteCount == -1) { + if (errno == EINTR) { + continue; + } + ALOGE("Error writing to buffer: %d", errno); + return false; + } + remainingBytes -= writtenByteCount; + writeBuffer += writtenByteCount; + } + return true; +} + +static int writeBlock(FPDF_FILEWRITE* owner, const void* buffer, unsigned long size) { + const PdfToFdWriter* writer = reinterpret_cast<PdfToFdWriter*>(owner); + const bool success = writeAllBytes(writer->dstFd, buffer, size); + if (!success) { + ALOGE("Cannot write to file descriptor. Error:%d", errno); + return 0; + } + return 1; +} + +static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + PdfToFdWriter writer; + writer.dstFd = fd; + writer.WriteBlock = &writeBlock; + const bool success = FPDF_SaveAsCopy(document, &writer, FPDF_NO_INCREMENTAL); + if (!success) { + jniThrowExceptionFmt(env, "java/io/IOException", + "cannot write to fd. Error: %d", errno); + } +} + +static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + // PDF's coordinate system origin is left-bottom while in graphics it + // is the top-left. So, translate the PDF coordinates to ours. + SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1); + SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page)); + SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX); + + // Apply the transformation what was created in our coordinates. + SkMatrix matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr), + coordinateChange); + + // Translate the result back to PDF coordinates. + matrix.setConcat(coordinateChange, matrix); + + SkScalar transformValues[6]; + if (!matrix.asAffine(transformValues)) { + FPDF_ClosePage(page); + + jniThrowException(env, "java/lang/IllegalArgumentException", + "transform matrix has perspective. Only affine matrices are allowed."); + return; + } + + FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], + transformValues[SkMatrix::kATransY]}; + + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDFPage_TransFormWithClip(page, &transform, &clip); + + FPDF_ClosePage(page); +} + +static void nativeGetPageSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + FPDF_ClosePage(page); +} + +static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject outBox) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return false; + } + + float left; + float top; + float right; + float bottom; + + const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA) + ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom) + : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom); + + FPDF_ClosePage(page); + + if (!success) { + return false; + } + + env->SetIntField(outBox, gRectClassInfo.left, (int) left); + env->SetIntField(outBox, gRectClassInfo.top, (int) top); + env->SetIntField(outBox, gRectClassInfo.right, (int) right); + env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom); + + return true; +} + +static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject box) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + const int left = env->GetIntField(box, gRectClassInfo.left); + const int top = env->GetIntField(box, gRectClassInfo.top); + const int right = env->GetIntField(box, gRectClassInfo.right); + const int bottom = env->GetIntField(box, gRectClassInfo.bottom); + + if (pageBox == PAGE_BOX_MEDIA) { + FPDFPage_SetMediaBox(page, left, top, right, bottom); + } else { + FPDFPage_SetCropBox(page, left, top, right, bottom); + } + + FPDF_ClosePage(page); +} + +static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox); +} + +static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox); +} + +static const JNINativeMethod gPdfEditor_Methods[] = { + {"nativeOpen", "(IJ)J", (void*) nativeOpen}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, + {"nativeWrite", "(JI)V", (void*) nativeWrite}, + {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip}, + {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox}, + {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox}, + {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox}, + {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox} +}; + +int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { + const int result = RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, + NELEM(gPdfEditor_Methods)); + + jclass pointClass = FindClassOrDie(env, "android/graphics/Point"); + gPointClassInfo.x = GetFieldIDOrDie(env, pointClass, "x", "I"); + gPointClassInfo.y = GetFieldIDOrDie(env, pointClass, "y", "I"); + + jclass rectClass = FindClassOrDie(env, "android/graphics/Rect"); + gRectClassInfo.left = GetFieldIDOrDie(env, rectClass, "left", "I"); + gRectClassInfo.top = GetFieldIDOrDie(env, rectClass, "top", "I"); + gRectClassInfo.right = GetFieldIDOrDie(env, rectClass, "right", "I"); + gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClass, "bottom", "I"); + + return result; +}; + +}; |