diff --git a/.gitignore b/.gitignore index 995eb561..2208e473 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ src/main/obj .idea/ .DS_Store /build -/captures \ No newline at end of file +/captures +.cxx/ diff --git a/build.gradle b/build.gradle index 49863608..7cacdb23 100644 --- a/build.gradle +++ b/build.gradle @@ -5,46 +5,47 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.1.3' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.1' + classpath 'com.android.tools.build:gradle:3.5.0' +// classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } } apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' -ext { - bintrayRepo = 'maven' - bintrayName = 'pdfium-android' - - publishedGroupId = 'com.github.barteksc' - libraryName = 'PdfiumAndroid' - artifact = 'pdfium-android' - - libraryDescription = 'Fork of library for rendering PDFs on Android\'s Surface or Bitmap' - - siteUrl = 'https://github.com/barteksc/PdfiumAndroid' - gitUrl = 'https://github.com/barteksc/PdfiumAndroid.git' - - libraryVersion = '1.9.0' - - developerId = 'barteksc' - developerName = 'Bartosz Schiller' - developerEmail = 'barteksch@boo.pl' - - licenseName = 'The Apache Software License, Version 2.0' - licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - allLicenses = ["Apache-2.0"] -} +//ext { +// bintrayRepo = 'maven' +// bintrayName = 'pdfium-android' +// +// publishedGroupId = 'com.github.barteksc' +// libraryName = 'PdfiumAndroid' +// artifact = 'pdfium-android' +// +// libraryDescription = 'Fork of library for rendering PDFs on Android\'s Surface or Bitmap' +// +// siteUrl = 'https://github.com/barteksc/PdfiumAndroid' +// gitUrl = 'https://github.com/barteksc/PdfiumAndroid.git' +// +// libraryVersion = '1.9.1' +// +// developerId = 'barteksc' +// developerName = 'Bartosz Schiller' +// developerEmail = 'barteksch@boo.pl' +// +// licenseName = 'The Apache Software License, Version 2.0' +// licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' +// allLicenses = ["Apache-2.0"] +//} android { - compileSdkVersion 26 + compileSdkVersion 28 defaultConfig { - minSdkVersion 14 - targetSdkVersion 26 + minSdkVersion 16 + targetSdkVersion 28 versionCode 1 - versionName "1.9.0" + versionName "2.0.0" } buildTypes { release { @@ -53,12 +54,22 @@ android { } } + packagingOptions{ + pickFirst 'lib/**/*' + } + sourceSets{ main { jni.srcDirs = [] jniLibs.srcDir 'src/main/libs' } } + + externalNativeBuild { + ndkBuild { + path file('src/main/jni/Android.mk') + } + } } repositories { @@ -68,8 +79,8 @@ repositories { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:support-v4:26.1.0' + implementation 'com.android.support:support-v4:28.0.0' } -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' +//apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' +//apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7d202716..f76a2090 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew.bat b/gradlew.bat index aec99730..8a0b282a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,90 +1,90 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/com/shockwave/pdfium/PDFAreaModel.java b/src/main/java/com/shockwave/pdfium/PDFAreaModel.java new file mode 100644 index 00000000..a4cf5253 --- /dev/null +++ b/src/main/java/com/shockwave/pdfium/PDFAreaModel.java @@ -0,0 +1,46 @@ +package com.shockwave.pdfium; + +import android.graphics.RectF; + +public class PDFAreaModel { + private int pageIndex; + private int left; + private int top; + private int width; + private int height; + public int getPageIndex() { + return pageIndex; + } + public void setPageIndex(int pageIndex) { + this.pageIndex = pageIndex; + } + public int getLeft() { + return left; + } + public void setLeft(int left) { + this.left = left; + } + public int getTop() { + return top; + } + public void setTop(int top) { + this.top = top; + } + public int getWidth() { + return width; + } + public void setWidth(int width) { + this.width = width; + } + public int getHeight() { + return height; + } + public void setHeight(int height) { + this.height = height; + } + + public RectF toRectf(){ + return new RectF(left, top, left+width, top+height); + } + +} diff --git a/src/main/java/com/shockwave/pdfium/PDFView.java b/src/main/java/com/shockwave/pdfium/PDFView.java new file mode 100644 index 00000000..3d3f1a31 --- /dev/null +++ b/src/main/java/com/shockwave/pdfium/PDFView.java @@ -0,0 +1,387 @@ +package com.shockwave.pdfium; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.os.Handler; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Toast; + + +import java.io.File; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + + +public class PDFView extends View{ + + private static final long LONG_CLICK_TIME = 1500; + private Bitmap pdfBitmap = null; + private int currentIndex = 0; + private int totalCount = 0; + private float scale = 0f; + //上一个scale + private float lastScale = 0f; + private float defaultScale = 0f; + private float maxScale = 8; + private int translateX = 0; + private int translateY = 0; + private Paint p =new Paint(); + private int displayWidth = 0; + private int displayHeight = 0; + PDFViewListener listener; + List tipsModel; + private float pageWidth = 0; + private float pageHeight = 0; + final int REDRAW = 0; + String filePath = ""; + ExecutorService cachedThreadPool = Executors.newFixedThreadPool(1); + private int bitmapFactor = 2; + private Runnable longClickedRunable = null; + + private float sdkInnerScale = 1.f; + PdfiumCore core; + PdfDocument document; + private Handler handler = new Handler(){ + public void handleMessage(android.os.Message msg) { + switch(msg.what){ + case REDRAW: + int page = msg.arg1; + currentIndex = page; + if(listener!=null){ + listener.onPageChange(PDFView.this, currentIndex); + } + invalidate(); + break; + } + }; + }; + + public void setTips(List model){ + this.tipsModel = model; + } + public PDFView(Context context, String filePath) { + super(context); + this.filePath = filePath; + + initData(); + } + private void initData() { + + try{ + core = new PdfiumCore(getContext()); + ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(filePath), ParcelFileDescriptor.MODE_READ_ONLY); + document = core.newDocument(fd); + totalCount = core.getPageCount(document); + }catch(Exception e){ + e.printStackTrace(); + Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show(); + } + + + } + + public float[] getPDFLocation(float x, float y){ + float pdfX = (x - translateX)/(scale*bitmapFactor); + float pdfY = (y - translateY)/(scale*bitmapFactor); + pdfY = pageHeight-pdfY; + return new float[]{pdfX, pdfY}; + } + + private void parsePage(final int page) throws Exception{ + core.openPage(document, page); + int width = core.getPageWidthPoint(document, page); + int height = core.getPageHeightPoint(document, page); + pageWidth = width/sdkInnerScale; + pageHeight = height/sdkInnerScale; + + if(pdfBitmap!=null){ + pdfBitmap.recycle(); + pdfBitmap = null; + } + pdfBitmap = Bitmap.createBitmap((int)(pageWidth*bitmapFactor), (int)(pageHeight*bitmapFactor), Config.ARGB_8888); + + pdfBitmap.eraseColor(Color.WHITE); + core.renderPageBitmap(document, pdfBitmap, page,0, 0, (int)pageWidth*bitmapFactor, (int)pageHeight*bitmapFactor); + } + + public void setPage(int page){ + try{ + loadPage(page); + }catch(Exception e){ + e.printStackTrace(); + } + } + + public float getPageWidth(){ + return this.pageWidth; + } + + public float getPageHeight(){ + return this.pageHeight; + } + public int getCurrentPageIndex(){ + return currentIndex; + } + + public int getPageSize(){ + return totalCount; + } + + public void setListener(PDFViewListener listener){ + this.listener = listener; + } + + public void release(){ + if(core!=null){ + try{ + if(document!=null) { + core.closeDocument(document); + } + if(pdfBitmap!=null){ + pdfBitmap.recycle(); + pdfBitmap = null; + } + }catch(Exception e){ + e.printStackTrace(); + } + } + + } + + private void loadPage(final int page) throws Exception{ + cachedThreadPool.execute(new Runnable() { + + @Override + public void run() { + try{ + + parsePage(page); + Message msg = new Message(); + msg.what = REDRAW; + msg.arg1 = page; + handler.sendMessage(msg); + }catch(Exception e){ + e.printStackTrace(); + } + + + } + }); + } + + + + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + displayWidth = canvas.getWidth(); + displayHeight = canvas.getHeight(); + if(pdfBitmap!=null){ + int pdfWidth = pdfBitmap.getWidth(); + int pdfHeight = pdfBitmap.getHeight(); + //获取最小的缩放比 + if(scale==0f){ + scale = ((float)displayWidth/pdfWidth) < ((float)displayHeight/pdfHeight)?((float)displayWidth/pdfWidth):((float)displayHeight/pdfHeight); + defaultScale = scale; + } + Matrix matrix = new Matrix(); + matrix.postScale(scale, scale); + matrix.postTranslate(translateX, translateY); + canvas.drawBitmap(pdfBitmap, matrix, p); + } + if(tipsModel!=null){ + for(int i = 0;i < tipsModel.size();i++){ + PDFAreaModel model = tipsModel.get(i); + float left = model.getLeft(); + float top = pageHeight - model.getTop(); + float width = model.getWidth(); + float height = model.getHeight(); + left *=(scale*bitmapFactor); + top *=(scale*bitmapFactor); + width *=(scale*bitmapFactor); + height *=(scale*bitmapFactor); + RectF drawRect = new RectF(translateX+left, translateY+top-height, left+width+translateX, top+translateY); + if(model.getPageIndex() == currentIndex){ + Paint rectP = new Paint(); + rectP.setStyle(Style.STROKE); + rectP.setStrokeWidth(5); + rectP.setColor(Color.RED); + canvas.drawRect(drawRect, rectP); + } + } + } + + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + onPDFTouch(event); + return true; + } + boolean isLongClick = false; + public float accuracy = 10.0f; + + public float []lastx = new float[]{0,0}; + public float []lasty = new float[]{0,0}; + public float []downx = new float[]{0,0}; + public float []downy = new float[]{0,0}; + public float downScale = 0; + private void onPDFTouch(MotionEvent event) { + float []tmpx = new float[]{0,0}; + float []tmpy = new float[]{0,0}; + int actionIndex; + switch (event.getActionMasked()){ + case MotionEvent.ACTION_POINTER_DOWN: + actionIndex = event.getActionIndex(); + if(actionIndex>=2){ + isLongClick = false; + removeLongClickedEvent(); + return; + } + if(actionIndex==2){ + isLongClick = false; + removeLongClickedEvent(); + } + lastx[actionIndex] = event.getX(actionIndex); + lasty[actionIndex] = event.getY(actionIndex); + downx[actionIndex] = lastx[actionIndex]; + downy[actionIndex] = lasty[actionIndex]; + downScale = scale; + + break; + case MotionEvent.ACTION_POINTER_UP: + isLongClick = false; + removeLongClickedEvent(); + break; + case MotionEvent.ACTION_DOWN: + isLongClick = true; + removeLongClickedEvent(); + longClickedRunable = new Runnable() { + @Override + public void run() { + if(isLongClick&&listener!=null){ + listener.onLongClick(PDFView.this, downx[0], downy[0]); + } + } + }; + handler.postDelayed(longClickedRunable, LONG_CLICK_TIME); + lastx[0] = event.getX(0); + lasty[0] = event.getY(0); + downx[0] = lastx[0]; + downy[0] = lasty[0]; + break; + case MotionEvent.ACTION_UP: + isLongClick = false; + removeLongClickedEvent(); + break; + case MotionEvent.ACTION_MOVE: + if(event.getPointerCount()==1){ + //单手 + tmpx[0] = event.getX(0); + tmpy[0] = event.getY(0); + if(Math.abs(downx[0]-tmpx[0])>accuracy||Math.abs(downy[0]-tmpy[0])>accuracy){ + isLongClick = false; + removeLongClickedEvent(); + } + //移动图片 + float width = bitmapFactor*pageWidth*scale; + float height = bitmapFactor * pageHeight*scale; + float testX = translateX; + float testY = translateY; + testX -= (lastx[0]-tmpx[0]); + testY -= (lasty[0]-tmpy[0]); + if(translateX>0&&testX>0&&testX0&&testY>0&&testYdisplayWidth){ + translateX = (int)testX; + } + if(testY<0&&testY+height>displayHeight){ + translateY = (int)testY; + } + if(translateX<0&&testX<0&&testX>translateX){ + translateX = (int)testX; + } + if(translateY<0&&testY<0&&testY>translateY){ + translateY = (int)testY; + } + invalidate(); + + lastx[0] = tmpx[0]; + lasty[0] = tmpy[0]; + }else if(event.getPointerCount()>=2){ + isLongClick = false; + removeLongClickedEvent(); + //双手 + tmpx[0] = event.getX(0); + tmpy[0] = event.getY(0); + tmpx[1] = event.getX(1); + tmpy[1] = event.getY(1); + + float width = bitmapFactor*pageWidth*scale; + float height = bitmapFactor * pageHeight*scale; + float userScale = distance(tmpx[0], tmpy[0], tmpx[1], tmpy[1])/distance(downx[0], downy[0], downx[1], downy[1]); + float testScale = userScale*downScale; + if(testScalemaxScale){ + testScale = maxScale; + } + + float centerX = (downx[0]+downx[1])/2; + float centerY = (downy[0]+downy[1])/2; + float testX = (testScale/scale)*(translateX-centerX)+centerX; + float testY = (testScale/scale)*(translateY-centerY)+centerY; + + + if(Math.abs(testScale-scale)>0.01){ + translateX = (int)testX; + translateY = (int)testY; + scale = testScale; + } + invalidate(); + lastx[0] = tmpx[0]; + lasty[0] = tmpy[0]; + lastx[1] = tmpx[1]; + lasty[1] = tmpy[1]; + + + }else{ + isLongClick = false; + removeLongClickedEvent(); + } + break; + } + } + + private void removeLongClickedEvent() { + if(longClickedRunable!=null){ + handler.removeCallbacks(longClickedRunable); + longClickedRunable = null; + } + } + + + private float distance(float x1, float y1, float x2, float y2){ + return (float) Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); + } + + + +} diff --git a/src/main/java/com/shockwave/pdfium/PDFViewListener.java b/src/main/java/com/shockwave/pdfium/PDFViewListener.java new file mode 100644 index 00000000..bc21153c --- /dev/null +++ b/src/main/java/com/shockwave/pdfium/PDFViewListener.java @@ -0,0 +1,6 @@ +package com.shockwave.pdfium; + +public interface PDFViewListener { + void onPageChange(PDFView view, int pageIndex); + void onLongClick(PDFView view, float x, float y); +} diff --git a/src/main/java/com/shockwave/pdfium/PdfDocument.java b/src/main/java/com/shockwave/pdfium/PdfDocument.java index b5306a67..62cbf51f 100644 --- a/src/main/java/com/shockwave/pdfium/PdfDocument.java +++ b/src/main/java/com/shockwave/pdfium/PdfDocument.java @@ -111,4 +111,6 @@ public RectF getBounds() { public boolean hasPage(int index) { return mNativePagesPtr.containsKey(index); } + + /*package*/ final Map mNativeTextPagesPtr = new ArrayMap<>(); } diff --git a/src/main/java/com/shockwave/pdfium/PdfiumCore.java b/src/main/java/com/shockwave/pdfium/PdfiumCore.java index 0b2421e6..00c3507c 100644 --- a/src/main/java/com/shockwave/pdfium/PdfiumCore.java +++ b/src/main/java/com/shockwave/pdfium/PdfiumCore.java @@ -3,6 +3,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.RectF; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -13,6 +14,8 @@ import java.io.FileDescriptor; import java.io.IOException; import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; @@ -69,6 +72,12 @@ private native void nativeRenderPageBitmap(long pagePtr, Bitmap bitmap, int dpi, int drawSizeHor, int drawSizeVer, boolean renderAnnot); + // With form support + private native void nativeRenderPageBitmap(long docPtr, long pagePtr, Bitmap bitmap, int dpi, + int startX, int startY, + int drawSizeHor, int drawSizeVer, + boolean renderAnnot); + private native String nativeGetDocumentMetaText(long docPtr, String tag); private native Long nativeGetFirstChildBookmark(long docPtr, Long bookmarkPtr); @@ -79,6 +88,26 @@ private native void nativeRenderPageBitmap(long pagePtr, Bitmap bitmap, int dpi, private native long nativeGetBookmarkDestIndex(long docPtr, long bookmarkPtr); + private native long nativeLoadTextPage(long docPtr, long pagePtr); + + private native void nativeCloseTextPage(long pagePtr); + + private native int nativeTextCountChars(long textPagePtr); + + private native int nativeTextGetText(long textPagePtr, int start_index, int count, short[] result); + + private native int nativeTextGetUnicode(long textPagePtr, int index); + + private native double[] nativeTextGetCharBox(long textPagePtr, int index); + + private native int nativeTextGetCharIndexAtPos(long textPagePtr, double x, double y, double xTolerance, double yTolerance); + + private native int nativeTextCountRects(long textPagePtr, int start_index, int count); + + private native double[] nativeTextGetRect(long textPagePtr, int rect_index); + + private native int nativeTextGetBoundedText(long textPagePtr, double left, double top, double right, double bottom, short[] arr); + private native Size nativeGetPageSizeByIndex(long docPtr, int pageIndex, int dpi); private native long[] nativeGetPageLinks(long pagePtr); @@ -92,6 +121,9 @@ private native void nativeRenderPageBitmap(long pagePtr, Bitmap bitmap, int dpi, private native Point nativePageCoordsToDevice(long pagePtr, int startX, int startY, int sizeX, int sizeY, int rotate, double pageX, double pageY); + private native PointF nativeDeviceCoordsToPage(long pagePtr, int startX, int startY, int sizeX, + int sizeY, int rotate, int deviceX, int deviceY); + /* synchronize native methods */ private static final Object lock = new Object(); @@ -321,6 +353,29 @@ public void renderPageBitmap(PdfDocument doc, Bitmap bitmap, int pageIndex, } } + /** + * Render page fragment on {@link Bitmap}. This method allows to render annotations and forms.
+ * Page must be opened before rendering. + *

