Merge pull request #909 from ag2s20150909/master
添加自定义epublib,支持epub v3解析目录。pull/913/head 3.21.032520
commit
820a3c4fad
@ -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 < 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 < 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 < 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: |
||||||
|
* <meta property="rendition:layout">pre-paginated</meta> |
||||||
|
* @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: |
||||||
|
* <meta property="rendition:layout">pre-paginated</meta> |
||||||
|
* @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 < 0 then totalNrRead is returned, if |
||||||
|
* (nrRead + totalNrRead) < Integer.MAX_VALUE then nrRead + totalNrRead |
||||||
|
* is returned, -1 otherwise. |
||||||
|
* |
||||||
|
* @param nrRead |
||||||
|
* @param totalNrNread |
||||||
|
* @return if totalNrRead < 0 then totalNrRead is returned, if |
||||||
|
* (nrRead + totalNrRead) < 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 > 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 > 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; |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -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…
Reference in new issue