/* * Copyright (C) 2009-2011 Geometer Plus <contact@geometerplus.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ package org.geometerplus.fbreader.formats.pdb; import java.io.*; import org.geometerplus.zlibrary.core.filesystem.ZLFile; import org.geometerplus.zlibrary.core.image.ZLFileImage; import org.geometerplus.zlibrary.core.image.ZLImage; import org.geometerplus.zlibrary.core.constants.MimeTypes; import org.geometerplus.zlibrary.core.encoding.ZLEncodingCollection; import org.geometerplus.zlibrary.core.language.ZLLanguageUtil; import org.geometerplus.fbreader.library.Book; import org.geometerplus.fbreader.bookmodel.BookModel; public class MobipocketPlugin extends PdbPlugin { @Override public boolean acceptsFile(ZLFile file) { return super.acceptsFile(file) && (fileType(file) == "BOOKMOBI"||fileType(file) == "TEXtREAd"); } @Override public boolean readMetaInfo(Book book) { InputStream stream = null; try { stream = book.File.getInputStream(); final PdbHeader header = new PdbHeader(stream); PdbUtil.skip(stream, header.Offsets[0] + 16 - header.length()); if(header.Id.equals("TEXtREAd")){ book.addAuthor("TEXtREAd"); book.setEncoding(header.encodingName); book.setTitle(header.DocName); return true; } long typelong = PdbUtil.readInt(stream); if (typelong != 0x4D4F4249) /* "MOBI" */ { System.out.println("--------hym-mobi--"+typelong); return false; } final int length = (int)PdbUtil.readInt(stream); // System.out.println("--------hym---"+length); long mobitype=PdbUtil.readInt(stream); System.out.println("--------hym---mobitype:"+mobitype); // PdbUtil.skip(stream, 4); final int encodingCode = (int)PdbUtil.readInt(stream); String encodingName = ZLEncodingCollection.Instance().getEncodingName(encodingCode); if (encodingName == null) { encodingName = "utf-8"; } book.setEncoding(encodingName); PdbUtil.skip(stream, 52); final int fullNameOffset = (int)PdbUtil.readInt(stream); final int fullNameLength = (int)PdbUtil.readInt(stream); final int languageCode = (int)PdbUtil.readInt(stream); book.setLanguage(ZLLanguageUtil.languageByIntCode(languageCode & 0xFF, (languageCode >> 8) & 0xFF)); PdbUtil.skip(stream, 32); int offset = 132; if ((PdbUtil.readInt(stream) & 0x40) != 0) {//if bit 6 (0x40) is set, then there's an EXTH record PdbUtil.skip(stream, length - 116); offset = length + 20; if (PdbUtil.readInt(stream) == 0x45585448) /* "EXTH" */ { PdbUtil.skip(stream, 4); final int recordsNumber = (int)PdbUtil.readInt(stream); offset += 8; for (int i = 0; i < recordsNumber; ++i) { final int type = (int)PdbUtil.readInt(stream); final int size = (int)PdbUtil.readInt(stream); offset += size; if (size <= 8) { continue; } switch (type) { default: PdbUtil.skip(stream, size - 8); break; case 100: { final byte[] buffer = new byte[size - 8]; stream.read(buffer); String author = new String(buffer, encodingName); final int index = author.indexOf(','); if (index != -1) { author = author.substring(index + 1).trim() + ' ' + author.substring(0, index).trim(); } else { author = author.trim(); } book.addAuthor(author); break; } case 105: { final byte[] buffer = new byte[size - 8]; stream.read(buffer); book.addTag(new String(buffer, encodingName)); break; } } } } } PdbUtil.skip(stream, fullNameOffset - offset); final byte[] titleBuffer = new byte[fullNameLength]; stream.read(titleBuffer); book.setTitle(new String(titleBuffer, encodingName)); return true; } catch (IOException e) { e.printStackTrace(); return false; } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { } } } } @Override public boolean readModel(BookModel model) { try { if(fileType(model.Book.File) == "TEXtREAd"){ return new PdbReader(model).readBook(model.Book.File); }else{ return new MobipocketHtmlBookReader(model).readBook(); } } catch (IOException e) { //e.printStackTrace(); return false; } } @Override public ZLImage readCover(ZLFile file) { InputStream stream = null; try { stream = file.getInputStream(); final PdbHeader header = new PdbHeader(stream); PdbUtil.skip(stream, header.Offsets[0] + 16 - header.length()); if (PdbUtil.readInt(stream) != 0x4D4F4249) /* "MOBI" */ { return null; } final int length = (int)PdbUtil.readInt(stream); PdbUtil.skip(stream, 104); final int exthFlags = (int)PdbUtil.readInt(stream); int coverIndex = -1; int thumbIndex = -1; int offset = 132; if ((exthFlags & 0x40) != 0) { PdbUtil.skip(stream, length - 116); offset = length + 20; if (PdbUtil.readInt(stream) != 0x45585448) /* "EXTH" */ { return null; } PdbUtil.skip(stream, 4); final int recordsNumber = (int)PdbUtil.readInt(stream); offset += 8; for (int i = 0; i < recordsNumber; ++i) { final int type = (int)PdbUtil.readInt(stream); final int size = (int)PdbUtil.readInt(stream); offset += size; if (size <= 8) { continue; } switch (type) { default: PdbUtil.skip(stream, size - 8); break; case 201: { if (size == 12) { coverIndex = (int)PdbUtil.readInt(stream); } else { PdbUtil.skip(stream, size - 8); } break; } case 202: { if (size == 12) { thumbIndex = (int)PdbUtil.readInt(stream); } else { PdbUtil.skip(stream, size - 8); } break; } } } } final InputStream tempStream = stream; stream = null; tempStream.close(); if (coverIndex == -1) { if (thumbIndex == -1) { return null; } coverIndex = thumbIndex; } MobipocketStream myMobipocketStream = new MobipocketStream(file); int start = myMobipocketStream.getImageOffset(coverIndex); if (start >= 0) { int len = myMobipocketStream.getImageLength(coverIndex); if (len > 0) { return new ZLFileImage(MimeTypes.MIME_IMAGE_AUTO, file, start, len); } } return null; } catch (IOException e) { return null; } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { } } } } @Override public String readAnnotation(ZLFile file) { return null; } }