1 // CoolReader3 Engine JNI interface
2 // BASED on Android NDK Plasma example
3 
4 #include <jni.h>
5 #include <time.h>
6 #include <android/log.h>
7 #include <android/api-level.h>
8 
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 
14 #include "org_coolreader_crengine_Engine.h"
15 #include "org_coolreader_crengine_DocView.h"
16 
17 #include "cr3java.h"
18 #include "../../crengine/include/cr3version.h"
19 #include "docview.h"
20 #include "../../crengine/include/crengine.h"
21 #include "../../crengine/include/epubfmt.h"
22 #include "../../crengine/include/pdbfmt.h"
23 #include "../../crengine/include/lvopc.h"
24 #include "../../crengine/include/fb3fmt.h"
25 #include "../../crengine/include/docxfmt.h"
26 #include "../../crengine/include/odtfmt.h"
27 #include "../../crengine/include/lvstream.h"
28 
29 
30 #include <../../crengine/include/fb2def.h>
31 
32 #include "fc-lang-cat.h"
33 
34 #define XS_IMPLEMENT_SCHEME 1
35 #include <../../crengine/include/fb2def.h>
36 #include <sys/stat.h>
37 
38 #if defined(__arm__) || defined(__aarch64__) || defined(__i386__) || defined(__mips__)
39 #define USE_COFFEECATCH 1
40 #endif
41 
42 
43 #if USE_COFFEECATCH == 1
44 #include "coffeecatch/coffeecatch.h"
45 #include "coffeecatch/coffeejni.h"
46 #else
47 #define COFFEE_TRY_JNI(ENV, CODE) CODE;
48 #endif
49 
50 #ifdef _DEBUG
51 // missing in system ZLIB with DEBUG option turned off
52 int z_verbose=0;
53 extern "C" void z_error(char * msg);
z_error(char * msg)54 void z_error(char * msg) {
55 	fprintf(stderr, "%s\n", msg);
56 	exit(1);
57 }
58 #endif
59 /// returns current time representation string
getDateTimeString(time_t t)60 static lString32 getDateTimeString( time_t t )
61 {
62     tm * bt = localtime(&t);
63     char str[32];
64 #ifdef _LINUX
65     snprintf(str, 32,
66 #else
67     sprintf(str,
68 #endif
69         "%04d/%02d/%02d %02d:%02d", bt->tm_year+1900, bt->tm_mon+1, bt->tm_mday, bt->tm_hour, bt->tm_min);
70     str[31] = 0;
71     return Utf8ToUnicode( lString8( str ) );
72 }
73 
74 #if 0
75 static lString32 extractDocSeriesReverse( ldomDocument * doc, int & seriesNumber )
76 {
77 	seriesNumber = 0;
78     lString32 res;
79     ldomXPointer p = doc->createXPointer(L"/FictionBook/description/title-info/sequence");
80     if ( p.isNull() )
81         return res;
82     ldomNode * series = p.getNode();
83     if ( series ) {
84         lString32 sname = series->getAttributeValue( attr_name );
85         lString32 snumber = series->getAttributeValue( attr_number );
86         if ( !sname.empty() ) {
87             res << L"(";
88             if ( !snumber.empty() ) {
89                 res << L"#" << snumber << L" ";
90                 seriesNumber = snumber.atoi();
91             }
92             res << sname;
93             res << L")";
94         }
95     }
96     return res;
97 }
98 #endif
99 
100 class BookProperties
101 {
102 public:
103     lString32 filename;
104     lString32 title;
105     lString32 author;
106     lString32 series;
107     int filesize;
108     lString32 filedate;
109     int seriesNumber;
110     lString32 language;
111     lUInt32 crc32;
112     lString32 keywords;
113     lString32 description;
114     doc_format_t format;
115 };
116 
GetEPUBBookProperties(const char * name,LVStreamRef stream,BookProperties * pBookProps)117 static bool GetEPUBBookProperties(const char *name, LVStreamRef stream, BookProperties * pBookProps)
118 {
119     LVContainerRef arc = LVOpenArchieve(stream );
120     if ( arc.isNull() )
121         return false; // not a ZIP archive
122 
123     // check root media type
124     lString32 rootfilePath = EpubGetRootFilePath(arc);
125     if ( rootfilePath.empty() )
126     	return false;
127 
128     lString32 codeBase;
129     codeBase=LVExtractPath(rootfilePath, false);
130 
131     LVStreamRef content_stream = arc->OpenStream(rootfilePath.c_str(), LVOM_READ);
132     if ( content_stream.isNull() )
133         return false;
134 
135     ldomDocument * doc = LVParseXMLStream( content_stream );
136     if ( !doc )
137         return false;
138 
139     time_t t = (time_t)time(0);
140     struct stat fs;
141     if ( !stat( name, &fs ) ) {
142         t = fs.st_mtime;
143     }
144 
145     lString32 author = doc->textFromXPath( lString32("package/metadata/creator")).trim();
146     lString32 title = doc->textFromXPath( lString32("package/metadata/title")).trim();
147     lString32 language = doc->textFromXPath( lString32("package/metadata/language")).trim();
148 	// There may be multiple <dc:subject> tags, which are usually used for keywords, categories
149 	bool subjects_set = false;
150 	lString32 subjects;
151 	for ( size_t i=1; i<=EPUB_META_MAX_ITER; i++ ) {
152 		ldomNode * item = doc->nodeFromXPath(lString32("package/metadata/subject[") << fmt::decimal(i) << "]");
153 		if (!item)
154 			break;
155 		lString32 subject = item->getText().trim();
156 		if (subjects_set) {
157 			subjects << "\n" << subject;
158 		}
159 		else {
160 			subjects << subject;
161 			subjects_set = true;
162 		}
163 	}
164     lString32 description = doc->textFromXPath( cs32("package/metadata/description")).trim();
165     pBookProps->author = author;
166     pBookProps->title = title;
167     pBookProps->language = language;
168     pBookProps->keywords = subjects;
169     pBookProps->description = description;
170 
171     for ( int i=1; i<20; i++ ) {
172         ldomNode * item = doc->nodeFromXPath( lString32("package/metadata/meta[") << fmt::decimal(i) << "]" );
173         if ( !item )
174             break;
175         lString32 name = item->getAttributeValue("name");
176         lString32 content = item->getAttributeValue("content");
177         if (name == "calibre:series")
178         	pBookProps->series = content.trim();
179         else if (name == "calibre:series_index")
180         	pBookProps->seriesNumber = content.trim().atoi();
181     }
182 
183     pBookProps->filesize = (long)stream->GetSize();
184     pBookProps->filename = lString32(name);
185     pBookProps->filedate = getDateTimeString( t );
186     pBookProps->crc32 = stream->getcrc32();
187 
188     delete doc;
189 
190     return true;
191 }
192 
GetFB3BookProperties(const char * name,LVStreamRef stream,BookProperties * pBookProps)193 static bool GetFB3BookProperties(const char *name, LVStreamRef stream, BookProperties * pBookProps)
194 {
195 	LVContainerRef arc = LVOpenArchieve( stream );
196 	if ( arc.isNull() )
197 		return false; // not a ZIP archive
198 
199 	OpcPackage package(arc);
200 
201 	fb3ImportContext context(&package);
202 
203 	CRPropRef doc_props = LVCreatePropsContainer();
204 	package.readCoreProperties(doc_props);
205 	pBookProps->title = doc_props->getStringDef(DOC_PROP_TITLE, "");
206 	pBookProps->author = doc_props->getStringDef(DOC_PROP_AUTHORS, "");
207 	pBookProps->description = doc_props->getStringDef(DOC_PROP_DESCRIPTION, "");
208 
209 	ldomDocument * descDoc = context.getDescription();
210 	if ( descDoc ) {
211 		pBookProps->language = descDoc->textFromXPath( cs32("fb3-description/lang") );
212 	} else {
213 		CRLog::error("Couldn't parse description doc");
214 	}
215 
216 	time_t t = (time_t)time(0);
217 	struct stat fs;
218 	if ( !stat( name, &fs ) ) {
219 		t = fs.st_mtime;
220 	}
221 	pBookProps->filesize = (long)stream->GetSize();
222 	pBookProps->filename = lString32(name);
223 	pBookProps->filedate = getDateTimeString( t );
224 	pBookProps->crc32 = stream->getcrc32();
225 	return true;
226 }
227 
GetDOCXBookProperties(const char * name,LVStreamRef stream,BookProperties * pBookProps)228 static bool GetDOCXBookProperties(const char *name, LVStreamRef stream, BookProperties * pBookProps)
229 {
230 	LVContainerRef arc = LVOpenArchieve( stream );
231 	if ( arc.isNull() )
232 		return false; // not a ZIP archive
233 
234 	OpcPackage package(arc);
235 
236 	CRPropRef doc_props = LVCreatePropsContainer();
237 	package.readCoreProperties(doc_props);
238 	pBookProps->title = doc_props->getStringDef(DOC_PROP_TITLE, "");
239 	pBookProps->author = doc_props->getStringDef(DOC_PROP_AUTHORS, "");
240 	pBookProps->description = doc_props->getStringDef(DOC_PROP_DESCRIPTION, "");
241 	pBookProps->language = doc_props->getStringDef(DOC_PROP_LANGUAGE, "");
242 
243 	time_t t = (time_t)time(0);
244 	struct stat fs;
245 	if ( !stat( name, &fs ) ) {
246 		t = fs.st_mtime;
247 	}
248 	pBookProps->filesize = (long)stream->GetSize();
249 	pBookProps->filename = lString32(name);
250 	pBookProps->filedate = getDateTimeString( t );
251 	pBookProps->crc32 = stream->getcrc32();
252 
253 	return true;
254 }
255 
GetODTBookProperties(const char * name,LVStreamRef stream,BookProperties * pBookProps)256 static bool GetODTBookProperties(const char *name, LVStreamRef stream, BookProperties * pBookProps)
257 {
258 	LVContainerRef arc = LVOpenArchieve( stream );
259 	if ( arc.isNull() )
260 		return false; // not a ZIP archive
261 
262 	OpcPackage package(arc);
263 
264 	//Read document metadata
265 	LVStreamRef meta_stream = arc->OpenStream(U"meta.xml", LVOM_READ);
266 	if ( meta_stream.isNull() )
267 		return false;
268 	ldomDocument * metaDoc = LVParseXMLStream( meta_stream );
269 	if ( !metaDoc ) {
270 		CRLog::error("Couldn't parse document meta data");
271 		return false;
272 	} else {
273 		CRPropRef doc_props = LVCreatePropsContainer();
274 
275 		lString32 author = metaDoc->textFromXPath( cs32("document-meta/meta/creator") );
276 		lString32 title = metaDoc->textFromXPath( cs32("document-meta/meta/title") );
277 		lString32 description = metaDoc->textFromXPath( cs32("document-meta/meta/description") );
278 		doc_props->setString(DOC_PROP_TITLE, title);
279 		doc_props->setString(DOC_PROP_AUTHORS, author );
280 		doc_props->setString(DOC_PROP_DESCRIPTION, description );
281 		delete metaDoc;
282 	}
283 
284 	time_t t = (time_t)time(0);
285 	struct stat fs;
286 	if ( !stat( name, &fs ) ) {
287 		t = fs.st_mtime;
288 	}
289 	pBookProps->filesize = (long)stream->GetSize();
290 	pBookProps->filename = lString32(name);
291 	pBookProps->filedate = getDateTimeString( t );
292 	pBookProps->crc32 = stream->getcrc32();
293 
294 	return true;
295 }
296 
GetBookProperties(const char * name,BookProperties * pBookProps)297 static bool GetBookProperties(const char *name,  BookProperties * pBookProps)
298 {
299     CRLog::trace("GetBookProperties( %s )", name);
300 
301     // check archieve
302     lString32 arcPathName;
303     lString32 arcItemPathName;
304     bool isArchiveFile = LVSplitArcName( lString32(name), arcPathName, arcItemPathName );
305 
306     // open stream
307     LVStreamRef stream = LVOpenFileStream( (isArchiveFile ? arcPathName : Utf8ToUnicode(lString8(name))).c_str() , LVOM_READ);
308     if (!stream) {
309         CRLog::error("cannot open file %s", name);
310         return false;
311     }
312 
313 
314     pBookProps->format = doc_format_none;
315     if ( DetectEpubFormat( stream ) ) {
316         CRLog::trace("GetBookProperties() : epub format detected");
317         pBookProps->format = doc_format_epub;
318     	return GetEPUBBookProperties( name, stream, pBookProps );
319     }
320     if ( DetectFb3Format( stream ) ) {
321         CRLog::trace("GetBookProperties() : fb3 format detected");
322         pBookProps->format = doc_format_fb3;
323         return GetFB3BookProperties( name, stream, pBookProps );
324     }
325 	if ( DetectDocXFormat( stream ) ) {
326 		CRLog::trace("GetBookProperties() : docx format detected");
327         pBookProps->format = doc_format_docx;
328 		return GetDOCXBookProperties( name, stream, pBookProps );
329 	}
330 	if ( DetectOpenDocumentFormat( stream ) ) {
331 		CRLog::trace("GetBookProperties() : odt format detected");
332         pBookProps->format = doc_format_odt;
333 		return GetODTBookProperties( name, stream, pBookProps );
334 	}
335 
336     time_t t = (time_t)time(0);
337 
338     if ( isArchiveFile ) {
339         int arcsize = (int)stream->GetSize();
340         LVContainerRef container = LVOpenArchieve(stream);
341         if ( container.isNull() ) {
342             CRLog::error( "Cannot read archive contents from %s", LCSTR(arcPathName) );
343             return false;
344         }
345         stream = container->OpenStream(arcItemPathName.c_str(), LVOM_READ);
346         if ( stream.isNull() ) {
347             CRLog::error( "Cannot open archive file item stream %s", LCSTR(lString32(name)) );
348             return false;
349         }
350     }
351     struct stat fs;
352     if ( !stat( name, &fs ) ) {
353         t = fs.st_mtime;
354     }
355 
356     // read document
357 #if COMPACT_DOM==1
358     ldomDocument doc(stream, 0);
359 #else
360     ldomDocument doc;
361 #endif
362     ldomDocumentWriter writer(&doc, true);
363     doc.setNodeTypes( fb2_elem_table );
364     doc.setAttributeTypes( fb2_attr_table );
365     doc.setNameSpaceTypes( fb2_ns_table );
366     LVXMLParser parser( stream, &writer );
367     CRLog::trace( "checking format..." );
368     if ( !parser.CheckFormat() ) {
369         return false;
370     }
371     CRLog::trace( "parsing..." );
372     if ( !parser.Parse() ) {
373         return false;
374     }
375     CRLog::trace( "parsed" );
376     #if 0
377         char ofname[512];
378         sprintf(ofname, "%s.xml", name);
379         CRLog::trace("    writing to file %s", ofname);
380         LVStreamRef out = LVOpenFileStream(ofname, LVOM_WRITE);
381         doc.saveToStream(out, "utf16");
382     #endif
383     lString32 authors = extractDocAuthors( &doc, lString32("|"), false );
384     lString32 title = extractDocTitle( &doc );
385     lString32 language = extractDocLanguage( &doc );
386     lString32 series = extractDocSeries( &doc, &pBookProps->seriesNumber );
387     lString32 keywords = extractDocKeywords( &doc );
388     lString32 description = extractDocDescription( &doc );
389 #if SERIES_IN_AUTHORS==1
390     if ( !series.empty() )
391         authors << "    " << series;
392 #endif
393     pBookProps->format = doc_format_fb2;
394     pBookProps->title = title;
395     pBookProps->author = authors;
396     pBookProps->series = series;
397     pBookProps->filesize = (long)stream->GetSize();
398     pBookProps->filename = lString32(name);
399     pBookProps->filedate = getDateTimeString( t );
400     pBookProps->language = language;
401     pBookProps->keywords = keywords;
402     pBookProps->description = description;
403     pBookProps->crc32 = stream->getcrc32();
404     return true;
405 }
406 
407 
408 /*
409  * Class:     org_coolreader_crengine_Engine
410  * Method:    scanBookPropertiesInternal
411  * Signature: (Lorg/coolreader/crengine/FileInfo;)Z
412  */
Java_org_coolreader_crengine_Engine_scanBookPropertiesInternal(JNIEnv * _env,jclass _engine,jobject _fileInfo)413 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_scanBookPropertiesInternal
414   (JNIEnv * _env, jclass _engine, jobject _fileInfo)
415 {
416 	CRJNIEnv env(_env);
417 	jclass objclass = env->GetObjectClass(_fileInfo);
418 	jfieldID fid = env->GetFieldID(objclass, "pathname", "Ljava/lang/String;");
419 	lString32 filename = env.fromJavaString( (jstring)env->GetObjectField(_fileInfo, fid) );
420     fid = env->GetFieldID(objclass, "arcname", "Ljava/lang/String;");
421     lString32 arcname = env.fromJavaString( (jstring)env->GetObjectField(_fileInfo, fid) );
422 	if ( filename.empty() )
423 		return JNI_FALSE;
424 	if ( !arcname.empty() )
425        filename = arcname + "@/" + filename;
426 
427 	BookProperties props;
428 	CRLog::debug("Looking for properties of file %s", LCSTR(filename));
429 	bool res = GetBookProperties(LCSTR(filename),  &props);
430 	if ( !res )
431 		return JNI_FALSE;
432 	#define SET_STR_FLD(fldname,src) \
433 	{ \
434 	    jfieldID fid = env->GetFieldID(objclass, fldname, "Ljava/lang/String;"); \
435 	    env->SetObjectField(_fileInfo,fid,env.toJavaString(src)); \
436 	}
437 	#define SET_INT_FLD(fldname,src) \
438 	{ \
439 	    jfieldID fid = env->GetFieldID(objclass, fldname, "I"); \
440 	    env->SetIntField(_fileInfo,fid,src); \
441 	}
442 	#define SET_LONG_FLD(fldname,src) \
443 	{ \
444 	    jfieldID fid = env->GetFieldID(objclass, fldname, "J"); \
445 	    env->SetLongField(_fileInfo,fid,src); \
446 	}
447 	SET_STR_FLD("title",props.title);
448 	SET_STR_FLD("authors",props.author);
449 	SET_STR_FLD("series",props.series);
450 	SET_INT_FLD("seriesNumber",props.seriesNumber);
451 	SET_STR_FLD("language",props.language);
452 	SET_LONG_FLD("crc32",props.crc32);
453 	if (doc_format_fb2 == props.format) {
454 		// TODO: may be fb3 too...
455 		// keywords separated by "\n", see lvtinydom.cpp:
456 		//    lString32 extractDocKeywords( ldomDocument * doc )
457 		int pos = props.keywords.pos('\n');
458 		while (pos > 0) {
459 			props.keywords[pos] = '|';
460 			pos = props.keywords.pos('\n', pos + 1);
461 		}
462 		SET_STR_FLD("genres", props.keywords);
463 	}
464 	SET_STR_FLD("description",props.description);
465 
466 	return JNI_TRUE;
467 }
468 
469 /*
470  * Class:     org_coolreader_crengine_Engine
471  * Method:    updateFileCRC32Internal
472  * Signature: (Lorg/coolreader/crengine/FileInfo;)Z
473  */
Java_org_coolreader_crengine_Engine_updateFileCRC32Internal(JNIEnv * _env,jclass _engine,jobject _fileInfo)474 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_updateFileCRC32Internal
475 		(JNIEnv * _env, jclass _engine, jobject _fileInfo)
476 {
477 	CRJNIEnv env(_env);
478 	jclass objclass = env->GetObjectClass(_fileInfo);
479 	jfieldID fid = env->GetFieldID(objclass, "pathname", "Ljava/lang/String;");
480 	lString32 filename = env.fromJavaString( (jstring)env->GetObjectField(_fileInfo, fid) );
481 	fid = env->GetFieldID(objclass, "arcname", "Ljava/lang/String;");
482 	lString32 arcname = env.fromJavaString( (jstring)env->GetObjectField(_fileInfo, fid) );
483 	if ( filename.empty() )
484 		return JNI_FALSE;
485 	bool isArchiveFile = !arcname.empty();
486 	// open stream
487 	LVStreamRef stream = LVOpenFileStream( (isArchiveFile ? arcname : filename).c_str() , LVOM_READ );
488 	if (!stream.isNull()) {
489 		if (isArchiveFile) {
490 			LVContainerRef container = LVOpenArchieve(stream);
491 			if (!container.isNull()) {
492 				stream = container->OpenStream(filename.c_str(), LVOM_READ);
493 				if (stream.isNull()) {
494 					CRLog::error("Cannot open archive file item stream %s", LCSTR(filename));
495 				}
496 			} else {
497 				CRLog::error("Cannot read archive contents from %s", LCSTR(arcname));
498 				stream = LVStreamRef();
499 			}
500 		}
501 	}
502 	if (!stream.isNull()) {
503 		fid = env->GetFieldID(objclass, "crc32", "J");
504 	    env->SetLongField(_fileInfo, fid, stream->getcrc32());
505 	} else {
506 		CRLog::error("cannot open file %s", LCSTR(isArchiveFile ? arcname : filename));
507 		return JNI_FALSE;
508 	}
509 	return JNI_TRUE;
510 }
511 
512 
drawBookCoverInternal(JNIEnv * _env,jclass _engine,jobject bitmap,jbyteArray _data,jstring _fontFace,jstring _title,jstring _authors,jstring _seriesName,jint seriesNumber,jint bpp)513 void drawBookCoverInternal(JNIEnv * _env, jclass _engine, jobject bitmap, jbyteArray _data, jstring _fontFace, jstring _title, jstring _authors, jstring _seriesName, jint seriesNumber, jint bpp)
514 {
515 	CRJNIEnv env(_env);
516 	CRLog::debug("drawBookCoverInternal called");
517 	lString8 fontFace = UnicodeToUtf8(env.fromJavaString(_fontFace));
518 	lString32 title = env.fromJavaString(_title);
519 	lString32 authors = env.fromJavaString(_authors);
520 	lString32 seriesName = env.fromJavaString(_seriesName);
521 	LVStreamRef stream;
522 	LVDrawBuf * drawbuf = BitmapAccessorInterface::getInstance()->lock(_env, bitmap);
523 	if (drawbuf != NULL) {
524 		LVImageSourceRef image;
525 		if (_data != NULL && _env->GetArrayLength(_data) > 0) {
526 			CRLog::debug("drawBookCoverInternal : cover image from array");
527 			stream = env.jbyteArrayToStream(_data);
528 			if (!stream.isNull())
529 				image = LVCreateStreamImageSource(stream);
530 		}
531 
532 		int factor = 1;
533 		int dx = drawbuf->GetWidth();
534 		int dy = drawbuf->GetHeight();
535 		int MIN_WIDTH = 300;
536 		int MIN_HEIGHT = 400;
537 		if (dx < MIN_WIDTH || dy < MIN_HEIGHT) {
538 			if (dx * 2 < MIN_WIDTH || dy * 2 < MIN_HEIGHT) {
539 				dx *= 3;
540 				dy *= 3;
541 				factor = 3;
542 			} else {
543 				dx *= 2;
544 				dy *= 2;
545 				factor = 2;
546 			}
547 		}
548 		LVDrawBuf * drawbuf2 = drawbuf;
549 		if (factor > 1)
550 			drawbuf2 = new LVColorDrawBuf(dx, dy, drawbuf->GetBitsPerPixel());
551 
552 		if (bpp >= 16) {
553 			// native color resolution
554 			CRLog::debug("drawBookCoverInternal : calling LVDrawBookCover");
555 			LVDrawBookCover(*drawbuf2, image, fontFace, title, authors, seriesName, seriesNumber);
556 			image.Clear();
557 		} else {
558 			LVGrayDrawBuf grayBuf(drawbuf2->GetWidth(), drawbuf2->GetHeight(), bpp);
559 			LVDrawBookCover(grayBuf, image, fontFace, title, authors, seriesName, seriesNumber);
560 			image.Clear();
561 			grayBuf.DrawTo(drawbuf2, 0, 0, 0, NULL);
562 		}
563 
564 		if (factor > 1) {
565 			CRLog::debug("drawBookCoverInternal : rescaling");
566 			drawbuf->DrawRescaled(drawbuf2, 0, 0, drawbuf->GetWidth(), drawbuf->GetHeight(), 0);
567 			delete drawbuf2;
568 		}
569 
570 		//CRLog::trace("getPageImageInternal calling bitmap->unlock");
571 		BitmapAccessorInterface::getInstance()->unlock(_env, bitmap, drawbuf);
572 	} else {
573 		CRLog::error("bitmap accessor is invalid");
574 	}
575 	CRLog::debug("drawBookCoverInternal finished");
576 }
577 
578 /*
579  * Class:     org_coolreader_crengine_Engine
580  * Method:    drawBookCoverInternal
581  * Signature: (Landroid/graphics/Bitmap;[BLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V
582  */
Java_org_coolreader_crengine_Engine_drawBookCoverInternal(JNIEnv * _env,jclass _engine,jobject bitmap,jbyteArray _data,jstring _fontFace,jstring _title,jstring _authors,jstring _seriesName,jint seriesNumber,jint bpp)583 JNIEXPORT void JNICALL Java_org_coolreader_crengine_Engine_drawBookCoverInternal
584   (JNIEnv * _env, jclass _engine, jobject bitmap, jbyteArray _data, jstring _fontFace, jstring _title, jstring _authors, jstring _seriesName, jint seriesNumber, jint bpp)
585 {
586 	COFFEE_TRY_JNI(_env, drawBookCoverInternal(_env, _engine, bitmap, _data, _fontFace, _title, _authors, _seriesName, seriesNumber, bpp));
587 }
588 
scanBookCoverInternal(JNIEnv * _env,jclass _class,jstring _path)589 jbyteArray scanBookCoverInternal
590   (JNIEnv * _env, jclass _class, jstring _path)
591 {
592 	CRJNIEnv env(_env);
593 	lString32 path = env.fromJavaString(_path);
594 	CRLog::debug("scanBookCoverInternal(%s) called", LCSTR(path));
595 	lString32 arcname, item;
596     LVStreamRef res;
597     jbyteArray array = NULL;
598     LVContainerRef arc;
599 	if (!LVSplitArcName(path, arcname, item)) {
600 		// not in archive
601 		LVStreamRef stream = LVOpenFileStream(path.c_str(), LVOM_READ);
602 		if (!stream.isNull()) {
603 			arc = LVOpenArchieve(stream);
604 			if (!arc.isNull()) {
605 				// ZIP-based format
606 				if (DetectEpubFormat(stream)) {
607 					// EPUB
608 					// extract coverpage from epub
609 					res = GetEpubCoverpage(arc);
610 				}
611 			} else {
612 				res = GetFB2Coverpage(stream);
613 				if (res.isNull()) {
614 					doc_format_t fmt;
615 					if (DetectPDBFormat(stream, fmt)) {
616 						res = GetPDBCoverpage(stream);
617 					}
618 				}
619 			}
620 		}
621 	} else {
622     	CRLog::debug("scanBookCoverInternal() : is archive, item=%s, arc=%s", LCSTR(item), LCSTR(arcname));
623 		LVStreamRef arcstream = LVOpenFileStream(arcname.c_str(), LVOM_READ);
624 		if (!arcstream.isNull()) {
625 			arc = LVOpenArchieve(arcstream);
626 			if (!arc.isNull()) {
627 				LVStreamRef stream = arc->OpenStream(item.c_str(), LVOM_READ);
628 				if (!stream.isNull()) {
629 			    	CRLog::debug("scanBookCoverInternal() : archive stream opened ok, parsing");
630 					res = GetFB2Coverpage(stream);
631 					if (res.isNull()) {
632 						doc_format_t fmt;
633 						if (DetectPDBFormat(stream, fmt)) {
634 							res = GetPDBCoverpage(stream);
635 						}
636 					}
637 				}
638 			}
639 		}
640 	}
641 	if (!res.isNull())
642 		array = env.streamToJByteArray(res);
643     if (array != NULL)
644     	CRLog::debug("scanBookCoverInternal() : returned cover page array");
645     else
646     	CRLog::debug("scanBookCoverInternal() : cover page data not found");
647     return array;
648 }
649 
650 /*
651  * Class:     org_coolreader_crengine_Engine
652  * Method:    scanBookCoverInternal
653  * Signature: (Ljava/lang/String;)[B
654  */
Java_org_coolreader_crengine_Engine_scanBookCoverInternal(JNIEnv * _env,jclass _class,jstring _path)655 JNIEXPORT jbyteArray JNICALL Java_org_coolreader_crengine_Engine_scanBookCoverInternal
656   (JNIEnv * _env, jclass _class, jstring _path)
657 {
658 	jbyteArray res = NULL;
659 	COFFEE_TRY_JNI(_env, res = scanBookCoverInternal( _env, _class, _path));
660 	return res;
661 }
662 
663 /*
664  * Class:     org_coolreader_crengine_Engine
665  * Method:    getArchiveItemsInternal
666  * Signature: (Ljava/lang/String;)[Ljava/lang/String;
667  */
Java_org_coolreader_crengine_Engine_getArchiveItemsInternal(JNIEnv * _env,jclass,jstring jarcName)668 JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_getArchiveItemsInternal
669   (JNIEnv * _env, jclass, jstring jarcName)
670 {
671     CRJNIEnv env(_env);
672     lString32 arcName = env.fromJavaString(jarcName);
673     lString32Collection list;
674 
675     //fontMan->getFaceList(list);
676     LVStreamRef stream = LVOpenFileStream( arcName.c_str(), LVOM_READ );
677     if ( !stream.isNull() ) {
678         LVContainerRef arc = LVOpenArchieve(stream);
679         if ( !arc.isNull() ) {
680             // convert
681             for ( int i=0; i<arc->GetObjectCount(); i++ ) {
682                 const LVContainerItemInfo * item = arc->GetObjectInfo(i);
683                 if ( item->IsContainer())
684                     continue;
685                 list.add( item->GetName() );
686                 list.add( lString32::itoa(item->GetSize()) );
687             }
688         }
689     }
690     return env.toJavaStringArray(list);
691 }
692 
693 
694 class JNICDRLogger : public CRLog
695 {
696 public:
JNICDRLogger()697     JNICDRLogger()
698     {
699     	curr_level = CRLog::LL_DEBUG;
700     }
701 protected:
702 
log(const char * lvl,const char * msg,va_list args)703 	virtual void log( const char * lvl, const char * msg, va_list args)
704 	{
705 	    #define MAX_LOG_MSG_SIZE 1024
706 		static char buffer[MAX_LOG_MSG_SIZE+1];
707 		vsnprintf(buffer, MAX_LOG_MSG_SIZE, msg, args);
708 		int level = ANDROID_LOG_DEBUG;
709 		//LOGD("CRLog::log is called with LEVEL %s, pattern %s", lvl, msg);
710 		if ( !strcmp(lvl, "FATAL") )
711 			level = ANDROID_LOG_FATAL;
712 		else if ( !strcmp(lvl, "ERROR") )
713 			level = ANDROID_LOG_ERROR;
714 		else if ( !strcmp(lvl, "WARN") )
715 			level = ANDROID_LOG_WARN;
716 		else if ( !strcmp(lvl, "INFO") )
717 			level = ANDROID_LOG_INFO;
718 		else if ( !strcmp(lvl, "DEBUG") )
719 			level = ANDROID_LOG_DEBUG;
720 		else if ( !strcmp(lvl, "TRACE") )
721 			level = ANDROID_LOG_VERBOSE;
722 		__android_log_write(level, LOG_TAG, buffer);
723 	}
724 };
725 
726 //typedef void (lv_FatalErrorHandler_t)(int errorCode, const char * errorText );
727 
cr3androidFatalErrorHandler(int errorCode,const char * errorText)728 void cr3androidFatalErrorHandler(int errorCode, const char * errorText )
729 {
730 	LOGE("CoolReader Fatal Error #%d: %s", errorCode, errorText);
731 	LOGASSERTFAILED("CoolReader Fatal Error", "CoolReader Fatal Error #%d: %s", errorCode, errorText);
732 	//static char str[1001];
733 	//snprintf(str, 1000, "CoolReader Fatal Error #%d: %s", errorCode, errorText);
734 	//LOGE("CoolReader Fatal Error #%d: %s", errorCode, errorText);
735 	//LOGASSERTFAILED(errorText, "CoolReader Fatal Error #%d: %s", errorCode, errorText);
736 }
737 
738 /// set fatal error handler
739 void crSetFatalErrorHandler( lv_FatalErrorHandler_t * handler );
740 
initInternal(JNIEnv * penv,jclass obj,jobjectArray fontArray,jint sdk_int)741 jboolean initInternal(JNIEnv * penv, jclass obj, jobjectArray fontArray, jint sdk_int) {
742 
743 	CRJNIEnv::sdk_int = sdk_int;
744 
745 	CRJNIEnv env(penv);
746 
747 	// to catch crashes and remove current cache file on crash (SIGSEGV etc.)
748 	crSetSignalHandler();
749 
750 	LOGI("initInternal called");
751 	// set fatal error handler
752 	crSetFatalErrorHandler( &cr3androidFatalErrorHandler );
753 	LOGD("Redirecting CDRLog to Android");
754 	CRLog::setLogger( new JNICDRLogger() );
755 	CRLog::setLogLevel( CRLog::LL_TRACE );
756 	CRLog::info("CREngine log redirected");
757 	CRLog::info("CRENGINE version %s %s", CR_ENGINE_VERSION, CR_ENGINE_BUILD_DATE);
758 
759 	CRLog::info("initializing hyphenation manager");
760     HyphMan::initDictionaries(lString32::empty_str); //don't look for dictionaries
761 	HyphMan::activateDictionary(lString32(HYPH_DICT_ID_NONE));
762 	CRLog::info("creating font manager");
763     InitFontManager(lString8::empty_str);
764 	CRLog::debug("converting fonts array: %d items", (int)env->GetArrayLength(fontArray));
765 	lString32Collection fonts;
766 	env.fromJavaStringArray(fontArray, fonts);
767 	int len = fonts.length();
768 	CRLog::debug("registering fonts: %d fonts in list", len);
769 	for ( int i=0; i<len; i++ ) {
770 		lString8 fontName = UnicodeToUtf8(fonts[i]);
771 		CRLog::debug("registering font %s", fontName.c_str());
772 		if ( !fontMan->RegisterFont( fontName ) )
773 			CRLog::error("cannot load font %s", fontName.c_str());
774 	}
775     CRLog::info("%d fonts registered", fontMan->GetFontCount());
776 	return fontMan->GetFontCount() ? JNI_TRUE : JNI_FALSE;
777 }
778 
779 /*
780  * Class:     org_coolreader_crengine_Engine
781  * Method:    initInternal
782  * Signature: ([Ljava/lang/String;I)Z
783  */
Java_org_coolreader_crengine_Engine_initInternal(JNIEnv * penv,jclass obj,jobjectArray fontArray,jint sdk_int)784 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_initInternal
785   (JNIEnv * penv, jclass obj, jobjectArray fontArray, jint sdk_int)
786 {
787 	jboolean res = JNI_FALSE;
788 	COFFEE_TRY_JNI(penv, res = initInternal(penv, obj, fontArray, sdk_int));
789 	return res;
790 }
791 
792 class HyphDataLoaderProxy : public HyphDataLoader {
793 	JavaVM *mJavaVM;
794 public:
HyphDataLoaderProxy(JavaVM * jvm)795 	HyphDataLoaderProxy(JavaVM *jvm) :
796 			HyphDataLoader(), mJavaVM(jvm) {
797 	}
798 
~HyphDataLoaderProxy()799 	virtual ~HyphDataLoaderProxy() {}
800 
loadData(lString32 id)801 	virtual LVStreamRef loadData(lString32 id) {
802 		JNIEnv *penv = NULL;
803 		bool attached = false;
804 		mJavaVM->GetEnv((void **) &penv, JNI_VERSION_1_6);
805 		if (NULL == penv) {
806 			// caller thread is not attached yet
807 			mJavaVM->AttachCurrentThread(&penv, NULL);
808 			attached = true;
809 		}
810 		LVStreamRef stream = LVStreamRef();
811 		jclass pjcEngine = penv->FindClass("org/coolreader/crengine/Engine");
812 		if (NULL == pjcEngine)
813 			return stream;
814 		jmethodID pjmEngine_loadHyphDictData = penv->GetStaticMethodID(pjcEngine, "loadHyphDictData", "(Ljava/lang/String;)[B");
815 		if (NULL == pjmEngine_loadHyphDictData)
816 			return stream;
817 		CRJNIEnv env(penv);
818 		jstring jid = env.toJavaString(id);
819 		jbyteArray data = static_cast<jbyteArray>(penv->CallStaticObjectMethod(pjcEngine, pjmEngine_loadHyphDictData, jid));
820 		stream = env.jbyteArrayToStream(data);
821 		if (attached)
822 			mJavaVM->DetachCurrentThread();
823 		return stream;
824 	}
825 };
826 
initDictionaries(JNIEnv * penv,jclass clazz,jobjectArray dictArray)827 jboolean initDictionaries(JNIEnv *penv, jclass clazz, jobjectArray dictArray) {
828 	jclass pjcHyphDict = penv->FindClass("org/coolreader/crengine/Engine$HyphDict");
829 	if (NULL == pjcHyphDict)
830 		return JNI_FALSE;
831 	jfieldID pjfHyphDict_type = penv->GetFieldID(pjcHyphDict, "type", "I");
832 	if (NULL == pjfHyphDict_type)
833 		return JNI_FALSE;
834     jfieldID pjfHyphDict_code = penv->GetFieldID(pjcHyphDict, "code", "Ljava/lang/String;");
835     if (NULL == pjfHyphDict_code)
836         return JNI_FALSE;
837 
838 	int len = penv->GetArrayLength(dictArray);
839 	HyphDictionary *dict;
840 	CRJNIEnv env(penv);
841 	HyphDictType dict_type;
842 	for (int i = 0; i < len; i++) {
843 		jobject obj = penv->GetObjectArrayElement(dictArray, i);
844 		int type = penv->GetIntField(obj, pjfHyphDict_type);
845 		jstring code = static_cast<jstring>(penv->GetObjectField(obj, pjfHyphDict_code));
846 		switch (type) {     // convert org/coolreader/crengine/Engine$HyphDict$type into HyphDictType
847 			case 0:         // org/coolreader/crengine/Engine$HYPH_NONE
848 				dict_type = HDT_NONE;
849 				break;
850 			case 1:         // org/coolreader/crengine/Engine$HYPH_ALGO
851 				dict_type = HDT_ALGORITHM;
852 				break;
853 			case 2:         // org/coolreader/crengine/Engine$HYPH_DICT
854 				dict_type = HDT_DICT_TEX;
855 				break;
856 			default:
857 				dict_type = HDT_NONE;
858 				break;
859 		}
860 		lString32 dict_code = env.fromJavaString(code);
861 		dict = new HyphDictionary(dict_type, dict_code, dict_code, dict_code);
862 		if (!HyphMan::addDictionaryItem(dict))
863 		    delete dict;
864 	}
865 	JavaVM *jvm;
866 	env->GetJavaVM(&jvm);
867 	HyphMan::setDataLoader(new HyphDataLoaderProxy( jvm ));
868 	return JNI_TRUE;
869 }
870 
871 JNIEXPORT jboolean JNICALL
Java_org_coolreader_crengine_Engine_initDictionaries(JNIEnv * penv,jclass clazz,jobjectArray dictArray)872 Java_org_coolreader_crengine_Engine_initDictionaries
873  (JNIEnv * penv, jclass clazz, jobjectArray dictArray)
874 {
875     jboolean res = JNI_FALSE;
876     COFFEE_TRY_JNI(penv, res = initDictionaries(penv, clazz, dictArray));
877     return res;
878 }
879 
880 /*
881  * Class:     org_coolreader_crengine_Engine
882  * Method:    uninitInternal
883  * Signature: ()V
884  */
Java_org_coolreader_crengine_Engine_uninitInternal(JNIEnv *,jclass)885 JNIEXPORT void JNICALL Java_org_coolreader_crengine_Engine_uninitInternal
886   (JNIEnv *, jclass)
887 {
888 	LOGI("uninitInternal called");
889 	HyphMan::uninit();
890 	ShutdownFontManager();
891 	CRLog::setLogger(NULL);
892 }
893 
894 /*
895  * Class:     org_coolreader_crengine_Engine
896  * Method:    getFontFaceListInternal
897  * Signature: ()[Ljava/lang/String;
898  */
Java_org_coolreader_crengine_Engine_getFontFaceListInternal(JNIEnv * penv,jclass obj)899 JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_getFontFaceListInternal
900   (JNIEnv * penv, jclass obj)
901 {
902 	LOGI("getFontFaceListInternal called");
903 	CRJNIEnv env(penv);
904 	lString32Collection list;
905 	COFFEE_TRY_JNI(penv, fontMan->getFaceList(list));
906 	return env.toJavaStringArray(list);
907 }
908 
909 /*
910  * Class:     org_coolreader_crengine_Engine
911  * Method:    getFontFileNameListInternal
912  * Signature: ()[Ljava/lang/String;
913  */
Java_org_coolreader_crengine_Engine_getFontFileNameListInternal(JNIEnv * penv,jclass cls)914 JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_getFontFileNameListInternal
915         (JNIEnv * penv, jclass cls)
916 {
917     LOGI("getFontFileListInternal called");
918     CRJNIEnv env(penv);
919     lString32Collection list;
920     COFFEE_TRY_JNI(penv, fontMan->getFontFileNameList(list));
921     return env.toJavaStringArray(list);
922 }
923 
924 /*
925  * Class:     org_coolreader_crengine_Engine
926  * Method:    setCacheDirectoryInternal
927  * Signature: (Ljava/lang/String;I)Z
928  */
Java_org_coolreader_crengine_Engine_setCacheDirectoryInternal(JNIEnv * penv,jclass obj,jstring dir,jint size)929 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_setCacheDirectoryInternal
930   (JNIEnv * penv, jclass obj, jstring dir, jint size)
931 {
932 	CRJNIEnv env(penv);
933 	bool res = false;
934 	COFFEE_TRY_JNI(penv, res = ldomDocCache::init(env.fromJavaString(dir), size ));
935 	return res ? JNI_TRUE : JNI_FALSE;
936 }
937 
938 /*
939  * Class:     org_coolreader_crengine_Engine
940  * Method:    haveFcLangCodeInternal
941  * Signature: (Ljava/lang/String;)Z
942  */
Java_org_coolreader_crengine_Engine_haveFcLangCodeInternal(JNIEnv * env,jclass cls,jstring langCode)943 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_haveFcLangCodeInternal
944 		(JNIEnv *env, jclass cls, jstring langCode)
945 {
946 	jboolean res = JNI_FALSE;
947 	const char* langCode_ptr = env->GetStringUTFChars(langCode, 0);
948 	if (langCode_ptr) {
949 		struct fc_lang_catalog* lang_ptr = fc_lang_cat;
950 		for (int i = 0; i < fc_lang_cat_sz; i++)
951 		{
952 			if (strcmp(lang_ptr->lang_code, langCode_ptr) == 0)
953 			{
954 				res = JNI_TRUE;
955 				break;
956 			}
957 			lang_ptr++;
958 		}
959 		env->ReleaseStringUTFChars(langCode, langCode_ptr);
960 	}
961 	return res;
962 }
963 
964 
965 /*
966  * Class:     org_coolreader_crengine_Engine
967  * Method:    checkFontLanguageCompatibilityInternal
968  * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
969  */
Java_org_coolreader_crengine_Engine_checkFontLanguageCompatibilityInternal(JNIEnv * env,jclass cls,jstring fontFace,jstring langCode)970 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_checkFontLanguageCompatibilityInternal
971 		(JNIEnv *env, jclass cls, jstring fontFace, jstring langCode)
972 {
973 	jboolean res = JNI_TRUE;
974 	const char* fontFace_ptr = env->GetStringUTFChars(fontFace, 0);
975 	const char* langCode_ptr = env->GetStringUTFChars(langCode, 0);
976 	if (fontFace_ptr && langCode_ptr) {
977 		res = fontMan->checkFontLangCompat(lString8(fontFace_ptr), lString8(langCode_ptr)) ? JNI_TRUE : JNI_FALSE;
978 	}
979 	if (langCode_ptr)
980 		env->ReleaseStringUTFChars(langCode, langCode_ptr);
981 	if (fontFace_ptr)
982 		env->ReleaseStringUTFChars(fontFace, fontFace_ptr);
983 	return res;
984 }
985 
986 /*
987  * Class:     org_coolreader_crengine_Engine
988  * Method:    listFilesInternal
989  * Signature: (Ljava/io/File;)[Ljava/io/File;
990  */
Java_org_coolreader_crengine_Engine_listFilesInternal(JNIEnv * penv,jclass,jobject jdir)991 JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_listFilesInternal
992 		(JNIEnv *penv, jclass, jobject jdir)
993 {
994 	CRJNIEnv env(penv);
995 	if (NULL == jdir)
996 		return NULL;
997 	jclass pjcFile = env->GetObjectClass(jdir);
998 	if (NULL == pjcFile)
999 		return NULL;
1000 	jmethodID pjmFile_GetAbsolutePath = env->GetMethodID(pjcFile, "getAbsolutePath", "()Ljava/lang/String;");
1001 	if (NULL == pjmFile_GetAbsolutePath)
1002 		return NULL;
1003 	jmethodID pjmFile_Ctor = env->GetMethodID(pjcFile, "<init>", "(Ljava/lang/String;)V");
1004 	if (NULL == pjmFile_Ctor)
1005 		return NULL;
1006 	jstring jpathname = (jstring)env->CallObjectMethod(jdir, pjmFile_GetAbsolutePath);
1007 	if (NULL == jpathname)
1008 		return NULL;
1009 	lString32 path = env.fromJavaString(jpathname);
1010 	jobjectArray jarray = NULL;
1011 	LVContainerRef dir = LVOpenDirectory(path);
1012 	if ( !dir.isNull() ) {
1013 		jstring emptyString = env->NewStringUTF("");
1014 		jobject emptyFile = env->NewObject(pjcFile, pjmFile_Ctor, emptyString);
1015 		jarray = env->NewObjectArray(dir->GetObjectCount(), pjcFile, emptyFile);
1016 		if (NULL != jarray) {
1017 			for (int i = 0; i < dir->GetObjectCount(); i++) {
1018 				const LVContainerItemInfo *item = dir->GetObjectInfo(i);
1019 				if (item && item->GetName()) {
1020 					lString32 fileName = path + "/" + item->GetName();
1021 					jstring jfilename = env.toJavaString(fileName);
1022 					if (NULL != jfilename) {
1023 						env->ExceptionClear();
1024 						jobject jfile = env->NewObject(pjcFile, pjmFile_Ctor, jfilename);
1025 						if (env->ExceptionCheck() == JNI_TRUE)
1026 							env->ExceptionClear();
1027 						else {
1028 							if (NULL != jfile)
1029 								env->SetObjectArrayElement(jarray, i, jfile);
1030 						}
1031 						env->DeleteLocalRef(jfile);
1032 						env->DeleteLocalRef(jfilename);
1033 					}
1034 				}
1035 			}
1036 		}
1037 	}
1038 	return jarray;
1039 }
1040 
1041 /*
1042  * Class:     org_coolreader_crengine_Engine
1043  * Method:    isLink
1044  * Signature: (Ljava/lang/String;)Ljava/lang/String;
1045  */
Java_org_coolreader_crengine_Engine_isLink(JNIEnv * env,jclass obj,jstring pathname)1046 JNIEXPORT jstring JNICALL Java_org_coolreader_crengine_Engine_isLink
1047   (JNIEnv * env, jclass obj, jstring pathname)
1048 {
1049 	//CRLog::trace("isLink : enter");
1050 	if (!pathname)
1051 		return NULL;
1052 	//CRLog::trace("isLink : pathname is not null");
1053 	int res = JNI_FALSE;
1054 	jboolean iscopy;
1055 	const char * s = env->GetStringUTFChars(pathname, &iscopy);
1056 	//CRLog::trace("isLink : read utf from pathname");
1057 	struct stat st;
1058 	lString8 path;
1059 	if ( !lstat( s, &st) ) {
1060 		if ( S_ISLNK(st.st_mode) ) {
1061 			char buf[2048];
1062 			int len = readlink(s, buf, sizeof(buf) - 1);
1063 			if (len != -1) {
1064 				buf[len] = 0;
1065 				path = lString8(buf);
1066 			}
1067 		}
1068 	}
1069 	//CRLog::trace("isLink : releasing utf pathname");
1070 	env->ReleaseStringUTFChars(pathname, s);
1071 	//CRLog::trace("isLink : returning");
1072 	return !path.empty() ? (jstring)env->NewGlobalRef(env->NewStringUTF(path.c_str())) : NULL;
1073 }
1074 
1075 
1076 /*
1077  * Class:     org_coolreader_crengine_Engine
1078  * Method:    suspendLongOperationInternal
1079  * Signature: ()V
1080  */
Java_org_coolreader_crengine_Engine_suspendLongOperationInternal(JNIEnv *,jclass)1081 JNIEXPORT void JNICALL Java_org_coolreader_crengine_Engine_suspendLongOperationInternal
1082   (JNIEnv *, jclass)
1083 {
1084 	_timeoutControl.cancel();
1085 }
1086 
1087 
1088 #define BUTTON_BACKLIGHT_CONTROL_PATH "/sys/class/leds/button-backlight/brightness"
1089 /*
1090  * Class:     org_coolreader_crengine_Engine
1091  * Method:    setKeyBacklightInternal
1092  * Signature: (I)Z
1093  */
Java_org_coolreader_crengine_Engine_setKeyBacklightInternal(JNIEnv *,jclass,jint n)1094 JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_setKeyBacklightInternal
1095   (JNIEnv *, jclass, jint n)
1096 {
1097 	FILE * f = fopen(BUTTON_BACKLIGHT_CONTROL_PATH, "wb");
1098 	if (!f)
1099 		return JNI_FALSE;
1100 	fwrite(n ? "1" : "0", 1, 1, f);
1101 	fclose(f);
1102 	return JNI_TRUE;
1103 }
1104 
1105 /*
1106  * Class:     org_coolreader_crengine_Engine
1107  * Method:    getDomVersionCurrent
1108  * Signature: ()I
1109  */
Java_org_coolreader_crengine_Engine_getDomVersionCurrent(JNIEnv *,jclass)1110 JNIEXPORT jint JNICALL Java_org_coolreader_crengine_Engine_getDomVersionCurrent
1111   (JNIEnv *, jclass)
1112 {
1113 	return gDOMVersionCurrent;
1114 }
1115 
1116 //=====================================================================
1117 
1118 static JNINativeMethod sEngineMethods[] = {
1119   /* name, signature, funcPtr */
1120   {"initInternal", "([Ljava/lang/String;I)Z", (void*)Java_org_coolreader_crengine_Engine_initInternal},
1121   {"uninitInternal", "()V", (void*)Java_org_coolreader_crengine_Engine_uninitInternal},
1122   {"initDictionaries", "([Lorg/coolreader/crengine/Engine$HyphDict;)Z", (void*)Java_org_coolreader_crengine_Engine_initDictionaries},
1123   {"getFontFaceListInternal", "()[Ljava/lang/String;", (void*)Java_org_coolreader_crengine_Engine_getFontFaceListInternal},
1124   {"setCacheDirectoryInternal", "(Ljava/lang/String;I)Z", (void*)Java_org_coolreader_crengine_Engine_setCacheDirectoryInternal},
1125   {"scanBookPropertiesInternal", "(Lorg/coolreader/crengine/FileInfo;)Z", (void*)Java_org_coolreader_crengine_Engine_scanBookPropertiesInternal},
1126   {"updateFileCRC32Internal", "(Lorg/coolreader/crengine/FileInfo;)Z", (void*)Java_org_coolreader_crengine_Engine_updateFileCRC32Internal},
1127   {"getArchiveItemsInternal", "(Ljava/lang/String;)[Ljava/lang/String;", (void*)Java_org_coolreader_crengine_Engine_getArchiveItemsInternal},
1128   {"isLink", "(Ljava/lang/String;)Ljava/lang/String;", (void*)Java_org_coolreader_crengine_Engine_isLink},
1129   {"suspendLongOperationInternal", "()V", (void*)Java_org_coolreader_crengine_Engine_suspendLongOperationInternal},
1130   {"setKeyBacklightInternal", "(I)Z", (void*)Java_org_coolreader_crengine_Engine_setKeyBacklightInternal},
1131   {"scanBookCoverInternal", "(Ljava/lang/String;)[B", (void*)Java_org_coolreader_crengine_Engine_scanBookCoverInternal},
1132   {"drawBookCoverInternal", "(Landroid/graphics/Bitmap;[BLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V", (void*)Java_org_coolreader_crengine_Engine_drawBookCoverInternal},
1133   {"haveFcLangCodeInternal", "(Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_haveFcLangCodeInternal},
1134   {"checkFontLanguageCompatibilityInternal", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_checkFontLanguageCompatibilityInternal},
1135   {"listFilesInternal", "(Ljava/io/File;)[Ljava/io/File;", (void*)Java_org_coolreader_crengine_Engine_listFilesInternal},
1136   {"getDomVersionCurrent", "()I", (void*)Java_org_coolreader_crengine_Engine_getDomVersionCurrent}
1137 };
1138 
1139 
1140 static JNINativeMethod sDocViewMethods[] = {
1141   /* name, signature, funcPtr */
1142   {"createInternal", "()V", (void*)Java_org_coolreader_crengine_DocView_createInternal},
1143   {"destroyInternal", "()V", (void*)Java_org_coolreader_crengine_DocView_destroyInternal},
1144   {"getPageImageInternal", "(Landroid/graphics/Bitmap;I)V", (void*)Java_org_coolreader_crengine_DocView_getPageImageInternal},
1145   {"loadDocumentInternal", "(Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_DocView_loadDocumentInternal},
1146   {"loadDocumentFromMemoryInternal", "([BLjava/lang/String;)Z", (void*)Java_org_coolreader_crengine_DocView_loadDocumentFromMemoryInternal},
1147   {"getSettingsInternal", "()Ljava/util/Properties;", (void*)Java_org_coolreader_crengine_DocView_getSettingsInternal},
1148   {"getDocPropsInternal", "()Ljava/util/Properties;", (void*)Java_org_coolreader_crengine_DocView_getDocPropsInternal},
1149   {"applySettingsInternal", "(Ljava/util/Properties;)Z", (void*)Java_org_coolreader_crengine_DocView_applySettingsInternal},
1150   {"setStylesheetInternal", "(Ljava/lang/String;)V", (void*)Java_org_coolreader_crengine_DocView_setStylesheetInternal},
1151   {"resizeInternal", "(II)V", (void*)Java_org_coolreader_crengine_DocView_resizeInternal},
1152   {"doCommandInternal", "(II)Z", (void*)Java_org_coolreader_crengine_DocView_doCommandInternal},
1153   {"getCurrentPageBookmarkInternal", "()Lorg/coolreader/crengine/Bookmark;", (void*)Java_org_coolreader_crengine_DocView_getCurrentPageBookmarkInternal},
1154   {"goToPositionInternal", "(Ljava/lang/String;Z)Z", (void*)Java_org_coolreader_crengine_DocView_goToPositionInternal},
1155   {"getPositionPropsInternal", "(Ljava/lang/String;Z)Lorg/coolreader/crengine/PositionProperties;", (void*)Java_org_coolreader_crengine_DocView_getPositionPropsInternal},
1156   {"updateBookInfoInternal", "(Lorg/coolreader/crengine/BookInfo;)V", (void*)Java_org_coolreader_crengine_DocView_updateBookInfoInternal},
1157   {"getTOCInternal", "()Lorg/coolreader/crengine/TOCItem;", (void*)Java_org_coolreader_crengine_DocView_getTOCInternal},
1158   {"clearSelectionInternal", "()V", (void*)Java_org_coolreader_crengine_DocView_clearSelectionInternal},
1159   {"findTextInternal", "(Ljava/lang/String;III)Z", (void*)Java_org_coolreader_crengine_DocView_findTextInternal},
1160   {"setBatteryStateInternal", "(I)V", (void*)Java_org_coolreader_crengine_DocView_setBatteryStateInternal},
1161   {"getCoverPageDataInternal", "()[B", (void*)Java_org_coolreader_crengine_DocView_getCoverPageDataInternal},
1162   {"setPageBackgroundTextureInternal", "([BI)V", (void*)Java_org_coolreader_crengine_DocView_setPageBackgroundTextureInternal},
1163   {"updateSelectionInternal", "(Lorg/coolreader/crengine/Selection;)V", (void*)Java_org_coolreader_crengine_DocView_updateSelectionInternal},
1164   {"checkLinkInternal", "(III)Ljava/lang/String;", (void*)Java_org_coolreader_crengine_DocView_checkLinkInternal},
1165   {"goLinkInternal", "(Ljava/lang/String;)I", (void*)Java_org_coolreader_crengine_DocView_goLinkInternal},
1166   {"moveSelectionInternal", "(Lorg/coolreader/crengine/Selection;II)Z", (void*)Java_org_coolreader_crengine_DocView_moveSelectionInternal},
1167   {"swapToCacheInternal", "()I", (void*)Java_org_coolreader_crengine_DocView_swapToCacheInternal},
1168   {"checkImageInternal", "(IILorg/coolreader/crengine/ImageInfo;)Z", (void*)Java_org_coolreader_crengine_DocView_checkImageInternal},
1169   {"drawImageInternal", "(Landroid/graphics/Bitmap;ILorg/coolreader/crengine/ImageInfo;)Z", (void*)Java_org_coolreader_crengine_DocView_drawImageInternal},
1170   {"closeImageInternal", "()Z", (void*)Java_org_coolreader_crengine_DocView_closeImageInternal},
1171   {"hilightBookmarksInternal", "([Lorg/coolreader/crengine/Bookmark;)V", (void*)Java_org_coolreader_crengine_DocView_hilightBookmarksInternal},
1172   {"checkBookmarkInternal", "(IILorg/coolreader/crengine/Bookmark;)Z", (void*)Java_org_coolreader_crengine_DocView_checkBookmarkInternal},
1173   {"isRenderedInternal", "()Z", (void*)Java_org_coolreader_crengine_DocView_isRenderedInternal}
1174 };
1175 
1176 /*
1177  * Register native JNI-callable methods.
1178  *
1179  * "className" looks like "java/lang/String".
1180  */
jniRegisterNativeMethods(JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)1181 static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
1182     const JNINativeMethod* gMethods, int numMethods)
1183 {
1184     jclass clazz;
1185 
1186     LOGV("Registering %s natives\n", className);
1187     clazz = env->FindClass(className);
1188     if (clazz == NULL) {
1189         LOGE("Native registration unable to find class '%s'\n", className);
1190         return -1;
1191     }
1192     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
1193         LOGE("RegisterNatives failed for '%s'\n", className);
1194         return -1;
1195     }
1196     return 0;
1197 }
1198 
1199 
JNI_OnLoad(JavaVM * vm,void * reserved)1200 jint JNI_OnLoad(JavaVM* vm, void* reserved)
1201 {
1202    JNIEnv* env = NULL;
1203    jint res = -1;
1204 
1205 #ifdef JNI_VERSION_1_6
1206     if (res==-1 && vm->GetEnv((void**) &env, JNI_VERSION_1_6) == JNI_OK) {
1207         LOGI("JNI_OnLoad: JNI_VERSION_1_6\n");
1208    	    res = JNI_VERSION_1_6;
1209     }
1210 #endif
1211 #ifdef JNI_VERSION_1_4
1212     if (res==-1 && vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
1213         LOGI("JNI_OnLoad: JNI_VERSION_1_4\n");
1214    	    res = JNI_VERSION_1_4;
1215     }
1216 #endif
1217 #ifdef JNI_VERSION_1_2
1218     if (res==-1 && vm->GetEnv((void**) &env, JNI_VERSION_1_2) == JNI_OK) {
1219         LOGI("JNI_OnLoad: JNI_VERSION_1_2\n");
1220    	    res = JNI_VERSION_1_2;
1221     }
1222 #endif
1223 	if ( res==-1 )
1224 		return res;
1225 
1226     jniRegisterNativeMethods(env, "org/coolreader/crengine/Engine", sEngineMethods, sizeof(sEngineMethods)/sizeof(JNINativeMethod));
1227     jniRegisterNativeMethods(env, "org/coolreader/crengine/DocView", sDocViewMethods, sizeof(sDocViewMethods)/sizeof(JNINativeMethod));
1228     LOGI("JNI_OnLoad: native methods are registered!\n");
1229     return res;
1230 }
1231