From de71be6ed2ac0542abb790006896980b2c2211a8 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:35:56 +0000 Subject: [PATCH] 2003/09/19 08:33:47 darcs-hash:20040130073556-2ba56-8129e7bd75563cd731e222ed8b67ba1251b9e09a.gz --- src/org/xwt/plat/Darwin.cc | 10 +- src/org/xwt/plat/Darwin.java | 10 +- src/org/xwt/plat/Java2.java | 10 +- src/org/xwt/plat/OpenGL.java | 3 +- src/org/xwt/plat/Win32.java | 10 +- src/org/xwt/plat/X11.cc | 2 +- src/org/xwt/plat/X11.java | 6 +- src/org/xwt/translators/Font.java | 76 ++++ src/org/xwt/translators/Freetype.c | 96 +++++ src/org/xwt/translators/GIF.java | 444 +++++++++++++++++++++++ src/org/xwt/translators/HTML.java | 341 ++++++++++++++++++ src/org/xwt/translators/PNG.java | 700 ++++++++++++++++++++++++++++++++++++ src/org/xwt/translators/SVG.java | 159 ++++++++ src/org/xwt/util/CAB.java | 421 ++++++++++++++++++++++ src/org/xwt/util/Hash.java | 7 +- 15 files changed, 2266 insertions(+), 29 deletions(-) create mode 100644 src/org/xwt/translators/Font.java create mode 100644 src/org/xwt/translators/Freetype.c create mode 100644 src/org/xwt/translators/GIF.java create mode 100644 src/org/xwt/translators/HTML.java create mode 100644 src/org/xwt/translators/PNG.java create mode 100644 src/org/xwt/translators/SVG.java create mode 100644 src/org/xwt/util/CAB.java diff --git a/src/org/xwt/plat/Darwin.cc b/src/org/xwt/plat/Darwin.cc index c38f173..0e0b2a7 100644 --- a/src/org/xwt/plat/Darwin.cc +++ b/src/org/xwt/plat/Darwin.cc @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -707,9 +707,9 @@ void Darwin::natSetClipBoard(jstring js) { checkStatus(r,"PutScrapFlavor"); } -Proxy *Darwin::natDetectProxy() { - using org::xwt::Proxy; - Proxy *p=0; +HTTP$Proxy *Darwin::natDetectProxy() { + using org::xwt::HTTP$Proxy; + HTTP$Proxy *p=0; CFStringRef string; CFNumberRef number; SmartCFString smartString; @@ -728,7 +728,7 @@ Proxy *Darwin::natDetectProxy() { number = (CFNumberRef) CFDictionaryGetValue(proxyInfo, kSCPropNetProxies ## proto ## Port); \ if(number != NULL && CFGetTypeID(number) != CFNumberGetTypeID()) number = NULL; \ if(string && number && CFNumberGetValue(number,kCFNumberIntType,&i) && i) { \ - if(!p) p = new Proxy(); \ + if(!p) p = new HTTP$Proxy(); \ p->var ## ProxyHost = cfStringToJString(string); \ p->var ## ProxyPort = i; \ } \ diff --git a/src/org/xwt/plat/Darwin.java b/src/org/xwt/plat/Darwin.java index 34030ca..17877c1 100644 --- a/src/org/xwt/plat/Darwin.java +++ b/src/org/xwt/plat/Darwin.java @@ -32,7 +32,7 @@ public class Darwin extends POSIX { private native static int cgScreenWidth(); private native static int cgScreenHeight(); protected native void _newBrowserWindow(String url); - protected native Proxy natDetectProxy(); + protected native HTTP.Proxy natDetectProxy(); private native void natInit(); protected native void _exit(); @@ -81,7 +81,7 @@ public class Darwin extends POSIX { } } - protected synchronized Proxy _detectProxy() { + protected synchronized HTTP.Proxy _detectProxy() { return natDetectProxy(); } @@ -196,7 +196,7 @@ public class Darwin extends POSIX { public void reshape(int w, int h) { } } - static class GLCarbonPixelBuffer extends OpenGL.GLDoubleBuffer { + static class GLCarbonPixelBuffer extends OpenGL.GLPixelBuffer { RawData rawCTX; RawData rawWindowRef; int textureName; @@ -267,7 +267,7 @@ public class Darwin extends POSIX { public native void natDispose(); } - /*private class QZCarbonPixelBuffer extends DoubleBuffer { + /*private class QZCarbonPixelBuffer extends PixelBuffer { public QZCarbonPixelBuffer(int width, int height) { } @@ -292,7 +292,7 @@ public class Darwin extends POSIX { } }*/ - protected PixelBuffer _createDoubleBuffer(int w, int h, Surface owner) { + protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { if(openGL != null) return new GLCarbonPixelBuffer(w,h,openGL); else diff --git a/src/org/xwt/plat/Java2.java b/src/org/xwt/plat/Java2.java index e6a2ad4..b38fd0d 100644 --- a/src/org/xwt/plat/Java2.java +++ b/src/org/xwt/plat/Java2.java @@ -49,11 +49,11 @@ public class Java2 extends AWT { } /** this is done with reflection in case a new version of the plugin comes out that doesn't let us pull the sun.plugin.* trick */ - protected synchronized org.xwt.Proxy _detectProxy() { - return (org.xwt.Proxy)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { + protected synchronized org.xwt.HTTP.Proxy _detectProxy() { + return (org.xwt.HTTP.Proxy)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { public Object run() { try { - org.xwt.Proxy pi = new org.xwt.Proxy(); + org.xwt.HTTP.Proxy pi = new org.xwt.HTTP.Proxy(); Class PluginProxyHandler = Class.forName("sun.plugin.protocol.PluginProxyHandler"); Method getDefaultProxyHandler = PluginProxyHandler.getMethod("getDefaultProxyHandler", new Class[] { }); @@ -106,7 +106,7 @@ public class Java2 extends AWT { }); } - protected PixelBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new Java2DoubleBuffer(w, h); } + protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { return new Java2PixelBuffer(w, h); } protected Surface _createSurface(final Box root, final boolean framed) { return (Surface)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { public Object run() { @@ -197,7 +197,7 @@ public class Java2 extends AWT { } } - protected static class Java2PixelBuffer extends AWTDoubleBuffer { + protected static class Java2PixelBuffer extends AWTPixelBuffer { private static ColorModel cm = Toolkit.getDefaultToolkit().getColorModel(); private static Hashtable emptyHashtable = new Hashtable(); private static short[] sbank = null; diff --git a/src/org/xwt/plat/OpenGL.java b/src/org/xwt/plat/OpenGL.java index d730e0e..ecacec1 100644 --- a/src/org/xwt/plat/OpenGL.java +++ b/src/org/xwt/plat/OpenGL.java @@ -52,7 +52,7 @@ abstract class OpenGL { public NotSupportedException(String s) { super(s); } } - public static abstract class GLPixelBuffer extends DoubleBuffer { + public static abstract class GLPixelBuffer extends PixelBuffer { protected int width; protected int height; public int getWidth() { return width; } @@ -100,7 +100,6 @@ abstract class OpenGL { } public Picture createPicture(int[] data, int w, int h) { - if(w*h != data.length) throw new Error("w*h != data.length"); if(rectangularTextures && w <= maxRectTexSize && h <= maxRectTexSize) new RectGLPicture(data,w,h,this); if(w <= maxTexSize && h <= maxTexSize) return new SquareGLPicture(data,w,h,this); return new MosaicGLPicture(data,w,h,this); diff --git a/src/org/xwt/plat/Win32.java b/src/org/xwt/plat/Win32.java index 4a14394..f0b6a69 100644 --- a/src/org/xwt/plat/Win32.java +++ b/src/org/xwt/plat/Win32.java @@ -119,7 +119,7 @@ public class Win32 extends GCJ { protected boolean _needsAutoClick() { return true; } protected String getDescriptiveName() { return "GCJ Win32 Binary"; } protected Surface _createSurface(Box b, boolean framed) { return new Win32Surface(b, framed); } - protected PixelBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new Win32DoubleBuffer(w, h, (Win32Surface)owner); } + protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { return new Win32PixelBuffer(w, h, (Win32Surface)owner); } protected Picture _createPicture(int[] b, int w, int h) { return new Win32Picture(b, w, h); } protected native int _getScreenWidth(); protected native int _getScreenHeight(); @@ -131,7 +131,7 @@ public class Win32 extends GCJ { private native void __detectProxy(String[] container); - protected synchronized Proxy _detectProxy() { + protected synchronized HTTP.Proxy _detectProxy() { String[] container = new String[] { null, null, null }; if (Log.on) Log.log(this, "accessing Win32 registry"); @@ -145,9 +145,9 @@ public class Win32 extends GCJ { if (Log.on) Log.log(this, "Proxy Server String: " + container[0]); if (Log.on) Log.log(this, "Proxy Override String: " + container[1]); - Proxy ret = new Proxy(); + HTTP.Proxy ret = new HTTP.Proxy(); if (container[2] != null) { - ret.proxyAutoConfigFunction = Proxy.getProxyAutoConfigFunction(container[2]); + ret.proxyAutoConfigFunction = HTTP.Proxy.getProxyAutoConfigFunction(container[2]); if (ret.proxyAutoConfigFunction != null) return ret; } @@ -291,7 +291,7 @@ public class Win32 extends GCJ { // Win32PixelBuffer ////////////////////////////////////////////////////////////////////////// - public static class Win32PixelBuffer extends DoubleBuffer { + public static class Win32PixelBuffer extends PixelBuffer { int w = 0; int h = 0; diff --git a/src/org/xwt/plat/X11.cc b/src/org/xwt/plat/X11.cc index 2d146e6..b861832 100644 --- a/src/org/xwt/plat/X11.cc +++ b/src/org/xwt/plat/X11.cc @@ -274,7 +274,7 @@ void org::xwt::plat::X11$X11PixelBuffer::createStipple(org::xwt::plat::X11$X11Pi } void org::xwt::plat::X11$X11Surface::blit(org::xwt::PixelBuffer* db, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) { - org::xwt::plat::X11$X11PixelBuffer *xdb = (org::xwt::plat::X11$X11DoubleBuffer*)db; + org::xwt::plat::X11$X11PixelBuffer *xdb = (org::xwt::plat::X11$X11PixelBuffer*)db; XCopyArea(display, *((Pixmap*)xdb->pm), *((Window*)window), *((GC*)gc), sx, sy, dx2 - dx, dy2 - dy, dx, dy); XFlush(display); } diff --git a/src/org/xwt/plat/X11.java b/src/org/xwt/plat/X11.java index da7be81..3b29f4a 100644 --- a/src/org/xwt/plat/X11.java +++ b/src/org/xwt/plat/X11.java @@ -35,7 +35,7 @@ public class X11 extends POSIX { protected String[] _listFonts() { return fontList; } protected Picture _createPicture(int[] data, int w, int h) { return new X11Picture(data, w, h); } - protected PixelBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new X11DoubleBuffer(w, h); } + protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { return new X11PixelBuffer(w, h); } protected Surface _createSurface(Box b, boolean framed) { return new X11Surface(b, framed); } protected boolean _needsAutoClick() { return true; } protected native int _getScreenWidth(); @@ -129,7 +129,7 @@ public class X11 extends POSIX { void buildPixelBuffer(boolean needsStipple) { if (doublebuf != null) return; // no point in using a shared pixmap since we'll only write to this image once - X11PixelBuffer b = new X11DoubleBuffer(width, height, false); + X11PixelBuffer b = new X11PixelBuffer(width, height, false); b.drawPicture(this, 0, 0); if (needsStipple) b.createStipple(this); doublebuf = b; @@ -144,7 +144,7 @@ public class X11 extends POSIX { * with all-or-nothing alpha will not use shared pixmaps, however * (since they are only written to once. */ - public static class X11PixelBuffer extends DoubleBuffer { + public static class X11PixelBuffer extends PixelBuffer { int clipx, clipy, clipw, cliph; int width; diff --git a/src/org/xwt/translators/Font.java b/src/org/xwt/translators/Font.java new file mode 100644 index 0000000..190f927 --- /dev/null +++ b/src/org/xwt/translators/Font.java @@ -0,0 +1,76 @@ +package org.xwt.translators; +import org.xwt.*; +import org.xwt.util.*; +import java.io.*; + +// FEATURE: use streams, not memoryfont's +// FEATURE: kerning pairs +public class Font { + + Font() { } + + private static org.xwt.mips.Interpreter vm = null; + + public static synchronized void renderGlyphs(Res res, int pointsize, int firstGlyph, int lastGlyph, Cache glyphCache) { + try { + if (vm == null) { + vm = new org.xwt.mips.Interpreter("freetype.mips"); + vm.start(new String[]{ "freetype.mips"}); + vm.execute(); + } + + int FONT_RESERVED = 256*1024; + int baseAddr = vm.sbrk(FONT_RESERVED); + + byte[] fontstream = Resources.isToByteArray(res.getInputStream()); + vm.copyout(fontstream, baseAddr, fontstream.length); + vm.setUserInfo(0, baseAddr); + vm.setUserInfo(1, fontstream.length); + vm.setUserInfo(2, firstGlyph); + vm.setUserInfo(3, lastGlyph); + vm.setUserInfo(4, pointsize); + + long start = System.currentTimeMillis(); + + for(int g = firstGlyph; g <= lastGlyph; g++) { + vm.execute(); + + Glyph glyph = new Glyph(); + glyph.max_ascent = vm.getUserInfo(8); + glyph.max_descent = vm.getUserInfo(9) - glyph.max_ascent; + glyph.baseline = vm.getUserInfo(10); + glyph.advance = vm.getUserInfo(11); + glyph.c = (char)g; + + gid.width = vm.getUserInfo(6); + gid.height = vm.getUserInfo(7); + if (gid.data == null || gid.data.length < gid.width * gid.height) + gid.data = new int[gid.width * gid.height]; + int addr = vm.getUserInfo(5); + + for(int i=0; i= 0; k--) { + if (i + k < gid.width * gid.height) + gid.data[i + k] = (val & 0xff) << 24; + val >>>= 8; + } + } + + glyph.p = Platform.createPicture(gid); + glyphCache.put(res, new Integer((g << 16) | pointsize), glyph); + } + } catch (Exception e) { + Log.log(Font.class, e); + } + } + + private static GlyphImageDecoder gid = new GlyphImageDecoder(); + private static class GlyphImageDecoder extends ImageDecoder { + int[] data = null; + int width, height; + public int getWidth() { return width; } + public int getHeight() { return height; } + public int[] getData() { return data; } + } +} diff --git a/src/org/xwt/translators/Freetype.c b/src/org/xwt/translators/Freetype.c new file mode 100644 index 0000000..03a3fbe --- /dev/null +++ b/src/org/xwt/translators/Freetype.c @@ -0,0 +1,96 @@ +// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL] + +#include + +/* NOTE: _user_info is defined in crt0.c. It points to a 4096 byte + block of memory that contains 1024 32-bit values that can be set + with the setUserInfo() method of MIPSEmu. + + The VM will pause after initialization. When unpaused, it expects + that: + + user_info[0] = ptr to font byte stream in memory + user_info[1] = length of font stream + user_info[2] = unicode index of first glyph to render + user_info[3] = unicode index of last glyph to render + user_info[4] = point size to render in + + The VM will then iterate over the requested glyphs, performing the + following actions for each glyph: + + - render the glyph + - store the address of the glyph bitmap in user_info[5] + - store the width of the glyph bitmap in user_info[6] + - store the height of the glyph bitmap in user_info[7] + - store the font's ascender into user_info[8] + - store the font's height into user_info[9] + - store the glyph's ascender into user_info[10] + - store the glyph's advance into user_info[11] + + The VM will then pause after each glyph. The VM should not be + unpaused after the last glyph until the next glyph set has been + configured in user_info (ie it does not pause twice). + +*/ + +extern char **_user_info; + +FT_Library library; /* handle to library */ +FT_Face face; /* handle to face object */ + +#define FT_Check(expr) do { \ + if((expr) != 0) { \ + fprintf(stderr, #expr " failed\n"); \ + exit(EXIT_FAILURE); \ + } \ +} while(0) + +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) + +extern void emu_pause(); + +int main(int argc, char** argv) { + char *fontdata; + int glyph_index; + short charcode; + + FT_Check(FT_Init_FreeType(&library)); + emu_pause(); + + for(;;) { + FT_Check(FT_New_Memory_Face(library, _user_info[0], (int)_user_info[1], 0, &face)); + FT_Check(FT_Set_Char_Size(face, 0, ((int)_user_info[4]) * 64, 72, 72)); + + for(charcode = (int)_user_info[2]; charcode <= (int)_user_info[3]; charcode++) { + + glyph_index = FT_Get_Char_Index(face, charcode); + FT_Check(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_FORCE_AUTOHINT)); + FT_Check(FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)); + + _user_info[5] = (char*)face->glyph->bitmap.buffer; + _user_info[6] = (char*)face->glyph->bitmap.width; + _user_info[7] = (char*)face->glyph->bitmap.rows; + _user_info[8] = (char*)(face->size->metrics.ascender >> 6); + _user_info[9] = (char*)(face->size->metrics.height >> 6); + _user_info[10] = (char*)(face->glyph->metrics.horiBearingY >> 6); + _user_info[11] = (char*)(face->glyph->advance.x >> 6); + + emu_pause(); + } + } +} + + +// Kerning code; add back in later +/* +if (old_glyph_index != -1) { + if (FT_HAS_KERNING(face)) { + FT_Check(FT_Get_Kerning(face, old_glyph_index, glyph_index, 0, &kerning)); + x += kerning.x >> 6; + } else { + x += face->glyph->advance.x >> 6; + } +} +old_glyph_index = glyph_index; +*/ diff --git a/src/org/xwt/translators/GIF.java b/src/org/xwt/translators/GIF.java new file mode 100644 index 0000000..b3359f5 --- /dev/null +++ b/src/org/xwt/translators/GIF.java @@ -0,0 +1,444 @@ +/* + * This file was adapted from D J Hagberg's GifDecoder.java + * + * This software is copyrighted by D. J. Hagberg, Jr., and other parties. + * The following terms apply to all files associated with the software + * unless explicitly disclaimed in individual files. + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + * + * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + * MODIFICATIONS. + * + * GOVERNMENT USE: If you are acquiring this software on behalf of the + * U.S. government, the Government shall have only "Restricted Rights" + * in the software and related documentation as defined in the Federal + * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + * are acquiring the software on behalf of the Department of Defense, the + * software shall be classified as "Commercial Computer Software" and the + * Government shall have only "Restricted Rights" as defined in Clause + * 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + * authors grant the U.S. Government and others acting in its behalf + * permission to use and distribute the software in accordance with the + * terms specified in this license. + * + */ +package org.xwt.translators; + +import org.xwt.*; +import org.xwt.util.*; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.PrintWriter; + +/** Converts an InputStream carrying a GIF image into an ARGB int[] */ +public class GIF extends ImageDecoder { + + // Public Methods ///////////////////////////////////////////////////////// + + public int[] getData() { return data; } + public int getWidth() { return width; } + public int getHeight() { return height; } + + /** Processes an image from InputStream is; returns null if there is an error + @param name A string describing the image; used for error reporting. + */ + public static GIF decode(InputStream is, String name) { + try { + return new GIF(is, name); + } catch (Exception e) { + if (Log.on) Log.log(GIF.class, e); + return null; + } + } + + private GIF(InputStream is, String name) throws IOException { + if (is instanceof BufferedInputStream) _in = (BufferedInputStream)is; + else _in = new BufferedInputStream(is); + decodeAsBufferedImage(0); + } + + // Private Methods ///////////////////////////////////////////////////////// + + private int[] data = null; + + /** Decode a particular frame from the GIF file. Frames must be decoded in order, starting with 0. */ + private void decodeAsBufferedImage(int page) throws IOException, IOException { + + // If the user requested a page already decoded, we cannot go back. + if (page <= index ) return; + + // If we just started reading this stream, initialize the global info. + if (index < 0 ) { + if (!readIntoBuf(6) || _buf[0] != 'G' || _buf[1] != 'I' || _buf[2] != 'F' || + _buf[3] != '8' || !(_buf[4] == '7' || _buf[4] == '9') || _buf[5] != 'a') + throw new IOException("Not a GIF8Xa file."); + if (!readGlobalImageDescriptor()) throw new IOException("Unable to read GIF header."); + } + + // Loop through the blocks in the image. + int block_identifier; + while (true) { + if(!readIntoBuf(1)) throw new IOException("Unexpected EOF(1)"); + block_identifier = _buf[0]; + if (block_identifier == ';') throw new IOException("No image data"); + if (block_identifier == '!') { + if (!readExtensionBlock()) throw new IOException("Unexpected EOF(2)"); + continue; + } + + // not a valid start character -- ignore it. + if (block_identifier != ',') continue; + + if (!readLocalImageDescriptor()) throw new IOException("Unexpected EOF(3)"); + data = new int[width * height]; + readImage(); + + // If we did not decode the requested index, need to go on + // to the next one (future implementations should consider + // caching already-decoded images here to allow for random + // access to animated GIF pages). + index++; + if (index < page) continue; + + // If we did decode the requested index, we can return. + break; + } + + // Return the image thus-far decoded + return; + } + + /** Actually read the image data */ + private void readImage() + throws IOException, IOException { + int len = width; + int rows = height; + int initialCodeSize; + int v; + int xpos = 0, ypos = 0, pass = 0, i; + int prefix[] = new int[(1 << MAX_LWZ_BITS)]; + int append[] = new int[(1 << MAX_LWZ_BITS)]; + int stack[] = new int[(1 << MAX_LWZ_BITS)*2]; + int top_idx; + int codeSize, clearCode, inCode, endCode, oldCode, maxCode, code, firstCode; + + // Initialize the decoder + if (!readIntoBuf(1)) throw new IOException("Unexpected EOF decoding image"); + initialCodeSize = _buf[0]; + + // Look at the right color map, setting up transparency if called for. + int[] cmap = global_color_map; + if (hascmap) cmap = color_map; + if (trans_idx >= 0) cmap[trans_idx] = 0x00000000; + + /* Initialize the decoder */ + /* Set values for "special" numbers: + * clear code reset the decoder + * end code stop decoding + * code size size of the next code to retrieve + * max code next available table position + */ + clearCode = 1 << initialCodeSize; + endCode = clearCode + 1; + codeSize = initialCodeSize + 1; + maxCode = clearCode + 2; + oldCode = -1; + firstCode = -1; + + for (i = 0; i < clearCode; i++) append[i] = i; + top_idx = 0; // top of stack. + + bitsInWindow = 0; + bytes = 0; + window = 0L; + done = false; + c = -1; + + /* Read until we finish the image */ + ypos = 0; + for (i = 0; i < rows; i++) { + for (xpos = 0; xpos < len;) { + + if (top_idx == 0) { + /* Bummer -- our stack is empty. Now we have to work! */ + code = getCode(codeSize); + if (code < 0) return; + + if (code > maxCode || code == endCode) return; + if (code == clearCode) { + codeSize = initialCodeSize + 1; + maxCode = clearCode + 2; + oldCode = -1; + continue; + } + + // Last pass reset the decoder, so the first code we + // see must be a singleton. Seed the stack with it, + // and set up the old/first code pointers for + // insertion into the string table. We can't just + // roll this into the clearCode test above, because + // at that point we have not yet read the next code. + if (oldCode == -1) { + stack[top_idx++] = append[code]; + oldCode = code; + firstCode = code; + continue; + } + + inCode = code; + + // maxCode is always one bigger than our + // highest assigned code. If the code we see + // is equal to maxCode, then we are about to + // add a new string to the table. ??? + if (code == maxCode) { + stack[top_idx++] = firstCode; + code = oldCode; + } + + // Populate the stack by tracing the string in the + // string table from its tail to its head + while (code > clearCode) { + stack[top_idx++] = append[code]; + code = prefix[code]; + } + firstCode = append[code]; + + // If there's no more room in our string table, quit. + // Otherwise, add a new string to the table + if (maxCode >= (1 << MAX_LWZ_BITS)) return; + + // Push the head of the string onto the stack + stack[top_idx++] = firstCode; + + // Add a new string to the string table + prefix[maxCode] = oldCode; + append[maxCode] = firstCode; + maxCode++; + + // maxCode tells us the maximum code value we can accept. + // If we see that we need more bits to represent it than + // we are requesting from the unpacker, we need to increase + // the number we ask for. + if ((maxCode >= (1 << codeSize)) && (maxCode < (1<= rows) { + pass++; + if (pass > 3) return; + ypos = _interlaceStart[pass]; + } + } else ypos++; + } + return; + } + + /** Extract the next compression code from the file. */ + private int getCode(int code_size) throws IOException { + int ret; + + while (bitsInWindow < code_size) { + // Not enough bits in our window to cover the request + if (done) return -1; + + if (bytes == 0) { + // Not enough bytes in our buffer to add to the window + bytes = getDataBlock(); + c = 0; + if (bytes <= 0) { + done = true; + break; + } + } + // Tack another byte onto the window, see if that's enough + window += (_buf[c]) << bitsInWindow; + ++c; + bitsInWindow += 8; + bytes--; + } + + + // The next code will always be the last code_size bits of the window + ret = ((int)window) & ((1 << code_size) - 1); + + // Shift data in the window to put the next code at the end + window >>= code_size; + bitsInWindow -= code_size; + return ret; + } + + /** Read the global image descriptor and optional global color map. Sets global_* variables. */ + private boolean readGlobalImageDescriptor() throws IOException { + int packed; + int aspect; // we ignore this. + int ofs; + + if (!readIntoBuf(7) ) return false; + global_width = _buf[0] | (_buf[1] << 8); + global_height = _buf[2] | (_buf[3] << 8); + packed = _buf[4]; + global_bgcolor = _buf[5]; + aspect = _buf[6]; + global_cmapsize = 2 << (packed & 0x07); + global_hascmap = (packed & GLOBALCOLORMAP) == GLOBALCOLORMAP; + global_color_map = null; + + // Read the color map, if we have one. + if (global_hascmap) { + if (!readColorMap(global_cmapsize,true)) { + return false; + } + } + + return true; + } + + /** Read a local image descriptor and optional local color map. */ + private boolean readLocalImageDescriptor() throws IOException { + int packed; + + if (!readIntoBuf(9) ) return false; + + left = _buf[0] | (_buf[1] << 8); + top = _buf[2] | (_buf[3] << 8); + width = _buf[4] | (_buf[5] << 8); + height = _buf[6] | (_buf[7] << 8); + packed = _buf[8]; + hascmap = (packed & LOCALCOLORMAP) == LOCALCOLORMAP; + cmapsize = 2 << (packed & 0x07); + interlaced = (packed & INTERLACE) == INTERLACE; + color_map = null; + + // Read the local color table, if there is one. + return !(hascmap && !readColorMap(cmapsize,false)); + } + + /** Read a color map (global or local). */ + private boolean readColorMap(int nColors, boolean isGlobal) + throws IOException { + int[] map = new int[nColors]; + for( int i=0; i < nColors; ++i) { + if (!readIntoBuf(3) ) return false; + map[i] = (_buf[0] << 16) | (_buf[1] << 8) | _buf[2] | 0xFF000000; + } + if (isGlobal) global_color_map = map; + else color_map = map; + return true; + } + + /** Read the contents of a GIF89a Graphical Extension Block. */ + private boolean readExtensionBlock() throws IOException { + if (!readIntoBuf(1) ) return false; + int label = _buf[0]; + int count = -1; + switch (label) { + case 0x01: // Plain Text Extension + case 0xff: // Application Extension + case 0xfe: // Comment Extension + break; + case 0xf9: // Graphic Control Extension + count = getDataBlock(); + if (count < 0) return true; + // Check for transparency setting. + if ((_buf[0] & HASTRANSPARENCY) != 0) trans_idx = _buf[3]; + else trans_idx = -1; + } + do { count = getDataBlock(); } while (count > 0); + return true; + } + + /** Read a block of data from the GIF file. */ + private int getDataBlock() throws IOException { + if (!readIntoBuf(1) ) return -1; + int count = _buf[0]; + if (count != 0) if (!readIntoBuf(count) ) return -1; + return count; + } + + /** Read the indicated number of bytes into _buf, our instance-wide buffer. */ + private boolean readIntoBuf(int count) throws IOException { + for(int i = 0; i < count; i++) if ((_buf[i] = _in.read()) == -1) return false; + return true; + } + + // Private Data ////////////////////////////////////////////////////////// + + // State management stuff + private int index = -1; + private BufferedInputStream _in = null; + private int[] _buf = new int[BUFSIZE]; + + // Transparency settings + private int trans_idx = -1; + + // Global image descriptor contents + private int global_width = 0; + private int global_height = 0; + private int global_bgcolor = 0; + private int global_cmapsize = 0; + private boolean global_hascmap = false; + private int[] global_color_map = null; + + // Local image descriptor contents + private int left = 0; + private int top = 0; + private int width = 0; + private int height = 0; + private int cmapsize = 0; + private boolean hascmap = false; + private boolean interlaced = false; + private int[] color_map = null; + + // Variables used in getCode(...) to track sliding bit-window. + private int bytes = 0; + private boolean done; + private int c; + private long window; + private int bitsInWindow = 0; + + // Class-wide constants. + private static final int INTERLACE = 0x40; + private static final int GLOBALCOLORMAP = 0x80; + private static final int LOCALCOLORMAP = 0x80; + private static final int HASTRANSPARENCY = 0x01; + private static final int MAX_LWZ_BITS = 12; + private static final int BUFSIZE = 280; + private static final int[] _interlaceStep = { 8, 8, 4, 2 }; + private static final int[] _interlaceStart = { 0, 4, 2, 1 }; +} + + + diff --git a/src/org/xwt/translators/HTML.java b/src/org/xwt/translators/HTML.java new file mode 100644 index 0000000..b8ece56 --- /dev/null +++ b/src/org/xwt/translators/HTML.java @@ -0,0 +1,341 @@ +// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.translators; + +import java.util.*; +import java.net.*; +import java.io.*; +import org.xwt.js.*; +import org.xwt.util.*; + +/* + * While entities are limited to a subset of Unicode characters , + * numeric character references can specify any character. Numeric + * character references may be given in decimal or hexadecimal, though + * browser support is stronger for decimal references. Decimal + * references are of the form &#number; while hexadecimal references + * take the case-insensitive form &#xnumber;. Examples of numeric + * character references include © or © for the copyright + * symbol, Α or Α for the Greek capital letter alpha, and + * ا or ا for the Arabic letter ALEF. + * + * http://www.htmlhelp.com/reference/html40/entities/special.html + * http://www.htmlhelp.com/reference/html40/entities/symbols.html + * http://www.htmlhelp.com/reference/html40/entities/latin1.html + */ + +/** + * This class parses an InputStream containing HTML and returns it + * as an XWT DOM tree. Each HTML Element is returned as a struct, + * with the following members: + * + * Since HTML may have multiple top level elements (unlike XML), + * this class will search all top level elements for one with a tag + * name 'html'. If such a node is found, only it is returned. If no + * top-level element has the tag name 'html', such a node is + * fabricated, and all top level elements become the children of + * that node, which is then returned. + */ +public class HTML { + + // FIXME: fill in + private final static String[] bodylessTags = new String[] { "br", "hr", "input", "img", "isindex" }; + + /** we keep a char[] around for use by removeRedundantWhitespace() */ + private static char[] cbuf = null; + + /** we keep a StringBuffer around for use by removeRedundantWhitespace() */ + private static StringBuffer sbuf = null; + + /** true iff we have encountered an LI more recently than the last OL/UL */ + private static boolean withinLI = false; + + public static synchronized JS parseReader(Reader r) throws IOException { + CharStream cs = new CharStream(r); + JS.Obj h = new JS.Obj(); + + withinLI = false; + h.put("$name", "html"); + + try { + while (true) parseBody(cs, h, null); + } catch (EOFException e) { + // continue until we get an EOFException + } + + Object[] ids = h.keys(); + for(int i=0; ih. The + * CharStream should be positioned immediately after the + * open bracket. + * + * If a close tag not matching this open tag is found, the + * tagname on the close tag will be returned in order to + * facilitate correcting broken HTML. Otherwise, this returns + * null. + */ + private static String parseElement(CharStream cs, JS h) throws IOException { + // scan element name + while(Character.isSpace(cs.peek())) cs.get(); + String elementName = parseElementName(cs); + + // FIXME: this might not deal correctly with EOFExceptions + boolean saveWithinLI = withinLI; + if (elementName.equals("li")) { + if (withinLI) { + cs.unread(new char[] { '<', 'l', 'i', ' ' }); + return "li"; + } else { + withinLI = true; + } + } else if (elementName.equals("ol") || elementName.equals("ul")) { + withinLI = false; + } + + h.put("$name", elementName); + if (elementName.equals("!--")) { + h.put("0", parseComment(cs)); + h.put("$numchildren", new Integer(0)); + return null; + } + + // scan attributes + while (cs.peek() != '>') { + String name = parseAttributeName(cs); + if (name.equals("")) break; + String value = expandEntities(parseAttributeValue(cs)); + h.put(name, value); + } + + // eat the close-angle bracket + cs.get(); + + // bodyless tags return here + for(int i=0; i 0) { + h.put(String.valueOf(length), expanded); + h.put("$numchildren", new Integer(++length)); + } + cdata = ""; + + } catch (EOFException e) { + String expanded = removeRedundantWhitespace(expandEntities(cdata)); + if (expanded.length() > 0) { + h.put(String.valueOf(length), expanded); + h.put("$numchildren", new Integer(++length)); + } + throw e; + } + + try { + // scan subelement + if (cs.peek() != '/') { + JS kid = new JS.Obj(); + closetag = parseElement(cs, kid); + h.put(String.valueOf(length), kid); + h.put("$numchildren", new Integer(++length)); + + // scan close-tag + } else { + cs.get(); // drop the slash + closetag = parseElementName(cs); + while(cs.get() != '>'); + } + } catch (EOFException e) { + throw e; + + } + + if (closetag != null) + return closetag.equals(elementName) ? null : closetag; + } + } + + /** Parses an element name and returns it. The CharStream should + * be positioned at the first character of the name. + */ + private static String parseElementName(CharStream cs) throws IOException { + String ret = ""; + while (cs.peek() != '>' && !Character.isSpace(cs.peek())) ret += cs.get(); + return ret.toLowerCase(); + } + + /** Parses an attribute name and returns it. The CharStream should + * be positioned at the first character of the name, possibly + * with intervening whitespace. + */ + private static String parseAttributeName(CharStream cs) throws IOException { + while(Character.isSpace(cs.peek())) cs.get(); + String ret = ""; + while(!Character.isSpace(cs.peek()) && cs.peek() != '=' && cs.peek() != '>') ret += cs.get(); + return ret.toLowerCase(); + } + + /** Parses an attribute value and returns it. The CharStream + * should be positioned at the equals sign, possibly with + * intervening whitespace. + */ + private static String parseAttributeValue(CharStream cs) throws IOException { + + // eat whitespace and equals sign + while(Character.isSpace(cs.peek())) cs.get(); + if (cs.peek() != '=') return ""; + cs.get(); + while(Character.isSpace(cs.peek())) cs.get(); + + boolean doublequoted = false; + boolean singlequoted = false; + String ret = ""; + + if (cs.peek() == '\"') { doublequoted = true; cs.get(); } + else if (cs.peek() == '\'') { singlequoted = true; cs.get(); } + + while(true) { + char c = cs.peek(); + if (!doublequoted && !singlequoted && (Character.isSpace(c) || c == '>')) break; + if (singlequoted && c == '\'') { cs.get(); break; } + if (doublequoted && c == '\"') { cs.get(); break; } + ret += cs.get(); + } + return ret; + } + + /** Parses a comment and returns its body. The CharStream should + * be positioned immediately after the <!-- + */ + private static String parseComment(CharStream cs) throws IOException { + int dashes = 0; + String ret = ""; + while(true) { + char c = cs.get(); + if (c == '>' && dashes == 2) return ret.substring(0, ret.length() - 2); + if (c == '-') dashes++; + else dashes = 0; + ret += c; + } + } + + /** Expands all SGML entities in string s */ + public static String expandEntities(String s) throws IOException { + if (s.indexOf('&') == -1) return s; + StringBuffer sb = new StringBuffer(); + int i=0; + int nextamp = 0; + while(nextamp != -1) { + nextamp = s.indexOf('&', i); + sb.append(nextamp == -1 ? s.substring(i) : s.substring(i, nextamp)); + if (nextamp == -1) break; + if (s.regionMatches(nextamp, "&", 0, 5)) { + sb.append("&"); + i = nextamp + 5; + } else if (s.regionMatches(nextamp, ">", 0, 4)) { + sb.append(">"); + i = nextamp + 4; + } else if (s.regionMatches(nextamp, "<", 0, 4)) { + sb.append("<"); + i = nextamp + 4; + } else if (s.regionMatches(nextamp, """, 0, 6)) { + sb.append("\""); + i = nextamp + 6; + } else if (s.regionMatches(nextamp, " ", 0, 6)) { + // FIXME: should have a way to indicate this... + sb.append(" "); + i = nextamp + 6; + } else { + sb.append("&"); + i = nextamp + 1; + } + } + return sb.toString(); + } + + // FIXME double check this + /** removes all redundant whitespace */ + private static String removeRedundantWhitespace(String s) { + + if (s.indexOf(' ') == -1 && s.indexOf('\n') == -1 && s.indexOf('\t') == -1 && s.indexOf('\r') == -1) return s; + + int len = s.length(); + if (cbuf == null || cbuf.length < len) { + cbuf = new char[len * 2]; + sbuf = new StringBuffer(len * 2); + } + sbuf.setLength(0); + s.getChars(0, len, cbuf, 0); + + int last = 0; + boolean lastWasWhitespace = false; + for(int i=0; i 0;) { + newPalette[--i] = trans[--transLength]; + newPalette[--i] = palette[--len]; + newPalette[--i] = palette[--len]; + newPalette[--i] = palette[--len]; + } + palette = newPalette; + + } else { + inputStream.skip(chunkLength); + } + } + + /// Helper functions for IDAT /////////////////////////////////////////////////////////////////////////////////////////// + + /** Read Image data in off of a compression stream */ + private void readImageData() throws IOException { + InputStream dataStream = new SequenceInputStream(new IDATEnumeration(this)); + DataInputStream dis = new DataInputStream(new BufferedInputStream(new InflaterInputStream(dataStream, new Inflater()))); + int bps, filterOffset; + switch (colorType) { + case 0: case 3: bps = depth; break; + case 2: bps = 3 * depth; break; + case 4: bps = depth<<1; break; + case 6: bps = depth<<2; break; + default: throw new IOException("Unknown color type encountered."); + } + + filterOffset = (bps + 7) >> 3; + + for (pass = (multipass ? 1 : 0); pass < 8; pass++) { + int pass = this.pass; + int rInc = rowInc[pass]; + int cInc = colInc[pass]; + int sCol = startingCol[pass]; + int val = (width - sCol + cInc - 1) / cInc; + int samples = val * filterOffset; + int rowSize = (val * bps)>>3; + int sRow = startingRow[pass]; + if (height <= sRow || rowSize == 0) continue; + int sInc = rInc * width; + byte inbuf[] = new byte[rowSize]; + int pix[] = new int[rowSize]; + int upix[] = null; + int temp[] = new int[rowSize]; + int nextY = sRow; // next Y value and number of rows to report to sendPixels + int rows = 0; + int rowStart = sRow * width; + + for (int y = sRow; y < height; y += rInc, rowStart += sInc) { + rows += rInc; + int rowFilter = dis.read(); + dis.readFully(inbuf); + if (!filterRow(inbuf, pix, upix, rowFilter, filterOffset)) throw new IOException("Unknown filter type: " + rowFilter); + insertPixels(pix, rowStart + sCol, samples); + if (multipass && (pass < 6)) blockFill(rowStart); + upix = pix; + pix = temp; + temp = upix; + } + if (!multipass) break; + } + while(dis.read() != -1) System.err.println("Leftover data encountered."); + + // 24-bit color is our native format + if (colorType == 2 || colorType == 6) { + data = (int[])pixels; + if (colorType == 2) { + for(int i=0; i> depth) << (8 - depth); + data[i] = + (alpha << 24) | + (val << 16) | + (val << 8) | + val; + } + } + } + + } + + private void insertGreyPixels(int pix[], int offset, int samples) { + int p = pix[0]; + int ipix[] = ipixels; + int cInc = colInc[pass]; + int rs = 0; + + if (colorType == 0) { + switch (depth) { + case 1: + for (int j = 0; j < samples; j++, offset += cInc) { + if (rs != 0) rs--; + else { rs = 7; p = pix[j>>3]; } + ipix[offset] = (p>>rs) & 0x1; + } + break; + case 2: + for (int j = 0; j < samples; j++, offset += cInc) { + if (rs != 0) rs -= 2; + else { rs = 6; p = pix[j>>2]; } + ipix[offset] = (p>>rs) & 0x3; + } + break; + case 4: + for (int j = 0; j < samples; j++, offset += cInc) { + if (rs != 0) rs = 0; + else { rs = 4; p = pix[j>>1]; } + ipix[offset] = (p>>rs) & 0xf; + } + break; + case 8: + for (int j = 0; j < samples; offset += cInc) ipix[offset] = (byte) pix[j++]; + break; + case 16: + samples = samples<<1; + for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = pix[j]; + break; + default: break; + } + } else if (colorType == 4) { + if (depth == 8) { + for (int j = 0; j < samples; offset += cInc) ipix[offset] = (pix[j++]<<8) | pix[j++]; + } else { + samples = samples<<1; + for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = (pix[j]<<8) | pix[j+=2]; + } + } + } + + private void insertPalettedPixels(int pix[], int offset, int samples) { + int rs = 0; + int p = pix[0]; + byte bpix[] = bpixels; + int cInc = colInc[pass]; + + switch (depth) { + case 1: + for (int j = 0; j < samples; j++, offset += cInc) { + if (rs != 0) rs--; + else { rs = 7; p = pix[j>>3]; } + bpix[offset] = (byte) ((p>>rs) & 0x1); + } + break; + case 2: + for (int j = 0; j < samples; j++, offset += cInc) { + if (rs != 0) rs -= 2; + else { rs = 6; p = pix[j>>2]; } + bpix[offset] = (byte) ((p>>rs) & 0x3); + } + break; + case 4: + for (int j = 0; j < samples; j++, offset += cInc) { + if (rs != 0) rs = 0; + else { rs = 4; p = pix[j>>1]; } + bpix[offset] = (byte) ((p>>rs) & 0xf); + } + break; + case 8: + for (int j = 0; j < samples; j++, offset += cInc) bpix[offset] = (byte) pix[j]; + break; + } + } + + private void insertPixels(int pix[], int offset, int samples) { + switch (colorType) { + case 0: + case 4: + insertGreyPixels(pix, offset, samples); + break; + case 2: { + int j = 0; + int ipix[] = ipixels; + int cInc = colInc[pass]; + if (depth == 8) { + for (j = 0; j < samples; offset += cInc) + ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++]; + } else { + samples = samples<<1; + for (j = 0; j < samples; j += 2, offset += cInc) + ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2]; + } + break; } + case 3: + insertPalettedPixels(pix, offset, samples); + break; + case 6: { + int j = 0; + int ipix[] = ipixels; + int cInc = colInc[pass]; + if (depth == 8) { + for (j = 0; j < samples; offset += cInc) { + ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++] | + (pix[j++]<<24); + } + } else { + samples = samples<<1; + for (j = 0; j < samples; j += 2, offset += cInc) { + ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2] | + (pix[j+=2]<<24); + } + } + break; } + default: + break; + } + } + + private void blockFill(int rowStart) { + int counter; + int dw = width; + int pass = this.pass; + int w = blockWidth[pass]; + int sCol = startingCol[pass]; + int cInc = colInc[pass]; + int wInc = cInc - w; + int maxW = rowStart + dw - w; + int len; + int h = blockHeight[pass]; + int maxH = rowStart + (dw * h); + int startPos = rowStart + sCol; + counter = startPos; + + if (colorType == 3) { + byte bpix[] = bpixels; + byte pixel; + len = bpix.length; + for (; counter <= maxW;) { + int end = counter + w; + pixel = bpix[counter++]; + for (; counter < end; counter++) bpix[counter] = pixel; + counter += wInc; + } + maxW += w; + if (counter < maxW) + for (pixel = bpix[counter++]; counter < maxW; counter++) + bpix[counter] = pixel; + if (len < maxH) maxH = len; + for (counter = startPos + dw; counter < maxH; counter += dw) + System.arraycopy(bpix, startPos, bpix, counter, dw - sCol); + } else { + int ipix[] = ipixels; + int pixel; + len = ipix.length; + for (; counter <= maxW;) { + int end = counter + w; + pixel = ipix[counter++]; + for (; counter < end; counter++) + ipix[counter] = pixel; + counter += wInc; + } + maxW += w; + if (counter < maxW) + for (pixel = ipix[counter++]; counter < maxW; counter++) + ipix[counter] = pixel; + if (len < maxH) maxH = len; + for (counter = startPos + dw; counter < maxH; counter += dw) + System.arraycopy(ipix, startPos, ipix, counter, dw - sCol); + } + } + + private boolean filterRow(byte inbuf[], int pix[], int upix[], int rowFilter, int boff) { + int rowWidth = pix.length; + switch (rowFilter) { + case 0: { + for (int x = 0; x < rowWidth; x++) pix[x] = 0xff & inbuf[x]; + break; } + case 1: { + int x = 0; + for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x]; + for ( ; x < rowWidth; x++) pix[x] = 0xff & (inbuf[x] + pix[x - boff]); + break; } + case 2: { + if (upix != null) { + for (int x = 0; x < rowWidth; x++) + pix[x] = 0xff & (upix[x] + inbuf[x]); + } else { + for (int x = 0; x < rowWidth; x++) + pix[x] = 0xff & inbuf[x]; + } + break; } + case 3: { + if (upix != null) { + int x = 0; + for ( ; x < boff; x++) { + int rval = upix[x]; + pix[x] = 0xff & ((rval>>1) + inbuf[x]); + } + for ( ; x < rowWidth; x++) { + int rval = upix[x] + pix[x - boff]; + pix[x] = 0xff & ((rval>>1) + inbuf[x]); + } + } else { + int x = 0; + for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x]; + for ( ; x < rowWidth; x++) { + int rval = pix[x - boff]; + pix[x] = 0xff & ((rval>>1) + inbuf[x]); + } + } + break; } + case 4: { + if (upix != null) { + int x = 0; + for ( ; x < boff; x++) pix[x] = 0xff & (upix[x] + inbuf[x]); + for ( ; x < rowWidth; x++) { + int a, b, c, p, pa, pb, pc, rval; + a = pix[x - boff]; + b = upix[x]; + c = upix[x - boff]; + p = a + b - c; + pa = p > a ? p - a : a - p; + pb = p > b ? p - b : b - p; + pc = p > c ? p - c : c - p; + if ((pa <= pb) && (pa <= pc)) rval = a; + else if (pb <= pc) rval = b; + else rval = c; + pix[x] = 0xff & (rval + inbuf[x]); + } + } else { + int x = 0; + for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x]; + for ( ; x < rowWidth; x++) { + int rval = pix[x - boff]; + pix[x] = 0xff & (rval + inbuf[x]); + } + } + break; } + default: return false; + } + return true; + } + + // Private Data /////////////////////////////////////////////////////////////////////////////////////// + + private int target_offset = 0; + private int width = -1; + private int height = -1; + private int sigmask = 0xffff; + private Object pixels = null; + private int ipixels[] = null; + private byte bpixels[] = null; + private boolean multipass = false; + private boolean complete = false; + private boolean error = false; + + private int[] data = null; + + private InputStream underlyingStream = null; + private DataInputStream inputStream = null; + private Thread controlThread = null; + private boolean infoAvailable = false; + private int updateDelay = 750; + + // Image decoding state variables + private boolean headerFound = false; + private int compressionMethod = -1; + private int depth = -1; + private int colorType = -1; + private int filterMethod = -1; + private int interlaceMethod = -1; + private int pass = 0; + private byte palette[] = null; + private int mask = 0x0; + private int smask = 0x0; + private boolean transparency = false; + + private int chunkLength = 0; + private int chunkType = 0; + private boolean needChunkInfo = true; + + private static final int CHUNK_bKGD = 0x624B4744; // "bKGD" + private static final int CHUNK_cHRM = 0x6348524D; // "cHRM" + private static final int CHUNK_gAMA = 0x67414D41; // "gAMA" + private static final int CHUNK_hIST = 0x68495354; // "hIST" + private static final int CHUNK_IDAT = 0x49444154; // "IDAT" + private static final int CHUNK_IEND = 0x49454E44; // "IEND" + private static final int CHUNK_IHDR = 0x49484452; // "IHDR" + private static final int CHUNK_PLTE = 0x504C5445; // "PLTE" + private static final int CHUNK_pHYs = 0x70485973; // "pHYs" + private static final int CHUNK_sBIT = 0x73424954; // "sBIT" + private static final int CHUNK_tEXt = 0x74455874; // "tEXt" + private static final int CHUNK_tIME = 0x74494D45; // "tIME" + private static final int CHUNK_tRNS = 0x74524E53; // "tIME" + private static final int CHUNK_zTXt = 0x7A545874; // "zTXt" + + private static final int startingRow[] = { 0, 0, 0, 4, 0, 2, 0, 1 }; + private static final int startingCol[] = { 0, 0, 4, 0, 2, 0, 1, 0 }; + private static final int rowInc[] = { 1, 8, 8, 8, 4, 4, 2, 2 }; + private static final int colInc[] = { 1, 8, 8, 4, 4, 2, 2, 1 }; + private static final int blockHeight[] = { 1, 8, 8, 4, 4, 2, 2, 1 }; + private static final int blockWidth[] = { 1, 8, 4, 4, 2, 2, 1, 1 }; + + // Helper Classes //////////////////////////////////////////////////////////////////// + + private static class MeteredInputStream extends FilterInputStream { + int bytesLeft; + int marked; + + public MeteredInputStream(InputStream in, int size) { + super(in); + bytesLeft = size; + } + + public final int read() throws IOException { + if (bytesLeft > 0) { + int val = in.read(); + if (val != -1) bytesLeft--; + return val; + } + return -1; + } + + public final int read(byte b[]) throws IOException { + return read(b, 0, b.length); + } + + public final int read(byte b[], int off, int len) throws IOException { + if (bytesLeft > 0) { + len = (len > bytesLeft ? bytesLeft : len); + int read = in.read(b, off, len); + if (read > 0) bytesLeft -= read; + return read; + } + return -1; + } + + public final long skip(long n) throws IOException { + n = (n > bytesLeft ? bytesLeft : n); + long skipped = in.skip(n); + if (skipped > 0) bytesLeft -= skipped; + return skipped; + } + + public final int available() throws IOException { + int n = in.available(); + return (n > bytesLeft ? bytesLeft : n); + } + + public final void close() throws IOException { /* Eat this */ } + + public final void mark(int readlimit) { + marked = bytesLeft; + in.mark(readlimit); + } + + public final void reset() throws IOException { + in.reset(); + bytesLeft = marked; + } + + public final boolean markSupported() { return in.markSupported(); } + } + + /** Support class, used to eat the IDAT headers dividing up the deflated stream */ + private static class IDATEnumeration implements Enumeration { + InputStream underlyingStream; + PNG owner; + boolean firstStream = true; + + public IDATEnumeration(PNG owner) { + this.owner = owner; + this.underlyingStream = owner.underlyingStream; + } + + public Object nextElement() { + firstStream = false; + return new MeteredInputStream(underlyingStream, owner.chunkLength); + } + + public boolean hasMoreElements() { + DataInputStream dis = new DataInputStream(underlyingStream); + if (!firstStream) { + try { + int crc = dis.readInt(); + owner.needChunkInfo = false; + owner.chunkLength = dis.readInt(); + owner.chunkType = dis.readInt(); + } catch (IOException ioe) { + return false; + } + } + if (owner.chunkType == PNG.CHUNK_IDAT) return true; + return false; + } + } + +} diff --git a/src/org/xwt/translators/SVG.java b/src/org/xwt/translators/SVG.java new file mode 100644 index 0000000..9abab1d --- /dev/null +++ b/src/org/xwt/translators/SVG.java @@ -0,0 +1,159 @@ +// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.translators; +import java.util.*; + +public class SVG { + + /** Copied verbatim from the SVG specification */ + public static Hashtable colors = new Hashtable(400); + static { + colors.put("aliceblue", new Integer((240 << 16) | (248 << 8) | 255)); + colors.put("antiquewhite", new Integer((250 << 16) | (235 << 8) | 215)); + colors.put("aqua", new Integer((0 << 16) | (255 << 8) | 255)); + colors.put("aquamarine", new Integer((127 << 16) | (255 << 8) | 212)); + colors.put("azure", new Integer((240 << 16) | (255 << 8) | 255)); + colors.put("beige", new Integer((245 << 16) | (245 << 8) | 220)); + colors.put("bisque", new Integer((255 << 16) | (228 << 8) | 196)); + colors.put("black", new Integer((0 << 16) | (0 << 8) | 0)); + colors.put("blanchedalmond", new Integer((255 << 16) | (235 << 8) | 205)); + colors.put("blue", new Integer((0 << 16) | (0 << 8) | 255)); + colors.put("blueviolet", new Integer((138 << 16) | (43 << 8) | 226)); + colors.put("brown", new Integer((165 << 16) | (42 << 8) | 42)); + colors.put("burlywood", new Integer((222 << 16) | (184 << 8) | 135)); + colors.put("cadetblue", new Integer((95 << 16) | (158 << 8) | 160)); + colors.put("chartreuse", new Integer((127 << 16) | (255 << 8) | 0)); + colors.put("chocolate", new Integer((210 << 16) | (105 << 8) | 30)); + colors.put("coral", new Integer((255 << 16) | (127 << 8) | 80)); + colors.put("cornflowerblue", new Integer((100 << 16) | (149 << 8) | 237)); + colors.put("cornsilk", new Integer((255 << 16) | (248 << 8) | 220)); + colors.put("crimson", new Integer((220 << 16) | (20 << 8) | 60)); + colors.put("cyan", new Integer((0 << 16) | (255 << 8) | 255)); + colors.put("darkblue", new Integer((0 << 16) | (0 << 8) | 139)); + colors.put("darkcyan", new Integer((0 << 16) | (139 << 8) | 139)); + colors.put("darkgoldenrod", new Integer((184 << 16) | (134 << 8) | 11)); + colors.put("darkgray", new Integer((169 << 16) | (169 << 8) | 169)); + colors.put("darkgreen", new Integer((0 << 16) | (100 << 8) | 0)); + colors.put("darkgrey", new Integer((169 << 16) | (169 << 8) | 169)); + colors.put("darkkhaki", new Integer((189 << 16) | (183 << 8) | 107)); + colors.put("darkmagenta", new Integer((139 << 16) | (0 << 8) | 139)); + colors.put("darkolivegreen", new Integer((85 << 16) | (107 << 8) | 47)); + colors.put("darkorange", new Integer((255 << 16) | (140 << 8) | 0)); + colors.put("darkorchid", new Integer((153 << 16) | (50 << 8) | 204)); + colors.put("darkred", new Integer((139 << 16) | (0 << 8) | 0)); + colors.put("darksalmon", new Integer((233 << 16) | (150 << 8) | 122)); + colors.put("darkseagreen", new Integer((143 << 16) | (188 << 8) | 143)); + colors.put("darkslateblue", new Integer((72 << 16) | (61 << 8) | 139)); + colors.put("darkslategray", new Integer((47 << 16) | (79 << 8) | 79)); + colors.put("darkslategrey", new Integer((47 << 16) | (79 << 8) | 79)); + colors.put("darkturquoise", new Integer((0 << 16) | (206 << 8) | 209)); + colors.put("darkviolet", new Integer((148 << 16) | (0 << 8) | 211)); + colors.put("deeppink", new Integer((255 << 16) | (20 << 8) | 147)); + colors.put("deepskyblue", new Integer((0 << 16) | (191 << 8) | 255)); + colors.put("dimgray", new Integer((105 << 16) | (105 << 8) | 105)); + colors.put("dimgrey", new Integer((105 << 16) | (105 << 8) | 105)); + colors.put("dodgerblue", new Integer((30 << 16) | (144 << 8) | 255)); + colors.put("firebrick", new Integer((178 << 16) | (34 << 8) | 34)); + colors.put("floralwhite", new Integer((255 << 16) | (250 << 8) | 240)); + colors.put("forestgreen", new Integer((34 << 16) | (139 << 8) | 34)); + colors.put("fuchsia", new Integer((255 << 16) | (0 << 8) | 255)); + colors.put("gainsboro", new Integer((220 << 16) | (220 << 8) | 220)); + colors.put("ghostwhite", new Integer((248 << 16) | (248 << 8) | 255)); + colors.put("gold", new Integer((255 << 16) | (215 << 8) | 0)); + colors.put("goldenrod", new Integer((218 << 16) | (165 << 8) | 32)); + colors.put("gray", new Integer((128 << 16) | (128 << 8) | 128)); + colors.put("grey", new Integer((128 << 16) | (128 << 8) | 128)); + colors.put("green", new Integer((0 << 16) | (128 << 8) | 0)); + colors.put("greenyellow", new Integer((173 << 16) | (255 << 8) | 47)); + colors.put("honeydew", new Integer((240 << 16) | (255 << 8) | 240)); + colors.put("hotpink", new Integer((255 << 16) | (105 << 8) | 180)); + colors.put("indianred", new Integer((205 << 16) | (92 << 8) | 92)); + colors.put("indigo", new Integer((75 << 16) | (0 << 8) | 130)); + colors.put("ivory", new Integer((255 << 16) | (255 << 8) | 240)); + colors.put("khaki", new Integer((240 << 16) | (230 << 8) | 140)); + colors.put("lavender", new Integer((230 << 16) | (230 << 8) | 250)); + colors.put("lavenderblush", new Integer((255 << 16) | (240 << 8) | 245)); + colors.put("lawngreen", new Integer((124 << 16) | (252 << 8) | 0)); + colors.put("lemonchiffon", new Integer((255 << 16) | (250 << 8) | 205)); + colors.put("lightblue", new Integer((173 << 16) | (216 << 8) | 230)); + colors.put("lightcoral", new Integer((240 << 16) | (128 << 8) | 128)); + colors.put("lightcyan", new Integer((224 << 16) | (255 << 8) | 255)); + colors.put("lightgoldenrodyellow", new Integer((250 << 16) | (250 << 8) | 210)); + colors.put("lightgray", new Integer((211 << 16) | (211 << 8) | 211)); + colors.put("lightgreen", new Integer((144 << 16) | (238 << 8) | 144)); + colors.put("lightgrey", new Integer((211 << 16) | (211 << 8) | 211)); + colors.put("lightpink", new Integer((255 << 16) | (182 << 8) | 193)); + colors.put("lightsalmon", new Integer((255 << 16) | (160 << 8) | 122)); + colors.put("lightseagreen", new Integer((32 << 16) | (178 << 8) | 170)); + colors.put("lightskyblue", new Integer((135 << 16) | (206 << 8) | 250)); + colors.put("lightslategray", new Integer((119 << 16) | (136 << 8) | 153)); + colors.put("lightslategrey", new Integer((119 << 16) | (136 << 8) | 153)); + colors.put("lightsteelblue", new Integer((176 << 16) | (196 << 8) | 222)); + colors.put("lightyellow", new Integer((255 << 16) | (255 << 8) | 224)); + colors.put("lime", new Integer((0 << 16) | (255 << 8) | 0)); + colors.put("limegreen", new Integer((50 << 16) | (205 << 8) | 50)); + colors.put("linen", new Integer((250 << 16) | (240 << 8) | 230)); + colors.put("magenta", new Integer((255 << 16) | (0 << 8) | 255)); + colors.put("maroon", new Integer((128 << 16) | (0 << 8) | 0)); + colors.put("mediumaquamarine", new Integer((102 << 16) | (205 << 8) | 170)); + colors.put("mediumblue", new Integer((0 << 16) | (0 << 8) | 205)); + colors.put("mediumorchid", new Integer((186 << 16) | (85 << 8) | 211)); + colors.put("mediumpurple", new Integer((147 << 16) | (112 << 8) | 219)); + colors.put("mediumseagreen", new Integer((60 << 16) | (179 << 8) | 113)); + colors.put("mediumslateblue", new Integer((123 << 16) | (104 << 8) | 238)); + colors.put("mediumspringgreen", new Integer((0 << 16) | (250 << 8) | 154)); + colors.put("mediumturquoise", new Integer((72 << 16) | (209 << 8) | 204)); + colors.put("mediumvioletred", new Integer((199 << 16) | (21 << 8) | 133)); + colors.put("midnightblue", new Integer((25 << 16) | (25 << 8) | 112)); + colors.put("mintcream", new Integer((245 << 16) | (255 << 8) | 250)); + colors.put("mistyrose", new Integer((255 << 16) | (228 << 8) | 225)); + colors.put("moccasin", new Integer((255 << 16) | (228 << 8) | 181)); + colors.put("navajowhite", new Integer((255 << 16) | (222 << 8) | 173)); + colors.put("navy", new Integer((0 << 16) | (0 << 8) | 128)); + colors.put("oldlace", new Integer((253 << 16) | (245 << 8) | 230)); + colors.put("olive", new Integer((128 << 16) | (128 << 8) | 0)); + colors.put("olivedrab", new Integer((107 << 16) | (142 << 8) | 35)); + colors.put("orange", new Integer((255 << 16) | (165 << 8) | 0)); + colors.put("orangered", new Integer((255 << 16) | (69 << 8) | 0)); + colors.put("orchid", new Integer((218 << 16) | (112 << 8) | 214)); + colors.put("palegoldenrod", new Integer((238 << 16) | (232 << 8) | 170)); + colors.put("palegreen", new Integer((152 << 16) | (251 << 8) | 152)); + colors.put("paleturquoise", new Integer((175 << 16) | (238 << 8) | 238)); + colors.put("palevioletred", new Integer((219 << 16) | (112 << 8) | 147)); + colors.put("papayawhip", new Integer((255 << 16) | (239 << 8) | 213)); + colors.put("peachpuff", new Integer((255 << 16) | (218 << 8) | 185)); + colors.put("peru", new Integer((205 << 16) | (133 << 8) | 63)); + colors.put("pink", new Integer((255 << 16) | (192 << 8) | 203)); + colors.put("plum", new Integer((221 << 16) | (160 << 8) | 221)); + colors.put("powderblue", new Integer((176 << 16) | (224 << 8) | 230)); + colors.put("purple", new Integer((128 << 16) | (0 << 8) | 128)); + colors.put("red", new Integer((255 << 16) | (0 << 8) | 0)); + colors.put("rosybrown", new Integer((188 << 16) | (143 << 8) | 143)); + colors.put("royalblue", new Integer((65 << 16) | (105 << 8) | 225)); + colors.put("saddlebrown", new Integer((139 << 16) | (69 << 8) | 19)); + colors.put("salmon", new Integer((250 << 16) | (128 << 8) | 114)); + colors.put("sandybrown", new Integer((244 << 16) | (164 << 8) | 96)); + colors.put("seagreen", new Integer((46 << 16) | (139 << 8) | 87)); + colors.put("seashell", new Integer((255 << 16) | (245 << 8) | 238)); + colors.put("sienna", new Integer((160 << 16) | (82 << 8) | 45)); + colors.put("silver", new Integer((192 << 16) | (192 << 8) | 192)); + colors.put("skyblue", new Integer((135 << 16) | (206 << 8) | 235)); + colors.put("slateblue", new Integer((106 << 16) | (90 << 8) | 205)); + colors.put("slategray", new Integer((112 << 16) | (128 << 8) | 144)); + colors.put("slategrey", new Integer((112 << 16) | (128 << 8) | 144)); + colors.put("snow", new Integer((255 << 16) | (250 << 8) | 250)); + colors.put("springgreen", new Integer((0 << 16) | (255 << 8) | 127)); + colors.put("steelblue", new Integer((70 << 16) | (130 << 8) | 180)); + colors.put("tan", new Integer((210 << 16) | (180 << 8) | 140)); + colors.put("teal", new Integer((0 << 16) | (128 << 8) | 128)); + colors.put("thistle", new Integer((216 << 16) | (191 << 8) | 216)); + colors.put("tomato", new Integer((255 << 16) | (99 << 8) | 71)); + colors.put("turquoise", new Integer((64 << 16) | (224 << 8) | 208)); + colors.put("violet", new Integer((238 << 16) | (130 << 8) | 238)); + colors.put("wheat", new Integer((245 << 16) | (222 << 8) | 179)); + colors.put("white", new Integer((255 << 16) | (255 << 8) | 255)); + colors.put("whitesmoke", new Integer((245 << 16) | (245 << 8) | 245)); + colors.put("yellow", new Integer((255 << 16) | (255 << 8) | 0)); + colors.put("yellowgreen", new Integer((154 << 16) | (205 << 8) | 50)); + } + +} diff --git a/src/org/xwt/util/CAB.java b/src/org/xwt/util/CAB.java new file mode 100644 index 0000000..e178135 --- /dev/null +++ b/src/org/xwt/util/CAB.java @@ -0,0 +1,421 @@ +// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] +package org.xwt.shoehorn3; + +import java.io.*; +import java.util.*; +import java.util.zip.*; +import java.math.*; + +/** Reads a CAB file structure */ +public class CAB { + + /** reads a CAB file, parses it, and returns an InputStream representing the named file */ + public static InputStream getFileInputStream(InputStream is, String fileName, boolean mscfAlreadyConsumed) throws IOException { + DataInputStream dis = new DataInputStream(is); + CFHEADER h = new CFHEADER(); + h.read(dis, mscfAlreadyConsumed); + + for(int i=0; i limit) len = limit; + if (limit == 0) return -1; + int ret = super.read(b, off, len); + limit -= ret; + return ret; + } + } + + /** Encapsulates a CFHEADER entry */ + public static class CFHEADER { + byte[] reserved1 = new byte[4]; // reserved + int fileSize = 0; // size of this cabinet file in bytes + byte[] reserved2 = new byte[4]; // reserved + int offsetOfFirstCFFILEEntry; // offset of the first CFFILE entry + byte[] reserved3 = new byte[4]; // reserved + byte versionMinor = 3; // cabinet file format version, minor + byte versionMajor = 1; // cabinet file format version, major + boolean prevCAB = false; // true iff there is a cabinet before this one in a sequence + boolean nextCAB = false; // true iff there is a cabinet after this one in a sequence + boolean hasReserved = false; // true iff the cab has per-{cabinet, folder, block} reserved areas + int setID = 0; // must be the same for all cabinets in a set + int indexInCabinetSet = 0; // number of this cabinet file in a set + byte perCFFOLDERReservedSize = 0; // (optional) size of per-folder reserved area + byte perDatablockReservedSize = 0; // (optional) size of per-datablock reserved area + byte[] perCabinetReservedArea = null; // per-cabinet reserved area + String previousCabinet = null; // name of previous cabinet file in a set + String previousDisk = null; // name of previous disk in a set + String nextCabinet = null; // name of next cabinet in a set + String nextDisk = null; // name of next disk in a set + + CFFOLDER[] folders = new CFFOLDER[0]; + CFFILE[] files = new CFFILE[0]; + + int readCFFOLDERs = 0; // the number of folders read in so far + int readCFFILEs = 0; // the number of folders read in so far + + public CFHEADER() { } + + public void print(PrintStream ps) { + ps.println("CAB CFFILE CFHEADER v" + ((int)versionMajor) + "." + ((int)versionMinor)); + ps.println(" total file size = " + fileSize); + ps.println(" offset of first file = " + offsetOfFirstCFFILEEntry); + ps.println(" total folders = " + folders.length); + ps.println(" total files = " + files.length); + ps.println(" flags = 0x" + + Integer.toString((prevCAB ? 0x1 : 0x0) | + (nextCAB ? 0x2 : 0x0) | + (hasReserved ? 0x4 : 0x0), 16) + " [ " + + (prevCAB ? "prev " : "") + + (nextCAB ? "next " : "") + + (hasReserved ? "reserve_present " : "") + "]"); + ps.println(" set id = " + setID); + ps.println(" index in set = " + indexInCabinetSet); + ps.println(" header reserved area #1 =" + + " 0x" + Integer.toString(reserved1[0], 16) + + " 0x" + Integer.toString(reserved1[1], 16) + + " 0x" + Integer.toString(reserved1[2], 16) + + " 0x" + Integer.toString(reserved1[3], 16)); + ps.println(" header reserved area #2 =" + + " 0x" + Integer.toString(reserved2[0], 16) + + " 0x" + Integer.toString(reserved2[1], 16) + + " 0x" + Integer.toString(reserved2[2], 16) + + " 0x" + Integer.toString(reserved2[3], 16)); + ps.println(" header reserved area #3 =" + + " 0x" + Integer.toString(reserved3[0], 16) + + " 0x" + Integer.toString(reserved3[1], 16) + + " 0x" + Integer.toString(reserved3[2], 16) + + " 0x" + Integer.toString(reserved3[3], 16)); + if (hasReserved) { + if (perCabinetReservedArea != null) { + ps.print(" per-cabinet reserved area = "); + for(int i=0; i 0) + dis.readFully(perCabinetReservedArea); + } + if (prevCAB) { + previousCabinet = readZeroTerminatedString(dis); + previousDisk = readZeroTerminatedString(dis); + } + if (nextCAB) { + nextCabinet = readZeroTerminatedString(dis); + nextDisk = readZeroTerminatedString(dis); + } + } + } + + /** Encapsulates a CFFOLDER entry */ + public static class CFFOLDER { + int firstBlockOffset = 0; // offset of first data block within this folder + int numBlocks = 0; // number of data blocks + int compressionType = 0; // compression type for this folder + byte[] reservedArea = null; // per-folder reserved area + int indexInCFHEADER = 0; // our index in CFHEADER.folders + Vector files = new Vector(); + + private CFHEADER header = null; + + public CFFOLDER(CFHEADER header) { this.header = header; } + + public String toString() { + return "[ CAB CFFOLDER, " + numBlocks + " data blocks, compression type " + + (compressionType == 1 ? "MSZIP" : "unknown") + + ", " + reservedArea.length + " bytes of reserved data ]"; + } + + public void read(DataInputStream dis) throws IOException { + firstBlockOffset = readLittleInt(dis); + numBlocks = readLittleShort(dis); + compressionType = readLittleShort(dis); + reservedArea = new byte[header.perCFFOLDERReservedSize]; + if (reservedArea.length > 0) dis.readFully(reservedArea); + indexInCFHEADER = header.readCFFOLDERs++; + header.folders[indexInCFHEADER] = this; + } + } + + /** Encapsulates a CFFILE entry */ + public static class CFFILE { + int fileSize = 0; // size of this file + int uncompressedOffsetInCFFOLDER = 0; // offset of this file within the folder, not accounting for compression + int folderIndex = 0; // index of the CFFOLDER we belong to + Date date = null; // modification date + int attrs = 0; // attrs + boolean readOnly = false; // read-only flag + boolean hidden = false; // hidden flag + boolean system = false; // system flag + boolean arch = false; // archive flag + boolean runAfterExec = false; // true if file should be run during extraction + boolean UTFfileName = false; // true if filename is UTF-encoded + String fileName = null; // filename + int indexInCFHEADER = 0; // our index in CFHEADER.files + CFFOLDER folder = null; // the folder we belong to + private CFHEADER header = null; + File myFile; + + public CFFILE(CFHEADER header) { this.header = header; } + + public CFFILE(File f, String pathName) throws IOException { + fileSize = (int)f.length(); + folderIndex = 0; + date = new java.util.Date(f.lastModified()); + fileName = pathName; + myFile = f; + } + + public String toString() { + return "[ CAB CFFILE: " + fileName + ", " + fileSize + " bytes [ " + + (readOnly ? "readonly " : "") + + (system ? "system " : "") + + (hidden ? "hidden " : "") + + (arch ? "arch " : "") + + (runAfterExec ? "run_after_exec " : "") + + (UTFfileName ? "UTF_filename " : "") + + "]"; + } + + public void read(DataInputStream dis) throws IOException { + fileSize = readLittleInt(dis); + uncompressedOffsetInCFFOLDER = readLittleInt(dis); + folderIndex = readLittleShort(dis); + readLittleShort(dis); // FIXME: date + readLittleShort(dis); // FIXME: time + attrs = readLittleShort(dis); + readOnly = (attrs & 0x1) != 0; + hidden = (attrs & 0x2) != 0; + system = (attrs & 0x4) != 0; + arch = (attrs & 0x20) != 0; + runAfterExec = (attrs & 0x40) != 0; + UTFfileName = (attrs & 0x80) != 0; + fileName = readZeroTerminatedString(dis); + + indexInCFHEADER = header.readCFFILEs++; + header.files[indexInCFHEADER] = this; + folder = header.folders[folderIndex]; + folder.files.addElement(this); + } + } + + + + + // Compressing Input and Output Streams /////////////////////////////////////////////// + + /** an InputStream that decodes CFDATA blocks belonging to a CFFOLDER */ + private static class CFFOLDERInputStream extends InputStream { + CFFOLDER folder; + DataInputStream dis; + InputStream iis = null; + + byte[] compressed = new byte[128 * 1024]; + byte[] uncompressed = new byte[256 * 1024]; + + public CFFOLDERInputStream(CFFOLDER f, DataInputStream dis) { + this.folder = f; + this.dis = dis; + } + + InputStream readBlock() throws IOException { + int checksum = readLittleInt(dis); + int compressedBytes = readLittleShort(dis); + int unCompressedBytes = readLittleShort(dis); + byte[] reserved = new byte[/*folder.header.perDatablockReservedSize*/0]; + if (reserved.length > 0) dis.readFully(reserved); + if (dis.readByte() != 0x43) throw new CABException("malformed block header"); + if (dis.readByte() != 0x4B) throw new CABException("malformed block header"); + + dis.readFully(compressed, 0, compressedBytes - 2); + + Inflater i = new Inflater(true); + i.setInput(compressed, 0, compressedBytes - 2); + + if (unCompressedBytes > uncompressed.length) uncompressed = new byte[unCompressedBytes]; + try { i.inflate(uncompressed, 0, uncompressed.length); + } catch (DataFormatException dfe) { + dfe.printStackTrace(); + throw new CABException(dfe.toString()); + } + return new ByteArrayInputStream(uncompressed, 0, unCompressedBytes); + } + + public int available() throws IOException { return iis == null ? 0 : iis.available(); } + public void close() throws IOException { iis.close(); } + public void mark(int i) { } + public boolean markSupported() { return false; } + public void reset() { } + + public long skip(long l) throws IOException { + if (iis == null) iis = readBlock(); + int ret = 0; + while (l > ret) { + long numread = iis.skip(l - ret); + if (numread == 0 || numread == -1) iis = readBlock(); + else ret += numread; + } + return ret; + } + + public int read(byte[] b, int off, int len) throws IOException { + if (iis == null) iis = readBlock(); + int ret = 0; + while (len > ret) { + int numread = iis.read(b, off + ret, len - ret); + if (numread == 0 || numread == -1) iis = readBlock(); + else ret += numread; + } + return ret; + } + + public int read() throws IOException { + if (iis == null) iis = readBlock(); + int ret = iis.read(); + if (ret == -1) { + iis = readBlock(); + ret = iis.read(); + } + return ret; + } + } + + + + // Misc Stuff ////////////////////////////////////////////////////////////// + + public static String readZeroTerminatedString(DataInputStream dis) throws IOException { + int numBytes = 0; + byte[] b = new byte[256]; + while(true) { + byte next = dis.readByte(); + if (next == 0x0) return new String(b, 0, numBytes); + b[numBytes++] = next; + } + } + + public static int readLittleInt(DataInputStream dis) throws IOException { + int lowest = (int)(dis.readByte() & 0xff); + int low = (int)(dis.readByte() & 0xff); + int high = (int)(dis.readByte() & 0xff); + int highest = (int)(dis.readByte() & 0xff); + return (highest << 24) | (high << 16) | (low << 8) | lowest; + } + + public static int readLittleShort(DataInputStream dis) throws IOException { + int low = (int)(dis.readByte() & 0xff); + int high = (int)(dis.readByte() & 0xff); + return (high << 8) | low; + } + + public static class CABException extends IOException { + public CABException(String s) { super(s); } + } + + + /** scratch space for isToByteArray() */ + static byte[] workspace = new byte[16 * 1024]; + + /** Trivial method to completely read an InputStream */ + public static synchronized byte[] isToByteArray(InputStream is) throws IOException { + int pos = 0; + while (true) { + int numread = is.read(workspace, pos, workspace.length - pos); + if (numread == -1) break; + else if (pos + numread < workspace.length) pos += numread; + else { + pos += numread; + byte[] temp = new byte[workspace.length * 2]; + System.arraycopy(workspace, 0, temp, 0, workspace.length); + workspace = temp; + } + } + byte[] ret = new byte[pos]; + System.arraycopy(workspace, 0, ret, 0, pos); + return ret; + } + + +} + diff --git a/src/org/xwt/util/Hash.java b/src/org/xwt/util/Hash.java index b9626d1..30d07ed 100644 --- a/src/org/xwt/util/Hash.java +++ b/src/org/xwt/util/Hash.java @@ -70,7 +70,7 @@ public class Hash { } public void remove(Object k1) { remove(k1, null); } - public void remove(Object k1, Object k2) { put(k1, k2, null); } + public void remove(Object k1, Object k2) { put_(k1, k2, null); } private void rehash() { Object[] oldkeys1 = keys1; @@ -83,7 +83,7 @@ public class Hash { usedslots = 0; for(int i=0; i vals.length) rehash(); int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode()); int dest = Math.abs(hash) % vals.length; -- 1.7.10.4