From 4bd83a37c63e9aff2938fed9192e815e9d0c1d66 Mon Sep 17 00:00:00 2001 From: Sebastian Rasmussen Date: Mon, 13 Nov 2017 19:31:29 +0100 Subject: jni/js: Add support for annotation modification dates. --- include/mupdf/pdf/annot.h | 11 +- platform/java/example/Viewer.java | 1 + platform/java/mupdf_native.c | 35 ++++++ platform/java/mupdf_native.h | 16 +++ .../src/com/artifex/mupdf/fitz/PDFAnnotation.java | 10 ++ source/pdf/pdf-annot-edit.c | 129 ++++++++++++++++++++- source/tools/murun.c | 30 +++++ 7 files changed, 223 insertions(+), 9 deletions(-) diff --git a/include/mupdf/pdf/annot.h b/include/mupdf/pdf/annot.h index e03ded3f..bebe77f7 100644 --- a/include/mupdf/pdf/annot.h +++ b/include/mupdf/pdf/annot.h @@ -205,11 +205,14 @@ char *pdf_copy_annot_author(fz_context *ctx, pdf_annot *annot); void pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author); /* - pdf_annot_author: return the date of an annotation. + pdf_annot_modification_date: Get annotation's modification date in seconds since the epoch. */ -// TODO: creation date -// TODO: modification date -const char *pdf_annot_date(fz_context *ctx, pdf_annot *annot); +int pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot); + +/* + pdf_set_annot_modification_date: Set annotation's modification date in seconds since the epoch. +*/ +void pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int time); /* pdf_annot_irt: return the indirect reference that this annotation is in reply to. diff --git a/platform/java/example/Viewer.java b/platform/java/example/Viewer.java index a559c218..e9a24eae 100644 --- a/platform/java/example/Viewer.java +++ b/platform/java/example/Viewer.java @@ -10,6 +10,7 @@ import java.io.File; import java.io.FilenameFilter; import java.lang.reflect.Field; import java.util.Vector; +import java.util.Date; public class Viewer extends Frame implements WindowListener, ActionListener, ItemListener, TextListener { diff --git a/platform/java/mupdf_native.c b/platform/java/mupdf_native.c index 890bc6b1..eda01ed9 100644 --- a/platform/java/mupdf_native.c +++ b/platform/java/mupdf_native.c @@ -8300,6 +8300,41 @@ FUN(PDFAnnotation_setAuthor)(JNIEnv *env, jobject self, jstring jauthor) jni_rethrow(env, ctx); } +JNIEXPORT jlong JNICALL +FUN(PDFAnnotation_getModificationDateNative)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + pdf_annot *annot = from_PDFAnnotation(env, self); + jlong t; + + if (!ctx || !annot) return -1; + + fz_try(ctx) + t = pdf_annot_modification_date(ctx, annot); + fz_catch(ctx) + { + jni_rethrow(env, ctx); + return -1; + } + + return t * 1000; +} + +JNIEXPORT void JNICALL +FUN(PDFAnnotation_setModificationDate)(JNIEnv *env, jobject self, jlong time) +{ + fz_context *ctx = get_context(env); + pdf_annot *annot = from_PDFAnnotation(env, self); + + fz_try(ctx) + pdf_set_annot_modification_date(ctx, annot, time / 1000); + fz_catch(ctx) + { + jni_rethrow(env, ctx); + return; + } +} + JNIEXPORT jobject JNICALL FUN(PDFAnnotation_getRect)(JNIEnv *env, jobject self) { diff --git a/platform/java/mupdf_native.h b/platform/java/mupdf_native.h index dfc62d85..2065f93e 100644 --- a/platform/java/mupdf_native.h +++ b/platform/java/mupdf_native.h @@ -1520,6 +1520,22 @@ JNIEXPORT jstring JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_getAuthor JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_setAuthor (JNIEnv *, jobject, jstring); +/* + * Class: com_artifex_mupdf_fitz_PDFAnnotation + * Method: getModificationDateNative + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_getModificationDateNative + (JNIEnv *, jobject); + +/* + * Class: com_artifex_mupdf_fitz_PDFAnnotation + * Method: setModificationDate + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_artifex_mupdf_fitz_PDFAnnotation_setModificationDate + (JNIEnv *, jobject, jlong); + /* * Class: com_artifex_mupdf_fitz_PDFAnnotation * Method: getLineEndingStyles diff --git a/platform/java/src/com/artifex/mupdf/fitz/PDFAnnotation.java b/platform/java/src/com/artifex/mupdf/fitz/PDFAnnotation.java index f836a601..52a433de 100644 --- a/platform/java/src/com/artifex/mupdf/fitz/PDFAnnotation.java +++ b/platform/java/src/com/artifex/mupdf/fitz/PDFAnnotation.java @@ -1,5 +1,7 @@ package com.artifex.mupdf.fitz; +import java.util.Date; + public class PDFAnnotation extends Annotation { static { @@ -62,6 +64,14 @@ public class PDFAnnotation extends Annotation public native void setInteriorColor(float[] color); public native String getAuthor(); public native void setAuthor(String author); + protected native long getModificationDateNative(); + protected native void setModificationDate(long time); + public Date getModificationDate() { + return new Date(getModificationDateNative()); + } + public void setModificationDate(Date date) { + setModificationDate(date.getTime()); + } public native int[] getLineEndingStyles(); public native void setLineEndingStyles(int startStyle, int endStyle); diff --git a/source/pdf/pdf-annot-edit.c b/source/pdf/pdf-annot-edit.c index 09e63002..730d7b93 100644 --- a/source/pdf/pdf-annot-edit.c +++ b/source/pdf/pdf-annot-edit.c @@ -2,9 +2,16 @@ #include "mupdf/pdf.h" #include +#include + +#ifdef _WIN32 +#define timegm _mkgmtime +#endif #define TEXT_ANNOT_SIZE (25.0f) +#define isdigit(c) (c >= '0' && c <= '9') + const char * pdf_string_from_annot_type(fz_context *ctx, fz_annot_type type) { @@ -280,7 +287,6 @@ static pdf_obj *open_subtypes[] = { NULL, }; - int pdf_annot_has_open(fz_context *ctx, pdf_annot *annot) { @@ -843,6 +849,106 @@ pdf_set_text_annot_position(fz_context *ctx, pdf_annot *annot, fz_point pt) pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_F, pdf_new_int(ctx, doc, flags)); } +static void +pdf_format_date(fz_context *ctx, char *s, int n, time_t secs) +{ +#ifdef _POSIX_SOURCE + struct tm tmbuf, *tm = gmtime_r(&secs, &tmbuf); +#else + struct tm *tm = gmtime(&secs); +#endif + if (!tm) + fz_strlcpy(s, "D:19700101000000Z", n); + else + strftime(s, n, "D:%Y%m%d%H%M%SZ", tm); +} + +static int +pdf_parse_date(fz_context *ctx, const char *s) +{ + int tz_sign, tz_hour, tz_min, tz_adj; + struct tm tm; + time_t utc; + + if (!s) + return 0; + + memset(&tm, 0, sizeof tm); + tm.tm_mday = 1; + + tz_sign = 1; + tz_hour = 0; + tz_min = 0; + + if (s[0] == 'D' && s[1] == ':') + s += 2; + + if (!isdigit(s[0]) || !isdigit(s[1]) || !isdigit(s[2]) || !isdigit(s[3])) + { + fz_warn(ctx, "invalid date format (missing year)"); + return 0; + } + tm.tm_year = (s[0]-'0')*1000 + (s[1]-'0')*100 + (s[2]-'0')*10 + (s[3]-'0') - 1900; + s += 4; + + if (isdigit(s[0]) && isdigit(s[1])) + { + tm.tm_mon = (s[0]-'0')*10 + (s[1]-'0') - 1; /* month is 0-11 in struct tm */ + s += 2; + if (isdigit(s[0]) && isdigit(s[1])) + { + tm.tm_mday = (s[0]-'0')*10 + (s[1]-'0'); + s += 2; + if (isdigit(s[0]) && isdigit(s[1])) + { + tm.tm_hour = (s[0]-'0')*10 + (s[1]-'0'); + s += 2; + if (isdigit(s[0]) && isdigit(s[1])) + { + tm.tm_min = (s[0]-'0')*10 + (s[1]-'0'); + s += 2; + if (isdigit(s[0]) && isdigit(s[1])) + { + tm.tm_sec = (s[0]-'0')*10 + (s[1]-'0'); + s += 2; + } + } + } + } + } + + if (s[0] == 'Z') + { + s += 1; + } + else if ((s[0] == '-' || s[0] == '+') && isdigit(s[1]) && isdigit(s[2])) + { + tz_sign = (s[0] == '-') ? -1 : 1; + tz_hour = (s[1]-'0')*10 + (s[2]-'0'); + s += 3; + if (s[0] == '\'' && isdigit(s[1]) && isdigit(s[2])) + { + tz_min = (s[1]-'0')*10 + (s[2]-'0'); + s += 3; + if (s[0] == '\'') + s += 1; + } + } + + if (s[0] != 0) + fz_warn(ctx, "invalid date format (garbage at end)"); + + utc = timegm(&tm); + if (utc == (time_t)-1) + { + fz_warn(ctx, "date overflow error"); + return 0; + } + + tz_adj = tz_sign * (tz_hour * 3600 + tz_min * 60); + return utc - tz_adj; +} + static pdf_obj *markup_subtypes[] = { PDF_NAME_Text, PDF_NAME_FreeText, @@ -863,11 +969,24 @@ static pdf_obj *markup_subtypes[] = { NULL, }; -const char * -pdf_annot_date(fz_context *ctx, pdf_annot *annot) +int +pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot) { - // TODO: PDF_NAME_M - return pdf_to_str_buf(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME_CreationDate)); + pdf_obj *date = pdf_dict_get(ctx, annot->obj, PDF_NAME_M); + return date ? pdf_parse_date(ctx, pdf_to_str_buf(ctx, date)) : 0; +} + +void +pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int secs) +{ + pdf_document *doc = annot->page->doc; + char s[40]; + + check_allowed_subtypes(ctx, annot, PDF_NAME_M, markup_subtypes); + + pdf_format_date(ctx, s, sizeof s, secs); + pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_M, pdf_new_string(ctx, doc, s, strlen(s))); + pdf_dirty_annot(ctx, annot); } int diff --git a/source/tools/murun.c b/source/tools/murun.c index 69765164..3b03e8f7 100644 --- a/source/tools/murun.c +++ b/source/tools/murun.c @@ -4312,6 +4312,34 @@ static void ffi_PDFAnnotation_setAuthor(js_State *J) rethrow(J); } +static void ffi_PDFAnnotation_getModificationDate(js_State *J) +{ + fz_context *ctx = js_getcontext(J); + pdf_annot *annot = js_touserdata(J, 0, "pdf_annot"); + double time; + + fz_try(ctx) + time = pdf_annot_modification_date(ctx, annot); + fz_catch(ctx) + rethrow(J); + + js_getglobal(J, "Date"); + js_pushnumber(J, time * 1000); + js_construct(J, 1); +} + +static void ffi_PDFAnnotation_setModificationDate(js_State *J) +{ + fz_context *ctx = js_getcontext(J); + pdf_annot *annot = js_touserdata(J, 0, "pdf_annot"); + double time = js_tonumber(J, 1); + + fz_try(ctx) + pdf_set_annot_modification_date(ctx, annot, time / 1000); + fz_catch(ctx) + rethrow(J); +} + static void ffi_PDFAnnotation_updateAppearance(js_State *J) { fz_context *ctx = js_getcontext(J); @@ -4643,6 +4671,8 @@ int murun_main(int argc, char **argv) jsB_propfun(J, "PDFAnnotation.setInkList", ffi_PDFAnnotation_setInkList, 1); jsB_propfun(J, "PDFAnnotation.getAuthor", ffi_PDFAnnotation_getAuthor, 0); jsB_propfun(J, "PDFAnnotation.setAuthor", ffi_PDFAnnotation_setAuthor, 1); + jsB_propfun(J, "PDFAnnotation.getModificationDate", ffi_PDFAnnotation_getModificationDate, 0); + jsB_propfun(J, "PDFAnnotation.setModificationDate", ffi_PDFAnnotation_setModificationDate, 0); jsB_propfun(J, "PDFAnnotation.updateAppearance", ffi_PDFAnnotation_updateAppearance, 0); } js_setregistry(J, "pdf_annot"); -- cgit v1.2.3