+ * For more info see {@link PdfiumCore#renderPageBitmap(PdfDocument, Bitmap, int, int, int, int, int)} + */ + public void renderPageBitmap(PdfDocument doc, Bitmap bitmap, int pageIndex, + int startX, int startY, int drawSizeX, int drawSizeY, + boolean renderAnnot, boolean renderForm) { + synchronized (lock) { + try { + nativeRenderPageBitmap(doc.mNativeDocPtr, doc.mNativePagesPtr.get(pageIndex), bitmap, mCurrentDpi, + startX, startY, drawSizeX, drawSizeY, renderAnnot); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + } + /** Release native resources and opened file */ public void closeDocument(PdfDocument doc) { synchronized (lock) { @@ -433,6 +488,27 @@ public Point mapPageCoordsToDevice(PdfDocument doc, int pageIndex, int startX, i return nativePageCoordsToDevice(pagePtr, startX, startY, sizeX, sizeY, rotate, pageX, pageY); } + /** + * Map device screen coordinates to page coordinates + * + * @param doc pdf document + * @param pageIndex index of page + * @param startX left pixel position of the display area in device coordinates + * @param startY top pixel position of the display area in device coordinates + * @param sizeX horizontal size (in pixels) for displaying the page + * @param sizeY vertical size (in pixels) for displaying the page + * @param rotate page orientation: 0 (normal), 1 (rotated 90 degrees clockwise), + * 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise) + * @param deviceX X value in page coordinates + * @param deviceY Y value in page coordinate + * @return mapped coordinates + */ + public PointF mapDeviceCoordsToPage(PdfDocument doc, int pageIndex, int startX, int startY, int sizeX, + int sizeY, int rotate, int deviceX, int deviceY) { + long pagePtr = doc.mNativePagesPtr.get(pageIndex); + return nativeDeviceCoordsToPage(pagePtr, startX, startY, sizeX, sizeY, rotate, deviceX, deviceY); + } + /** * @return mapped coordinates * @see PdfiumCore#mapPageCoordsToDevice(PdfDocument, int, int, int, int, int, int, double, double) @@ -446,4 +522,221 @@ public RectF mapRectToDevice(PdfDocument doc, int pageIndex, int startX, int sta coords.right, coords.bottom); return new RectF(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y); } + + /** + * @see PdfiumCore#mapDeviceCoordsToPage(PdfDocument, int, int, int, int, int, int, int, int) + * @return mapped coordinates + */ + public RectF mapRectToPage(PdfDocument doc, int pageIndex, int startX, int startY, int sizeX, + int sizeY, int rotate, RectF coords) { + + PointF leftTop = mapDeviceCoordsToPage(doc, pageIndex, startX, startY, sizeX, sizeY, rotate, + (int)coords.left, (int)coords.top); + PointF rightBottom = mapDeviceCoordsToPage(doc, pageIndex, startX, startY, sizeX, sizeY, rotate, + (int)coords.right, (int)coords.bottom); + return new RectF(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y); + } + + public long openTextPage(PdfDocument doc, int pageIndex) { + synchronized (lock) { + long page = openPage(doc, pageIndex); + Long textPagePtr = doc.mNativeTextPagesPtr.get(pageIndex); + if (textPagePtr == null) { + textPagePtr = nativeLoadTextPage(doc.mNativeDocPtr, page); + doc.mNativeTextPagesPtr.put(pageIndex, textPagePtr); + } + return textPagePtr; + } + + } + + public void closeTextPage(PdfDocument doc, int pageIndex) { + synchronized (lock) { + final Long nativeLoadTextPage = doc.mNativeTextPagesPtr.get(pageIndex); + if (nativeLoadTextPage != null) { + nativeCloseTextPage(nativeLoadTextPage); + doc.mNativeTextPagesPtr.remove(pageIndex); + } + } + } + + public long[] openTextPage(PdfDocument doc, int fromIndex, int toIndex) { + long[] textPagesPtr; + synchronized (lock) { + textPagesPtr = nativeLoadPages(doc.mNativeDocPtr, fromIndex, toIndex); + int pageIndex = fromIndex; + for (long page : textPagesPtr) { + if (pageIndex > toIndex) break; + doc.mNativeTextPagesPtr.put(pageIndex, page); + pageIndex++; + } + + return textPagesPtr; + } + } + + public int textPageCountChars(PdfDocument doc, int textPageIndex) { + synchronized (lock) { + try { + return nativeTextCountChars(doc.mNativeTextPagesPtr.get(textPageIndex)); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + return -1; + } + + public String textPageGetText(PdfDocument doc, int textPageIndex, int startIndex, int length) { + synchronized (lock) { + try { + short[] buf = new short[length+1]; + + int r = nativeTextGetText(doc.mNativeTextPagesPtr.get(textPageIndex), startIndex, length, buf); + + byte[] bytes = new byte[(r-1)*2]; + ByteBuffer bb = ByteBuffer.wrap(bytes); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for (int i = 0; i < r-1; i++) { + short s = buf[i]; + bb.putShort(s); + } + return new String(bytes, "UTF-16LE"); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + return null; + } + } + + + + + + + + + + + public char textPageGetUnicode(PdfDocument doc, int textPageIndex, int index) { + synchronized (lock) { + try { + return (char)nativeTextGetUnicode(doc.mNativeTextPagesPtr.get(textPageIndex), index); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + return 0; + } + + public RectF textPageGetCharBox(PdfDocument doc, int textPageIndex, int index) { + synchronized (lock) { + try { + double[] o = nativeTextGetCharBox(doc.mNativeTextPagesPtr.get(textPageIndex), index); + RectF r = new RectF(); + r.left = (float)o[0]; + r.right = (float)o[1]; + r.bottom = (float)o[2]; + r.top = (float)o[3]; + return r; + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + return null; + } + + public int textPageGetCharIndexAtPos(PdfDocument doc, int textPageIndex, double x, double y, double xTolerance, double yTolerance) { + synchronized (lock) { + try { + return nativeTextGetCharIndexAtPos(doc.mNativeTextPagesPtr.get(textPageIndex), x, y, xTolerance, yTolerance); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + return -1; + } + + public int textPageCountRects(PdfDocument doc, int textPageIndex, int start_index, int count) { + synchronized (lock) { + try { + return nativeTextCountRects(doc.mNativeTextPagesPtr.get(textPageIndex), start_index, count); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + return -1; + } + + public RectF textPageGetRect(PdfDocument doc, int textPageIndex, int rect_index) { + synchronized (lock) { + try { + double[] o = nativeTextGetRect(doc.mNativeTextPagesPtr.get(textPageIndex), rect_index); + RectF r = new RectF(); + r.left = (float)o[0]; + r.top = (float)o[1]; + r.right = (float)o[2]; + r.bottom = (float)o[3]; + return r; + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + } + return null; + } + + public String textPageGetBoundedText(PdfDocument doc, int textPageIndex, RectF rect, int length) { + synchronized (lock) { + try { + short[] buf = new short[length+1]; + + int r = nativeTextGetBoundedText(doc.mNativeTextPagesPtr.get(textPageIndex), rect.left, rect.top, rect.right, rect.bottom, buf); + + byte[] bytes = new byte[(r-1)*2]; + ByteBuffer bb = ByteBuffer.wrap(bytes); + bb.order(ByteOrder.LITTLE_ENDIAN); + + for (int i = 0; i < r-1; i++) { + short s = buf[i]; + bb.putShort(s); + } + return new String(bytes, "UTF-16LE"); + } catch (NullPointerException e) { + Log.e(TAG, "mContext may be null"); + e.printStackTrace(); + } catch (Exception e) { + Log.e(TAG, "Exception throw from native"); + e.printStackTrace(); + } + return null; + } + } } diff --git a/src/main/jni/Application.mk b/src/main/jni/Application.mk index df5c5088..ebba990d 100644 --- a/src/main/jni/Application.mk +++ b/src/main/jni/Application.mk @@ -2,11 +2,9 @@ APP_STL := c++_shared APP_CPPFLAGS += -fexceptions #For ANativeWindow support -APP_PLATFORM = android-14 +APP_PLATFORM = android-16 APP_ABI := armeabi-v7a \ arm64-v8a \ - mips \ - mips64 \ x86 \ x86_64 \ No newline at end of file diff --git a/src/main/jni/lib/.gitkeep b/src/main/jni/lib/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/main/jni/lib/mips/libc++_shared.so b/src/main/jni/lib/mips/libc++_shared.so deleted file mode 100755 index 533554ca..00000000 Binary files a/src/main/jni/lib/mips/libc++_shared.so and /dev/null differ diff --git a/src/main/jni/lib/mips/libmodft2.so b/src/main/jni/lib/mips/libmodft2.so deleted file mode 100755 index 2aeb607d..00000000 Binary files a/src/main/jni/lib/mips/libmodft2.so and /dev/null differ diff --git a/src/main/jni/lib/mips/libmodpdfium.so b/src/main/jni/lib/mips/libmodpdfium.so deleted file mode 100755 index 94a7100a..00000000 Binary files a/src/main/jni/lib/mips/libmodpdfium.so and /dev/null differ diff --git a/src/main/jni/lib/mips/libmodpng.so b/src/main/jni/lib/mips/libmodpng.so deleted file mode 100755 index bcb4bb73..00000000 Binary files a/src/main/jni/lib/mips/libmodpng.so and /dev/null differ diff --git a/src/main/jni/lib/mips64/libc++_shared.so b/src/main/jni/lib/mips64/libc++_shared.so deleted file mode 100755 index 3844c40a..00000000 Binary files a/src/main/jni/lib/mips64/libc++_shared.so and /dev/null differ diff --git a/src/main/jni/lib/mips64/libmodft2.so b/src/main/jni/lib/mips64/libmodft2.so deleted file mode 100755 index 1e179a4d..00000000 Binary files a/src/main/jni/lib/mips64/libmodft2.so and /dev/null differ diff --git a/src/main/jni/lib/mips64/libmodpdfium.so b/src/main/jni/lib/mips64/libmodpdfium.so deleted file mode 100755 index fd261ca6..00000000 Binary files a/src/main/jni/lib/mips64/libmodpdfium.so and /dev/null differ diff --git a/src/main/jni/lib/mips64/libmodpng.so b/src/main/jni/lib/mips64/libmodpng.so deleted file mode 100755 index aa45e700..00000000 Binary files a/src/main/jni/lib/mips64/libmodpng.so and /dev/null differ diff --git a/src/main/jni/src/mainJNILib.cpp b/src/main/jni/src/mainJNILib.cpp index a888e69f..f1f809c9 100644 --- a/src/main/jni/src/mainJNILib.cpp +++ b/src/main/jni/src/mainJNILib.cpp @@ -16,9 +16,22 @@ using namespace android; #include #include +#include +#include #include #include +#define RGB565_R(p) ((((p) & 0xF800) >> 11) << 3) +#define RGB565_G(p) ((((p) & 0x7E0 ) >> 5) << 2) +#define RGB565_B(p) ( ((p) & 0x1F ) << 3) +#define MAKE_RGB565(r,g,b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3)) + +#define RGBA_A(p) (((p) & 0xFF000000) >> 24) +#define RGBA_R(p) (((p) & 0x00FF0000) >> 16) +#define RGBA_G(p) (((p) & 0x0000FF00) >> 8) +#define RGBA_B(p) ((p) & 0x000000FF) +#define MAKE_RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) + static Mutex sLibraryLock; static int sLibraryReferenceCount = 0; @@ -53,6 +66,7 @@ class DocumentFile { public: FPDF_DOCUMENT pdfDocument = NULL; + FPDF_FORMHANDLE m_form = NULL; size_t fileSize; DocumentFile() { initLibraryIfNeed(); } @@ -169,6 +183,60 @@ void rgbBitmapTo565(void *source, int sourceStride, void *dest, AndroidBitmapInf extern "C" { //For JNI support +int PDFForm_Alert(IPDF_JSPLATFORM*, FPDF_WIDESTRING, FPDF_WIDESTRING, int, int) +{ + LOGE("%s", "Form_Alert called.\n"); + return 0; +} + +void changeBitmapBR(AndroidBitmapInfo info, void * pixels){ + int x = 0, y = 0; + // From top to bottom + for (y = 0; y < info.height; ++y) { + // From left to right + for (x = 0; x < info.width; ++x) { + int a = 0, r = 0, g = 0, b = 0; + void *pixel = NULL; + // Get each pixel by format + pixel = ((uint32_t *)pixels) + y * info.width + x; + uint32_t v = *(uint32_t *)pixel; + a = RGBA_A(v); + r = RGBA_R(v); + g = RGBA_G(v); + b = RGBA_B(v); + *((uint32_t *)pixel) = MAKE_RGBA(b, g, r, a); + } + } +} + + +bool PDFForm_Render(DocumentFile *docFile) +{ + IPDF_JSPLATFORM platform_callbacks; + FPDF_FORMFILLINFO form_callbacks; + + + memset(&platform_callbacks, '\0', sizeof(platform_callbacks)); + platform_callbacks.version = 1; + platform_callbacks.app_alert = PDFForm_Alert; + + + memset(&form_callbacks, '\0', sizeof(form_callbacks)); + form_callbacks.version = 1; + form_callbacks.m_pJsPlatform = &platform_callbacks; + docFile->m_form = FPDFDOC_InitFormFillEnvironment(docFile->pdfDocument, &form_callbacks); + if(docFile->m_form == NULL) + return false; + + FPDF_SetFormFieldHighlightColor(docFile->m_form, 0, 0xFFFFFF); + FPDF_SetFormFieldHighlightAlpha(docFile->m_form, 100); + FORM_DoDocumentJSAction(docFile->m_form); + FORM_DoDocumentOpenAction(docFile->m_form); + LOGE("%s", "add form"); + return true; + +} + static int getBlock(void* param, unsigned long position, unsigned char* outBuffer, unsigned long size) { const int fd = reinterpret_cast(param); @@ -466,6 +534,7 @@ JNI_FUNC(void, PdfiumCore, nativeRenderPage)(JNI_ARGS, jlong pagePtr, jobject ob ANativeWindow_release(nativeWindow); } +/* JNI_FUNC(void, PdfiumCore, nativeRenderPageBitmap)(JNI_ARGS, jlong pagePtr, jobject bitmap, jint dpi, jint startX, jint startY, jint drawSizeHor, jint drawSizeVer, @@ -512,6 +581,86 @@ JNI_FUNC(void, PdfiumCore, nativeRenderPageBitmap)(JNI_ARGS, jlong pagePtr, jobj format = FPDFBitmap_BGRA; } + FPDF_BITMAP pdfBitmap = FPDFBitmap_CreateEx( canvasHorSize, canvasVerSize, + format, tmp, sourceStride); + + if(drawSizeHor < canvasHorSize || drawSizeVer < canvasVerSize){ + FPDFBitmap_FillRect( pdfBitmap, 0, 0, canvasHorSize, canvasVerSize, + 0x848484FF); //Gray + } + + int baseHorSize = (canvasHorSize < drawSizeHor)? canvasHorSize : (int)drawSizeHor; + int baseVerSize = (canvasVerSize < drawSizeVer)? canvasVerSize : (int)drawSizeVer; + int baseX = (startX < 0)? 0 : (int)startX; + int baseY = (startY < 0)? 0 : (int)startY; + int flags = FPDF_REVERSE_BYTE_ORDER; + + if(renderAnnot) { + flags |= FPDF_ANNOT; + } + + FPDFBitmap_FillRect( pdfBitmap, baseX, baseY, baseHorSize, baseVerSize, + 0xFFFFFFFF); //White + + FPDF_RenderPageBitmap( pdfBitmap, page, + startX, startY, + (int)drawSizeHor, (int)drawSizeVer, + 0, flags ); + + if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) { + rgbBitmapTo565(tmp, sourceStride, addr, &info); + free(tmp); + } + + AndroidBitmap_unlockPixels(env, bitmap); +}*/ + +JNI_FUNC(void, PdfiumCore, nativeRenderPageBitmap)(JNI_ARGS, jlong docPtr, jlong pagePtr, jobject bitmap, + jint dpi, jint startX, jint startY, + jint drawSizeHor, jint drawSizeVer, + jboolean renderAnnot){ + DocumentFile *doc = reinterpret_cast(docPtr); + FPDF_PAGE page = reinterpret_cast(pagePtr); + + if(page == NULL || bitmap == NULL){ + LOGE("Render page pointers invalid"); + return; + } + + AndroidBitmapInfo info; + int ret; + if((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { + LOGE("Fetching bitmap info failed: %s", strerror(ret * -1)); + return; + } + + int canvasHorSize = info.width; + int canvasVerSize = info.height; + + if(info.format != ANDROID_BITMAP_FORMAT_RGBA_8888 && info.format != ANDROID_BITMAP_FORMAT_RGB_565){ + LOGE("Bitmap format must be RGBA_8888 or RGB_565"); + return; + } + + void *addr; + if( (ret = AndroidBitmap_lockPixels(env, bitmap, &addr)) != 0 ){ + LOGE("Locking bitmap failed: %s", strerror(ret * -1)); + return; + } + + void *tmp; + int format; + int sourceStride; + if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) { + tmp = malloc(canvasVerSize * canvasHorSize * sizeof(rgb)); + sourceStride = canvasHorSize * sizeof(rgb); + format = FPDFBitmap_BGR; + } else { + tmp = addr; + sourceStride = info.stride; + format = FPDFBitmap_BGRA; + } + FPDF_BITMAP pdfBitmap = FPDFBitmap_CreateEx( canvasHorSize, canvasVerSize, format, tmp, sourceStride); @@ -545,10 +694,22 @@ JNI_FUNC(void, PdfiumCore, nativeRenderPageBitmap)(JNI_ARGS, jlong pagePtr, jobj (int)drawSizeHor, (int)drawSizeVer, 0, flags ); - if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) { + /*if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) { rgbBitmapTo565(tmp, sourceStride, addr, &info); free(tmp); - } + }*/ + + changeBitmapBR(info, addr); + PDFForm_Render(doc); + FORM_OnAfterLoadPage(page, doc->m_form); + FORM_DoPageAAction(page, doc->m_form, FPDFPAGE_AACTION_OPEN); + FPDF_FFLDraw(doc->m_form, + pdfBitmap, page, + startX, startY, + (int)drawSizeHor, (int)drawSizeVer, + 0, flags ); + + changeBitmapBR(info, addr); AndroidBitmap_unlockPixels(env, bitmap); } @@ -688,4 +849,183 @@ JNI_FUNC(jobject, PdfiumCore, nativePageCoordsToDevice)(JNI_ARGS, jlong pagePtr, return env->NewObject(clazz, constructorID, deviceX, deviceY); } +JNI_FUNC(jobject, PdfiumCore, nativeDeviceCoordsToPage)(JNI_ARGS, jlong pagePtr, jint startX, jint startY, jint sizeX, + jint sizeY, jint rotate, jint deviceX, jint deviceY) { + FPDF_PAGE page = reinterpret_cast(pagePtr); + double pageX, pageY; + + FPDF_DeviceToPage(page, startX, startY, sizeX, sizeY, rotate, deviceX, deviceY, &pageX, &pageY); + + jclass clazz = env->FindClass("android/graphics/PointF"); + jmethodID constructorID = env->GetMethodID(clazz, "", "(FF)V"); + return env->NewObject(clazz, constructorID, pageX, pageY); +} + +////////////////////////////////////////// +//Begin FPDF_TEXTPAGE section + +static jlong loadTextPageInternal(JNIEnv *env, DocumentFile *doc, jlong pagePtr){ + try{ + if(doc == NULL) throw "Get page document null"; + + FPDF_PAGE page = reinterpret_cast(pagePtr); + if(page != NULL){ + FPDF_TEXTPAGE textPage = FPDFText_LoadPage(page); + if (textPage == NULL) { + throw "Loaded text page is null"; + } + return reinterpret_cast(textPage); + }else{ + throw "Load page null"; + } + }catch(const char *msg){ + LOGE("%s", msg); + + jniThrowException(env, "java/lang/IllegalStateException", + "cannot load text page"); + + return -1; + } +} + +static void closeTextPageInternal(jlong textPagePtr) { FPDFText_ClosePage(reinterpret_cast(textPagePtr)); } + +JNI_FUNC(jlong, PdfiumCore, nativeLoadTextPage)(JNI_ARGS, jlong docPtr, jlong pagePtr){ + DocumentFile *doc = reinterpret_cast(docPtr); + return loadTextPageInternal(env, doc, pagePtr); +} +JNI_FUNC(jlongArray, PdfiumCore, nativeLoadTextPages)(JNI_ARGS, jlong docPtr, jint fromIndex, jint toIndex){ + DocumentFile *doc = reinterpret_cast(docPtr); + + if(toIndex < fromIndex) return NULL; + jlong pages[ toIndex - fromIndex + 1 ]; + + int i; + for(i = 0; i <= (toIndex - fromIndex); i++){ + pages[i] = loadTextPageInternal(env, doc, (int)(i + fromIndex)); + } + + jlongArray javaPages = env -> NewLongArray( (jsize)(toIndex - fromIndex + 1) ); + env -> SetLongArrayRegion(javaPages, 0, (jsize)(toIndex - fromIndex + 1), (const jlong*)pages); + + return javaPages; +} + +JNI_FUNC(void, PdfiumCore, nativeCloseTextPage)(JNI_ARGS, jlong textPagePtr){ closeTextPageInternal(textPagePtr); } +JNI_FUNC(void, PdfiumCore, nativeCloseTextPages)(JNI_ARGS, jlongArray textPagesPtr){ + int length = (int)(env -> GetArrayLength(textPagesPtr)); + jlong *textPages = env -> GetLongArrayElements(textPagesPtr, NULL); + + int i; + for(i = 0; i < length; i++){ closeTextPageInternal(textPages[i]); } +} + +//DLLEXPORT int STDCALL FPDFText_CountChars(FPDF_TEXTPAGE text_page); +JNI_FUNC(jint, PdfiumCore, nativeTextCountChars)(JNI_ARGS, jlong textPagePtr){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + return (jint)FPDFText_CountChars(textPage); +} + +//DLLEXPORT unsigned int STDCALL FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index); +JNI_FUNC(jint, PdfiumCore, nativeTextGetUnicode)(JNI_ARGS, jlong textPagePtr, jint index){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + return (jint)FPDFText_GetUnicode(textPage, (int)index); +} + +/*DLLEXPORT void STDCALL FPDFText_GetCharBox(FPDF_TEXTPAGE text_page, + int index, + double* left, + double* right, + double* bottom, + double* top);*/ +JNI_FUNC(jdoubleArray, PdfiumCore, nativeTextGetCharBox)(JNI_ARGS, jlong textPagePtr, jint index){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + jdoubleArray result = env->NewDoubleArray(4); + if (result == NULL) { + return NULL; + } + double fill[4]; + FPDFText_GetCharBox(textPage, (int)index, &fill[0], &fill[1], &fill[2], &fill[3]); + env->SetDoubleArrayRegion(result, 0, 4, (jdouble*)fill); + return result; +} + +/*DLLEXPORT int STDCALL FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page, + double x, + double y, + double xTolerance, + double yTolerance);*/ +JNI_FUNC(jint, PdfiumCore, nativeTextGetCharIndexAtPos)(JNI_ARGS, jlong textPagePtr, jdouble x, jdouble y, jdouble xTolerance, jdouble yTolerance){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + return (jint)FPDFText_GetCharIndexAtPos(textPage, (double)x, (double)y, (double)xTolerance, (double)yTolerance); +} + +/*DLLEXPORT int STDCALL FPDFText_GetText(FPDF_TEXTPAGE text_page, + int start_index, + int count, + unsigned short* result);*/ +JNI_FUNC(jint, PdfiumCore, nativeTextGetText)(JNI_ARGS, jlong textPagePtr, jint start_index, jint count, jshortArray result){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + jboolean isCopy = 0; + unsigned short *arr = (unsigned short *)env->GetShortArrayElements(result, &isCopy); + jint output = (jint)FPDFText_GetText(textPage, (int)start_index, (int)count, arr); + if (isCopy) { + env->SetShortArrayRegion(result, 0, output, (jshort*)arr); + env->ReleaseShortArrayElements(result, (jshort*)arr, JNI_ABORT); + } + return output; +} + +/*DLLEXPORT int STDCALL FPDFText_CountRects(FPDF_TEXTPAGE text_page, + int start_index, + int count);*/ +JNI_FUNC(jint, PdfiumCore, nativeTextCountRects)(JNI_ARGS, jlong textPagePtr, jint start_index, jint count){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + return (jint)FPDFText_CountRects(textPage, (int)start_index, (int) count); +} + +/*DLLEXPORT void STDCALL FPDFText_GetRect(FPDF_TEXTPAGE text_page, + int rect_index, + double* left, + double* top, + double* right, + double* bottom);*/ +JNI_FUNC(jdoubleArray, PdfiumCore, nativeTextGetRect)(JNI_ARGS, jlong textPagePtr, jint rect_index){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + jdoubleArray result = env->NewDoubleArray(4); + if (result == NULL) { + return NULL; + } + double fill[4]; + FPDFText_GetRect(textPage, (int)rect_index, &fill[0], &fill[1], &fill[2], &fill[3]); + env->SetDoubleArrayRegion(result, 0, 4, (jdouble*)fill); + return result; +} + +/*DLLEXPORT int STDCALL FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page, + double left, + double top, + double right, + double bottom, + unsigned short* buffer, + int buflen); +*/ + +JNI_FUNC(jint, PdfiumCore, nativeTextGetBoundedText)(JNI_ARGS, jlong textPagePtr, jdouble left, jdouble top, jdouble right, jdouble bottom, jshortArray arr){ + FPDF_TEXTPAGE *textPage = reinterpret_cast(textPagePtr); + jboolean isCopy = 0; + unsigned short *buffer = NULL; + int bufLen = 0; + if (arr != NULL) { + buffer = (unsigned short *)env->GetShortArrayElements(arr, &isCopy); + bufLen = env->GetArrayLength(arr); + } + jint output = (jint)FPDFText_GetBoundedText(textPage, (double)left, (double)top, (double)right, (double)bottom, buffer, bufLen); + if (isCopy) { + env->SetShortArrayRegion(arr, 0, output, (jshort*)buffer); + env->ReleaseShortArrayElements(arr, (jshort*)buffer, JNI_ABORT); + } + return output; +} + }//extern C