PdfEditor.cpp revision 96bf5985d5a360568832fd26b6d5b44236c9343e
162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav/*
262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * Copyright (C) 2014 The Android Open Source Project
362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav *
462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * Licensed under the Apache License, Version 2.0 (the "License");
562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * you may not use this file except in compliance with the License.
662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * You may obtain a copy of the License at
762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav *
862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav *      http://www.apache.org/licenses/LICENSE-2.0
962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav *
1062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * Unless required by applicable law or agreed to in writing, software
1162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * distributed under the License is distributed on an "AS IS" BASIS,
1262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * See the License for the specific language governing permissions and
1462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * limitations under the License.
1562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav */
16121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn#define LOG_TAG "PdfEditor"
17121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn
18121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn#include <sys/types.h>
19121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn#include <unistd.h>
20121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn
21121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn#include <vector>
22121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn
2396bf5985d5a360568832fd26b6d5b44236c9343eMark Salyzyn#include <log/log.h>
24121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn#include <utils/Log.h>
2562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
2662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav#include "jni.h"
2762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav#include "JNIHelp.h"
28f4e341d99c4c29cb6cc7380829a316cf4847f3dfAndreas Gampe
29f4e341d99c4c29cb6cc7380829a316cf4847f3dfAndreas Gampe#pragma GCC diagnostic push
30f4e341d99c4c29cb6cc7380829a316cf4847f3dfAndreas Gampe#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
3162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav#include "fpdfview.h"
32fbd02886559e7ca2b619ed0d9bff76b10cdb23c3Svet Ganov#include "fpdf_edit.h"
33fbd02886559e7ca2b619ed0d9bff76b10cdb23c3Svet Ganov#include "fpdf_save.h"
34bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav#include "fsdk_rendercontext.h"
35bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav#include "fpdf_transformpage.h"
36f4e341d99c4c29cb6cc7380829a316cf4847f3dfAndreas Gampe#pragma GCC diagnostic pop
37f4e341d99c4c29cb6cc7380829a316cf4847f3dfAndreas Gampe
38bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav#include "SkMatrix.h"
3962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
40ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe#include <core_jni_helpers.h>
4162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
4262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavnamespace android {
4362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
44bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavenum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP};
45bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
46bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic struct {
47bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    jfieldID x;
48bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    jfieldID y;
49bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav} gPointClassInfo;
50bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
51bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic struct {
52bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    jfieldID left;
53bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    jfieldID top;
54bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    jfieldID right;
55bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    jfieldID bottom;
56bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav} gRectClassInfo;
57bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
58be2e7121872afe2f561f20c4660da854c31e1b66Philip P. Moltmann// Also used in PdfRenderer.cpp
59be2e7121872afe2f561f20c4660da854c31e1b66Philip P. Moltmannint sUnmatchedPdfiumInitRequestCount = 0;
6062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
6162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic void initializeLibraryIfNeeded() {
62be2e7121872afe2f561f20c4660da854c31e1b66Philip P. Moltmann    if (sUnmatchedPdfiumInitRequestCount == 0) {
63fbd02886559e7ca2b619ed0d9bff76b10cdb23c3Svet Ganov        FPDF_InitLibrary();
6462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
65be2e7121872afe2f561f20c4660da854c31e1b66Philip P. Moltmann    sUnmatchedPdfiumInitRequestCount++;
6662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
6762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
6862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic void destroyLibraryIfNeeded() {
69be2e7121872afe2f561f20c4660da854c31e1b66Philip P. Moltmann    sUnmatchedPdfiumInitRequestCount--;
70be2e7121872afe2f561f20c4660da854c31e1b66Philip P. Moltmann    if (sUnmatchedPdfiumInitRequestCount == 0) {
7162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav       FPDF_DestroyLibrary();
7262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
7362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
7462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
7562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic int getBlock(void* param, unsigned long position, unsigned char* outBuffer,
7662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        unsigned long size) {
7762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    const int fd = reinterpret_cast<intptr_t>(param);
7862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    const int readCount = pread(fd, outBuffer, size, position);
7962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    if (readCount < 0) {
8062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        ALOGE("Cannot read from file descriptor. Error:%d", errno);
8162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        return 0;
8262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
8362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    return 1;
8462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
8562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
8662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) {
8762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    initializeLibraryIfNeeded();
8862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
8962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_FILEACCESS loader;
9062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    loader.m_FileLen = size;
9162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    loader.m_Param = reinterpret_cast<void*>(intptr_t(fd));
9262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    loader.m_GetBlock = &getBlock;
9362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
9462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL);
9562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
9662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    if (!document) {
9762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        const long error = FPDF_GetLastError();
98fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov        switch (error) {
99fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            case FPDF_ERR_PASSWORD:
100fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            case FPDF_ERR_SECURITY: {
1013a091b79978caa9b5d58ae19f693279e5a717c2aDan Albert                jniThrowExceptionFmt(env, "java/lang/SecurityException",
1023a091b79978caa9b5d58ae19f693279e5a717c2aDan Albert                        "cannot create document. Error: %ld", error);
103fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            } break;
104fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            default: {
1053a091b79978caa9b5d58ae19f693279e5a717c2aDan Albert                jniThrowExceptionFmt(env, "java/io/IOException",
1063a091b79978caa9b5d58ae19f693279e5a717c2aDan Albert                        "cannot create document. Error: %ld", error);
107fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            } break;
108fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov        }
10962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        destroyLibraryIfNeeded();
11062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        return -1;
11162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
11262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
11362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    return reinterpret_cast<jlong>(document);
11462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
11562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
11662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
11762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
11862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_CloseDocument(document);
11962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    destroyLibraryIfNeeded();
12062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
12162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
12262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
12362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
12462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    return FPDF_GetPageCount(document);
12562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
12662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
12762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic jint nativeRemovePage(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex) {
12862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
12962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDFPage_Delete(document, pageIndex);
13062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    return FPDF_GetPageCount(document);
13162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
13262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
13362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstruct PdfToFdWriter : FPDF_FILEWRITE {
13462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    int dstFd;
13562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav};
13662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
13762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic bool writeAllBytes(const int fd, const void* buffer, const size_t byteCount) {
13862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    char* writeBuffer = static_cast<char*>(const_cast<void*>(buffer));
13962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    size_t remainingBytes = byteCount;
14062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    while (remainingBytes > 0) {
14162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes);
14262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        if (writtenByteCount == -1) {
14362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav            if (errno == EINTR) {
14462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav                continue;
14562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav            }
146121ab7001b3d818f7e7cf3cd4a95bcb3453b44e2Mark Salyzyn            ALOGE("Error writing to buffer: %d", errno);
14762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav            return false;
14862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        }
14962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        remainingBytes -= writtenByteCount;
15062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        writeBuffer += writtenByteCount;
15162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
15262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    return true;
15362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
15462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
15562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic int writeBlock(FPDF_FILEWRITE* owner, const void* buffer, unsigned long size) {
15662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    const PdfToFdWriter* writer = reinterpret_cast<PdfToFdWriter*>(owner);
15762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    const bool success = writeAllBytes(writer->dstFd, buffer, size);
15892ddfcfff5cec32456e2d1e0d96671f1eb0e2986Bernhard Rosenkränzer    if (!success) {
15962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        ALOGE("Cannot write to file descriptor. Error:%d", errno);
16062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        return 0;
16162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
16262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    return 1;
16362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
16462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
16562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavstatic void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) {
16662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
16762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    PdfToFdWriter writer;
16862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    writer.dstFd = fd;
16962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    writer.WriteBlock = &writeBlock;
17062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    const bool success = FPDF_SaveAsCopy(document, &writer, FPDF_NO_INCREMENTAL);
17162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    if (!success) {
17246d8444631b4b1253a76bfcc78a29d26014d022fDan Albert        jniThrowExceptionFmt(env, "java/io/IOException",
17346d8444631b4b1253a76bfcc78a29d26014d022fDan Albert                "cannot write to fd. Error: %d", errno);
17462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        destroyLibraryIfNeeded();
17562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    }
17662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav}
17762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
178bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
179bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) {
180bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
181bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
182bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    CPDF_Page* page = (CPDF_Page*) FPDF_LoadPage(document, pageIndex);
183bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!page) {
184bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jniThrowException(env, "java/lang/IllegalStateException",
185bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav                "cannot open page");
186bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return;
187bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
188bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
189bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    double width = 0;
190bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    double height = 0;
191bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
192bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
193bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!result) {
194bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jniThrowException(env, "java/lang/IllegalStateException",
195bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav                    "cannot get page size");
196bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return;
197bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
198bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
19979bd8d48ad69c39834291809fe78ea478d067b68Philip P. Moltmann    CFX_Matrix matrix;
200bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
201bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    SkMatrix* skTransform = reinterpret_cast<SkMatrix*>(transformPtr);
202bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
203bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    SkScalar transformValues[6];
20471487eb0ceb2b7dea02649e78d99bb5952f5eaefMike Reed    if (!skTransform->asAffine(transformValues)) {
20571487eb0ceb2b7dea02649e78d99bb5952f5eaefMike Reed        jniThrowException(env, "java/lang/IllegalArgumentException",
20671487eb0ceb2b7dea02649e78d99bb5952f5eaefMike Reed                "transform matrix has perspective. Only affine matrices are allowed.");
20771487eb0ceb2b7dea02649e78d99bb5952f5eaefMike Reed        return;
20871487eb0ceb2b7dea02649e78d99bb5952f5eaefMike Reed    }
209bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
210bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    // PDF's coordinate system origin is left-bottom while in graphics it
211bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    // is the top-left. So, translate the PDF coordinates to ours.
212bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    matrix.Set(1, 0, 0, -1, 0, page->GetPageHeight());
213bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
214bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    // Apply the transformation what was created in our coordinates.
215bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    matrix.Concat(transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY],
216bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav            transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY],
217bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav            transformValues[SkMatrix::kATransX], transformValues[SkMatrix::kATransY]);
218bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
219bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    // Translate the result back to PDF coordinates.
220bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    matrix.Concat(1, 0, 0, -1, 0, page->GetPageHeight());
221bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
222bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FS_MATRIX transform = {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f};
223bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom};
224bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
225bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDFPage_TransFormWithClip(page, &transform, &clip);
226bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
227bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_ClosePage(page);
228bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
229bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
230bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic void nativeGetPageSize(JNIEnv* env, jclass thiz, jlong documentPtr,
231bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jint pageIndex, jobject outSize) {
232bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
233bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
234bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
235bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!page) {
236bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jniThrowException(env, "java/lang/IllegalStateException",
237bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav                "cannot open page");
238bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return;
239bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
240bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
241bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    double width = 0;
242bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    double height = 0;
243bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
244bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
245bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!result) {
246bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jniThrowException(env, "java/lang/IllegalStateException",
247bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav                    "cannot get page size");
248bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return;
249bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
250bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
251bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    env->SetIntField(outSize, gPointClassInfo.x, width);
252bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    env->SetIntField(outSize, gPointClassInfo.y, height);
253bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
254bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_ClosePage(page);
255bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
256bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
257bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) {
258bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
259bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_BOOL success = FPDF_VIEWERREF_GetPrintScaling(document);
260bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    return success ? JNI_TRUE : JNI_FALSE;
261bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
262bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
263bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
264bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        PageBox pageBox, jobject outBox) {
265bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
266bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
267bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
268bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!page) {
269bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jniThrowException(env, "java/lang/IllegalStateException",
270bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav                "cannot open page");
271bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return false;
272bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
273bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
274bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    float left;
275bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    float top;
276bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    float right;
277bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    float bottom;
278bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
279bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA)
280bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom)
281bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom);
282bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
283bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_ClosePage(page);
284bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
285bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!success) {
286bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return false;
287bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
288bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
289bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    env->SetIntField(outBox, gRectClassInfo.left, (int) left);
290bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    env->SetIntField(outBox, gRectClassInfo.top, (int) top);
291bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    env->SetIntField(outBox, gRectClassInfo.right, (int) right);
292bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom);
293bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
294bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    return true;
295bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
296bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
297bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
298bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jobject outMediaBox) {
299bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA,
300bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav            outMediaBox);
301bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    return success ? JNI_TRUE : JNI_FALSE;
302bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
303bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
304bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
305bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jobject outMediaBox) {
306bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP,
307bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav         outMediaBox);
308bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    return success ? JNI_TRUE : JNI_FALSE;
309bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
310bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
311bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
312bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        PageBox pageBox, jobject box) {
313bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
314bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
315bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
316bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (!page) {
317bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jniThrowException(env, "java/lang/IllegalStateException",
318bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav                "cannot open page");
319bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        return;
320bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
321bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
322bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const int left = env->GetIntField(box, gRectClassInfo.left);
323bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const int top = env->GetIntField(box, gRectClassInfo.top);
324bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const int right = env->GetIntField(box, gRectClassInfo.right);
325bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    const int bottom = env->GetIntField(box, gRectClassInfo.bottom);
326bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
327bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    if (pageBox == PAGE_BOX_MEDIA) {
328bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        FPDFPage_SetMediaBox(page, left, top, right, bottom);
329bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    } else {
330bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        FPDFPage_SetCropBox(page, left, top, right, bottom);
331bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    }
332bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
333bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    FPDF_ClosePage(page);
334bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
335bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
336bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
337bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jobject mediaBox) {
338bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox);
339bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
340bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
341bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslavstatic void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
342bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav        jobject mediaBox) {
343bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox);
344bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav}
345bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
34676f6a86de25e1bf74717e047e55fd44b089673f3Daniel Micaystatic const JNINativeMethod gPdfEditor_Methods[] = {
34762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    {"nativeOpen", "(IJ)J", (void*) nativeOpen},
34862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    {"nativeClose", "(J)V", (void*) nativeClose},
34962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
35062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav    {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage},
351bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeWrite", "(JI)V", (void*) nativeWrite},
352bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip},
353bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize},
354bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting},
355bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox},
356bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox},
357bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox},
358bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox}
35962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav};
36062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
36162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavint register_android_graphics_pdf_PdfEditor(JNIEnv* env) {
3627d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    const int result = RegisterMethodsOrDie(
36362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav            env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods,
36462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav            NELEM(gPdfEditor_Methods));
365bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
3667d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    jclass pointClass = FindClassOrDie(env, "android/graphics/Point");
3677d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    gPointClassInfo.x = GetFieldIDOrDie(env, pointClass, "x", "I");
3687d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    gPointClassInfo.y = GetFieldIDOrDie(env, pointClass, "y", "I");
369bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
3707d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    jclass rectClass = FindClassOrDie(env, "android/graphics/Rect");
3717d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    gRectClassInfo.left = GetFieldIDOrDie(env, rectClass, "left", "I");
3727d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    gRectClassInfo.top = GetFieldIDOrDie(env, rectClass, "top", "I");
3737d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    gRectClassInfo.right = GetFieldIDOrDie(env, rectClass, "right", "I");
3747d13d9db1ef90063cb542ccd6554042a6a3263b7Andreas Gampe    gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClass, "bottom", "I");
375bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav
376bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav    return result;
37762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav};
37862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav
37962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav};
380