Merge pull request #909 from ag2s20150909/master

添加自定义epublib,支持epub v3解析目录。
pull/913/head 3.21.032520
kunfei 4 years ago committed by GitHub
commit 820a3c4fad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      app/build.gradle
  2. 19
      app/src/main/java/io/legado/app/model/localBook/EPUBFile.kt
  3. 1
      epublib/.gitignore
  4. 40
      epublib/build.gradle
  5. 0
      epublib/consumer-rules.pro
  6. 21
      epublib/proguard-rules.pro
  7. 26
      epublib/src/androidTest/java/me/ag2s/epublib/ExampleInstrumentedTest.java
  8. 7
      epublib/src/main/AndroidManifest.xml
  9. 139
      epublib/src/main/java/com/jcraft/jzlib/Adler32.java
  10. 179
      epublib/src/main/java/com/jcraft/jzlib/CRC32.java
  11. 43
      epublib/src/main/java/com/jcraft/jzlib/Checksum.java
  12. 1757
      epublib/src/main/java/com/jcraft/jzlib/Deflate.java
  13. 171
      epublib/src/main/java/com/jcraft/jzlib/Deflater.java
  14. 181
      epublib/src/main/java/com/jcraft/jzlib/DeflaterOutputStream.java
  15. 44
      epublib/src/main/java/com/jcraft/jzlib/GZIPException.java
  16. 195
      epublib/src/main/java/com/jcraft/jzlib/GZIPHeader.java
  17. 145
      epublib/src/main/java/com/jcraft/jzlib/GZIPInputStream.java
  18. 90
      epublib/src/main/java/com/jcraft/jzlib/GZIPOutputStream.java
  19. 614
      epublib/src/main/java/com/jcraft/jzlib/InfBlocks.java
  20. 610
      epublib/src/main/java/com/jcraft/jzlib/InfCodes.java
  21. 518
      epublib/src/main/java/com/jcraft/jzlib/InfTree.java
  22. 751
      epublib/src/main/java/com/jcraft/jzlib/Inflate.java
  23. 168
      epublib/src/main/java/com/jcraft/jzlib/Inflater.java
  24. 247
      epublib/src/main/java/com/jcraft/jzlib/InflaterInputStream.java
  25. 92
      epublib/src/main/java/com/jcraft/jzlib/JZlib.java
  26. 148
      epublib/src/main/java/com/jcraft/jzlib/StaticTree.java
  27. 367
      epublib/src/main/java/com/jcraft/jzlib/Tree.java
  28. 126
      epublib/src/main/java/com/jcraft/jzlib/ZInputStream.java
  29. 159
      epublib/src/main/java/com/jcraft/jzlib/ZOutputStream.java
  30. 377
      epublib/src/main/java/com/jcraft/jzlib/ZStream.java
  31. 44
      epublib/src/main/java/com/jcraft/jzlib/ZStreamException.java
  32. 12
      epublib/src/main/java/me/ag2s/epublib/Constants.java
  33. 155
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationEvent.java
  34. 18
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationEventListener.java
  35. 207
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationHistory.java
  36. 219
      epublib/src/main/java/me/ag2s/epublib/browsersupport/Navigator.java
  37. 7
      epublib/src/main/java/me/ag2s/epublib/browsersupport/package-info.java
  38. 79
      epublib/src/main/java/me/ag2s/epublib/domain/Author.java
  39. 316
      epublib/src/main/java/me/ag2s/epublib/domain/Book.java
  40. 104
      epublib/src/main/java/me/ag2s/epublib/domain/Date.java
  41. 33
      epublib/src/main/java/me/ag2s/epublib/domain/EpubResourceProvider.java
  42. 128
      epublib/src/main/java/me/ag2s/epublib/domain/Guide.java
  43. 102
      epublib/src/main/java/me/ag2s/epublib/domain/GuideReference.java
  44. 129
      epublib/src/main/java/me/ag2s/epublib/domain/Identifier.java
  45. 135
      epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java
  46. 12
      epublib/src/main/java/me/ag2s/epublib/domain/LazyResourceProvider.java
  47. 21
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestItemProperties.java
  48. 16
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestItemRefProperties.java
  49. 6
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestProperties.java
  50. 73
      epublib/src/main/java/me/ag2s/epublib/domain/MediaType.java
  51. 95
      epublib/src/main/java/me/ag2s/epublib/domain/MediaTypes.java
  52. 230
      epublib/src/main/java/me/ag2s/epublib/domain/Metadata.java
  53. 1144
      epublib/src/main/java/me/ag2s/epublib/domain/Relator.java
  54. 327
      epublib/src/main/java/me/ag2s/epublib/domain/Resource.java
  55. 36
      epublib/src/main/java/me/ag2s/epublib/domain/ResourceInputStream.java
  56. 43
      epublib/src/main/java/me/ag2s/epublib/domain/ResourceReference.java
  57. 387
      epublib/src/main/java/me/ag2s/epublib/domain/Resources.java
  58. 190
      epublib/src/main/java/me/ag2s/epublib/domain/Spine.java
  59. 52
      epublib/src/main/java/me/ag2s/epublib/domain/SpineReference.java
  60. 59
      epublib/src/main/java/me/ag2s/epublib/domain/TOCReference.java
  61. 259
      epublib/src/main/java/me/ag2s/epublib/domain/TableOfContents.java
  62. 78
      epublib/src/main/java/me/ag2s/epublib/domain/TitledResourceReference.java
  63. 20
      epublib/src/main/java/me/ag2s/epublib/epub/BookProcessor.java
  64. 72
      epublib/src/main/java/me/ag2s/epublib/epub/BookProcessorPipeline.java
  65. 134
      epublib/src/main/java/me/ag2s/epublib/epub/DOMUtil.java
  66. 138
      epublib/src/main/java/me/ag2s/epublib/epub/EpubProcessorSupport.java
  67. 169
      epublib/src/main/java/me/ag2s/epublib/epub/EpubReader.java
  68. 184
      epublib/src/main/java/me/ag2s/epublib/epub/EpubWriter.java
  69. 9
      epublib/src/main/java/me/ag2s/epublib/epub/HtmlProcessor.java
  70. 5
      epublib/src/main/java/me/ag2s/epublib/epub/Main.java
  71. 297
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocument.java
  72. 334
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocumentV2.java
  73. 206
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocumentV3.java
  74. 89
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java
  75. 223
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataReader.java
  76. 176
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java
  77. 425
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentReader.java
  78. 254
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentWriter.java
  79. 173
      epublib/src/main/java/me/ag2s/epublib/epub/ResourcesLoader.java
  80. 70
      epublib/src/main/java/me/ag2s/epublib/util/CollectionUtil.java
  81. 165
      epublib/src/main/java/me/ag2s/epublib/util/IOUtil.java
  82. 32
      epublib/src/main/java/me/ag2s/epublib/util/NoCloseOutputStream.java
  83. 35
      epublib/src/main/java/me/ag2s/epublib/util/NoCloseWriter.java
  84. 174
      epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java
  85. 202
      epublib/src/main/java/me/ag2s/epublib/util/StreamWriterDelegate.txt
  86. 275
      epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java
  87. 340
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/BOMInputStream.java
  88. 170
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/ByteOrderMark.java
  89. 238
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/ProxyInputStream.java
  90. 752
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReader.java
  91. 138
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReaderException.java
  92. 1135
      epublib/src/main/resources/dtd/openebook.org/dtds/oeb-1.2/oeb12.ent
  93. 390
      epublib/src/main/resources/dtd/openebook.org/dtds/oeb-1.2/oebpkg12.dtd
  94. 269
      epublib/src/main/resources/dtd/www.daisy.org/z3986/2005/ncx-2005-1.dtd
  95. 242
      epublib/src/main/resources/dtd/www.w3.org/TR/ruby/xhtml-ruby-1.mod
  96. 51
      epublib/src/main/resources/dtd/www.w3.org/TR/xhtml-modularization/DTD/xhtml-arch-1.mod
  97. 142
      epublib/src/main/resources/dtd/www.w3.org/TR/xhtml-modularization/DTD/xhtml-attribs-1.mod
  98. 53
      epublib/src/main/resources/dtd/www.w3.org/TR/xhtml-modularization/DTD/xhtml-base-1.mod
  99. 47
      epublib/src/main/resources/dtd/www.w3.org/TR/xhtml-modularization/DTD/xhtml-bdo-1.mod
  100. 164
      epublib/src/main/resources/dtd/www.w3.org/TR/xhtml-modularization/DTD/xhtml-blkphras-1.mod
  101. Some files were not shown because too many files have changed in this diff Show More

@ -111,8 +111,9 @@ kapt {
}
dependencies {
implementation project(path: ':epublib')
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
@ -211,10 +212,10 @@ dependencies {
//
implementation 'com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core:0.1.3'
//epub
implementation('com.positiondev.epublib:epublib-core:3.1') {
exclude group: 'org.slf4j'
exclude group: 'xmlpull'
}
// implementation('com.positiondev.epublib:epublib-core:3.1') {
// exclude group: 'org.slf4j'
// exclude group: 'xmlpull'
// }
}

@ -8,14 +8,13 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.EpubChapter
import io.legado.app.utils.*
import net.sf.jazzlib.ZipEntry
import net.sf.jazzlib.ZipInputStream
import nl.siegmann.epublib.domain.Book
import nl.siegmann.epublib.domain.Resources
import nl.siegmann.epublib.domain.TOCReference
import nl.siegmann.epublib.epub.EpubReader
import nl.siegmann.epublib.service.MediatypeService
import nl.siegmann.epublib.util.ResourceUtil
import me.ag2s.epublib.domain.Book
import me.ag2s.epublib.domain.MediaTypes
import me.ag2s.epublib.domain.Resources
import me.ag2s.epublib.domain.TOCReference
import me.ag2s.epublib.epub.EpubReader
import me.ag2s.epublib.util.ResourceUtil
import org.jsoup.Jsoup
import splitties.init.appCtx
import java.io.File
@ -24,6 +23,8 @@ import java.io.IOException
import java.io.InputStream
import java.nio.charset.Charset
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
class EPUBFile(var book: io.legado.app.data.entities.Book) {
@ -111,7 +112,7 @@ class EPUBFile(var book: io.legado.app.data.entities.Book) {
zipEntry = inZip.nextEntry
if ((zipEntry == null) || zipEntry.isDirectory || zipEntry == ZipEntry("<error>")) continue
val resource = ResourceUtil.createResource(zipEntry, inZip)
if (resource.mediaType == MediatypeService.XHTML) resource.inputEncoding = "UTF-8"
if (resource.mediaType == MediaTypes.XHTML) resource.inputEncoding = "UTF-8"
if (zipEntry.name.endsWith(".opf")) {
/*掌上书苑有很多自制书OPF的nameSpace格式不标准,强制修复成正确的格式*/
val newS = String(resource.data).replace(

@ -0,0 +1 @@
/build

@ -0,0 +1,40 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
//XmlPullParserFactory
//me.ag2s.epublib.epub.EpubProcessorSupport
//createXmlSerializer方法可使用kxml2
// https://mvnrepository.com/artifact/kxml2/kxml2
//implementation 'kxml2:kxml2:2.3.0'
//slf4j依赖
//implementation 'org.slf4j:-android:1.7.25'
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,26 @@
package me.ag2s.epublib;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("me.ag2s.epublib.test", appContext.getPackageName());
}
}

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.ag2s.epublib">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest>

@ -0,0 +1,139 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final public class Adler32 implements Checksum {
// largest prime smaller than 65536
static final private int BASE=65521;
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
static final private int NMAX=5552;
private long s1=1L;
private long s2=0L;
public void reset(long init){
s1=init&0xffff;
s2=(init>>16)&0xffff;
}
public void reset(){
s1=1L;
s2=0L;
}
public long getValue(){
return ((s2<<16)|s1);
}
public void update(byte[] buf, int index, int len){
if(len==1){
s1+=buf[index++]&0xff; s2+=s1;
s1%=BASE;
s2%=BASE;
return;
}
int len1 = len/NMAX;
int len2 = len%NMAX;
while(len1-->0) {
int k=NMAX;
len-=k;
while(k-->0){
s1+=buf[index++]&0xff; s2+=s1;
}
s1%=BASE;
s2%=BASE;
}
int k=len2;
len-=k;
while(k-->0){
s1+=buf[index++]&0xff; s2+=s1;
}
s1%=BASE;
s2%=BASE;
}
public Adler32 copy(){
Adler32 foo = new Adler32();
foo.s1 = this.s1;
foo.s2 = this.s2;
return foo;
}
// The following logic has come from zlib.1.2.
static long combine(long adler1, long adler2, long len2){
long BASEL = (long)BASE;
long sum1;
long sum2;
long rem; // unsigned int
rem = len2 % BASEL;
sum1 = adler1 & 0xffffL;
sum2 = rem * sum1;
sum2 %= BASEL; // MOD(sum2);
sum1 += (adler2 & 0xffffL) + BASEL - 1;
sum2 += ((adler1 >> 16) & 0xffffL) + ((adler2 >> 16) & 0xffffL) + BASEL - rem;
if (sum1 >= BASEL) sum1 -= BASEL;
if (sum1 >= BASEL) sum1 -= BASEL;
if (sum2 >= (BASEL << 1)) sum2 -= (BASEL << 1);
if (sum2 >= BASEL) sum2 -= BASEL;
return sum1 | (sum2 << 16);
}
/*
private java.util.zip.Adler32 adler=new java.util.zip.Adler32();
public void update(byte[] buf, int index, int len){
if(buf==null) {adler.reset();}
else{adler.update(buf, index, len);}
}
public void reset(){
adler.reset();
}
public void reset(long init){
if(init==1L){
adler.reset();
}
else{
System.err.println("unsupported operation");
}
}
public long getValue(){
return adler.getValue();
}
*/
}

@ -0,0 +1,179 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final public class CRC32 implements Checksum {
/*
* The following logic has come from RFC1952.
*/
private int v = 0;
private static int[] crc_table = null;
static {
crc_table = new int[256];
for (int n = 0; n < 256; n++) {
int c = n;
for (int k = 8; --k >= 0; ) {
if ((c & 1) != 0)
c = 0xedb88320 ^ (c >>> 1);
else
c = c >>> 1;
}
crc_table[n] = c;
}
}
public void update (byte[] buf, int index, int len) {
int c = ~v;
while (--len >= 0)
c = crc_table[(c^buf[index++])&0xff]^(c >>> 8);
v = ~c;
}
public void reset(){
v = 0;
}
public void reset(long vv){
v = (int)(vv&0xffffffffL);
}
public long getValue(){
return (long)(v&0xffffffffL);
}
// The following logic has come from zlib.1.2.
private static final int GF2_DIM = 32;
static long combine(long crc1, long crc2, long len2){
long row;
long[] even = new long[GF2_DIM];
long[] odd = new long[GF2_DIM];
// degenerate case (also disallow negative lengths)
if (len2 <= 0)
return crc1;
// put operator for one zero bit in odd
odd[0] = 0xedb88320L; // CRC-32 polynomial
row = 1;
for (int n = 1; n < GF2_DIM; n++) {
odd[n] = row;
row <<= 1;
}
// put operator for two zero bits in even
gf2_matrix_square(even, odd);
// put operator for four zero bits in odd
gf2_matrix_square(odd, even);
// apply len2 zeros to crc1 (first square will put the operator for one
// zero byte, eight zero bits, in even)
do {
// apply zeros operator for this bit of len2
gf2_matrix_square(even, odd);
if ((len2 & 1)!=0)
crc1 = gf2_matrix_times(even, crc1);
len2 >>= 1;
// if no more bits set, then done
if (len2 == 0)
break;
// another iteration of the loop with odd and even swapped
gf2_matrix_square(odd, even);
if ((len2 & 1)!=0)
crc1 = gf2_matrix_times(odd, crc1);
len2 >>= 1;
// if no more bits set, then done
} while (len2 != 0);
/* return combined crc */
crc1 ^= crc2;
return crc1;
}
private static long gf2_matrix_times(long[] mat, long vec){
long sum = 0;
int index = 0;
while (vec!=0) {
if ((vec & 1)!=0)
sum ^= mat[index];
vec >>= 1;
index++;
}
return sum;
}
static final void gf2_matrix_square(long[] square, long[] mat) {
for (int n = 0; n < GF2_DIM; n++)
square[n] = gf2_matrix_times(mat, mat[n]);
}
/*
private java.util.zip.CRC32 crc32 = new java.util.zip.CRC32();
public void update(byte[] buf, int index, int len){
if(buf==null) {crc32.reset();}
else{crc32.update(buf, index, len);}
}
public void reset(){
crc32.reset();
}
public void reset(long init){
if(init==0L){
crc32.reset();
}
else{
System.err.println("unsupported operation");
}
}
public long getValue(){
return crc32.getValue();
}
*/
public CRC32 copy(){
CRC32 foo = new CRC32();
foo.v = this.v;
return foo;
}
public static int[] getCRC32Table(){
int[] tmp = new int[crc_table.length];
System.arraycopy(crc_table, 0, tmp, 0, tmp.length);
return tmp;
}
}

@ -0,0 +1,43 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
interface Checksum {
void update(byte[] buf, int index, int len);
void reset();
void reset(long init);
long getValue();
Checksum copy();
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,171 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final public class Deflater extends ZStream{
static final private int MAX_WBITS=15; // 32K LZ77 window
static final private int DEF_WBITS=MAX_WBITS;
static final private int Z_NO_FLUSH=0;
static final private int Z_PARTIAL_FLUSH=1;
static final private int Z_SYNC_FLUSH=2;
static final private int Z_FULL_FLUSH=3;
static final private int Z_FINISH=4;
static final private int MAX_MEM_LEVEL=9;
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
private boolean finished = false;
public Deflater(){
super();
}
public Deflater(int level) throws GZIPException {
this(level, MAX_WBITS);
}
public Deflater(int level, boolean nowrap) throws GZIPException {
this(level, MAX_WBITS, nowrap);
}
public Deflater(int level, int bits) throws GZIPException {
this(level, bits, false);
}
public Deflater(int level, int bits, boolean nowrap) throws GZIPException {
super();
int ret = init(level, bits, nowrap);
if(ret!=Z_OK)
throw new GZIPException(ret+": "+msg);
}
public Deflater(int level, int bits, int memlevel, JZlib.WrapperType wrapperType) throws GZIPException {
super();
int ret = init(level, bits, memlevel, wrapperType);
if(ret!=Z_OK)
throw new GZIPException(ret+": "+msg);
}
public Deflater(int level, int bits, int memlevel) throws GZIPException {
super();
int ret = init(level, bits, memlevel);
if(ret!=Z_OK)
throw new GZIPException(ret+": "+msg);
}
public int init(int level){
return init(level, MAX_WBITS);
}
public int init(int level, boolean nowrap){
return init(level, MAX_WBITS, nowrap);
}
public int init(int level, int bits){
return init(level, bits, false);
}
public int init(int level, int bits, int memlevel, JZlib.WrapperType wrapperType){
if(bits < 9 || bits > 15){
return Z_STREAM_ERROR;
}
if(wrapperType == JZlib.W_NONE) {
bits *= -1;
}
else if(wrapperType == JZlib.W_GZIP) {
bits += 16;
}
else if(wrapperType == JZlib.W_ANY) {
return Z_STREAM_ERROR;
}
else if(wrapperType == JZlib.W_ZLIB) {
}
return init(level, bits, memlevel);
}
public int init(int level, int bits, int memlevel){
finished = false;
dstate=new Deflate(this);
return dstate.deflateInit(level, bits, memlevel);
}
public int init(int level, int bits, boolean nowrap){
finished = false;
dstate=new Deflate(this);
return dstate.deflateInit(level, nowrap?-bits:bits);
}
public int deflate(int flush){
if(dstate==null){
return Z_STREAM_ERROR;
}
int ret = dstate.deflate(flush);
if(ret == Z_STREAM_END)
finished = true;
return ret;
}
public int end(){
finished = true;
if(dstate==null) return Z_STREAM_ERROR;
int ret=dstate.deflateEnd();
dstate=null;
free();
return ret;
}
public int params(int level, int strategy){
if(dstate==null) return Z_STREAM_ERROR;
return dstate.deflateParams(level, strategy);
}
public int setDictionary (byte[] dictionary, int dictLength){
if(dstate == null)
return Z_STREAM_ERROR;
return dstate.deflateSetDictionary(dictionary, dictLength);
}
public boolean finished(){
return finished;
}
public int copy(Deflater src){
this.finished = src.finished;
return Deflate.deflateCopy(this, src);
}
}

@ -0,0 +1,181 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jzlib;
import java.io.*;
public class DeflaterOutputStream extends FilterOutputStream {
protected final Deflater deflater;
protected byte[] buffer;
private boolean closed = false;
private boolean syncFlush = false;
private final byte[] buf1 = new byte[1];
protected boolean mydeflater = false;
private boolean close_out = true;
protected static final int DEFAULT_BUFSIZE = 512;
public DeflaterOutputStream(OutputStream out) throws IOException {
this(out,
new Deflater(JZlib.Z_DEFAULT_COMPRESSION),
DEFAULT_BUFSIZE, true);
mydeflater = true;
}
public DeflaterOutputStream(OutputStream out, Deflater def) throws IOException {
this(out, def, DEFAULT_BUFSIZE, true);
}
public DeflaterOutputStream(OutputStream out,
Deflater deflater,
int size) throws IOException {
this(out, deflater, size, true);
}
public DeflaterOutputStream(OutputStream out,
Deflater deflater,
int size,
boolean close_out) throws IOException {
super(out);
if (out == null || deflater == null) {
throw new NullPointerException();
}
else if (size <= 0) {
throw new IllegalArgumentException("buffer size must be greater than 0");
}
this.deflater = deflater;
buffer = new byte[size];
this.close_out = close_out;
}
public void write(int b) throws IOException {
buf1[0] = (byte)(b & 0xff);
write(buf1, 0, 1);
}
public void write(byte[] b, int off, int len) throws IOException {
if (deflater.finished()) {
throw new IOException("finished");
}
else if (off<0 | len<0 | off+len>b.length) {
throw new IndexOutOfBoundsException();
}
else if (len == 0) {
return;
}
else {
int flush = syncFlush ? JZlib.Z_SYNC_FLUSH : JZlib.Z_NO_FLUSH;
deflater.setInput(b, off, len, true);
while (deflater.avail_in>0) {
int err = deflate(flush);
if (err == JZlib.Z_STREAM_END)
break;
}
}
}
public void finish() throws IOException {
while (!deflater.finished()) {
deflate(JZlib.Z_FINISH);
}
}
public void close() throws IOException {
if (!closed) {
finish();
if (mydeflater){
deflater.end();
}
if(close_out)
out.close();
closed = true;
}
}
protected int deflate(int flush) throws IOException {
deflater.setOutput(buffer, 0, buffer.length);
int err = deflater.deflate(flush);
switch(err) {
case JZlib.Z_OK:
case JZlib.Z_STREAM_END:
break;
case JZlib.Z_BUF_ERROR:
if(deflater.avail_in<=0 && flush!=JZlib.Z_FINISH){
// flush() without any data
break;
}
default:
throw new IOException("failed to deflate");
}
int len = deflater.next_out_index;
if (len > 0) {
out.write(buffer, 0, len);
}
return err;
}
public void flush() throws IOException {
if (syncFlush && !deflater.finished()) {
while (true) {
int err = deflate(JZlib.Z_SYNC_FLUSH);
if (deflater.next_out_index < buffer.length)
break;
if (err == JZlib.Z_STREAM_END)
break;
}
}
out.flush();
}
public long getTotalIn() {
return deflater.getTotalIn();
}
public long getTotalOut() {
return deflater.getTotalOut();
}
public void setSyncFlush(boolean syncFlush){
this.syncFlush = syncFlush;
}
public boolean getSyncFlush(){
return this.syncFlush;
}
public Deflater getDeflater(){
return deflater;
}
}

@ -0,0 +1,44 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
public class GZIPException extends java.io.IOException {
public GZIPException() {
super();
}
public GZIPException(String s) {
super(s);
}
}

@ -0,0 +1,195 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
/**
* @see "http://www.ietf.org/rfc/rfc1952.txt"
*/
public class GZIPHeader implements Cloneable {
public static final byte OS_MSDOS = (byte) 0x00;
public static final byte OS_AMIGA = (byte) 0x01;
public static final byte OS_VMS = (byte) 0x02;
public static final byte OS_UNIX = (byte) 0x03;
public static final byte OS_ATARI = (byte) 0x05;
public static final byte OS_OS2 = (byte) 0x06;
public static final byte OS_MACOS = (byte) 0x07;
public static final byte OS_TOPS20 = (byte) 0x0a;
public static final byte OS_WIN32 = (byte) 0x0b;
public static final byte OS_VMCMS = (byte) 0x04;
public static final byte OS_ZSYSTEM = (byte) 0x08;
public static final byte OS_CPM = (byte) 0x09;
public static final byte OS_QDOS = (byte) 0x0c;
public static final byte OS_RISCOS = (byte) 0x0d;
public static final byte OS_UNKNOWN = (byte) 0xff;
boolean text = false;
private boolean fhcrc = false;
long time;
int xflags;
int os = 255;
byte[] extra;
byte[] name;
byte[] comment;
int hcrc;
long crc;
boolean done = false;
long mtime = 0;
public void setModifiedTime(long mtime) {
this.mtime = mtime;
}
public long getModifiedTime() {
return mtime;
}
public void setOS(int os) {
if((0<=os && os <=13) || os==255)
this.os=os;
else
throw new IllegalArgumentException("os: "+os);
}
public int getOS(){
return os;
}
public void setName(String name) {
this.name=name.getBytes(StandardCharsets.ISO_8859_1);
}
public String getName(){
if(name==null) return "";
return new String(name, StandardCharsets.ISO_8859_1);
}
public void setComment(String comment) {
this.comment=comment.getBytes(StandardCharsets.ISO_8859_1);
}
public String getComment(){
if(comment==null) return "";
return new String(comment, StandardCharsets.ISO_8859_1);
}
public void setCRC(long crc){
this.crc = crc;
}
public long getCRC(){
return crc;
}
void put(Deflate d){
int flag = 0;
if(text){
flag |= 1; // FTEXT
}
if(fhcrc){
flag |= 2; // FHCRC
}
if(extra!=null){
flag |= 4; // FEXTRA
}
if(name!=null){
flag |= 8; // FNAME
}
if(comment!=null){
flag |= 16; // FCOMMENT
}
int xfl = 0;
if(d.level == JZlib.Z_BEST_SPEED){
xfl |= 4;
}
else if (d.level == JZlib.Z_BEST_COMPRESSION){
xfl |= 2;
}
d.put_short((short)0x8b1f); // ID1 ID2
d.put_byte((byte)8); // CM(Compression Method)
d.put_byte((byte)flag);
d.put_byte((byte)mtime);
d.put_byte((byte)(mtime>>8));
d.put_byte((byte)(mtime>>16));
d.put_byte((byte)(mtime>>24));
d.put_byte((byte)xfl);
d.put_byte((byte)os);
if(extra!=null){
d.put_byte((byte)extra.length);
d.put_byte((byte)(extra.length>>8));
d.put_byte(extra, 0, extra.length);
}
if(name!=null){
d.put_byte(name, 0, name.length);
d.put_byte((byte)0);
}
if(comment!=null){
d.put_byte(comment, 0, comment.length);
d.put_byte((byte)0);
}
}
@Override
public Object clone() throws CloneNotSupportedException {
GZIPHeader gheader = (GZIPHeader)super.clone();
byte[] tmp;
if(gheader.extra!=null){
tmp=new byte[gheader.extra.length];
System.arraycopy(gheader.extra, 0, tmp, 0, tmp.length);
gheader.extra = tmp;
}
if(gheader.name!=null){
tmp=new byte[gheader.name.length];
System.arraycopy(gheader.name, 0, tmp, 0, tmp.length);
gheader.name = tmp;
}
if(gheader.comment!=null){
tmp=new byte[gheader.comment.length];
System.arraycopy(gheader.comment, 0, tmp, 0, tmp.length);
gheader.comment = tmp;
}
return gheader;
}
}

@ -0,0 +1,145 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jzlib;
import java.io.*;
public class GZIPInputStream extends InflaterInputStream {
public GZIPInputStream(InputStream in) throws IOException {
this(in, DEFAULT_BUFSIZE, true);
}
public GZIPInputStream(InputStream in,
int size,
boolean close_in) throws IOException {
this(in, new Inflater(15+16), size, close_in);
myinflater = true;
}
public GZIPInputStream(InputStream in,
Inflater inflater,
int size,
boolean close_in) throws IOException {
super(in, inflater, size, close_in);
}
public long getModifiedtime() {
return inflater.istate.getGZIPHeader().getModifiedTime();
}
public int getOS() {
return inflater.istate.getGZIPHeader().getOS();
}
public String getName() {
return inflater.istate.getGZIPHeader().getName();
}
public String getComment() {
return inflater.istate.getGZIPHeader().getComment();
}
public long getCRC() throws GZIPException {
if(inflater.istate.mode != 12 /*DONE*/)
throw new GZIPException("checksum is not calculated yet.");
return inflater.istate.getGZIPHeader().getCRC();
}
public void readHeader() throws IOException {
byte[] empty = "".getBytes();
inflater.setOutput(empty, 0, 0);
inflater.setInput(empty, 0, 0, false);
byte[] b = new byte[10];
int n = fill(b);
if(n!=10){
if(n>0){
inflater.setInput(b, 0, n, false);
//inflater.next_in_index = n;
inflater.next_in_index = 0;
inflater.avail_in = n;
}
throw new IOException("no input");
}
inflater.setInput(b, 0, n, false);
byte[] b1 = new byte[1];
do{
if(inflater.avail_in<=0){
int i = in.read(b1);
if(i<=0)
throw new IOException("no input");
inflater.setInput(b1, 0, 1, true);
}
int err = inflater.inflate(JZlib.Z_NO_FLUSH);
if(err!=0/*Z_OK*/){
int len = 2048-inflater.next_in.length;
if(len>0){
byte[] tmp = new byte[len];
n = fill(tmp);
if(n>0){
inflater.avail_in += inflater.next_in_index;
inflater.next_in_index = 0;
inflater.setInput(tmp, 0, n, true);
}
}
//inflater.next_in_index = inflater.next_in.length;
inflater.avail_in += inflater.next_in_index;
inflater.next_in_index = 0;
throw new IOException(inflater.msg);
}
}
while(inflater.istate.inParsingHeader());
}
private int fill(byte[] buf) {
int len = buf.length;
int n = 0;
do{
int i = -1;
try {
i = in.read(buf, n, buf.length - n);
}
catch(IOException e){
}
if(i == -1){
break;
}
n+=i;
}
while(n<len);
return n;
}
}

@ -0,0 +1,90 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jzlib;
import java.io.*;
public class GZIPOutputStream extends DeflaterOutputStream {
public GZIPOutputStream(OutputStream out) throws IOException {
this(out, DEFAULT_BUFSIZE);
}
public GZIPOutputStream(OutputStream out, int size) throws IOException {
this(out, size, true);
}
public GZIPOutputStream(OutputStream out,
int size,
boolean close_out) throws IOException {
this(out,
new Deflater(JZlib.Z_DEFAULT_COMPRESSION, 15+16),
size, close_out);
mydeflater=true;
}
public GZIPOutputStream(OutputStream out,
Deflater deflater,
int size,
boolean close_out) throws IOException{
super(out, deflater, size, close_out);
}
private void check() throws GZIPException {
if(deflater.dstate.status != 42 /*INIT_STATUS*/)
throw new GZIPException("header is already written.");
}
public void setModifiedTime(long mtime) throws GZIPException {
check();
deflater.dstate.getGZIPHeader().setModifiedTime(mtime);
}
public void setOS(int os) throws GZIPException {
check();
deflater.dstate.getGZIPHeader().setOS(os);
}
public void setName(String name) throws GZIPException {
check();
deflater.dstate.getGZIPHeader().setName(name);
}
public void setComment(String comment) throws GZIPException {
check();
deflater.dstate.getGZIPHeader().setComment(comment);
}
public long getCRC() throws GZIPException {
if(deflater.dstate.status != 666 /*FINISH_STATE*/)
throw new GZIPException("checksum is not calculated yet.");
return deflater.dstate.getGZIPHeader().getCRC();
}
}

@ -0,0 +1,614 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final class InfBlocks{
static final private int MANY=1440;
// And'ing with mask[n] masks the lower n bits
static final private int[] inflate_mask = {
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff,
0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff,
0x00007fff, 0x0000ffff
};
// Table for deflate from PKZIP's appnote.txt.
static final int[] border = { // Order of the bit length code lengths
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
static final private int TYPE=0; // get type bits (3, including end bit)
static final private int LENS=1; // get lengths for stored
static final private int STORED=2;// processing stored block
static final private int TABLE=3; // get table lengths
static final private int BTREE=4; // get bit lengths tree for a dynamic block
static final private int DTREE=5; // get length, distance trees for a dynamic block
static final private int CODES=6; // processing fixed or dynamic block
static final private int DRY=7; // output remaining window bytes
static final private int DONE=8; // finished last block, done
static final private int BAD=9; // ot a data error--stuck here
int mode; // current inflate_block mode
int left; // if STORED, bytes left to copy
int table; // table lengths (14 bits)
int index; // index into blens (or border)
int[] blens; // bit lengths of codes
int[] bb=new int[1]; // bit length tree depth
int[] tb=new int[1]; // bit length decoding tree
int[] bl=new int[1];
int[] bd=new int[1];
int[][] tl=new int[1][];
int[][] td=new int[1][];
int[] tli=new int[1]; // tl_index
int[] tdi=new int[1]; // td_index
private final InfCodes codes; // if CODES, current state
int last; // true if this block is the last block
// mode independent information
int bitk; // bits in bit buffer
int bitb; // bit buffer
int[] hufts; // single malloc for tree space
byte[] window; // sliding window
int end; // one byte after sliding window
int read; // window read pointer
int write; // window write pointer
private boolean check;
private final InfTree inftree=new InfTree();
private final ZStream z;
InfBlocks(ZStream z, int w){
this.z=z;
this.codes=new InfCodes(this.z, this);
hufts=new int[MANY*3];
window=new byte[w];
end=w;
this.check = (z.istate.wrap==0) ? false : true;
mode = TYPE;
reset();
}
void reset(){
if(mode==BTREE || mode==DTREE){
}
if(mode==CODES){
codes.free(z);
}
mode=TYPE;
bitk=0;
bitb=0;
read=write=0;
if(check){
z.adler.reset();
}
}
int proc(int r){
int t; // temporary storage
int b; // bit buffer
int k; // bits in bit buffer
int p; // input data pointer
int n; // bytes available there
int q; // output window write pointer
int m; // bytes to end of window or read pointer
// copy input/output information to locals (UPDATE macro restores)
{p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;}
{q=write;m=(int)(q<read?read-q-1:end-q);}
// process input based on current state
while(true){
switch (mode){
case TYPE:
while(k<(3)){
if(n!=0){
r=Z_OK;
}
else{
bitb=b; bitk=k;
z.avail_in=n;
z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
t = (int)(b & 7);
last = t & 1;
switch (t >>> 1){
case 0: // stored
{b>>>=(3);k-=(3);}
t = k & 7; // go to byte boundary
{b>>>=(t);k-=(t);}
mode = LENS; // get length of stored block
break;
case 1: // fixed
InfTree.inflate_trees_fixed(bl, bd, tl, td, z);
codes.init(bl[0], bd[0], tl[0], 0, td[0], 0);
{b>>>=(3);k-=(3);}
mode = CODES;
break;
case 2: // dynamic
{b>>>=(3);k-=(3);}
mode = TABLE;
break;
case 3: // illegal
{b>>>=(3);k-=(3);}
mode = BAD;
z.msg = "invalid block type";
r = Z_DATA_ERROR;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
break;
case LENS:
while(k<(32)){
if(n!=0){
r=Z_OK;
}
else{
bitb=b; bitk=k;
z.avail_in=n;
z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
if ((((~b) >>> 16) & 0xffff) != (b & 0xffff)){
mode = BAD;
z.msg = "invalid stored block lengths";
r = Z_DATA_ERROR;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
left = (b & 0xffff);
b = k = 0; // dump bits
mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE);
break;
case STORED:
if (n == 0){
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
if(m==0){
if(q==end&&read!=0){
q=0; m=(int)(q<read?read-q-1:end-q);
}
if(m==0){
write=q;
r=inflate_flush(r);
q=write;m=(int)(q<read?read-q-1:end-q);
if(q==end&&read!=0){
q=0; m=(int)(q<read?read-q-1:end-q);
}
if(m==0){
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
}
}
r=Z_OK;
t = left;
if(t>n) t = n;
if(t>m) t = m;
System.arraycopy(z.next_in, p, window, q, t);
p += t; n -= t;
q += t; m -= t;
if ((left -= t) != 0)
break;
mode = last!=0 ? DRY : TYPE;
break;
case TABLE:
while(k<(14)){
if(n!=0){
r=Z_OK;
}
else{
bitb=b; bitk=k;
z.avail_in=n;
z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
table = t = (b & 0x3fff);
if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
{
mode = BAD;
z.msg = "too many length or distance symbols";
r = Z_DATA_ERROR;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
if(blens==null || blens.length<t){
blens=new int[t];
}
else{
for(int i=0; i<t; i++){blens[i]=0;}
}
{b>>>=(14);k-=(14);}
index = 0;
mode = BTREE;
case BTREE:
while (index < 4 + (table >>> 10)){
while(k<(3)){
if(n!=0){
r=Z_OK;
}
else{
bitb=b; bitk=k;
z.avail_in=n;
z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
blens[border[index++]] = b&7;
{b>>>=(3);k-=(3);}
}
while(index < 19){
blens[border[index++]] = 0;
}
bb[0] = 7;
t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z);
if (t != Z_OK){
r = t;
if (r == Z_DATA_ERROR){
blens=null;
mode = BAD;
}
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
index = 0;
mode = DTREE;
case DTREE:
while (true){
t = table;
if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){
break;
}
int[] h;
int i, j, c;
t = bb[0];
while(k<(t)){
if(n!=0){
r=Z_OK;
}
else{
bitb=b; bitk=k;
z.avail_in=n;
z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
if(tb[0]==-1){
//System.err.println("null...");
}
t=hufts[(tb[0]+(b&inflate_mask[t]))*3+1];
c=hufts[(tb[0]+(b&inflate_mask[t]))*3+2];
if (c < 16){
b>>>=(t);k-=(t);
blens[index++] = c;
}
else { // c == 16..18
i = c == 18 ? 7 : c - 14;
j = c == 18 ? 11 : 3;
while(k<(t+i)){
if(n!=0){
r=Z_OK;
}
else{
bitb=b; bitk=k;
z.avail_in=n;
z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
b>>>=(t);k-=(t);
j += (b & inflate_mask[i]);
b>>>=(i);k-=(i);
i = index;
t = table;
if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
(c == 16 && i < 1)){
blens=null;
mode = BAD;
z.msg = "invalid bit length repeat";
r = Z_DATA_ERROR;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
c = c == 16 ? blens[i-1] : 0;
do{
blens[i++] = c;
}
while (--j!=0);
index = i;
}
}
tb[0]=-1;
{
bl[0] = 9; // must be <= 9 for lookahead assumptions
bd[0] = 6; // must be <= 9 for lookahead assumptions
t = table;
t = inftree.inflate_trees_dynamic(257 + (t & 0x1f),
1 + ((t >> 5) & 0x1f),
blens, bl, bd, tli, tdi, hufts, z);
if (t != Z_OK){
if (t == Z_DATA_ERROR){
blens=null;
mode = BAD;
}
r = t;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
codes.init(bl[0], bd[0], hufts, tli[0], hufts, tdi[0]);
}
mode = CODES;
case CODES:
bitb=b; bitk=k;
z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
if ((r = codes.proc(r)) != Z_STREAM_END){
return inflate_flush(r);
}
r = Z_OK;
codes.free(z);
p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk;
q=write;m=(int)(q<read?read-q-1:end-q);
if (last==0){
mode = TYPE;
break;
}
mode = DRY;
case DRY:
write=q;
r=inflate_flush(r);
q=write; m=(int)(q<read?read-q-1:end-q);
if (read != write){
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
mode = DONE;
case DONE:
r = Z_STREAM_END;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
case BAD:
r = Z_DATA_ERROR;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
default:
r = Z_STREAM_ERROR;
bitb=b; bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
write=q;
return inflate_flush(r);
}
}
}
void free(){
reset();
window=null;
hufts=null;
//ZFREE(z, s);
}
void set_dictionary(byte[] d, int start, int n){
System.arraycopy(d, start, window, 0, n);
read = write = n;
}
// Returns true if inflate is currently at the end of a block generated
// by Z_SYNC_FLUSH or Z_FULL_FLUSH.
int sync_point(){
return mode == LENS ? 1 : 0;
}
// copy as much as possible from the sliding window to the output area
int inflate_flush(int r){
int n;
int p;
int q;
// local copies of source and destination pointers
p = z.next_out_index;
q = read;
// compute number of bytes to copy as far as end of window
n = (int)((q <= write ? write : end) - q);
if(n > z.avail_out) n = z.avail_out;
if(n!=0 && r == Z_BUF_ERROR) r = Z_OK;
// update counters
z.avail_out -= n;
z.total_out += n;
// update check information
if(check && n>0){
z.adler.update(window, q, n);
}
// copy as far as end of window
System.arraycopy(window, q, z.next_out, p, n);
p += n;
q += n;
// see if more to copy at beginning of window
if (q == end){
// wrap pointers
q = 0;
if (write == end)
write = 0;
// compute bytes to copy
n = write - q;
if (n > z.avail_out) n = z.avail_out;
if (n!=0 && r == Z_BUF_ERROR) r = Z_OK;
// update counters
z.avail_out -= n;
z.total_out += n;
// update check information
if(check && n>0){
z.adler.update(window, q, n);
}
// copy
System.arraycopy(window, q, z.next_out, p, n);
p += n;
q += n;
}
// update pointers
z.next_out_index = p;
read = q;
// done
return r;
}
}

@ -0,0 +1,610 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final class InfCodes{
static final private int[] inflate_mask = {
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff,
0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff,
0x00007fff, 0x0000ffff
};
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
// waiting for "i:"=input,
// "o:"=output,
// "x:"=nothing
static final private int START=0; // x: set up for LEN
static final private int LEN=1; // i: get length/literal/eob next
static final private int LENEXT=2; // i: getting length extra (have base)
static final private int DIST=3; // i: get distance next
static final private int DISTEXT=4;// i: getting distance extra
static final private int COPY=5; // o: copying bytes in window, waiting for space
static final private int LIT=6; // o: got literal, waiting for output space
static final private int WASH=7; // o: got eob, possibly still output waiting
static final private int END=8; // x: got eob and all data flushed
static final private int BADCODE=9;// x: got error
int mode; // current inflate_codes mode
// mode dependent information
int len;
int[] tree; // pointer into tree
int tree_index=0;
int need; // bits needed
int lit;
// if EXT or COPY, where and how much
int get; // bits to get for extra
int dist; // distance back to copy from
byte lbits; // ltree bits decoded per branch
byte dbits; // dtree bits decoder per branch
int[] ltree; // literal/length/eob tree
int ltree_index; // literal/length/eob tree
int[] dtree; // distance tree
int dtree_index; // distance tree
private final ZStream z;
private final InfBlocks s;
InfCodes(ZStream z, InfBlocks s){
this.z=z;
this.s=s;
}
void init(int bl, int bd,
int[] tl, int tl_index,
int[] td, int td_index){
mode=START;
lbits=(byte)bl;
dbits=(byte)bd;
ltree=tl;
ltree_index=tl_index;
dtree = td;
dtree_index=td_index;
tree=null;
}
int proc(int r){
int j; // temporary storage
int[] t; // temporary pointer
int tindex; // temporary pointer
int e; // extra bits or operation
int b=0; // bit buffer
int k=0; // bits in bit buffer
int p=0; // input data pointer
int n; // bytes available there
int q; // output window write pointer
int m; // bytes to end of window or read pointer
int f; // pointer to copy strings from
// copy input/output information to locals (UPDATE macro restores)
p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk;
q=s.write;m=q<s.read?s.read-q-1:s.end-q;
// process input and output based on current state
while (true){
switch (mode){
// waiting for "i:"=input, "o:"=output, "x:"=nothing
case START: // x: set up for LEN
if (m >= 258 && n >= 10){
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
r = inflate_fast(lbits, dbits,
ltree, ltree_index,
dtree, dtree_index,
s, z);
p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk;
q=s.write;m=q<s.read?s.read-q-1:s.end-q;
if (r != Z_OK){
mode = r == Z_STREAM_END ? WASH : BADCODE;
break;
}
}
need = lbits;
tree = ltree;
tree_index=ltree_index;
mode = LEN;
case LEN: // i: get length/literal/eob next
j = need;
while(k<(j)){
if(n!=0)r=Z_OK;
else{
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
n--;
b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
tindex=(tree_index+(b&inflate_mask[j]))*3;
b>>>=(tree[tindex+1]);
k-=(tree[tindex+1]);
e=tree[tindex];
if(e == 0){ // literal
lit = tree[tindex+2];
mode = LIT;
break;
}
if((e & 16)!=0 ){ // length
get = e & 15;
len = tree[tindex+2];
mode = LENEXT;
break;
}
if ((e & 64) == 0){ // next table
need = e;
tree_index = tindex/3+tree[tindex+2];
break;
}
if ((e & 32)!=0){ // end of block
mode = WASH;
break;
}
mode = BADCODE; // invalid code
z.msg = "invalid literal/length code";
r = Z_DATA_ERROR;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
case LENEXT: // i: getting length extra (have base)
j = get;
while(k<(j)){
if(n!=0)r=Z_OK;
else{
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
n--; b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
len += (b & inflate_mask[j]);
b>>=j;
k-=j;
need = dbits;
tree = dtree;
tree_index=dtree_index;
mode = DIST;
case DIST: // i: get distance next
j = need;
while(k<(j)){
if(n!=0)r=Z_OK;
else{
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
n--; b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
tindex=(tree_index+(b & inflate_mask[j]))*3;
b>>=tree[tindex+1];
k-=tree[tindex+1];
e = (tree[tindex]);
if((e & 16)!=0){ // distance
get = e & 15;
dist = tree[tindex+2];
mode = DISTEXT;
break;
}
if ((e & 64) == 0){ // next table
need = e;
tree_index = tindex/3 + tree[tindex+2];
break;
}
mode = BADCODE; // invalid code
z.msg = "invalid distance code";
r = Z_DATA_ERROR;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
case DISTEXT: // i: getting distance extra
j = get;
while(k<(j)){
if(n!=0)r=Z_OK;
else{
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
n--; b|=(z.next_in[p++]&0xff)<<k;
k+=8;
}
dist += (b & inflate_mask[j]);
b>>=j;
k-=j;
mode = COPY;
case COPY: // o: copying bytes in window, waiting for space
f = q - dist;
while(f < 0){ // modulo window size-"while" instead
f += s.end; // of "if" handles invalid distances
}
while (len!=0){
if(m==0){
if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;}
if(m==0){
s.write=q; r=s.inflate_flush(r);
q=s.write;m=q<s.read?s.read-q-1:s.end-q;
if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;}
if(m==0){
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
}
}
s.window[q++]=s.window[f++]; m--;
if (f == s.end)
f = 0;
len--;
}
mode = START;
break;
case LIT: // o: got literal, waiting for output space
if(m==0){
if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;}
if(m==0){
s.write=q; r=s.inflate_flush(r);
q=s.write;m=q<s.read?s.read-q-1:s.end-q;
if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;}
if(m==0){
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
}
}
r=Z_OK;
s.window[q++]=(byte)lit; m--;
mode = START;
break;
case WASH: // o: got eob, possibly more output
if (k > 7){ // return unused byte, if any
k -= 8;
n++;
p--; // can always return one
}
s.write=q; r=s.inflate_flush(r);
q=s.write;m=q<s.read?s.read-q-1:s.end-q;
if (s.read != s.write){
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
mode = END;
case END:
r = Z_STREAM_END;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
case BADCODE: // x: got error
r = Z_DATA_ERROR;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
default:
r = Z_STREAM_ERROR;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return s.inflate_flush(r);
}
}
}
void free(ZStream z){
// ZFREE(z, c);
}
// Called with number of bytes left to write in window at least 258
// (the maximum string length) and number of input bytes available
// at least ten. The ten bytes are six bytes for the longest length/
// distance pair plus four bytes for overloading the bit buffer.
int inflate_fast(int bl, int bd,
int[] tl, int tl_index,
int[] td, int td_index,
InfBlocks s, ZStream z){
int t; // temporary pointer
int[] tp; // temporary pointer
int tp_index; // temporary pointer
int e; // extra bits or operation
int b; // bit buffer
int k; // bits in bit buffer
int p; // input data pointer
int n; // bytes available there
int q; // output window write pointer
int m; // bytes to end of window or read pointer
int ml; // mask for literal/length tree
int md; // mask for distance tree
int c; // bytes to copy
int d; // distance back to copy from
int r; // copy source pointer
int tp_index_t_3; // (tp_index+t)*3
// load input, output, bit values
p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk;
q=s.write;m=q<s.read?s.read-q-1:s.end-q;
// initialize masks
ml = inflate_mask[bl];
md = inflate_mask[bd];
// do until not enough input or output space for fast loop
do { // assume called with m >= 258 && n >= 10
// get literal/length code
while(k<(20)){ // max bits for literal/length code
n--;
b|=(z.next_in[p++]&0xff)<<k;k+=8;
}
t= b&ml;
tp=tl;
tp_index=tl_index;
tp_index_t_3=(tp_index+t)*3;
if ((e = tp[tp_index_t_3]) == 0){
b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]);
s.window[q++] = (byte)tp[tp_index_t_3+2];
m--;
continue;
}
do {
b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]);
if((e&16)!=0){
e &= 15;
c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]);
b>>=e; k-=e;
// decode distance base of block to copy
while(k<(15)){ // max bits for distance code
n--;
b|=(z.next_in[p++]&0xff)<<k;k+=8;
}
t= b&md;
tp=td;
tp_index=td_index;
tp_index_t_3=(tp_index+t)*3;
e = tp[tp_index_t_3];
do {
b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]);
if((e&16)!=0){
// get extra bits to add to distance base
e &= 15;
while(k<(e)){ // get extra bits (up to 13)
n--;
b|=(z.next_in[p++]&0xff)<<k;k+=8;
}
d = tp[tp_index_t_3+2] + (b&inflate_mask[e]);
b>>=(e); k-=(e);
// do the copy
m -= c;
if (q >= d){ // offset before dest
// just copy
r=q-d;
if(q-r>0 && 2>(q-r)){
s.window[q++]=s.window[r++]; // minimum count is three,
s.window[q++]=s.window[r++]; // so unroll loop a little
c-=2;
}
else{
System.arraycopy(s.window, r, s.window, q, 2);
q+=2; r+=2; c-=2;
}
}
else{ // else offset after destination
r=q-d;
do{
r+=s.end; // force pointer in window
}while(r<0); // covers invalid distances
e=s.end-r;
if(c>e){ // if source crosses,
c-=e; // wrapped copy
if(q-r>0 && e>(q-r)){
do{s.window[q++] = s.window[r++];}
while(--e!=0);
}
else{
System.arraycopy(s.window, r, s.window, q, e);
q+=e; r+=e; e=0;
}
r = 0; // copy rest from start of window
}
}
// copy all or what's left
if(q-r>0 && c>(q-r)){
do{s.window[q++] = s.window[r++];}
while(--c!=0);
}
else{
System.arraycopy(s.window, r, s.window, q, c);
q+=c; r+=c; c=0;
}
break;
}
else if((e&64)==0){
t+=tp[tp_index_t_3+2];
t+=(b&inflate_mask[e]);
tp_index_t_3=(tp_index+t)*3;
e=tp[tp_index_t_3];
}
else{
z.msg = "invalid distance code";
c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return Z_DATA_ERROR;
}
}
while(true);
break;
}
if((e&64)==0){
t+=tp[tp_index_t_3+2];
t+=(b&inflate_mask[e]);
tp_index_t_3=(tp_index+t)*3;
if((e=tp[tp_index_t_3])==0){
b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]);
s.window[q++]=(byte)tp[tp_index_t_3+2];
m--;
break;
}
}
else if((e&32)!=0){
c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return Z_STREAM_END;
}
else{
z.msg="invalid literal/length code";
c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return Z_DATA_ERROR;
}
}
while(true);
}
while(m>=258 && n>= 10);
// not enough input or output--restore pointers and return
c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;
s.bitb=b;s.bitk=k;
z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p;
s.write=q;
return Z_OK;
}
}

@ -0,0 +1,518 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final class InfTree{
static final private int MANY=1440;
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
static final int fixed_bl = 9;
static final int fixed_bd = 5;
static final int[] fixed_tl = {
96,7,256, 0,8,80, 0,8,16, 84,8,115,
82,7,31, 0,8,112, 0,8,48, 0,9,192,
80,7,10, 0,8,96, 0,8,32, 0,9,160,
0,8,0, 0,8,128, 0,8,64, 0,9,224,
80,7,6, 0,8,88, 0,8,24, 0,9,144,
83,7,59, 0,8,120, 0,8,56, 0,9,208,
81,7,17, 0,8,104, 0,8,40, 0,9,176,
0,8,8, 0,8,136, 0,8,72, 0,9,240,
80,7,4, 0,8,84, 0,8,20, 85,8,227,
83,7,43, 0,8,116, 0,8,52, 0,9,200,
81,7,13, 0,8,100, 0,8,36, 0,9,168,
0,8,4, 0,8,132, 0,8,68, 0,9,232,
80,7,8, 0,8,92, 0,8,28, 0,9,152,
84,7,83, 0,8,124, 0,8,60, 0,9,216,
82,7,23, 0,8,108, 0,8,44, 0,9,184,
0,8,12, 0,8,140, 0,8,76, 0,9,248,
80,7,3, 0,8,82, 0,8,18, 85,8,163,
83,7,35, 0,8,114, 0,8,50, 0,9,196,
81,7,11, 0,8,98, 0,8,34, 0,9,164,
0,8,2, 0,8,130, 0,8,66, 0,9,228,
80,7,7, 0,8,90, 0,8,26, 0,9,148,
84,7,67, 0,8,122, 0,8,58, 0,9,212,
82,7,19, 0,8,106, 0,8,42, 0,9,180,
0,8,10, 0,8,138, 0,8,74, 0,9,244,
80,7,5, 0,8,86, 0,8,22, 192,8,0,
83,7,51, 0,8,118, 0,8,54, 0,9,204,
81,7,15, 0,8,102, 0,8,38, 0,9,172,
0,8,6, 0,8,134, 0,8,70, 0,9,236,
80,7,9, 0,8,94, 0,8,30, 0,9,156,
84,7,99, 0,8,126, 0,8,62, 0,9,220,
82,7,27, 0,8,110, 0,8,46, 0,9,188,
0,8,14, 0,8,142, 0,8,78, 0,9,252,
96,7,256, 0,8,81, 0,8,17, 85,8,131,
82,7,31, 0,8,113, 0,8,49, 0,9,194,
80,7,10, 0,8,97, 0,8,33, 0,9,162,
0,8,1, 0,8,129, 0,8,65, 0,9,226,
80,7,6, 0,8,89, 0,8,25, 0,9,146,
83,7,59, 0,8,121, 0,8,57, 0,9,210,
81,7,17, 0,8,105, 0,8,41, 0,9,178,
0,8,9, 0,8,137, 0,8,73, 0,9,242,
80,7,4, 0,8,85, 0,8,21, 80,8,258,
83,7,43, 0,8,117, 0,8,53, 0,9,202,
81,7,13, 0,8,101, 0,8,37, 0,9,170,
0,8,5, 0,8,133, 0,8,69, 0,9,234,
80,7,8, 0,8,93, 0,8,29, 0,9,154,
84,7,83, 0,8,125, 0,8,61, 0,9,218,
82,7,23, 0,8,109, 0,8,45, 0,9,186,
0,8,13, 0,8,141, 0,8,77, 0,9,250,
80,7,3, 0,8,83, 0,8,19, 85,8,195,
83,7,35, 0,8,115, 0,8,51, 0,9,198,
81,7,11, 0,8,99, 0,8,35, 0,9,166,
0,8,3, 0,8,131, 0,8,67, 0,9,230,
80,7,7, 0,8,91, 0,8,27, 0,9,150,
84,7,67, 0,8,123, 0,8,59, 0,9,214,
82,7,19, 0,8,107, 0,8,43, 0,9,182,
0,8,11, 0,8,139, 0,8,75, 0,9,246,
80,7,5, 0,8,87, 0,8,23, 192,8,0,
83,7,51, 0,8,119, 0,8,55, 0,9,206,
81,7,15, 0,8,103, 0,8,39, 0,9,174,
0,8,7, 0,8,135, 0,8,71, 0,9,238,
80,7,9, 0,8,95, 0,8,31, 0,9,158,
84,7,99, 0,8,127, 0,8,63, 0,9,222,
82,7,27, 0,8,111, 0,8,47, 0,9,190,
0,8,15, 0,8,143, 0,8,79, 0,9,254,
96,7,256, 0,8,80, 0,8,16, 84,8,115,
82,7,31, 0,8,112, 0,8,48, 0,9,193,
80,7,10, 0,8,96, 0,8,32, 0,9,161,
0,8,0, 0,8,128, 0,8,64, 0,9,225,
80,7,6, 0,8,88, 0,8,24, 0,9,145,
83,7,59, 0,8,120, 0,8,56, 0,9,209,
81,7,17, 0,8,104, 0,8,40, 0,9,177,
0,8,8, 0,8,136, 0,8,72, 0,9,241,
80,7,4, 0,8,84, 0,8,20, 85,8,227,
83,7,43, 0,8,116, 0,8,52, 0,9,201,
81,7,13, 0,8,100, 0,8,36, 0,9,169,
0,8,4, 0,8,132, 0,8,68, 0,9,233,
80,7,8, 0,8,92, 0,8,28, 0,9,153,
84,7,83, 0,8,124, 0,8,60, 0,9,217,
82,7,23, 0,8,108, 0,8,44, 0,9,185,
0,8,12, 0,8,140, 0,8,76, 0,9,249,
80,7,3, 0,8,82, 0,8,18, 85,8,163,
83,7,35, 0,8,114, 0,8,50, 0,9,197,
81,7,11, 0,8,98, 0,8,34, 0,9,165,
0,8,2, 0,8,130, 0,8,66, 0,9,229,
80,7,7, 0,8,90, 0,8,26, 0,9,149,
84,7,67, 0,8,122, 0,8,58, 0,9,213,
82,7,19, 0,8,106, 0,8,42, 0,9,181,
0,8,10, 0,8,138, 0,8,74, 0,9,245,
80,7,5, 0,8,86, 0,8,22, 192,8,0,
83,7,51, 0,8,118, 0,8,54, 0,9,205,
81,7,15, 0,8,102, 0,8,38, 0,9,173,
0,8,6, 0,8,134, 0,8,70, 0,9,237,
80,7,9, 0,8,94, 0,8,30, 0,9,157,
84,7,99, 0,8,126, 0,8,62, 0,9,221,
82,7,27, 0,8,110, 0,8,46, 0,9,189,
0,8,14, 0,8,142, 0,8,78, 0,9,253,
96,7,256, 0,8,81, 0,8,17, 85,8,131,
82,7,31, 0,8,113, 0,8,49, 0,9,195,
80,7,10, 0,8,97, 0,8,33, 0,9,163,
0,8,1, 0,8,129, 0,8,65, 0,9,227,
80,7,6, 0,8,89, 0,8,25, 0,9,147,
83,7,59, 0,8,121, 0,8,57, 0,9,211,
81,7,17, 0,8,105, 0,8,41, 0,9,179,
0,8,9, 0,8,137, 0,8,73, 0,9,243,
80,7,4, 0,8,85, 0,8,21, 80,8,258,
83,7,43, 0,8,117, 0,8,53, 0,9,203,
81,7,13, 0,8,101, 0,8,37, 0,9,171,
0,8,5, 0,8,133, 0,8,69, 0,9,235,
80,7,8, 0,8,93, 0,8,29, 0,9,155,
84,7,83, 0,8,125, 0,8,61, 0,9,219,
82,7,23, 0,8,109, 0,8,45, 0,9,187,
0,8,13, 0,8,141, 0,8,77, 0,9,251,
80,7,3, 0,8,83, 0,8,19, 85,8,195,
83,7,35, 0,8,115, 0,8,51, 0,9,199,
81,7,11, 0,8,99, 0,8,35, 0,9,167,
0,8,3, 0,8,131, 0,8,67, 0,9,231,
80,7,7, 0,8,91, 0,8,27, 0,9,151,
84,7,67, 0,8,123, 0,8,59, 0,9,215,
82,7,19, 0,8,107, 0,8,43, 0,9,183,
0,8,11, 0,8,139, 0,8,75, 0,9,247,
80,7,5, 0,8,87, 0,8,23, 192,8,0,
83,7,51, 0,8,119, 0,8,55, 0,9,207,
81,7,15, 0,8,103, 0,8,39, 0,9,175,
0,8,7, 0,8,135, 0,8,71, 0,9,239,
80,7,9, 0,8,95, 0,8,31, 0,9,159,
84,7,99, 0,8,127, 0,8,63, 0,9,223,
82,7,27, 0,8,111, 0,8,47, 0,9,191,
0,8,15, 0,8,143, 0,8,79, 0,9,255
};
static final int[] fixed_td = {
80,5,1, 87,5,257, 83,5,17, 91,5,4097,
81,5,5, 89,5,1025, 85,5,65, 93,5,16385,
80,5,3, 88,5,513, 84,5,33, 92,5,8193,
82,5,9, 90,5,2049, 86,5,129, 192,5,24577,
80,5,2, 87,5,385, 83,5,25, 91,5,6145,
81,5,7, 89,5,1537, 85,5,97, 93,5,24577,
80,5,4, 88,5,769, 84,5,49, 92,5,12289,
82,5,13, 90,5,3073, 86,5,193, 192,5,24577
};
// Tables for deflate from PKZIP's appnote.txt.
static final int[] cplens = { // Copy lengths for literal codes 257..285
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
};
// see note #13 above about 258
static final int[] cplext = { // Extra bits for literal codes 257..285
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid
};
static final int[] cpdist = { // Copy offsets for distance codes 0..29
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577
};
static final int[] cpdext = { // Extra bits for distance codes
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13};
// If BMAX needs to be larger than 16, then h and x[] should be uLong.
static final int BMAX=15; // maximum bit length of any code
int[] hn = null; // hufts used in space
int[] v = null; // work area for huft_build
int[] c = null; // bit length count table
int[] r = null; // table entry for structure assignment
int[] u = null; // table stack
int[] x = null; // bit offsets, then code stack
private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX)
int bindex,
int n, // number of codes (assumed <= 288)
int s, // number of simple-valued codes (0..s-1)
int[] d, // list of base values for non-simple codes
int[] e, // list of extra bits for non-simple codes
int[] t, // result: starting table
int[] m, // maximum lookup bits, returns actual
int[] hp,// space for trees
int[] hn,// hufts used in space
int[] v // working area: values in order of bit length
){
// Given a list of code lengths and a maximum table size, make a set of
// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
// if the given code set is incomplete (the tables are still built in this
// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
// lengths), or Z_MEM_ERROR if not enough memory.
int a; // counter for codes of length k
int f; // i repeats in table every f entries
int g; // maximum code length
int h; // table level
int i; // counter, current code
int j; // counter
int k; // number of bits in current code
int l; // bits per table (returned in m)
int mask; // (1 << w) - 1, to avoid cc -O bug on HP
int p; // pointer into c[], b[], or v[]
int q; // points to current table
int w; // bits before this table == (l * h)
int xp; // pointer into x
int y; // number of dummy codes added
int z; // number of entries in current table
// Generate counts for each bit length
p = 0; i = n;
do {
c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX
}while(i!=0);
if(c[0] == n){ // null input--all zero length codes
t[0] = -1;
m[0] = 0;
return Z_OK;
}
// Find minimum and maximum length, bound *m by those
l = m[0];
for (j = 1; j <= BMAX; j++)
if(c[j]!=0) break;
k = j; // minimum code length
if(l < j){
l = j;
}
for (i = BMAX; i!=0; i--){
if(c[i]!=0) break;
}
g = i; // maximum code length
if(l > i){
l = i;
}
m[0] = l;
// Adjust last length count to fill out codes, if needed
for (y = 1 << j; j < i; j++, y <<= 1){
if ((y -= c[j]) < 0){
return Z_DATA_ERROR;
}
}
if ((y -= c[i]) < 0){
return Z_DATA_ERROR;
}
c[i] += y;
// Generate starting offsets into the value table for each length
x[1] = j = 0;
p = 1; xp = 2;
while (--i!=0) { // note that i == g from above
x[xp] = (j += c[p]);
xp++;
p++;
}
// Make a table of values in order of bit lengths
i = 0; p = 0;
do {
if ((j = b[bindex+p]) != 0){
v[x[j]++] = i;
}
p++;
}
while (++i < n);
n = x[g]; // set n to length of v
// Generate the Huffman codes and for each, make the table entries
x[0] = i = 0; // first Huffman code is zero
p = 0; // grab values in bit order
h = -1; // no tables yet--level -1
w = -l; // bits decoded == (l * h)
u[0] = 0; // just to keep compilers happy
q = 0; // ditto
z = 0; // ditto
// go through the bit lengths (k already is bits in shortest code)
for (; k <= g; k++){
a = c[k];
while (a--!=0){
// here i is the Huffman code of length k bits for value *p
// make tables up to required level
while (k > w + l){
h++;
w += l; // previous table always l bits
// compute minimum size table less than or equal to l bits
z = g - w;
z = (z > l) ? l : z; // table size upper limit
if((f=1<<(j=k-w))>a+1){ // try a k-w bit table
// too few codes for k-w bit table
f -= a + 1; // deduct codes from patterns left
xp = k;
if(j < z){
while (++j < z){ // try smaller tables up to z bits
if((f <<= 1) <= c[++xp])
break; // enough codes to use up j bits
f -= c[xp]; // else deduct codes from patterns
}
}
}
z = 1 << j; // table entries for j-bit table
// allocate new table
if (hn[0] + z > MANY){ // (note: doesn't matter for fixed)
return Z_DATA_ERROR; // overflow of MANY
}
u[h] = q = /*hp+*/ hn[0]; // DEBUG
hn[0] += z;
// connect to last table, if there is one
if(h!=0){
x[h]=i; // save pattern for backing up
r[0]=(byte)j; // bits in this table
r[1]=(byte)l; // bits to dump before this table
j=i>>>(w - l);
r[2] = (int)(q - u[h-1] - j); // offset to this table
System.arraycopy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table
}
else{
t[0] = q; // first table is returned result
}
}
// set up table entry in r
r[1] = (byte)(k - w);
if (p >= n){
r[0] = 128 + 64; // out of values--invalid code
}
else if (v[p] < s){
r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block
r[2] = v[p++]; // simple code is just the value
}
else{
r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists
r[2]=d[v[p++] - s];
}
// fill code-like entries with r
f=1<<(k-w);
for (j=i>>>w;j<z;j+=f){
System.arraycopy(r, 0, hp, (q+j)*3, 3);
}
// backwards increment the k-bit code i
for (j = 1 << (k - 1); (i & j)!=0; j >>>= 1){
i ^= j;
}
i ^= j;
// backup over finished tables
mask = (1 << w) - 1; // needed on HP, cc -O bug
while ((i & mask) != x[h]){
h--; // don't need to update q
w -= l;
mask = (1 << w) - 1;
}
}
}
// Return Z_BUF_ERROR if we were given an incomplete table
return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
}
int inflate_trees_bits(int[] c, // 19 code lengths
int[] bb, // bits tree desired/actual depth
int[] tb, // bits tree result
int[] hp, // space for trees
ZStream z // for messages
){
int result;
initWorkArea(19);
hn[0]=0;
result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
if(result == Z_DATA_ERROR){
z.msg = "oversubscribed dynamic bit lengths tree";
}
else if(result == Z_BUF_ERROR || bb[0] == 0){
z.msg = "incomplete dynamic bit lengths tree";
result = Z_DATA_ERROR;
}
return result;
}
int inflate_trees_dynamic(int nl, // number of literal/length codes
int nd, // number of distance codes
int[] c, // that many (total) code lengths
int[] bl, // literal desired/actual bit depth
int[] bd, // distance desired/actual bit depth
int[] tl, // literal/length tree result
int[] td, // distance tree result
int[] hp, // space for trees
ZStream z // for messages
){
int result;
// build literal/length tree
initWorkArea(288);
hn[0]=0;
result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v);
if (result != Z_OK || bl[0] == 0){
if(result == Z_DATA_ERROR){
z.msg = "oversubscribed literal/length tree";
}
else if (result != Z_MEM_ERROR){
z.msg = "incomplete literal/length tree";
result = Z_DATA_ERROR;
}
return result;
}
// build distance tree
initWorkArea(288);
result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v);
if (result != Z_OK || (bd[0] == 0 && nl > 257)){
if (result == Z_DATA_ERROR){
z.msg = "oversubscribed distance tree";
}
else if (result == Z_BUF_ERROR) {
z.msg = "incomplete distance tree";
result = Z_DATA_ERROR;
}
else if (result != Z_MEM_ERROR){
z.msg = "empty distance tree with lengths";
result = Z_DATA_ERROR;
}
return result;
}
return Z_OK;
}
static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth
int[] bd, //distance desired/actual bit depth
int[][] tl,//literal/length tree result
int[][] td,//distance tree result
ZStream z //for memory allocation
){
bl[0]=fixed_bl;
bd[0]=fixed_bd;
tl[0]=fixed_tl;
td[0]=fixed_td;
return Z_OK;
}
private void initWorkArea(int vsize){
if(hn==null){
hn=new int[1];
v=new int[vsize];
c=new int[BMAX+1];
r=new int[3];
u=new int[BMAX];
x=new int[BMAX+1];
}
if(v.length<vsize){ v=new int[vsize]; }
for(int i=0; i<vsize; i++){v[i]=0;}
for(int i=0; i<BMAX+1; i++){c[i]=0;}
for(int i=0; i<3; i++){r[i]=0;}
System.arraycopy(c, 0, u, 0, BMAX);
System.arraycopy(c, 0, x, 0, BMAX+1);
}
}

@ -0,0 +1,751 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final class Inflate{
static final private int MAX_WBITS=15; // 32K LZ77 window
// preset dictionary flag in zlib header
static final private int PRESET_DICT=0x20;
static final int Z_NO_FLUSH=0;
static final int Z_PARTIAL_FLUSH=1;
static final int Z_SYNC_FLUSH=2;
static final int Z_FULL_FLUSH=3;
static final int Z_FINISH=4;
static final private int Z_DEFLATED=8;
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
static final private int METHOD=0; // waiting for method byte
static final private int FLAG=1; // waiting for flag byte
static final private int DICT4=2; // four dictionary check bytes to go
static final private int DICT3=3; // three dictionary check bytes to go
static final private int DICT2=4; // two dictionary check bytes to go
static final private int DICT1=5; // one dictionary check byte to go
static final private int DICT0=6; // waiting for inflateSetDictionary
static final private int BLOCKS=7; // decompressing blocks
static final private int CHECK4=8; // four check bytes to go
static final private int CHECK3=9; // three check bytes to go
static final private int CHECK2=10; // two check bytes to go
static final private int CHECK1=11; // one check byte to go
static final private int DONE=12; // finished check, done
static final private int BAD=13; // got an error--stay here
static final private int HEAD=14;
static final private int LENGTH=15;
static final private int TIME=16;
static final private int OS=17;
static final private int EXLEN=18;
static final private int EXTRA=19;
static final private int NAME=20;
static final private int COMMENT=21;
static final private int HCRC=22;
static final private int FLAGS=23;
static final int INFLATE_ANY=0x40000000;
int mode; // current inflate mode
// mode dependent information
int method; // if FLAGS, method byte
// if CHECK, check values to compare
long was = -1; // computed check value
long need; // stream check value
// if BAD, inflateSync's marker bytes count
int marker;
// mode independent information
int wrap; // flag for no wrapper
// 0: no wrapper
// 1: zlib header
// 2: gzip header
// 4: auto detection
int wbits; // log2(window size) (8..15, defaults to 15)
InfBlocks blocks; // current inflate_blocks state
private final ZStream z;
private int flags;
private int need_bytes = -1;
private byte[] crcbuf=new byte[4];
GZIPHeader gheader = null;
int inflateReset(){
if(z == null) return Z_STREAM_ERROR;
z.total_in = z.total_out = 0;
z.msg = null;
this.mode = HEAD;
this.need_bytes = -1;
this.blocks.reset();
return Z_OK;
}
int inflateEnd(){
if(blocks != null){
blocks.free();
}
return Z_OK;
}
Inflate(ZStream z){
this.z=z;
}
int inflateInit(int w){
z.msg = null;
blocks = null;
// handle undocumented wrap option (no zlib header or check)
wrap = 0;
if(w < 0){
w = - w;
}
else if((w&INFLATE_ANY) != 0){
wrap = 4;
w &= ~INFLATE_ANY;
if(w < 48)
w &= 15;
}
else if((w & ~31) != 0) { // for example, DEF_WBITS + 32
wrap = 4; // zlib and gzip wrapped data should be accepted.
w &= 15;
}
else {
wrap = (w >> 4) + 1;
if(w < 48)
w &= 15;
}
if(w<8 ||w>15){
inflateEnd();
return Z_STREAM_ERROR;
}
if(blocks != null && wbits != w){
blocks.free();
blocks=null;
}
// set window size
wbits=w;
this.blocks=new InfBlocks(z, 1<<w);
// reset state
inflateReset();
return Z_OK;
}
int inflate(int f){
int hold = 0;
int r;
int b;
if(z == null || z.next_in == null){
if(f == Z_FINISH && this.mode==HEAD)
return Z_OK;
return Z_STREAM_ERROR;
}
f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
r = Z_BUF_ERROR;
while (true){
switch (this.mode){
case HEAD:
if(wrap==0){
this.mode = BLOCKS;
break;
}
try { r=readBytes(2, r, f); }
catch(Return e){ return e.r; }
if((wrap == 4 || (wrap&2)!=0) &&
this.need == 0x8b1fL) { // gzip header
if(wrap == 4){
wrap = 2;
}
z.adler=new CRC32();
checksum(2, this.need);
if(gheader==null)
gheader=new GZIPHeader();
this.mode = FLAGS;
break;
}
if((wrap&2) != 0){
this.mode = BAD;
z.msg = "incorrect header check";
break;
}
flags = 0;
this.method = ((int)this.need)&0xff;
b=((int)(this.need>>8))&0xff;
if(((wrap&1)==0 || // check if zlib header allowed
(((this.method << 8)+b) % 31)!=0) &&
(this.method&0xf)!=Z_DEFLATED){
if(wrap == 4){
z.next_in_index -= 2;
z.avail_in += 2;
z.total_in -= 2;
wrap = 0;
this.mode = BLOCKS;
break;
}
this.mode = BAD;
z.msg = "incorrect header check";
// since zlib 1.2, it is allowted to inflateSync for this case.
/*
this.marker = 5; // can't try inflateSync
*/
break;
}
if((this.method&0xf)!=Z_DEFLATED){
this.mode = BAD;
z.msg="unknown compression method";
// since zlib 1.2, it is allowted to inflateSync for this case.
/*
this.marker = 5; // can't try inflateSync
*/
break;
}
if(wrap == 4){
wrap = 1;
}
if((this.method>>4)+8>this.wbits){
this.mode = BAD;
z.msg="invalid window size";
// since zlib 1.2, it is allowted to inflateSync for this case.
/*
this.marker = 5; // can't try inflateSync
*/
break;
}
z.adler=new Adler32();
if((b&PRESET_DICT)==0){
this.mode = BLOCKS;
break;
}
this.mode = DICT4;
case DICT4:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L;
this.mode=DICT3;
case DICT3:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L;
this.mode=DICT2;
case DICT2:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L;
this.mode=DICT1;
case DICT1:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need += (z.next_in[z.next_in_index++]&0xffL);
z.adler.reset(this.need);
this.mode = DICT0;
return Z_NEED_DICT;
case DICT0:
this.mode = BAD;
z.msg = "need dictionary";
this.marker = 0; // can try inflateSync
return Z_STREAM_ERROR;
case BLOCKS:
r = this.blocks.proc(r);
if(r == Z_DATA_ERROR){
this.mode = BAD;
this.marker = 0; // can try inflateSync
break;
}
if(r == Z_OK){
r = f;
}
if(r != Z_STREAM_END){
return r;
}
r = f;
this.was=z.adler.getValue();
this.blocks.reset();
if(this.wrap==0){
this.mode=DONE;
break;
}
this.mode=CHECK4;
case CHECK4:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L;
this.mode=CHECK3;
case CHECK3:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L;
this.mode = CHECK2;
case CHECK2:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L;
this.mode = CHECK1;
case CHECK1:
if(z.avail_in==0)return r;r=f;
z.avail_in--; z.total_in++;
this.need+=(z.next_in[z.next_in_index++]&0xffL);
if(flags!=0){ // gzip
this.need = ((this.need&0xff000000)>>24 |
(this.need&0x00ff0000)>>8 |
(this.need&0x0000ff00)<<8 |
(this.need&0x0000ffff)<<24)&0xffffffffL;
}
if(((int)(this.was)) != ((int)(this.need))){
z.msg = "incorrect data check";
// chack is delayed
/*
this.mode = BAD;
this.marker = 5; // can't try inflateSync
break;
*/
}
else if(flags!=0 && gheader!=null){
gheader.crc = this.need;
}
this.mode = LENGTH;
case LENGTH:
if (wrap!=0 && flags!=0) {
try { r=readBytes(4, r, f); }
catch(Return e){ return e.r; }
if(z.msg!=null && z.msg.equals("incorrect data check")){
this.mode = BAD;
this.marker = 5; // can't try inflateSync
break;
}
if (this.need != (z.total_out & 0xffffffffL)) {
z.msg = "incorrect length check";
this.mode = BAD;
break;
}
z.msg = null;
}
else {
if(z.msg!=null && z.msg.equals("incorrect data check")){
this.mode = BAD;
this.marker = 5; // can't try inflateSync
break;
}
}
this.mode = DONE;
case DONE:
return Z_STREAM_END;
case BAD:
return Z_DATA_ERROR;
case FLAGS:
try { r=readBytes(2, r, f); }
catch(Return e){ return e.r; }
flags = ((int)this.need)&0xffff;
if ((flags & 0xff) != Z_DEFLATED) {
z.msg = "unknown compression method";
this.mode = BAD;
break;
}
if ((flags & 0xe000)!=0) {
z.msg = "unknown header flags set";
this.mode = BAD;
break;
}
if ((flags & 0x0200)!=0){
checksum(2, this.need);
}
this.mode = TIME;
case TIME:
try { r=readBytes(4, r, f); }
catch(Return e){ return e.r; }
if(gheader!=null)
gheader.time = this.need;
if ((flags & 0x0200)!=0){
checksum(4, this.need);
}
this.mode = OS;
case OS:
try { r=readBytes(2, r, f); }
catch(Return e){ return e.r; }
if(gheader!=null){
gheader.xflags = ((int)this.need)&0xff;
gheader.os = (((int)this.need)>>8)&0xff;
}
if ((flags & 0x0200)!=0){
checksum(2, this.need);
}
this.mode = EXLEN;
case EXLEN:
if ((flags & 0x0400)!=0) {
try { r=readBytes(2, r, f); }
catch(Return e){ return e.r; }
if(gheader!=null){
gheader.extra = new byte[((int)this.need)&0xffff];
}
if ((flags & 0x0200)!=0){
checksum(2, this.need);
}
}
else if(gheader!=null){
gheader.extra=null;
}
this.mode = EXTRA;
case EXTRA:
if ((flags & 0x0400)!=0) {
try {
r=readBytes(r, f);
if(gheader!=null){
byte[] foo = tmp_string.toByteArray();
tmp_string=null;
if(foo.length == gheader.extra.length){
System.arraycopy(foo, 0, gheader.extra, 0, foo.length);
}
else{
z.msg = "bad extra field length";
this.mode = BAD;
break;
}
}
}
catch(Return e){ return e.r; }
}
else if(gheader!=null){
gheader.extra=null;
}
this.mode = NAME;
case NAME:
if ((flags & 0x0800)!=0) {
try {
r=readString(r, f);
if(gheader!=null){
gheader.name=tmp_string.toByteArray();
}
tmp_string=null;
}
catch(Return e){ return e.r; }
}
else if(gheader!=null){
gheader.name=null;
}
this.mode = COMMENT;
case COMMENT:
if ((flags & 0x1000)!=0) {
try {
r=readString(r, f);
if(gheader!=null){
gheader.comment=tmp_string.toByteArray();
}
tmp_string=null;
}
catch(Return e){ return e.r; }
}
else if(gheader!=null){
gheader.comment=null;
}
this.mode = HCRC;
case HCRC:
if ((flags & 0x0200)!=0) {
try { r=readBytes(2, r, f); }
catch(Return e){ return e.r; }
if(gheader!=null){
gheader.hcrc=(int)(this.need&0xffff);
}
if(this.need != (z.adler.getValue()&0xffffL)){
this.mode = BAD;
z.msg = "header crc mismatch";
this.marker = 5; // can't try inflateSync
break;
}
}
z.adler = new CRC32();
this.mode = BLOCKS;
break;
default:
return Z_STREAM_ERROR;
}
}
}
int inflateSetDictionary(byte[] dictionary, int dictLength){
if(z==null || (this.mode != DICT0 && this.wrap != 0)){
return Z_STREAM_ERROR;
}
int index=0;
int length = dictLength;
if(this.mode==DICT0){
long adler_need=z.adler.getValue();
z.adler.reset();
z.adler.update(dictionary, 0, dictLength);
if(z.adler.getValue()!=adler_need){
return Z_DATA_ERROR;
}
}
z.adler.reset();
if(length >= (1<<this.wbits)){
length = (1<<this.wbits)-1;
index=dictLength - length;
}
this.blocks.set_dictionary(dictionary, index, length);
this.mode = BLOCKS;
return Z_OK;
}
static private byte[] mark = {(byte)0, (byte)0, (byte)0xff, (byte)0xff};
int inflateSync(){
int n; // number of bytes to look at
int p; // pointer to bytes
int m; // number of marker bytes found in a row
long r, w; // temporaries to save total_in and total_out
// set up
if(z == null)
return Z_STREAM_ERROR;
if(this.mode != BAD){
this.mode = BAD;
this.marker = 0;
}
if((n=z.avail_in)==0)
return Z_BUF_ERROR;
p=z.next_in_index;
m=this.marker;
// search
while (n!=0 && m < 4){
if(z.next_in[p] == mark[m]){
m++;
}
else if(z.next_in[p]!=0){
m = 0;
}
else{
m = 4 - m;
}
p++; n--;
}
// restore
z.total_in += p-z.next_in_index;
z.next_in_index = p;
z.avail_in = n;
this.marker = m;
// return no joy or set up to restart on a new block
if(m != 4){
return Z_DATA_ERROR;
}
r=z.total_in; w=z.total_out;
inflateReset();
z.total_in=r; z.total_out = w;
this.mode = BLOCKS;
return Z_OK;
}
// Returns true if inflate is currently at the end of a block generated
// by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
// implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
// but removes the length bytes of the resulting empty stored block. When
// decompressing, PPP checks that at the end of input packet, inflate is
// waiting for these length bytes.
int inflateSyncPoint(){
if(z == null || this.blocks == null)
return Z_STREAM_ERROR;
return this.blocks.sync_point();
}
private int readBytes(int n, int r, int f) throws Return{
if(need_bytes == -1){
need_bytes=n;
this.need=0;
}
while(need_bytes>0){
if(z.avail_in==0){ throw new Return(r); }; r=f;
z.avail_in--; z.total_in++;
this.need = this.need |
((z.next_in[z.next_in_index++]&0xff)<<((n-need_bytes)*8));
need_bytes--;
}
if(n==2){
this.need&=0xffffL;
}
else if(n==4) {
this.need&=0xffffffffL;
}
need_bytes=-1;
return r;
}
class Return extends Exception{
int r;
Return(int r){this.r=r; }
}
private java.io.ByteArrayOutputStream tmp_string = null;
private int readString(int r, int f) throws Return{
if(tmp_string == null){
tmp_string=new java.io.ByteArrayOutputStream();
}
int b=0;
do {
if(z.avail_in==0){ throw new Return(r); }; r=f;
z.avail_in--; z.total_in++;
b = z.next_in[z.next_in_index];
if(b!=0) tmp_string.write(z.next_in, z.next_in_index, 1);
z.adler.update(z.next_in, z.next_in_index, 1);
z.next_in_index++;
}while(b!=0);
return r;
}
private int readBytes(int r, int f) throws Return{
if(tmp_string == null){
tmp_string=new java.io.ByteArrayOutputStream();
}
int b=0;
while(this.need>0){
if(z.avail_in==0){ throw new Return(r); }; r=f;
z.avail_in--; z.total_in++;
b = z.next_in[z.next_in_index];
tmp_string.write(z.next_in, z.next_in_index, 1);
z.adler.update(z.next_in, z.next_in_index, 1);
z.next_in_index++;
this.need--;
}
return r;
}
private void checksum(int n, long v){
for(int i=0; i<n; i++){
crcbuf[i]=(byte)(v&0xff);
v>>=8;
}
z.adler.update(crcbuf, 0, n);
}
public GZIPHeader getGZIPHeader(){
return gheader;
}
boolean inParsingHeader(){
switch(mode){
case HEAD:
case DICT4:
case DICT3:
case DICT2:
case DICT1:
case FLAGS:
case TIME:
case OS:
case EXLEN:
case EXTRA:
case NAME:
case COMMENT:
case HCRC:
return true;
default:
return false;
}
}
}

@ -0,0 +1,168 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final public class Inflater extends ZStream{
static final private int MAX_WBITS=15; // 32K LZ77 window
static final private int DEF_WBITS=MAX_WBITS;
static final private int Z_NO_FLUSH=0;
static final private int Z_PARTIAL_FLUSH=1;
static final private int Z_SYNC_FLUSH=2;
static final private int Z_FULL_FLUSH=3;
static final private int Z_FINISH=4;
static final private int MAX_MEM_LEVEL=9;
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
public Inflater() {
super();
init();
}
public Inflater(JZlib.WrapperType wrapperType) throws GZIPException {
this(DEF_WBITS, wrapperType);
}
public Inflater(int w, JZlib.WrapperType wrapperType) throws GZIPException {
super();
int ret = init(w, wrapperType);
if(ret!=Z_OK)
throw new GZIPException(ret+": "+msg);
}
public Inflater(int w) throws GZIPException {
this(w, false);
}
public Inflater(boolean nowrap) throws GZIPException {
this(DEF_WBITS, nowrap);
}
public Inflater(int w, boolean nowrap) throws GZIPException {
super();
int ret = init(w, nowrap);
if(ret!=Z_OK)
throw new GZIPException(ret+": "+msg);
}
private boolean finished = false;
public int init(){
return init(DEF_WBITS);
}
public int init(JZlib.WrapperType wrapperType){
return init(DEF_WBITS, wrapperType);
}
public int init(int w, JZlib.WrapperType wrapperType) {
boolean nowrap = false;
if(wrapperType == JZlib.W_NONE){
nowrap = true;
}
else if(wrapperType == JZlib.W_GZIP) {
w += 16;
}
else if(wrapperType == JZlib.W_ANY) {
w |= Inflate.INFLATE_ANY;
}
else if(wrapperType == JZlib.W_ZLIB) {
}
return init(w, nowrap);
}
public int init(boolean nowrap){
return init(DEF_WBITS, nowrap);
}
public int init(int w){
return init(w, false);
}
public int init(int w, boolean nowrap){
finished = false;
istate=new Inflate(this);
return istate.inflateInit(nowrap?-w:w);
}
public int inflate(int f){
if(istate==null) return Z_STREAM_ERROR;
int ret = istate.inflate(f);
if(ret == Z_STREAM_END)
finished = true;
return ret;
}
public int end(){
finished = true;
if(istate==null) return Z_STREAM_ERROR;
int ret=istate.inflateEnd();
// istate = null;
return ret;
}
public int sync(){
if(istate == null)
return Z_STREAM_ERROR;
return istate.inflateSync();
}
public int syncPoint(){
if(istate == null)
return Z_STREAM_ERROR;
return istate.inflateSyncPoint();
}
public int setDictionary(byte[] dictionary, int dictLength){
if(istate == null)
return Z_STREAM_ERROR;
return istate.inflateSetDictionary(dictionary, dictLength);
}
public boolean finished(){
return istate.mode==12 /*DONE*/;
}
}

@ -0,0 +1,247 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jzlib;
import java.io.*;
public class InflaterInputStream extends FilterInputStream {
protected final Inflater inflater;
protected byte[] buf;
private boolean closed = false;
private boolean eof = false;
private boolean close_in = true;
protected static final int DEFAULT_BUFSIZE = 512;
public InflaterInputStream(InputStream in) throws IOException {
this(in, false);
}
public InflaterInputStream(InputStream in, boolean nowrap) throws IOException {
this(in, new Inflater(nowrap));
myinflater = true;
}
public InflaterInputStream(InputStream in, Inflater inflater) throws IOException {
this(in, inflater, DEFAULT_BUFSIZE);
}
public InflaterInputStream(InputStream in,
Inflater inflater, int size) throws IOException {
this(in, inflater, size, true);
}
public InflaterInputStream(InputStream in,
Inflater inflater,
int size, boolean close_in) throws IOException {
super(in);
if (in == null || inflater == null) {
throw new NullPointerException();
}
else if (size <= 0) {
throw new IllegalArgumentException("buffer size must be greater than 0");
}
this.inflater = inflater;
buf = new byte[size];
this.close_in = close_in;
}
protected boolean myinflater = false;
private byte[] byte1 = new byte[1];
public int read() throws IOException {
if (closed) { throw new IOException("Stream closed"); }
return read(byte1, 0, 1) == -1 ? -1 : byte1[0] & 0xff;
}
public int read(byte[] b, int off, int len) throws IOException {
if (closed) { throw new IOException("Stream closed"); }
if (b == null) {
throw new NullPointerException();
}
else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
else if (len == 0) {
return 0;
}
else if (eof) {
return -1;
}
int n = 0;
inflater.setOutput(b, off, len);
while(!eof) {
if(inflater.avail_in==0)
fill();
int err = inflater.inflate(JZlib.Z_NO_FLUSH);
n += inflater.next_out_index - off;
off = inflater.next_out_index;
switch(err) {
case JZlib.Z_DATA_ERROR:
throw new IOException(inflater.msg);
case JZlib.Z_STREAM_END:
case JZlib.Z_NEED_DICT:
eof = true;
if(err == JZlib.Z_NEED_DICT)
return -1;
break;
default:
}
if(inflater.avail_out==0)
break;
}
return n;
}
public int available() throws IOException {
if (closed) { throw new IOException("Stream closed"); }
if (eof) {
return 0;
}
else {
return 1;
}
}
private byte[] b = new byte[512];
public long skip(long n) throws IOException {
if (n < 0) {
throw new IllegalArgumentException("negative skip length");
}
if (closed) { throw new IOException("Stream closed"); }
int max = (int)Math.min(n, Integer.MAX_VALUE);
int total = 0;
while (total < max) {
int len = max - total;
if (len > b.length) {
len = b.length;
}
len = read(b, 0, len);
if (len == -1) {
eof = true;
break;
}
total += len;
}
return total;
}
public void close() throws IOException {
if (!closed) {
if (myinflater)
inflater.end();
if(close_in)
in.close();
closed = true;
}
}
protected void fill() throws IOException {
if (closed) { throw new IOException("Stream closed"); }
int len = in.read(buf, 0, buf.length);
if (len == -1) {
if(inflater.istate.wrap == 0 &&
!inflater.finished()){
buf[0]=0;
len=1;
}
else if(inflater.istate.was != -1){ // in reading trailer
throw new IOException("footer is not found");
}
else{
throw new EOFException("Unexpected end of ZLIB input stream");
}
}
inflater.setInput(buf, 0, len, true);
}
public boolean markSupported() {
return false;
}
public synchronized void mark(int readlimit) {
}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public long getTotalIn() {
return inflater.getTotalIn();
}
public long getTotalOut() {
return inflater.getTotalOut();
}
public byte[] getAvailIn() {
if(inflater.avail_in<=0)
return null;
byte[] tmp = new byte[inflater.avail_in];
System.arraycopy(inflater.next_in, inflater.next_in_index,
tmp, 0, inflater.avail_in);
return tmp;
}
public void readHeader() throws IOException {
byte[] empty = "".getBytes();
inflater.setInput(empty, 0, 0, false);
inflater.setOutput(empty, 0, 0);
int err = inflater.inflate(JZlib.Z_NO_FLUSH);
if(!inflater.istate.inParsingHeader()){
return;
}
byte[] b1 = new byte[1];
do{
int i = in.read(b1);
if(i<=0)
throw new IOException("no input");
inflater.setInput(b1);
err = inflater.inflate(JZlib.Z_NO_FLUSH);
if(err!=0/*Z_OK*/)
throw new IOException(inflater.msg);
}
while(inflater.istate.inParsingHeader());
}
public Inflater getInflater(){
return inflater;
}
}

@ -0,0 +1,92 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final public class JZlib{
private static final String version="1.1.0";
public static String version(){return version;}
static final public int MAX_WBITS=15; // 32K LZ77 window
static final public int DEF_WBITS=MAX_WBITS;
public enum WrapperType {
NONE, ZLIB, GZIP, ANY
}
public static final WrapperType W_NONE = WrapperType.NONE;
public static final WrapperType W_ZLIB = WrapperType.ZLIB;
public static final WrapperType W_GZIP = WrapperType.GZIP;
public static final WrapperType W_ANY = WrapperType.ANY;
// compression levels
static final public int Z_NO_COMPRESSION=0;
static final public int Z_BEST_SPEED=1;
static final public int Z_BEST_COMPRESSION=9;
static final public int Z_DEFAULT_COMPRESSION=(-1);
// compression strategy
static final public int Z_FILTERED=1;
static final public int Z_HUFFMAN_ONLY=2;
static final public int Z_DEFAULT_STRATEGY=0;
static final public int Z_NO_FLUSH=0;
static final public int Z_PARTIAL_FLUSH=1;
static final public int Z_SYNC_FLUSH=2;
static final public int Z_FULL_FLUSH=3;
static final public int Z_FINISH=4;
static final public int Z_OK=0;
static final public int Z_STREAM_END=1;
static final public int Z_NEED_DICT=2;
static final public int Z_ERRNO=-1;
static final public int Z_STREAM_ERROR=-2;
static final public int Z_DATA_ERROR=-3;
static final public int Z_MEM_ERROR=-4;
static final public int Z_BUF_ERROR=-5;
static final public int Z_VERSION_ERROR=-6;
// The three kinds of block type
static final public byte Z_BINARY = 0;
static final public byte Z_ASCII = 1;
static final public byte Z_UNKNOWN = 2;
public static long adler32_combine(long adler1, long adler2, long len2){
return Adler32.combine(adler1, adler2, len2);
}
public static long crc32_combine(long crc1, long crc2, long len2){
return CRC32.combine(crc1, crc2, len2);
}
}

@ -0,0 +1,148 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final class StaticTree{
static final private int MAX_BITS=15;
static final private int BL_CODES=19;
static final private int D_CODES=30;
static final private int LITERALS=256;
static final private int LENGTH_CODES=29;
static final private int L_CODES=(LITERALS+1+LENGTH_CODES);
// Bit length codes must not exceed MAX_BL_BITS bits
static final int MAX_BL_BITS=7;
static final short[] static_ltree = {
12, 8, 140, 8, 76, 8, 204, 8, 44, 8,
172, 8, 108, 8, 236, 8, 28, 8, 156, 8,
92, 8, 220, 8, 60, 8, 188, 8, 124, 8,
252, 8, 2, 8, 130, 8, 66, 8, 194, 8,
34, 8, 162, 8, 98, 8, 226, 8, 18, 8,
146, 8, 82, 8, 210, 8, 50, 8, 178, 8,
114, 8, 242, 8, 10, 8, 138, 8, 74, 8,
202, 8, 42, 8, 170, 8, 106, 8, 234, 8,
26, 8, 154, 8, 90, 8, 218, 8, 58, 8,
186, 8, 122, 8, 250, 8, 6, 8, 134, 8,
70, 8, 198, 8, 38, 8, 166, 8, 102, 8,
230, 8, 22, 8, 150, 8, 86, 8, 214, 8,
54, 8, 182, 8, 118, 8, 246, 8, 14, 8,
142, 8, 78, 8, 206, 8, 46, 8, 174, 8,
110, 8, 238, 8, 30, 8, 158, 8, 94, 8,
222, 8, 62, 8, 190, 8, 126, 8, 254, 8,
1, 8, 129, 8, 65, 8, 193, 8, 33, 8,
161, 8, 97, 8, 225, 8, 17, 8, 145, 8,
81, 8, 209, 8, 49, 8, 177, 8, 113, 8,
241, 8, 9, 8, 137, 8, 73, 8, 201, 8,
41, 8, 169, 8, 105, 8, 233, 8, 25, 8,
153, 8, 89, 8, 217, 8, 57, 8, 185, 8,
121, 8, 249, 8, 5, 8, 133, 8, 69, 8,
197, 8, 37, 8, 165, 8, 101, 8, 229, 8,
21, 8, 149, 8, 85, 8, 213, 8, 53, 8,
181, 8, 117, 8, 245, 8, 13, 8, 141, 8,
77, 8, 205, 8, 45, 8, 173, 8, 109, 8,
237, 8, 29, 8, 157, 8, 93, 8, 221, 8,
61, 8, 189, 8, 125, 8, 253, 8, 19, 9,
275, 9, 147, 9, 403, 9, 83, 9, 339, 9,
211, 9, 467, 9, 51, 9, 307, 9, 179, 9,
435, 9, 115, 9, 371, 9, 243, 9, 499, 9,
11, 9, 267, 9, 139, 9, 395, 9, 75, 9,
331, 9, 203, 9, 459, 9, 43, 9, 299, 9,
171, 9, 427, 9, 107, 9, 363, 9, 235, 9,
491, 9, 27, 9, 283, 9, 155, 9, 411, 9,
91, 9, 347, 9, 219, 9, 475, 9, 59, 9,
315, 9, 187, 9, 443, 9, 123, 9, 379, 9,
251, 9, 507, 9, 7, 9, 263, 9, 135, 9,
391, 9, 71, 9, 327, 9, 199, 9, 455, 9,
39, 9, 295, 9, 167, 9, 423, 9, 103, 9,
359, 9, 231, 9, 487, 9, 23, 9, 279, 9,
151, 9, 407, 9, 87, 9, 343, 9, 215, 9,
471, 9, 55, 9, 311, 9, 183, 9, 439, 9,
119, 9, 375, 9, 247, 9, 503, 9, 15, 9,
271, 9, 143, 9, 399, 9, 79, 9, 335, 9,
207, 9, 463, 9, 47, 9, 303, 9, 175, 9,
431, 9, 111, 9, 367, 9, 239, 9, 495, 9,
31, 9, 287, 9, 159, 9, 415, 9, 95, 9,
351, 9, 223, 9, 479, 9, 63, 9, 319, 9,
191, 9, 447, 9, 127, 9, 383, 9, 255, 9,
511, 9, 0, 7, 64, 7, 32, 7, 96, 7,
16, 7, 80, 7, 48, 7, 112, 7, 8, 7,
72, 7, 40, 7, 104, 7, 24, 7, 88, 7,
56, 7, 120, 7, 4, 7, 68, 7, 36, 7,
100, 7, 20, 7, 84, 7, 52, 7, 116, 7,
3, 8, 131, 8, 67, 8, 195, 8, 35, 8,
163, 8, 99, 8, 227, 8
};
static final short[] static_dtree = {
0, 5, 16, 5, 8, 5, 24, 5, 4, 5,
20, 5, 12, 5, 28, 5, 2, 5, 18, 5,
10, 5, 26, 5, 6, 5, 22, 5, 14, 5,
30, 5, 1, 5, 17, 5, 9, 5, 25, 5,
5, 5, 21, 5, 13, 5, 29, 5, 3, 5,
19, 5, 11, 5, 27, 5, 7, 5, 23, 5
};
static StaticTree static_l_desc =
new StaticTree(static_ltree, Tree.extra_lbits,
LITERALS+1, L_CODES, MAX_BITS);
static StaticTree static_d_desc =
new StaticTree(static_dtree, Tree.extra_dbits,
0, D_CODES, MAX_BITS);
static StaticTree static_bl_desc =
new StaticTree(null, Tree.extra_blbits,
0, BL_CODES, MAX_BL_BITS);
short[] static_tree; // static tree or null
int[] extra_bits; // extra bits for each code or null
int extra_base; // base index for extra_bits
int elems; // max number of elements in the tree
int max_length; // max bit length for the codes
private StaticTree(short[] static_tree,
int[] extra_bits,
int extra_base,
int elems,
int max_length){
this.static_tree=static_tree;
this.extra_bits=extra_bits;
this.extra_base=extra_base;
this.elems=elems;
this.max_length=max_length;
}
}

@ -0,0 +1,367 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
final class Tree{
static final private int MAX_BITS=15;
static final private int BL_CODES=19;
static final private int D_CODES=30;
static final private int LITERALS=256;
static final private int LENGTH_CODES=29;
static final private int L_CODES=(LITERALS+1+LENGTH_CODES);
static final private int HEAP_SIZE=(2*L_CODES+1);
// Bit length codes must not exceed MAX_BL_BITS bits
static final int MAX_BL_BITS=7;
// end of block literal code
static final int END_BLOCK=256;
// repeat previous bit length 3-6 times (2 bits of repeat count)
static final int REP_3_6=16;
// repeat a zero length 3-10 times (3 bits of repeat count)
static final int REPZ_3_10=17;
// repeat a zero length 11-138 times (7 bits of repeat count)
static final int REPZ_11_138=18;
// extra bits for each length code
static final int[] extra_lbits={
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0
};
// extra bits for each distance code
static final int[] extra_dbits={
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13
};
// extra bits for each bit length code
static final int[] extra_blbits={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7
};
static final byte[] bl_order={
16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
// The lengths of the bit length codes are sent in order of decreasing
// probability, to avoid transmitting the lengths for unused bit
// length codes.
static final int Buf_size=8*2;
// see definition of array dist_code below
static final int DIST_CODE_LEN=512;
static final byte[] _dist_code = {
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
};
static final byte[] _length_code={
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
};
static final int[] base_length = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
64, 80, 96, 112, 128, 160, 192, 224, 0
};
static final int[] base_dist = {
0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
};
// Mapping from a distance to a distance code. dist is the distance - 1 and
// must not have side effects. _dist_code[256] and _dist_code[257] are never
// used.
static int d_code(int dist){
return ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>>7)]);
}
short[] dyn_tree; // the dynamic tree
int max_code; // largest code with non zero frequency
StaticTree stat_desc; // the corresponding static tree
// Compute the optimal bit lengths for a tree and update the total bit length
// for the current block.
// IN assertion: the fields freq and dad are set, heap[heap_max] and
// above are the tree nodes sorted by increasing frequency.
// OUT assertions: the field len is set to the optimal bit length, the
// array bl_count contains the frequencies for each bit length.
// The length opt_len is updated; static_len is also updated if stree is
// not null.
void gen_bitlen(Deflate s){
short[] tree = dyn_tree;
short[] stree = stat_desc.static_tree;
int[] extra = stat_desc.extra_bits;
int base = stat_desc.extra_base;
int max_length = stat_desc.max_length;
int h; // heap index
int n, m; // iterate over the tree elements
int bits; // bit length
int xbits; // extra bits
short f; // frequency
int overflow = 0; // number of elements with bit length too large
for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0;
// In a first pass, compute the optimal bit lengths (which may
// overflow in the case of the bit length tree).
tree[s.heap[s.heap_max]*2+1] = 0; // root of the heap
for(h=s.heap_max+1; h<HEAP_SIZE; h++){
n = s.heap[h];
bits = tree[tree[n*2+1]*2+1] + 1;
if (bits > max_length){ bits = max_length; overflow++; }
tree[n*2+1] = (short)bits;
// We overwrite tree[n*2+1] which is no longer needed
if (n > max_code) continue; // not a leaf node
s.bl_count[bits]++;
xbits = 0;
if (n >= base) xbits = extra[n-base];
f = tree[n*2];
s.opt_len += f * (bits + xbits);
if (stree!=null) s.static_len += f * (stree[n*2+1] + xbits);
}
if (overflow == 0) return;
// This happens for example on obj2 and pic of the Calgary corpus
// Find the first bit length which could increase:
do {
bits = max_length-1;
while(s.bl_count[bits]==0) bits--;
s.bl_count[bits]--; // move one leaf down the tree
s.bl_count[bits+1]+=2; // move one overflow item as its brother
s.bl_count[max_length]--;
// The brother of the overflow item also moves one step up,
// but this does not affect bl_count[max_length]
overflow -= 2;
}
while (overflow > 0);
for (bits = max_length; bits != 0; bits--) {
n = s.bl_count[bits];
while (n != 0) {
m = s.heap[--h];
if (m > max_code) continue;
if (tree[m*2+1] != bits) {
s.opt_len += ((long)bits - (long)tree[m*2+1])*(long)tree[m*2];
tree[m*2+1] = (short)bits;
}
n--;
}
}
}
// Construct one Huffman tree and assigns the code bit strings and lengths.
// Update the total bit length for the current block.
// IN assertion: the field freq is set for all tree elements.
// OUT assertions: the fields len and code are set to the optimal bit length
// and corresponding code. The length opt_len is updated; static_len is
// also updated if stree is not null. The field max_code is set.
void build_tree(Deflate s){
short[] tree=dyn_tree;
short[] stree=stat_desc.static_tree;
int elems=stat_desc.elems;
int n, m; // iterate over heap elements
int max_code=-1; // largest code with non zero frequency
int node; // new node being created
// Construct the initial heap, with least frequent element in
// heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
// heap[0] is not used.
s.heap_len = 0;
s.heap_max = HEAP_SIZE;
for(n=0; n<elems; n++) {
if(tree[n*2] != 0) {
s.heap[++s.heap_len] = max_code = n;
s.depth[n] = 0;
}
else{
tree[n*2+1] = 0;
}
}
// The pkzip format requires that at least one distance code exists,
// and that at least one bit should be sent even if there is only one
// possible code. So to avoid special checks later on we force at least
// two codes of non zero frequency.
while (s.heap_len < 2) {
node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
tree[node*2] = 1;
s.depth[node] = 0;
s.opt_len--; if (stree!=null) s.static_len -= stree[node*2+1];
// node is 0 or 1 so it does not have extra bits
}
this.max_code = max_code;
// The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
// establish sub-heaps of increasing lengths:
for(n=s.heap_len/2;n>=1; n--)
s.pqdownheap(tree, n);
// Construct the Huffman tree by repeatedly combining the least two
// frequent nodes.
node=elems; // next internal node of the tree
do{
// n = node of least frequency
n=s.heap[1];
s.heap[1]=s.heap[s.heap_len--];
s.pqdownheap(tree, 1);
m=s.heap[1]; // m = node of next least frequency
s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency
s.heap[--s.heap_max] = m;
// Create a new node father of n and m
tree[node*2] = (short)(tree[n*2] + tree[m*2]);
s.depth[node] = (byte)(Math.max(s.depth[n],s.depth[m])+1);
tree[n*2+1] = tree[m*2+1] = (short)node;
// and insert the new node in the heap
s.heap[1] = node++;
s.pqdownheap(tree, 1);
}
while(s.heap_len>=2);
s.heap[--s.heap_max] = s.heap[1];
// At this point, the fields freq and dad are set. We can now
// generate the bit lengths.
gen_bitlen(s);
// The field len is now set, we can generate the bit codes
gen_codes(tree, max_code, s.bl_count, s.next_code);
}
// Generate the codes for a given tree and bit counts (which need not be
// optimal).
// IN assertion: the array bl_count contains the bit length statistics for
// the given tree and the field len is set for all tree elements.
// OUT assertion: the field code is set for all tree elements of non
// zero code length.
private final static void gen_codes(
short[] tree, // the tree to decorate
int max_code, // largest code with non zero frequency
short[] bl_count, // number of codes at each bit length
short[] next_code){
short code = 0; // running code value
int bits; // bit index
int n; // code index
// The distribution counts are first used to generate the code values
// without bit reversal.
next_code[0]=0;
for (bits = 1; bits <= MAX_BITS; bits++) {
next_code[bits] = code = (short)((code + bl_count[bits-1]) << 1);
}
// Check that the bit counts in bl_count are consistent. The last code
// must be all ones.
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
// "inconsistent bit counts");
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
for (n = 0; n <= max_code; n++) {
int len = tree[n*2+1];
if (len == 0) continue;
// Now reverse the bits
tree[n*2] = (short)(bi_reverse(next_code[len]++, len));
}
}
// Reverse the first len bits of a code, using straightforward code (a faster
// method would use a table)
// IN assertion: 1 <= len <= 15
private final static int bi_reverse(
int code, // the value to invert
int len // its bit length
){
int res = 0;
do{
res|=code&1;
code>>>=1;
res<<=1;
}
while(--len>0);
return res>>>1;
}
}

@ -0,0 +1,126 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jzlib;
import java.io.*;
/**
* ZInputStream
*
* @deprecated use DeflaterOutputStream or InflaterInputStream
*/
@Deprecated
public class ZInputStream extends FilterInputStream {
protected int flush=JZlib.Z_NO_FLUSH;
protected boolean compress;
protected InputStream in=null;
protected Deflater deflater;
protected InflaterInputStream iis;
public ZInputStream(InputStream in) throws IOException {
this(in, false);
}
public ZInputStream(InputStream in, boolean nowrap) throws IOException {
super(in);
iis = new InflaterInputStream(in, nowrap);
compress=false;
}
public ZInputStream(InputStream in, int level) throws IOException {
super(in);
this.in=in;
deflater = new Deflater();
deflater.init(level);
compress=true;
}
private byte[] buf1 = new byte[1];
public int read() throws IOException {
if(read(buf1, 0, 1)==-1) return -1;
return(buf1[0]&0xFF);
}
private byte[] buf = new byte[512];
public int read(byte[] b, int off, int len) throws IOException {
if(compress){
deflater.setOutput(b, off, len);
while(true){
int datalen = in.read(buf, 0, buf.length);
if(datalen == -1) return -1;
deflater.setInput(buf, 0, datalen, true);
int err = deflater.deflate(flush);
if(deflater.next_out_index>0)
return deflater.next_out_index;
if(err == JZlib.Z_STREAM_END)
return 0;
if(err == JZlib.Z_STREAM_ERROR ||
err == JZlib.Z_DATA_ERROR){
throw new ZStreamException("deflating: "+deflater.msg);
}
}
}
else{
return iis.read(b, off, len);
}
}
public long skip(long n) throws IOException {
int len=512;
if(n<len)
len=(int)n;
byte[] tmp=new byte[len];
return((long)read(tmp));
}
public int getFlushMode() {
return flush;
}
public void setFlushMode(int flush) {
this.flush=flush;
}
public long getTotalIn() {
if(compress) return deflater.total_in;
else return iis.getTotalIn();
}
public long getTotalOut() {
if(compress) return deflater.total_out;
else return iis.getTotalOut();
}
public void close() throws IOException{
if(compress) deflater.end();
else iis.close();
}
}

@ -0,0 +1,159 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jzlib;
import java.io.*;
/**
* ZOutputStream
*
* @deprecated use DeflaterOutputStream or InflaterInputStream
*/
@Deprecated
public class ZOutputStream extends FilterOutputStream {
protected int bufsize=512;
protected int flush=JZlib.Z_NO_FLUSH;
protected byte[] buf=new byte[bufsize];
protected boolean compress;
protected OutputStream out;
private boolean end=false;
private DeflaterOutputStream dos;
private Inflater inflater;
public ZOutputStream(OutputStream out) throws IOException {
super(out);
this.out=out;
inflater = new Inflater();
inflater.init();
compress=false;
}
public ZOutputStream(OutputStream out, int level) throws IOException {
this(out, level, false);
}
public ZOutputStream(OutputStream out, int level, boolean nowrap) throws IOException {
super(out);
this.out=out;
Deflater deflater = new Deflater(level, nowrap);
dos = new DeflaterOutputStream(out, deflater);
compress=true;
}
private byte[] buf1 = new byte[1];
public void write(int b) throws IOException {
buf1[0]=(byte)b;
write(buf1, 0, 1);
}
public void write(byte b[], int off, int len) throws IOException {
if(len==0) return;
if(compress){
dos.write(b, off, len);
}
else {
inflater.setInput(b, off, len, true);
int err = JZlib.Z_OK;
while(inflater.avail_in>0){
inflater.setOutput(buf, 0, buf.length);
err = inflater.inflate(flush);
if(inflater.next_out_index>0)
out.write(buf, 0, inflater.next_out_index);
if(err != JZlib.Z_OK)
break;
}
if(err != JZlib.Z_OK)
throw new ZStreamException("inflating: "+inflater.msg);
return;
}
}
public int getFlushMode() {
return flush;
}
public void setFlushMode(int flush) {
this.flush=flush;
}
public void finish() throws IOException {
int err;
if(compress){
int tmp = flush;
int flush = JZlib.Z_FINISH;
try{
write("".getBytes(), 0, 0);
}
finally { flush = tmp; }
}
else{
dos.finish();
}
flush();
}
public synchronized void end() {
if(end) return;
if(compress){
try { dos.finish(); } catch(Exception e){}
}
else{
inflater.end();
}
end=true;
}
public void close() throws IOException {
try{
try{finish();}
catch (IOException ignored) {}
}
finally{
end();
out.close();
out=null;
}
}
public long getTotalIn() {
if(compress) return dos.getTotalIn();
else return inflater.total_in;
}
public long getTotalOut() {
if(compress) return dos.getTotalOut();
else return inflater.total_out;
}
public void flush() throws IOException {
out.flush();
}
}

@ -0,0 +1,377 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
/**
* ZStream
*
* @deprecated Not for public use in the future.
*/
@Deprecated
public class ZStream{
static final private int MAX_WBITS=15; // 32K LZ77 window
static final private int DEF_WBITS=MAX_WBITS;
static final private int Z_NO_FLUSH=0;
static final private int Z_PARTIAL_FLUSH=1;
static final private int Z_SYNC_FLUSH=2;
static final private int Z_FULL_FLUSH=3;
static final private int Z_FINISH=4;
static final private int MAX_MEM_LEVEL=9;
static final private int Z_OK=0;
static final private int Z_STREAM_END=1;
static final private int Z_NEED_DICT=2;
static final private int Z_ERRNO=-1;
static final private int Z_STREAM_ERROR=-2;
static final private int Z_DATA_ERROR=-3;
static final private int Z_MEM_ERROR=-4;
static final private int Z_BUF_ERROR=-5;
static final private int Z_VERSION_ERROR=-6;
public byte[] next_in; // next input byte
public int next_in_index;
public int avail_in; // number of bytes available at next_in
public long total_in; // total nb of input bytes read so far
public byte[] next_out; // next output byte should be put there
public int next_out_index;
public int avail_out; // remaining free space at next_out
public long total_out; // total nb of bytes output so far
public String msg;
Deflate dstate;
Inflate istate;
int data_type; // best guess about the data type: ascii or binary
Checksum adler;
public ZStream(){
this(new Adler32());
}
public ZStream(Checksum adler){
this.adler=adler;
}
public int inflateInit(){
return inflateInit(DEF_WBITS);
}
public int inflateInit(boolean nowrap){
return inflateInit(DEF_WBITS, nowrap);
}
public int inflateInit(int w){
return inflateInit(w, false);
}
public int inflateInit(JZlib.WrapperType wrapperType) {
return inflateInit(DEF_WBITS, wrapperType);
}
public int inflateInit(int w, JZlib.WrapperType wrapperType) {
boolean nowrap = false;
if(wrapperType == JZlib.W_NONE){
nowrap = true;
}
else if(wrapperType == JZlib.W_GZIP) {
w += 16;
}
else if(wrapperType == JZlib.W_ANY) {
w |= Inflate.INFLATE_ANY;
}
else if(wrapperType == JZlib.W_ZLIB) {
}
return inflateInit(w, nowrap);
}
public int inflateInit(int w, boolean nowrap){
istate=new Inflate(this);
return istate.inflateInit(nowrap?-w:w);
}
public int inflate(int f){
if(istate==null) return Z_STREAM_ERROR;
return istate.inflate(f);
}
public int inflateEnd(){
if(istate==null) return Z_STREAM_ERROR;
int ret=istate.inflateEnd();
// istate = null;
return ret;
}
public int inflateSync(){
if(istate == null)
return Z_STREAM_ERROR;
return istate.inflateSync();
}
public int inflateSyncPoint(){
if(istate == null)
return Z_STREAM_ERROR;
return istate.inflateSyncPoint();
}
public int inflateSetDictionary(byte[] dictionary, int dictLength){
if(istate == null)
return Z_STREAM_ERROR;
return istate.inflateSetDictionary(dictionary, dictLength);
}
public boolean inflateFinished(){
return istate.mode==12 /*DONE*/;
}
public int deflateInit(int level){
return deflateInit(level, MAX_WBITS);
}
public int deflateInit(int level, boolean nowrap){
return deflateInit(level, MAX_WBITS, nowrap);
}
public int deflateInit(int level, int bits){
return deflateInit(level, bits, false);
}
public int deflateInit(int level, int bits, int memlevel, JZlib.WrapperType wrapperType){
if(bits < 9 || bits > 15){
return Z_STREAM_ERROR;
}
if(wrapperType == JZlib.W_NONE) {
bits *= -1;
}
else if(wrapperType == JZlib.W_GZIP) {
bits += 16;
}
else if(wrapperType == JZlib.W_ANY) {
return Z_STREAM_ERROR;
}
else if(wrapperType == JZlib.W_ZLIB) {
}
return this.deflateInit(level, bits, memlevel);
}
public int deflateInit(int level, int bits, int memlevel){
dstate=new Deflate(this);
return dstate.deflateInit(level, bits, memlevel);
}
public int deflateInit(int level, int bits, boolean nowrap){
dstate=new Deflate(this);
return dstate.deflateInit(level, nowrap?-bits:bits);
}
public int deflate(int flush){
if(dstate==null){
return Z_STREAM_ERROR;
}
return dstate.deflate(flush);
}
public int deflateEnd(){
if(dstate==null) return Z_STREAM_ERROR;
int ret=dstate.deflateEnd();
dstate=null;
return ret;
}
public int deflateParams(int level, int strategy){
if(dstate==null) return Z_STREAM_ERROR;
return dstate.deflateParams(level, strategy);
}
public int deflateSetDictionary (byte[] dictionary, int dictLength){
if(dstate == null)
return Z_STREAM_ERROR;
return dstate.deflateSetDictionary(dictionary, dictLength);
}
// Flush as much pending output as possible. All deflate() output goes
// through this function so some applications may wish to modify it
// to avoid allocating a large strm->next_out buffer and copying into it.
// (See also read_buf()).
void flush_pending(){
int len=dstate.pending;
if(len>avail_out) len=avail_out;
if(len==0) return;
if(dstate.pending_buf.length<=dstate.pending_out ||
next_out.length<=next_out_index ||
dstate.pending_buf.length<(dstate.pending_out+len) ||
next_out.length<(next_out_index+len)){
//System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+
// ", "+next_out.length+", "+next_out_index+", "+len);
//System.out.println("avail_out="+avail_out);
}
System.arraycopy(dstate.pending_buf, dstate.pending_out,
next_out, next_out_index, len);
next_out_index+=len;
dstate.pending_out+=len;
total_out+=len;
avail_out-=len;
dstate.pending-=len;
if(dstate.pending==0){
dstate.pending_out=0;
}
}
// Read a new buffer from the current input stream, update the adler32
// and total number of bytes read. All deflate() input goes through
// this function so some applications may wish to modify it to avoid
// allocating a large strm->next_in buffer and copying from it.
// (See also flush_pending()).
int read_buf(byte[] buf, int start, int size) {
int len=avail_in;
if(len>size) len=size;
if(len==0) return 0;
avail_in-=len;
if(dstate.wrap!=0) {
adler.update(next_in, next_in_index, len);
}
System.arraycopy(next_in, next_in_index, buf, start, len);
next_in_index += len;
total_in += len;
return len;
}
public long getAdler(){
return adler.getValue();
}
public void free(){
next_in=null;
next_out=null;
msg=null;
}
public void setOutput(byte[] buf){
setOutput(buf, 0, buf.length);
}
public void setOutput(byte[] buf, int off, int len){
next_out = buf;
next_out_index = off;
avail_out = len;
}
public void setInput(byte[] buf){
setInput(buf, 0, buf.length, false);
}
public void setInput(byte[] buf, boolean append){
setInput(buf, 0, buf.length, append);
}
public void setInput(byte[] buf, int off, int len, boolean append){
if(len<=0 && append && next_in!=null) return;
if(avail_in>0 && append){
byte[] tmp = new byte[avail_in+len];
System.arraycopy(next_in, next_in_index, tmp, 0, avail_in);
System.arraycopy(buf, off, tmp, avail_in, len);
next_in=tmp;
next_in_index=0;
avail_in+=len;
}
else{
next_in=buf;
next_in_index=off;
avail_in=len;
}
}
public byte[] getNextIn(){
return next_in;
}
public void setNextIn(byte[] next_in){
this.next_in = next_in;
}
public int getNextInIndex(){
return next_in_index;
}
public void setNextInIndex(int next_in_index){
this.next_in_index = next_in_index;
}
public int getAvailIn(){
return avail_in;
}
public void setAvailIn(int avail_in){
this.avail_in = avail_in;
}
public byte[] getNextOut(){
return next_out;
}
public void setNextOut(byte[] next_out){
this.next_out = next_out;
}
public int getNextOutIndex(){
return next_out_index;
}
public void setNextOutIndex(int next_out_index){
this.next_out_index = next_out_index;
}
public int getAvailOut(){
return avail_out;
}
public void setAvailOut(int avail_out){
this.avail_out = avail_out;
}
public long getTotalOut(){
return total_out;
}
public long getTotalIn(){
return total_in;
}
public String getMessage(){
return msg;
}
/**
* Those methods are expected to be override by Inflater and Deflater.
* In the future, they will become abstract methods.
*/
public int end(){ return Z_OK; }
public boolean finished(){ return false; }
}

@ -0,0 +1,44 @@
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
package com.jcraft.jzlib;
public class ZStreamException extends java.io.IOException {
public ZStreamException() {
super();
}
public ZStreamException(String s) {
super(s);
}
}

@ -0,0 +1,12 @@
package me.ag2s.epublib;
public interface Constants {
String CHARACTER_ENCODING = "UTF-8";
String DOCTYPE_XHTML = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">";
String NAMESPACE_XHTML = "http://www.w3.org/1999/xhtml";
String EPUB4J_GENERATOR_NAME = "EpubLib";
char FRAGMENT_SEPARATOR_CHAR = '#';
String DEFAULT_TOC_ID = "toc";
}

@ -0,0 +1,155 @@
package me.ag2s.epublib.browsersupport;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.util.StringUtil;
import java.util.EventObject;
/**
* Used to tell NavigationEventListener just what kind of navigation action
* the user just did.
*
* @author paul
*
*/
public class NavigationEvent extends EventObject {
private static final long serialVersionUID = -6346750144308952762L;
private Resource oldResource;
private int oldSpinePos;
private Navigator navigator;
private Book oldBook;
private int oldSectionPos;
private String oldFragmentId;
public NavigationEvent(Object source) {
super(source);
}
public NavigationEvent(Object source, Navigator navigator) {
super(source);
this.navigator = navigator;
this.oldBook = navigator.getBook();
this.oldFragmentId = navigator.getCurrentFragmentId();
this.oldSectionPos = navigator.getCurrentSectionPos();
this.oldResource = navigator.getCurrentResource();
this.oldSpinePos = navigator.getCurrentSpinePos();
}
/**
* The previous position within the section.
*
* @return The previous position within the section.
*/
public int getOldSectionPos() {
return oldSectionPos;
}
public Navigator getNavigator() {
return navigator;
}
public String getOldFragmentId() {
return oldFragmentId;
}
// package
void setOldFragmentId(String oldFragmentId) {
this.oldFragmentId = oldFragmentId;
}
public Book getOldBook() {
return oldBook;
}
// package
void setOldPagePos(int oldPagePos) {
this.oldSectionPos = oldPagePos;
}
public int getCurrentSectionPos() {
return navigator.getCurrentSectionPos();
}
public int getOldSpinePos() {
return oldSpinePos;
}
public int getCurrentSpinePos() {
return navigator.getCurrentSpinePos();
}
public String getCurrentFragmentId() {
return navigator.getCurrentFragmentId();
}
public boolean isBookChanged() {
if (oldBook == null) {
return true;
}
return oldBook != navigator.getBook();
}
public boolean isSpinePosChanged() {
return getOldSpinePos() != getCurrentSpinePos();
}
public boolean isFragmentChanged() {
return StringUtil.equals(getOldFragmentId(), getCurrentFragmentId());
}
public Resource getOldResource() {
return oldResource;
}
public Resource getCurrentResource() {
return navigator.getCurrentResource();
}
public void setOldResource(Resource oldResource) {
this.oldResource = oldResource;
}
public void setOldSpinePos(int oldSpinePos) {
this.oldSpinePos = oldSpinePos;
}
public void setNavigator(Navigator navigator) {
this.navigator = navigator;
}
public void setOldBook(Book oldBook) {
this.oldBook = oldBook;
}
public Book getCurrentBook() {
return getNavigator().getBook();
}
public boolean isResourceChanged() {
return oldResource != getCurrentResource();
}
public String toString() {
return StringUtil.toString(
"oldSectionPos", oldSectionPos,
"oldResource", oldResource,
"oldBook", oldBook,
"oldFragmentId", oldFragmentId,
"oldSpinePos", oldSpinePos,
"currentPagePos", getCurrentSectionPos(),
"currentResource", getCurrentResource(),
"currentBook", getCurrentBook(),
"currentFragmentId", getCurrentFragmentId(),
"currentSpinePos", getCurrentSpinePos()
);
}
public boolean isSectionPosChanged() {
return oldSectionPos != getCurrentSectionPos();
}
}

@ -0,0 +1,18 @@
package me.ag2s.epublib.browsersupport;
/**
* Implemented by classes that want to be notified if the user moves to
* another location in the book.
*
* @author paul
*
*/
public interface NavigationEventListener {
/**
* Called whenever the user navigates to another position in the book.
*
* @param navigationEvent
*/
void navigationPerformed(NavigationEvent navigationEvent);
}

@ -0,0 +1,207 @@
package me.ag2s.epublib.browsersupport;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* A history of the user's locations with the epub.
*
* @author paul.siegmann
*
*/
public class NavigationHistory implements NavigationEventListener {
public static final int DEFAULT_MAX_HISTORY_SIZE = 1000;
private static final long DEFAULT_HISTORY_WAIT_TIME = 1000;
private static class Location {
private String href;
public Location(String href) {
super();
this.href = href;
}
@SuppressWarnings("unused")
public void setHref(String href) {
this.href = href;
}
public String getHref() {
return href;
}
}
private long lastUpdateTime = 0;
private List<Location> locations = new ArrayList<Location>();
private Navigator navigator;
private int currentPos = -1;
private int currentSize = 0;
private int maxHistorySize = DEFAULT_MAX_HISTORY_SIZE;
private long historyWaitTime = DEFAULT_HISTORY_WAIT_TIME;
public NavigationHistory(Navigator navigator) {
this.navigator = navigator;
navigator.addNavigationEventListener(this);
initBook(navigator.getBook());
}
public int getCurrentPos() {
return currentPos;
}
public int getCurrentSize() {
return currentSize;
}
public void initBook(Book book) {
if (book == null) {
return;
}
locations = new ArrayList<Location>();
currentPos = -1;
currentSize = 0;
if (navigator.getCurrentResource() != null) {
addLocation(navigator.getCurrentResource().getHref());
}
}
/**
* If the time between a navigation event is less than the historyWaitTime
* then the new location is not added to the history.
*
* When a user is rapidly viewing many pages using the slider we do not
* want all of them to be added to the history.
*
* @return the time we wait before adding the page to the history
*/
public long getHistoryWaitTime() {
return historyWaitTime;
}
public void setHistoryWaitTime(long historyWaitTime) {
this.historyWaitTime = historyWaitTime;
}
public void addLocation(Resource resource) {
if (resource == null) {
return;
}
addLocation(resource.getHref());
}
/**
* Adds the location after the current position.
* If the currentposition is not the end of the list then the elements
* between the current element and the end of the list will be discarded.
*
* Does nothing if the new location matches the current location.
* <br/>
* If this nr of locations becomes larger then the historySize then the
* first item(s) will be removed.
*
* @param location
*/
public void addLocation(Location location) {
// do nothing if the new location matches the current location
if (!(locations.isEmpty()) &&
location.getHref().equals(locations.get(currentPos).getHref())) {
return;
}
currentPos++;
if (currentPos != currentSize) {
locations.set(currentPos, location);
} else {
locations.add(location);
checkHistorySize();
}
currentSize = currentPos + 1;
}
/**
* Removes all elements that are too much for the maxHistorySize
* out of the history.
*/
private void checkHistorySize() {
while (locations.size() > maxHistorySize) {
locations.remove(0);
currentSize--;
currentPos--;
}
}
public void addLocation(String href) {
addLocation(new Location(href));
}
private String getLocationHref(int pos) {
if (pos < 0 || pos >= locations.size()) {
return null;
}
return locations.get(currentPos).getHref();
}
/**
* Moves the current positions delta positions.
*
* move(-1) to go one position back in history.<br/>
* move(1) to go one position forward.<br/>
*
* @param delta
*
* @return Whether we actually moved. If the requested value is illegal
* it will return false, true otherwise.
*/
public boolean move(int delta) {
if (((currentPos + delta) < 0)
|| ((currentPos + delta) >= currentSize)) {
return false;
}
currentPos += delta;
navigator.gotoResource(getLocationHref(currentPos), this);
return true;
}
/**
* If this is not the source of the navigationEvent then the addLocation
* will be called with the href of the currentResource in the navigationEvent.
*/
@Override
public void navigationPerformed(NavigationEvent navigationEvent) {
if (this == navigationEvent.getSource()) {
return;
}
if (navigationEvent.getCurrentResource() == null) {
return;
}
if ((System.currentTimeMillis() - this.lastUpdateTime) > historyWaitTime) {
// if the user scrolled rapidly through the pages then the last page
// will not be added to the history. We fix that here:
addLocation(navigationEvent.getOldResource());
addLocation(navigationEvent.getCurrentResource().getHref());
}
lastUpdateTime = System.currentTimeMillis();
}
public String getCurrentHref() {
if (currentPos < 0 || currentPos >= locations.size()) {
return null;
}
return locations.get(currentPos).getHref();
}
public void setMaxHistorySize(int maxHistorySize) {
this.maxHistorySize = maxHistorySize;
}
public int getMaxHistorySize() {
return maxHistorySize;
}
}

@ -0,0 +1,219 @@
package me.ag2s.epublib.browsersupport;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class for epub browser applications.
*
* It helps moving from one resource to the other, from one resource
* to the other and keeping other elements of the application up-to-date
* by calling the NavigationEventListeners.
*
* @author paul
*/
public class Navigator implements Serializable {
private static final long serialVersionUID = 1076126986424925474L;
private Book book;
private int currentSpinePos;
private Resource currentResource;
private int currentPagePos;
private String currentFragmentId;
private List<NavigationEventListener> eventListeners = new ArrayList<>();
public Navigator() {
this(null);
}
public Navigator(Book book) {
this.book = book;
this.currentSpinePos = 0;
if (book != null) {
this.currentResource = book.getCoverPage();
}
this.currentPagePos = 0;
}
private synchronized void handleEventListeners(
NavigationEvent navigationEvent) {
for (int i = 0; i < eventListeners.size(); i++) {
NavigationEventListener navigationEventListener = eventListeners.get(i);
navigationEventListener.navigationPerformed(navigationEvent);
}
}
public boolean addNavigationEventListener(
NavigationEventListener navigationEventListener) {
return this.eventListeners.add(navigationEventListener);
}
public boolean removeNavigationEventListener(
NavigationEventListener navigationEventListener) {
return this.eventListeners.remove(navigationEventListener);
}
public int gotoFirstSpineSection(Object source) {
return gotoSpineSection(0, source);
}
public int gotoPreviousSpineSection(Object source) {
return gotoPreviousSpineSection(0, source);
}
public int gotoPreviousSpineSection(int pagePos, Object source) {
if (currentSpinePos < 0) {
return gotoSpineSection(0, pagePos, source);
} else {
return gotoSpineSection(currentSpinePos - 1, pagePos, source);
}
}
public boolean hasNextSpineSection() {
return (currentSpinePos < (book.getSpine().size() - 1));
}
public boolean hasPreviousSpineSection() {
return (currentSpinePos > 0);
}
public int gotoNextSpineSection(Object source) {
if (currentSpinePos < 0) {
return gotoSpineSection(0, source);
} else {
return gotoSpineSection(currentSpinePos + 1, source);
}
}
public int gotoResource(String resourceHref, Object source) {
Resource resource = book.getResources().getByHref(resourceHref);
return gotoResource(resource, source);
}
public int gotoResource(Resource resource, Object source) {
return gotoResource(resource, 0, null, source);
}
public int gotoResource(Resource resource, String fragmentId, Object source) {
return gotoResource(resource, 0, fragmentId, source);
}
public int gotoResource(Resource resource, int pagePos, Object source) {
return gotoResource(resource, pagePos, null, source);
}
public int gotoResource(Resource resource, int pagePos, String fragmentId,
Object source) {
if (resource == null) {
return -1;
}
NavigationEvent navigationEvent = new NavigationEvent(source, this);
this.currentResource = resource;
this.currentSpinePos = book.getSpine().getResourceIndex(currentResource);
this.currentPagePos = pagePos;
this.currentFragmentId = fragmentId;
handleEventListeners(navigationEvent);
return currentSpinePos;
}
public int gotoResourceId(String resourceId, Object source) {
return gotoSpineSection(book.getSpine().findFirstResourceById(resourceId),
source);
}
public int gotoSpineSection(int newSpinePos, Object source) {
return gotoSpineSection(newSpinePos, 0, source);
}
/**
* Go to a specific section.
* Illegal spine positions are silently ignored.
*
* @param newSpinePos
* @param source
* @return The current position within the spine
*/
public int gotoSpineSection(int newSpinePos, int newPagePos, Object source) {
if (newSpinePos == currentSpinePos) {
return currentSpinePos;
}
if (newSpinePos < 0 || newSpinePos >= book.getSpine().size()) {
return currentSpinePos;
}
NavigationEvent navigationEvent = new NavigationEvent(source, this);
currentSpinePos = newSpinePos;
currentPagePos = newPagePos;
currentResource = book.getSpine().getResource(currentSpinePos);
handleEventListeners(navigationEvent);
return currentSpinePos;
}
public int gotoLastSpineSection(Object source) {
return gotoSpineSection(book.getSpine().size() - 1, source);
}
public void gotoBook(Book book, Object source) {
NavigationEvent navigationEvent = new NavigationEvent(source, this);
this.book = book;
this.currentFragmentId = null;
this.currentPagePos = 0;
this.currentResource = null;
this.currentSpinePos = book.getSpine().getResourceIndex(currentResource);
handleEventListeners(navigationEvent);
}
/**
* The current position within the spine.
*
* @return something &lt; 0 if the current position is not within the spine.
*/
public int getCurrentSpinePos() {
return currentSpinePos;
}
public Resource getCurrentResource() {
return currentResource;
}
/**
* Sets the current index and resource without calling the eventlisteners.
*
* If you want the eventListeners called use gotoSection(index);
*
* @param currentIndex
*/
public void setCurrentSpinePos(int currentIndex) {
this.currentSpinePos = currentIndex;
this.currentResource = book.getSpine().getResource(currentIndex);
}
public Book getBook() {
return book;
}
/**
* Sets the current index and resource without calling the eventlisteners.
*
* If you want the eventListeners called use gotoSection(index);
*
*/
public int setCurrentResource(Resource currentResource) {
this.currentSpinePos = book.getSpine().getResourceIndex(currentResource);
this.currentResource = currentResource;
return currentSpinePos;
}
public String getCurrentFragmentId() {
return currentFragmentId;
}
public int getCurrentSectionPos() {
return currentPagePos;
}
}

@ -0,0 +1,7 @@
/**
* Provides classes that help make an epub reader application.
*
* These classes have no dependencies on graphic toolkits, they're purely
* to help with the browsing/navigation logic.
*/
package me.ag2s.epublib.browsersupport;

@ -0,0 +1,79 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
/**
* Represents one of the authors of the book
*
* @author paul
*
*/
public class Author implements Serializable {
private static final long serialVersionUID = 6663408501416574200L;
private String firstname;
private String lastname;
private Relator relator = Relator.AUTHOR;
public Author(String singleName) {
this("", singleName);
}
public Author(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String toString() {
return lastname + ", " + firstname;
}
public int hashCode() {
return StringUtil.hashCode(firstname, lastname);
}
public boolean equals(Object authorObject) {
if (!(authorObject instanceof Author)) {
return false;
}
Author other = (Author) authorObject;
return StringUtil.equals(firstname, other.firstname)
&& StringUtil.equals(lastname, other.lastname);
}
public Relator setRole(String code) {
Relator result = Relator.byCode(code);
if (result == null) {
result = Relator.AUTHOR;
}
this.relator = result;
return result;
}
public Relator getRelator() {
return relator;
}
public void setRelator(Relator relator) {
this.relator = relator;
}
}

@ -0,0 +1,316 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Representation of a Book.
*
* All resources of a Book (html, css, xml, fonts, images) are represented
* as Resources. See getResources() for access to these.<br/>
* A Book as 3 indexes into these Resources, as per the epub specification.<br/>
* <dl>
* <dt>Spine</dt>
* <dd>these are the Resources to be shown when a user reads the book from
* start to finish.</dd>
* <dt>Table of Contents<dt>
* <dd>The table of contents. Table of Contents references may be in a
* different order and contain different Resources than the spine, and often do.
* <dt>Guide</dt>
* <dd>The Guide has references to a set of special Resources like the
* cover page, the Glossary, the copyright page, etc.
* </dl>
* <p/>
* The complication is that these 3 indexes may and usually do point to
* different pages.
* A chapter may be split up in 2 pieces to fit it in to memory. Then the
* spine will contain both pieces, but the Table of Contents only the first.
*
* The Content page may be in the Table of Contents, the Guide, but not
* in the Spine.
* Etc.
* <p/>
*
* Please see the illustration at: doc/schema.svg
*
* @author paul
* @author jake
*/
public class Book implements Serializable {
private static final long serialVersionUID = 2068355170895770100L;
private Resources resources = new Resources();
private Metadata metadata = new Metadata();
private Spine spine = new Spine();
private TableOfContents tableOfContents = new TableOfContents();
private Guide guide = new Guide();
private Resource opfResource;
private Resource ncxResource;
private Resource coverImage;
private String version;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public TOCReference addSection(
TOCReference parentSection, String sectionTitle, Resource resource) {
return addSection(parentSection, sectionTitle, resource, null);
}
/**
* Adds the resource to the table of contents of the book as a child
* section of the given parentSection
*
* @param parentSection
* @param sectionTitle
* @param resource
* @param fragmentId
* @return The table of contents
*/
public TOCReference addSection(
TOCReference parentSection, String sectionTitle, Resource resource,
String fragmentId) {
getResources().add(resource);
if (spine.findFirstResourceById(resource.getId()) < 0) {
spine.addSpineReference(new SpineReference(resource));
}
return parentSection.addChildSection(
new TOCReference(sectionTitle, resource, fragmentId));
}
public TOCReference addSection(String title, Resource resource) {
return addSection(title, resource, null);
}
/**
* Adds a resource to the book's set of resources, table of contents and
* if there is no resource with the id in the spine also adds it to the spine.
*
* @param title
* @param resource
* @param fragmentId
* @return The table of contents
*/
public TOCReference addSection(
String title, Resource resource, String fragmentId) {
getResources().add(resource);
TOCReference tocReference = tableOfContents
.addTOCReference(new TOCReference(title, resource, fragmentId));
if (spine.findFirstResourceById(resource.getId()) < 0) {
spine.addSpineReference(new SpineReference(resource));
}
return tocReference;
}
public void generateSpineFromTableOfContents() {
Spine spine = new Spine(tableOfContents);
// in case the tocResource was already found and assigned
spine.setTocResource(this.spine.getTocResource());
this.spine = spine;
}
/**
* The Book's metadata (titles, authors, etc)
*
* @return The Book's metadata (titles, authors, etc)
*/
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
public void setResources(Resources resources) {
this.resources = resources;
}
public Resource addResource(Resource resource) {
return resources.add(resource);
}
/**
* The collection of all images, chapters, sections, xhtml files,
* stylesheets, etc that make up the book.
*
* @return The collection of all images, chapters, sections, xhtml files,
* stylesheets, etc that make up the book.
*/
public Resources getResources() {
return resources;
}
/**
* The sections of the book that should be shown if a user reads the book
* from start to finish.
*
* @return The Spine
*/
public Spine getSpine() {
return spine;
}
public void setSpine(Spine spine) {
this.spine = spine;
}
/**
* The Table of Contents of the book.
*
* @return The Table of Contents of the book.
*/
public TableOfContents getTableOfContents() {
return tableOfContents;
}
public void setTableOfContents(TableOfContents tableOfContents) {
this.tableOfContents = tableOfContents;
}
/**
* The book's cover page as a Resource.
* An XHTML document containing a link to the cover image.
*
* @return The book's cover page as a Resource
*/
public Resource getCoverPage() {
Resource coverPage = guide.getCoverPage();
if (coverPage == null) {
coverPage = spine.getResource(0);
}
return coverPage;
}
public void setCoverPage(Resource coverPage) {
if (coverPage == null) {
return;
}
if (!resources.containsByHref(coverPage.getHref())) {
resources.add(coverPage);
}
guide.setCoverPage(coverPage);
}
/**
* Gets the first non-blank title from the book's metadata.
*
* @return the first non-blank title from the book's metadata.
*/
public String getTitle() {
return getMetadata().getFirstTitle();
}
/**
* The book's cover image.
*
* @return The book's cover image.
*/
public Resource getCoverImage() {
return coverImage;
}
public void setCoverImage(Resource coverImage) {
if (coverImage == null) {
return;
}
if (!resources.containsByHref(coverImage.getHref())) {
resources.add(coverImage);
}
this.coverImage = coverImage;
}
/**
* The guide; contains references to special sections of the book like
* colophon, glossary, etc.
*
* @return The guide; contains references to special sections of the book
* like colophon, glossary, etc.
*/
public Guide getGuide() {
return guide;
}
/**
* All Resources of the Book that can be reached via the Spine, the
* TableOfContents or the Guide.
* <p/>
* Consists of a list of "reachable" resources:
* <ul>
* <li>The coverpage</li>
* <li>The resources of the Spine that are not already in the result</li>
* <li>The resources of the Table of Contents that are not already in the
* result</li>
* <li>The resources of the Guide that are not already in the result</li>
* </ul>
* To get all html files that make up the epub file use
* {@link #getResources()}
* @return All Resources of the Book that can be reached via the Spine,
* the TableOfContents or the Guide.
*/
public List<Resource> getContents() {
Map<String, Resource> result = new LinkedHashMap<String, Resource>();
addToContentsResult(getCoverPage(), result);
for (SpineReference spineReference : getSpine().getSpineReferences()) {
addToContentsResult(spineReference.getResource(), result);
}
for (Resource resource : getTableOfContents().getAllUniqueResources()) {
addToContentsResult(resource, result);
}
for (GuideReference guideReference : getGuide().getReferences()) {
addToContentsResult(guideReference.getResource(), result);
}
return new ArrayList<Resource>(result.values());
}
private static void addToContentsResult(Resource resource,
Map<String, Resource> allReachableResources) {
if (resource != null && (!allReachableResources
.containsKey(resource.getHref()))) {
allReachableResources.put(resource.getHref(), resource);
}
}
public Resource getOpfResource() {
return opfResource;
}
public void setOpfResource(Resource opfResource) {
this.opfResource = opfResource;
}
public void setNcxResource(Resource ncxResource) {
this.ncxResource = ncxResource;
}
public Resource getNcxResource() {
return ncxResource;
}
}

@ -0,0 +1,104 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.epub.PackageDocumentBase;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Locale;
/**
* A Date used by the book's metadata.
*
* Examples: creation-date, modification-date, etc
*
* @author paul
*
*/
public class Date implements Serializable {
private static final long serialVersionUID = 7533866830395120136L;
public enum Event {
PUBLICATION("publication"),
MODIFICATION("modification"),
CREATION("creation");
private final String value;
Event(String v) {
value = v;
}
public static Event fromValue(String v) {
for (Event c : Event.values()) {
if (c.value.equals(v)) {
return c;
}
}
return null;
}
public String toString() {
return value;
}
}
private Event event;
private String dateString;
public Date(java.util.Date date) {
this(date, (Event) null);
}
public Date(String dateString) {
this(dateString, (Event) null);
}
public Date(java.util.Date date, Event event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date),
event);
}
public Date(String dateString, Event event) {
this.dateString = dateString;
this.event = event;
}
public Date(java.util.Date date, String event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat,Locale.US)).format(date),
event);
}
public Date(String dateString, String event) {
this(checkDate(dateString), Event.fromValue(event));
this.dateString = dateString;
}
private static String checkDate(String dateString) {
if (dateString == null) {
throw new IllegalArgumentException(
"Cannot create a date from a blank string");
}
return dateString;
}
public String getValue() {
return dateString;
}
public Event getEvent() {
return event;
}
public void setEvent(Event event) {
this.event = event;
}
public String toString() {
if (event == null) {
return dateString;
}
return "" + event + ":" + dateString;
}
}

@ -0,0 +1,33 @@
package me.ag2s.epublib.domain;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* @author jake
*/
public class EpubResourceProvider implements LazyResourceProvider {
private final String epubFilename;
/**
* @param epubFilename the file name for the epub we're created from.
*/
public EpubResourceProvider(String epubFilename) {
this.epubFilename = epubFilename;
}
@Override
public InputStream getResourceStream(String href) throws IOException {
ZipFile zipFile = new ZipFile(epubFilename);
ZipEntry zipEntry = zipFile.getEntry(href);
if (zipEntry == null) {
zipFile.close();
throw new IllegalStateException(
"Cannot find entry " + href + " in epub file " + epubFilename);
}
return new ResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
}
}

@ -0,0 +1,128 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* The guide is a selection of special pages of the book.
* Examples of these are the cover, list of illustrations, etc.
*
* It is an optional part of an epub, and support for the various types
* of references varies by reader.
*
* The only part of this that is heavily used is the cover page.
*
* @author paul
*
*/
public class Guide implements Serializable {
/**
*
*/
private static final long serialVersionUID = -6256645339915751189L;
public static final String DEFAULT_COVER_TITLE = GuideReference.COVER;
private List<GuideReference> references = new ArrayList<GuideReference>();
private static final int COVERPAGE_NOT_FOUND = -1;
private static final int COVERPAGE_UNITIALIZED = -2;
private int coverPageIndex = -1;
public List<GuideReference> getReferences() {
return references;
}
public void setReferences(List<GuideReference> references) {
this.references = references;
uncheckCoverPage();
}
private void uncheckCoverPage() {
coverPageIndex = COVERPAGE_UNITIALIZED;
}
public GuideReference getCoverReference() {
checkCoverPage();
if (coverPageIndex >= 0) {
return references.get(coverPageIndex);
}
return null;
}
public int setCoverReference(GuideReference guideReference) {
if (coverPageIndex >= 0) {
references.set(coverPageIndex, guideReference);
} else {
references.add(0, guideReference);
coverPageIndex = 0;
}
return coverPageIndex;
}
private void checkCoverPage() {
if (coverPageIndex == COVERPAGE_UNITIALIZED) {
initCoverPage();
}
}
private void initCoverPage() {
int result = COVERPAGE_NOT_FOUND;
for (int i = 0; i < references.size(); i++) {
GuideReference guideReference = references.get(i);
if (guideReference.getType().equals(GuideReference.COVER)) {
result = i;
break;
}
}
coverPageIndex = result;
}
/**
* The coverpage of the book.
*
* @return The coverpage of the book.
*/
public Resource getCoverPage() {
GuideReference guideReference = getCoverReference();
if (guideReference == null) {
return null;
}
return guideReference.getResource();
}
public void setCoverPage(Resource coverPage) {
GuideReference coverpageGuideReference = new GuideReference(coverPage,
GuideReference.COVER, DEFAULT_COVER_TITLE);
setCoverReference(coverpageGuideReference);
}
public ResourceReference addReference(GuideReference reference) {
this.references.add(reference);
uncheckCoverPage();
return reference;
}
/**
* A list of all GuideReferences that have the given
* referenceTypeName (ignoring case).
*
* @param referenceTypeName
* @return A list of all GuideReferences that have the given
* referenceTypeName (ignoring case).
*/
public List<GuideReference> getGuideReferencesByType(
String referenceTypeName) {
List<GuideReference> result = new ArrayList<GuideReference>();
for (GuideReference guideReference : references) {
if (referenceTypeName.equalsIgnoreCase(guideReference.getType())) {
result.add(guideReference);
}
}
return result;
}
}

@ -0,0 +1,102 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
/**
* These are references to elements of the book's guide.
*
* @see Guide
*
* @author paul
*
*/
public class GuideReference extends TitledResourceReference
implements Serializable {
private static final long serialVersionUID = -316179702440631834L;
/**
* the book cover(s), jacket information, etc.
*/
public static String COVER = "cover";
/**
* human-readable page with title, author, publisher, and other metadata
*/
public static String TITLE_PAGE = "title-page";
/**
* Human-readable table of contents.
* Not to be confused the epub file table of contents
*
*/
public static String TOC = "toc";
/**
* back-of-book style index
*/
public static String INDEX = "index";
public static String GLOSSARY = "glossary";
public static String ACKNOWLEDGEMENTS = "acknowledgements";
public static String BIBLIOGRAPHY = "bibliography";
public static String COLOPHON = "colophon";
public static String COPYRIGHT_PAGE = "copyright-page";
public static String DEDICATION = "dedication";
/**
* an epigraph is a phrase, quotation, or poem that is set at the
* beginning of a document or component.
*
* source: http://en.wikipedia.org/wiki/Epigraph_%28literature%29
*/
public static String EPIGRAPH = "epigraph";
public static String FOREWORD = "foreword";
/**
* list of illustrations
*/
public static String LOI = "loi";
/**
* list of tables
*/
public static String LOT = "lot";
public static String NOTES = "notes";
public static String PREFACE = "preface";
/**
* A page of content (e.g. "Chapter 1")
*/
public static String TEXT = "text";
private String type;
public GuideReference(Resource resource) {
this(resource, null);
}
public GuideReference(Resource resource, String title) {
super(resource, title);
}
public GuideReference(Resource resource, String type, String title) {
this(resource, type, title, null);
}
public GuideReference(Resource resource, String type, String title,
String fragmentId) {
super(resource, title, fragmentId);
this.type = StringUtil.isNotBlank(type) ? type.toLowerCase() : null;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

@ -0,0 +1,129 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
/**
* A Book's identifier.
*
* Defaults to a random UUID and scheme "UUID"
*
* @author paul
*/
public class Identifier implements Serializable {
private static final long serialVersionUID = 955949951416391810L;
public interface Scheme {
String UUID = "UUID";
String ISBN = "ISBN";
String URL = "URL";
String URI = "URI";
}
private boolean bookId = false;
private String scheme;
private String value;
/**
* Creates an Identifier with as value a random UUID and scheme "UUID"
*/
public Identifier() {
this(Scheme.UUID, UUID.randomUUID().toString());
}
public Identifier(String scheme, String value) {
this.scheme = scheme;
this.value = value;
}
/**
* The first identifier for which the bookId is true is made the
* bookId identifier.
*
* If no identifier has bookId == true then the first bookId identifier
* is written as the primary.
*
* @param identifiers
* @return The first identifier for which the bookId is true is made
* the bookId identifier.
*/
public static Identifier getBookIdIdentifier(List<Identifier> identifiers) {
if (identifiers == null || identifiers.isEmpty()) {
return null;
}
Identifier result = null;
for (Identifier identifier : identifiers) {
if (identifier.isBookId()) {
result = identifier;
break;
}
}
if (result == null) {
result = identifiers.get(0);
}
return result;
}
public String getScheme() {
return scheme;
}
public void setScheme(String scheme) {
this.scheme = scheme;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public void setBookId(boolean bookId) {
this.bookId = bookId;
}
/**
* This bookId property allows the book creator to add multiple ids and
* tell the epubwriter which one to write out as the bookId.
*
* The Dublin Core metadata spec allows multiple identifiers for a Book.
* The epub spec requires exactly one identifier to be marked as the book id.
*
* @return whether this is the unique book id.
*/
public boolean isBookId() {
return bookId;
}
public int hashCode() {
return StringUtil.defaultIfNull(scheme).hashCode() ^ StringUtil
.defaultIfNull(value).hashCode();
}
public boolean equals(Object otherIdentifier) {
if (!(otherIdentifier instanceof Identifier)) {
return false;
}
return StringUtil.equals(scheme, ((Identifier) otherIdentifier).scheme)
&& StringUtil.equals(value, ((Identifier) otherIdentifier).value);
}
public String toString() {
if (StringUtil.isBlank(scheme)) {
return "" + value;
}
return "" + scheme + ":" + value;
}
}

@ -0,0 +1,135 @@
package me.ag2s.epublib.domain;
import android.util.Log;
import me.ag2s.epublib.util.IOUtil;
//import io.documentnode.minilog.Logger;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* A Resource that loads its data only on-demand from a EPUB book file.
* This way larger books can fit into memory and can be opened faster.
*/
public class LazyResource extends Resource {
private static final long serialVersionUID = 5089400472352002866L;
private static String TAG= LazyResource.class.getName();
private final LazyResourceProvider resourceProvider;
private final long cachedSize;
/**
* Creates a lazy resource, when the size is unknown.
*
* @param resourceProvider The resource provider loads data on demand.
* @param href The resource's href within the epub.
*/
public LazyResource(LazyResourceProvider resourceProvider, String href) {
this(resourceProvider, -1, href);
}
/**
* Creates a Lazy resource, by not actually loading the data for this entry.
*
* The data will be loaded on the first call to getData()
*
* @param resourceProvider The resource provider loads data on demand.
* @param size The size of this resource.
* @param href The resource's href within the epub.
*/
public LazyResource(
LazyResourceProvider resourceProvider, long size, String href) {
super(null, null, href, MediaTypes.determineMediaType(href));
this.resourceProvider = resourceProvider;
this.cachedSize = size;
}
/**
* Gets the contents of the Resource as an InputStream.
*
* @return The contents of the Resource.
*
* @throws IOException
*/
public InputStream getInputStream() throws IOException {
if (isInitialized()) {
return new ByteArrayInputStream(getData());
} else {
return resourceProvider.getResourceStream(this.originalHref);
}
}
/**
* Initializes the resource by loading its data into memory.
*
* @throws IOException
*/
public void initialize() throws IOException {
getData();
}
/**
* The contents of the resource as a byte[]
*
* If this resource was lazy-loaded and the data was not yet loaded,
* it will be loaded into memory at this point.
* This included opening the zip file, so expect a first load to be slow.
*
* @return The contents of the resource
*/
public byte[] getData() throws IOException {
if (data == null) {
Log.d(TAG, "Initializing lazy resource: " + this.getHref());
InputStream in = resourceProvider.getResourceStream(this.originalHref);
byte[] readData = IOUtil.toByteArray(in, (int) this.cachedSize);
if (readData == null) {
throw new IOException(
"Could not load the contents of resource: " + this.getHref());
} else {
this.data = readData;
}
in.close();
}
return data;
}
/**
* Tells this resource to release its cached data.
*
* If this resource was not lazy-loaded, this is a no-op.
*/
public void close() {
if (this.resourceProvider != null) {
this.data = null;
}
}
/**
* Returns if the data for this resource has been loaded into memory.
*
* @return true if data was loaded.
*/
public boolean isInitialized() {
return data != null;
}
/**
* Returns the size of this resource in bytes.
*
* @return the size.
*/
public long getSize() {
if (data != null) {
return data.length;
}
return cachedSize;
}
}

@ -0,0 +1,12 @@
package me.ag2s.epublib.domain;
import java.io.IOException;
import java.io.InputStream;
/**
* @author jake
*/
public interface LazyResourceProvider {
InputStream getResourceStream(String href) throws IOException;
}

@ -0,0 +1,21 @@
package me.ag2s.epublib.domain;
public enum ManifestItemProperties implements ManifestProperties {
COVER_IMAGE("cover-image"),
MATHML("mathml"),
NAV("nav"),
REMOTE_RESOURCES("remote-resources"),
SCRIPTED("scripted"),
SVG("svg"),
SWITCH("switch");
private String name;
ManifestItemProperties(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

@ -0,0 +1,16 @@
package me.ag2s.epublib.domain;
public enum ManifestItemRefProperties implements ManifestProperties {
PAGE_SPREAD_LEFT("page-spread-left"),
PAGE_SPREAD_RIGHT("page-spread-right");
private String name;
ManifestItemRefProperties(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

@ -0,0 +1,6 @@
package me.ag2s.epublib.domain;
public interface ManifestProperties {
String getName();
}

@ -0,0 +1,73 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
/**
* MediaType is used to tell the type of content a resource is.
*
* Examples of mediatypes are image/gif, text/css and application/xhtml+xml
*
* All allowed mediaTypes are maintained bye the MediaTypeService.
*
* @see MediaTypes
*
* @author paul
*/
public class MediaType implements Serializable {
private static final long serialVersionUID = -7256091153727506788L;
private String name;
private String defaultExtension;
private Collection<String> extensions;
public MediaType(String name, String defaultExtension) {
this(name, defaultExtension, new String[]{defaultExtension});
}
public MediaType(String name, String defaultExtension,
String[] extensions) {
this(name, defaultExtension, Arrays.asList(extensions));
}
public int hashCode() {
if (name == null) {
return 0;
}
return name.hashCode();
}
public MediaType(String name, String defaultExtension,
Collection<String> extensions) {
super();
this.name = name;
this.defaultExtension = defaultExtension;
this.extensions = extensions;
}
public String getName() {
return name;
}
public String getDefaultExtension() {
return defaultExtension;
}
public Collection<String> getExtensions() {
return extensions;
}
public boolean equals(Object otherMediaType) {
if (!(otherMediaType instanceof MediaType)) {
return false;
}
return name.equals(((MediaType) otherMediaType).getName());
}
public String toString() {
return name;
}
}

@ -0,0 +1,95 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.util.StringUtil;
import java.util.HashMap;
import java.util.Map;
/**
* Manages mediatypes that are used by epubs
*
* @author paul
*/
public class MediaTypes {
public static final MediaType XHTML = new MediaType("application/xhtml+xml",
".xhtml", new String[]{".htm", ".html", ".xhtml"});
public static final MediaType EPUB = new MediaType("application/epub+zip",
".epub");
public static final MediaType NCX = new MediaType("application/x-dtbncx+xml",
".ncx");
public static final MediaType JAVASCRIPT = new MediaType("text/javascript",
".js");
public static final MediaType CSS = new MediaType("text/css", ".css");
// images
public static final MediaType JPG = new MediaType("image/jpeg", ".jpg",
new String[]{".jpg", ".jpeg"});
public static final MediaType PNG = new MediaType("image/png", ".png");
public static final MediaType GIF = new MediaType("image/gif", ".gif");
public static final MediaType SVG = new MediaType("image/svg+xml", ".svg");
// fonts
public static final MediaType TTF = new MediaType(
"application/x-truetype-font", ".ttf");
public static final MediaType OPENTYPE = new MediaType(
"application/vnd.ms-opentype", ".otf");
public static final MediaType WOFF = new MediaType("application/font-woff",
".woff");
// audio
public static final MediaType MP3 = new MediaType("audio/mpeg", ".mp3");
public static final MediaType OGG = new MediaType("audio/ogg", ".ogg");
// video
public static final MediaType MP4 = new MediaType("video/mp4", ".mp4");
public static final MediaType SMIL = new MediaType("application/smil+xml",
".smil");
public static final MediaType XPGT = new MediaType(
"application/adobe-page-template+xml", ".xpgt");
public static final MediaType PLS = new MediaType("application/pls+xml",
".pls");
public static MediaType[] mediaTypes = new MediaType[]{
XHTML, EPUB, JPG, PNG, GIF, CSS, SVG, TTF, NCX, XPGT, OPENTYPE, WOFF,
SMIL, PLS, JAVASCRIPT, MP3, MP4, OGG
};
public static Map<String, MediaType> mediaTypesByName = new HashMap<String, MediaType>();
static {
for (int i = 0; i < mediaTypes.length; i++) {
mediaTypesByName.put(mediaTypes[i].getName(), mediaTypes[i]);
}
}
public static boolean isBitmapImage(MediaType mediaType) {
return mediaType == JPG || mediaType == PNG || mediaType == GIF;
}
/**
* Gets the MediaType based on the file extension.
* Null of no matching extension found.
*
* @param filename
* @return the MediaType based on the file extension.
*/
public static MediaType determineMediaType(String filename) {
for (MediaType mediaType : mediaTypesByName.values()) {
for (String extension : mediaType.getExtensions()) {
if (StringUtil.endsWithIgnoreCase(filename, extension)) {
return mediaType;
}
}
}
return null;
}
public static MediaType getMediaTypeByName(String mediaTypeName) {
return mediaTypesByName.get(mediaTypeName);
}
}

@ -0,0 +1,230 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
/**
* A Book's collection of Metadata.
* In the future it should contain all Dublin Core attributes, for now
* it contains a set of often-used ones.
*
* @author paul
*/
public class Metadata implements Serializable {
private static final long serialVersionUID = -2437262888962149444L;
public static final String DEFAULT_LANGUAGE = "en";
private boolean autoGeneratedId = true;
private List<Author> authors = new ArrayList<Author>();
private List<Author> contributors = new ArrayList<Author>();
private List<Date> dates = new ArrayList<Date>();
private String language = DEFAULT_LANGUAGE;
private Map<QName, String> otherProperties = new HashMap<QName, String>();
private List<String> rights = new ArrayList<String>();
private List<String> titles = new ArrayList<String>();
private List<Identifier> identifiers = new ArrayList<Identifier>();
private List<String> subjects = new ArrayList<String>();
private String format = MediaTypes.EPUB.getName();
private List<String> types = new ArrayList<String>();
private List<String> descriptions = new ArrayList<String>();
private List<String> publishers = new ArrayList<String>();
private Map<String, String> metaAttributes = new HashMap<String, String>();
public Metadata() {
identifiers.add(new Identifier());
autoGeneratedId = true;
}
public boolean isAutoGeneratedId() {
return autoGeneratedId;
}
/**
* Metadata properties not hard-coded like the author, title, etc.
*
* @return Metadata properties not hard-coded like the author, title, etc.
*/
public Map<QName, String> getOtherProperties() {
return otherProperties;
}
public void setOtherProperties(Map<QName, String> otherProperties) {
this.otherProperties = otherProperties;
}
public Date addDate(Date date) {
this.dates.add(date);
return date;
}
public List<Date> getDates() {
return dates;
}
public void setDates(List<Date> dates) {
this.dates = dates;
}
public Author addAuthor(Author author) {
authors.add(author);
return author;
}
public List<Author> getAuthors() {
return authors;
}
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
public Author addContributor(Author contributor) {
contributors.add(contributor);
return contributor;
}
public List<Author> getContributors() {
return contributors;
}
public void setContributors(List<Author> contributors) {
this.contributors = contributors;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public List<String> getSubjects() {
return subjects;
}
public void setSubjects(List<String> subjects) {
this.subjects = subjects;
}
public void setRights(List<String> rights) {
this.rights = rights;
}
public List<String> getRights() {
return rights;
}
/**
* Gets the first non-blank title of the book.
* Will return "" if no title found.
*
* @return the first non-blank title of the book.
*/
public String getFirstTitle() {
if (titles == null || titles.isEmpty()) {
return "";
}
for (String title : titles) {
if (StringUtil.isNotBlank(title)) {
return title;
}
}
return "";
}
public String addTitle(String title) {
this.titles.add(title);
return title;
}
public void setTitles(List<String> titles) {
this.titles = titles;
}
public List<String> getTitles() {
return titles;
}
public String addPublisher(String publisher) {
this.publishers.add(publisher);
return publisher;
}
public void setPublishers(List<String> publishers) {
this.publishers = publishers;
}
public List<String> getPublishers() {
return publishers;
}
public String addDescription(String description) {
this.descriptions.add(description);
return description;
}
public void setDescriptions(List<String> descriptions) {
this.descriptions = descriptions;
}
public List<String> getDescriptions() {
return descriptions;
}
public Identifier addIdentifier(Identifier identifier) {
if (autoGeneratedId && (!(identifiers.isEmpty()))) {
identifiers.set(0, identifier);
} else {
identifiers.add(identifier);
}
autoGeneratedId = false;
return identifier;
}
public void setIdentifiers(List<Identifier> identifiers) {
this.identifiers = identifiers;
autoGeneratedId = false;
}
public List<Identifier> getIdentifiers() {
return identifiers;
}
public void setFormat(String format) {
this.format = format;
}
public String getFormat() {
return format;
}
public String addType(String type) {
this.types.add(type);
return type;
}
public List<String> getTypes() {
return types;
}
public void setTypes(List<String> types) {
this.types = types;
}
public String getMetaAttribute(String name) {
return metaAttributes.get(name);
}
public void setMetaAttributes(Map<String, String> metaAttributes) {
this.metaAttributes = metaAttributes;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,327 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.IOUtil;
import me.ag2s.epublib.util.StringUtil;
import me.ag2s.epublib.util.commons.io.XmlStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Serializable;
/**
* Represents a resource that is part of the epub.
* A resource can be a html file, image, xml, etc.
*
* @author paul
*
*/
public class Resource implements Serializable {
private static final long serialVersionUID = 1043946707835004037L;
private String id;
private String title;
private String href;
private String properties;
protected String originalHref;
private MediaType mediaType;
private String inputEncoding = Constants.CHARACTER_ENCODING;
protected byte[] data;
/**
* Creates an empty Resource with the given href.
*
* Assumes that if the data is of a text type (html/css/etc) then the encoding will be UTF-8
*
* @param href The location of the resource within the epub. Example: "chapter1.html".
*/
public Resource(String href) {
this(null, new byte[0], href, MediaTypes.determineMediaType(href));
}
/**
* Creates a Resource with the given data and MediaType.
* The href will be automatically generated.
*
* Assumes that if the data is of a text type (html/css/etc) then the encoding will be UTF-8
*
* @param data The Resource's contents
* @param mediaType The MediaType of the Resource
*/
public Resource(byte[] data, MediaType mediaType) {
this(null, data, null, mediaType);
}
/**
* Creates a resource with the given data at the specified href.
* The MediaType will be determined based on the href extension.
*
* Assumes that if the data is of a text type (html/css/etc) then the encoding will be UTF-8
*
* @see MediaTypes#determineMediaType(String)
*
* @param data The Resource's contents
* @param href The location of the resource within the epub. Example: "chapter1.html".
*/
public Resource(byte[] data, String href) {
this(null, data, href, MediaTypes.determineMediaType(href),
Constants.CHARACTER_ENCODING);
}
/**
* Creates a resource with the data from the given Reader at the specified href.
* The MediaType will be determined based on the href extension.
*
* @see MediaTypes#determineMediaType(String)
*
* @param in The Resource's contents
* @param href The location of the resource within the epub. Example: "cover.jpg".
*/
public Resource(Reader in, String href) throws IOException {
this(null, IOUtil.toByteArray(in, Constants.CHARACTER_ENCODING), href,
MediaTypes.determineMediaType(href),
Constants.CHARACTER_ENCODING);
}
/**
* Creates a resource with the data from the given InputStream at the specified href.
* The MediaType will be determined based on the href extension.
*
* @see MediaTypes#determineMediaType(String)
*
* Assumes that if the data is of a text type (html/css/etc) then the encoding will be UTF-8
*
* It is recommended to us the {@link #Resource(Reader, String)} method for creating textual
* (html/css/etc) resources to prevent encoding problems.
* Use this method only for binary Resources like images, fonts, etc.
*
*
* @param in The Resource's contents
* @param href The location of the resource within the epub. Example: "cover.jpg".
*/
public Resource(InputStream in, String href) throws IOException {
this(null, IOUtil.toByteArray(in), href,
MediaTypes.determineMediaType(href));
}
/**
* Creates a resource with the given id, data, mediatype at the specified href.
* Assumes that if the data is of a text type (html/css/etc) then the encoding will be UTF-8
*
* @param id The id of the Resource. Internal use only. Will be auto-generated if it has a null-value.
* @param data The Resource's contents
* @param href The location of the resource within the epub. Example: "chapter1.html".
* @param mediaType The resources MediaType
*/
public Resource(String id, byte[] data, String href, MediaType mediaType) {
this(id, data, href, mediaType, Constants.CHARACTER_ENCODING);
}
/**
* Creates a resource with the given id, data, mediatype at the specified href.
* If the data is of a text type (html/css/etc) then it will use the given inputEncoding.
*
* @param id The id of the Resource. Internal use only. Will be auto-generated if it has a null-value.
* @param data The Resource's contents
* @param href The location of the resource within the epub. Example: "chapter1.html".
* @param mediaType The resources MediaType
* @param inputEncoding If the data is of a text type (html/css/etc) then it will use the given inputEncoding.
*/
public Resource(String id, byte[] data, String href, MediaType mediaType,
String inputEncoding) {
this.id = id;
this.href = href;
this.originalHref = href;
this.mediaType = mediaType;
this.inputEncoding = inputEncoding;
this.data = data;
}
/**
* Gets the contents of the Resource as an InputStream.
*
* @return The contents of the Resource.
*
* @throws IOException
*/
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(getData());
}
/**
* The contents of the resource as a byte[]
*
* @return The contents of the resource
*/
public byte[] getData() throws IOException {
return data;
}
/**
* Tells this resource to release its cached data.
*
* If this resource was not lazy-loaded, this is a no-op.
*/
public void close() {
}
/**
* Sets the data of the Resource.
* If the data is a of a different type then the original data then make sure to change the MediaType.
*
* @param data
*/
public void setData(byte[] data) {
this.data = data;
}
/**
* Returns the size of this resource in bytes.
*
* @return the size.
*/
public long getSize() {
return data.length;
}
/**
* If the title is found by scanning the underlying html document then it is cached here.
*
* @return the title
*/
public String getTitle() {
return title;
}
/**
* Sets the Resource's id: Make sure it is unique and a valid identifier.
*
* @param id
*/
public void setId(String id) {
this.id = id;
}
/**
* The resources Id.
*
* Must be both unique within all the resources of this book and a valid identifier.
* @return The resources Id.
*/
public String getId() {
return id;
}
/**
* The location of the resource within the contents folder of the epub file.
*
* Example:<br/>
* images/cover.jpg<br/>
* content/chapter1.xhtml<br/>
*
* @return The location of the resource within the contents folder of the epub file.
*/
public String getHref() {
return href;
}
/**
* Sets the Resource's href.
*
* @param href
*/
public void setHref(String href) {
this.href = href;
}
/**
* The character encoding of the resource.
* Is allowed to be null for non-text resources like images.
*
* @return The character encoding of the resource.
*/
public String getInputEncoding() {
return inputEncoding;
}
/**
* Sets the Resource's input character encoding.
*
* @param encoding
*/
public void setInputEncoding(String encoding) {
this.inputEncoding = encoding;
}
/**
* Gets the contents of the Resource as Reader.
*
* Does all sorts of smart things (courtesy of apache commons io XMLStreamREader) to handle encodings, byte order markers, etc.
*
* @return the contents of the Resource as Reader.
* @throws IOException
*/
public Reader getReader() throws IOException {
return new XmlStreamReader(new ByteArrayInputStream(getData()),
getInputEncoding());
}
/**
* Gets the hashCode of the Resource's href.
*
*/
public int hashCode() {
return href.hashCode();
}
/**
* Checks to see of the given resourceObject is a resource and whether its href is equal to this one.
*
* @return whether the given resourceObject is a resource and whether its href is equal to this one.
*/
public boolean equals(Object resourceObject) {
if (!(resourceObject instanceof Resource)) {
return false;
}
return href.equals(((Resource) resourceObject).getHref());
}
/**
* This resource's mediaType.
*
* @return This resource's mediaType.
*/
public MediaType getMediaType() {
return mediaType;
}
public void setMediaType(MediaType mediaType) {
this.mediaType = mediaType;
}
public void setTitle(String title) {
this.title = title;
}
public String getProperties() {
return properties;
}
public void setProperties(String properties) {
this.properties = properties;
}
public String toString() {
return StringUtil.toString("id", id,
"title", title,
"encoding", inputEncoding,
"mediaType", mediaType,
"href", href,
"size", (data == null ? 0 : data.length));
}
}

@ -0,0 +1,36 @@
package me.ag2s.epublib.domain;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipFile;
/**
* A wrapper class for closing a ZipFile object when the InputStream derived
* from it is closed.
*
* @author ttopalov
*/
public class ResourceInputStream extends FilterInputStream {
private final ZipFile zipFile;
/**
* Constructor.
*
* @param in
* The InputStream object.
* @param zipFile
* The ZipFile object.
*/
public ResourceInputStream(InputStream in, ZipFile zipFile) {
super(in);
this.zipFile = zipFile;
}
@Override
public void close() throws IOException {
super.close();
zipFile.close();
}
}

@ -0,0 +1,43 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
public class ResourceReference implements Serializable {
private static final long serialVersionUID = 2596967243557743048L;
protected Resource resource;
public ResourceReference(Resource resource) {
this.resource = resource;
}
public Resource getResource() {
return resource;
}
/**
* Besides setting the resource it also sets the fragmentId to null.
*
* @param resource
*/
public void setResource(Resource resource) {
this.resource = resource;
}
/**
* The id of the reference referred to.
*
* null of the reference is null or has a null id itself.
*
* @return The id of the reference referred to.
*/
public String getResourceId() {
if (resource != null) {
return resource.getId();
}
return null;
}
}

@ -0,0 +1,387 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* All the resources that make up the book.
* XHTML files, images and epub xml documents must be here.
*
* @author paul
*
*/
public class Resources implements Serializable {
private static final long serialVersionUID = 2450876953383871451L;
private static final String IMAGE_PREFIX = "image_";
private static final String ITEM_PREFIX = "item_";
private int lastId = 1;
private Map<String, Resource> resources = new HashMap<>();
/**
* Adds a resource to the resources.
*
* Fixes the resources id and href if necessary.
*
* @param resource
* @return the newly added resource
*/
public Resource add(Resource resource) {
fixResourceHref(resource);
fixResourceId(resource);
this.resources.put(resource.getHref(), resource);
return resource;
}
/**
* Checks the id of the given resource and changes to a unique identifier if it isn't one already.
*
* @param resource
*/
public void fixResourceId(Resource resource) {
String resourceId = resource.getId();
// first try and create a unique id based on the resource's href
if (StringUtil.isBlank(resource.getId())) {
resourceId = StringUtil.substringBeforeLast(resource.getHref(), '.');
resourceId = StringUtil.substringAfterLast(resourceId, '/');
}
resourceId = makeValidId(resourceId, resource);
// check if the id is unique. if not: create one from scratch
if (StringUtil.isBlank(resourceId) || containsId(resourceId)) {
resourceId = createUniqueResourceId(resource);
}
resource.setId(resourceId);
}
/**
* Check if the id is a valid identifier. if not: prepend with valid identifier
*
* @param resource
* @return a valid id
*/
private String makeValidId(String resourceId, Resource resource) {
if (StringUtil.isNotBlank(resourceId) && !Character
.isJavaIdentifierStart(resourceId.charAt(0))) {
resourceId = getResourceItemPrefix(resource) + resourceId;
}
return resourceId;
}
private String getResourceItemPrefix(Resource resource) {
String result;
if (MediaTypes.isBitmapImage(resource.getMediaType())) {
result = IMAGE_PREFIX;
} else {
result = ITEM_PREFIX;
}
return result;
}
/**
* Creates a new resource id that is guaranteed to be unique for this set of Resources
*
* @param resource
* @return a new resource id that is guaranteed to be unique for this set of Resources
*/
private String createUniqueResourceId(Resource resource) {
int counter = lastId;
if (counter == Integer.MAX_VALUE) {
if (resources.size() == Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Resources contains " + Integer.MAX_VALUE
+ " elements: no new elements can be added");
} else {
counter = 1;
}
}
String prefix = getResourceItemPrefix(resource);
String result = prefix + counter;
while (containsId(result)) {
result = prefix + (++counter);
}
lastId = counter;
return result;
}
/**
* Whether the map of resources already contains a resource with the given id.
*
* @param id
* @return Whether the map of resources already contains a resource with the given id.
*/
public boolean containsId(String id) {
if (StringUtil.isBlank(id)) {
return false;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return true;
}
}
return false;
}
/**
* Gets the resource with the given id.
*
* @param id
* @return null if not found
*/
public Resource getById(String id) {
if (StringUtil.isBlank(id)) {
return null;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return resource;
}
}
return null;
}
public Resource getByProperties(String properties) {
if (StringUtil.isBlank(properties)) {
return null;
}
for (Resource resource : resources.values()) {
if (properties.equals(resource.getProperties())) {
return resource;
}
}
return null;
}
/**
* Remove the resource with the given href.
*
* @param href
* @return the removed resource, null if not found
*/
public Resource remove(String href) {
return resources.remove(href);
}
private void fixResourceHref(Resource resource) {
if (StringUtil.isNotBlank(resource.getHref())
&& !resources.containsKey(resource.getHref())) {
return;
}
if (StringUtil.isBlank(resource.getHref())) {
if (resource.getMediaType() == null) {
throw new IllegalArgumentException(
"Resource must have either a MediaType or a href");
}
int i = 1;
String href = createHref(resource.getMediaType(), i);
while (resources.containsKey(href)) {
href = createHref(resource.getMediaType(), (++i));
}
resource.setHref(href);
}
}
private String createHref(MediaType mediaType, int counter) {
if (MediaTypes.isBitmapImage(mediaType)) {
return "image_" + counter + mediaType.getDefaultExtension();
} else {
return "item_" + counter + mediaType.getDefaultExtension();
}
}
public boolean isEmpty() {
return resources.isEmpty();
}
/**
* The number of resources
* @return The number of resources
*/
public int size() {
return resources.size();
}
/**
* The resources that make up this book.
* Resources can be xhtml pages, images, xml documents, etc.
*
* @return The resources that make up this book.
*/
public Map<String, Resource> getResourceMap() {
return resources;
}
public Collection<Resource> getAll() {
return resources.values();
}
/**
* Whether there exists a resource with the given href
* @param href
* @return Whether there exists a resource with the given href
*/
public boolean containsByHref(String href) {
if (StringUtil.isBlank(href)) {
return false;
}
return resources.containsKey(
StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR));
}
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources
*/
public void set(Collection<Resource> resources) {
this.resources.clear();
addAll(resources);
}
/**
* Adds all resources from the given Collection of resources to the existing collection.
*
* @param resources
*/
public void addAll(Collection<Resource> resources) {
for (Resource resource : resources) {
fixResourceHref(resource);
this.resources.put(resource.getHref(), resource);
}
}
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources A map with as keys the resources href and as values the Resources
*/
public void set(Map<String, Resource> resources) {
this.resources = new HashMap<String, Resource>(resources);
}
/**
* First tries to find a resource with as id the given idOrHref, if that
* fails it tries to find one with the idOrHref as href.
*
* @param idOrHref
* @return the found Resource
*/
public Resource getByIdOrHref(String idOrHref) {
Resource resource = getById(idOrHref);
if (resource == null) {
resource = getByHref(idOrHref);
}
return resource;
}
/**
* Gets the resource with the given href.
* If the given href contains a fragmentId then that fragment id will be ignored.
*
* @param href
* @return null if not found.
*/
public Resource getByHref(String href) {
if (StringUtil.isBlank(href)) {
return null;
}
href = StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR);
Resource result = resources.get(href);
return result;
}
/**
* Gets the first resource (random order) with the give mediatype.
*
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
*
* @param mediaType
* @return the first resource (random order) with the give mediatype.
*/
public Resource findFirstResourceByMediaType(MediaType mediaType) {
return findFirstResourceByMediaType(resources.values(), mediaType);
}
/**
* Gets the first resource (random order) with the give mediatype.
*
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
*
* @param mediaType
* @return the first resource (random order) with the give mediatype.
*/
public static Resource findFirstResourceByMediaType(
Collection<Resource> resources, MediaType mediaType) {
for (Resource resource : resources) {
if (resource.getMediaType() == mediaType) {
return resource;
}
}
return null;
}
/**
* All resources that have the given MediaType.
*
* @param mediaType
* @return All resources that have the given MediaType.
*/
public List<Resource> getResourcesByMediaType(MediaType mediaType) {
List<Resource> result = new ArrayList<Resource>();
if (mediaType == null) {
return result;
}
for (Resource resource : getAll()) {
if (resource.getMediaType() == mediaType) {
result.add(resource);
}
}
return result;
}
/**
* All Resources that match any of the given list of MediaTypes
*
* @param mediaTypes
* @return All Resources that match any of the given list of MediaTypes
*/
public List<Resource> getResourcesByMediaTypes(MediaType[] mediaTypes) {
List<Resource> result = new ArrayList<Resource>();
if (mediaTypes == null) {
return result;
}
// this is the fastest way of doing this according to
// http://stackoverflow.com/questions/1128723/in-java-how-can-i-test-if-an-array-contains-a-certain-value
List<MediaType> mediaTypesList = Arrays.asList(mediaTypes);
for (Resource resource : getAll()) {
if (mediaTypesList.contains(resource.getMediaType())) {
result.add(resource);
}
}
return result;
}
/**
* All resource hrefs
*
* @return all resource hrefs
*/
public Collection<String> getAllHrefs() {
return resources.keySet();
}
}

@ -0,0 +1,190 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* The spine sections are the sections of the book in the order in which the book should be read.
*
* This contrasts with the Table of Contents sections which is an index into the Book's sections.
*
* @see TableOfContents
*
* @author paul
*/
public class Spine implements Serializable {
private static final long serialVersionUID = 3878483958947357246L;
private Resource tocResource;
private List<SpineReference> spineReferences;
public Spine() {
this(new ArrayList<SpineReference>());
}
/**
* Creates a spine out of all the resources in the table of contents.
*
* @param tableOfContents
*/
public Spine(TableOfContents tableOfContents) {
this.spineReferences = createSpineReferences(
tableOfContents.getAllUniqueResources());
}
public Spine(List<SpineReference> spineReferences) {
this.spineReferences = spineReferences;
}
public static List<SpineReference> createSpineReferences(
Collection<Resource> resources) {
List<SpineReference> result = new ArrayList<SpineReference>(
resources.size());
for (Resource resource : resources) {
result.add(new SpineReference(resource));
}
return result;
}
public List<SpineReference> getSpineReferences() {
return spineReferences;
}
public void setSpineReferences(List<SpineReference> spineReferences) {
this.spineReferences = spineReferences;
}
/**
* Gets the resource at the given index.
* Null if not found.
*
* @param index
* @return the resource at the given index.
*/
public Resource getResource(int index) {
if (index < 0 || index >= spineReferences.size()) {
return null;
}
return spineReferences.get(index).getResource();
}
/**
* Finds the first resource that has the given resourceId.
*
* Null if not found.
*
* @param resourceId
* @return the first resource that has the given resourceId.
*/
public int findFirstResourceById(String resourceId) {
if (StringUtil.isBlank(resourceId)) {
return -1;
}
for (int i = 0; i < spineReferences.size(); i++) {
SpineReference spineReference = spineReferences.get(i);
if (resourceId.equals(spineReference.getResourceId())) {
return i;
}
}
return -1;
}
/**
* Adds the given spineReference to the spine references and returns it.
*
* @param spineReference
* @return the given spineReference
*/
public SpineReference addSpineReference(SpineReference spineReference) {
if (spineReferences == null) {
this.spineReferences = new ArrayList<SpineReference>();
}
spineReferences.add(spineReference);
return spineReference;
}
/**
* Adds the given resource to the spine references and returns it.
*
* @return the given spineReference
*/
public SpineReference addResource(Resource resource) {
return addSpineReference(new SpineReference(resource));
}
/**
* The number of elements in the spine.
*
* @return The number of elements in the spine.
*/
public int size() {
return spineReferences.size();
}
/**
* As per the epub file format the spine officially maintains a reference to the Table of Contents.
* The epubwriter will look for it here first, followed by some clever tricks to find it elsewhere if not found.
* Put it here to be sure of the expected behaviours.
*
* @param tocResource
*/
public void setTocResource(Resource tocResource) {
this.tocResource = tocResource;
}
/**
* The resource containing the XML for the tableOfContents.
* When saving an epub file this resource needs to be in this place.
*
* @return The resource containing the XML for the tableOfContents.
*/
public Resource getTocResource() {
return tocResource;
}
/**
* The position within the spine of the given resource.
*
* @param currentResource
* @return something &lt; 0 if not found.
*
*/
public int getResourceIndex(Resource currentResource) {
if (currentResource == null) {
return -1;
}
return getResourceIndex(currentResource.getHref());
}
/**
* The first position within the spine of a resource with the given href.
*
* @return something &lt; 0 if not found.
*
*/
public int getResourceIndex(String resourceHref) {
int result = -1;
if (StringUtil.isBlank(resourceHref)) {
return result;
}
for (int i = 0; i < spineReferences.size(); i++) {
if (resourceHref.equals(spineReferences.get(i).getResource().getHref())) {
result = i;
break;
}
}
return result;
}
/**
* Whether the spine has any references
* @return Whether the spine has any references
*/
public boolean isEmpty() {
return spineReferences.isEmpty();
}
}

@ -0,0 +1,52 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
/**
* A Section of a book.
* Represents both an item in the package document and a item in the index.
*
* @author paul
*/
public class SpineReference extends ResourceReference implements Serializable {
private static final long serialVersionUID = -7921609197351510248L;
private boolean linear = true;
public SpineReference(Resource resource) {
this(resource, true);
}
public SpineReference(Resource resource, boolean linear) {
super(resource);
this.linear = linear;
}
/**
* Linear denotes whether the section is Primary or Auxiliary.
* Usually the cover page has linear set to false and all the other sections
* have it set to true.
*
* It's an optional property that readers may also ignore.
*
* <blockquote>primary or auxiliary is useful for Reading Systems which
* opt to present auxiliary content differently than primary content.
* For example, a Reading System might opt to render auxiliary content in
* a popup window apart from the main window which presents the primary
* content. (For an example of the types of content that may be considered
* auxiliary, refer to the example below and the subsequent discussion.)</blockquote>
* @see <a href="http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4">OPF Spine specification</a>
*
* @return whether the section is Primary or Auxiliary.
*/
public boolean isLinear() {
return linear;
}
public void setLinear(boolean linear) {
this.linear = linear;
}
}

@ -0,0 +1,59 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* An item in the Table of Contents.
*
* @see TableOfContents
*
* @author paul
*/
public class TOCReference extends TitledResourceReference
implements Serializable {
private static final long serialVersionUID = 5787958246077042456L;
private List<TOCReference> children;
private static final Comparator<TOCReference> COMPARATOR_BY_TITLE_IGNORE_CASE =
(tocReference1, tocReference2) ->
String.CASE_INSENSITIVE_ORDER
.compare(tocReference1.getTitle(), tocReference2.getTitle());
public TOCReference() {
this(null, null, null);
}
public TOCReference(String name, Resource resource) {
this(name, resource, null);
}
public TOCReference(String name, Resource resource, String fragmentId) {
this(name, resource, fragmentId, new ArrayList<>());
}
public TOCReference(String title, Resource resource, String fragmentId,
List<TOCReference> children) {
super(resource, title, fragmentId);
this.children = children;
}
public static Comparator<TOCReference> getComparatorByTitleIgnoreCase() {
return COMPARATOR_BY_TITLE_IGNORE_CASE;
}
public List<TOCReference> getChildren() {
return children;
}
public TOCReference addChildSection(TOCReference childSection) {
this.children.add(childSection);
return childSection;
}
public void setChildren(List<TOCReference> children) {
this.children = children;
}
}

@ -0,0 +1,259 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* The table of contents of the book.
* The TableOfContents is a tree structure at the root it is a list of TOCReferences, each if which may have as children another list of TOCReferences.
*
* The table of contents is used by epub as a quick index to chapters and sections within chapters.
* It may contain duplicate entries, may decide to point not to certain chapters, etc.
*
* See the spine for the complete list of sections in the order in which they should be read.
*
* @see Spine
*
* @author paul
*/
public class TableOfContents implements Serializable {
private static final long serialVersionUID = -3147391239966275152L;
public static final String DEFAULT_PATH_SEPARATOR = "/";
private List<TOCReference> tocReferences;
public TableOfContents() {
this(new ArrayList<TOCReference>());
}
public TableOfContents(List<TOCReference> tocReferences) {
this.tocReferences = tocReferences;
}
public List<TOCReference> getTocReferences() {
return tocReferences;
}
public void setTocReferences(List<TOCReference> tocReferences) {
this.tocReferences = tocReferences;
}
/**
* Calls addTOCReferenceAtLocation after splitting the path using the DEFAULT_PATH_SEPARATOR.
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String path) {
return addSection(resource, path, DEFAULT_PATH_SEPARATOR);
}
/**
* Calls addTOCReferenceAtLocation after splitting the path using the given pathSeparator.
*
* @param resource
* @param path
* @param pathSeparator
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String path,
String pathSeparator) {
String[] pathElements = path.split(pathSeparator);
return addSection(resource, pathElements);
}
/**
* Finds the first TOCReference in the given list that has the same title as the given Title.
*
* @param title
* @param tocReferences
* @return null if not found.
*/
private static TOCReference findTocReferenceByTitle(String title,
List<TOCReference> tocReferences) {
for (TOCReference tocReference : tocReferences) {
if (title.equals(tocReference.getTitle())) {
return tocReference;
}
}
return null;
}
/**
* Adds the given Resources to the TableOfContents at the location specified by the pathElements.
*
* Example:
* Calling this method with a Resource and new String[] {"chapter1", "paragraph1"} will result in the following:
* <ul>
* <li>a TOCReference with the title "chapter1" at the root level.<br/>
* If this TOCReference did not yet exist it will have been created and does not point to any resource</li>
* <li>A TOCReference that has the title "paragraph1". This TOCReference will be the child of TOCReference "chapter1" and
* will point to the given Resource</li>
* </ul>
*
* @param resource
* @param pathElements
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String[] pathElements) {
if (pathElements == null || pathElements.length == 0) {
return null;
}
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences;
for (int i = 0; i < pathElements.length; i++) {
String currentTitle = pathElements[i];
result = findTocReferenceByTitle(currentTitle, currentTocReferences);
if (result == null) {
result = new TOCReference(currentTitle, null);
currentTocReferences.add(result);
}
currentTocReferences = result.getChildren();
}
result.setResource(resource);
return result;
}
/**
* Adds the given Resources to the TableOfContents at the location specified by the pathElements.
*
* Example:
* Calling this method with a Resource and new int[] {0, 0} will result in the following:
* <ul>
* <li>a TOCReference at the root level.<br/>
* If this TOCReference did not yet exist it will have been created with a title of "" and does not point to any resource</li>
* <li>A TOCReference that points to the given resource and is a child of the previously created TOCReference.<br/>
* If this TOCReference didn't exist yet it will be created and have a title of ""</li>
* </ul>
*
* @param resource
* @param pathElements
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, int[] pathElements,
String sectionTitlePrefix, String sectionNumberSeparator) {
if (pathElements == null || pathElements.length == 0) {
return null;
}
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences;
for (int i = 0; i < pathElements.length; i++) {
int currentIndex = pathElements[i];
if (currentIndex > 0 && currentIndex < (currentTocReferences.size()
- 1)) {
result = currentTocReferences.get(currentIndex);
} else {
result = null;
}
if (result == null) {
paddTOCReferences(currentTocReferences, pathElements, i,
sectionTitlePrefix, sectionNumberSeparator);
result = currentTocReferences.get(currentIndex);
}
currentTocReferences = result.getChildren();
}
result.setResource(resource);
return result;
}
private void paddTOCReferences(List<TOCReference> currentTocReferences,
int[] pathElements, int pathPos, String sectionPrefix,
String sectionNumberSeparator) {
for (int i = currentTocReferences.size(); i <= pathElements[pathPos]; i++) {
String sectionTitle = createSectionTitle(pathElements, pathPos, i,
sectionPrefix,
sectionNumberSeparator);
currentTocReferences.add(new TOCReference(sectionTitle, null));
}
}
private String createSectionTitle(int[] pathElements, int pathPos,
int lastPos,
String sectionPrefix, String sectionNumberSeparator) {
StringBuilder title = new StringBuilder(sectionPrefix);
for (int i = 0; i < pathPos; i++) {
if (i > 0) {
title.append(sectionNumberSeparator);
}
title.append(pathElements[i] + 1);
}
if (pathPos > 0) {
title.append(sectionNumberSeparator);
}
title.append(lastPos + 1);
return title.toString();
}
public TOCReference addTOCReference(TOCReference tocReference) {
if (tocReferences == null) {
tocReferences = new ArrayList<>();
}
tocReferences.add(tocReference);
return tocReference;
}
/**
* All unique references (unique by href) in the order in which they are referenced to in the table of contents.
*
* @return All unique references (unique by href) in the order in which they are referenced to in the table of contents.
*/
public List<Resource> getAllUniqueResources() {
Set<String> uniqueHrefs = new HashSet<>();
List<Resource> result = new ArrayList<>();
getAllUniqueResources(uniqueHrefs, result, tocReferences);
return result;
}
private static void getAllUniqueResources(Set<String> uniqueHrefs,
List<Resource> result, List<TOCReference> tocReferences) {
for (TOCReference tocReference : tocReferences) {
Resource resource = tocReference.getResource();
if (resource != null && !uniqueHrefs.contains(resource.getHref())) {
uniqueHrefs.add(resource.getHref());
result.add(resource);
}
getAllUniqueResources(uniqueHrefs, result, tocReference.getChildren());
}
}
/**
* The total number of references in this table of contents.
*
* @return The total number of references in this table of contents.
*/
public int size() {
return getTotalSize(tocReferences);
}
private static int getTotalSize(Collection<TOCReference> tocReferences) {
int result = tocReferences.size();
for (TOCReference tocReference : tocReferences) {
result += getTotalSize(tocReference.getChildren());
}
return result;
}
/**
* The maximum depth of the reference tree
* @return The maximum depth of the reference tree
*/
public int calculateDepth() {
return calculateDepth(tocReferences, 0);
}
private int calculateDepth(List<TOCReference> tocReferences,
int currentDepth) {
int maxChildDepth = 0;
for (TOCReference tocReference : tocReferences) {
int childDepth = calculateDepth(tocReference.getChildren(), 1);
if (childDepth > maxChildDepth) {
maxChildDepth = childDepth;
}
}
return currentDepth + maxChildDepth;
}
}

@ -0,0 +1,78 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
public class TitledResourceReference extends ResourceReference
implements Serializable {
private static final long serialVersionUID = 3918155020095190080L;
private String fragmentId;
private String title;
public TitledResourceReference(Resource resource) {
this(resource, null);
}
public TitledResourceReference(Resource resource, String title) {
this(resource, title, null);
}
public TitledResourceReference(Resource resource, String title,
String fragmentId) {
super(resource);
this.title = title;
this.fragmentId = fragmentId;
}
public String getFragmentId() {
return fragmentId;
}
public void setFragmentId(String fragmentId) {
this.fragmentId = fragmentId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
/**
* If the fragmentId is blank it returns the resource href, otherwise
* it returns the resource href + '#' + the fragmentId.
*
* @return If the fragmentId is blank it returns the resource href,
* otherwise it returns the resource href + '#' + the fragmentId.
*/
public String getCompleteHref() {
if (StringUtil.isBlank(fragmentId)) {
return resource.getHref();
} else {
return resource.getHref() + Constants.FRAGMENT_SEPARATOR_CHAR
+ fragmentId;
}
}
@Override
public Resource getResource() {
resource.setTitle(title);
return resource;
}
public void setResource(Resource resource, String fragmentId) {
super.setResource(resource);
this.fragmentId = fragmentId;
}
/**
* Sets the resource to the given resource and sets the fragmentId to null.
*/
public void setResource(Resource resource) {
setResource(resource, null);
}
}

@ -0,0 +1,20 @@
package me.ag2s.epublib.epub;
import me.ag2s.epublib.domain.Book;
/**
* Post-processes a book.
*
* Can be used to clean up a book after reading or before writing.
*
* @author paul
*/
public interface BookProcessor {
/**
* A BookProcessor that returns the input book unchanged.
*/
BookProcessor IDENTITY_BOOKPROCESSOR = book -> book;
Book processBook(Book book);
}

@ -0,0 +1,72 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.domain.Book;
//import io.documentnode.minilog.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* A book processor that combines several other bookprocessors
*
* Fixes coverpage/coverimage.
* Cleans up the XHTML.
*
* @author paul.siegmann
*/
public class BookProcessorPipeline implements BookProcessor {
private static String TAG= BookProcessorPipeline.class.getName();
private List<BookProcessor> bookProcessors;
public BookProcessorPipeline() {
this(null);
}
public BookProcessorPipeline(List<BookProcessor> bookProcessingPipeline) {
this.bookProcessors = bookProcessingPipeline;
}
@Override
public Book processBook(Book book) {
if (bookProcessors == null) {
return book;
}
for (BookProcessor bookProcessor : bookProcessors) {
try {
book = bookProcessor.processBook(book);
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
}
}
return book;
}
public void addBookProcessor(BookProcessor bookProcessor) {
if (this.bookProcessors == null) {
bookProcessors = new ArrayList<BookProcessor>();
}
this.bookProcessors.add(bookProcessor);
}
public void addBookProcessors(Collection<BookProcessor> bookProcessors) {
if (this.bookProcessors == null) {
this.bookProcessors = new ArrayList<BookProcessor>();
}
this.bookProcessors.addAll(bookProcessors);
}
public List<BookProcessor> getBookProcessors() {
return bookProcessors;
}
public void setBookProcessingPipeline(
List<BookProcessor> bookProcessingPipeline) {
this.bookProcessors = bookProcessingPipeline;
}
}

@ -0,0 +1,134 @@
package me.ag2s.epublib.epub;
import me.ag2s.epublib.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* Utility methods for working with the DOM.
*
* @author paul
*
*/
// package
class DOMUtil {
/**
* First tries to get the attribute value by doing an getAttributeNS on the element, if that gets an empty element it does a getAttribute without namespace.
*
* @param element
* @param namespace
* @param attribute
* @return
*/
public static String getAttribute(Element element, String namespace,
String attribute) {
String result = element.getAttributeNS(namespace, attribute);
if (StringUtil.isEmpty(result)) {
result = element.getAttribute(attribute);
}
return result;
}
/**
* Gets all descendant elements of the given parentElement with the given namespace and tagname and returns their text child as a list of String.
*
* @param parentElement
* @param namespace
* @param tagname
* @return
*/
public static List<String> getElementsTextChild(Element parentElement,
String namespace, String tagname) {
NodeList elements = parentElement
.getElementsByTagNameNS(namespace, tagname);
List<String> result = new ArrayList<String>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) {
result.add(getTextChildrenContent((Element) elements.item(i)));
}
return result;
}
/**
* Finds in the current document the first element with the given namespace and elementName and with the given findAttributeName and findAttributeValue.
* It then returns the value of the given resultAttributeName.
*
* @param document
* @param namespace
* @param elementName
* @param findAttributeName
* @param findAttributeValue
* @param resultAttributeName
* @return
*/
public static String getFindAttributeValue(Document document,
String namespace, String elementName, String findAttributeName,
String findAttributeValue, String resultAttributeName) {
NodeList metaTags = document.getElementsByTagNameNS(namespace, elementName);
for (int i = 0; i < metaTags.getLength(); i++) {
Element metaElement = (Element) metaTags.item(i);
if (findAttributeValue
.equalsIgnoreCase(metaElement.getAttribute(findAttributeName))
&& StringUtil
.isNotBlank(metaElement.getAttribute(resultAttributeName))) {
return metaElement.getAttribute(resultAttributeName);
}
}
return null;
}
/**
* Gets the first element that is a child of the parentElement and has the given namespace and tagName
*
* @param parentElement
* @param namespace
* @param tagName
* @return
*/
public static Element getFirstElementByTagNameNS(Element parentElement,
String namespace, String tagName) {
NodeList nodes = parentElement.getElementsByTagNameNS(namespace, tagName);
if (nodes.getLength() != 0) {
return (Element) nodes.item(0);
}
nodes= parentElement.getElementsByTagName(tagName);
if (nodes.getLength()==0){
return null;
}
return (Element) nodes.item(0);
}
/**
* The contents of all Text nodes that are children of the given parentElement.
* The result is trim()-ed.
*
* The reason for this more complicated procedure instead of just returning the data of the firstChild is that
* when the text is Chinese characters then on Android each Characater is represented in the DOM as
* an individual Text node.
*
* @param parentElement
* @return
*/
public static String getTextChildrenContent(Element parentElement) {
if (parentElement == null) {
return null;
}
StringBuilder result = new StringBuilder();
NodeList childNodes = parentElement.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if ((node == null) ||
(node.getNodeType() != Node.TEXT_NODE)) {
continue;
}
result.append(((Text) node).getData());
}
return result.toString().trim();
}
}

@ -0,0 +1,138 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.Constants;
//import io.documentnode.minilog.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
//import org.kxml2.io.KXmlSerializer;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
/**
* Various low-level support methods for reading/writing epubs.
*
* @author paul.siegmann
*
*/
public class EpubProcessorSupport {
private static String TAG= EpubProcessorSupport.class.getName();
protected static DocumentBuilderFactory documentBuilderFactory;
static {
init();
}
static class EntityResolverImpl implements EntityResolver {
private String previousLocation;
@Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
String resourcePath;
if (systemId.startsWith("http:")) {
URL url = new URL(systemId);
resourcePath = "dtd/" + url.getHost() + url.getPath();
previousLocation = resourcePath
.substring(0, resourcePath.lastIndexOf('/'));
} else {
resourcePath =
previousLocation + systemId.substring(systemId.lastIndexOf('/'));
}
if (this.getClass().getClassLoader().getResource(resourcePath) == null) {
throw new RuntimeException(
"remote resource is not cached : [" + systemId
+ "] cannot continue");
}
InputStream in = EpubProcessorSupport.class.getClassLoader()
.getResourceAsStream(resourcePath);
return new InputSource(in);
}
}
private static void init() {
EpubProcessorSupport.documentBuilderFactory = DocumentBuilderFactory
.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setValidating(false);
}
public static XmlSerializer createXmlSerializer(OutputStream out)
throws UnsupportedEncodingException {
return createXmlSerializer(
new OutputStreamWriter(out, Constants.CHARACTER_ENCODING));
}
public static XmlSerializer createXmlSerializer(Writer out) {
XmlSerializer result = null;
try {
/*
* Disable XmlPullParserFactory here before it doesn't work when
* building native image using GraalVM
*/
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setValidating(true);
result = factory.newSerializer();
//result = new KXmlSerializer();
result.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
result.setOutput(out);
} catch (Exception e) {
Log.e(TAG,
"When creating XmlSerializer: " + e.getClass().getName() + ": " + e
.getMessage());
}
return result;
}
/**
* Gets an EntityResolver that loads dtd's and such from the epub4j classpath.
* In order to enable the loading of relative urls the given EntityResolver contains the previousLocation.
* Because of a new EntityResolver is created every time this method is called.
* Fortunately the EntityResolver created uses up very little memory per instance.
*
* @return an EntityResolver that loads dtd's and such from the epub4j classpath.
*/
public static EntityResolver getEntityResolver() {
return new EntityResolverImpl();
}
public DocumentBuilderFactory getDocumentBuilderFactory() {
return documentBuilderFactory;
}
/**
* Creates a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath.
*
* @return a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath.
*/
public static DocumentBuilder createDocumentBuilder() {
DocumentBuilder result = null;
try {
result = documentBuilderFactory.newDocumentBuilder();
result.setEntityResolver(getEntityResolver());
} catch (ParserConfigurationException e) {
Log.e(TAG,e.getMessage());
}
return result;
}
}

@ -0,0 +1,169 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.Resources;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Reads an epub file.
*
* @author paul
*
*/
public class EpubReader {
private static String TAG= EpubReader.class.getName();
private BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR;
public Book readEpub(InputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING);
}
public Book readEpub(ZipInputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING);
}
public Book readEpub(ZipFile zipfile) throws IOException {
return readEpub(zipfile, Constants.CHARACTER_ENCODING);
}
/**
* Read epub from inputstream
*
* @param in the inputstream from which to read the epub
* @param encoding the encoding to use for the html files within the epub
* @return the Book as read from the inputstream
* @throws IOException
*/
public Book readEpub(InputStream in, String encoding) throws IOException {
return readEpub(new ZipInputStream(in), encoding);
}
/**
* Reads this EPUB without loading any resources into memory.
*
* @param zipFile the file to load
* @param encoding the encoding for XHTML files
*
* @return this Book without loading all resources into memory.
* @throws IOException
*/
public Book readEpubLazy(ZipFile zipFile, String encoding)
throws IOException {
return readEpubLazy(zipFile, encoding,
Arrays.asList(MediaTypes.mediaTypes));
}
public Book readEpub(ZipInputStream in, String encoding) throws IOException {
return readEpub(ResourcesLoader.loadResources(in, encoding));
}
public Book readEpub(ZipFile in, String encoding) throws IOException {
return readEpub(ResourcesLoader.loadResources(in, encoding));
}
/**
* Reads this EPUB without loading all resources into memory.
*
* @param zipFile the file to load
* @param encoding the encoding for XHTML files
* @param lazyLoadedTypes a list of the MediaType to load lazily
* @return this Book without loading all resources into memory.
* @throws IOException
*/
public Book readEpubLazy(ZipFile zipFile, String encoding,
List<MediaType> lazyLoadedTypes) throws IOException {
Resources resources = ResourcesLoader
.loadResources(zipFile, encoding, lazyLoadedTypes);
return readEpub(resources);
}
public Book readEpub(Resources resources) throws IOException {
return readEpub(resources, new Book());
}
public Book readEpub(Resources resources, Book result) throws IOException {
if (result == null) {
result = new Book();
}
handleMimeType(result, resources);
String packageResourceHref = getPackageResourceHref(resources);
Resource packageResource = processPackageResource(packageResourceHref,
result, resources);
result.setOpfResource(packageResource);
Resource ncxResource = processNcxResource(packageResource, result);
result.setNcxResource(ncxResource);
result = postProcessBook(result);
return result;
}
private Book postProcessBook(Book book) {
if (bookProcessor != null) {
book = bookProcessor.processBook(book);
}
return book;
}
private Resource processNcxResource(Resource packageResource, Book book) {
Log.d(TAG,"OPF:getHref()"+packageResource.getHref());
return NCXDocument.read(book, this);
}
private Resource processPackageResource(String packageResourceHref, Book book,
Resources resources) {
Resource packageResource = resources.remove(packageResourceHref);
try {
PackageDocumentReader.read(packageResource, this, book, resources);
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
}
return packageResource;
}
private String getPackageResourceHref(Resources resources) {
String defaultResult = "OEBPS/content.opf";
String result = defaultResult;
Resource containerResource = resources.remove("META-INF/container.xml");
if (containerResource == null) {
return result;
}
try {
Document document = ResourceUtil.getAsDocument(containerResource);
Element rootFileElement = (Element) ((Element) document
.getDocumentElement().getElementsByTagName("rootfiles").item(0))
.getElementsByTagName("rootfile").item(0);
result = rootFileElement.getAttribute("full-path");
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
}
if (StringUtil.isBlank(result)) {
result = defaultResult;
}
return result;
}
private void handleMimeType(Book result, Resources resources) {
resources.remove("mimetype");
}
}

@ -0,0 +1,184 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.util.IOUtil;
//import io.documentnode.minilog.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.xmlpull.v1.XmlSerializer;
/**
* Generates an epub file. Not thread-safe, single use object.
*
* @author paul
*/
public class EpubWriter {
private static String TAG= EpubWriter.class.getName();
// package
static final String EMPTY_NAMESPACE_PREFIX = "";
private BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR;
public EpubWriter() {
this(BookProcessor.IDENTITY_BOOKPROCESSOR);
}
public EpubWriter(BookProcessor bookProcessor) {
this.bookProcessor = bookProcessor;
}
public void write(Book book, OutputStream out) throws IOException {
book = processBook(book);
ZipOutputStream resultStream = new ZipOutputStream(out);
writeMimeType(resultStream);
writeContainer(resultStream);
initTOCResource(book);
writeResources(book, resultStream);
writePackageDocument(book, resultStream);
resultStream.close();
}
private Book processBook(Book book) {
if (bookProcessor != null) {
book = bookProcessor.processBook(book);
}
return book;
}
private void initTOCResource(Book book) {
Resource tocResource;
try {
tocResource = NCXDocument.createNCXResource(book);
Resource currentTocResource = book.getSpine().getTocResource();
if (currentTocResource != null) {
book.getResources().remove(currentTocResource.getHref());
}
book.getSpine().setTocResource(tocResource);
book.getResources().add(tocResource);
} catch (Exception ex) {
Log.e(TAG,
"Error writing table of contents: "
+ ex.getClass().getName() + ": " + ex.getMessage(), ex);
}
}
private void writeResources(Book book, ZipOutputStream resultStream)
throws IOException {
for (Resource resource : book.getResources().getAll()) {
writeResource(resource, resultStream);
}
}
/**
* Writes the resource to the resultStream.
*
* @param resource
* @param resultStream
* @throws IOException
*/
private void writeResource(Resource resource, ZipOutputStream resultStream)
throws IOException {
if (resource == null) {
return;
}
try {
resultStream.putNextEntry(new ZipEntry("OEBPS/" + resource.getHref()));
InputStream inputStream = resource.getInputStream();
IOUtil.copy(inputStream, resultStream);
inputStream.close();
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
}
}
private void writePackageDocument(Book book, ZipOutputStream resultStream)
throws IOException {
resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf"));
XmlSerializer xmlSerializer = EpubProcessorSupport
.createXmlSerializer(resultStream);
PackageDocumentWriter.write(this, xmlSerializer, book);
xmlSerializer.flush();
// String resultAsString = result.toString();
// resultStream.write(resultAsString.getBytes(Constants.ENCODING));
}
/**
* Writes the META-INF/container.xml file.
*
* @param resultStream
* @throws IOException
*/
private void writeContainer(ZipOutputStream resultStream) throws IOException {
resultStream.putNextEntry(new ZipEntry("META-INF/container.xml"));
Writer out = new OutputStreamWriter(resultStream);
out.write("<?xml version=\"1.0\"?>\n");
out.write(
"<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n");
out.write("\t<rootfiles>\n");
out.write(
"\t\t<rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>\n");
out.write("\t</rootfiles>\n");
out.write("</container>");
out.flush();
}
/**
* Stores the mimetype as an uncompressed file in the ZipOutputStream.
*
* @param resultStream
* @throws IOException
*/
private void writeMimeType(ZipOutputStream resultStream) throws IOException {
ZipEntry mimetypeZipEntry = new ZipEntry("mimetype");
mimetypeZipEntry.setMethod(ZipEntry.STORED);
byte[] mimetypeBytes = MediaTypes.EPUB.getName().getBytes();
mimetypeZipEntry.setSize(mimetypeBytes.length);
mimetypeZipEntry.setCrc(calculateCrc(mimetypeBytes));
resultStream.putNextEntry(mimetypeZipEntry);
resultStream.write(mimetypeBytes);
}
private long calculateCrc(byte[] data) {
CRC32 crc = new CRC32();
crc.update(data);
return crc.getValue();
}
String getNcxId() {
return "ncx";
}
String getNcxHref() {
return "toc.ncx";
}
String getNcxMediaType() {
return MediaTypes.NCX.getName();
}
public BookProcessor getBookProcessor() {
return bookProcessor;
}
public void setBookProcessor(BookProcessor bookProcessor) {
this.bookProcessor = bookProcessor;
}
}

@ -0,0 +1,9 @@
package me.ag2s.epublib.epub;
import me.ag2s.epublib.domain.Resource;
import java.io.OutputStream;
public interface HtmlProcessor {
void processHtmlResource(Resource resource, OutputStream out);
}

@ -0,0 +1,5 @@
package me.ag2s.epublib.epub;
public class Main {
}

@ -0,0 +1,297 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.TOCReference;
import me.ag2s.epublib.domain.TableOfContents;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
//import javax.xml.stream.FactoryConfigurationError;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
/**
* Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
*
* @author paul
*/
public class NCXDocument {
public static final String NAMESPACE_NCX = "http://www.daisy.org/z3986/2005/ncx/";
public static final String PREFIX_NCX = "ncx";
public static final String NCX_ITEM_ID = "ncx";
public static final String DEFAULT_NCX_HREF = "toc.ncx";
public static final String PREFIX_DTB = "dtb";
private static String TAG = NCXDocument.class.getName();
private interface NCXTags {
String ncx = "ncx";
String meta = "meta";
String navPoint = "navPoint";
String navMap = "navMap";
String navLabel = "navLabel";
String content = "content";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head";
}
private interface NCXAttributes {
String src = "src";
String name = "name";
String content = "content";
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
private interface NCXAttributeValues {
String chapter = "chapter";
String version = "2005-1";
}
public static Resource read(Book book, EpubReader epubReader) {
Log.d(TAG, book.getVersion());
String version = book.getVersion();
if (version.startsWith("2.")) {
return NCXDocumentV2.read(book, epubReader);
} else if (version.startsWith("3.")) {
return NCXDocumentV3.read(book, epubReader);
} else {
return NCXDocumentV2.read(book, epubReader);
}
}
private static List<TOCReference> readTOCReferences(NodeList navpoints,
Book book) {
Log.d(TAG, book.getVersion());
String version = book.getVersion();
if (version.startsWith("2.")) {
return NCXDocumentV2.readTOCReferences(navpoints,book);
} else if (version.startsWith("3.")) {
return NCXDocumentV3.readTOCReferences(navpoints,book);
} else {
return NCXDocumentV2.readTOCReferences(navpoints,book);
}
}
static TOCReference readTOCReference(Element navpointElement, Book book) {
Log.d(TAG, book.getVersion());
String version = book.getVersion();
if (version.startsWith("2.")) {
return NCXDocumentV2.readTOCReference(navpointElement,book);
} else if (version.startsWith("3.")) {
return NCXDocumentV3.readTOCReference(navpointElement,book);
} else {
return NCXDocumentV2.readTOCReference(navpointElement,book);
}
}
private static String readNavReference(Element navpointElement) {
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.content);
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXAttributes.src);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, e.getMessage());
}
return result;
}
private static String readNavLabel(Element navpointElement) {
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.navLabel);
return DOMUtil.getTextChildrenContent(DOMUtil
.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXTags.text));
}
public static void write(EpubWriter epubWriter, Book book,
ZipOutputStream resultStream) throws IOException {
resultStream
.putNextEntry(new ZipEntry(book.getSpine().getTocResource().getHref()));
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(resultStream);
write(out, book);
out.flush();
}
/**
* Generates a resource containing an xml document containing the table of contents of the book in ncx format.
*
* @param xmlSerializer the serializer used
* @param book the book to serialize
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* @1throws FactoryConfigurationError
*/
public static void write(XmlSerializer xmlSerializer, Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
write(xmlSerializer, book.getMetadata().getIdentifiers(), book.getTitle(),
book.getMetadata().getAuthors(), book.getTableOfContents());
}
public static Resource createNCXResource(Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
return createNCXResource(book.getMetadata().getIdentifiers(),
book.getTitle(), book.getMetadata().getAuthors(),
book.getTableOfContents());
}
public static Resource createNCXResource(List<Identifier> identifiers,
String title, List<Author> authors, TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(data);
write(out, identifiers, title, authors, tableOfContents);
Resource resource = new Resource(NCX_ITEM_ID, data.toByteArray(),
DEFAULT_NCX_HREF, MediaTypes.NCX);
return resource;
}
public static void write(XmlSerializer serializer,
List<Identifier> identifiers, String title, List<Author> authors,
TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(EpubWriter.EMPTY_NAMESPACE_PREFIX, NAMESPACE_NCX);
serializer.startTag(NAMESPACE_NCX, NCXTags.ncx);
// serializer.writeNamespace("ncx", NAMESPACE_NCX);
// serializer.attribute("xmlns", NAMESPACE_NCX);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.version,
NCXAttributeValues.version);
serializer.startTag(NAMESPACE_NCX, NCXTags.head);
for (Identifier identifier : identifiers) {
writeMetaElement(identifier.getScheme(), identifier.getValue(),
serializer);
}
writeMetaElement("generator", Constants.EPUB4J_GENERATOR_NAME, serializer);
writeMetaElement("depth", String.valueOf(tableOfContents.calculateDepth()),
serializer);
writeMetaElement("totalPageCount", "0", serializer);
writeMetaElement("maxPageNumber", "0", serializer);
serializer.endTag(NAMESPACE_NCX, "head");
serializer.startTag(NAMESPACE_NCX, NCXTags.docTitle);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
// write the first title
serializer.text(StringUtil.defaultIfNull(title));
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docTitle);
for (Author author : authors) {
serializer.startTag(NAMESPACE_NCX, NCXTags.docAuthor);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(author.getLastname() + ", " + author.getFirstname());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docAuthor);
}
serializer.startTag(NAMESPACE_NCX, NCXTags.navMap);
writeNavPoints(tableOfContents.getTocReferences(), 1, serializer);
serializer.endTag(NAMESPACE_NCX, NCXTags.navMap);
serializer.endTag(NAMESPACE_NCX, "ncx");
serializer.endDocument();
}
private static void writeMetaElement(String dtbName, String content,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.name,
PREFIX_DTB + ":" + dtbName);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.content,
content);
serializer.endTag(NAMESPACE_NCX, NCXTags.meta);
}
private static int writeNavPoints(List<TOCReference> tocReferences,
int playOrder,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (TOCReference tocReference : tocReferences) {
if (tocReference.getResource() == null) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
continue;
}
writeNavPointStart(tocReference, playOrder, serializer);
playOrder++;
if (!tocReference.getChildren().isEmpty()) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
}
writeNavPointEnd(tocReference, serializer);
}
return playOrder;
}
private static void writeNavPointStart(TOCReference tocReference,
int playOrder, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.navPoint);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.id,
"navPoint-" + playOrder);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.playOrder,
String.valueOf(playOrder));
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.clazz,
NCXAttributeValues.chapter);
serializer.startTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(tocReference.getTitle());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.content);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.src,
tocReference.getCompleteHref());
serializer.endTag(NAMESPACE_NCX, NCXTags.content);
}
private static void writeNavPointEnd(TOCReference tocReference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.endTag(NAMESPACE_NCX, NCXTags.navPoint);
}
}

@ -0,0 +1,334 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.TOCReference;
import me.ag2s.epublib.domain.TableOfContents;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
/**
* Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
*
* @author paul
*/
public class NCXDocumentV2 extends NCXDocument{
public static final String NAMESPACE_NCX = "http://www.daisy.org/z3986/2005/ncx/";
public static final String PREFIX_NCX = "ncx";
public static final String NCX_ITEM_ID = "ncx";
public static final String DEFAULT_NCX_HREF = "toc.ncx";
public static final String PREFIX_DTB = "dtb";
private static String TAG= NCXDocumentV2.class.getName();
private interface NCXTags {
String ncx = "ncx";
String meta = "meta";
String navPoint = "navPoint";
String navMap = "navMap";
String navLabel = "navLabel";
String content = "content";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head";
}
private interface NCXAttributes {
String src = "src";
String name = "name";
String content = "content";
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
private interface NCXAttributeValues {
String chapter = "chapter";
String version = "2005-1";
}
public static Resource read(Book book, EpubReader epubReader) {
Resource ncxResource = null;
if (book.getSpine().getTocResource() == null) {
Log.e(TAG,"Book does not contain a table of contents file");
return ncxResource;
}
try {
ncxResource = book.getSpine().getTocResource();
if (ncxResource == null) {
return ncxResource;
}
Log.d(TAG,ncxResource.getHref());
Document ncxDocument = ResourceUtil.getAsDocument(ncxResource);
Element navMapElement = DOMUtil
.getFirstElementByTagNameNS(ncxDocument.getDocumentElement(),
NAMESPACE_NCX, NCXTags.navMap);
TableOfContents tableOfContents = new TableOfContents(
readTOCReferences(navMapElement.getChildNodes(), book));
book.setTableOfContents(tableOfContents);
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
}
return ncxResource;
}
static List<TOCReference> readTOCReferences(NodeList navpoints,
Book book) {
if (navpoints == null) {
return new ArrayList<>();
}
List<TOCReference> result = new ArrayList<>(
navpoints.getLength());
for (int i = 0; i < navpoints.getLength(); i++) {
Node node = navpoints.item(i);
if (node.getNodeType() != Document.ELEMENT_NODE) {
continue;
}
if (!(node.getLocalName().equals(NCXTags.navPoint))) {
continue;
}
TOCReference tocReference = readTOCReference((Element) node, book);
result.add(tocReference);
}
return result;
}
static TOCReference readTOCReference(Element navpointElement, Book book) {
String label = readNavLabel(navpointElement);
//Log.d(TAG,"label:"+label);
String tocResourceRoot = StringUtil
.substringBeforeLast(book.getSpine().getTocResource().getHref(), '/');
if (tocResourceRoot.length() == book.getSpine().getTocResource().getHref()
.length()) {
tocResourceRoot = "";
} else {
tocResourceRoot = tocResourceRoot + "/";
}
String reference = StringUtil
.collapsePathDots(tocResourceRoot + readNavReference(navpointElement));
String href = StringUtil
.substringBefore(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
String fragmentId = StringUtil
.substringAfter(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
Resource resource = book.getResources().getByHref(href);
if (resource == null) {
Log.e(TAG,"Resource with href " + href + " in NCX document not found");
}
TOCReference result = new TOCReference(label, resource, fragmentId);
List<TOCReference> childTOCReferences = readTOCReferences(
navpointElement.getChildNodes(), book);
result.setChildren(childTOCReferences);
return result;
}
private static String readNavReference(Element navpointElement) {
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.content);
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXAttributes.src);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG,e.getMessage());
}
return result;
}
private static String readNavLabel(Element navpointElement) {
//Log.d(TAG,navpointElement.getTagName());
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.navLabel);
return DOMUtil.getTextChildrenContent(DOMUtil
.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXTags.text));
}
public static void write(EpubWriter epubWriter, Book book,
ZipOutputStream resultStream) throws IOException {
resultStream
.putNextEntry(new ZipEntry(book.getSpine().getTocResource().getHref()));
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(resultStream);
write(out, book);
out.flush();
}
/**
* Generates a resource containing an xml document containing the table of contents of the book in ncx format.
*
* @param xmlSerializer the serializer used
* @param book the book to serialize
*
* @1throws FactoryConfigurationError
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
public static void write(XmlSerializer xmlSerializer, Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
write(xmlSerializer, book.getMetadata().getIdentifiers(), book.getTitle(),
book.getMetadata().getAuthors(), book.getTableOfContents());
}
public static Resource createNCXResource(Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
return createNCXResource(book.getMetadata().getIdentifiers(),
book.getTitle(), book.getMetadata().getAuthors(),
book.getTableOfContents());
}
public static Resource createNCXResource(List<Identifier> identifiers,
String title, List<Author> authors, TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(data);
write(out, identifiers, title, authors, tableOfContents);
Resource resource = new Resource(NCX_ITEM_ID, data.toByteArray(),
DEFAULT_NCX_HREF, MediaTypes.NCX);
return resource;
}
public static void write(XmlSerializer serializer,
List<Identifier> identifiers, String title, List<Author> authors,
TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(EpubWriter.EMPTY_NAMESPACE_PREFIX, NAMESPACE_NCX);
serializer.startTag(NAMESPACE_NCX, NCXTags.ncx);
// serializer.writeNamespace("ncx", NAMESPACE_NCX);
// serializer.attribute("xmlns", NAMESPACE_NCX);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.version,
NCXAttributeValues.version);
serializer.startTag(NAMESPACE_NCX, NCXTags.head);
for (Identifier identifier : identifiers) {
writeMetaElement(identifier.getScheme(), identifier.getValue(),
serializer);
}
writeMetaElement("generator", Constants.EPUB4J_GENERATOR_NAME, serializer);
writeMetaElement("depth", String.valueOf(tableOfContents.calculateDepth()),
serializer);
writeMetaElement("totalPageCount", "0", serializer);
writeMetaElement("maxPageNumber", "0", serializer);
serializer.endTag(NAMESPACE_NCX, "head");
serializer.startTag(NAMESPACE_NCX, NCXTags.docTitle);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
// write the first title
serializer.text(StringUtil.defaultIfNull(title));
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docTitle);
for (Author author : authors) {
serializer.startTag(NAMESPACE_NCX, NCXTags.docAuthor);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(author.getLastname() + ", " + author.getFirstname());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docAuthor);
}
serializer.startTag(NAMESPACE_NCX, NCXTags.navMap);
writeNavPoints(tableOfContents.getTocReferences(), 1, serializer);
serializer.endTag(NAMESPACE_NCX, NCXTags.navMap);
serializer.endTag(NAMESPACE_NCX, "ncx");
serializer.endDocument();
}
private static void writeMetaElement(String dtbName, String content,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.name,
PREFIX_DTB + ":" + dtbName);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.content,
content);
serializer.endTag(NAMESPACE_NCX, NCXTags.meta);
}
private static int writeNavPoints(List<TOCReference> tocReferences,
int playOrder,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (TOCReference tocReference : tocReferences) {
if (tocReference.getResource() == null) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
continue;
}
writeNavPointStart(tocReference, playOrder, serializer);
playOrder++;
if (!tocReference.getChildren().isEmpty()) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
}
writeNavPointEnd(tocReference, serializer);
}
return playOrder;
}
private static void writeNavPointStart(TOCReference tocReference,
int playOrder, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.navPoint);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.id,
"navPoint-" + playOrder);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.playOrder,
String.valueOf(playOrder));
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.clazz,
NCXAttributeValues.chapter);
serializer.startTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(tocReference.getTitle());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.content);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.src,
tocReference.getCompleteHref());
serializer.endTag(NAMESPACE_NCX, NCXTags.content);
}
private static void writeNavPointEnd(TOCReference tocReference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.endTag(NAMESPACE_NCX, NCXTags.navPoint);
}
}

@ -0,0 +1,206 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.TOCReference;
import me.ag2s.epublib.domain.TableOfContents;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
public class NCXDocumentV3 extends NCXDocument {
public static final String NAMESPACE_NCX = "";
public static final String PREFIX_NCX = "html";
public static final String NCX_ITEM_ID = "ncx";
public static final String DEFAULT_NCX_HREF = "toc.xhtml";
public static final String PREFIX_DTB = "dtb";
private static String TAG = NCXDocumentV3.class.getName();
private interface NCXTags {
//String nav="nav";
//String li="li";
//String ncx = "ncx";
String meta = "meta";
String navPoint = "li";
String navMap = "nav";
String navLabel = "a";
String content = "a";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head";
}
private interface NCXAttributes {
String src = "href";
String name = "name";
String content = "content";
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
private interface NCXAttributeValues {
String chapter = "chapter";
String version = "2007";
}
public static Resource read(Book book, EpubReader epubReader) {
Resource ncxResource = null;
if (book.getSpine().getTocResource() == null) {
Log.e(TAG, "Book does not contain a table of contents file");
return ncxResource;
}
try {
ncxResource = book.getSpine().getTocResource();
if (ncxResource == null) {
return ncxResource;
}
//Log.d(TAG, ncxResource.getHref());
Document ncxDocument = ResourceUtil.getAsDocument(ncxResource);
//Log.d(TAG, ncxDocument.getNodeName());
Element navMapElement = (Element) ncxDocument.getElementsByTagName("nav").item(0);
navMapElement = (Element) navMapElement.getElementsByTagName("ol").item(0);
Log.d(TAG, navMapElement.getTagName());
TableOfContents tableOfContents = new TableOfContents(
readTOCReferences(navMapElement.getChildNodes(), book));
Log.d(TAG, tableOfContents.toString());
book.setTableOfContents(tableOfContents);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
return ncxResource;
}
public static List<TOCReference> doToc(Node n, Book book) {
List<TOCReference> result = new ArrayList<>();
if (n == null || n.getNodeType() != Document.ELEMENT_NODE) {
return result;
} else {
Element el = (Element) n;
NodeList nodeList = el.getElementsByTagName("li");
for (int i = 0; i < nodeList.getLength(); i++) {
result.add(readTOCReference((Element) nodeList.item(i), book));
}
}
return result;
}
static List<TOCReference> readTOCReferences(NodeList navpoints,
Book book) {
if (navpoints == null) {
return new ArrayList<>();
}
//Log.d(TAG, "readTOCReferences:navpoints.getLength()" + navpoints.getLength());
List<TOCReference> result = new ArrayList<>();
for (int i = 0; i < navpoints.getLength(); i++) {
Node node = navpoints.item(i);
if (node == null || node.getNodeType() != Document.ELEMENT_NODE) {
continue;
} else {
Element el = (Element) node;
if (el.getTagName().equals("li")) {
result.add(readTOCReference(el, book));
}
//NodeList nodeList=el.getElementsByTagName("li");
//for (int i=0;i<nodeList.getLength();i++){
//result.add(readTOCReference((Element) nodeList.item(i),book));
// }
}
//result.addAll(doToc(node, book));
}
return result;
}
static TOCReference readTOCReference(Element navpointElement, Book book) {
String label = readNavLabel(navpointElement);
//Log.d(TAG, "label:" + label);
String tocResourceRoot = StringUtil
.substringBeforeLast(book.getSpine().getTocResource().getHref(), '/');
if (tocResourceRoot.length() == book.getSpine().getTocResource().getHref()
.length()) {
tocResourceRoot = "";
} else {
tocResourceRoot = tocResourceRoot + "/";
}
String reference = StringUtil
.collapsePathDots(tocResourceRoot + readNavReference(navpointElement));
String href = StringUtil
.substringBefore(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
String fragmentId = StringUtil
.substringAfter(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
Resource resource = book.getResources().getByHref(href);
if (resource == null) {
Log.e(TAG, "Resource with href " + href + " in NCX document not found");
}
TOCReference result = new TOCReference(label, resource, fragmentId);
List<TOCReference> childTOCReferences = doToc(navpointElement, book);
//readTOCReferences(
//navpointElement.getChildNodes(), book);
result.setChildren(childTOCReferences);
return result;
}
private static String readNavReference(Element navpointElement) {
//Log.d(TAG, "readNavReference:" + navpointElement.getTagName());
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXDocumentV3.NCXTags.content);
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXDocumentV3.NCXAttributes.src);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, e.getMessage());
}
return result;
}
private static String readNavLabel(Element navpointElement) {
//Log.d(TAG, "readNavLabel:" + navpointElement.getTagName());
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXDocumentV3.NCXTags.navLabel);
return navLabel.getTextContent();//DOMUtil.getTextChildrenContent(DOMUtil
//.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXDocumentV3.NCXTags.text));
}
}

@ -0,0 +1,89 @@
package me.ag2s.epublib.epub;
/**
* Functionality shared by the PackageDocumentReader and the PackageDocumentWriter
*
* @author paul
*
*/
public class PackageDocumentBase {
public static final String BOOK_ID_ID = "BookId";
public static final String NAMESPACE_OPF = "http://www.idpf.org/2007/opf";
public static final String NAMESPACE_DUBLIN_CORE = "http://purl.org/dc/elements/1.1/";
public static final String PREFIX_DUBLIN_CORE = "dc";
//public static final String PREFIX_OPF = "opf";
//Some Epub Reader not reconize opt:packge,So just let it empty;
public static final String PREFIX_OPF = "";
public static final String version="version";
public static final String dateFormat = "yyyy-MM-dd";
protected interface DCTags {
String title = "title";
String creator = "creator";
String subject = "subject";
String description = "description";
String publisher = "publisher";
String contributor = "contributor";
String date = "date";
String type = "type";
String format = "format";
String identifier = "identifier";
String source = "source";
String language = "language";
String relation = "relation";
String coverage = "coverage";
String rights = "rights";
}
protected interface DCAttributes {
String scheme = "scheme";
String id = "id";
}
protected interface OPFTags {
String metadata = "metadata";
String meta = "meta";
String manifest = "manifest";
String packageTag = "package";
String itemref = "itemref";
String spine = "spine";
String reference = "reference";
String guide = "guide";
String item = "item";
}
protected interface OPFAttributes {
String uniqueIdentifier = "unique-identifier";
String idref = "idref";
String name = "name";
String content = "content";
String type = "type";
String href = "href";
String linear = "linear";
String event = "event";
String role = "role";
String file_as = "file-as";
String id = "id";
String media_type = "media-type";
String title = "title";
String toc = "toc";
String version = "version";
String scheme = "scheme";
String property = "property";
String properties="properties";
}
protected interface OPFValues {
String meta_cover = "cover";
String reference_cover = "cover";
String no = "no";
String generator = "generator";
}
}

@ -0,0 +1,223 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Date;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.Metadata;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Reads the package document metadata.
*
* In its own separate class because the PackageDocumentReader became a bit large and unwieldy.
*
* @author paul
*/
// package
class PackageDocumentMetadataReader extends PackageDocumentBase {
private static String TAG= PackageDocumentMetadataReader.class.getName();
public static Metadata readMetadata(Document packageDocument) {
Metadata result = new Metadata();
Element metadataElement = DOMUtil
.getFirstElementByTagNameNS(packageDocument.getDocumentElement(),
NAMESPACE_OPF, OPFTags.metadata);
if (metadataElement == null) {
Log.e(TAG,"Package does not contain element " + OPFTags.metadata);
return result;
}
result.setTitles(DOMUtil
.getElementsTextChild(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.title));
result.setPublishers(DOMUtil
.getElementsTextChild(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.publisher));
result.setDescriptions(DOMUtil
.getElementsTextChild(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.description));
result.setRights(DOMUtil
.getElementsTextChild(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.rights));
result.setTypes(DOMUtil
.getElementsTextChild(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.type));
result.setSubjects(DOMUtil
.getElementsTextChild(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.subject));
result.setIdentifiers(readIdentifiers(metadataElement));
result.setAuthors(readCreators(metadataElement));
result.setContributors(readContributors(metadataElement));
result.setDates(readDates(metadataElement));
result.setOtherProperties(readOtherProperties(metadataElement));
result.setMetaAttributes(readMetaProperties(metadataElement));
Element languageTag = DOMUtil
.getFirstElementByTagNameNS(metadataElement, NAMESPACE_DUBLIN_CORE,
DCTags.language);
if (languageTag != null) {
result.setLanguage(DOMUtil.getTextChildrenContent(languageTag));
}
return result;
}
/**
* consumes meta tags that have a property attribute as defined in the standard. For example:
* &lt;meta property="rendition:layout"&gt;pre-paginated&lt;/meta&gt;
* @param metadataElement
* @return
*/
private static Map<QName, String> readOtherProperties(
Element metadataElement) {
Map<QName, String> result = new HashMap<QName, String>();
NodeList metaTags = metadataElement.getElementsByTagName(OPFTags.meta);
for (int i = 0; i < metaTags.getLength(); i++) {
Node metaNode = metaTags.item(i);
Node property = metaNode.getAttributes()
.getNamedItem(OPFAttributes.property);
if (property != null) {
String name = property.getNodeValue();
String value = metaNode.getTextContent();
result.put(new QName(name), value);
}
}
return result;
}
/**
* consumes meta tags that have a property attribute as defined in the standard. For example:
* &lt;meta property="rendition:layout"&gt;pre-paginated&lt;/meta&gt;
* @param metadataElement
* @return
*/
private static Map<String, String> readMetaProperties(
Element metadataElement) {
Map<String, String> result = new HashMap<String, String>();
NodeList metaTags = metadataElement.getElementsByTagName(OPFTags.meta);
for (int i = 0; i < metaTags.getLength(); i++) {
Element metaElement = (Element) metaTags.item(i);
String name = metaElement.getAttribute(OPFAttributes.name);
String value = metaElement.getAttribute(OPFAttributes.content);
result.put(name, value);
}
return result;
}
private static String getBookIdId(Document document) {
Element packageElement = DOMUtil
.getFirstElementByTagNameNS(document.getDocumentElement(),
NAMESPACE_OPF, OPFTags.packageTag);
if (packageElement == null) {
return null;
}
String result = packageElement
.getAttributeNS(NAMESPACE_OPF, OPFAttributes.uniqueIdentifier);
return result;
}
private static List<Author> readCreators(Element metadataElement) {
return readAuthors(DCTags.creator, metadataElement);
}
private static List<Author> readContributors(Element metadataElement) {
return readAuthors(DCTags.contributor, metadataElement);
}
private static List<Author> readAuthors(String authorTag,
Element metadataElement) {
NodeList elements = metadataElement
.getElementsByTagNameNS(NAMESPACE_DUBLIN_CORE, authorTag);
List<Author> result = new ArrayList<Author>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) {
Element authorElement = (Element) elements.item(i);
Author author = createAuthor(authorElement);
if (author != null) {
result.add(author);
}
}
return result;
}
private static List<Date> readDates(Element metadataElement) {
NodeList elements = metadataElement
.getElementsByTagNameNS(NAMESPACE_DUBLIN_CORE, DCTags.date);
List<Date> result = new ArrayList<Date>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) {
Element dateElement = (Element) elements.item(i);
Date date;
try {
date = new Date(DOMUtil.getTextChildrenContent(dateElement),
dateElement.getAttributeNS(NAMESPACE_OPF, OPFAttributes.event));
result.add(date);
} catch (IllegalArgumentException e) {
Log.e(TAG,e.getMessage());
}
}
return result;
}
private static Author createAuthor(Element authorElement) {
String authorString = DOMUtil.getTextChildrenContent(authorElement);
if (StringUtil.isBlank(authorString)) {
return null;
}
int spacePos = authorString.lastIndexOf(' ');
Author result;
if (spacePos < 0) {
result = new Author(authorString);
} else {
result = new Author(authorString.substring(0, spacePos),
authorString.substring(spacePos + 1));
}
result.setRole(
authorElement.getAttributeNS(NAMESPACE_OPF, OPFAttributes.role));
return result;
}
private static List<Identifier> readIdentifiers(Element metadataElement) {
NodeList identifierElements = metadataElement
.getElementsByTagNameNS(NAMESPACE_DUBLIN_CORE, DCTags.identifier);
if (identifierElements.getLength() == 0) {
Log.e(TAG,"Package does not contain element " + DCTags.identifier);
return new ArrayList<Identifier>();
}
String bookIdId = getBookIdId(metadataElement.getOwnerDocument());
List<Identifier> result = new ArrayList<Identifier>(
identifierElements.getLength());
for (int i = 0; i < identifierElements.getLength(); i++) {
Element identifierElement = (Element) identifierElements.item(i);
String schemeName = identifierElement
.getAttributeNS(NAMESPACE_OPF, DCAttributes.scheme);
String identifierValue = DOMUtil
.getTextChildrenContent(identifierElement);
if (StringUtil.isBlank(identifierValue)) {
continue;
}
Identifier identifier = new Identifier(schemeName, identifierValue);
if (identifierElement.getAttribute("id").equals(bookIdId)) {
identifier.setBookId(true);
}
result.add(identifier);
}
return result;
}
}

@ -0,0 +1,176 @@
package me.ag2s.epublib.epub;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Date;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.util.StringUtil;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.xmlpull.v1.XmlSerializer;
public class PackageDocumentMetadataWriter extends PackageDocumentBase {
/**
* Writes the book's metadata.
*
* @param book
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
public static void writeMetaData(Book book, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.metadata);
serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE);
serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF);
writeIdentifiers(book.getMetadata().getIdentifiers(), serializer);
writeSimpleMetdataElements(DCTags.title, book.getMetadata().getTitles(),
serializer);
writeSimpleMetdataElements(DCTags.subject, book.getMetadata().getSubjects(),
serializer);
writeSimpleMetdataElements(DCTags.description,
book.getMetadata().getDescriptions(), serializer);
writeSimpleMetdataElements(DCTags.publisher,
book.getMetadata().getPublishers(), serializer);
writeSimpleMetdataElements(DCTags.type, book.getMetadata().getTypes(),
serializer);
writeSimpleMetdataElements(DCTags.rights, book.getMetadata().getRights(),
serializer);
// write authors
for (Author author : book.getMetadata().getAuthors()) {
serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.creator);
serializer.attribute(NAMESPACE_OPF, OPFAttributes.role,
author.getRelator().getCode());
serializer.attribute(NAMESPACE_OPF, OPFAttributes.file_as,
author.getLastname() + ", " + author.getFirstname());
serializer.text(author.getFirstname() + " " + author.getLastname());
serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.creator);
}
// write contributors
for (Author author : book.getMetadata().getContributors()) {
serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.contributor);
serializer.attribute(NAMESPACE_OPF, OPFAttributes.role,
author.getRelator().getCode());
serializer.attribute(NAMESPACE_OPF, OPFAttributes.file_as,
author.getLastname() + ", " + author.getFirstname());
serializer.text(author.getFirstname() + " " + author.getLastname());
serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.contributor);
}
// write dates
for (Date date : book.getMetadata().getDates()) {
serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.date);
if (date.getEvent() != null) {
serializer.attribute(NAMESPACE_OPF, OPFAttributes.event,
date.getEvent().toString());
}
serializer.text(date.getValue());
serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.date);
}
// write language
if (StringUtil.isNotBlank(book.getMetadata().getLanguage())) {
serializer.startTag(NAMESPACE_DUBLIN_CORE, "language");
serializer.text(book.getMetadata().getLanguage());
serializer.endTag(NAMESPACE_DUBLIN_CORE, "language");
}
// write other properties
if (book.getMetadata().getOtherProperties() != null) {
for (Map.Entry<QName, String> mapEntry : book.getMetadata()
.getOtherProperties().entrySet()) {
serializer.startTag(mapEntry.getKey().getNamespaceURI(), OPFTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX,
OPFAttributes.property, mapEntry.getKey().getLocalPart());
serializer.text(mapEntry.getValue());
serializer.endTag(mapEntry.getKey().getNamespaceURI(), OPFTags.meta);
}
}
// write coverimage
if (book.getCoverImage() != null) { // write the cover image
serializer.startTag(NAMESPACE_OPF, OPFTags.meta);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.name,
OPFValues.meta_cover);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.content,
book.getCoverImage().getId());
serializer.endTag(NAMESPACE_OPF, OPFTags.meta);
}
// write generator
serializer.startTag(NAMESPACE_OPF, OPFTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.name,
OPFValues.generator);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.content,
Constants.EPUB4J_GENERATOR_NAME);
serializer.endTag(NAMESPACE_OPF, OPFTags.meta);
serializer.endTag(NAMESPACE_OPF, OPFTags.metadata);
}
private static void writeSimpleMetdataElements(String tagName,
List<String> values, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (String value : values) {
if (StringUtil.isBlank(value)) {
continue;
}
serializer.startTag(NAMESPACE_DUBLIN_CORE, tagName);
serializer.text(value);
serializer.endTag(NAMESPACE_DUBLIN_CORE, tagName);
}
}
/**
* Writes out the complete list of Identifiers to the package document.
* The first identifier for which the bookId is true is made the bookId identifier.
* If no identifier has bookId == true then the first bookId identifier is written as the primary.
*
* @param identifiers
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* @
*/
private static void writeIdentifiers(List<Identifier> identifiers,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
Identifier bookIdIdentifier = Identifier.getBookIdIdentifier(identifiers);
if (bookIdIdentifier == null) {
return;
}
serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, DCAttributes.id,
BOOK_ID_ID);
serializer.attribute(NAMESPACE_OPF, OPFAttributes.scheme,
bookIdIdentifier.getScheme());
serializer.text(bookIdIdentifier.getValue());
serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier);
for (Identifier identifier : identifiers.subList(1, identifiers.size())) {
if (identifier == bookIdIdentifier) {
continue;
}
serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier);
serializer.attribute(NAMESPACE_OPF, "scheme", identifier.getScheme());
serializer.text(identifier.getValue());
serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier);
}
}
}

@ -0,0 +1,425 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import android.widget.Toast;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Guide;
import me.ag2s.epublib.domain.GuideReference;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.Resources;
import me.ag2s.epublib.domain.Spine;
import me.ag2s.epublib.domain.SpineReference;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Reads the opf package document as defined by namespace http://www.idpf.org/2007/opf
*
* @author paul
*
*/
public class PackageDocumentReader extends PackageDocumentBase {
private static String TAG= PackageDocumentReader.class.getName();
private static final String[] POSSIBLE_NCX_ITEM_IDS = new String[]{"toc",
"ncx", "ncxtoc","htmltoc"};
public static void read(
Resource packageResource, EpubReader epubReader, Book book,
Resources resources)
throws UnsupportedEncodingException, SAXException, IOException, ParserConfigurationException {
Document packageDocument = ResourceUtil.getAsDocument(packageResource);
String packageHref = packageResource.getHref();
resources = fixHrefs(packageHref, resources);
readGuide(packageDocument, epubReader, book, resources);
// Books sometimes use non-identifier ids. We map these here to legal ones
Map<String, String> idMapping = new HashMap<String, String>();
String version=DOMUtil.getAttribute(packageDocument.getDocumentElement(),PREFIX_OPF,PackageDocumentBase.version);
resources = readManifest(packageDocument, packageHref, epubReader,
resources, idMapping);
book.setResources(resources);
book.setVersion(version);
readCover(packageDocument, book);
book.setMetadata(
PackageDocumentMetadataReader.readMetadata(packageDocument));
book.setSpine(readSpine(packageDocument, book.getResources(), idMapping));
// if we did not find a cover page then we make the first page of the book the cover page
if (book.getCoverPage() == null && book.getSpine().size() > 0) {
book.setCoverPage(book.getSpine().getResource(0));
}
}
// private static Resource readCoverImage(Element metadataElement, Resources resources) {
// String coverResourceId = DOMUtil.getFindAttributeValue(metadataElement.getOwnerDocument(), NAMESPACE_OPF, OPFTags.meta, OPFAttributes.name, OPFValues.meta_cover, OPFAttributes.content);
// if (StringUtil.isBlank(coverResourceId)) {
// return null;
// }
// Resource coverResource = resources.getByIdOrHref(coverResourceId);
// return coverResource;
// }
/**
* Reads the manifest containing the resource ids, hrefs and mediatypes.
*
* @param packageDocument
* @param packageHref
* @param epubReader
* @param resources
* @param idMapping
* @return a Map with resources, with their id's as key.
*/
private static Resources readManifest(Document packageDocument,
String packageHref,
EpubReader epubReader, Resources resources,
Map<String, String> idMapping) {
Element manifestElement = DOMUtil
.getFirstElementByTagNameNS(packageDocument.getDocumentElement(),
NAMESPACE_OPF, OPFTags.manifest);
Resources result = new Resources();
if (manifestElement == null) {
Log.e(TAG,
"Package document does not contain element " + OPFTags.manifest);
return result;
}
NodeList itemElements = manifestElement
.getElementsByTagNameNS(NAMESPACE_OPF, OPFTags.item);
for (int i = 0; i < itemElements.getLength(); i++) {
Element itemElement = (Element) itemElements.item(i);
String id = DOMUtil
.getAttribute(itemElement, NAMESPACE_OPF, OPFAttributes.id);
String href = DOMUtil
.getAttribute(itemElement, NAMESPACE_OPF, OPFAttributes.href);
String properties=DOMUtil.getAttribute(itemElement,NAMESPACE_OPF,OPFAttributes.properties);
try {
href = URLDecoder.decode(href, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG,e.getMessage());
}
String mediaTypeName = DOMUtil
.getAttribute(itemElement, NAMESPACE_OPF, OPFAttributes.media_type);
Resource resource = resources.remove(href);
if (resource == null) {
Log.e(TAG,"resource with href '" + href + "' not found");
continue;
}
resource.setId(id);
resource.setProperties(properties);
MediaType mediaType = MediaTypes.getMediaTypeByName(mediaTypeName);
if (mediaType != null) {
resource.setMediaType(mediaType);
}
result.add(resource);
idMapping.put(id, resource.getId());
}
return result;
}
/**
* Reads the book's guide.
* Here some more attempts are made at finding the cover page.
*
* @param packageDocument
* @param epubReader
* @param book
* @param resources
*/
private static void readGuide(Document packageDocument,
EpubReader epubReader, Book book, Resources resources) {
Element guideElement = DOMUtil
.getFirstElementByTagNameNS(packageDocument.getDocumentElement(),
NAMESPACE_OPF, OPFTags.guide);
if (guideElement == null) {
return;
}
Guide guide = book.getGuide();
NodeList guideReferences = guideElement
.getElementsByTagNameNS(NAMESPACE_OPF, OPFTags.reference);
for (int i = 0; i < guideReferences.getLength(); i++) {
Element referenceElement = (Element) guideReferences.item(i);
String resourceHref = DOMUtil
.getAttribute(referenceElement, NAMESPACE_OPF, OPFAttributes.href);
if (StringUtil.isBlank(resourceHref)) {
continue;
}
Resource resource = resources.getByHref(StringUtil
.substringBefore(resourceHref, Constants.FRAGMENT_SEPARATOR_CHAR));
if (resource == null) {
Log.e(TAG,"Guide is referencing resource with href " + resourceHref
+ " which could not be found");
continue;
}
String type = DOMUtil
.getAttribute(referenceElement, NAMESPACE_OPF, OPFAttributes.type);
if (StringUtil.isBlank(type)) {
Log.e(TAG,"Guide is referencing resource with href " + resourceHref
+ " which is missing the 'type' attribute");
continue;
}
String title = DOMUtil
.getAttribute(referenceElement, NAMESPACE_OPF, OPFAttributes.title);
if (GuideReference.COVER.equalsIgnoreCase(type)) {
continue; // cover is handled elsewhere
}
GuideReference reference = new GuideReference(resource, type, title,
StringUtil
.substringAfter(resourceHref, Constants.FRAGMENT_SEPARATOR_CHAR));
guide.addReference(reference);
}
}
/**
* Strips off the package prefixes up to the href of the packageHref.
*
* Example:
* If the packageHref is "OEBPS/content.opf" then a resource href like "OEBPS/foo/bar.html" will be turned into "foo/bar.html"
*
* @param packageHref
* @param resourcesByHref
* @return The stripped package href
*/
static Resources fixHrefs(String packageHref,
Resources resourcesByHref) {
int lastSlashPos = packageHref.lastIndexOf('/');
if (lastSlashPos < 0) {
return resourcesByHref;
}
Resources result = new Resources();
for (Resource resource : resourcesByHref.getAll()) {
if (StringUtil.isNotBlank(resource.getHref())
&& resource.getHref().length() > lastSlashPos) {
resource.setHref(resource.getHref().substring(lastSlashPos + 1));
}
result.add(resource);
}
return result;
}
/**
* Reads the document's spine, containing all sections in reading order.
*
* @param packageDocument
* @param resources
* @param idMapping
* @return the document's spine, containing all sections in reading order.
*/
private static Spine readSpine(Document packageDocument, Resources resources,
Map<String, String> idMapping) {
Element spineElement = DOMUtil
.getFirstElementByTagNameNS(packageDocument.getDocumentElement(),
NAMESPACE_OPF, OPFTags.spine);
if (spineElement == null) {
Log.e(TAG,"Element " + OPFTags.spine
+ " not found in package document, generating one automatically");
return generateSpineFromResources(resources);
}
Spine result = new Spine();
String tocResourceId = DOMUtil
.getAttribute(spineElement, NAMESPACE_OPF, OPFAttributes.toc);
result
.setTocResource(findTableOfContentsResource(tocResourceId, resources));
NodeList spineNodes = packageDocument
.getElementsByTagNameNS(NAMESPACE_OPF, OPFTags.itemref);
List<SpineReference> spineReferences = new ArrayList<SpineReference>(
spineNodes.getLength());
for (int i = 0; i < spineNodes.getLength(); i++) {
Element spineItem = (Element) spineNodes.item(i);
String itemref = DOMUtil
.getAttribute(spineItem, NAMESPACE_OPF, OPFAttributes.idref);
if (StringUtil.isBlank(itemref)) {
Log.e(TAG,"itemref with missing or empty idref"); // XXX
continue;
}
String id = idMapping.get(itemref);
if (id == null) {
id = itemref;
}
Resource resource = resources.getByIdOrHref(id);
if (resource == null) {
Log.e(TAG,"resource with id \'" + id + "\' not found");
continue;
}
SpineReference spineReference = new SpineReference(resource);
if (OPFValues.no.equalsIgnoreCase(DOMUtil
.getAttribute(spineItem, NAMESPACE_OPF, OPFAttributes.linear))) {
spineReference.setLinear(false);
}
spineReferences.add(spineReference);
}
result.setSpineReferences(spineReferences);
return result;
}
/**
* Creates a spine out of all resources in the resources.
* The generated spine consists of all XHTML pages in order of their href.
*
* @param resources
* @return a spine created out of all resources in the resources.
*/
private static Spine generateSpineFromResources(Resources resources) {
Spine result = new Spine();
List<String> resourceHrefs = new ArrayList<String>();
resourceHrefs.addAll(resources.getAllHrefs());
Collections.sort(resourceHrefs, String.CASE_INSENSITIVE_ORDER);
for (String resourceHref : resourceHrefs) {
Resource resource = resources.getByHref(resourceHref);
if (resource.getMediaType() == MediaTypes.NCX) {
result.setTocResource(resource);
} else if (resource.getMediaType() == MediaTypes.XHTML) {
result.addSpineReference(new SpineReference(resource));
}
}
return result;
}
/**
* The spine tag should contain a 'toc' attribute with as value the resource id of the table of contents resource.
*
* Here we try several ways of finding this table of contents resource.
* We try the given attribute value, some often-used ones and finally look through all resources for the first resource with the table of contents mimetype.
*
* @param tocResourceId
* @param resources
* @return the Resource containing the table of contents
*/
static Resource findTableOfContentsResource(String tocResourceId,
Resources resources) {
Resource tocResource = null;
if (StringUtil.isNotBlank(tocResourceId)) {
tocResource = resources.getByIdOrHref(tocResourceId);
}
if (tocResource != null) {
return tocResource;
}
// get the first resource with the NCX mediatype
tocResource = resources.findFirstResourceByMediaType(MediaTypes.NCX);
if (tocResource == null) {
for (int i = 0; i < POSSIBLE_NCX_ITEM_IDS.length; i++) {
tocResource = resources.getByIdOrHref(POSSIBLE_NCX_ITEM_IDS[i]);
if (tocResource != null) {
break;
}
tocResource = resources
.getByIdOrHref(POSSIBLE_NCX_ITEM_IDS[i].toUpperCase());
if (tocResource != null) {
break;
}
}
}
//For EPUB3
if (tocResource==null){
tocResource=resources.getByProperties("nav");
}
if (tocResource == null) {
Log.e(TAG,
"Could not find table of contents resource. Tried resource with id '"
+ tocResourceId + "', " + Constants.DEFAULT_TOC_ID + ", "
+ Constants.DEFAULT_TOC_ID.toUpperCase()
+ " and any NCX resource.");
}
return tocResource;
}
/**
* Find all resources that have something to do with the coverpage and the cover image.
* Search the meta tags and the guide references
*
* @param packageDocument
* @return all resources that have something to do with the coverpage and the cover image.
*/
// package
static Set<String> findCoverHrefs(Document packageDocument) {
Set<String> result = new HashSet<String>();
// try and find a meta tag with name = 'cover' and a non-blank id
String coverResourceId = DOMUtil
.getFindAttributeValue(packageDocument, NAMESPACE_OPF,
OPFTags.meta, OPFAttributes.name, OPFValues.meta_cover,
OPFAttributes.content);
if (StringUtil.isNotBlank(coverResourceId)) {
String coverHref = DOMUtil
.getFindAttributeValue(packageDocument, NAMESPACE_OPF,
OPFTags.item, OPFAttributes.id, coverResourceId,
OPFAttributes.href);
if (StringUtil.isNotBlank(coverHref)) {
result.add(coverHref);
} else {
result.add(
coverResourceId); // maybe there was a cover href put in the cover id attribute
}
}
// try and find a reference tag with type is 'cover' and reference is not blank
String coverHref = DOMUtil
.getFindAttributeValue(packageDocument, NAMESPACE_OPF,
OPFTags.reference, OPFAttributes.type, OPFValues.reference_cover,
OPFAttributes.href);
if (StringUtil.isNotBlank(coverHref)) {
result.add(coverHref);
}
return result;
}
/**
* Finds the cover resource in the packageDocument and adds it to the book if found.
* Keeps the cover resource in the resources map
* @param packageDocument
* @param book
* @1param resources
*/
private static void readCover(Document packageDocument, Book book) {
Collection<String> coverHrefs = findCoverHrefs(packageDocument);
for (String coverHref : coverHrefs) {
Resource resource = book.getResources().getByHref(coverHref);
if (resource == null) {
Log.e(TAG,"Cover resource " + coverHref + " not found");
continue;
}
if (resource.getMediaType() == MediaTypes.XHTML) {
book.setCoverPage(resource);
} else if (MediaTypes.isBitmapImage(resource.getMediaType())) {
book.setCoverImage(resource);
}
}
}
}

@ -0,0 +1,254 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Guide;
import me.ag2s.epublib.domain.GuideReference;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.Spine;
import me.ag2s.epublib.domain.SpineReference;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
//import javax.xml.stream.XMLStreamException;
import org.xmlpull.v1.XmlSerializer;
/**
* Writes the opf package document as defined by namespace http://www.idpf.org/2007/opf
*
* @author paul
*
*/
public class PackageDocumentWriter extends PackageDocumentBase {
//private static final Logger log = Logger.create(PackageDocumentWriter.class);
private static String TAG= PackageDocumentWriter.class.getName();
public static void write(EpubWriter epubWriter, XmlSerializer serializer,
Book book) throws IOException {
try {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF);
serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE);
serializer.startTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version,
"2.0");
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX,
OPFAttributes.uniqueIdentifier, BOOK_ID_ID);
PackageDocumentMetadataWriter.writeMetaData(book, serializer);
writeManifest(book, epubWriter, serializer);
writeSpine(book, epubWriter, serializer);
writeGuide(book, epubWriter, serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer.endDocument();
serializer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Writes the package's spine.
*
* @param book
* @param epubWriter
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* 1@throws XMLStreamException
*/
private static void writeSpine(Book book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.spine);
Resource tocResource = book.getSpine().getTocResource();
String tocResourceId = tocResource.getId();
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.toc,
tocResourceId);
if (book.getCoverPage() != null // there is a cover page
&& book.getSpine().findFirstResourceById(book.getCoverPage().getId())
< 0) { // cover page is not already in the spine
// write the cover html file
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref,
book.getCoverPage().getId());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear,
"no");
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
}
writeSpineItems(book.getSpine(), serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.spine);
}
private static void writeManifest(Book book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.manifest);
serializer.startTag(NAMESPACE_OPF, OPFTags.item);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
epubWriter.getNcxId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
epubWriter.getNcxHref());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type,
epubWriter.getNcxMediaType());
serializer.endTag(NAMESPACE_OPF, OPFTags.item);
// writeCoverResources(book, serializer);
for (Resource resource : getAllResourcesSortById(book)) {
writeItem(book, resource, serializer);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.manifest);
}
private static List<Resource> getAllResourcesSortById(Book book) {
List<Resource> allResources = new ArrayList<Resource>(
book.getResources().getAll());
Collections.sort(allResources, new Comparator<Resource>() {
@Override
public int compare(Resource resource1, Resource resource2) {
return resource1.getId().compareToIgnoreCase(resource2.getId());
}
});
return allResources;
}
/**
* Writes a resources as an item element
* @param resource
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* 1@throws XMLStreamException
*/
private static void writeItem(Book book, Resource resource,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (resource == null ||
(resource.getMediaType() == MediaTypes.NCX
&& book.getSpine().getTocResource() != null)) {
return;
}
if (StringUtil.isBlank(resource.getId())) {
// log.error("resource id must not be empty (href: " + resource.getHref()
// + ", mediatype:" + resource.getMediaType() + ")");
Log.e(TAG,"resource id must not be empty (href: " + resource.getHref()
+ ", mediatype:" + resource.getMediaType() + ")");
return;
}
if (StringUtil.isBlank(resource.getHref())) {
// log.error("resource href must not be empty (id: " + resource.getId()
// + ", mediatype:" + resource.getMediaType() + ")");
Log.e(TAG,"resource href must not be empty (id: " + resource.getId()
+ ", mediatype:" + resource.getMediaType() + ")");
return;
}
if (resource.getMediaType() == null) {
// log.error("resource mediatype must not be empty (id: " + resource.getId()
// + ", href:" + resource.getHref() + ")");
Log.e(TAG,"resource mediatype must not be empty (id: " + resource.getId()
+ ", href:" + resource.getHref() + ")");
return;
}
serializer.startTag(NAMESPACE_OPF, OPFTags.item);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
resource.getId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
resource.getHref());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type,
resource.getMediaType().getName());
serializer.endTag(NAMESPACE_OPF, OPFTags.item);
}
/**
* List all spine references
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
private static void writeSpineItems(Spine spine, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (SpineReference spineReference : spine.getSpineReferences()) {
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref,
spineReference.getResourceId());
if (!spineReference.isLinear()) {
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear,
OPFValues.no);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
}
}
private static void writeGuide(Book book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.guide);
ensureCoverPageGuideReferenceWritten(book.getGuide(), epubWriter,
serializer);
for (GuideReference reference : book.getGuide().getReferences()) {
writeGuideReference(reference, serializer);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.guide);
}
private static void ensureCoverPageGuideReferenceWritten(Guide guide,
EpubWriter epubWriter, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (!(guide.getGuideReferencesByType(GuideReference.COVER).isEmpty())) {
return;
}
Resource coverPage = guide.getCoverPage();
if (coverPage != null) {
writeGuideReference(
new GuideReference(guide.getCoverPage(), GuideReference.COVER,
GuideReference.COVER), serializer);
}
}
private static void writeGuideReference(GuideReference reference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (reference == null) {
return;
}
serializer.startTag(NAMESPACE_OPF, OPFTags.reference);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type,
reference.getType());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
reference.getCompleteHref());
if (StringUtil.isNotBlank(reference.getTitle())) {
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title,
reference.getTitle());
}
serializer.endTag(NAMESPACE_OPF, OPFTags.reference);
}
}

@ -0,0 +1,173 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.domain.EpubResourceProvider;
import me.ag2s.epublib.domain.LazyResource;
import me.ag2s.epublib.domain.LazyResourceProvider;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.Resources;
import me.ag2s.epublib.util.CollectionUtil;
import me.ag2s.epublib.util.ResourceUtil;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
/**
* Loads Resources from inputStreams, ZipFiles, etc
*
* @author paul
*
*/
public class ResourcesLoader {
private static String TAG=ResourceUtil.class.getName();
/**
* Loads the entries of the zipFile as resources.
*
* The MediaTypes that are in the lazyLoadedTypes will not get their
* contents loaded, but are stored as references to entries into the
* ZipFile and are loaded on demand by the Resource system.
*
* @param zipFile
* @param defaultHtmlEncoding
* @param lazyLoadedTypes
* @return
* @throws IOException
*/
public static Resources loadResources(ZipFile zipFile,
String defaultHtmlEncoding,
List<MediaType> lazyLoadedTypes) throws IOException {
LazyResourceProvider resourceProvider =
new EpubResourceProvider(zipFile.getName());
Resources result = new Resources();
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
if (zipEntry == null || zipEntry.isDirectory()) {
continue;
}
String href = zipEntry.getName();
Resource resource;
if (shouldLoadLazy(href, lazyLoadedTypes)) {
resource = new LazyResource(resourceProvider, zipEntry.getSize(), href);
} else {
resource = ResourceUtil
.createResource(zipEntry, zipFile.getInputStream(zipEntry));
}
if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding);
}
result.add(resource);
}
return result;
}
/**
* Whether the given href will load a mediaType that is in the
* collection of lazilyLoadedMediaTypes.
*
* @param href
* @param lazilyLoadedMediaTypes
* @return Whether the given href will load a mediaType that is
* in the collection of lazilyLoadedMediaTypes.
*/
private static boolean shouldLoadLazy(String href,
Collection<MediaType> lazilyLoadedMediaTypes) {
if (CollectionUtil.isEmpty(lazilyLoadedMediaTypes)) {
return false;
}
MediaType mediaType = MediaTypes.determineMediaType(href);
return lazilyLoadedMediaTypes.contains(mediaType);
}
/**
* Loads all entries from the ZipInputStream as Resources.
*
* Loads the contents of all ZipEntries into memory.
* Is fast, but may lead to memory problems when reading large books
* on devices with small amounts of memory.
*
* @param zipInputStream
* @param defaultHtmlEncoding
* @return
* @throws IOException
*/
public static Resources loadResources(ZipInputStream zipInputStream,
String defaultHtmlEncoding) throws IOException {
Resources result = new Resources();
ZipEntry zipEntry;
do {
// get next valid zipEntry
zipEntry = getNextZipEntry(zipInputStream);
if ((zipEntry == null) || zipEntry.isDirectory()) {
continue;
}
// store resource
Resource resource = ResourceUtil.createResource(zipEntry, zipInputStream);
if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding);
}
result.add(resource);
} while (zipEntry != null);
return result;
}
private static ZipEntry getNextZipEntry(ZipInputStream zipInputStream)
throws IOException {
try {
return zipInputStream.getNextEntry();
} catch (ZipException e) {
//see <a href="https://github.com/psiegman/epublib/issues/122">Issue #122 Infinite loop</a>.
//when reading a file that is not a real zip archive or a zero length file, zipInputStream.getNextEntry()
//throws an exception and does not advance, so loadResources enters an infinite loop
//log.error("Invalid or damaged zip file.", e);
Log.e(TAG,"",e);
try {
zipInputStream.closeEntry();
} catch (Exception ignored) {
}
throw e;
}
}
/**
* Loads all entries from the ZipInputStream as Resources.
*
* Loads the contents of all ZipEntries into memory.
* Is fast, but may lead to memory problems when reading large books
* on devices with small amounts of memory.
*
* @param zipFile
* @param defaultHtmlEncoding
* @return
* @throws IOException
*/
public static Resources loadResources(ZipFile zipFile,
String defaultHtmlEncoding) throws IOException {
return loadResources(zipFile, defaultHtmlEncoding, Collections.emptyList());
}
}

@ -0,0 +1,70 @@
package me.ag2s.epublib.util;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
public class CollectionUtil {
/**
* Wraps an Enumeration around an Iterator
* @author paul.siegmann
*
* @param <T>
*/
private static class IteratorEnumerationAdapter<T> implements Enumeration<T> {
private Iterator<T> iterator;
public IteratorEnumerationAdapter(Iterator<T> iter) {
this.iterator = iter;
}
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public T nextElement() {
return iterator.next();
}
}
/**
* Creates an Enumeration out of the given Iterator.
* @param <T>
* @param it
* @return an Enumeration created out of the given Iterator.
*/
public static <T> Enumeration<T> createEnumerationFromIterator(
Iterator<T> it) {
return new IteratorEnumerationAdapter<T>(it);
}
/**
* Returns the first element of the list, null if the list is null or empty.
*
* @param <T>
* @param list
* @return the first element of the list, null if the list is null or empty.
*/
public static <T> T first(List<T> list) {
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0);
}
/**
* Whether the given collection is null or has no elements.
*
* @param collection
* @return Whether the given collection is null or has no elements.
*/
public static boolean isEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
}

@ -0,0 +1,165 @@
package me.ag2s.epublib.util;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
/**
* Most of the functions herein are re-implementations of the ones in
* apache io IOUtils.
*
* The reason for re-implementing this is that the functions are fairly simple
* and using my own implementation saves the inclusion of a 200Kb jar file.
*/
public class IOUtil {
public static final int IO_COPY_BUFFER_SIZE = 1024 * 4;
/**
* Gets the contents of the Reader as a byte[], with the given character encoding.
*
* @param in
* @param encoding
* @return the contents of the Reader as a byte[], with the given character encoding.
* @throws IOException
*/
public static byte[] toByteArray(Reader in, String encoding)
throws IOException {
StringWriter out = new StringWriter();
copy(in, out);
out.flush();
return out.toString().getBytes(encoding);
}
/**
* Returns the contents of the InputStream as a byte[]
*
* @param in
* @return the contents of the InputStream as a byte[]
* @throws IOException
*/
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
copy(in, result);
result.flush();
return result.toByteArray();
}
/**
* Reads data from the InputStream, using the specified buffer size.
*
* This is meant for situations where memory is tight, since
* it prevents buffer expansion.
*
* @param in the stream to read data from
* @param size the size of the array to create
* @return the array, or null
* @throws IOException
*/
public static byte[] toByteArray(InputStream in, int size)
throws IOException {
try {
ByteArrayOutputStream result;
if (size > 0) {
result = new ByteArrayOutputStream(size);
} else {
result = new ByteArrayOutputStream();
}
copy(in, result);
result.flush();
return result.toByteArray();
} catch (OutOfMemoryError error) {
//Return null so it gets loaded lazily.
return null;
}
}
/**
* if totalNrRead &lt; 0 then totalNrRead is returned, if
* (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead
* is returned, -1 otherwise.
*
* @param nrRead
* @param totalNrNread
* @return if totalNrRead &lt; 0 then totalNrRead is returned, if
* (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead
* is returned, -1 otherwise.
*/
protected static int calcNewNrReadSize(int nrRead, int totalNrNread) {
if (totalNrNread < 0) {
return totalNrNread;
}
if (totalNrNread > (Integer.MAX_VALUE - nrRead)) {
return -1;
} else {
return (totalNrNread + nrRead);
}
}
/**
* Copies the contents of the InputStream to the OutputStream.
*
* @param in
* @param out
* @return the nr of bytes read, or -1 if the amount &gt; Integer.MAX_VALUE
* @throws IOException
*/
public static int copy(InputStream in, OutputStream out)
throws IOException {
byte[] buffer = new byte[IO_COPY_BUFFER_SIZE];
int readSize = -1;
int result = 0;
while ((readSize = in.read(buffer)) >= 0) {
out.write(buffer, 0, readSize);
result = calcNewNrReadSize(readSize, result);
}
out.flush();
return result;
}
/**
* Copies the contents of the Reader to the Writer.
*
* @param in
* @param out
* @return the nr of characters read, or -1 if the amount &gt; Integer.MAX_VALUE
* @throws IOException
*/
public static int copy(Reader in, Writer out) throws IOException {
char[] buffer = new char[IO_COPY_BUFFER_SIZE];
int readSize = -1;
int result = 0;
while ((readSize = in.read(buffer)) >= 0) {
out.write(buffer, 0, readSize);
result = calcNewNrReadSize(readSize, result);
}
out.flush();
return result;
}
public static String Stream2String(InputStream inputStream) {
String str="";
try {
BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
for (int result = bis.read(); result != -1; result = bis.read()) {
buf.write((byte) result);
}
str=buf.toString("UTF-8");
}catch (Exception e){
str=null;
}
// StandardCharsets.UTF_8.name() > JDK 7
return str;
}
}

@ -0,0 +1,32 @@
package me.ag2s.epublib.util;
import java.io.IOException;
import java.io.OutputStream;
/**
* OutputStream with the close() disabled.
* We write multiple documents to a ZipOutputStream.
* Some of the formatters call a close() after writing their data.
* We don't want them to do that, so we wrap regular OutputStreams in this NoCloseOutputStream.
*
* @author paul
*/
public class NoCloseOutputStream extends OutputStream {
private OutputStream outputStream;
public NoCloseOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void write(int b) throws IOException {
outputStream.write(b);
}
/**
* A close() that does not call it's parent's close()
*/
public void close() {
}
}

@ -0,0 +1,35 @@
package me.ag2s.epublib.util;
import java.io.IOException;
import java.io.Writer;
/**
* Writer with the close() disabled.
* We write multiple documents to a ZipOutputStream.
* Some of the formatters call a close() after writing their data.
* We don't want them to do that, so we wrap regular Writers in this NoCloseWriter.
*
* @author paul
*/
public class NoCloseWriter extends Writer {
private Writer writer;
public NoCloseWriter(Writer writer) {
this.writer = writer;
}
@Override
public void close() throws IOException {
}
@Override
public void flush() throws IOException {
writer.flush();
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
writer.write(cbuf, off, len);
}
}

@ -0,0 +1,174 @@
package me.ag2s.epublib.util;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.epub.EpubProcessorSupport;
import me.ag2s.epublib.domain.MediaTypes;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Various resource utility methods
*
* @author paul
*/
public class ResourceUtil {
public static Resource createHTMLResource(String title, String string) {
String html=createHtml(title,string);
MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes();
Resource result = new Resource(data, mediaTypeProperty);
return result;
}
public static Resource createHTMLResource(String title, String string,String href) {
String html=createHtml(title,string);
MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes();
Resource result = new Resource(null,data,href ,mediaTypeProperty);
return result;
}
private static String createHtml(String title,String txt){
StringBuilder body= new StringBuilder();
for(String s:txt.split("\\r?\\n")){
s=s.trim();
if(s!=null&&s.length()!=0) {
body.append("<p>").append(s).append("</p>");
}
}
String html="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
html+="<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">";
html+="<head><title>"+title+"</title>" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"css/style.css\"/>" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/></head>";
html+="<body><h1>"+title+"</h1>" +
body+
"</body></html>";
return html;
}
public static Resource createResource(File file) throws IOException {
if (file == null) {
return null;
}
MediaType mediaType = MediaTypes.determineMediaType(file.getName());
byte[] data = IOUtil.toByteArray(new FileInputStream(file));
Resource result = new Resource(data, mediaType);
return result;
}
/**
* Creates a resource with as contents a html page with the given title.
*
* @param title
* @param href
* @return a resource with as contents a html page with the given title.
*/
public static Resource createResource(String title, String href) {
String content =
"<html><head><title>" + title + "</title></head><body><h1>" + title
+ "</h1></body></html>";
return new Resource(null, content.getBytes(), href, MediaTypes.XHTML,
Constants.CHARACTER_ENCODING);
}
/**
* Creates a resource out of the given zipEntry and zipInputStream.
*
* @param zipEntry
* @param zipInputStream
* @return a resource created out of the given zipEntry and zipInputStream.
* @throws IOException
*/
public static Resource createResource(ZipEntry zipEntry,
ZipInputStream zipInputStream) throws IOException {
return new Resource(zipInputStream, zipEntry.getName());
}
public static Resource createResource(ZipEntry zipEntry,
InputStream zipInputStream) throws IOException {
return new Resource(zipInputStream, zipEntry.getName());
}
/**
* Converts a given string from given input character encoding to the requested output character encoding.
*
* @param inputEncoding
* @param outputEncoding
* @param input
* @return the string from given input character encoding converted to the requested output character encoding.
* @throws UnsupportedEncodingException
*/
public static byte[] recode(String inputEncoding, String outputEncoding,
byte[] input) throws UnsupportedEncodingException {
return new String(input, inputEncoding).getBytes(outputEncoding);
}
/**
* Gets the contents of the Resource as an InputSource in a null-safe manner.
*
*/
public static InputSource getInputSource(Resource resource)
throws IOException {
if (resource == null) {
return null;
}
Reader reader = resource.getReader();
if (reader == null) {
return null;
}
InputSource inputSource = new InputSource(reader);
return inputSource;
}
/**
* Reads parses the xml therein and returns the result as a Document
*/
public static Document getAsDocument(Resource resource)
throws SAXException, IOException, ParserConfigurationException {
return getAsDocument(resource,
EpubProcessorSupport.createDocumentBuilder());
}
/**
* Reads the given resources inputstream, parses the xml therein and returns the result as a Document
*
* @param resource
* @param documentBuilder
* @return the document created from the given resource
* @throws UnsupportedEncodingException
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
public static Document getAsDocument(Resource resource,
DocumentBuilder documentBuilder)
throws UnsupportedEncodingException, SAXException, IOException, ParserConfigurationException {
InputSource inputSource = getInputSource(resource);
if (inputSource == null) {
return null;
}
Document result = documentBuilder.parse(inputSource);
return result;
}
}

@ -0,0 +1,202 @@
package me.ag2s.epublib.util;
/*
* Copyright (c) 2006, John Kristian
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of StAX-Utils nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
* Abstract class for writing filtered XML streams. This class provides methods
* that merely delegate to the contained stream. Subclasses should override some
* of these methods, and may also provide additional methods and fields.
*
* @author <a href="mailto:jk2006@engineer.com">John Kristian</a>
*/
public abstract class StreamWriterDelegate implements XMLStreamWriter {
protected StreamWriterDelegate(XMLStreamWriter out) {
this.out = out;
}
protected XMLStreamWriter out;
public Object getProperty(String name)
throws IllegalArgumentException {
return out.getProperty(name);
}
public NamespaceContext getNamespaceContext() {
return out.getNamespaceContext();
}
public void setNamespaceContext(NamespaceContext context)
throws XMLStreamException {
out.setNamespaceContext(context);
}
public void setDefaultNamespace(String uri)
throws XMLStreamException {
out.setDefaultNamespace(uri);
}
public void writeStartDocument() throws XMLStreamException {
out.writeStartDocument();
}
public void writeStartDocument(String version)
throws XMLStreamException {
out.writeStartDocument(version);
}
public void writeStartDocument(String encoding, String version)
throws XMLStreamException {
out.writeStartDocument(encoding, version);
}
public void writeDTD(String dtd) throws XMLStreamException {
out.writeDTD(dtd);
}
public void writeProcessingInstruction(String target)
throws XMLStreamException {
out.writeProcessingInstruction(target);
}
public void writeProcessingInstruction(String target, String data)
throws XMLStreamException {
out.writeProcessingInstruction(target, data);
}
public void writeComment(String data) throws XMLStreamException {
out.writeComment(data);
}
public void writeEmptyElement(String localName)
throws XMLStreamException {
out.writeEmptyElement(localName);
}
public void writeEmptyElement(String namespaceURI, String localName)
throws XMLStreamException {
out.writeEmptyElement(namespaceURI, localName);
}
public void writeEmptyElement(String prefix, String localName,
String namespaceURI) throws XMLStreamException {
out.writeEmptyElement(prefix, localName, namespaceURI);
}
public void writeStartElement(String localName)
throws XMLStreamException {
out.writeStartElement(localName);
}
public void writeStartElement(String namespaceURI, String localName)
throws XMLStreamException {
out.writeStartElement(namespaceURI, localName);
}
public void writeStartElement(String prefix, String localName,
String namespaceURI) throws XMLStreamException {
out.writeStartElement(prefix, localName, namespaceURI);
}
public void writeDefaultNamespace(String namespaceURI)
throws XMLStreamException {
out.writeDefaultNamespace(namespaceURI);
}
public void writeNamespace(String prefix, String namespaceURI)
throws XMLStreamException {
out.writeNamespace(prefix, namespaceURI);
}
public String getPrefix(String uri) throws XMLStreamException {
return out.getPrefix(uri);
}
public void setPrefix(String prefix, String uri)
throws XMLStreamException {
out.setPrefix(prefix, uri);
}
public void writeAttribute(String localName, String value)
throws XMLStreamException {
out.writeAttribute(localName, value);
}
public void writeAttribute(String namespaceURI, String localName,
String value) throws XMLStreamException {
out.writeAttribute(namespaceURI, localName, value);
}
public void writeAttribute(String prefix, String namespaceURI,
String localName, String value) throws XMLStreamException {
out.writeAttribute(prefix, namespaceURI, localName, value);
}
public void writeCharacters(String text) throws XMLStreamException {
out.writeCharacters(text);
}
public void writeCharacters(char[] text, int start, int len)
throws XMLStreamException {
out.writeCharacters(text, start, len);
}
public void writeCData(String data) throws XMLStreamException {
out.writeCData(data);
}
public void writeEntityRef(String name) throws XMLStreamException {
out.writeEntityRef(name);
}
public void writeEndElement() throws XMLStreamException {
out.writeEndElement();
}
public void writeEndDocument() throws XMLStreamException {
out.writeEndDocument();
}
public void flush() throws XMLStreamException {
out.flush();
}
public void close() throws XMLStreamException {
out.close();
}
}

@ -0,0 +1,275 @@
package me.ag2s.epublib.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Various String utility functions.
*
* Most of the functions herein are re-implementations of the ones in apache
* commons StringUtils. The reason for re-implementing this is that the
* functions are fairly simple and using my own implementation saves the
* inclusion of a 200Kb jar file.
*
* @author paul.siegmann
*
*/
public class StringUtil {
/**
* Changes a path containing '..', '.' and empty dirs into a path that
* doesn't. X/foo/../Y is changed into 'X/Y', etc. Does not handle invalid
* paths like "../".
*
* @param path
* @return the normalized path
*/
public static String collapsePathDots(String path) {
String[] stringParts = path.split("/");
List<String> parts = new ArrayList<String>(Arrays.asList(stringParts));
for (int i = 0; i < parts.size() - 1; i++) {
String currentDir = parts.get(i);
if (currentDir.length() == 0 || currentDir.equals(".")) {
parts.remove(i);
i--;
} else if (currentDir.equals("..")) {
parts.remove(i - 1);
parts.remove(i - 1);
i -= 2;
}
}
StringBuilder result = new StringBuilder();
if (path.startsWith("/")) {
result.append('/');
}
for (int i = 0; i < parts.size(); i++) {
result.append(parts.get(i));
if (i < (parts.size() - 1)) {
result.append('/');
}
}
return result.toString();
}
/**
* Whether the String is not null, not zero-length and does not contain of
* only whitespace.
*
* @param text
* @return Whether the String is not null, not zero-length and does not contain of
*/
public static boolean isNotBlank(String text) {
return !isBlank(text);
}
/**
* Whether the String is null, zero-length and does contain only whitespace.
*
* @return Whether the String is null, zero-length and does contain only whitespace.
*/
public static boolean isBlank(String text) {
if (isEmpty(text)) {
return true;
}
for (int i = 0; i < text.length(); i++) {
if (!Character.isWhitespace(text.charAt(i))) {
return false;
}
}
return true;
}
/**
* Whether the given string is null or zero-length.
*
* @param text the input for this method
* @return Whether the given string is null or zero-length.
*/
public static boolean isEmpty(String text) {
return (text == null) || (text.length() == 0);
}
/**
* Whether the given source string ends with the given suffix, ignoring
* case.
*
* @param source
* @param suffix
* @return Whether the given source string ends with the given suffix, ignoring case.
*/
public static boolean endsWithIgnoreCase(String source, String suffix) {
if (isEmpty(suffix)) {
return true;
}
if (isEmpty(source)) {
return false;
}
if (suffix.length() > source.length()) {
return false;
}
return source.substring(source.length() - suffix.length())
.toLowerCase().endsWith(suffix.toLowerCase());
}
/**
* If the given text is null return "", the original text otherwise.
*
* @param text
* @return If the given text is null "", the original text otherwise.
*/
public static String defaultIfNull(String text) {
return defaultIfNull(text, "");
}
/**
* If the given text is null return "", the given defaultValue otherwise.
*
* @param text
* @param defaultValue
* @return If the given text is null "", the given defaultValue otherwise.
*/
public static String defaultIfNull(String text, String defaultValue) {
if (text == null) {
return defaultValue;
}
return text;
}
/**
* Null-safe string comparator
*
* @param text1
* @param text2
* @return whether the two strings are equal
*/
public static boolean equals(String text1, String text2) {
if (text1 == null) {
return (text2 == null);
}
return text1.equals(text2);
}
/**
* Pretty toString printer.
*
* @param keyValues
* @return a string representation of the input values
*/
public static String toString(Object... keyValues) {
StringBuilder result = new StringBuilder();
result.append('[');
for (int i = 0; i < keyValues.length; i += 2) {
if (i > 0) {
result.append(", ");
}
result.append(keyValues[i]);
result.append(": ");
Object value = null;
if ((i + 1) < keyValues.length) {
value = keyValues[i + 1];
}
if (value == null) {
result.append("<null>");
} else {
result.append('\'');
result.append(value);
result.append('\'');
}
}
result.append(']');
return result.toString();
}
public static int hashCode(String... values) {
int result = 31;
for (int i = 0; i < values.length; i++) {
result ^= String.valueOf(values[i]).hashCode();
}
return result;
}
/**
* Gives the substring of the given text before the given separator.
*
* If the text does not contain the given separator then the given text is
* returned.
*
* @param text
* @param separator
* @return the substring of the given text before the given separator.
*/
public static String substringBefore(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int sepPos = text.indexOf(separator);
if (sepPos < 0) {
return text;
}
return text.substring(0, sepPos);
}
/**
* Gives the substring of the given text before the last occurrence of the
* given separator.
*
* If the text does not contain the given separator then the given text is
* returned.
*
* @param text
* @param separator
* @return the substring of the given text before the last occurrence of the given separator.
*/
public static String substringBeforeLast(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int cPos = text.lastIndexOf(separator);
if (cPos < 0) {
return text;
}
return text.substring(0, cPos);
}
/**
* Gives the substring of the given text after the last occurrence of the
* given separator.
*
* If the text does not contain the given separator then "" is returned.
*
* @param text
* @param separator
* @return the substring of the given text after the last occurrence of the given separator.
*/
public static String substringAfterLast(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int cPos = text.lastIndexOf(separator);
if (cPos < 0) {
return "";
}
return text.substring(cPos + 1);
}
/**
* Gives the substring of the given text after the given separator.
*
* If the text does not contain the given separator then "" is returned.
*
* @param text the input text
* @param c the separator char
* @return the substring of the given text after the given separator.
*/
public static String substringAfter(String text, char c) {
if (isEmpty(text)) {
return text;
}
int cPos = text.indexOf(c);
if (cPos < 0) {
return "";
}
return text.substring(cPos + 1);
}
}

@ -0,0 +1,340 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.ag2s.epublib.util.commons.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
/**
* This class is used to wrap a stream that includes an encoded
* {@link ByteOrderMark} as its first bytes.
*
* This class detects these bytes and, if required, can automatically skip them
* and return the subsequent byte as the first byte in the stream.
*
* The {@link ByteOrderMark} implementation has the following pre-defined BOMs:
* <ul>
* <li>UTF-8 - {@link ByteOrderMark#UTF_8}</li>
* <li>UTF-16BE - {@link ByteOrderMark#UTF_16LE}</li>
* <li>UTF-16LE - {@link ByteOrderMark#UTF_16BE}</li>
* </ul>
*
*
* <h3>Example 1 - Detect and exclude a UTF-8 BOM</h3>
* <pre>
* BOMInputStream bomIn = new BOMInputStream(in);
* if (bomIn.hasBOM()) {
* // has a UTF-8 BOM
* }
* </pre>
*
* <h3>Example 2 - Detect a UTF-8 BOM (but don't exclude it)</h3>
* <pre>
* boolean include = true;
* BOMInputStream bomIn = new BOMInputStream(in, include);
* if (bomIn.hasBOM()) {
* // has a UTF-8 BOM
* }
* </pre>
*
* <h3>Example 3 - Detect Multiple BOMs</h3>
* <pre>
* BOMInputStream bomIn = new BOMInputStream(in, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE);
* if (bomIn.hasBOM() == false) {
* // No BOM found
* } else if (bomIn.hasBOM(ByteOrderMark.UTF_16LE)) {
* // has a UTF-16LE BOM
* } else if (bomIn.hasBOM(ByteOrderMark.UTF_16BE)) {
* // has a UTF-16BE BOM
* }
* </pre>
*
* @see ByteOrderMark
* @see <a href="http://en.wikipedia.org/wiki/Byte_order_mark">Wikipedia - Byte Order Mark</a>
* @version $Revision: 1052095 $ $Date: 2010-12-22 23:03:20 +0000 (Wed, 22 Dec 2010) $
* @since Commons IO 2.0
*/
public class BOMInputStream extends ProxyInputStream {
private final boolean include;
private final List<ByteOrderMark> boms;
private ByteOrderMark byteOrderMark;
private int[] firstBytes;
private int fbLength;
private int fbIndex;
private int markFbIndex;
private boolean markedAtStart;
/**
* Constructs a new BOM InputStream that excludes
* a {@link ByteOrderMark#UTF_8} BOM.
* @param delegate the InputStream to delegate to
*/
public BOMInputStream(InputStream delegate) {
this(delegate, false, ByteOrderMark.UTF_8);
}
/**
* Constructs a new BOM InputStream that detects a
* a {@link ByteOrderMark#UTF_8} and optionally includes it.
* @param delegate the InputStream to delegate to
* @param include true to include the UTF-8 BOM or
* false to exclude it
*/
public BOMInputStream(InputStream delegate, boolean include) {
this(delegate, include, ByteOrderMark.UTF_8);
}
/**
* Constructs a new BOM InputStream that excludes
* the specified BOMs.
* @param delegate the InputStream to delegate to
* @param boms The BOMs to detect and exclude
*/
public BOMInputStream(InputStream delegate, ByteOrderMark... boms) {
this(delegate, false, boms);
}
/**
* Constructs a new BOM InputStream that detects the
* specified BOMs and optionally includes them.
* @param delegate the InputStream to delegate to
* @param include true to include the specified BOMs or
* false to exclude them
* @param boms The BOMs to detect and optionally exclude
*/
public BOMInputStream(InputStream delegate, boolean include, ByteOrderMark... boms) {
super(delegate);
if (boms == null || boms.length == 0) {
throw new IllegalArgumentException("No BOMs specified");
}
this.include = include;
this.boms = Arrays.asList(boms);
}
/**
* Indicates whether the stream contains one of the specified BOMs.
*
* @return true if the stream has one of the specified BOMs, otherwise false
* if it does not
* @throws IOException if an error reading the first bytes of the stream occurs
*/
public boolean hasBOM() throws IOException {
return (getBOM() != null);
}
/**
* Indicates whether the stream contains the specified BOM.
*
* @param bom The BOM to check for
* @return true if the stream has the specified BOM, otherwise false
* if it does not
* @throws IllegalArgumentException if the BOM is not one the stream
* is configured to detect
* @throws IOException if an error reading the first bytes of the stream occurs
*/
public boolean hasBOM(ByteOrderMark bom) throws IOException {
if (!boms.contains(bom)) {
throw new IllegalArgumentException("Stream not configure to detect " + bom);
}
return (byteOrderMark != null && getBOM().equals(bom));
}
/**
* Return the BOM (Byte Order Mark).
*
* @return The BOM or null if none
* @throws IOException if an error reading the first bytes of the stream occurs
*/
public ByteOrderMark getBOM() throws IOException {
if (firstBytes == null) {
int max = 0;
for (ByteOrderMark bom : boms) {
max = Math.max(max, bom.length());
}
firstBytes = new int[max];
for (int i = 0; i < firstBytes.length; i++) {
firstBytes[i] = in.read();
fbLength++;
if (firstBytes[i] < 0) {
break;
}
byteOrderMark = find();
if (byteOrderMark != null) {
if (!include) {
fbLength = 0;
}
break;
}
}
}
return byteOrderMark;
}
/**
* Return the BOM charset Name - {@link ByteOrderMark#getCharsetName()}.
*
* @return The BOM charset Name or null if no BOM found
* @throws IOException if an error reading the first bytes of the stream occurs
*
*/
public String getBOMCharsetName() throws IOException {
getBOM();
return (byteOrderMark == null ? null : byteOrderMark.getCharsetName());
}
/**
* This method reads and either preserves or skips the first bytes in the
* stream. It behaves like the single-byte <code>read()</code> method,
* either returning a valid byte or -1 to indicate that the initial bytes
* have been processed already.
* @return the byte read (excluding BOM) or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
private int readFirstBytes() throws IOException {
getBOM();
return (fbIndex < fbLength) ? firstBytes[fbIndex++] : -1;
}
/**
* Find a BOM with the specified bytes.
*
* @return The matched BOM or null if none matched
*/
private ByteOrderMark find() {
for (ByteOrderMark bom : boms) {
if (matches(bom)) {
return bom;
}
}
return null;
}
/**
* Check if the bytes match a BOM.
*
* @param bom The BOM
* @return true if the bytes match the bom, otherwise false
*/
private boolean matches(ByteOrderMark bom) {
if (bom.length() != fbLength) {
return false;
}
for (int i = 0; i < bom.length(); i++) {
if (bom.get(i) != firstBytes[i]) {
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
// Implementation of InputStream
//----------------------------------------------------------------------------
/**
* Invokes the delegate's <code>read()</code> method, detecting and
* optionally skipping BOM.
* @return the byte read (excluding BOM) or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read() throws IOException {
int b = readFirstBytes();
return (b >= 0) ? b : in.read();
}
/**
* Invokes the delegate's <code>read(byte[], int, int)</code> method, detecting
* and optionally skipping BOM.
* @param buf the buffer to read the bytes into
* @param off The start offset
* @param len The number of bytes to read (excluding BOM)
* @return the number of bytes read or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read(byte[] buf, int off, int len) throws IOException {
int firstCount = 0;
int b = 0;
while ((len > 0) && (b >= 0)) {
b = readFirstBytes();
if (b >= 0) {
buf[off++] = (byte) (b & 0xFF);
len--;
firstCount++;
}
}
int secondCount = in.read(buf, off, len);
return (secondCount < 0) ? (firstCount > 0 ? firstCount : -1) : firstCount + secondCount;
}
/**
* Invokes the delegate's <code>read(byte[])</code> method, detecting and
* optionally skipping BOM.
* @param buf the buffer to read the bytes into
* @return the number of bytes read (excluding BOM)
* or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read(byte[] buf) throws IOException {
return read(buf, 0, buf.length);
}
/**
* Invokes the delegate's <code>mark(int)</code> method.
* @param readlimit read ahead limit
*/
@Override
public synchronized void mark(int readlimit) {
markFbIndex = fbIndex;
markedAtStart = (firstBytes == null);
in.mark(readlimit);
}
/**
* Invokes the delegate's <code>reset()</code> method.
* @throws IOException if an I/O error occurs
*/
@Override
public synchronized void reset() throws IOException {
fbIndex = markFbIndex;
if (markedAtStart) {
firstBytes = null;
}
in.reset();
}
/**
* Invokes the delegate's <code>skip(long)</code> method, detecting
* and optionallyskipping BOM.
* @param n the number of bytes to skip
* @return the number of bytes to skipped or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public long skip(long n) throws IOException {
while ((n > 0) && (readFirstBytes() >= 0)) {
n--;
}
return in.skip(n);
}
}

@ -0,0 +1,170 @@
package me.ag2s.epublib.util.commons.io;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.Serializable;
/**
* Byte Order Mark (BOM) representation -
* see {@link BOMInputStream}.
*
* @see BOMInputStream
* @see <a href="http://en.wikipedia.org/wiki/Byte_order_mark">Wikipedia - Byte Order Mark</a>
* @version $Id: ByteOrderMark.java 1005099 2010-10-06 16:13:01Z niallp $
* @since Commons IO 2.0
*/
public class ByteOrderMark implements Serializable {
private static final long serialVersionUID = 1L;
/** UTF-8 BOM */
public static final ByteOrderMark UTF_8 = new ByteOrderMark("UTF-8", 0xEF, 0xBB, 0xBF);
/** UTF-16BE BOM (Big Endian) */
public static final ByteOrderMark UTF_16BE = new ByteOrderMark("UTF-16BE", 0xFE, 0xFF);
/** UTF-16LE BOM (Little Endian) */
public static final ByteOrderMark UTF_16LE = new ByteOrderMark("UTF-16LE", 0xFF, 0xFE);
private final String charsetName;
private final int[] bytes;
/**
* Construct a new BOM.
*
* @param charsetName The name of the charset the BOM represents
* @param bytes The BOM's bytes
* @throws IllegalArgumentException if the charsetName is null or
* zero length
* @throws IllegalArgumentException if the bytes are null or zero
* length
*/
public ByteOrderMark(String charsetName, int... bytes) {
if (charsetName == null || charsetName.length() == 0) {
throw new IllegalArgumentException("No charsetName specified");
}
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException("No bytes specified");
}
this.charsetName = charsetName;
this.bytes = new int[bytes.length];
System.arraycopy(bytes, 0, this.bytes, 0, bytes.length);
}
/**
* Return the name of the {@link java.nio.charset.Charset} the BOM represents.
*
* @return the character set name
*/
public String getCharsetName() {
return charsetName;
}
/**
* Return the length of the BOM's bytes.
*
* @return the length of the BOM's bytes
*/
public int length() {
return bytes.length;
}
/**
* The byte at the specified position.
*
* @param pos The position
* @return The specified byte
*/
public int get(int pos) {
return bytes[pos];
}
/**
* Return a copy of the BOM's bytes.
*
* @return a copy of the BOM's bytes
*/
public byte[] getBytes() {
byte[] copy = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
copy[i] = (byte)bytes[i];
}
return copy;
}
/**
* Indicates if this BOM's bytes equals another.
*
* @param obj The object to compare to
* @return true if the bom's bytes are equal, otherwise
* false
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ByteOrderMark)) {
return false;
}
ByteOrderMark bom = (ByteOrderMark)obj;
if (bytes.length != bom.length()) {
return false;
}
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] != bom.get(i)) {
return false;
}
}
return true;
}
/**
* Return the hashcode for this BOM.
*
* @return the hashcode for this BOM.
* @see Object#hashCode()
*/
@Override
public int hashCode() {
int hashCode = getClass().hashCode();
for (int b : bytes) {
hashCode += b;
}
return hashCode;
}
/**
* Provide a String representation of the BOM.
*
* @return the length of the BOM's bytes
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName());
builder.append('[');
builder.append(charsetName);
builder.append(": ");
for (int i = 0; i < bytes.length; i++) {
if (i > 0) {
builder.append(",");
}
builder.append("0x");
builder.append(Integer.toHexString(0xFF & bytes[i]).toUpperCase());
}
builder.append(']');
return builder.toString();
}
}

@ -0,0 +1,238 @@
package me.ag2s.epublib.util.commons.io;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* A Proxy stream which acts as expected, that is it passes the method
* calls on to the proxied stream and doesn't change which methods are
* being called.
* <p>
* It is an alternative base class to FilterInputStream
* to increase reusability, because FilterInputStream changes the
* methods being called, such as read(byte[]) to read(byte[], int, int).
* <p>
* See the protected methods for ways in which a subclass can easily decorate
* a stream with custom pre-, post- or error processing functionality.
*
* @author Stephen Colebourne
* @version $Id: ProxyInputStream.java 934041 2010-04-14 17:37:24Z jukka $
*/
public abstract class ProxyInputStream extends FilterInputStream {
/**
* Constructs a new ProxyInputStream.
*
* @param proxy the InputStream to delegate to
*/
public ProxyInputStream(InputStream proxy) {
super(proxy);
// the proxy is stored in a protected superclass variable named 'in'
}
/**
* Invokes the delegate's <code>read()</code> method.
* @return the byte read or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read() throws IOException {
try {
beforeRead(1);
int b = in.read();
afterRead(b != -1 ? 1 : -1);
return b;
} catch (IOException e) {
handleIOException(e);
return -1;
}
}
/**
* Invokes the delegate's <code>read(byte[])</code> method.
* @param bts the buffer to read the bytes into
* @return the number of bytes read or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read(byte[] bts) throws IOException {
try {
beforeRead(bts != null ? bts.length : 0);
int n = in.read(bts);
afterRead(n);
return n;
} catch (IOException e) {
handleIOException(e);
return -1;
}
}
/**
* Invokes the delegate's <code>read(byte[], int, int)</code> method.
* @param bts the buffer to read the bytes into
* @param off The start offset
* @param len The number of bytes to read
* @return the number of bytes read or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read(byte[] bts, int off, int len) throws IOException {
try {
beforeRead(len);
int n = in.read(bts, off, len);
afterRead(n);
return n;
} catch (IOException e) {
handleIOException(e);
return -1;
}
}
/**
* Invokes the delegate's <code>skip(long)</code> method.
* @param ln the number of bytes to skip
* @return the actual number of bytes skipped
* @throws IOException if an I/O error occurs
*/
@Override
public long skip(long ln) throws IOException {
try {
return in.skip(ln);
} catch (IOException e) {
handleIOException(e);
return 0;
}
}
/**
* Invokes the delegate's <code>available()</code> method.
* @return the number of available bytes
* @throws IOException if an I/O error occurs
*/
@Override
public int available() throws IOException {
try {
return super.available();
} catch (IOException e) {
handleIOException(e);
return 0;
}
}
/**
* Invokes the delegate's <code>close()</code> method.
* @throws IOException if an I/O error occurs
*/
@Override
public void close() throws IOException {
try {
in.close();
} catch (IOException e) {
handleIOException(e);
}
}
/**
* Invokes the delegate's <code>mark(int)</code> method.
* @param readlimit read ahead limit
*/
@Override
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
/**
* Invokes the delegate's <code>reset()</code> method.
* @throws IOException if an I/O error occurs
*/
@Override
public synchronized void reset() throws IOException {
try {
in.reset();
} catch (IOException e) {
handleIOException(e);
}
}
/**
* Invokes the delegate's <code>markSupported()</code> method.
* @return true if mark is supported, otherwise false
*/
@Override
public boolean markSupported() {
return in.markSupported();
}
/**
* Invoked by the read methods before the call is proxied. The number
* of bytes that the caller wanted to read (1 for the {@link #read()}
* method, buffer length for {@link #read(byte[])}, etc.) is given as
* an argument.
* <p>
* Subclasses can override this method to add common pre-processing
* functionality without having to override all the read methods.
* The default implementation does nothing.
* <p>
* Note this method is <em>not</em> called from {@link #skip(long)} or
* {@link #reset()}. You need to explicitly override those methods if
* you want to add pre-processing steps also to them.
*
* @since Commons IO 2.0
* @param n number of bytes that the caller asked to be read
* @throws IOException if the pre-processing fails
*/
protected void beforeRead(int n) throws IOException {
}
/**
* Invoked by the read methods after the proxied call has returned
* successfully. The number of bytes returned to the caller (or -1 if
* the end of stream was reached) is given as an argument.
* <p>
* Subclasses can override this method to add common post-processing
* functionality without having to override all the read methods.
* The default implementation does nothing.
* <p>
* Note this method is <em>not</em> called from {@link #skip(long)} or
* {@link #reset()}. You need to explicitly override those methods if
* you want to add post-processing steps also to them.
*
* @since Commons IO 2.0
* @param n number of bytes read, or -1 if the end of stream was reached
* @throws IOException if the post-processing fails
*/
protected void afterRead(int n) throws IOException {
}
/**
* Handle any IOExceptions thrown.
* <p>
* This method provides a point to implement custom exception
* handling. The default behaviour is to re-throw the exception.
* @param e The IOException thrown
* @throws IOException if an I/O error occurs
* @since Commons IO 2.0
*/
protected void handleIOException(IOException e) throws IOException {
throw e;
}
}

@ -0,0 +1,752 @@
package me.ag2s.epublib.util.commons.io;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Character stream that handles all the necessary Voodo to figure out the
* charset encoding of the XML document within the stream.
* <p>
* IMPORTANT: This class is not related in any way to the org.xml.sax.XMLReader.
* This one IS a character stream.
* <p>
* All this has to be done without consuming characters from the stream, if not
* the XML parser will not recognized the document as a valid XML. This is not
* 100% true, but it's close enough (UTF-8 BOM is not handled by all parsers
* right now, XmlStreamReader handles it and things work in all parsers).
* <p>
* The XmlStreamReader class handles the charset encoding of XML documents in
* Files, raw streams and HTTP streams by offering a wide set of constructors.
* <p>
* By default the charset encoding detection is lenient, the constructor with
* the lenient flag can be used for an script (following HTTP MIME and XML
* specifications). All this is nicely explained by Mark Pilgrim in his blog, <a
* href="http://diveintomark.org/archives/2004/02/13/xml-media-types">
* Determining the character encoding of a feed</a>.
* <p>
* Originally developed for <a href="http://rome.dev.java.net">ROME</a> under
* Apache License 2.0.
*
* @author Alejandro Abdelnur
* @version $Id: XmlStreamReader.java 1052161 2010-12-23 03:12:09Z niallp $
* @see "org.apache.commons.io.output.XmlStreamWriter"
* @since Commons IO 2.0
*/
public class XmlStreamReader extends Reader {
private static final int BUFFER_SIZE = 4096;
private static final String UTF_8 = "UTF-8";
private static final String US_ASCII = "US-ASCII";
private static final String UTF_16BE = "UTF-16BE";
private static final String UTF_16LE = "UTF-16LE";
private static final String UTF_16 = "UTF-16";
private static final String EBCDIC = "CP1047";
private static final ByteOrderMark[] BOMS = new ByteOrderMark[] {
ByteOrderMark.UTF_8,
ByteOrderMark.UTF_16BE,
ByteOrderMark.UTF_16LE
};
private static final ByteOrderMark[] XML_GUESS_BYTES = new ByteOrderMark[] {
new ByteOrderMark(UTF_8, 0x3C, 0x3F, 0x78, 0x6D),
new ByteOrderMark(UTF_16BE, 0x00, 0x3C, 0x00, 0x3F),
new ByteOrderMark(UTF_16LE, 0x3C, 0x00, 0x3F, 0x00),
new ByteOrderMark(EBCDIC, 0x4C, 0x6F, 0xA7, 0x94)
};
private final Reader reader;
private final String encoding;
private final String defaultEncoding;
/**
* Returns the default encoding to use if none is set in HTTP content-type,
* XML prolog and the rules based on content-type are not adequate.
* <p>
* If it is NULL the content-type based rules are used.
*
* @return the default encoding to use.
*/
public String getDefaultEncoding() {
return defaultEncoding;
}
/**
* Creates a Reader for a File.
* <p>
* It looks for the UTF-8 BOM first, if none sniffs the XML prolog charset,
* if this is also missing defaults to UTF-8.
* <p>
* It does a lenient charset encoding detection, check the constructor with
* the lenient parameter for details.
*
* @param file File to create a Reader from.
* @throws IOException thrown if there is a problem reading the file.
*/
public XmlStreamReader(File file) throws IOException {
this(new FileInputStream(file));
}
/**
* Creates a Reader for a raw InputStream.
* <p>
* It follows the same logic used for files.
* <p>
* It does a lenient charset encoding detection, check the constructor with
* the lenient parameter for details.
*
* @param is InputStream to create a Reader from.
* @throws IOException thrown if there is a problem reading the stream.
*/
public XmlStreamReader(InputStream is) throws IOException {
this(is, true);
}
/**
* Creates a Reader for a raw InputStream.
* <p>
* It follows the same logic used for files.
* <p>
* If lenient detection is indicated and the detection above fails as per
* specifications it then attempts the following:
* <p>
* If the content type was 'text/html' it replaces it with 'text/xml' and
* tries the detection again.
* <p>
* Else if the XML prolog had a charset encoding that encoding is used.
* <p>
* Else if the content type had a charset encoding that encoding is used.
* <p>
* Else 'UTF-8' is used.
* <p>
* If lenient detection is indicated an XmlStreamReaderException is never
* thrown.
*
* @param is InputStream to create a Reader from.
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @throws IOException thrown if there is a problem reading the stream.
* @throws XmlStreamReaderException thrown if the charset encoding could not
* be determined according to the specs.
*/
public XmlStreamReader(InputStream is, boolean lenient) throws IOException {
this(is, lenient, null);
}
/**
* Creates a Reader for a raw InputStream.
* <p>
* It follows the same logic used for files.
* <p>
* If lenient detection is indicated and the detection above fails as per
* specifications it then attempts the following:
* <p>
* If the content type was 'text/html' it replaces it with 'text/xml' and
* tries the detection again.
* <p>
* Else if the XML prolog had a charset encoding that encoding is used.
* <p>
* Else if the content type had a charset encoding that encoding is used.
* <p>
* Else 'UTF-8' is used.
* <p>
* If lenient detection is indicated an XmlStreamReaderException is never
* thrown.
*
* @param is InputStream to create a Reader from.
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @param defaultEncoding The default encoding
* @throws IOException thrown if there is a problem reading the stream.
* @throws XmlStreamReaderException thrown if the charset encoding could not
* be determined according to the specs.
*/
public XmlStreamReader(InputStream is, boolean lenient, String defaultEncoding) throws IOException {
this.defaultEncoding = defaultEncoding;
BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, BUFFER_SIZE), false, BOMS);
BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES);
this.encoding = doRawStream(bom, pis, lenient);
this.reader = new InputStreamReader(pis, encoding);
}
/**
* Creates a Reader using the InputStream of a URL.
* <p>
* If the URL is not of type HTTP and there is not 'content-type' header in
* the fetched data it uses the same logic used for Files.
* <p>
* If the URL is a HTTP Url or there is a 'content-type' header in the
* fetched data it uses the same logic used for an InputStream with
* content-type.
* <p>
* It does a lenient charset encoding detection, check the constructor with
* the lenient parameter for details.
*
* @param url URL to create a Reader from.
* @throws IOException thrown if there is a problem reading the stream of
* the URL.
*/
public XmlStreamReader(URL url) throws IOException {
this(url.openConnection(), null);
}
/**
* Creates a Reader using the InputStream of a URLConnection.
* <p>
* If the URLConnection is not of type HttpURLConnection and there is not
* 'content-type' header in the fetched data it uses the same logic used for
* files.
* <p>
* If the URLConnection is a HTTP Url or there is a 'content-type' header in
* the fetched data it uses the same logic used for an InputStream with
* content-type.
* <p>
* It does a lenient charset encoding detection, check the constructor with
* the lenient parameter for details.
*
* @param conn URLConnection to create a Reader from.
* @param defaultEncoding The default encoding
* @throws IOException thrown if there is a problem reading the stream of
* the URLConnection.
*/
public XmlStreamReader(URLConnection conn, String defaultEncoding) throws IOException {
this.defaultEncoding = defaultEncoding;
boolean lenient = true;
String contentType = conn.getContentType();
InputStream is = conn.getInputStream();
BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, BUFFER_SIZE), false, BOMS);
BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES);
if (conn instanceof HttpURLConnection || contentType != null) {
this.encoding = doHttpStream(bom, pis, contentType, lenient);
} else {
this.encoding = doRawStream(bom, pis, lenient);
}
this.reader = new InputStreamReader(pis, encoding);
}
/**
* Creates a Reader using an InputStream an the associated content-type
* header.
* <p>
* First it checks if the stream has BOM. If there is not BOM checks the
* content-type encoding. If there is not content-type encoding checks the
* XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
* <p>
* It does a lenient charset encoding detection, check the constructor with
* the lenient parameter for details.
*
* @param is InputStream to create the reader from.
* @param httpContentType content-type header to use for the resolution of
* the charset encoding.
* @throws IOException thrown if there is a problem reading the file.
*/
public XmlStreamReader(InputStream is, String httpContentType)
throws IOException {
this(is, httpContentType, true);
}
/**
* Creates a Reader using an InputStream an the associated content-type
* header. This constructor is lenient regarding the encoding detection.
* <p>
* First it checks if the stream has BOM. If there is not BOM checks the
* content-type encoding. If there is not content-type encoding checks the
* XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
* <p>
* If lenient detection is indicated and the detection above fails as per
* specifications it then attempts the following:
* <p>
* If the content type was 'text/html' it replaces it with 'text/xml' and
* tries the detection again.
* <p>
* Else if the XML prolog had a charset encoding that encoding is used.
* <p>
* Else if the content type had a charset encoding that encoding is used.
* <p>
* Else 'UTF-8' is used.
* <p>
* If lenient detection is indicated an XmlStreamReaderException is never
* thrown.
*
* @param is InputStream to create the reader from.
* @param httpContentType content-type header to use for the resolution of
* the charset encoding.
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @param defaultEncoding The default encoding
* @throws IOException thrown if there is a problem reading the file.
* @throws XmlStreamReaderException thrown if the charset encoding could not
* be determined according to the specs.
*/
public XmlStreamReader(InputStream is, String httpContentType,
boolean lenient, String defaultEncoding) throws IOException {
this.defaultEncoding = defaultEncoding;
BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, BUFFER_SIZE), false, BOMS);
BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES);
this.encoding = doHttpStream(bom, pis, httpContentType, lenient);
this.reader = new InputStreamReader(pis, encoding);
}
/**
* Creates a Reader using an InputStream an the associated content-type
* header. This constructor is lenient regarding the encoding detection.
* <p>
* First it checks if the stream has BOM. If there is not BOM checks the
* content-type encoding. If there is not content-type encoding checks the
* XML prolog encoding. If there is not XML prolog encoding uses the default
* encoding mandated by the content-type MIME type.
* <p>
* If lenient detection is indicated and the detection above fails as per
* specifications it then attempts the following:
* <p>
* If the content type was 'text/html' it replaces it with 'text/xml' and
* tries the detection again.
* <p>
* Else if the XML prolog had a charset encoding that encoding is used.
* <p>
* Else if the content type had a charset encoding that encoding is used.
* <p>
* Else 'UTF-8' is used.
* <p>
* If lenient detection is indicated an XmlStreamReaderException is never
* thrown.
*
* @param is InputStream to create the reader from.
* @param httpContentType content-type header to use for the resolution of
* the charset encoding.
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @throws IOException thrown if there is a problem reading the file.
* @throws XmlStreamReaderException thrown if the charset encoding could not
* be determined according to the specs.
*/
public XmlStreamReader(InputStream is, String httpContentType,
boolean lenient) throws IOException {
this(is, httpContentType, lenient, null);
}
/**
* Returns the charset encoding of the XmlStreamReader.
*
* @return charset encoding.
*/
public String getEncoding() {
return encoding;
}
/**
* Invokes the underlying reader's <code>read(char[], int, int)</code> method.
* @param buf the buffer to read the characters into
* @param offset The start offset
* @param len The number of bytes to read
* @return the number of characters read or -1 if the end of stream
* @throws IOException if an I/O error occurs
*/
@Override
public int read(char[] buf, int offset, int len) throws IOException {
return reader.read(buf, offset, len);
}
/**
* Closes the XmlStreamReader stream.
*
* @throws IOException thrown if there was a problem closing the stream.
*/
@Override
public void close() throws IOException {
reader.close();
}
/**
* Process the raw stream.
*
* @param bom BOMInputStream to detect byte order marks
* @param pis BOMInputStream to guess XML encoding
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @return the encoding to be used
* @throws IOException thrown if there is a problem reading the stream.
*/
private String doRawStream(BOMInputStream bom, BOMInputStream pis, boolean lenient)
throws IOException {
String bomEnc = bom.getBOMCharsetName();
String xmlGuessEnc = pis.getBOMCharsetName();
String xmlEnc = getXmlProlog(pis, xmlGuessEnc);
try {
return calculateRawEncoding(bomEnc, xmlGuessEnc, xmlEnc);
} catch (XmlStreamReaderException ex) {
if (lenient) {
return doLenientDetection(null, ex);
} else {
throw ex;
}
}
}
/**
* Process a HTTP stream.
*
* @param bom BOMInputStream to detect byte order marks
* @param pis BOMInputStream to guess XML encoding
* @param httpContentType The HTTP content type
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @return the encoding to be used
* @throws IOException thrown if there is a problem reading the stream.
*/
private String doHttpStream(BOMInputStream bom, BOMInputStream pis, String httpContentType,
boolean lenient) throws IOException {
String bomEnc = bom.getBOMCharsetName();
String xmlGuessEnc = pis.getBOMCharsetName();
String xmlEnc = getXmlProlog(pis, xmlGuessEnc);
try {
return calculateHttpEncoding(httpContentType, bomEnc,
xmlGuessEnc, xmlEnc, lenient);
} catch (XmlStreamReaderException ex) {
if (lenient) {
return doLenientDetection(httpContentType, ex);
} else {
throw ex;
}
}
}
/**
* Do lenient detection.
*
* @param httpContentType content-type header to use for the resolution of
* the charset encoding.
* @param ex The thrown exception
* @return the encoding
* @throws IOException thrown if there is a problem reading the stream.
*/
private String doLenientDetection(String httpContentType,
XmlStreamReaderException ex) throws IOException {
if (httpContentType != null && httpContentType.startsWith("text/html")) {
httpContentType = httpContentType.substring("text/html".length());
httpContentType = "text/xml" + httpContentType;
try {
return calculateHttpEncoding(httpContentType, ex.getBomEncoding(),
ex.getXmlGuessEncoding(), ex.getXmlEncoding(), true);
} catch (XmlStreamReaderException ex2) {
ex = ex2;
}
}
String encoding = ex.getXmlEncoding();
if (encoding == null) {
encoding = ex.getContentTypeEncoding();
}
if (encoding == null) {
encoding = (defaultEncoding == null) ? UTF_8 : defaultEncoding;
}
return encoding;
}
/**
* Calculate the raw encoding.
*
* @param bomEnc BOM encoding
* @param xmlGuessEnc XML Guess encoding
* @param xmlEnc XML encoding
* @return the raw encoding
* @throws IOException thrown if there is a problem reading the stream.
*/
String calculateRawEncoding(String bomEnc, String xmlGuessEnc,
String xmlEnc) throws IOException {
// BOM is Null
if (bomEnc == null) {
if (xmlGuessEnc == null || xmlEnc == null) {
return (defaultEncoding == null ? UTF_8 : defaultEncoding);
}
if (xmlEnc.equals(UTF_16) &&
(xmlGuessEnc.equals(UTF_16BE) || xmlGuessEnc.equals(UTF_16LE))) {
return xmlGuessEnc;
}
return xmlEnc;
}
// BOM is UTF-8
if (bomEnc.equals(UTF_8)) {
if (xmlGuessEnc != null && !xmlGuessEnc.equals(UTF_8)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
if (xmlEnc != null && !xmlEnc.equals(UTF_8)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
return bomEnc;
}
// BOM is UTF-16BE or UTF-16LE
if (bomEnc.equals(UTF_16BE) || bomEnc.equals(UTF_16LE)) {
if (xmlGuessEnc != null && !xmlGuessEnc.equals(bomEnc)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
if (xmlEnc != null && !xmlEnc.equals(UTF_16) && !xmlEnc.equals(bomEnc)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
return bomEnc;
}
// BOM is something else
String msg = MessageFormat.format(RAW_EX_2, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
/**
* Calculate the HTTP encoding.
*
* @param httpContentType The HTTP content type
* @param bomEnc BOM encoding
* @param xmlGuessEnc XML Guess encoding
* @param xmlEnc XML encoding
* @param lenient indicates if the charset encoding detection should be
* relaxed.
* @return the HTTP encoding
* @throws IOException thrown if there is a problem reading the stream.
*/
String calculateHttpEncoding(String httpContentType,
String bomEnc, String xmlGuessEnc, String xmlEnc,
boolean lenient) throws IOException {
// Lenient and has XML encoding
if (lenient && xmlEnc != null) {
return xmlEnc;
}
// Determine mime/encoding content types from HTTP Content Type
String cTMime = getContentTypeMime(httpContentType);
String cTEnc = getContentTypeEncoding(httpContentType);
boolean appXml = isAppXml(cTMime);
boolean textXml = isTextXml(cTMime);
// Mime type NOT "application/xml" or "text/xml"
if (!appXml && !textXml) {
String msg = MessageFormat.format(HTTP_EX_3, cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc);
}
// No content type encoding
if (cTEnc == null) {
if (appXml) {
return calculateRawEncoding(bomEnc, xmlGuessEnc, xmlEnc);
} else {
return (defaultEncoding == null) ? US_ASCII : defaultEncoding;
}
}
// UTF-16BE or UTF-16LE content type encoding
if (cTEnc.equals(UTF_16BE) || cTEnc.equals(UTF_16LE)) {
if (bomEnc != null) {
String msg = MessageFormat.format(HTTP_EX_1, cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc);
}
return cTEnc;
}
// UTF-16 content type encoding
if (cTEnc.equals(UTF_16)) {
if (bomEnc != null && bomEnc.startsWith(UTF_16)) {
return bomEnc;
}
String msg = MessageFormat.format(HTTP_EX_2, cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, cTMime, cTEnc, bomEnc, xmlGuessEnc, xmlEnc);
}
return cTEnc;
}
/**
* Returns MIME type or NULL if httpContentType is NULL.
*
* @param httpContentType the HTTP content type
* @return The mime content type
*/
static String getContentTypeMime(String httpContentType) {
String mime = null;
if (httpContentType != null) {
int i = httpContentType.indexOf(";");
if (i >= 0) {
mime = httpContentType.substring(0, i);
} else {
mime = httpContentType;
}
mime = mime.trim();
}
return mime;
}
private static final Pattern CHARSET_PATTERN = Pattern
.compile("charset=[\"']?([.[^; \"']]*)[\"']?");
/**
* Returns charset parameter value, NULL if not present, NULL if
* httpContentType is NULL.
*
* @param httpContentType the HTTP content type
* @return The content type encoding
*/
static String getContentTypeEncoding(String httpContentType) {
String encoding = null;
if (httpContentType != null) {
int i = httpContentType.indexOf(";");
if (i > -1) {
String postMime = httpContentType.substring(i + 1);
Matcher m = CHARSET_PATTERN.matcher(postMime);
encoding = (m.find()) ? m.group(1) : null;
encoding = (encoding != null) ? encoding.toUpperCase() : null;
}
}
return encoding;
}
public static final Pattern ENCODING_PATTERN = Pattern.compile(
"<\\?xml.*encoding[\\s]*=[\\s]*((?:\".[^\"]*\")|(?:'.[^']*'))",
Pattern.MULTILINE);
/**
* Returns the encoding declared in the <?xml encoding=...?>, NULL if none.
*
* @param is InputStream to create the reader from.
* @param guessedEnc guessed encoding
* @return the encoding declared in the <?xml encoding=...?>
* @throws IOException thrown if there is a problem reading the stream.
*/
private static String getXmlProlog(InputStream is, String guessedEnc)
throws IOException {
String encoding = null;
if (guessedEnc != null) {
byte[] bytes = new byte[BUFFER_SIZE];
is.mark(BUFFER_SIZE);
int offset = 0;
int max = BUFFER_SIZE;
int c = is.read(bytes, offset, max);
int firstGT = -1;
String xmlProlog = null;
while (c != -1 && firstGT == -1 && offset < BUFFER_SIZE) {
offset += c;
max -= c;
c = is.read(bytes, offset, max);
xmlProlog = new String(bytes, 0, offset, guessedEnc);
firstGT = xmlProlog.indexOf('>');
}
if (firstGT == -1) {
if (c == -1) {
throw new IOException("Unexpected end of XML stream");
} else {
throw new IOException(
"XML prolog or ROOT element not found on first "
+ offset + " bytes");
}
}
int bytesRead = offset;
if (bytesRead > 0) {
is.reset();
BufferedReader bReader = new BufferedReader(new StringReader(
xmlProlog.substring(0, firstGT + 1)));
StringBuilder prolog = new StringBuilder();
String line = bReader.readLine();
while (line != null) {
prolog.append(line);
line = bReader.readLine();
}
Matcher m = ENCODING_PATTERN.matcher(prolog);
if (m.find()) {
encoding = m.group(1).toUpperCase();
encoding = encoding.substring(1, encoding.length() - 1);
}
}
}
return encoding;
}
/**
* Indicates if the MIME type belongs to the APPLICATION XML family.
*
* @param mime The mime type
* @return true if the mime type belongs to the APPLICATION XML family,
* otherwise false
*/
static boolean isAppXml(String mime) {
return mime != null &&
(mime.equals("application/xml") ||
mime.equals("application/xml-dtd") ||
mime.equals("application/xml-external-parsed-entity") ||
(mime.startsWith("application/") && mime.endsWith("+xml")));
}
/**
* Indicates if the MIME type belongs to the TEXT XML family.
*
* @param mime The mime type
* @return true if the mime type belongs to the TEXT XML family,
* otherwise false
*/
static boolean isTextXml(String mime) {
return mime != null &&
(mime.equals("text/xml") ||
mime.equals("text/xml-external-parsed-entity") ||
(mime.startsWith("text/") && mime.endsWith("+xml")));
}
private static final String RAW_EX_1 =
"Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch";
private static final String RAW_EX_2 =
"Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM";
private static final String HTTP_EX_1 =
"Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL";
private static final String HTTP_EX_2 =
"Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch";
private static final String HTTP_EX_3 =
"Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Invalid MIME";
}

@ -0,0 +1,138 @@
package me.ag2s.epublib.util.commons.io;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
/**
* The XmlStreamReaderException is thrown by the XmlStreamReader constructors if
* the charset encoding can not be determined according to the XML 1.0
* specification and RFC 3023.
* <p>
* The exception returns the unconsumed InputStream to allow the application to
* do an alternate processing with the stream. Note that the original
* InputStream given to the XmlStreamReader cannot be used as that one has been
* already read.
*
* @author Alejandro Abdelnur
* @version $Id: XmlStreamReaderException.java 1004112 2010-10-04 04:48:25Z niallp $
* @since Commons IO 2.0
*/
public class XmlStreamReaderException extends IOException {
private static final long serialVersionUID = 1L;
private final String bomEncoding;
private final String xmlGuessEncoding;
private final String xmlEncoding;
private final String contentTypeMime;
private final String contentTypeEncoding;
/**
* Creates an exception instance if the charset encoding could not be
* determined.
* <p>
* Instances of this exception are thrown by the XmlStreamReader.
*
* @param msg message describing the reason for the exception.
* @param bomEnc BOM encoding.
* @param xmlGuessEnc XML guess encoding.
* @param xmlEnc XML prolog encoding.
*/
public XmlStreamReaderException(String msg, String bomEnc,
String xmlGuessEnc, String xmlEnc) {
this(msg, null, null, bomEnc, xmlGuessEnc, xmlEnc);
}
/**
* Creates an exception instance if the charset encoding could not be
* determined.
* <p>
* Instances of this exception are thrown by the XmlStreamReader.
*
* @param msg message describing the reason for the exception.
* @param ctMime MIME type in the content-type.
* @param ctEnc encoding in the content-type.
* @param bomEnc BOM encoding.
* @param xmlGuessEnc XML guess encoding.
* @param xmlEnc XML prolog encoding.
*/
public XmlStreamReaderException(String msg, String ctMime, String ctEnc,
String bomEnc, String xmlGuessEnc, String xmlEnc) {
super(msg);
contentTypeMime = ctMime;
contentTypeEncoding = ctEnc;
bomEncoding = bomEnc;
xmlGuessEncoding = xmlGuessEnc;
xmlEncoding = xmlEnc;
}
/**
* Returns the BOM encoding found in the InputStream.
*
* @return the BOM encoding, null if none.
*/
public String getBomEncoding() {
return bomEncoding;
}
/**
* Returns the encoding guess based on the first bytes of the InputStream.
*
* @return the encoding guess, null if it couldn't be guessed.
*/
public String getXmlGuessEncoding() {
return xmlGuessEncoding;
}
/**
* Returns the encoding found in the XML prolog of the InputStream.
*
* @return the encoding of the XML prolog, null if none.
*/
public String getXmlEncoding() {
return xmlEncoding;
}
/**
* Returns the MIME type in the content-type used to attempt determining the
* encoding.
*
* @return the MIME type in the content-type, null if there was not
* content-type or the encoding detection did not involve HTTP.
*/
public String getContentTypeMime() {
return contentTypeMime;
}
/**
* Returns the encoding in the content-type used to attempt determining the
* encoding.
*
* @return the encoding in the content-type, null if there was not
* content-type, no encoding in it or the encoding detection did not
* involve HTTP.
*/
public String getContentTypeEncoding() {
return contentTypeEncoding;
}
}

@ -0,0 +1,390 @@
<!--
Title:
The Package Document Type Definition (DTD) for the Open
eBook Publication Structure Version 1.2
Version:
1.2
Revision:
20020930-x (supercedes 20020605-x)
Revision History Note:
This revision, 20020930-x, which supercedes the prior revision
20020605-x, solely updates the email addresses within this
comment prologue. No changes whatsover were made to the parsed
content of this DTD.
Previous Released Version:
1.0.1 (Revision of 01-February-2001, "Document Type
Definition for the Open eBook package version
1.0.1")
Authors:
Version 1.0; 1.0.1
Steve DeRose <sjd@stg.brown.edu>
Gunter Hille <hille@abc.de>
Ben Trafford <ben@legendary.org>
Garret Wilson <garret@globalmentor.com>
This Version 1.2 updated and edited by:
Jon Noring <jon@noring.name>
Benjamin Jung <benjamin.jung@deepx.com>
Usage:
<?xml version="1.0"?>
<!DOCTYPE package
PUBLIC "+//ISBN 0-9673008-1-9//DTD OEB 1.2 Package//EN"
"http://openebook.org/dtds/oeb-1.2/oebpkg12.dtd">
<package unique-identifier="foo">
metadata
manifest
spine
tours
guide
</package>
Summary Description:
This is the Package Document Type Definition (DTD) for
the Open eBook Publication Structure version 1.2.
Changes to this DTD from version 1.0.1 include:
a. Upgrading the <dc-metadata> content model to
fully conform with the OEBPS 1.2 specification
requirements. Specifically, <dc:Language> is
now required, while in OEBPS 1.0.1 it was
optional.
b. Updating the mnemonic character entity
declaration to refer to version 1.2.
c. Updating the xmlns:dc namespace to refer to
version 1.1 of the Dublin Core Metadata
Initiative.
d. Editing and updating the various non-parsed
comments.
e. Revising the layout (e.g., white space
alteration) to aid in readability.
-->
<!-- *************************************************** -->
<!-- XHTML MNEMONIC CHARACTER ENTITIES ................. -->
<!ENTITY % OEBEntities
PUBLIC "+//ISBN 0-9673008-1-9//DTD OEB 1.2 Entities//EN"
"http://openebook.org/dtds/oeb-1.2/oeb12.ent">
%OEBEntities;
<!-- *************************************************** -->
<!-- DATATYPE ENTITIES ................................. -->
<!-- Uniform Resource Identifier (URI), per [RFC2396] -->
<!ENTITY % URI "CDATA">
<!-- Language code, per [RFC3066] -->
<!ENTITY % LanguageCode "NMTOKEN">
<!-- *************************************************** -->
<!-- NAMESPACE ENTITIES ................................ -->
<!ENTITY % dc.xmlns
"'http://purl.org/dc/elements/1.1/'">
<!ENTITY % oebpk.xmlns
"'http://openebook.org/namespaces/oeb-package/1.0/'">
<!-- *************************************************** -->
<!-- ELEMENT ENTITIES .................................. -->
<!-- The entity 'DCMetadataOpt' includes the 12 optional
<dc:Xxx> children elements of <dc-metadata>. It will
be used in the <dc-metadata> content model. -->
<!ENTITY % DCMetadataOpt
"dc:Contributor |
dc:Coverage |
dc:Creator |
dc:Date |
dc:Description |
dc:Format |
dc:Publisher |
dc:Relation |
dc:Rights |
dc:Source |
dc:Subject |
dc:Type ">
<!-- *************************************************** -->
<!-- ATTRIBUTE ENTITIES ................................ -->
<!ENTITY % CoreAttributes
"id ID #IMPLIED">
<!ENTITY % InternationalAttributes
"xml:lang %LanguageCode; #IMPLIED">
<!ENTITY % CommonAttributes
"%CoreAttributes;
%InternationalAttributes;">
<!-- 'DCNamespaceAttribute' is an attribute entity declaring
the Dublin Core namespace. Used on each <dc:Xxx> element
to accommodate XML parsers which unnecessarily require
this. -->
<!ENTITY % DCNamespaceAttribute
"xmlns:dc %URI; #FIXED %dc.xmlns;">
<!-- *************************************************** -->
<!-- ELEMENTS AND ATTRIBUTES ........................... -->
<!-- <package> must have as children elements, in this order:
<metadata>, <manifest>, and <spine>, and optionally may
include <tours> and/or <guide>. The 'unique-identifier'
attribute is required for <package> (see comment for
<dc:Identifier>.) -->
<!ELEMENT package (metadata, manifest, spine, tours?, guide?)>
<!ATTLIST package
%CommonAttributes;
unique-identifier IDREF #REQUIRED
xmlns %URI; #FIXED %oebpk.xmlns;>
<!-- <metadata> must contain one <dc-metadata>, and
optionally contain one <x-metadata>. There are no
attributes for <metadata>. -->
<!ELEMENT metadata (dc-metadata, x-metadata?)>
<!-- <dc-metadata> must contain at least one <dc:Title>,
one <dc:Identifier>, and one <dc:Language>, and may
contain one or more of each of the other twelve
optional <dc:XXX> elements, all in any order. -->
<!ELEMENT dc-metadata
( (%DCMetadataOpt;)*,
( (dc:Title, (%DCMetadataOpt; | dc:Title)*,
( (dc:Identifier, (%DCMetadataOpt; | dc:Title | dc:Identifier)*,
dc:Language) |
(dc:Language, (%DCMetadataOpt; | dc:Title | dc:Language)*,
dc:Identifier) ) ) |
(dc:Identifier, (%DCMetadataOpt; | dc:Identifier)*,
( (dc:Title, (%DCMetadataOpt; | dc:Identifier | dc:Title)*,
dc:Language) |
(dc:Language, (%DCMetadataOpt; | dc:Identifier | dc:Language)*,
dc:Title) ) ) |
(dc:Language, (%DCMetadataOpt; | dc:Language)*,
( (dc:Identifier, (%DCMetadataOpt; | dc:Language | dc:Identifier)*,
dc:Title) |
(dc:Title, (%DCMetadataOpt; | dc:Language | dc:Title)*,
dc:Identifier) ) ) ),
(%DCMetadataOpt; | dc:Title | dc:Identifier | dc:Language)* )>
<!ATTLIST dc-metadata
%CommonAttributes;
%DCNamespaceAttribute;
xmlns:oebpackage %URI; #FIXED %oebpk.xmlns;>
<!-- Required elements for <dc-metadata>. -->
<!ELEMENT dc:Title (#PCDATA)>
<!ATTLIST dc:Title
%CommonAttributes;
%DCNamespaceAttribute;>
<!-- One <dc:Identifier> must specify an 'id' identical to
the value of the required <package> 'unique-identifier'
attribute. -->
<!ELEMENT dc:Identifier (#PCDATA)>
<!ATTLIST dc:Identifier
%CommonAttributes;
%DCNamespaceAttribute;
scheme NMTOKEN #IMPLIED>
<!ELEMENT dc:Language (#PCDATA)>
<!ATTLIST dc:Language
%CommonAttributes;
%DCNamespaceAttribute;>
<!-- Optional elements for <dc-metadata>. -->
<!ELEMENT dc:Contributor (#PCDATA)>
<!ATTLIST dc:Contributor
%CommonAttributes;
%DCNamespaceAttribute;
file-as CDATA #IMPLIED
role NMTOKEN #IMPLIED>
<!ELEMENT dc:Coverage (#PCDATA)>
<!ATTLIST dc:Coverage
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Creator (#PCDATA)>
<!ATTLIST dc:Creator
%CommonAttributes;
%DCNamespaceAttribute;
file-as CDATA #IMPLIED
role NMTOKEN #IMPLIED>
<!ELEMENT dc:Date (#PCDATA)>
<!ATTLIST dc:Date
%CommonAttributes;
%DCNamespaceAttribute;
event NMTOKEN #IMPLIED>
<!ELEMENT dc:Description (#PCDATA)>
<!ATTLIST dc:Description
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Format (#PCDATA)>
<!ATTLIST dc:Format
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Publisher (#PCDATA)>
<!ATTLIST dc:Publisher
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Relation (#PCDATA)>
<!ATTLIST dc:Relation
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Rights (#PCDATA)>
<!ATTLIST dc:Rights
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Source (#PCDATA)>
<!ATTLIST dc:Source
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Subject (#PCDATA)>
<!ATTLIST dc:Subject
%CommonAttributes;
%DCNamespaceAttribute;>
<!ELEMENT dc:Type (#PCDATA)>
<!ATTLIST dc:Type
%CommonAttributes;
%DCNamespaceAttribute;>
<!-- <x-metadata> must contain at least one <meta>. -->
<!ELEMENT x-metadata (meta+)>
<!ATTLIST x-metadata %CommonAttributes;>
<!-- Note that 'content' and 'name' are required attributes
for <meta>. -->
<!ELEMENT meta EMPTY>
<!ATTLIST meta
%CommonAttributes;
content CDATA #REQUIRED
name NMTOKEN #REQUIRED
scheme CDATA #IMPLIED>
<!-- <manifest> must contain at least one <item>. -->
<!ELEMENT manifest (item+)>
<!ATTLIST manifest %CommonAttributes;>
<!-- Note that 'href', 'id' and 'media-type' are required
attributes for <item>. -->
<!ELEMENT item EMPTY>
<!ATTLIST item
%InternationalAttributes;
fallback IDREF #IMPLIED
href %URI; #REQUIRED
id ID #REQUIRED
media-type CDATA #REQUIRED>
<!-- <spine> must contain at least one <itemref>. -->
<!ELEMENT spine (itemref+)>
<!ATTLIST spine %CommonAttributes;>
<!-- Note that 'idref' is a required attribute for
<itemref>. -->
<!ELEMENT itemref EMPTY>
<!ATTLIST itemref
%CommonAttributes;
idref IDREF #REQUIRED>
<!-- <tours> must contain at least one <tour>. -->
<!ELEMENT tours (tour+)>
<!ATTLIST tours %CommonAttributes;>
<!-- <tour> must contain at least one <site>. Note that
'title' is a required attribute for <tour>. -->
<!ELEMENT tour (site+)>
<!ATTLIST tour
%CommonAttributes;
title CDATA #REQUIRED>
<!-- Note that 'href' and 'title' are required attributes
for <site>. -->
<!ELEMENT site EMPTY>
<!ATTLIST site
%CommonAttributes;
href %URI; #REQUIRED
title CDATA #REQUIRED>
<!-- <guide> must contain at least one <reference>. -->
<!ELEMENT guide (reference+)>
<!ATTLIST guide %CommonAttributes;>
<!-- Note that 'href', 'title' and 'type' are required
attributes for <reference>. -->
<!ELEMENT reference EMPTY>
<!ATTLIST reference
%CommonAttributes;
href %URI; #REQUIRED
title CDATA #REQUIRED
type NMTOKEN #REQUIRED>

@ -0,0 +1,269 @@
<!-- NCX 2005-1 DTD 2005-06-26
file: ncx-2005-1.dtd
Authors: Mark Hakkinen, George Kerscher, Tom McLaughlin, James Pritchett, and Michael Moodie
Change list:
2002-02-12 M. Moodie. Changed content model of navLabel element to eliminate ambiguity.
2002-02-27 M. Moodie. Grammatical changes suggested by editor.
2004-03-31 J. Pritchett. Various changes per the 2004 change list:
- Changed internal version numbers from 1.1.0 to 1.2.0
- Made audio clipBegin/clipEnd mandatory (change #10)
- Dropped value attribute from navPoint (change #11)
- Replaced lang attribute with xml:lang (change #12)
- Added <pageList> and <pageTarget> elements (change #48)
- Dropped onFocus and onBlur attributes from navPoint and navTarget (change #49)
- Added <img> to content models of docTitle and docAuthor (change #50)
- Removed reference to pages in description of navList (change #52)
- Added <navInfo> element (change #53)
- Added default namespace attribute to description of <ncx> (change #L8)
- Removed pageRef and mapRef attributes
2004-04-05 J. Pritchett. Changes after feedback from MM and MG to 2004-03-31 version
- Changed internal version numbers from 1.2.0 to 1.1.2 (per MM e-mail of 3/31)
- Changed system identifier to use z3986/2004 as path instead of z3986/v100 (per 3/31 con call)
- Added class attribute to both pageTarget and pageList (per MG e-mail of 4/1)
- Added comment text describing value attribute for pageTarget and navTarget (per MM e-mail of 3/31)
- Changed declaration of type attribute on pageTarget to enumerate allowed values
- Added playOrder attribute to navPoint, navTarget, and pageTarget (per Lloyd's proposal)
2004-04-05 T. McLaughlin. In description of smilCustomTest, added id and defaultState are to be copied.
Version update to 1.1.3.
2004-05-14 T. McLaughlin. Reinstated override attribute to be copied also. Added bookStruct attribute
and enum list to smilCustomTest. Update to 1.1.4.
Revised, 4/5/2004: Changed version to 1.1.2
Revised, 4/5/2004: Changed system identifier to use '2004' path
Revised, 4/5/2004: TM, Changed version to 1.1.3
Revised, 5/14/2004: TM, Changed version to 1.1.4
2004-07-07 M. Moodie Updated version to 1.2.0 everywhere but at top, where version was set to 1.1.5.
2004-09-15 M. Moodie. Changed uri to URI throughout. Set version to 1.1.6.
2004-09-16 M. Moodie. Changed version to 1.2.0
2005-06-26 M. Gylling. Changed pid, sid, ns uri, and filename for Z3986-2005
Description:
NCX (Navigation Control for XML applications) is a generalized navigation definition DTD for application
to Digital Talking Books, eBooks, and general web content models.
This DTD is an XML application that layers navigation functionality on top of SMIL 2.0 content.
The NCX defines a navigation path/model that may be applied upon existing publications,
without modification of the existing publication source, so long as the navigation targets within
the source publication can be directly referenced via a URI.
The following identifiers apply to this DTD:
"-//NISO//DTD ncx 2005-1//EN"
"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd"
-->
<!-- Basic Entities -->
<!ENTITY % i18n
"xml:lang NMTOKEN #IMPLIED
dir (ltr|rtl) #IMPLIED" >
<!ENTITY % SMILtimeVal "CDATA" >
<!ENTITY % URI "CDATA" >
<!ENTITY % script "CDATA" >
<!-- ELEMENTS -->
<!-- Top Level NCX Container. -->
<!-- Revised, 3/31/2004: Added pageList to content model -->
<!ELEMENT ncx (head, docTitle, docAuthor*, navMap, pageList?, navList*)>
<!-- Revised, 4/5/2004: Changed version to 1.1.2 -->
<!-- Revised 3/29/2004: Added xmlns -->
<!-- Revised, 4/5/2004: TM, Changed version to 1.1.3 -->
<!-- Revised, 5/14/2004: TM, Changed version to 1.1.4 -->
<!-- Revised, 7/7/2004: MM, Changed version to 1.2.0 -->
<!ATTLIST ncx
version CDATA #FIXED "2005-1"
xmlns %URI; #FIXED "http://www.daisy.org/z3986/2005/ncx/"
%i18n;
>
<!-- Document Head - Contains all NCX metadata.
-->
<!ELEMENT head (smilCustomTest | meta)+>
<!-- 2004-04-05 TM - only id and defaultState are copied -->
<!-- 2004-05-14 TM - revert to override copied too; added bookStruct attribute -->
<!-- smilCustomTest - Duplicates customTest data found in SMIL files. Each unique customTest
element that appears in one or more SMIL files must have its id, defaultState and override
attributes duplicated in a smilCustomTest element in the NCX. The NCX thus gathers in one
place all customTest elements used in the SMIL files, for presentation to the user.
-->
<!ELEMENT smilCustomTest EMPTY>
<!ATTLIST smilCustomTest
id ID #REQUIRED
defaultState (true|false) 'false'
override (visible|hidden) 'hidden'
bookStruct (PAGE_NUMBER|NOTE|NOTE_REFERENCE|ANNOTATION|LINE_NUMBER|OPTIONAL_SIDEBAR|OPTIONAL_PRODUCER_NOTE) #IMPLIED
>
<!-- Meta Element - metadata about this NCX -->
<!ELEMENT meta EMPTY>
<!ATTLIST meta
name CDATA #REQUIRED
content CDATA #REQUIRED
scheme CDATA #IMPLIED
>
<!-- DocTitle - the title of the document, required and must immediately follow head.
-->
<!-- Revised, 3/31/2004: Added img to content model -->
<!ELEMENT docTitle (text, audio?, img?)>
<!ATTLIST docTitle
id ID #IMPLIED
%i18n;
>
<!-- DocAuthor - the author of the document, immediately follows docTitle.
-->
<!-- Revised, 3/31/2004: Added img to content model -->
<!ELEMENT docAuthor (text, audio?, img?)>
<!ATTLIST docAuthor
id ID #IMPLIED
%i18n;
>
<!-- Navigation Structure - container for all of the NCX objects that are part of the
hierarchical structure of the document.
-->
<!-- Revised, 3/31/2004: Added navInfo to content model -->
<!ELEMENT navMap (navInfo*, navLabel*, navPoint+)>
<!ATTLIST navMap
id ID #IMPLIED
>
<!-- Navigation Point - contains description(s) of target, as well as a pointer to
entire content of target.
Hierarchy is represented by nesting navPoints. "class" attribute describes the kind
of structural unit this object represents (e.g., "chapter", "section").
-->
<!ELEMENT navPoint (navLabel+, content, navPoint*)>
<!-- Revised, 3/29/2004: Removed onFocus/onBlur -->
<!-- Revised, 3/29/2004: Removed value -->
<!-- Revised, 3/31/2004: Removed pageRef -->
<!-- Revised, 4/5/2004: Added playOrder -->
<!ATTLIST navPoint
id ID #REQUIRED
class CDATA #IMPLIED
playOrder CDATA #REQUIRED
>
<!-- Revised, 3/31/2004: Added pageList element -->
<!-- Page List - Container for pagination information.
-->
<!ELEMENT pageList (navInfo*, navLabel*, pageTarget+)>
<!-- Revised, 4/5/2004: Added class attribute -->
<!ATTLIST pageList
id ID #IMPLIED
class CDATA #IMPLIED
>
<!-- Revised, 3/31/2004: Added pageTarget element -->
<!-- Revised, 4/5/2004: Added description of value attribute to comment -->
<!-- Page Target - Container for
text, audio, image, and content elements containing navigational
information for pages. The "value" attribute is a positive integer representing
the numeric value associated with a page. Combination of values of type and
value attributes must be unique, when value attribute is present.
-->
<!ELEMENT pageTarget (navLabel+, content)>
<!-- Revised, 4/5/2004: Added class attribute -->
<!-- Revised, 4/5/2004: Changed declaration of type attribute to enumerate values -->
<!-- Revised, 4/5/2004: Added playOrder -->
<!ATTLIST pageTarget
id ID #IMPLIED
value CDATA #IMPLIED
type (front | normal | special) #REQUIRED
class CDATA #IMPLIED
playOrder CDATA #REQUIRED
>
<!-- Navigation List - container for distinct, flat sets of navigable elements, e.g.
notes, figures, tables, etc. Essentially a flat version of navMap. The "class" attribute
describes the type of object contained in this navList, using dtbook element names, e.g., note.
-->
<!-- Revised, 3/31/2004: Added navInfo to content model -->
<!ELEMENT navList (navInfo*, navLabel+, navTarget+) >
<!ATTLIST navList
id ID #IMPLIED
class CDATA #IMPLIED
>
<!-- Revised, 4/5/2004: Added description of value attribute to comment -->
<!-- Navigation Target - contains description(s) of target, as well as a pointer to
entire content of target.
navTargets are the equivalent of navPoints for use in navLists. "class" attribute
describes the kind of structure this target represents, using its dtbook element
name, e.g., note. The "value" attribute is a positive integer representing the
numeric value associated with the navTarget.
-->
<!ELEMENT navTarget (navLabel+, content) >
<!-- Revised, 3/29/2004: Removed onFocus/onBlur -->
<!-- Revised, 3/31/2004: Removed mapRef -->
<!-- Revised, 4/5/2004: Added playOrder -->
<!ATTLIST navTarget
id ID #REQUIRED
class CDATA #IMPLIED
value CDATA #IMPLIED
playOrder CDATA #REQUIRED
>
<!-- Revised, 3/31/2004: Added navInfo element -->
<!-- Navigation Information - Contains an informative comment
about a navMap, pageList, or navList in various media for presentation to the user.
-->
<!ELEMENT navInfo (((text, audio?) | audio), img?)>
<!ATTLIST navInfo
%i18n;
>
<!-- Navigation Label - Contains a description of a given <navMap>, <navPoint>,
<navList>, or <navTarget> in various media for presentation to the user. Can be
repeated so descriptions can be provided in multiple languages. -->
<!ELEMENT navLabel (((text, audio?) | audio), img?)>
<!ATTLIST navLabel
%i18n;
>
<!-- Content Element - pointer into SMIL to beginning of navPoint. -->
<!ELEMENT content EMPTY>
<!ATTLIST content
id ID #IMPLIED
src %URI; #REQUIRED
>
<!-- Text Element - Contains text of docTitle, navPoint heading, navTarget (e.g., page number),
or label for navMap or navList. -->
<!ELEMENT text (#PCDATA)>
<!ATTLIST text
id ID #IMPLIED
class CDATA #IMPLIED
>
<!-- Audio Element - audio clip of navPoint heading. -->
<!ELEMENT audio EMPTY>
<!-- Revised, 3/29/2004: clipBegin/clipEnd now REQUIRED -->
<!ATTLIST audio
id ID #IMPLIED
class CDATA #IMPLIED
src %URI; #REQUIRED
clipBegin %SMILtimeVal; #REQUIRED
clipEnd %SMILtimeVal; #REQUIRED
>
<!-- Image Element - image that may accompany heading. -->
<!ELEMENT img EMPTY>
<!ATTLIST img
id ID #IMPLIED
class CDATA #IMPLIED
src %URI; #REQUIRED
>

@ -0,0 +1,242 @@
<!-- ...................................................................... -->
<!-- XHTML Ruby Module .................................................... -->
<!-- file: xhtml-ruby-1.mod
This is XHTML, a reformulation of HTML as a modular XML application.
Copyright 1999-2001 W3C (MIT, INRIA, Keio), All Rights Reserved.
Revision: $Id: xhtml-ruby-1.mod,v 4.0 2001/04/03 23:14:33 altheim Exp $
This module is based on the W3C Ruby Annotation Specification:
http://www.w3.org/TR/ruby
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//W3C//ELEMENTS XHTML Ruby 1.0//EN"
SYSTEM "http://www.w3.org/TR/ruby/xhtml-ruby-1.mod"
...................................................................... -->
<!-- Ruby Elements
ruby, rbc, rtc, rb, rt, rp
This module declares the elements and their attributes used to
support ruby annotation markup.
-->
<!-- declare qualified element type names:
-->
<!ENTITY % ruby.qname "ruby" >
<!ENTITY % rbc.qname "rbc" >
<!ENTITY % rtc.qname "rtc" >
<!ENTITY % rb.qname "rb" >
<!ENTITY % rt.qname "rt" >
<!ENTITY % rp.qname "rp" >
<!-- rp fallback is included by default.
-->
<!ENTITY % Ruby.fallback "INCLUDE" >
<!ENTITY % Ruby.fallback.mandatory "IGNORE" >
<!-- Complex ruby is included by default; it may be
overridden by other modules to ignore it.
-->
<!ENTITY % Ruby.complex "INCLUDE" >
<!-- Fragments for the content model of the ruby element -->
<![%Ruby.fallback;[
<![%Ruby.fallback.mandatory;[
<!ENTITY % Ruby.content.simple
"( %rb.qname;, %rp.qname;, %rt.qname;, %rp.qname; )"
>
]]>
<!ENTITY % Ruby.content.simple
"( %rb.qname;, ( %rt.qname; | ( %rp.qname;, %rt.qname;, %rp.qname; ) ) )"
>
]]>
<!ENTITY % Ruby.content.simple "( %rb.qname;, %rt.qname; )" >
<![%Ruby.complex;[
<!ENTITY % Ruby.content.complex
"| ( %rbc.qname;, %rtc.qname;, %rtc.qname;? )"
>
]]>
<!ENTITY % Ruby.content.complex "" >
<!-- Content models of the rb and the rt elements are intended to
allow other inline-level elements of its parent markup language,
but it should not include ruby descendent elements. The following
parameter entity %NoRuby.content; can be used to redefine
those content models with minimum effort. It's defined as
'( #PCDATA )' by default.
-->
<!ENTITY % NoRuby.content "( #PCDATA )" >
<!-- one or more digits (NUMBER) -->
<!ENTITY % Number.datatype "CDATA" >
<!-- ruby element ...................................... -->
<!ENTITY % ruby.element "INCLUDE" >
<![%ruby.element;[
<!ENTITY % ruby.content
"( %Ruby.content.simple; %Ruby.content.complex; )"
>
<!ELEMENT %ruby.qname; %ruby.content; >
<!-- end of ruby.element -->]]>
<![%Ruby.complex;[
<!-- rbc (ruby base component) element ................. -->
<!ENTITY % rbc.element "INCLUDE" >
<![%rbc.element;[
<!ENTITY % rbc.content
"(%rb.qname;)+"
>
<!ELEMENT %rbc.qname; %rbc.content; >
<!-- end of rbc.element -->]]>
<!-- rtc (ruby text component) element ................. -->
<!ENTITY % rtc.element "INCLUDE" >
<![%rtc.element;[
<!ENTITY % rtc.content
"(%rt.qname;)+"
>
<!ELEMENT %rtc.qname; %rtc.content; >
<!-- end of rtc.element -->]]>
]]>
<!-- rb (ruby base) element ............................ -->
<!ENTITY % rb.element "INCLUDE" >
<![%rb.element;[
<!-- %rb.content; uses %NoRuby.content; as its content model,
which is '( #PCDATA )' by default. It may be overridden
by other modules to allow other inline-level elements
of its parent markup language, but it should not include
ruby descendent elements.
-->
<!ENTITY % rb.content "%NoRuby.content;" >
<!ELEMENT %rb.qname; %rb.content; >
<!-- end of rb.element -->]]>
<!-- rt (ruby text) element ............................ -->
<!ENTITY % rt.element "INCLUDE" >
<![%rt.element;[
<!-- %rt.content; uses %NoRuby.content; as its content model,
which is '( #PCDATA )' by default. It may be overridden
by other modules to allow other inline-level elements
of its parent markup language, but it should not include
ruby descendent elements.
-->
<!ENTITY % rt.content "%NoRuby.content;" >
<!ELEMENT %rt.qname; %rt.content; >
<!-- end of rt.element -->]]>
<!-- rbspan attribute is used for complex ruby only ...... -->
<![%Ruby.complex;[
<!ENTITY % rt.attlist "INCLUDE" >
<![%rt.attlist;[
<!ATTLIST %rt.qname;
rbspan %Number.datatype; "1"
>
<!-- end of rt.attlist -->]]>
]]>
<!-- rp (ruby parenthesis) element ..................... -->
<![%Ruby.fallback;[
<!ENTITY % rp.element "INCLUDE" >
<![%rp.element;[
<!ENTITY % rp.content
"( #PCDATA )"
>
<!ELEMENT %rp.qname; %rp.content; >
<!-- end of rp.element -->]]>
]]>
<!-- Ruby Common Attributes
The following optional ATTLIST declarations provide an easy way
to define common attributes for ruby elements. These declarations
are ignored by default.
Ruby elements are intended to have common attributes of its
parent markup language. For example, if a markup language defines
common attributes as a parameter entity %attrs;, you may add
those attributes by just declaring the following parameter entities
<!ENTITY % Ruby.common.attlists "INCLUDE" >
<!ENTITY % Ruby.common.attrib "%attrs;" >
before including the Ruby module.
-->
<!ENTITY % Ruby.common.attlists "IGNORE" >
<![%Ruby.common.attlists;[
<!ENTITY % Ruby.common.attrib "" >
<!-- common attributes for ruby ........................ -->
<!ENTITY % Ruby.common.attlist "INCLUDE" >
<![%Ruby.common.attlist;[
<!ATTLIST %ruby.qname;
%Ruby.common.attrib;
>
<!-- end of Ruby.common.attlist -->]]>
<![%Ruby.complex;[
<!-- common attributes for rbc ......................... -->
<!ENTITY % Rbc.common.attlist "INCLUDE" >
<![%Rbc.common.attlist;[
<!ATTLIST %rbc.qname;
%Ruby.common.attrib;
>
<!-- end of Rbc.common.attlist -->]]>
<!-- common attributes for rtc ......................... -->
<!ENTITY % Rtc.common.attlist "INCLUDE" >
<![%Rtc.common.attlist;[
<!ATTLIST %rtc.qname;
%Ruby.common.attrib;
>
<!-- end of Rtc.common.attlist -->]]>
]]>
<!-- common attributes for rb .......................... -->
<!ENTITY % Rb.common.attlist "INCLUDE" >
<![%Rb.common.attlist;[
<!ATTLIST %rb.qname;
%Ruby.common.attrib;
>
<!-- end of Rb.common.attlist -->]]>
<!-- common attributes for rt .......................... -->
<!ENTITY % Rt.common.attlist "INCLUDE" >
<![%Rt.common.attlist;[
<!ATTLIST %rt.qname;
%Ruby.common.attrib;
>
<!-- end of Rt.common.attlist -->]]>
<![%Ruby.fallback;[
<!-- common attributes for rp .......................... -->
<!ENTITY % Rp.common.attlist "INCLUDE" >
<![%Rp.common.attlist;[
<!ATTLIST %rp.qname;
%Ruby.common.attrib;
>
<!-- end of Rp.common.attlist -->]]>
]]>
]]>
<!-- end of xhtml-ruby-1.mod -->

@ -0,0 +1,51 @@
<!-- ...................................................................... -->
<!-- XHTML Base Architecture Module ...................................... -->
<!-- file: xhtml-arch-1.mod
This is XHTML, a reformulation of HTML as a modular XML application.
Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
Revision: $Id: xhtml-arch-1.mod,v 1.1 2010/07/29 13:42:46 bertails Exp $ SMI
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//W3C//ELEMENTS XHTML Base Architecture 1.0//EN"
SYSTEM "http://www.w3.org/MarkUp/DTD/xhtml-arch-1.mod"
Revisions:
(none)
....................................................................... -->
<!-- This optional module includes declarations that enable XHTML to be used
as a base architecture according to the 'Architectural Forms Definition
Requirements' (Annex A.3, ISO/IEC 10744, 2nd edition). For more information
on use of architectural forms, see the HyTime web site at:
http://www.hytime.org/
-->
<?IS10744 ArcBase xhtml ?>
<!NOTATION xhtml PUBLIC "-//W3C//NOTATION AFDR ARCBASE XHTML 1.1//EN" >
<!-- Entity declaration for associated Architectural DTD
-->
<!ENTITY xhtml-arch.dtd
PUBLIC "-//W3C//DTD XHTML Architecture 1.1//EN"
"xhtml11-arch.dtd" >
<?IS10744:arch xhtml
public-id = "-//W3C//NOTATION AFDR ARCBASE XHTML 1.1//EN"
dtd-public-id = "-//W3C//DTD XHTML 1.1//EN"
dtd-system-id = "xhtml11.dtd"
doc-elem-form = "html"
form-att = "html"
renamer-att = "htnames"
suppressor-att = "htsupp"
data-ignore-att = "htign"
auto = "ArcAuto"
options = "HtModReq HtModOpt"
HtModReq = "Framework Text Hypertext Lists Structure"
HtModOpt = "Standard"
?>
<!-- end of xhtml-arch-1.mod -->

@ -0,0 +1,142 @@
<!-- ...................................................................... -->
<!-- XHTML Common Attributes Module ...................................... -->
<!-- file: xhtml-attribs-1.mod
This is XHTML, a reformulation of HTML as a modular XML application.
Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
Revision: $Id: xhtml-attribs-1.mod,v 1.1 2010/07/29 13:42:46 bertails Exp $ SMI
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//W3C//ENTITIES XHTML Common Attributes 1.0//EN"
SYSTEM "http://www.w3.org/MarkUp/DTD/xhtml-attribs-1.mod"
Revisions:
(none)
....................................................................... -->
<!-- Common Attributes
This module declares many of the common attributes for the XHTML DTD.
%NS.decl.attrib; is declared in the XHTML Qname module.
Note that this file was extended in XHTML Modularization 1.1 to
include declarations of "global" versions of the attribute collections.
The global versions of the attributes are for use on elements in other
namespaces. The global version of "common" includes the xmlns declaration
for the prefixed version of the xhtml namespace. If you are only using a
specific attribute or an individual attribute collection, you must also
include the XHTML.xmlns.attrib.prefixed PE on your elements.
-->
<!ENTITY % id.attrib
"id ID #IMPLIED"
>
<![%XHTML.global.attrs.prefixed;[
<!ENTITY % XHTML.global.id.attrib
"%XHTML.prefix;:id ID #IMPLIED"
>
]]>
<!ENTITY % class.attrib
"class CDATA #IMPLIED"
>
<![%XHTML.global.attrs.prefixed;[
<!ENTITY % XHTML.global.class.attrib
"%XHTML.prefix;:class CDATA #IMPLIED"
>
]]>
<!ENTITY % title.attrib
"title %Text.datatype; #IMPLIED"
>
<![%XHTML.global.attrs.prefixed;[
<!ENTITY % XHTML.global.title.attrib
"%XHTML.prefix;:title %Text.datatype; #IMPLIED"
>
]]>
<!ENTITY % Core.extra.attrib "" >
<!ENTITY % Core.attrib
"%XHTML.xmlns.attrib;
%id.attrib;
%class.attrib;
%title.attrib;
xml:space ( preserve ) #FIXED 'preserve'
%Core.extra.attrib;"
>
<!ENTITY % XHTML.global.core.extra.attrib "" >
<![%XHTML.global.attrs.prefixed;[
<!ENTITY % XHTML.global.core.attrib
"%XHTML.global.id.attrib;
%XHTML.global.class.attrib;
%XHTML.global.title.attrib;
%XHTML.global.core.extra.attrib;"
>
]]>
<!ENTITY % XHTML.global.core.attrib "" >
<!ENTITY % lang.attrib
"xml:lang %LanguageCode.datatype; #IMPLIED"
>
<![%XHTML.bidi;[
<!ENTITY % dir.attrib
"dir ( ltr | rtl ) #IMPLIED"
>
<!ENTITY % I18n.attrib
"%dir.attrib;
%lang.attrib;"
>
<![%XHTML.global.attrs.prefixed;[
<!ENTITY XHTML.global.i18n.attrib
"%XHTML.prefix;:dir ( ltr | rtl ) #IMPLIED
%lang.attrib;"
>
]]>
<!ENTITY XHTML.global.i18n.attrib "" >
]]>
<!ENTITY % I18n.attrib
"%lang.attrib;"
>
<!ENTITY % XHTML.global.i18n.attrib
"%lang.attrib;"
>
<!ENTITY % Common.extra.attrib "" >
<!ENTITY % XHTML.global.common.extra.attrib "" >
<!-- intrinsic event attributes declared previously
-->
<!ENTITY % Events.attrib "" >
<!ENTITY % XHTML.global.events.attrib "" >
<!ENTITY % Common.attrib
"%Core.attrib;
%I18n.attrib;
%Events.attrib;
%Common.extra.attrib;"
>
<!ENTITY % XHTML.global.common.attrib
"%XHTML.xmlns.attrib.prefixed;
%XHTML.global.core.attrib;
%XHTML.global.i18n.attrib;
%XHTML.global.events.attrib;
%XHTML.global.common.extra.attrib;"
>
<!-- end of xhtml-attribs-1.mod -->

@ -0,0 +1,53 @@
<!-- ...................................................................... -->
<!-- XHTML Base Element Module ........................................... -->
<!-- file: xhtml-base-1.mod
This is XHTML, a reformulation of HTML as a modular XML application.
Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
Revision: $Id: xhtml-base-1.mod,v 1.1 2010/07/29 13:42:46 bertails Exp $ SMI
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//W3C//ELEMENTS XHTML Base Element 1.0//EN"
SYSTEM "http://www.w3.org/MarkUp/DTD/xhtml-base-1.mod"
Revisions:
(none)
....................................................................... -->
<!-- Base element
base
This module declares the base element type and its attributes,
used to define a base URI against which relative URIs in the
document will be resolved.
Note that this module also redeclares the content model for
the head element to include the base element.
-->
<!-- base: Document Base URI ........................... -->
<!ENTITY % base.element "INCLUDE" >
<![%base.element;[
<!ENTITY % base.content "EMPTY" >
<!ENTITY % base.qname "base" >
<!ELEMENT %base.qname; %base.content; >
<!-- end of base.element -->]]>
<!ENTITY % base.attlist "INCLUDE" >
<![%base.attlist;[
<!ATTLIST %base.qname;
%XHTML.xmlns.attrib;
href %URI.datatype; #REQUIRED
>
<!-- end of base.attlist -->]]>
<!ENTITY % head.content
"( %HeadOpts.mix;,
( ( %title.qname;, %HeadOpts.mix;, ( %base.qname;, %HeadOpts.mix; )? )
| ( %base.qname;, %HeadOpts.mix;, ( %title.qname;, %HeadOpts.mix; ))))"
>
<!-- end of xhtml-base-1.mod -->

@ -0,0 +1,47 @@
<!-- ...................................................................... -->
<!-- XHTML BDO Element Module ............................................. -->
<!-- file: xhtml-bdo-1.mod
This is XHTML, a reformulation of HTML as a modular XML application.
Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
Revision: $Id: xhtml-bdo-1.mod,v 1.1 2010/07/29 13:42:46 bertails Exp $ SMI
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//W3C//ELEMENTS XHTML BDO Element 1.0//EN"
SYSTEM "http://www.w3.org/MarkUp/DTD/xhtml-bdo-1.mod"
Revisions:
(none)
....................................................................... -->
<!-- Bidirectional Override (bdo) Element
This modules declares the element 'bdo', used to override the
Unicode bidirectional algorithm for selected fragments of text.
DEPENDENCIES:
Relies on the conditional section keyword %XHTML.bidi; declared
as "INCLUDE". Bidirectional text support includes both the bdo
element and the 'dir' attribute.
-->
<!ENTITY % bdo.element "INCLUDE" >
<![%bdo.element;[
<!ENTITY % bdo.content
"( #PCDATA | %Inline.mix; )*"
>
<!ENTITY % bdo.qname "bdo" >
<!ELEMENT %bdo.qname; %bdo.content; >
<!-- end of bdo.element -->]]>
<!ENTITY % bdo.attlist "INCLUDE" >
<![%bdo.attlist;[
<!ATTLIST %bdo.qname;
%Core.attrib;
%lang.attrib;
dir ( ltr | rtl ) #REQUIRED
>
]]>
<!-- end of xhtml-bdo-1.mod -->

@ -0,0 +1,164 @@
<!-- ...................................................................... -->
<!-- XHTML Block Phrasal Module .......................................... -->
<!-- file: xhtml-blkphras-1.mod
This is XHTML, a reformulation of HTML as a modular XML application.
Copyright 1998-2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
Revision: $Id: xhtml-blkphras-1.mod,v 1.1 2010/07/29 13:42:46 bertails Exp $ SMI
This DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//W3C//ELEMENTS XHTML Block Phrasal 1.0//EN"
SYSTEM "http://www.w3.org/MarkUp/DTD/xhtml-blkphras-1.mod"
Revisions:
(none)
....................................................................... -->
<!-- Block Phrasal
address, blockquote, pre, h1, h2, h3, h4, h5, h6
This module declares the elements and their attributes used to
support block-level phrasal markup.
-->
<!ENTITY % address.element "INCLUDE" >
<![%address.element;[
<!ENTITY % address.content
"( #PCDATA | %Inline.mix; )*" >
<!ENTITY % address.qname "address" >
<!ELEMENT %address.qname; %address.content; >
<!-- end of address.element -->]]>
<!ENTITY % address.attlist "INCLUDE" >
<![%address.attlist;[
<!ATTLIST %address.qname;
%Common.attrib;
>
<!-- end of address.attlist -->]]>
<!ENTITY % blockquote.element "INCLUDE" >
<![%blockquote.element;[
<!ENTITY % blockquote.content
"( %Block.mix; )*"
>
<!ENTITY % blockquote.qname "blockquote" >
<!ELEMENT %blockquote.qname; %blockquote.content; >
<!-- end of blockquote.element -->]]>
<!ENTITY % blockquote.attlist "INCLUDE" >
<![%blockquote.attlist;[
<!ATTLIST %blockquote.qname;
%Common.attrib;
cite %URI.datatype; #IMPLIED
>
<!-- end of blockquote.attlist -->]]>
<!ENTITY % pre.element "INCLUDE" >
<![%pre.element;[
<!ENTITY % pre.content
"( #PCDATA
| %InlStruct.class;
%InlPhras.class;
| %tt.qname; | %i.qname; | %b.qname;
%I18n.class;
%Anchor.class;
| %map.qname;
%Misc.class;
%Inline.extra; )*"
>
<!ENTITY % pre.qname "pre" >
<!ELEMENT %pre.qname; %pre.content; >
<!-- end of pre.element -->]]>
<!ENTITY % pre.attlist "INCLUDE" >
<![%pre.attlist;[
<!ATTLIST %pre.qname;
%Common.attrib;
>
<!-- end of pre.attlist -->]]>
<!-- ................... Heading Elements ................... -->
<!ENTITY % Heading.content "( #PCDATA | %Inline.mix; )*" >
<!ENTITY % h1.element "INCLUDE" >
<![%h1.element;[
<!ENTITY % h1.qname "h1" >
<!ELEMENT %h1.qname; %Heading.content; >
<!-- end of h1.element -->]]>
<!ENTITY % h1.attlist "INCLUDE" >
<![%h1.attlist;[
<!ATTLIST %h1.qname;
%Common.attrib;
>
<!-- end of h1.attlist -->]]>
<!ENTITY % h2.element "INCLUDE" >
<![%h2.element;[
<!ENTITY % h2.qname "h2" >
<!ELEMENT %h2.qname; %Heading.content; >
<!-- end of h2.element -->]]>
<!ENTITY % h2.attlist "INCLUDE" >
<![%h2.attlist;[
<!ATTLIST %h2.qname;
%Common.attrib;
>
<!-- end of h2.attlist -->]]>
<!ENTITY % h3.element "INCLUDE" >
<![%h3.element;[
<!ENTITY % h3.qname "h3" >
<!ELEMENT %h3.qname; %Heading.content; >
<!-- end of h3.element -->]]>
<!ENTITY % h3.attlist "INCLUDE" >
<![%h3.attlist;[
<!ATTLIST %h3.qname;
%Common.attrib;
>
<!-- end of h3.attlist -->]]>
<!ENTITY % h4.element "INCLUDE" >
<![%h4.element;[
<!ENTITY % h4.qname "h4" >
<!ELEMENT %h4.qname; %Heading.content; >
<!-- end of h4.element -->]]>
<!ENTITY % h4.attlist "INCLUDE" >
<![%h4.attlist;[
<!ATTLIST %h4.qname;
%Common.attrib;
>
<!-- end of h4.attlist -->]]>
<!ENTITY % h5.element "INCLUDE" >
<![%h5.element;[
<!ENTITY % h5.qname "h5" >
<!ELEMENT %h5.qname; %Heading.content; >
<!-- end of h5.element -->]]>
<!ENTITY % h5.attlist "INCLUDE" >
<![%h5.attlist;[
<!ATTLIST %h5.qname;
%Common.attrib;
>
<!-- end of h5.attlist -->]]>
<!ENTITY % h6.element "INCLUDE" >
<![%h6.element;[
<!ENTITY % h6.qname "h6" >
<!ELEMENT %h6.qname; %Heading.content; >
<!-- end of h6.element -->]]>
<!ENTITY % h6.attlist "INCLUDE" >
<![%h6.attlist;[
<!ATTLIST %h6.qname;
%Common.attrib;
>
<!-- end of h6.attlist -->]]>
<!-- end of xhtml-blkphras-1.mod -->

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save