diff options
Diffstat (limited to 'platform/java')
26 files changed, 4571 insertions, 0 deletions
diff --git a/platform/java/com/artifex/mupdf/fitz/Annotation.java b/platform/java/com/artifex/mupdf/fitz/Annotation.java new file mode 100644 index 00000000..6e6dd1c1 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Annotation.java @@ -0,0 +1,27 @@ +package com.artifex.mupdf.fitz; + +public class Annotation +{ + // Private data + private long nativeAnnot = 0; + + // Construction + private Annotation(long ptr) + { + nativeAnnot = ptr; + } + + // Operation + public native void run(Device dev, Matrix ctm, Cookie cookie); + + // FIXME: Write accessors + + // Destruction + public void destroy() + { + finalize(); + nativeAnnot = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/AwtDrawDevice.java b/platform/java/com/artifex/mupdf/fitz/AwtDrawDevice.java new file mode 100644 index 00000000..4e6fb33d --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/AwtDrawDevice.java @@ -0,0 +1,12 @@ +package com.artifex.mupdf.fitz; + +public final class AwtDrawDevice extends CDevice +{ + // Construction + public AwtDrawDevice(int rgba[], int width, int height) + { + nativeDevice = newNative(rgba, width, height); + } + + private native long newNative(int rgba[], int width, int height); +} diff --git a/platform/java/com/artifex/mupdf/fitz/CDevice.java b/platform/java/com/artifex/mupdf/fitz/CDevice.java new file mode 100644 index 00000000..3bfe9c70 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/CDevice.java @@ -0,0 +1,49 @@ +package com.artifex.mupdf.fitz; + +public abstract class CDevice extends Device +{ + // Private data + private Object nativeResource = null; + protected long nativeInfo = 0; + + // Operation + public native final void beginPage(Rect rect, Matrix ctm); + public native final void endPage(); + + public native final void fillPath(Path path, int even_odd, Matrix ctm, ColorSpace cs, float color[], float alpha); + public native final void strokePath(Path path, StrokeState stroke, Matrix ctm, ColorSpace cs, float color[], float alpha); + public native final void clipPath(Path path, Rect rect, int even_odd, Matrix ctm); + public native final void clipStrokePath(Path path, Rect rect, StrokeState stroke, Matrix ctm); + + public native final void fillText(Text text, Matrix ctm, ColorSpace cs, float color[], float alpha); + public native final void strokeText(Text text, StrokeState stroke, Matrix ctm, ColorSpace cs, float color[], float alpha); + public native final void clipText(Text text, Matrix ctm, int accumulate); + public native final void clipStrokeText(Text text, StrokeState stroke, Matrix ctm); + public native final void ignoreText(Text text, Matrix ctm); + + public native final void fillShade(Shade shade, Matrix ctm, float alpha); + public native final void fillImage(Image img, Matrix ctm, float alpha); + public native final void fillImageMask(Image img, Matrix ctm, ColorSpace cs, float color[], float alpha); + public native final void clipImageMask(Image img, Rect rect, Matrix ctm); + + public native final void popClip(); + + public native final void beginMask(Rect rect, int luminosity, ColorSpace cs, float bc[]); + public native final void endMask(); + public native final void beginGroup(Rect rect, int isolated, int knockout, int blendmode, float alpha); + public native final void endGroup(); + + public native final int beginTile(Rect area, Rect view, float xstep, float ystep, Matrix ctm, int id); + public native final void endTile(); + + // Destruction + public final void destroy() + { + finalize(); + nativeDevice = 0; + nativeResource = null; + nativeInfo = 0; + } + + protected native final void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/ColorSpace.java b/platform/java/com/artifex/mupdf/fitz/ColorSpace.java new file mode 100644 index 00000000..cad952c0 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/ColorSpace.java @@ -0,0 +1,34 @@ +package com.artifex.mupdf.fitz; + +public class ColorSpace +{ + // Private data + private long nativeColorSpace; + + // Statics + public static ColorSpace DeviceGray = new ColorSpace(newDeviceGray()); + public static ColorSpace DeviceRGB = new ColorSpace(newDeviceRGB()); + public static ColorSpace DeviceCMYK = new ColorSpace(newDeviceCMYK()); + + private static native long newDeviceGray(); + private static native long newDeviceRGB(); + private static native long newDeviceCMYK(); + + // Construction + private ColorSpace(long l) + { + nativeColorSpace = l; + } + + // Accessors + public native int getNumComponents(); + + // Destruction + public final void destroy() + { + finalize(); + nativeColorSpace = 0; + } + + protected final native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Context.java b/platform/java/com/artifex/mupdf/fitz/Context.java new file mode 100644 index 00000000..1baafb01 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Context.java @@ -0,0 +1,20 @@ +package com.artifex.mupdf.fitz; + +// This class handles the loading of the MuPDF shared library, together +// with the ThreadLocal magic to get the required context. +// +// The only publicly accessible method here is Context.setStoreSize, which +// sets the store size to use. This must be called before any other MuPDF +// function. +public class Context +{ + // Load our native library + static + { + System.loadLibrary("mupdf"); + } + + // FIXME: We should support the store size being changed dynamically. + // This requires changes within the MuPDF core. + //public native static void setStoreSize(long newSize); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Cookie.java b/platform/java/com/artifex/mupdf/fitz/Cookie.java new file mode 100644 index 00000000..b6761b42 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Cookie.java @@ -0,0 +1,30 @@ +package com.artifex.mupdf.fitz; + +public class Cookie +{ + // Private data + private long nativeCookie = 0; + + // Construction + public Cookie() + { + nativeCookie = newNative(); + } + + private native long newNative(); + + // Operation + public native void abort(); + + //FIXME: Cookie accessors + + // Destruction + protected native void finalize(); + + public void destroy() + { + finalize(); + nativeCookie = 0; + } + +} diff --git a/platform/java/com/artifex/mupdf/fitz/Device.java b/platform/java/com/artifex/mupdf/fitz/Device.java new file mode 100644 index 00000000..366022e9 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Device.java @@ -0,0 +1,172 @@ +package com.artifex.mupdf.fitz; + +public abstract class Device +{ + /* Flags */ + public static final int FZ_DEVFLAG_MASK = 1; + public static final int FZ_DEVFLAG_COLOR = 2; + public static final int FZ_DEVFLAG_UNCACHEABLE = 4; + public static final int FZ_DEVFLAG_FILLCOLOR_UNDEFINED = 8; + public static final int FZ_DEVFLAG_STROKECOLOR_UNDEFINED = 16; + public static final int FZ_DEVFLAG_STARTCAP_UNDEFINED = 32; + public static final int FZ_DEVFLAG_DASHCAP_UNDEFINED = 64; + public static final int FZ_DEVFLAG_ENDCAP_UNDEFINED = 128; + public static final int FZ_DEVFLAG_LINEJOIN_UNDEFINED = 256; + public static final int FZ_DEVFLAG_MITERLIMIT_UNDEFINED = 512; + public static final int FZ_DEVFLAG_LINEWIDTH_UNDEFINED = 1024; + + /* PDF 1.4 -- standard separable */ + public static final int FZ_BLEND_NORMAL = 0; + public static final int FZ_BLEND_MULTIPLY = 1; + public static final int FZ_BLEND_SCREEN = 2; + public static final int FZ_BLEND_OVERLAY = 3; + public static final int FZ_BLEND_DARKEN = 4; + public static final int FZ_BLEND_LIGHTEN = 5; + public static final int FZ_BLEND_COLOR_DODGE = 6; + public static final int FZ_BLEND_COLOR_BURN = 7; + public static final int FZ_BLEND_HARD_LIGHT = 8; + public static final int FZ_BLEND_SOFT_LIGHT = 9; + public static final int FZ_BLEND_DIFFERENCE = 10; + public static final int FZ_BLEND_EXCLUSION = 11; + + /* PDF 1.4 -- standard non-separable */ + public static final int FZ_BLEND_HUE = 12; + public static final int FZ_BLEND_SATURATION = 13; + public static final int FZ_BLEND_COLOR = 14; + public static final int FZ_BLEND_LUMINOSITY = 15; + + /* For packing purposes */ + public static final int FZ_BLEND_MODEMASK = 15; + public static final int FZ_BLEND_ISOLATED = 16; + public static final int FZ_BLEND_KNOCKOUT = 32; + + /* To implement your own device in Java, you should define your own + * class that extends this one, and override as many of the following + * functions as is appropriate. For example: + * + * class ImageTraceDevice extends Device + * { + * void fillImage(Image img, Matrix ctx, float alpha) { + * Debug.Log("Image!"); + * } + * }; + * + * There is no constructor here, as no one will ever construct a + * Device without subclassing. + */ + + /* Everything under here is private implementation details. + * Ideally we'd like to hide these from prying eyes, but Java doesn't + * allow that. + */ + + public static final int FZ_IGNORE_IMAGE = 1; + public static final int FZ_IGNORE_SHADE = 2; + + /* None of our device functions do anything. Anyone interested will + * override them in a subclass either in Java, or (as a subclass of + * CDevice) in C. + */ + public void beginPage(Rect rect, Matrix ctm) + { + } + + public void endPage() + { + } + + public void fillPath(Path path, int even_odd, Matrix ctm, ColorSpace cs, float color[], float alpha) + { + } + + public void strokePath(long ctx, Path path, StrokeState stroke, Matrix ctm, ColorSpace cs, float color[], float alpha) + { + } + + public void clipPath(Path path, Rect rect, int even_odd, Matrix ctm) + { + } + + public void clipStrokePath(Path path, Rect rect, StrokeState stroke, Matrix ctm) + { + } + + public void fillText(Text text, Matrix ctm, ColorSpace cs, float color[], float alpha) + { + } + + public void strokeText(Text text, StrokeState stroke, Matrix ctm, ColorSpace cs, float color[], float alpha) + { + } + + public void clipText(Text text, Matrix ctm) + { + } + + public void clipStrokeText(Text text, StrokeState stroke, Matrix ctm) + { + } + + public void ignoreText(Text text, Matrix ctm) + { + } + + public void fillShade(Shade shade, Matrix ctm, float alpha) + { + } + + public void fillImage(Image img, Matrix ctm, float alpha) + { + } + + public void fillImageMask(Image img, Matrix ctm, ColorSpace cs, float color[], float alpha) + { + } + + public void clipImageMask(Image img, Rect rect, Matrix ctm) + { + } + + public void popClip() + { + } + + public void beginMask(Rect rect, int luminosity, ColorSpace cs, float bc[]) + { + } + + public void endMask() + { + } + + public void beginGroup(Rect rect, int isolated, int knockout, int blendmode, float alpha) + { + } + + public void endGroup() + { + } + + public int beginTile(Rect area, Rect view, float xstep, float ystep, Matrix ctm, int id) + { + return 0; + } + + public void endTile() + { + } + + /* An accessor for device hints */ + final native int getHints(); + final native void enableDeviceHints(int hints); + final native void disableDeviceHints(int hints); + + // Destruction + public void destroy() + { + } + + // Private data. + // All java devices MUST leave this as 0. + protected long nativeDevice; +} diff --git a/platform/java/com/artifex/mupdf/fitz/DisplayList.java b/platform/java/com/artifex/mupdf/fitz/DisplayList.java new file mode 100644 index 00000000..8a2515e4 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/DisplayList.java @@ -0,0 +1,32 @@ +package com.artifex.mupdf.fitz; + +public class DisplayList +{ + // Private data + protected long nativeDisplayList; + + // Constructions + public DisplayList() + { + nativeDisplayList = newNative(); + } + + private native long newNative(); + + // Operation + public native void run(Device device, Matrix ctm, Rect scissor, Cookie cookie); + + public void run(Device device, Matrix ctm, Cookie cookie) + { + run(device, ctm, null, cookie); + } + + // Destruction + public void destroy() + { + finalize(); + nativeDisplayList = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/DisplayListDevice.java b/platform/java/com/artifex/mupdf/fitz/DisplayListDevice.java new file mode 100644 index 00000000..de3142dc --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/DisplayListDevice.java @@ -0,0 +1,14 @@ +package com.artifex.mupdf.fitz; + +import android.graphics.Bitmap; + +public final class DisplayListDevice extends CDevice +{ + // Construction + public DisplayListDevice(DisplayList list) + { + nativeDevice = newNative(list); + } + + private native long newNative(DisplayList list); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Document.java b/platform/java/com/artifex/mupdf/fitz/Document.java new file mode 100644 index 00000000..8c3136df --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Document.java @@ -0,0 +1,60 @@ +package com.artifex.mupdf.fitz; + +import java.lang.ref.WeakReference; + +public class Document +{ + // Private data + private long nativeDocument = 0; + + // Construction + public Document(String filename) throws Exception + { + nativeDocument = newNative(filename); + if (nativeDocument == 0) + throw(new Exception("Failed to load Document")); + } + private native final long newNative(String filename); + + // FIXME: Should support opening java streams and from byte buffers etc. + // Streams would need to be seekable. + public Document(byte buffer[], String magic) throws Exception + { + nativeDocument = 0;//newFromBufferNative(buffer, magic); + if (nativeDocument == 0) + throw(new Exception("Failed to load Document")); + } + //private native final long newFromBufferNative(byte buffer[], String magic); + + //public Document(SeekableStream stream, String magic) throws Exception + //{ + // nativeDocument = newFromStreamNative(stream, magic); + // if (nativeDocument == 0) + // throw(new Exception("Failed to load Document")); + //} + //private native final long newFromBufferNative(SeekableStream stream, String magic); + + // Operation + public native boolean needsPassword(); + + public native boolean authenticatePassword(String password); + + public native int countPages(); + + public native Page getPage(int n); + + public native String getFileFormat(); + + public native boolean isUnencryptedPDF(); + + public native Outline getOutline(); + + // Destruction + public void destroy() + { + finalize(); + nativeDocument = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Font.java b/platform/java/com/artifex/mupdf/fitz/Font.java new file mode 100644 index 00000000..7630dfa5 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Font.java @@ -0,0 +1,22 @@ +package com.artifex.mupdf.fitz; + +public class Font +{ + // Private data + private long nativeFont; + + // Construction + private Font(long font) + { + nativeFont = font; + } + + // Destruction + public void destroy() + { + finalize(); + nativeFont = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Image.java b/platform/java/com/artifex/mupdf/fitz/Image.java new file mode 100644 index 00000000..a736ce5f --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Image.java @@ -0,0 +1,56 @@ +package com.artifex.mupdf.fitz; +import android.graphics.Bitmap; + +public class Image +{ + // Private data + private long nativeImage = 0; + + // Construction + Image(Bitmap bm) throws Exception + { + if (bm == null) + throw new Exception("null Bitmap passed to Image"); + nativeImage = newFromBitmapNative(bm, null); + } + + Image(Bitmap bm, Image mask) throws Exception + { + if (bm == null) + throw new Exception("null Bitmap passed to Image"); + nativeImage = newFromBitmapNative(bm, mask); + } + + private native final long newFromBitmapNative(Bitmap bm, Image mask); + + // Private constructor for the C to use. Any objects created by the + // C are done for purposes of calling back to a java device, and + // should therefore be considered const. + private Image(long l) + { + nativeImage = l; + } + + // Accessors + public native int getWidth(); + public native int getHeight(); + public native int getNumComponents(); + public native int getBitsPerComponent(); + public native int getXResolution(); + public native int getYResolution(); + public native boolean getImageMask(); + public native boolean getInterpolate(); + public native Image getMask(); + + // FIXME: Get data back? + // FIXME: Create images from data or java streams? + + // Destruction + public void destroy() + { + finalize(); + nativeImage = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Link.java b/platform/java/com/artifex/mupdf/fitz/Link.java new file mode 100644 index 00000000..7c63fcf9 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Link.java @@ -0,0 +1,27 @@ +package com.artifex.mupdf.fitz; + +public class Link +{ + // Private data + private long nativeLink = 0; + + // Construction + private Link(long l) + { + nativeLink = l; + } + + // Operation + public native Link getNext(); + + //FIXME: Accessors + + // Destruction + public void destroy() + { + finalize(); + nativeLink = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Matrix.java b/platform/java/com/artifex/mupdf/fitz/Matrix.java new file mode 100644 index 00000000..ede57ccc --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Matrix.java @@ -0,0 +1,59 @@ +package com.artifex.mupdf.fitz; + +public class Matrix +{ + public float a; + public float b; + public float c; + public float d; + public float e; + public float f; + + public Matrix(float a, float b, float c, float d, float e, float f) + { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + } + + public Matrix(float a, float d) + { + this.a = a; + this.b = 0; + this.c = 0; + this.d = d; + this.e = 0; + this.f = 0; + } + + public Matrix(float a) + { + this.a = a; + this.b = 0; + this.c = 0; + this.d = a; + this.e = 0; + this.f = 0; + } + + public Matrix concat(Matrix m) + { + float a = this.a * m.a + this.b * m.c; + float b = this.a * m.b + this.b * m.d; + float c = this.c * m.a + this.d * m.c; + float d = this.c * m.b + this.d * m.d; + float e = this.e * m.a + this.f * m.c + m.e; + this.f = this.e * m.b + this.f * m.d + m.f; + + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + + return this; + } +} diff --git a/platform/java/com/artifex/mupdf/fitz/Outline.java b/platform/java/com/artifex/mupdf/fitz/Outline.java new file mode 100644 index 00000000..1b808d7f --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Outline.java @@ -0,0 +1,22 @@ +package com.artifex.mupdf.fitz; + +public class Outline +{ + // Private data + private long nativeOutline = 0; + + // Construction + private Outline(long out) + { + nativeOutline = out; + } + + // Destruction + public void destroy() + { + finalize(); + nativeOutline = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Page.java b/platform/java/com/artifex/mupdf/fitz/Page.java new file mode 100644 index 00000000..46d598ee --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Page.java @@ -0,0 +1,37 @@ +package com.artifex.mupdf.fitz; + +public class Page +{ + // Private data + private long nativePage = 0; + private Annotation nativeAnnots[]; + + // Construction + private Page(long page) + { + nativePage = page; + nativeAnnots = null; + } + + // Operation + public native Rect bound(); + public native void run(Device dev, Matrix ctm, Cookie cookie); + public native void runPageContents(Device dev, Matrix ctm, Cookie cookie); + public native Annotation[] getAnnotations(); + + // FIXME: Later + public native Link[] getLinks(); + + // FIXME: Later. Much later. + //fz_transition *fz_page_presentation(fz_document *doc, fz_page *page, float *duration); + + // Destruction + public void destroy() + { + finalize(); + nativePage = 0; + nativeAnnots = null; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Path.java b/platform/java/com/artifex/mupdf/fitz/Path.java new file mode 100644 index 00000000..17257f10 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Path.java @@ -0,0 +1,82 @@ +package com.artifex.mupdf.fitz; + +public class Path implements PathProcessor +{ + // Private data + private long nativePath = 0; + + // Construction + public Path() + { + nativePath = newNative(); + } + + private native long newNative(); + + private Path(long path) + { + nativePath = path; + } + + public Path(Path old) + { + nativePath = clone(old); + } + + private native long clone(Path old); + + // Operation + public native Point currentPoint(); + + public void moveTo(Point xy) + { + moveTo(xy.x, xy.y); + } + + public native void moveTo(float x, float y); + + public void lineTo(Point xy) + { + lineTo(xy.x, xy.y); + } + + public native void lineTo(float x, float y); + + public void curveTo(Point c1, Point c2, Point e) + { + curveTo(c1.x, c1.y, c2.x, c2.y, e.x, e.y); + } + + public native void curveTo(float cx1, float cy1, float cx2, float cy2, float ex, float ey); + + public void curveToV(Point c, Point e) + { + curveToV(c.x, c.y, e.x, e.y); + } + + public native void curveToV(float cx, float cy, float ex, float ey); + + public void curveToY(Point c, Point e) + { + curveToY(c.x, c.y, e.x, e.y); + } + + public native void curveToY(float cx, float cy, float ex, float ey); + + public native void close(); + + public native void transform(Matrix mat); + + public native Rect bound(StrokeState stroke, Matrix ctm); + + public native void process(PathProcessor proc); + + // Destruction + public void destroy() + { + finalize(); + nativePath = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/PathProcessor.java b/platform/java/com/artifex/mupdf/fitz/PathProcessor.java new file mode 100644 index 00000000..71d03c50 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/PathProcessor.java @@ -0,0 +1,9 @@ +package com.artifex.mupdf.fitz; + +public interface PathProcessor +{ + public void moveTo(float x, float y); + public void lineTo(float x, float y); + public void curveTo(float cx1, float cy1, float cx2, float cy2, float ex, float ey); + public void close(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Point.java b/platform/java/com/artifex/mupdf/fitz/Point.java new file mode 100644 index 00000000..08989dd7 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Point.java @@ -0,0 +1,29 @@ +package com.artifex.mupdf.fitz; + +public class Point +{ + public float x; + public float y; + + public Point(float x, float y) + { + this.x = x; + this.y = y; + } + + public Point(Point p) + { + this.x = p.x; + this.y = p.y; + } + + public Point transform(Matrix tm) + { + float old_x = this.x; + + this.x = old_x * tm.a + y * tm.c + tm.e; + this.y = old_x * tm.b + y * tm.d + tm.f; + + return this; + } +} diff --git a/platform/java/com/artifex/mupdf/fitz/Rect.java b/platform/java/com/artifex/mupdf/fitz/Rect.java new file mode 100644 index 00000000..eb9138cf --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Rect.java @@ -0,0 +1,79 @@ +package com.artifex.mupdf.fitz; + +public class Rect +{ + public float x0; + public float y0; + public float x1; + public float y1; + + public Rect(float x0, float y0, float x1, float y1) + { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + + public Rect(Rect r) + { + this.x0 = r.x0; + this.y0 = r.y0; + this.x1 = r.x1; + this.y1 = r.y1; + } + + public Rect transform(Matrix tm) + { + float ax0 = x0 * tm.a; + float ax1 = x1 * tm.a; + + if (ax0 > ax1) + { + float t = ax0; + ax0 = ax1; + ax1 = t; + } + + float cy0 = y0 * tm.c; + float cy1 = y1 * tm.c; + + if (cy0 > cy1) + { + float t = cy0; + cy0 = cy1; + cy1 = t; + } + ax0 += cy0 + tm.e; + ax1 += cy1 + tm.e; + + float bx0 = x0 * tm.b; + float bx1 = x1 * tm.b; + + if (bx0 > bx1) + { + float t = bx0; + bx0 = bx1; + bx1 = t; + } + + float dy0 = y0 * tm.d; + float dy1 = y1 * tm.d; + + if (dy0 > dy1) + { + float t = dy0; + dy0 = dy1; + dy1 = t; + } + bx0 += dy0 + tm.f; + bx1 += dy1 + tm.f; + + x0 = ax0; + x1 = ax1; + y0 = bx0; + y1 = bx1; + + return this; + } +} diff --git a/platform/java/com/artifex/mupdf/fitz/RectI.java b/platform/java/com/artifex/mupdf/fitz/RectI.java new file mode 100644 index 00000000..1f91c778 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/RectI.java @@ -0,0 +1,79 @@ +package com.artifex.mupdf.fitz; + +public class RectI +{ + public int x0; + public int y0; + public int x1; + public int y1; + + public RectI(int x0, int y0, int x1, int y1) + { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + + public RectI(Rect r) + { + this.x0 = (int)Math.floor(r.x0); + this.y0 = (int)Math.ceil(r.y0); + this.x1 = (int)Math.floor(r.x1); + this.y1 = (int)Math.ceil(r.y1); + } + + public RectI transform(Matrix tm) + { + float ax0 = x0 * tm.a; + float ax1 = x1 * tm.a; + + if (ax0 > ax1) + { + float t = ax0; + ax0 = ax1; + ax1 = t; + } + + float cy0 = y0 * tm.c; + float cy1 = y1 * tm.c; + + if (cy0 > cy1) + { + float t = cy0; + cy0 = cy1; + cy1 = t; + } + ax0 += cy0 + tm.e; + ax1 += cy1 + tm.e; + + float bx0 = x0 * tm.b; + float bx1 = x1 * tm.b; + + if (bx0 > bx1) + { + float t = bx0; + bx0 = bx1; + bx1 = t; + } + + float dy0 = y0 * tm.d; + float dy1 = y1 * tm.d; + + if (dy0 > dy1) + { + float t = dy0; + dy0 = dy1; + dy1 = t; + } + bx0 += dy0 + tm.f; + bx1 += dy1 + tm.f; + + x0 = (int)Math.floor(ax0); + x1 = (int)Math.ceil(ax1); + y0 = (int)Math.floor(bx0); + y1 = (int)Math.ceil(bx1); + + return this; + } +} diff --git a/platform/java/com/artifex/mupdf/fitz/Shade.java b/platform/java/com/artifex/mupdf/fitz/Shade.java new file mode 100644 index 00000000..bfadebc7 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Shade.java @@ -0,0 +1,28 @@ +package com.artifex.mupdf.fitz; + +public class Shade +{ + // Private data + private long nativeShade = 0; + + // Construction + // Private constructor for the C to use. Any objects created by the + // C are done for purposes of calling back to a java device, and + // should therefore be considered const. + private Shade(long l) + { + nativeShade = l; + } + + // FIXME: Constructors for the different types of shade + // FIXME: Accessors for shade data + + // Destruction + public void destroy() + { + finalize(); + nativeShade = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/StrokeState.java b/platform/java/com/artifex/mupdf/fitz/StrokeState.java new file mode 100644 index 00000000..2f2fcf96 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/StrokeState.java @@ -0,0 +1,64 @@ +package com.artifex.mupdf.fitz; + +import android.graphics.Rect; + +public class StrokeState +{ + public static final int FZ_LINECAP_BUTT = 0; + public static final int FZ_LINECAP_ROUND = 1; + public static final int FZ_LINECAP_SQUARE = 2; + public static final int FZ_LINECAP_TRIANGLE = 3; + + public static final int FZ_LINEJOIN_MITER = 0; + public static final int FZ_LINEJOIN_ROUND = 1; + public static final int FZ_LINEJOIN_BEVEL = 2; + public static final int FZ_LINEJOIN_MITER_XPS = 3; + + // Private data + private long nativeStroke; + + // Construction + StrokeState(int startCap, int endCap, int lineJoin, float lineWidth, float miterLimit) + { + nativeStroke = newNative(startCap, 0, endCap, lineJoin, lineWidth, miterLimit, 0, null); + } + + StrokeState(int startCap, int dashCap, int endCap, int lineJoin, float lineWidth, float miterLimit, float dashPhase, float dash[]) + { + nativeStroke = newNative(startCap, dashCap, endCap, lineJoin, lineWidth, miterLimit, dashPhase, dash); + } + + private native long newNative(int startCap, int dashCap, int endCap, int lineJoin, float lineWidth, float miterLimit, float dashPhase, float dash[]); + + // Private constructor for the C to use. Any objects created by the + // C are done for purposes of calling back to a java device, and + // should therefore be considered const. This is fine as we don't + // currently provide mechanisms for changing individual elements + // of the StrokeState. + private StrokeState(long l) + { + nativeStroke = l; + } + + // Operation + public native void adjustRectForStroke(Rect rect, Matrix ctm); + + // Accessors + public native int getStartCap(); + public native int getDashCap(); + public native int getEndCap(); + public native int getLineJoin(); + public native float getLineWidth(); + public native float getMiterLimit(); + public native float getDashPhase(); + public native float[] getDashes(); + + // Destruction + public void destroy() + { + finalize(); + nativeStroke = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/Text.java b/platform/java/com/artifex/mupdf/fitz/Text.java new file mode 100644 index 00000000..eada4635 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/Text.java @@ -0,0 +1,46 @@ +package com.artifex.mupdf.fitz; + +public class Text +{ + // Private data + private long nativeText = 0; + private boolean isConst = false; + + // Cloning + public Text(Text old) + { + nativeText = cloneNative(old); + } + + private native long cloneNative(Text old); + + //public Text(Font font, Matrix trm, int wmode) + //{ + // nativeText = newNative(font, trm, wmode); + //} + + // Private method used for creating Text entries for a + // device implemented in java. These entries should be + // immutable. + private Text(long ptr) + { + nativeText = ptr; + isConst = true; + } + + // Operation + public native Rect bound(StrokeState stroke, Matrix ctm); + + //public native void add(int gid, int ucs, float x, float y); + + // FIXME: Write accessors + + // Destruction + public void destroy() + { + finalize(); + nativeText = 0; + } + + protected native void finalize(); +} diff --git a/platform/java/com/artifex/mupdf/fitz/TryLaterException.java b/platform/java/com/artifex/mupdf/fitz/TryLaterException.java new file mode 100644 index 00000000..644c6af1 --- /dev/null +++ b/platform/java/com/artifex/mupdf/fitz/TryLaterException.java @@ -0,0 +1,9 @@ +package com.artifex.mupdf.fitz; + +public class TryLaterException extends Exception +{ + TryLaterException(String message) + { + super(message); + } +} diff --git a/platform/java/mupdf_native.c b/platform/java/mupdf_native.c new file mode 100644 index 00000000..c1931702 --- /dev/null +++ b/platform/java/mupdf_native.c @@ -0,0 +1,3473 @@ +#include <jni.h> +#include <time.h> +#include <pthread.h> +#include <android/log.h> +#include <android/bitmap.h> + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#ifdef NDK_PROFILER +#include "prof.h" +#endif + +#include "mupdf/fitz.h" +#include "mupdf/pdf.h" + +#define MY_JNI_VERSION JNI_VERSION_1_6 + +#define JNI_FN(A) Java_com_artifex_mupdf_fitz_ ## A +#define PACKAGENAME "com.artifex.mupdf.fitz" +#define PACKAGEPATH "com/artifex/mupdf/fitz/" + +#define LOG_TAG "libmupdf" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGT(...) __android_log_print(ANDROID_LOG_INFO,"alert",__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) + +/* Set to 1 to enable debug log traces. */ +#define DEBUG 0 + +/* All the cached classes/mids/fids we need */ + +static jclass annot_class; +static jfieldID annot_fid; +static jmethodID annot_const_mid; +static jclass cdevice_class; +static jfieldID cdevice_nativeresource_fid; +static jfieldID cdevice_nativeinfo_fid; +static jclass colorspace_class; +static jfieldID colorspace_fid; +static jmethodID colorspace_const_mid; +static jclass cookie_class; +static jfieldID cookie_fid; +static jclass device_class; +static jfieldID device_fid; +static jmethodID device_begin_page_mid; +static jmethodID device_end_page_mid; +static jmethodID device_fill_path_mid; +static jmethodID device_stroke_path_mid; +static jmethodID device_clip_path_mid; +static jmethodID device_clip_stroke_path_mid; +static jmethodID device_fill_text_mid; +static jmethodID device_stroke_text_mid; +static jmethodID device_clip_text_mid; +static jmethodID device_clip_stroke_text_mid; +static jmethodID device_ignore_text_mid; +static jmethodID device_fill_shade_mid; +static jmethodID device_fill_image_mid; +static jmethodID device_fill_image_mask_mid; +static jmethodID device_clip_image_mask_mid; +static jmethodID device_pop_clip_mid; +static jmethodID device_begin_mask_mid; +static jmethodID device_end_mask_mid; +static jmethodID device_begin_group_mid; +static jmethodID device_end_group_mid; +static jmethodID device_begin_tile_mid; +static jmethodID device_end_tile_mid; +static jclass displaylist_class; +static jfieldID displaylist_fid; +static jclass document_class; +static jfieldID document_fid; +static jclass exception_class; +static jclass font_class; +static jfieldID font_fid; +//static jfieldID font_isconst_fid; +static jclass image_class; +static jfieldID image_fid; +static jmethodID image_const_mid; +static jclass link_class; +static jfieldID link_fid; +static jclass matrix_class; +static jfieldID matrix_a_fid; +static jfieldID matrix_b_fid; +static jfieldID matrix_c_fid; +static jfieldID matrix_d_fid; +static jfieldID matrix_e_fid; +static jfieldID matrix_f_fid; +static jmethodID matrix_const_mid; +static jclass outline_class; +static jfieldID outline_fid; +static jmethodID outline_const_mid; +static jclass page_class; +static jfieldID page_fid; +static jmethodID page_const_mid; +static jfieldID page_document_fid; +static jfieldID page_annots_fid; +static jclass path_class; +static jfieldID path_fid; +static jmethodID path_const_mid; +static jclass pathproc_class; +static jmethodID pathproc_moveto_mid; +static jmethodID pathproc_lineto_mid; +static jmethodID pathproc_curveto_mid; +static jmethodID pathproc_close_mid; +static jclass point_class; +static jfieldID point_fid; +static jmethodID point_const_mid; +static jclass rect_class; +static jfieldID rect_x0_fid; +static jfieldID rect_x1_fid; +static jfieldID rect_y0_fid; +static jfieldID rect_y1_fid; +static jmethodID rect_const_mid; +static jclass shade_class; +static jfieldID shade_fid; +static jmethodID shade_const_mid; +static jclass stroke_class; +static jfieldID stroke_fid; +static jmethodID stroke_const_mid; +static jclass text_class; +static jfieldID text_fid; +static jmethodID text_const_mid; +static jclass trylaterexception_class; + +static pthread_key_t context_key; +static fz_context *base_context; + +static void +throwOutOfMemoryError(JNIEnv *env, const char *info) +{ + jclass oomCls = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + + if (oomCls == NULL) + return; /* Well, what hope have we got! */ + + (*env)->ExceptionClear(env); + (*env)->ThrowNew(env, oomCls, info); +} + +static const char *last_class_obtained = NULL; + +static jclass +get_class(int *failed, JNIEnv *env, const char *str) +{ + jclass local, global; + + if (*failed) + return NULL; + + last_class_obtained = str; + local = (*env)->FindClass(env, str); + if (local == NULL) + { + LOGI("Failed to find class %s", str); + *failed = 1; + return NULL; + } + + global = (*env)->NewGlobalRef(env, local); + if (global == NULL) + { + LOGI("Failed to make global ref for %s", str); + *failed = 1; + return NULL; + } + + (*env)->DeleteLocalRef(env, local); + + return global; +} + +static jfieldID +get_field(int *failed, JNIEnv *env, jclass cla, const char *field, const char *sig) +{ + jfieldID fid; + + if (*failed || cla == NULL) + return NULL; + + fid = (*env)->GetFieldID(env, cla, field, sig); + if (fid == (jfieldID)0) + { + LOGI("Failed to get field for %s %s %s", last_class_obtained ? last_class_obtained : "<noclass>", field, sig); + *failed = 1; + } + + return fid; +} + +static jmethodID +get_method(int *failed, JNIEnv *env, jclass cla, const char *method, const char *sig) +{ + jmethodID mid; + + if (*failed || cla == NULL) + return NULL; + + mid = (*env)->GetMethodID(env, cla, method, sig); + if (mid == (jmethodID)0) + { + LOGI("Failed to get method for %s %s %s", last_class_obtained ? last_class_obtained : "<noclass>", method, sig); + *failed = 1; + } + + return mid; +} + +static int find_fids(JNIEnv *env) +{ + int failed = 0; + + annot_class = get_class(&failed, env, PACKAGEPATH"Annotation"); + annot_fid = get_field(&failed, env, annot_class, "nativeAnnot", "J"); + annot_const_mid = get_method(&failed, env, annot_class, "<init>", "(J)V"); + cdevice_class = get_class(&failed, env, PACKAGEPATH"CDevice"); + cdevice_nativeresource_fid = get_field(&failed, env, cdevice_class, "nativeResource", "Ljava.lang.Object;"); + cdevice_nativeinfo_fid = get_field(&failed, env, cdevice_class, "nativeInfo", "J"); + colorspace_class = get_class(&failed, env, PACKAGEPATH"ColorSpace"); + colorspace_fid = get_field(&failed, env, colorspace_class, "nativeColorSpace", "J"); + colorspace_const_mid = get_method(&failed, env, colorspace_class, "<init>", "(J)V"); + cookie_class = get_class(&failed, env, PACKAGEPATH"Cookie"); + cookie_fid = get_field(&failed, env, cookie_class, "nativeCookie", "J"); + device_class = get_class(&failed, env, PACKAGEPATH"Device"); + device_fid = get_field(&failed, env, device_class, "nativeDevice", "J"); + device_begin_page_mid = get_method(&failed, env, device_class, "beginPage", "(L"PACKAGEPATH"Rect;L"PACKAGEPATH"Matrix;)V"); + device_end_page_mid = get_method(&failed, env, device_class, "endPage", "()V"); + device_fill_path_mid = get_method(&failed, env, device_class, "fillPath", "(L"PACKAGEPATH"Path;IL"PACKAGEPATH"Matrix;L"PACKAGEPATH"ColorSpace;[FF)V"); + device_stroke_path_mid = get_method(&failed, env, device_class, "strokePath", "(JL"PACKAGEPATH"Path;L"PACKAGEPATH"StrokeState;L"PACKAGEPATH"Matrix;L"PACKAGEPATH"ColorSpace;[FF)V"); + device_clip_path_mid = get_method(&failed, env, device_class, "clipPath", "(L"PACKAGEPATH"Path;L"PACKAGEPATH"Rect;IL"PACKAGEPATH"Matrix;)V"); + device_clip_stroke_path_mid = get_method(&failed, env, device_class, "clipStrokePath", "(L"PACKAGEPATH"Path;L"PACKAGEPATH"Rect;L"PACKAGEPATH"StrokeState;L"PACKAGEPATH"Matrix;)V"); + device_fill_text_mid = get_method(&failed, env, device_class, "fillText", "(L"PACKAGEPATH"Text;L"PACKAGEPATH"Matrix;L"PACKAGEPATH"ColorSpace;[FF)V"); + device_stroke_text_mid = get_method(&failed, env, device_class, "strokeText", "(L"PACKAGEPATH"Text;L"PACKAGEPATH"StrokeState;L"PACKAGEPATH"Matrix;L"PACKAGEPATH"ColorSpace;[FF)V"); + device_clip_text_mid = get_method(&failed, env, device_class, "clipText", "(L"PACKAGEPATH"Text;L"PACKAGEPATH"Matrix;)V"); + device_clip_stroke_text_mid = get_method(&failed, env, device_class, "clipStrokeText", "(L"PACKAGEPATH"Text;L"PACKAGEPATH"StrokeState;L"PACKAGEPATH"Matrix;)V"); + device_ignore_text_mid = get_method(&failed, env, device_class, "ignoreText", "(L"PACKAGEPATH"Text;L"PACKAGEPATH"Matrix;)V"); + device_fill_shade_mid = get_method(&failed, env, device_class, "fillShade", "(L"PACKAGEPATH"Shade;L"PACKAGEPATH"Matrix;F)V"); + device_fill_image_mid = get_method(&failed, env, device_class, "fillImage", "(L"PACKAGEPATH"Image;L"PACKAGEPATH"Matrix;F)V"); + device_fill_image_mask_mid = get_method(&failed, env, device_class, "fillImageMask", "(L"PACKAGEPATH"Image;L"PACKAGEPATH"Matrix;L"PACKAGEPATH"ColorSpace;[FF)V"); + device_clip_image_mask_mid = get_method(&failed, env, device_class, "clipImageMask", "(L"PACKAGEPATH"Image;L"PACKAGEPATH"Rect;L"PACKAGEPATH"Matrix;)V"); + device_pop_clip_mid = get_method(&failed, env, device_class, "popClip", "()V"); + device_begin_mask_mid = get_method(&failed, env, device_class, "beginMask", "(L"PACKAGEPATH"Rect;IL"PACKAGEPATH"ColorSpace;[F)V"); + device_end_mask_mid = get_method(&failed, env, device_class, "endMask", "()V"); + device_begin_group_mid = get_method(&failed, env, device_class, "beginGroup", "(L"PACKAGEPATH"Rect;IIIF)V"); + device_end_group_mid = get_method(&failed, env, device_class, "endGroup", "()V"); + device_begin_tile_mid = get_method(&failed, env, device_class, "beginTile", "(L"PACKAGEPATH"Rect;L"PACKAGEPATH"Rect;FFL"PACKAGEPATH"Matrix;I)I"); + device_end_tile_mid = get_method(&failed, env, device_class, "endTile", "()V"); + exception_class = get_class(&failed, env, "java/lang/Exception"); + displaylist_class = get_class(&failed, env, PACKAGEPATH"DisplayList"); + displaylist_fid = get_field(&failed, env, displaylist_class, "nativeDisplayList", "J"); + document_class = get_class(&failed, env, PACKAGEPATH"Document"); + document_fid = get_field(&failed, env, document_class, "nativeDocument", "J"); + font_class = get_class(&failed, env, PACKAGEPATH"Font"); + font_fid = get_field(&failed, env, font_class, "nativeFont", "J"); + //font_isconst_fid = get_field(&failed, env, font_class, "isConst", "Z"); + image_class = get_class(&failed, env, PACKAGEPATH"Image"); + image_fid = get_field(&failed, env, image_class, "nativeImage", "J"); + image_const_mid = get_method(&failed, env, image_class, "<init>", "(J)V"); + link_class = get_class(&failed, env, PACKAGEPATH"Link"); + link_fid = get_field(&failed, env, link_class, "nativeLink", "J"); + matrix_class = get_class(&failed, env, PACKAGEPATH"Matrix"); + matrix_a_fid = get_field(&failed, env, matrix_class, "a", "F"); + matrix_b_fid = get_field(&failed, env, matrix_class, "b", "F"); + matrix_c_fid = get_field(&failed, env, matrix_class, "c", "F"); + matrix_d_fid = get_field(&failed, env, matrix_class, "d", "F"); + matrix_e_fid = get_field(&failed, env, matrix_class, "e", "F"); + matrix_f_fid = get_field(&failed, env, matrix_class, "f", "F"); + matrix_const_mid = get_method(&failed, env, matrix_class, "<init>", "(FFFFFF)V"); + outline_class = get_class(&failed, env, PACKAGEPATH"Outline"); + outline_fid = get_field(&failed, env, outline_class, "nativeOutline", "J"); + outline_const_mid = get_method(&failed, env, outline_class, "<init>", "(J)V"); + page_class = get_class(&failed, env, PACKAGEPATH"Page"); + page_fid = get_field(&failed, env, page_class, "nativePage", "J"); + page_const_mid = get_method(&failed, env, page_class, "<init>", "(J)V"); + page_annots_fid = get_field(&failed, env, page_class, "nativeAnnots", "[L"PACKAGEPATH"Annotation;"); + path_class = get_class(&failed, env, PACKAGEPATH"Path"); + path_fid = get_field(&failed, env, path_class, "nativePath", "J"); + path_const_mid = get_method(&failed, env, path_class, "<init>", "(J)V"); + point_class = get_class(&failed, env, PACKAGEPATH"Point"); + point_const_mid = get_method(&failed, env, point_class, "<init>", "(FF)V"); + pathproc_class = get_class(&failed, env, PACKAGEPATH"PathProcessor"); + pathproc_moveto_mid = get_method(&failed, env, pathproc_class, "moveTo", "(FF)V"); + pathproc_lineto_mid = get_method(&failed, env, pathproc_class, "lineTo", "(FF)V"); + pathproc_curveto_mid = get_method(&failed, env, pathproc_class, "curveTo", "(FFFFFF)V"); + pathproc_close_mid = get_method(&failed, env, pathproc_class, "close", "()V"); + rect_class = get_class(&failed, env, PACKAGEPATH"Rect"); + rect_x0_fid = get_field(&failed, env, rect_class, "x0", "F"); + rect_x1_fid = get_field(&failed, env, rect_class, "x1", "F"); + rect_y0_fid = get_field(&failed, env, rect_class, "y0", "F"); + rect_y1_fid = get_field(&failed, env, rect_class, "y1", "F"); + rect_const_mid = get_method(&failed, env, rect_class, "<init>", "(FFFF)V"); + shade_class = get_class(&failed, env, PACKAGEPATH"Shade"); + shade_fid = get_field(&failed, env, shade_class, "nativeShade", "J"); + shade_const_mid = get_method(&failed, env, shade_class, "<init>", "(J)V"); + stroke_class = get_class(&failed, env, PACKAGEPATH"StrokeState"); + stroke_fid = get_field(&failed, env, stroke_class, "nativeStroke", "J"); + stroke_const_mid = get_method(&failed, env, stroke_class, "<init>", "(J)V"); + text_class = get_class(&failed, env, PACKAGEPATH"Text"); + text_fid = get_field(&failed, env, text_class, "nativeText", "J"); + text_const_mid = get_method(&failed, env, text_class, "<init>", "(J)V"); + trylaterexception_class = get_class(&failed, env, PACKAGEPATH"TryLaterException"); + + return failed; +} + +static void lose_fids(JNIEnv *env) +{ + (*env)->DeleteGlobalRef(env, annot_class); + (*env)->DeleteGlobalRef(env, cdevice_class); + (*env)->DeleteGlobalRef(env, colorspace_class); + (*env)->DeleteGlobalRef(env, cookie_class); + (*env)->DeleteGlobalRef(env, device_class); + (*env)->DeleteGlobalRef(env, displaylist_class); + (*env)->DeleteGlobalRef(env, document_class); + (*env)->DeleteGlobalRef(env, exception_class); + (*env)->DeleteGlobalRef(env, font_class); + (*env)->DeleteGlobalRef(env, image_class); + (*env)->DeleteGlobalRef(env, link_class); + (*env)->DeleteGlobalRef(env, matrix_class); + (*env)->DeleteGlobalRef(env, outline_class); + (*env)->DeleteGlobalRef(env, page_class); + (*env)->DeleteGlobalRef(env, path_class); + (*env)->DeleteGlobalRef(env, pathproc_class); + (*env)->DeleteGlobalRef(env, rect_class); + (*env)->DeleteGlobalRef(env, shade_class); + (*env)->DeleteGlobalRef(env, stroke_class); + (*env)->DeleteGlobalRef(env, text_class); + (*env)->DeleteGlobalRef(env, trylaterexception_class); +} + +static pthread_mutex_t mutexes[FZ_LOCK_MAX]; + +static void lock(void *user, int lock) +{ + (void)pthread_mutex_lock(&mutexes[lock]); +} + +static void unlock(void *user, int lock) +{ + (void)pthread_mutex_unlock(&mutexes[lock]); +} + +static const fz_locks_context locks = +{ + NULL, /* user */ + lock, + unlock +}; + +static void fin_context(void *ctx) +{ + fz_drop_context((fz_context *)ctx); +} + +static int fin_base_context(JNIEnv *env) +{ + int i; + + for (i = 0; i < FZ_LOCK_MAX; i++) + (void)pthread_mutex_destroy(&mutexes[i]); + + fz_drop_context(base_context); + base_context = NULL; +} + +static int init_base_context(JNIEnv *env) +{ + int i; + + for (i = 0; i < FZ_LOCK_MAX; i++) + (void)pthread_mutex_init(&mutexes[i], NULL); + + base_context = fz_new_context(NULL, &locks, FZ_STORE_DEFAULT); + if (base_context == NULL) + return -1; + + fz_register_document_handlers(base_context); + + return 0; +} + +static fz_context *get_context(JNIEnv *env) +{ + fz_context *ctx = (fz_context *)pthread_getspecific(context_key); + + if (ctx != NULL) + return ctx; + + ctx = fz_clone_context(base_context); + if (ctx == NULL) + { + throwOutOfMemoryError(env, "Failed to clone fz_context"); + return NULL; + } + pthread_setspecific(context_key, ctx); + return ctx; +} + +jint JNI_OnLoad(JavaVM *vm, void *reserved) +{ + JNIEnv *env; + + if ((*vm)->GetEnv(vm, (void **)&env, MY_JNI_VERSION) != JNI_OK) + return -1; + + /* Must init the context before find_finds, because the act of + * finding the fids can cause classes to load. This causes + * statics to be setup, which can in turn call JNI code, which + * requires the context. (For example see ColorSpace) */ + if (init_base_context(env) < 0) + return -1; + + if (find_fids(env) != 0) + { + fin_base_context(env); + return -1; + } + + return MY_JNI_VERSION; +} + +void JNI_OnUnload(JavaVM *vm, void *reserved) +{ + JNIEnv *env; + + if ((*vm)->GetEnv(vm, (void **)&env, MY_JNI_VERSION) != JNI_OK) + return; /* If this fails, we're really in trouble! */ + + fz_drop_context(base_context); + base_context = NULL; + lose_fids(env); +} + +// Do our best to avoid casting warnings. +#define CAST(type, var) (type)pointer_cast(var) + +static inline void *pointer_cast(jlong l) +{ + return (void *)(intptr_t)l; +} + +static inline jlong jlong_cast(const void *p) +{ + return (jlong)(intptr_t)p; +} + +/* Conversion functions: C to Java */ +static inline jobject Annotation_from_fz_annot(fz_context *ctx, JNIEnv *env, fz_annot *annot) +{ + jobject jannot; + + if (ctx == NULL) + return NULL; + + (*env)->NewObject(env, annot_class, annot_const_mid, annot); + if (jannot == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Annotation creation failed"); + + return jannot; +} + +static inline jobject ColorSpace_from_fz_colorspace(fz_context *ctx, JNIEnv *env, fz_colorspace *cs) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, colorspace_class, colorspace_const_mid, jlong_cast(cs)); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of ColorSpace failed"); + + fz_keep_colorspace(ctx, cs); + + return jobj; +} + +static inline jobject Image_from_fz_image(fz_context *ctx, JNIEnv *env, fz_image *img) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, image_class, image_const_mid, jlong_cast(img)); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Image failed"); + + fz_keep_image(ctx, img); + + return jobj; +} + +static inline jobject Matrix_from_fz_matrix(fz_context *ctx, JNIEnv *env, const fz_matrix *mat) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, matrix_class, matrix_const_mid, mat->a, mat->b, mat->c, mat->d, mat->e, mat->f); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Matrix failed"); + + return jobj; +} + +static inline jobject Outline_from_fz_outline(fz_context *ctx, JNIEnv *env, fz_outline *outline) +{ + jobject joutline; + + if (ctx == NULL) + return NULL; + + joutline = (*env)->NewObject(env, outline_class, outline_const_mid, jlong_cast(outline)); + if (joutline == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "getOutline failed (3)"); + + return joutline; +} + +static inline jobject Page_from_fz_page(fz_context *ctx, JNIEnv *env, fz_page *page) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, page_class, page_const_mid, jlong_cast(page)); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Page failed"); + + return jobj; +} + +static inline jobject Path_from_fz_path(fz_context *ctx, JNIEnv *env, fz_path *path) +{ + jobject jobj; + fz_path *new_path; + + if (ctx == NULL) + return NULL; + + new_path = fz_clone_path(ctx, path); + + jobj = (*env)->NewObject(env, path_class, path_const_mid, jlong_cast(new_path)); + if (jobj == NULL) + { + fz_drop_path(ctx, new_path); + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Path failed"); + } + + return jobj; +} + +static inline jobject Point_from_fz_point(fz_context *ctx, JNIEnv *env, fz_point point) +{ + jobject jpoint; + + if (ctx == NULL) + return NULL; + + jpoint = (*env)->NewObject(env, point_class, point_const_mid, point.x, point.y); + if (jpoint == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "currentPoint failed (3)"); + + return jpoint; +} + +static inline jobject Rect_from_fz_rect(fz_context *ctx, JNIEnv *env, const fz_rect *rect) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, rect_class, rect_const_mid, rect->x0, rect->y0, rect->x1, rect->y1); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Rect failed"); + + return jobj; +} + +static inline jobject Shade_from_fz_shade(fz_context *ctx, JNIEnv *env, fz_shade *shade) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, shade_class, shade_const_mid, jlong_cast(shade)); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Shade failed"); + + fz_keep_shade(ctx, shade); + + return jobj; +} + +static inline jobject StrokeState_from_fz_stroke_state(fz_context *ctx, JNIEnv *env, fz_stroke_state *state) +{ + jobject jobj; + + if (ctx == NULL) + return NULL; + + jobj = (*env)->NewObject(env, stroke_class, stroke_const_mid, jlong_cast(state)); + if (jobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of StrokeState failed"); + + fz_keep_stroke_state(ctx, state); + + return jobj; +} + +static inline jobject Text_from_fz_text(fz_context *ctx, JNIEnv *env, fz_text *text) +{ + jobject jobj; + fz_text *new_text; + + if (ctx == NULL) + return NULL; + + new_text = fz_clone_text(ctx, text); + + jobj = (*env)->NewObject(env, text_class, text_const_mid, jlong_cast(new_text)); + if (jobj == NULL) + { + fz_drop_text(ctx, new_text); + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Text failed"); + } + + return jobj; +} + +static inline jfloatArray jfloatArray_from_fz_color(fz_context *ctx, JNIEnv *env, float *color, int n) +{ + jfloatArray arr; + + if (ctx == NULL) + return NULL; + + arr = (*env)->NewFloatArray(env, n); + if (arr == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of floatArray failed"); + + (*env)->SetFloatArrayRegion(env, arr, 0, n, color); + + return arr; +} + +/* Devices can either be implemented in C, or in Java. + * + * We therefore have to think about 4 possible call combinations. + * + * 1) C -> C: The standard mupdf case. No special worries here. + * 2) C -> Java: This can only happen when we call run on a page/annotation/ + * displaylist. We need to ensure that the java Device has an + * appropriate fz_java_device generated for it. The 'run' calls + * take care to lock/unlock for us. + * 3) Java -> C: The C device will have a java shim (a subclass of CDevice). + * All calls will go through the device methods in CDevice, + * which converts the java objects to C ones, and lock/unlock + * any underlying objects as required. + * 4) Java -> Java: No special worries. + */ + +/* Our java device wrapping functions */ + +typedef struct +{ + fz_device base; + JNIEnv *env; + jobject self; +} +fz_java_device; + +static void +fz_java_device_begin_page(fz_context *ctx, fz_device *dev, const fz_rect *rect, const fz_matrix *ctm) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jrect = Rect_from_fz_rect(ctx, env, rect); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_begin_page_mid, jrect, jctm); +} + +static void +fz_java_device_end_page(fz_context *ctx, fz_device *dev) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + + (*env)->CallVoidMethod(env, jdev->self, device_end_page_mid); +} + +static void +fz_java_device_fill_path(fz_context *ctx, fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, fz_colorspace *cs, float *color, float alpha) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jpath = Path_from_fz_path(ctx, env, path); + jobject jcs = ColorSpace_from_fz_colorspace(ctx, env, cs); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + jfloatArray jcolor = jfloatArray_from_fz_color(ctx, env, color, cs ? cs->n : FZ_MAX_COLORS); + + (*env)->CallVoidMethod(env, jdev->self, device_fill_path_mid, jpath, even_odd, jctm, jcs, jcolor, alpha); +} + +static void +fz_java_device_stroke_path(fz_context *ctx, fz_device *dev, fz_path *path, fz_stroke_state *state, const fz_matrix *ctm, fz_colorspace *cs, float *color, float alpha) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jpath = Path_from_fz_path(ctx, env, path); + jobject jstate = StrokeState_from_fz_stroke_state(ctx, env, state); + jobject jcs = ColorSpace_from_fz_colorspace(ctx, env, cs); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + jfloatArray jcolor = jfloatArray_from_fz_color(ctx, env, color, cs ? cs->n : FZ_MAX_COLORS); + + (*env)->CallVoidMethod(env, jdev->self, device_stroke_path_mid, jpath, jstate, jctm, jcs, jcolor, alpha); +} + +static void +fz_java_device_clip_path(fz_context *ctx, fz_device *dev, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jpath = Path_from_fz_path(ctx, env, path); + jobject jrect = Rect_from_fz_rect(ctx, env, rect); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_clip_path_mid, jpath, jrect, even_odd, jctm); +} + +static void +fz_java_device_clip_stroke_path(fz_context *ctx, fz_device *dev, fz_path *path, const fz_rect *rect, fz_stroke_state *state, const fz_matrix *ctm) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jpath = Path_from_fz_path(ctx, env, path); + jobject jrect = Rect_from_fz_rect(ctx, env, rect); + jobject jstate = StrokeState_from_fz_stroke_state(ctx, env, state); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_clip_stroke_path_mid, jpath, jrect, jstate, jctm); +} + +static void +fz_java_device_fill_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm, fz_colorspace *cs, float *color, float alpha) +{ + LOGI("fz_java_device_fill_text"); + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jtext = Text_from_fz_text(ctx, env, text); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + jobject jcs = ColorSpace_from_fz_colorspace(ctx, env, cs); + jfloatArray jcolor = jfloatArray_from_fz_color(ctx, env, color, cs ? cs->n : FZ_MAX_COLORS); + + (*env)->CallVoidMethod(env, jdev->self, device_fill_text_mid, jtext, jctm, jcs, jcolor, alpha); +} + +static void +fz_java_device_stroke_text(fz_context *ctx, fz_device *dev, fz_text *text, fz_stroke_state *state, const fz_matrix *ctm, fz_colorspace *cs, float *color, float alpha) +{ + LOGI("fz_java_device_stroke_text"); + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jtext = Text_from_fz_text(ctx, env, text); + jobject jstate = StrokeState_from_fz_stroke_state(ctx, env, state); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + jobject jcs = ColorSpace_from_fz_colorspace(ctx, env, cs); + jfloatArray jcolor = jfloatArray_from_fz_color(ctx, env, color, cs ? cs->n : FZ_MAX_COLORS); + + (*env)->CallVoidMethod(env, jdev->self, device_stroke_text_mid, jtext, jstate, jctm, jcs, jcolor, alpha); +} + +static void +fz_java_device_clip_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ + LOGI("fz_java_device_clip_text"); + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jtext = Text_from_fz_text(ctx, env, text); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_clip_text_mid, jtext, jctm); +} + +static void +fz_java_device_clip_stroke_text(fz_context *ctx, fz_device *dev, fz_text *text, fz_stroke_state *state, const fz_matrix *ctm) +{ + LOGI("fz_java_device_clip_stroke_text"); + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jtext = Text_from_fz_text(ctx, env, text); + jobject jstate = StrokeState_from_fz_stroke_state(ctx, env, state); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_clip_stroke_text_mid, jtext, jstate, jctm); +} + +static void +fz_java_device_ignore_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm) +{ + LOGI("fz_java_device_ignore_text"); + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jtext = Text_from_fz_text(ctx, env, text); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_ignore_text_mid, jtext, jctm); +} + +static void +fz_java_device_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shd, const fz_matrix *ctm, float alpha) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jshd = Shade_from_fz_shade(ctx, env, shd); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_fill_shade_mid, jshd, jctm, alpha); +} + +static void +fz_java_device_fill_image(fz_context *ctx, fz_device *dev, fz_image *img, const fz_matrix *ctm, float alpha) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jimg = Image_from_fz_image(ctx, env, img); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_fill_image_mid, jimg, jctm, alpha); +} + +static void +fz_java_device_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, const fz_matrix *ctm, fz_colorspace *cs, float *color, float alpha) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jimg = Image_from_fz_image(ctx, env, img); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + jobject jcs = ColorSpace_from_fz_colorspace(ctx, env, cs); + jfloatArray jcolor = jfloatArray_from_fz_color(ctx, env, color, cs ? cs->n : FZ_MAX_COLORS); + + (*env)->CallVoidMethod(env, jdev->self, device_fill_image_mask_mid, jimg, jctm, jcs, jcolor, alpha); +} + +static void +fz_java_device_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, const fz_rect *rect, const fz_matrix *ctm) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jimg = Image_from_fz_image(ctx, env, img); + jobject jrect = Rect_from_fz_rect(ctx, env, rect); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + + (*env)->CallVoidMethod(env, jdev->self, device_clip_image_mask_mid, jimg, jrect, jctm); +} + +static void +fz_java_device_pop_clip(fz_context *ctx, fz_device *dev) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + + (*env)->CallVoidMethod(env, jdev->self, device_pop_clip_mid); +} + +static void +fz_java_device_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *cs, float *bc) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jrect = Rect_from_fz_rect(ctx, env, rect); + jobject jcs = ColorSpace_from_fz_colorspace(ctx, env, cs); + jfloatArray jbc = jfloatArray_from_fz_color(ctx, env, bc, cs ? cs->n : FZ_MAX_COLORS); + + (*env)->CallVoidMethod(env, jdev->self, device_begin_mask_mid, jrect, luminosity, jcs, jbc); +} + +static void +fz_java_device_end_mask(fz_context *ctx, fz_device *dev) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + + (*env)->CallVoidMethod(env, jdev->self, device_end_mask_mid); +} + +static void +fz_java_device_begin_group(fz_context *ctx, fz_device *dev, const fz_rect *rect, int isolated, int knockout, int blendmode, float alpha) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jrect = Rect_from_fz_rect(ctx, env, rect); + + (*env)->CallVoidMethod(env, jdev->self, device_begin_group_mid, jrect, isolated, knockout, blendmode, alpha); +} + +static void +fz_java_device_end_group(fz_context *ctx, fz_device *dev) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + + (*env)->CallVoidMethod(env, jdev->self, device_end_group_mid); +} + +static int +fz_java_device_begin_tile(fz_context *ctx, fz_device *dev, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + jobject jarea = Rect_from_fz_rect(ctx, env, area); + jobject jview = Rect_from_fz_rect(ctx, env, view); + jobject jctm = Matrix_from_fz_matrix(ctx, env, ctm); + int res; + + res = (*env)->CallIntMethod(env, jdev->self, device_begin_tile_mid, jarea, jview, xstep, ystep, jctm, id); + + return res; +} + +static void +fz_java_device_end_tile(fz_context *ctx, fz_device *dev) +{ + fz_java_device *jdev = (fz_java_device *)dev; + JNIEnv *env = jdev->env; + + (*env)->CallVoidMethod(env, jdev->self, device_end_tile_mid); +} + +static void +fz_java_device_drop_imp(fz_context *ctx, fz_device *dev) +{ + fz_java_device *jdev = (fz_java_device *)dev; + + /* Nothing to do, currently */ + jdev = jdev; +} + +static fz_device *fz_new_java_device(JNIEnv *env, jobject self, fz_context *ctx) +{ + fz_device *dev = NULL; + fz_java_device *jdev = NULL; + + fz_var(dev); + fz_var(jdev); + + fz_try(ctx) + { + jdev = fz_new_device(ctx, sizeof(fz_java_device)); + dev = &jdev->base; + jdev->env = env; + jdev->self = self; + dev->drop_imp = fz_java_device_drop_imp; + + dev->fill_path = fz_java_device_fill_path; + dev->stroke_path = fz_java_device_stroke_path; + dev->clip_path = fz_java_device_clip_path; + dev->clip_stroke_path = fz_java_device_clip_stroke_path; + + dev->fill_text = fz_java_device_fill_text; + dev->stroke_text = fz_java_device_stroke_text; + dev->clip_text = fz_java_device_clip_text; + dev->clip_stroke_text = fz_java_device_clip_stroke_text; + + dev->fill_shade = fz_java_device_fill_shade; + dev->fill_image = fz_java_device_fill_image; + dev->fill_image_mask = fz_java_device_fill_image_mask; + dev->clip_image_mask = fz_java_device_clip_image_mask; + + dev->pop_clip = fz_java_device_pop_clip; + + dev->begin_mask = fz_java_device_begin_mask; + dev->end_mask = fz_java_device_end_mask; + dev->begin_group = fz_java_device_begin_group; + dev->end_group = fz_java_device_end_group; + + dev->begin_tile = fz_java_device_begin_tile; + dev->end_tile = fz_java_device_end_tile; + } + fz_catch(ctx) + { + jclass exClass; + + fz_free(ctx, jdev); + throwOutOfMemoryError(env, "Failed to create fz_java_device"); + dev = NULL; + } + return dev; +} + +/* Conversion functions: Java to C */ +static inline fz_colorspace *fz_colorspace_from_ColorSpace(JNIEnv *env, jobject jobj) +{ + return CAST(fz_colorspace *, (*env)->GetLongField(env, jobj, colorspace_fid)); +} + + +static fz_device *fz_device_from_Device(JNIEnv *env, jobject self, fz_context *ctx) +{ + fz_device *dev = CAST(fz_device *, (*env)->GetLongField(env, self, device_fid)); + + if (dev == NULL) + { + /* This must be a Java device. Create a native shim. */ + dev = fz_new_java_device(env, self, ctx); + (*env)->SetLongField(env, self, device_fid, jlong_cast(dev)); + } + return dev; +} + +static inline fz_image *fz_image_from_Image(JNIEnv *env, jobject jobj) +{ + return CAST(fz_image *, (*env)->GetLongField(env, jobj, image_fid)); +} + +static inline fz_matrix fz_matrix_from_Matrix(JNIEnv *env, jobject jmat) +{ + fz_matrix mat; + + mat.a = (*env)->GetFloatField(env, jmat, matrix_a_fid); + mat.b = (*env)->GetFloatField(env, jmat, matrix_b_fid); + mat.c = (*env)->GetFloatField(env, jmat, matrix_c_fid); + mat.d = (*env)->GetFloatField(env, jmat, matrix_d_fid); + mat.e = (*env)->GetFloatField(env, jmat, matrix_e_fid); + mat.f = (*env)->GetFloatField(env, jmat, matrix_f_fid); + + return mat; +} + +static inline fz_path *fz_path_from_Path(JNIEnv *env, jobject jobj) +{ + return CAST(fz_path *, (*env)->GetLongField(env, jobj, path_fid)); +} + +static inline fz_rect fz_rect_from_Rect(JNIEnv *env, jobject jrect) +{ + fz_rect rect; + + rect.x0 = (*env)->GetFloatField(env, jrect, rect_x0_fid); + rect.x1 = (*env)->GetFloatField(env, jrect, rect_x1_fid); + rect.y0 = (*env)->GetFloatField(env, jrect, rect_y0_fid); + rect.y1 = (*env)->GetFloatField(env, jrect, rect_y1_fid); + + return rect; +} + +static inline fz_shade *fz_shade_from_Shade(JNIEnv *env, jobject jobj) +{ + return CAST(fz_shade *, (*env)->GetLongField(env, jobj, shade_fid)); +} + +static inline fz_stroke_state *fz_stroke_state_from_StrokeState(JNIEnv *env, jobject jobj) +{ + return CAST(fz_stroke_state *, (*env)->GetLongField(env, jobj, stroke_fid)); +} + +static inline fz_text *fz_text_from_Text(JNIEnv *env, jobject jobj) +{ + return CAST(fz_text *, (*env)->GetLongField(env, jobj, text_fid)); +} + +static inline fz_font *fz_font_from_Font(JNIEnv *env, jobject jobj) +{ + return CAST(fz_font *, (*env)->GetLongField(env, jobj, font_fid)); +} + +static inline void fz_color_from_jfloatArray(JNIEnv *env, float *color, int n, jfloatArray jcolor) +{ + jsize len = (*env)->GetArrayLength(env, jcolor); + if (len > n) + len = n; + (*env)->GetFloatArrayRegion(env, jcolor, 0, len, color); + if (len < n) + memset(color+len, 0, (n-len)*sizeof(float)); +} + +static inline fz_cookie *fz_cookie_from_Cookie(JNIEnv *env, jobject jobj) +{ + return CAST(fz_cookie *, (*env)->GetLongField(env, jobj, cookie_fid)); +} + +static inline fz_display_list *fz_display_list_from_DisplayList(JNIEnv *env, jobject jobj) +{ + return CAST(fz_display_list *, (*env)->GetLongField(env, jobj, displaylist_fid)); +} + +static inline fz_page *fz_page_from_Page(JNIEnv *env, jobject jobj) +{ + return CAST(fz_page *, (*env)->GetLongField(env, jobj, page_fid)); +} + +static inline fz_document *fz_document_from_Document(JNIEnv *env, jobject jobj) +{ + jlong l; + + l = (*env)->GetLongField(env, jobj, document_fid); + return CAST(fz_document *, l); +} + +static inline fz_annot *fz_annot_from_Annotation(JNIEnv *env, jobject jobj) +{ + return CAST(fz_annot *, (*env)->GetLongField(env, jobj, annot_fid)); +} + +static inline fz_outline *fz_outline_from_Outline(JNIEnv *env, jobject jobj) +{ + return CAST(fz_outline *, (*env)->GetLongField(env, jobj, outline_fid)); +} + +static inline fz_link *fz_link_from_Link(JNIEnv *env, jobject jobj) +{ + return CAST(fz_link *, (*env)->GetLongField(env, jobj, link_fid)); +} + +/* Helper function for exception handling */ + +static void jni_throw(JNIEnv *env, int type, const char *mess) +{ + const char *className; + int len; + jclass cla; + + switch(type) + { + case FZ_ERROR_TRYLATER: + cla = trylaterexception_class; + break; + default: + case FZ_ERROR_GENERIC: + cla = exception_class; + break; + } + + (void)(*env)->ThrowNew(env, cla, mess); +} + +static void jni_rethrow(JNIEnv *env, fz_context *ctx) +{ + jni_throw(env, fz_caught(ctx), fz_caught_message(ctx)); +} + +/* ColorSpace Interface */ + +JNIEXPORT void JNICALL +JNI_FN(ColorSpace_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, self); + + if (ctx == NULL || cs == NULL) + return; + + fz_drop_colorspace(ctx, cs); +} + +JNIEXPORT void JNICALL +JNI_FN(ColorSpace_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(ColorSpace_finalize)(env, self); + + (*env)->SetLongField(env, self, colorspace_fid, 0); +} + +JNIEXPORT int JNICALL +JNI_FN(ColorSpace_getNumComponents)(JNIEnv * env, jobject self) +{ + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, self); + + if (cs == NULL) + return 0; + + return cs->n; +} + +JNIEXPORT jlong JNICALL +JNI_FN(ColorSpace_newDeviceRGB)(JNIEnv * env, jobject self, jlong size) +{ + fz_context *ctx = get_context(env); + + return jlong_cast(fz_device_rgb(ctx)); +} + +JNIEXPORT jlong JNICALL +JNI_FN(ColorSpace_newDeviceGray)(JNIEnv * env, jobject self, jlong size) +{ + fz_context *ctx = get_context(env); + + return jlong_cast(fz_device_gray(ctx)); +} + +JNIEXPORT jlong JNICALL +JNI_FN(ColorSpace_newDeviceCMYK)(JNIEnv * env, jobject self, jlong size) +{ + fz_context *ctx = get_context(env); + + return jlong_cast(fz_device_cmyk(ctx)); +} + +/* Device Interface */ + +typedef struct CDeviceNativeInfo CDeviceNativeInfo; + +typedef void (CDeviceLockFn)(JNIEnv *env, CDeviceNativeInfo *info); +typedef void (CDeviceUnlockFn)(JNIEnv *env, CDeviceNativeInfo *info); + +struct CDeviceNativeInfo +{ + /* Some devices (like the AndroidDrawDevice, or AwtDrawDevice) need + * to lock/unlock the java object around device calls. We have functions + * here to do that. Other devices (like the DisplayList device) need + * no such locking, so these are NULL. */ + CDeviceLockFn *lock; /* Function to lock */ + CDeviceUnlockFn *unlock; /* Function to unlock */ + jobject object; /* The java object that needs to be locked. */ + + /* Conceptually, we support drawing onto a 'plane' of pixels. + * The plane is width/height in size. The page is positioned on this + * at pageX0,pageY0 -> pageX1,PageY1. We want to redraw the given patch + * of this. + * + * The samples pointer in pixmap is updated on every lock/unlock, to + * cope with the object moving in memory. + */ + fz_pixmap *pixmap; + int pageX0; + int pageY0; + int width; +}; + +static CDeviceNativeInfo *lockCDevice(JNIEnv *env, jobject self) +{ + CDeviceNativeInfo *info; + + info = CAST(CDeviceNativeInfo *, (*env)->GetLongField(env, self, cdevice_nativeinfo_fid)); + if (info == NULL) + { + /* Some devices (like the Displaylist device) need no locking, + * so have no info. */ + return NULL; + } + info->object = (*env)->GetObjectField(env, self, cdevice_nativeresource_fid); + + info->lock(env, info); + + return info; +} + +static void unlockCDevice(JNIEnv *env, CDeviceNativeInfo *info) +{ + if (info != NULL) + info->unlock(env, info); +} + +JNIEXPORT void JNICALL +JNI_FN(Device_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + + if (ctx == NULL || dev == NULL) + return; + + fz_drop_device(ctx, dev); +} + +JNIEXPORT void JNICALL +JNI_FN(Device_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Device_finalize)(env, self); + + (*env)->SetLongField(env, self, device_fid, 0); +} + + +JNIEXPORT void JNICALL +JNI_FN(CDevice_beginPage)(JNIEnv *env, jobject self, jobject jrect, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_rect rect = fz_rect_from_Rect(env, jrect); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_begin_page(ctx, dev, &rect, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_endPage)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_end_page(ctx, dev); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_fillPath)(JNIEnv *env, jobject self, jobject jpath, int even_odd, jobject jctm, jobject jcs, jfloatArray jcolor, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_path *path = fz_path_from_Path(env, jpath); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, jcs); + float color[FZ_MAX_COLORS]; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_color_from_jfloatArray(env, color, cs ? cs->n : FZ_MAX_COLORS, jcolor); + + fz_fill_path(ctx, dev, path, even_odd, &ctm, cs, color, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_strokePath)(JNIEnv *env, jobject self, jobject jpath, jobject jstroke, jobject jctm, jobject jcs, jfloatArray jcolor, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_path *path = fz_path_from_Path(env, jpath); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, jcs); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, jstroke); + float color[FZ_MAX_COLORS]; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_color_from_jfloatArray(env, color, cs ? cs->n : FZ_MAX_COLORS, jcolor); + + fz_stroke_path(ctx, dev, path, stroke, &ctm, cs, color, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_clipPath)(JNIEnv *env, jobject self, jobject jpath, jobject jrect, int even_odd, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_path *path = fz_path_from_Path(env, jpath); + fz_rect rect = fz_rect_from_Rect(env, jrect); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_clip_path(ctx, dev, path, &rect, even_odd, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_clipStrokePath)(JNIEnv *env, jobject self, jobject jpath, jobject jrect, jobject jstroke, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_path *path = fz_path_from_Path(env, jpath); + fz_rect rect = fz_rect_from_Rect(env, jrect); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, jstroke); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_clip_stroke_path(ctx, dev, path, &rect, stroke, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_fillText)(JNIEnv *env, jobject self, jobject jtext, jobject jctm, jobject jcs, jfloatArray jcolor, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_text *text = fz_text_from_Text(env, jtext); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, jcs); + float color[FZ_MAX_COLORS]; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_color_from_jfloatArray(env, color, cs ? cs->n : FZ_MAX_COLORS, jcolor); + + fz_fill_text(ctx, dev, text, &ctm, cs, color, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_strokeText)(JNIEnv *env, jobject self, jobject jtext, jobject jstroke, jobject jctm, jobject jcs, jfloatArray jcolor, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_text *text = fz_text_from_Text(env, jtext); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, jcs); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, jstroke); + float color[FZ_MAX_COLORS]; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_color_from_jfloatArray(env, color, cs ? cs->n : FZ_MAX_COLORS, jcolor); + + fz_stroke_text(ctx, dev, text, stroke, &ctm, cs, color, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_clipText)(JNIEnv *env, jobject self, jobject jtext, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_text *text = fz_text_from_Text(env, jtext); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_clip_text(ctx, dev, text, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_clipStrokeText)(JNIEnv *env, jobject self, jobject jtext, jobject jstroke, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_text *text = fz_text_from_Text(env, jtext); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, jstroke); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_clip_stroke_text(ctx, dev, text, stroke, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_ignoreText)(JNIEnv *env, jobject self, jobject jtext, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_text *text = fz_text_from_Text(env, jtext); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_ignore_text(ctx, dev, text, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_fillShade)(JNIEnv *env, jobject self, jobject jshade, jobject jctm, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_shade *shade = fz_shade_from_Shade(env, jshade); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_fill_shade(ctx, dev, shade, &ctm, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_fillImage)(JNIEnv *env, jobject self, jobject jimg, jobject jctm, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_image *image = fz_image_from_Image(env, jimg); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_fill_image(ctx, dev, image, &ctm, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_fillImageMask)(JNIEnv *env, jobject self, jobject jimg, jobject jctm, jobject jcs, jfloatArray jcolor, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_image *image = fz_image_from_Image(env, jimg); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, jcs); + float color[FZ_MAX_COLORS]; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_color_from_jfloatArray(env, color, cs ? cs->n : FZ_MAX_COLORS, jcolor); + + fz_fill_image_mask(ctx, dev, image, &ctm, cs, color, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_clipImageMask)(JNIEnv *env, jobject self, jobject jimg, jobject jrect, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_image *image = fz_image_from_Image(env, jimg); + fz_rect rect = fz_rect_from_Rect(env, jrect); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_clip_image_mask(ctx, dev, image, &rect, &ctm); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_popClip)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_pop_clip(ctx, dev); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_beginMask)(JNIEnv *env, jobject self, jobject jrect, int luminosity, jobject jcs, jfloatArray jcolor) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_rect rect = fz_rect_from_Rect(env, jrect); + fz_colorspace *cs = fz_colorspace_from_ColorSpace(env, jcs); + float color[FZ_MAX_COLORS]; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_color_from_jfloatArray(env, color, cs ? cs->n : FZ_MAX_COLORS, jcolor); + + fz_begin_mask(ctx, dev, &rect, luminosity, cs, color); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_endMask)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_end_mask(ctx, dev); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_beginGroup)(JNIEnv *env, jobject self, jobject jrect, int isolated, int knockout, int blendmode, float alpha) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_rect rect = fz_rect_from_Rect(env, jrect); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_begin_group(ctx, dev, &rect, isolated, knockout, blendmode, alpha); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_endGroup)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_end_group(ctx, dev); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT int JNICALL +JNI_FN(CDevice_beginTile)(JNIEnv *env, jobject self, jobject jarea, jobject jview, float xstep, float ystep, jobject jctm, int id) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_rect area = fz_rect_from_Rect(env, jarea); + fz_rect view = fz_rect_from_Rect(env, jview); + int i; + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + i = fz_begin_tile_id(ctx, dev, &area, &view, xstep, ystep, &ctm, id); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + + return i; +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_endTile)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_device *dev = fz_device_from_Device(env, self, ctx); + CDeviceNativeInfo *info; + + if (ctx == NULL || dev == NULL) + return; + + info = lockCDevice(env, self); + + fz_try(ctx) + { + fz_end_tile(ctx, dev); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +/* Draw Device interface */ +static jlong +newCDevice(JNIEnv *env, jobject self, fz_context *ctx, jobject obj, jint width, jint height, CDeviceLockFn *lock, CDeviceUnlockFn *unlock, int pageX0, int pageY0, int pageX1, int pageY1, int patchX0, int patchY0, int patchX1, int patchY1) +{ + fz_device *device = NULL; + fz_pixmap *pixmap = NULL; + int ret; + unsigned char dummy; + CDeviceNativeInfo *ninfo = NULL; + fz_irect clip, pixbbox; + + if (ctx == NULL) + return 0; + + fz_var(pixmap); + fz_var(ninfo); + + fz_try(ctx) + { + //LOGI("DrawDeviceNative: bitmap=%d,%d page=%d,%d->%d,%d patch=%d,%d->%d,%d", width, height, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1); + /* Sanitise patch w.r.t page. */ + if (patchX0 < pageX0) + patchX0 = pageX0; + if (patchY0 < pageY0) + patchY0 = pageY0; + if (patchX1 > pageX1) + patchX1 = pageX1; + if (patchY1 > pageY1) + patchY1 = pageY1; + + clip.x0 = patchX0; + clip.y0 = patchY0; + clip.x1 = patchX1; + clip.y1 = patchY1; + + /* Check for sanity. */ + //LOGI("clip = %d,%d->%d,%d", clip.x0, clip.y0, clip.x1, clip.y1); + if (clip.x0 < 0 || clip.y0 < 0 || clip.x1 > width || clip.y1 > height) + fz_throw(ctx, FZ_ERROR_GENERIC, "patch would draw out of bounds!"); + + clip.x0 -= pageX0; + clip.y0 -= pageY0; + clip.x1 -= pageX0; + clip.y1 -= pageY0; + + /* pixmaps cannot handle right-edge padding, so the bbox must be expanded to + * match the pixels data */ + pixbbox = clip; + pixbbox.x1 = pixbbox.x0 + width; + pixmap = fz_new_pixmap_with_bbox_and_data(ctx, fz_device_rgb(ctx), &pixbbox, &dummy); + ninfo = fz_malloc(ctx, sizeof(*ninfo)); + ninfo->pixmap = pixmap; + ninfo->lock = lock; + ninfo->unlock = unlock; + ninfo->pageX0 = pageX0; + ninfo->pageY0 = pageY0; + ninfo->width = width; + ninfo->object = obj; + (*env)->SetLongField(env, self, cdevice_nativeinfo_fid, jlong_cast(ninfo)); + (*env)->SetObjectField(env, self, cdevice_nativeresource_fid, obj); + lockCDevice(env,self); + fz_clear_pixmap_rect_with_value(ctx, pixmap, 0xff, &clip); + unlockCDevice(env,ninfo); + device = fz_new_draw_device_with_bbox(ctx, pixmap, &clip); + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, pixmap); + fz_free(ctx, ninfo); + jni_rethrow(env, ctx); + } + return jlong_cast(device); +} + +static void androidDrawDevice_lock(JNIEnv *env, CDeviceNativeInfo *info) +{ + uint8_t *pixels; + + assert(info != NULL); + assert(info->object != NULL); + + if (AndroidBitmap_lockPixels(env, info->object, (void **)&pixels) < 0) + { + jni_throw(env, FZ_ERROR_GENERIC, "Bitmap lock failed in DrawDevice call"); + return; + } + + /* Now offset pixels to allow for the page offsets */ + //pixels += sizeof(int32_t) * (info->pageX0 + info->width * info->pageY0); + + info->pixmap->samples = pixels; +} + +static void androidDrawDevice_unlock(JNIEnv *env, CDeviceNativeInfo *info) +{ + assert(info != NULL); + assert(info->object != NULL); + + if (AndroidBitmap_unlockPixels(env, info->object) < 0) + { + jni_throw(env, FZ_ERROR_GENERIC, "Bitmap unlock failed in DrawDevice call"); + } +} + +JNIEXPORT jlong JNICALL +JNI_FN(AndroidDrawDevice_newNative)(JNIEnv *env, jobject self, jobject jbitmap, int pageX0, int pageY0, int pageX1, int pageY1, int patchX0, int patchY0, int patchX1, int patchY1) +{ + fz_context *ctx = get_context(env); + AndroidBitmapInfo info; + jlong device = 0; + int ret; + + fz_try(ctx) + { + if ((ret = AndroidBitmap_getInfo(env, jbitmap, &info)) < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "new DrawDevice failed to get bitmap info"); + + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) + fz_throw(ctx, FZ_ERROR_GENERIC, "new DrawDevice failed as bitmap format is not RGBA_8888"); + + if (info.stride != info.width*4) + fz_throw(ctx, FZ_ERROR_GENERIC, "new DrawDevice failed as bitmap width != stride"); + + device = newCDevice(env, self, ctx, jbitmap, info.width, info.height, androidDrawDevice_lock, androidDrawDevice_unlock, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return device; +} + +static void awtDrawDevice_lock(JNIEnv *env, CDeviceNativeInfo *info) +{ + int8_t *pixels; + + assert(info != NULL); + assert(info->object != NULL); + assert(info->pixmap != NULL); + + //pixels = (unsigned char *)((*env)->GetIntArrayElements(env, info->object, 0)); + + /* Now offset pixels to allow for the page offsets */ + pixels += sizeof(int32_t) * (info->pageX0 + info->width * info->pageY0); + + info->pixmap->samples = pixels; +} + +static void awtDrawDevice_unlock(JNIEnv *env, CDeviceNativeInfo *info) +{ + int8_t *pixels = info->pixmap->samples; + + assert(info != NULL); + assert(info->object != NULL); + assert(info->pixmap != NULL); + + /* Now offset pixels to allow for the page offsets */ + //pixels -= sizeof(int32_t) * (info->pageX0 + info->width * info->pageY0); + + (*env)->ReleaseIntArrayElements(env, info->object, (int *)(void *)pixels, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(AwtDrawDevice_newNative)(JNIEnv *env, jobject self, jobject rgba, jint w, jint h, int pageX0, int pageY0, int pageX1, int pageY1, int patchX0, int patchY0, int patchX1, int patchY1) +{ + fz_context *ctx = get_context(env); + + return newCDevice(env, self, ctx, rgba, w, h, awtDrawDevice_lock, awtDrawDevice_unlock, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1); +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_finalize)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + CDeviceNativeInfo *ninfo; + + ninfo = CAST(CDeviceNativeInfo *, (*env)->GetLongField(env, self, cdevice_nativeinfo_fid)); + if (ninfo != NULL) + { + fz_drop_pixmap(ctx, ninfo->pixmap); + fz_free(ctx, ninfo); + } + (*env)->SetLongField(env, self, cdevice_nativeinfo_fid, 0); +} + +JNIEXPORT void JNICALL +JNI_FN(CDevice_destroy)(JNIEnv *env, jobject self) +{ + JNI_FN(CDevice_finalize)(env, self); + JNI_FN(Device_finalize)(env, self); /* Super class destroy */ +} + +/* Path Interface */ + +JNIEXPORT void JNICALL +JNI_FN(Path_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_drop_path(ctx, path); +} + +JNIEXPORT void JNICALL +JNI_FN(Path_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Path_finalize)(env, self); + + (*env)->SetLongField(env, self, path_fid, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(Path_newNative)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_path *path = NULL; + + if (ctx == NULL) + return 0; + + fz_try(ctx) + { + path = fz_new_path(ctx); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jlong_cast(path); +} + +JNIEXPORT jobject JNICALL +JNI_FN(Path_currentPoint)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + fz_point point; + jmethodID cons; + jobject jpoint; + + if (ctx == NULL || path == NULL) + return NULL; + + fz_try(ctx) + { + point = fz_currentpoint(ctx, path); + jpoint = Point_from_fz_point(ctx, env, point); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jpoint; +} + +JNIEXPORT void JNICALL +JNI_FN(Path_moveTo)(JNIEnv * env, jobject self, float x, float y) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_moveto(ctx, path, x, y); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Path_lineTo)(JNIEnv * env, jobject self, float x, float y) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_lineto(ctx, path, x, y); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Path_curveTo)(JNIEnv * env, jobject self, float cx1, float cy1, float cx2, float cy2, float ex, float ey) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_curveto(ctx, path, cx1, cy1, cx2, cy2, ex, ey); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Path_curveToV)(JNIEnv * env, jobject self, float cx, float cy, float ex, float ey) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_curvetov(ctx, path, cx, cy, ex, ey); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Path_curveToY)(JNIEnv * env, jobject self, float cx, float cy, float ex, float ey) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_curvetoy(ctx, path, cx, cy, ex, ey); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Path_close)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_closepath(ctx, path); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Path_transform)(JNIEnv * env, jobject self, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + + if (ctx == NULL || path == NULL) + return; + + fz_try(ctx) + { + fz_transform_path(ctx, path, &ctm); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT jlong JNICALL +JNI_FN(Path_clone)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + fz_path *path2 = NULL; + + if (ctx == NULL || path == NULL) + return 0; + + fz_try(ctx) + { + path2 = fz_clone_path(ctx, path); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jlong_cast(path2); +} + +JNIEXPORT jobject JNICALL +JNI_FN(Path_bound)(JNIEnv * env, jobject self, jobject jstroke, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + jobject jrect; + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, jstroke); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_rect rect; + + if (ctx == NULL || path == NULL) + return NULL; + + fz_try(ctx) + { + fz_bound_path(ctx, path, stroke, &ctm, &rect); + + jrect = Rect_from_fz_rect(ctx, env, &rect); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jrect; +} + +typedef struct { + JNIEnv *env; + jobject jproc; +} pproc_data; + +static void +pathProcMoveTo(fz_context *ctx, void *arg, float x, float y) +{ + pproc_data *pproc = (pproc_data *)arg; + + (*pproc->env)->CallVoidMethod(pproc->env, pproc->jproc, pathproc_moveto_mid, x, y); +} + +static void +pathProcLineTo(fz_context *ctx, void *arg, float x, float y) +{ + pproc_data *pproc = (pproc_data *)arg; + + (*pproc->env)->CallVoidMethod(pproc->env, pproc->jproc, pathproc_lineto_mid, x, y); +} + +static void +pathProcCurveTo(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3) +{ + pproc_data *pproc = (pproc_data *)arg; + + (*pproc->env)->CallVoidMethod(pproc->env, pproc->jproc, pathproc_curveto_mid, x1, y1, x2, y2, x3, y3); +} + +static void +pathProcClose(fz_context *ctx, void *arg) +{ + pproc_data *pproc = (pproc_data *) arg; + + (*pproc->env)->CallVoidMethod(pproc->env, pproc->jproc, pathproc_close_mid); +} + +static const fz_path_processor path_proc = +{ + pathProcMoveTo, + pathProcLineTo, + pathProcCurveTo, + pathProcClose +}; + +void JNICALL +JNI_FN(Path_process)(JNIEnv * env, jobject self, jobject jproc) +{ + fz_context *ctx = get_context(env); + fz_path *path = fz_path_from_Path(env, self); + int i = 0, k = 0; + int n; + pproc_data data; + + if (path == NULL || jproc == NULL) + return; + + data.env = env; + data.jproc = jproc; + + fz_process_path(ctx, &path_proc, &data, path); +} + + +/* StrokeState interface */ + +JNIEXPORT void JNICALL +JNI_FN(StrokeState_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + if (ctx == NULL || stroke == NULL) + return; + + fz_drop_stroke_state(ctx, stroke); +} + +JNIEXPORT void JNICALL +JNI_FN(StrokeState_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(StrokeState_finalize)(env, self); + + (*env)->SetLongField(env, self, stroke_fid, 0); +} + + +JNIEXPORT jlong JNICALL +JNI_FN(Path_newStrokeState)(JNIEnv * env, jobject self, int startCap, int dashCap, int endCap, int lineJoin, float lineWidth, float miterLimit, float dashPhase, jfloatArray dash) +{ + fz_context *ctx = get_context(env); + fz_stroke_state *stroke = NULL; + jsize len = (*env)->GetArrayLength(env, dash); + + if (ctx == NULL) + return 0; + + fz_var(stroke); + + fz_try(ctx) + { + stroke = fz_new_stroke_state_with_dash_len(ctx, len); + stroke->start_cap = startCap; + stroke->dash_cap = dashCap; + stroke->end_cap = endCap; + stroke->linejoin = lineJoin; + stroke->linewidth = lineWidth; + stroke->miterlimit = miterLimit; + stroke->dash_phase = dashPhase; + stroke->dash_len = len; + (*env)->GetFloatArrayRegion(env, dash, 0, len, &stroke->dash_list[0]); + } + fz_catch(ctx) + { + fz_drop_stroke_state(ctx, stroke); + jni_rethrow(env, ctx); + } + return jlong_cast(stroke); +} + +JNIEXPORT int JNICALL +JNI_FN(StrokeState_getStartCap)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->start_cap : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(StrokeState_getDashCap)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->dash_cap : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(StrokeState_getEndCap)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->end_cap : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(StrokeState_getLineJoin)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->linejoin : 0; +} + +JNIEXPORT float JNICALL +JNI_FN(StrokeState_getLineWidth)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->linewidth : 0; +} + +JNIEXPORT float JNICALL +JNI_FN(StrokeState_getMiterLimit)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->miterlimit : 0; +} + +JNIEXPORT float JNICALL +JNI_FN(StrokeState_getDashPhase)(JNIEnv * env, jobject self) +{ + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + + return stroke ? stroke->dash_phase : 0; +} + +JNIEXPORT jfloatArray JNICALL +JNI_FN(StrokeState_getDashes)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, self); + jfloatArray arr; + + if (stroke->dash_len == 0) + return NULL; + + arr = (*env)->NewFloatArray(env, stroke->dash_len); + if (arr == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of floatArray failed"); + + (*env)->SetFloatArrayRegion(env, arr, 0, stroke->dash_len, &stroke->dash_list[0]); + + return arr; +} + +/* Text interface */ + +JNIEXPORT void JNICALL +JNI_FN(Text_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_text *text = fz_text_from_Text(env, self); + + if (ctx == NULL || text == NULL) + return; + + fz_drop_text(ctx, text); +} + +JNIEXPORT void JNICALL +JNI_FN(Text_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Text_finalize)(env, self); + + (*env)->SetLongField(env, self, text_fid, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(Text_clone)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_text *text = fz_text_from_Text(env, self); + fz_text *text2 = NULL; + + if (ctx == NULL || text == NULL) + return 0; + + fz_try(ctx) + { + text2 = fz_clone_text(ctx, text); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jlong_cast(text2); +} + +//JNIEXPORT jlong JNICALL +//JNI_FN(Text_newText)(JNIEnv * env, jobject self, jobject jfont, jobject jctm, int wmode) +//{ +// fz_context *ctx = get_context(env); +// fz_text *text = NULL; +// fz_font *font = fz_font_from_Font(env, jfont); +// fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); +// +// if (ctx == NULL) +// return 0; +// +// fz_try(ctx) +// { +// text = fz_new_text(ctx, font, &ctm, wmode); +// } +// fz_catch(ctx) +// { +// jni_rethrow(env, ctx); +// } +// return jlong_cast(text); +//} + +JNIEXPORT jobject JNICALL +JNI_FN(Text_bound)(JNIEnv * env, jobject self, jobject jstroke, jobject jctm) +{ + fz_context *ctx = get_context(env); + fz_text *text = fz_text_from_Text(env, self); + fz_stroke_state *stroke = fz_stroke_state_from_StrokeState(env, jstroke); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + jobject jrect; + fz_rect rect; + + if (ctx == NULL || text == NULL) + return NULL; + + fz_try(ctx) + { + fz_bound_text(ctx, text, stroke, &ctm, &rect); + + jrect = Rect_from_fz_rect(ctx, env, &rect); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jrect; +} + +//JNIEXPORT void JNICALL +//JNI_FN(Text_add)(JNIEnv * env, jobject self, int gid, int ucs, float x, float y) +//{ +// fz_context *ctx = get_context(env); +// fz_text *text = fz_text_from_Text(env, self); +// +// if (ctx == NULL || text == NULL) +// return; +// +// fz_try(ctx) +// { +// fz_add_text(ctx, text, gid, ucs, x, y); +// } +// fz_catch(ctx) +// { +// jni_rethrow(env, ctx); +// } +//} + +/* Image interface */ + +JNIEXPORT void JNICALL +JNI_FN(Image_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_image *image = fz_image_from_Image(env, self); + + if (ctx == NULL || image == NULL) + return; + + fz_drop_image(ctx, image); +} + +JNIEXPORT void JNICALL +JNI_FN(Image_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Image_finalize)(env, self); + + (*env)->SetLongField(env, self, image_fid, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(Image_newImageFromBitmap)(JNIEnv * env, jobject self, jobject jbitmap, jlong jmask) +{ + fz_context *ctx = get_context(env); + fz_image *mask = CAST(fz_image *, jmask); + fz_image *image = NULL; + fz_pixmap *pixmap = NULL; + AndroidBitmapInfo info; + void *pixels; + int ret; + + if (ctx == NULL) + return 0; + + fz_var(pixmap); + + fz_try(ctx) + { + if (mask && mask->mask) + fz_throw(ctx, FZ_ERROR_GENERIC, "new Image failed as mask cannot be masked"); + + if ((ret = AndroidBitmap_getInfo(env, jbitmap, &info)) < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "new Image failed to get bitmap info"); + + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) + fz_throw(ctx, FZ_ERROR_GENERIC, "new Image failed as bitmap format is not RGBA_8888"); + + if (info.stride != info.width) + fz_throw(ctx, FZ_ERROR_GENERIC, "new Image failed as bitmap width != stride"); + + pixmap = fz_new_pixmap(ctx, fz_device_rgb(ctx), info.width, info.height); + if (AndroidBitmap_lockPixels(env, jbitmap, &pixels) < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "Bitmap lock failed in new Image"); + memcpy(pixmap->samples, pixels, info.width * info.height * 4); + (void)AndroidBitmap_unlockPixels(env, jbitmap); + + image = fz_new_image_from_pixmap(ctx, fz_keep_pixmap(ctx, pixmap), fz_keep_image(ctx, mask)); + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, pixmap); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jlong_cast(image); +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getWidth)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->w : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getHeight)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->h : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getNumComponents)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->n : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getBitsPerComponent)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->bpc : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getXResolution)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->xres : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getYResolution)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->yres : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getImageMask)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->imagemask : 0; +} + +JNIEXPORT int JNICALL +JNI_FN(Image_getInterpolate)(JNIEnv * env, jobject self) +{ + fz_image *image = fz_image_from_Image(env, self); + + return image ? image->interpolate : 0; +} + +JNIEXPORT jobject JNICALL +JNI_FN(Image_getMask)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_image *img = fz_image_from_Image(env, self); + jobject jobj; + + if (img == NULL || img->mask == NULL) + return NULL; + + jobj = Image_from_fz_image(ctx, env, img->mask); + if (jobj != NULL) + fz_keep_image(ctx, img->mask); + + return jobj; + +died: + fz_throw(ctx, FZ_ERROR_GENERIC, "JNI creation of Image(Mask) failed"); + return NULL; +} + +/* Outline interface */ + +JNIEXPORT void JNICALL +JNI_FN(Outline_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_outline *outline = fz_outline_from_Outline(env, self); + + if (ctx == NULL || outline == NULL) + return; + + fz_drop_outline(ctx, outline); +} + +JNIEXPORT void JNICALL +JNI_FN(Outline_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Outline_finalize)(env, self); + + (*env)->SetLongField(env, self, outline_fid, 0); +} + +/* Annotation Interface */ + +JNIEXPORT void JNICALL +JNI_FN(Annotation_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_annot *annot = fz_annot_from_Annotation(env, self); + + if (ctx == NULL || link == NULL) + return; + + fz_drop_annot(ctx, annot); +} + +JNIEXPORT void JNICALL +JNI_FN(Annotation_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Annotation_finalize)(env, self); + + (*env)->SetLongField(env, self, annot_fid, 0); +} + +JNIEXPORT void JNICALL +JNI_FN(Annotation_run)(JNIEnv * env, jobject self, jobject jdev, jobject jctm, jobject jcookie) +{ + fz_context *ctx = get_context(env); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_cookie *cookie= fz_cookie_from_Cookie(env, jcookie); + fz_device *dev = fz_device_from_Device(env, jdev, ctx); + jobject jdoc; + fz_annot *annot = fz_annot_from_Annotation(env, self); + CDeviceNativeInfo *info; + + if (ctx == NULL || self == NULL || jdev == NULL) + return; + + fz_var(dev); + + info = lockCDevice(env, jdev); + + fz_try(ctx) + { + fz_run_annot(ctx, annot, dev, &ctm, cookie); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT jlong JNICALL +JNI_FN(Annotation_advance)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_annot *annot; + + if (ctx == NULL) + return; + + fz_try(ctx) + { + annot = fz_annot_from_Annotation(env, self); + + annot = fz_next_annot(ctx, annot); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jlong_cast(annot); +} + +/* Link interface */ + +JNIEXPORT void JNICALL +JNI_FN(Link_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_link *link = fz_link_from_Link(env, self); + + if (ctx == NULL || link == NULL) + return; + + fz_drop_link(ctx, link); +} + +JNIEXPORT void JNICALL +JNI_FN(Link_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Link_finalize)(env, self); + + (*env)->SetLongField(env, self, link_fid, 0); +} + +/* Document interface */ + +JNIEXPORT void JNICALL +JNI_FN(Document_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = fz_document_from_Document(env, self); + + if (ctx == NULL || doc == NULL) + return; + + fz_drop_document(ctx, doc); +} + +JNIEXPORT void JNICALL +JNI_FN(Document_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Document_finalize)(env, self); + + (*env)->SetLongField(env, self, document_fid, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(Document_newNative)(JNIEnv * env, jobject self, jstring jfilename) +{ + fz_context *ctx = get_context(env); + fz_document *document = NULL; + const char *filename = NULL; + + if (ctx == NULL || jfilename == NULL) + return 0; + + fz_var(filename); + + fz_try(ctx) + { + filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (filename == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to convert filename"); + document = fz_open_document(ctx, filename); + } + fz_always(ctx) + { + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + + return jlong_cast(document); +} + +JNIEXPORT int JNICALL +JNI_FN(Document_needsPassword)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + int ret; + + if (ctx == NULL || document == NULL) + return 0; + + fz_try(ctx) + { + ret = fz_needs_password(ctx, document); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + ret = 0; + } + return ret; +} + +JNIEXPORT int JNICALL +JNI_FN(Document_authenticatePassword)(JNIEnv * env, jobject self, jstring jpassword) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + int ret; + const char *password = NULL; + + if (ctx == NULL || document == NULL) + return 0; + + fz_var(password); + + fz_try(ctx) + { + if (jpassword == NULL) + password = ""; + else + { + password = (*env)->GetStringUTFChars(env, jpassword, NULL); + if (password == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to convert password"); + } + + ret = fz_authenticate_password(ctx, document, password); + } + fz_always(ctx) + { + if (jpassword != NULL) + (*env)->ReleaseStringUTFChars(env, jpassword, password); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + ret = 0; + } + return ret; +} + +JNIEXPORT int JNICALL +JNI_FN(Document_countPages)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + int ret; + + if (ctx == NULL || document == NULL) + return 0; + + fz_try(ctx) + { + ret = fz_count_pages(ctx, document); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + ret = 0; + } + return ret; +} + +JNIEXPORT jobject JNICALL +JNI_FN(Document_getPage)(JNIEnv * env, jobject self, int n) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + fz_page *page = NULL; + jobject jpage; + + if (ctx == NULL || document == NULL) + return NULL; + + fz_var(page); + + fz_try(ctx) + { + page = fz_load_page(ctx, document, n); + if (page == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "getPage failed"); + + jpage = Page_from_fz_page(ctx, env, page); + } + fz_catch(ctx) + { + fz_drop_page(ctx, page); + jni_rethrow(env, ctx); + jpage = NULL; + } + return jpage; +} + +JNIEXPORT jobject JNICALL +JNI_FN(Document_getFileFormat)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + char info[64]; + + if (ctx == NULL || document == NULL) + return NULL; + + fz_lookup_metadata(ctx, document, FZ_META_FORMAT, info, sizeof(info)); + + return (*env)->NewStringUTF(env, info); +} + +JNIEXPORT jboolean JNICALL +JNI_FN(Document_isUnencryptedPDF)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + pdf_document *idoc = pdf_specifics(ctx, document); + int cryptVer; + + if (idoc == NULL) + return JNI_FALSE; // Not a PDF + + cryptVer = pdf_crypt_version(ctx, idoc); + return (cryptVer == 0) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jobject JNICALL +JNI_FN(Document_getOutline)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *document = fz_document_from_Document(env, self); + fz_outline *outline = NULL; + jobject joutline; + + if (ctx == NULL || document == NULL) + return NULL; + + fz_var(outline); + + fz_try(ctx) + { + outline = fz_load_outline(ctx, document); + if (outline == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "getOutline failed"); + + joutline = Outline_from_fz_outline(ctx, env, outline); + } + fz_catch(ctx) + { + fz_drop_outline(ctx, outline); + jni_rethrow(env, ctx); + joutline = NULL; + } + return joutline; +} + +/* Page interface */ + +JNIEXPORT void JNICALL +JNI_FN(Page_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_page *page = fz_page_from_Page(env, self); + + if (ctx == NULL || page == NULL) + return; + + fz_drop_page(ctx, page); +} + +JNIEXPORT void JNICALL +JNI_FN(Page_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Page_finalize)(env, self); + + (*env)->SetLongField(env, self, page_fid, 0); + (*env)->SetLongField(env, self, page_annots_fid, 0); +} + +JNIEXPORT jobject JNICALL +JNI_FN(Page_bound)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_page *page = fz_page_from_Page(env, self); + jobject jrect; + fz_rect rect; + + if (ctx == NULL || page == NULL) + return NULL; + + fz_try(ctx) + { + fz_bound_page(ctx, page, &rect); + + jrect = Rect_from_fz_rect(ctx, env, &rect); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jrect; +} + +JNIEXPORT void JNICALL +JNI_FN(Page_run)(JNIEnv * env, jobject self, jobject jdev, jobject jctm, jobject jcookie) +{ + fz_context *ctx = get_context(env); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_cookie *cookie = fz_cookie_from_Cookie(env, jcookie); + fz_device *dev = fz_device_from_Device(env, jdev, ctx); + fz_page *page = fz_page_from_Page(env, self); + CDeviceNativeInfo *info; + + if (ctx == NULL || self == NULL || jdev == NULL) + return; + + info = lockCDevice(env, jdev); + + fz_try(ctx) + { + fz_run_page(ctx, page, dev, &ctm, cookie); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(Page_runPageContents)(JNIEnv * env, jobject self, jobject jdev, jobject jctm, jobject jcookie) +{ + fz_context *ctx = get_context(env); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_cookie *cookie = fz_cookie_from_Cookie(env, jcookie); + fz_device *dev = fz_device_from_Device(env, jdev, ctx); + fz_page *page = fz_page_from_Page(env, self); + CDeviceNativeInfo *info; + + if (ctx == NULL) + return; + + info = lockCDevice(env, jdev); + + fz_try(ctx) + { + fz_run_page_contents(ctx, page, dev, &ctm, cookie); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT jobject JNICALL +JNI_FN(Page_getAnnotations)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_page *page = fz_page_from_Page(env, self); + jobject jrect; + fz_rect rect; + fz_annot *annot = NULL; + fz_annot *first = NULL; + jobject jannots = NULL; + int ret; + int annot_count; + int i; + + if (ctx == NULL || page == NULL) + return NULL; + + fz_var(annot); + fz_var(jannots); + + fz_try(ctx) + { + jannots = (*env)->GetObjectField(env, self, page_annots_fid); + + first = fz_first_annot(ctx, page); + + /* Count the annotations */ + annot = first; + for (annot_count = 0; annot != NULL; annot_count++) + annot = fz_next_annot(ctx, annot); + + if (annot_count == 0) + { + /* If no annotations, we don't want an annotation + * object stored in the page. */ + if (jannots != NULL) + { + (*env)->SetObjectField(env, self, page_annots_fid, NULL); + } + break; /* No annotations! */ + } + + jannots = (*env)->NewObjectArray(env, annot_count, annot_class, NULL); + if (jannots == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "getAnnotations failed (1)"); + (*env)->SetObjectField(env, self, page_annots_fid, jannots); + + /* Now run through actually creating the annotation objects */ + annot = first; + for (i = 0; annot != NULL && i < annot_count; i++) + { + jobject jannot = Annotation_from_fz_annot(ctx, env, annot); + (*env)->SetObjectArrayElement(env, jannots, i, jannot); + annot = fz_next_annot(ctx, annot); + } + if (annot != NULL || i != annot_count) + fz_throw(ctx, FZ_ERROR_GENERIC, "getAnnotations failed (4)"); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jannots; +} + +/* private native final Link[] getLinks(jlong ctx); */ + + +/* Cookie interface */ + +JNIEXPORT void JNICALL +JNI_FN(Cookie_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_cookie *cookie = fz_cookie_from_Cookie(env, self); + + if (ctx == NULL || cookie == NULL) + return; + + fz_free(ctx, cookie); +} + +JNIEXPORT void JNICALL +JNI_FN(Cookie_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(Cookie_finalize)(env, self); + + (*env)->SetLongField(env, self, cookie_fid, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(Cookie_newNative)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + + return jlong_cast(fz_malloc_struct(ctx, fz_cookie)); +} + +JNIEXPORT void JNICALL +JNI_FN(Cookie_abort)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_cookie *cookie = fz_cookie_from_Cookie(env, self); + + if (ctx == NULL || cookie == NULL) + return; + + cookie->abort = 1; +} + +/* DisplayList interface */ +JNIEXPORT jlong JNICALL +JNI_FN(DisplayList_newNative)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + + return jlong_cast(fz_new_display_list(ctx)); +} + +JNIEXPORT void JNICALL +JNI_FN(DisplayList_run)(JNIEnv * env, jobject self, jobject jdev, jobject jctm, jobject jrect, jobject jcookie) +{ + fz_context *ctx = get_context(env); + fz_display_list *list = fz_display_list_from_DisplayList(env, self); + fz_matrix ctm = fz_matrix_from_Matrix(env, jctm); + fz_cookie *cookie = fz_cookie_from_Cookie(env, jcookie); + fz_device *dev = fz_device_from_Device(env, jdev, ctx); + CDeviceNativeInfo *info; + fz_rect local_rect; + fz_rect *rect; + + if (ctx == NULL || self == NULL || jdev == NULL || list == NULL) + return; + + /* Use a scissor rectangle if one is supplied */ + if (jrect == NULL) + { + rect = NULL; + } + else + { + rect = &local_rect; + local_rect = fz_rect_from_Rect(env, jrect); + } + + info = lockCDevice(env, jdev); + + fz_try(ctx) + { + fz_run_display_list(ctx, list, dev, &ctm, rect, cookie); + } + fz_always(ctx) + { + unlockCDevice(env, info); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } +} + +JNIEXPORT void JNICALL +JNI_FN(DisplayList_finalize)(JNIEnv * env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_display_list *list = fz_display_list_from_DisplayList(env, self); + + if (ctx == NULL || list == NULL) + return; + + fz_drop_display_list(ctx, list); +} + +JNIEXPORT void JNICALL +JNI_FN(DisplayList_destroy)(JNIEnv * env, jobject self) +{ + JNI_FN(DisplayList_finalize)(env, self); + + (*env)->SetLongField(env, self, displaylist_fid, 0); +} + +JNIEXPORT jlong JNICALL +JNI_FN(DisplayListDevice_newNative)(JNIEnv *env, jobject self, jobject jlist) +{ + fz_context *ctx = get_context(env); + fz_display_list *list = fz_display_list_from_DisplayList(env, jlist); + fz_device *device = NULL; + int ret; + unsigned char dummy; + + if (ctx == NULL || list == NULL) + return 0; + + fz_try(ctx) + { + device = fz_new_list_device(ctx, list); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + return jlong_cast(device); +} |