diff options
author | Paul Gardiner <paulg.artifex@glidos.net> | 2013-01-29 14:53:35 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2013-01-31 11:17:40 +0000 |
commit | cea4109277d5e558f4cfa0707aee7e371ed06504 (patch) | |
tree | fa33497f5666cdf196f523fc412739ac63b2e04c | |
parent | aeed1e16e440cefbf5137eef7f4af608b0c70569 (diff) | |
download | mupdf-cea4109277d5e558f4cfa0707aee7e371ed06504.tar.xz |
Android: implement strikeout annotation creation
-rw-r--r-- | android/jni/mupdf.c | 121 | ||||
-rw-r--r-- | android/res/drawable-ldpi/ic_strike.png | bin | 0 -> 440 bytes | |||
-rw-r--r-- | android/res/drawable-mdpi/ic_strike.png | bin | 0 -> 589 bytes | |||
-rw-r--r-- | android/res/layout/buttons.xml | 10 | ||||
-rw-r--r-- | android/res/values/strings.xml | 1 | ||||
-rw-r--r-- | android/src/com/artifex/mupdfdemo/MuPDFActivity.java | 12 | ||||
-rw-r--r-- | android/src/com/artifex/mupdfdemo/MuPDFCore.java | 6 | ||||
-rw-r--r-- | android/src/com/artifex/mupdfdemo/MuPDFPageView.java | 5 | ||||
-rw-r--r-- | android/src/com/artifex/mupdfdemo/PageView.java | 54 |
9 files changed, 208 insertions, 1 deletions
diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c index bc49ac6c..f0a625eb 100644 --- a/android/jni/mupdf.c +++ b/android/jni/mupdf.c @@ -685,6 +685,7 @@ static char *widget_type_string(int t) case FZ_WIDGET_TYPE_TEXT: return "text"; case FZ_WIDGET_TYPE_LISTBOX: return "listbox"; case FZ_WIDGET_TYPE_COMBOBOX: return "combobox"; + default: return "non-widget"; } } JNIEXPORT jboolean JNICALL @@ -1230,7 +1231,7 @@ JNI_FN(MuPDFCore_text)(JNIEnv * env, jobject thiz) { jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); if (cls != NULL) - (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_searchPage"); + (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_text"); (*env)->DeleteLocalRef(env, cls); return NULL; @@ -1239,6 +1240,124 @@ JNI_FN(MuPDFCore_text)(JNIEnv * env, jobject thiz) return barr; } +JNIEXPORT void JNICALL +JNI_FN(MuPDFCore_addStrikeOutAnnotationInternal)(JNIEnv * env, jobject thiz, jobjectArray lines) +{ + globals *glo = get_globals(env, thiz); + fz_context *ctx = glo->ctx; + fz_document *doc = glo->doc; + fz_interactive *idoc = fz_interact(doc); + page_cache *pc = &glo->pages[glo->current]; + jclass rect_cls; + jfieldID x0_fid, y0_fid, x1_fid, y1_fid; + int i, n; + fz_path *path = NULL; + fz_stroke_state *stroke = NULL; + fz_device *dev = NULL; + fz_display_list *strike_list; + + if (idoc == NULL) + return; + + strike_list = fz_new_display_list(ctx); + + fz_var(path); + fz_var(stroke); + fz_var(dev); + fz_try(ctx) + { + fz_annot *annot; + fz_matrix ctm; + + float color[3] = {1.0, 0.0, 0.0}; + float zoom = glo->resolution / 72; + zoom = 1.0 / zoom; + ctm = fz_scale(zoom, zoom); + LOGI("P1 %f", zoom); + rect_cls = (*env)->FindClass(env, "android.graphics.RectF"); + if (rect_cls == NULL) fz_throw(ctx, "FindClass"); + LOGI("P2"); + x0_fid = (*env)->GetFieldID(env, rect_cls, "left", "F"); + if (x0_fid == NULL) fz_throw(ctx, "GetFieldID(left)"); + LOGI("P3"); + y0_fid = (*env)->GetFieldID(env, rect_cls, "top", "F"); + if (y0_fid == NULL) fz_throw(ctx, "GetFieldID(top)"); + LOGI("P4"); + x1_fid = (*env)->GetFieldID(env, rect_cls, "right", "F"); + if (x1_fid == NULL) fz_throw(ctx, "GetFieldID(right)"); + LOGI("P5"); + y1_fid = (*env)->GetFieldID(env, rect_cls, "bottom", "F"); + if (y1_fid == NULL) fz_throw(ctx, "GetFieldID(bottom)"); + LOGI("P6"); + + n = (*env)->GetArrayLength(env, lines); + LOGI("nlines=%d", n); + + dev = fz_new_list_device(ctx, strike_list); + + for (i = 0; i < n; i++) + { + jobject line = (*env)->GetObjectArrayElement(env, lines, i); + float x0 = (*env)->GetFloatField(env, line, x0_fid); + float y0 = (*env)->GetFloatField(env, line, y0_fid); + float x1 = (*env)->GetFloatField(env, line, x1_fid); + float y1 = (*env)->GetFloatField(env, line, y1_fid); + float vcenter = (y0 + y1)/2; + float thickness = y1 - y0; + + if (!stroke || stroke->linewidth != thickness) + { + if (stroke) + { + // assert(path) + fz_stroke_path(dev, path, stroke, ctm, fz_device_rgb, color, 1.0); + LOGI("Path stroked"); + fz_drop_stroke_state(ctx, stroke); + stroke = NULL; + fz_free_path(ctx, path); + path = NULL; + } + + stroke = fz_new_stroke_state(ctx); + LOGI("thickness(%f)", thickness); + stroke->linewidth = thickness; + path = fz_new_path(ctx); + } + + fz_moveto(ctx, path, x0, vcenter); + LOGI("moveto(%f,%f)", x0, vcenter); + fz_lineto(ctx, path, x1, vcenter); + LOGI("lineto(%f,%f)", x1, vcenter); + } + + if (stroke) + { + fz_stroke_path(dev, path, stroke, ctm, fz_device_rgb, color, 1.0); + LOGI("Path stroked"); + } + + annot = fz_make_annot(idoc, pc->page, FZ_ANNOT_STRIKEOUT); + fz_set_annot_appearance(idoc, annot, strike_list); + dump_annotation_display_lists(glo); + } + fz_always(ctx) + { + fz_free_device(dev); + fz_drop_stroke_state(ctx, stroke); + fz_free_path(ctx, path); + fz_free_display_list(ctx, strike_list); + } + fz_catch(ctx) + { + fz_free_device(dev); + LOGE("addStrikeOutAnnotation: %s failed", ctx->error->message); + jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + if (cls != NULL) + (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_searchPage"); + (*env)->DeleteLocalRef(env, cls); + } +} + static void close_doc(globals *glo) { int i; diff --git a/android/res/drawable-ldpi/ic_strike.png b/android/res/drawable-ldpi/ic_strike.png Binary files differnew file mode 100644 index 00000000..36ebe0f3 --- /dev/null +++ b/android/res/drawable-ldpi/ic_strike.png diff --git a/android/res/drawable-mdpi/ic_strike.png b/android/res/drawable-mdpi/ic_strike.png Binary files differnew file mode 100644 index 00000000..9b2cf71a --- /dev/null +++ b/android/res/drawable-mdpi/ic_strike.png diff --git a/android/res/layout/buttons.xml b/android/res/layout/buttons.xml index 910076b7..6764dfd1 100644 --- a/android/res/layout/buttons.xml +++ b/android/res/layout/buttons.xml @@ -139,6 +139,16 @@ android:src="@drawable/ic_cancel" /> <ImageButton + android:id="@+id/strikeOutButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toLeftOf="@+id/copySelectButton" + android:contentDescription="@string/strike_out" + android:background="@drawable/button" + android:src="@drawable/ic_strike" /> + + <ImageButton android:id="@+id/copySelectButton" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 1468d963..28995d2e 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -20,4 +20,5 @@ <string name="select">Select</string> <string name="search">Search</string> <string name="copy">Copy</string> + <string name="strike_out">Strike out</string> </resources> diff --git a/android/src/com/artifex/mupdfdemo/MuPDFActivity.java b/android/src/com/artifex/mupdfdemo/MuPDFActivity.java index f095a041..5ad3d630 100644 --- a/android/src/com/artifex/mupdfdemo/MuPDFActivity.java +++ b/android/src/com/artifex/mupdfdemo/MuPDFActivity.java @@ -106,6 +106,7 @@ public class MuPDFActivity extends Activity private ImageButton mSelectButton; private ImageButton mCancelSelectButton; private ImageButton mCopySelectButton; + private ImageButton mStrikeOutButton; private ImageButton mCancelButton; private ImageButton mOutlineButton; private ViewAnimator mTopBarSwitcher; @@ -652,6 +653,16 @@ public class MuPDFActivity extends Activity } }); + mStrikeOutButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + PageView pageView = (PageView) mDocView.getDisplayedView(); + if (pageView != null) + pageView.strikeOutSelection(); + mSelecting = false; + mTopBarSwitcher.setDisplayedChild(0); + } + }); + mCancelButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { searchModeOff(); @@ -943,6 +954,7 @@ public class MuPDFActivity extends Activity mSelectButton = (ImageButton)mButtonsView.findViewById(R.id.selectButton); mCancelSelectButton = (ImageButton)mButtonsView.findViewById(R.id.cancelSelectButton); mCopySelectButton = (ImageButton)mButtonsView.findViewById(R.id.copySelectButton); + mStrikeOutButton = (ImageButton)mButtonsView.findViewById(R.id.strikeOutButton); mCancelButton = (ImageButton)mButtonsView.findViewById(R.id.cancel); mOutlineButton = (ImageButton)mButtonsView.findViewById(R.id.outlineButton); mTopBarSwitcher = (ViewAnimator)mButtonsView.findViewById(R.id.switcher); diff --git a/android/src/com/artifex/mupdfdemo/MuPDFCore.java b/android/src/com/artifex/mupdfdemo/MuPDFCore.java index ed9ded92..73db5073 100644 --- a/android/src/com/artifex/mupdfdemo/MuPDFCore.java +++ b/android/src/com/artifex/mupdfdemo/MuPDFCore.java @@ -38,6 +38,7 @@ public class MuPDFCore int patchW, int patchH); private native RectF[] searchPage(String text); private native TextChar[][][][] text(); + private native void addStrikeOutAnnotationInternal(RectF[] lines); private native int passClickEventInternal(int page, float x, float y); private native void setFocusedWidgetChoiceSelectedInternal(String [] selected); private native String [] getFocusedWidgetChoiceSelected(); @@ -235,6 +236,11 @@ public class MuPDFCore return lns.toArray(new TextWord[lns.size()][]); } + public synchronized void addStrikeOutAnnotation(int page, RectF[] lines) { + gotoPage(page); + addStrikeOutAnnotationInternal(lines); + } + public synchronized boolean hasOutline() { return hasOutlineInternal(); } diff --git a/android/src/com/artifex/mupdfdemo/MuPDFPageView.java b/android/src/com/artifex/mupdfdemo/MuPDFPageView.java index 64877198..69157209 100644 --- a/android/src/com/artifex/mupdfdemo/MuPDFPageView.java +++ b/android/src/com/artifex/mupdfdemo/MuPDFPageView.java @@ -223,6 +223,11 @@ public class MuPDFPageView extends PageView { } @Override + protected void addStrikeOut(RectF[] lines) { + mCore.addStrikeOutAnnotation(mPageNumber, lines); + } + + @Override public void setPage(final int page, PointF size) { mLoadWidgetAreas = new AsyncTask<Void,Void,RectF[]> () { @Override diff --git a/android/src/com/artifex/mupdfdemo/PageView.java b/android/src/com/artifex/mupdfdemo/PageView.java index b793db3f..04065174 100644 --- a/android/src/com/artifex/mupdfdemo/PageView.java +++ b/android/src/com/artifex/mupdfdemo/PageView.java @@ -103,6 +103,8 @@ public abstract class PageView extends ViewGroup { private static final int LINK_COLOR = 0x80AC7225; private static final int BACKGROUND_COLOR = 0xFFFFFFFF; private static final int PROGRESS_DIALOG_DELAY = 200; + private static final float LINE_THICKNESS = 0.07f; + private static final float STRIKE_HEIGHT = 0.375f; private final Context mContext; protected int mPageNumber; private Point mParentSize; @@ -112,6 +114,7 @@ public abstract class PageView extends ViewGroup { private ImageView mEntire; // Image rendered at minimum zoom private BitmapHolder mEntireBmh; private AsyncTask<Void,Void,TextWord[][]> mGetText; + private AsyncTask<RectF[],Void,Void> mAddStrikeOut; private AsyncTask<Void,Void,LinkInfo[]> mGetLinkInfo; private AsyncTask<Void,Void,Bitmap> mDrawEntire; @@ -144,6 +147,7 @@ public abstract class PageView extends ViewGroup { protected abstract Bitmap updatePage(BitmapHolder h, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); protected abstract LinkInfo[] getLinkInfo(); protected abstract TextWord[][] getText(); + protected abstract void addStrikeOut(RectF[] lines); private void reinit() { // Cancel pending render task @@ -441,6 +445,56 @@ public abstract class PageView extends ViewGroup { return true; } + public void strikeOutSelection() { + final ArrayList<RectF> lines = new ArrayList<RectF>(); + TextSelector sel = new TextSelector(mText, mSelectBox) { + RectF rect; + + @Override + protected void onStartLine() { + rect = new RectF(); + } + + @Override + protected void onWord(TextWord word) { + rect.union(word); + } + + @Override + protected void onEndLine() { + if (!rect.isEmpty()) { + // These are vertical lines so we can specify + // both position and thickness with a RectF + float vcenter = rect.bottom - (rect.bottom - rect.top)*STRIKE_HEIGHT; + float thickness = (rect.bottom - rect.top)*LINE_THICKNESS; + rect.top = vcenter - thickness/2; + rect.bottom = vcenter + thickness/2; + lines.add(rect); + } + } + }; + + sel.select(); + + mAddStrikeOut = new AsyncTask<RectF[],Void,Void>() { + @Override + protected Void doInBackground(RectF[]... params) { + addStrikeOut(params[0]); + return null; + } + + @Override + protected void onPostExecute(Void result) { + update(); + } + }; + + mAddStrikeOut.execute(lines.toArray(new RectF[lines.size()])); + + mSelectBox = null; + mSearchView.invalidate(); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int x, y; |