r34539 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r34538‎ | r34539 | r34540 >
Date:22:59, 9 May 2008
Author:river
Status:old
Tags:
Comment:
- modular reader/writer support for non-ImageIO handlers
- TIFF support
- remove alpha channel from source if not supported by destination (e.g. JPEG)
- use chunked-style output format so output size needn't be known at start of writing
- write output directly to client instead of buffering in memory
Modified paths:
  • /trunk/imgserv/imgserv-client/imgserv.cxx (modified) (history)
  • /trunk/imgserv/imgserv-server/lib/pngds.jar (added) (history)
  • /trunk/imgserv/imgserv-server/mkdist.sh (modified) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/Configuration.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/ImageClient.java (modified) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/ImageClientInputStream.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/ImageClientOutputStream.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/ImageIOImageHandler.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/ImageTranscoder.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/ImageTranscoderException.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/MultiFormatImageFactory.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/MultiFormatImageReader.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/MultiFormatImageWriter.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/SVGImageHandler.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/SVGRasterizer.java (added) (history)
  • /trunk/imgserv/imgserv-server/src/imgservserver/TIFFImageHandler.java (added) (history)

Diff [purge]

Index: trunk/imgserv/imgserv-server/mkdist.sh
@@ -46,9 +46,14 @@
4747 permission java.io.FilePermission "\${here}/lib/-", "read";
4848 };
4949 EOF
50 -java -Djava.security.manager -Djava.security.policy=imgserv.policy -jar imgserv-server.jar "\$@"
 50+exec java -Djava.security.manager -Djava.security.policy=imgserv.policy -jar imgserv-server.jar "\$@"
5151 __EOF__
5252
 53+cat >bin/run.sh <<__EOF__
 54+#! /bin/sh
 55+exec nohup bin/start.sh "\$@" &
 56+__EOF__
 57+
5358 chmod 755 bin/start.sh
5459 cd ..
5560 tar cf imgserv-server-$vers.tar imgserv-server-$vers
Index: trunk/imgserv/imgserv-server/lib/pngds.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/imgserv/imgserv-server/lib/pngds.jar
___________________________________________________________________
Added: svn:mime-type
5661 + application/octet-stream
Index: trunk/imgserv/imgserv-server/src/imgservserver/SVGImageHandler.java
@@ -0,0 +1,46 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.awt.image.BufferedImage;
 15+import java.awt.image.RenderedImage;
 16+import java.io.ByteArrayInputStream;
 17+import java.io.OutputStream;
 18+import org.apache.batik.transcoder.TranscoderException;
 19+
 20+public class SVGImageHandler implements MultiFormatImageReader {
 21+ int width = 0, height = 0;
 22+
 23+ public SVGImageHandler(String format) {
 24+ }
 25+
 26+ public void setSizeHint(int width, int height) {
 27+ this.width = width;
 28+ this.height = height;
 29+ }
 30+
 31+ public RenderedImage readImage(byte[] data)
 32+ throws ImageTranscoderException {
 33+ SVGRasterizer r = new SVGRasterizer(new ByteArrayInputStream(data));
 34+
 35+ if (height > 0)
 36+ r.setImageHeight(height);
 37+ if (width > 0)
 38+ r.setImageWidth(width);
 39+
 40+ try {
 41+ return r.createBufferedImage();
 42+ } catch (TranscoderException e) {
 43+ throw new ImageTranscoderException(
 44+ "Cannot rasterize SVG image", e);
 45+ }
 46+ }
 47+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/ImageClient.java
@@ -10,37 +10,18 @@
1111
1212 package imgservserver;
1313
14 -import java.awt.Graphics2D;
15 -import java.awt.geom.AffineTransform;
16 -import java.awt.image.BufferedImage;
17 -import java.io.BufferedInputStream;
18 -import java.io.ByteArrayInputStream;
19 -import java.io.ByteArrayOutputStream;
2014 import java.io.File;
2115 import java.io.FileInputStream;
2216 import java.io.FileOutputStream;
2317 import java.io.IOException;
2418 import java.io.OutputStream;
2519 import java.net.Socket;
26 -import java.util.Iterator;
27 -import javax.imageio.ImageIO;
28 -import javax.imageio.ImageReader;
29 -import javax.imageio.ImageWriter;
30 -import javax.imageio.stream.MemoryCacheImageInputStream;
31 -import javax.imageio.stream.MemoryCacheImageOutputStream;
32 -import org.apache.batik.transcoder.Transcoder;
33 -import org.apache.batik.transcoder.TranscoderException;
34 -import org.apache.batik.transcoder.TranscoderInput;
35 -import org.apache.batik.transcoder.TranscoderOutput;
36 -import org.apache.batik.transcoder.TranscodingHints;
37 -import org.apache.batik.transcoder.image.JPEGTranscoder;
38 -import org.apache.batik.transcoder.image.PNGTranscoder;
39 -import org.apache.batik.transcoder.image.TIFFTranscoder;
4020 import pngds.PNGResizer;
4121
4222 public class ImageClient extends Thread {
4323 Socket client;
44 - BufferedInputStream reader;
 24+ ImageClientInputStream reader;
 25+ ImageClientOutputStream writer;
4526 Configuration config;
4627 int pngcount = 0;
4728
@@ -52,7 +33,7 @@
5334 handleRequest();
5435 } catch (Exception e) {
5536 System.out.printf("%% Error occurred handling client request: %s\n",
56 - e.getMessage());
 37+ e.toString());
5738 return;
5839 } finally {
5940 try {
@@ -62,7 +43,8 @@
6344 }
6445
6546 private void handleRequest() throws IOException {
66 - reader = new BufferedInputStream(client.getInputStream());
 47+ reader = new ImageClientInputStream(client.getInputStream());
 48+ writer = new ImageClientOutputStream(client);
6749
6850 /*
6951 * The protocol request format is like this:
@@ -78,8 +60,8 @@
7961 *
8062 * A successful reply looks like this:
8163 *
82 - * OK 2345
83 - * <2345 bytes of data>
 64+ * OK
 65+ * hhhh<hhhh bytes of data>hhhh<hhhh bytes of data>[etc]
8466 */
8567
8668 String informat = null, outformat = null;
@@ -87,7 +69,7 @@
8870 int width = -1, height = -1;
8971
9072 for (;;) {
91 - String line = readLine();
 73+ String line = reader.readLine();
9274 String[] args = line.split(" ");
9375
9476 if (args.length < 2) {
@@ -151,38 +133,40 @@
152134 break;
153135 }
154136
155 - byte[] out;
 137+ writer.setChunked(true);
 138+ writer.setHeader("OK\r\n");
 139+
 140+ ImageTranscoder tr = new ImageTranscoder();
156141
157 - if (informat.equals("svg")) {
158 - out = transcodeSVG(informat, outformat, width, height, data);
159 - } else {
160 - out = transcodeRaster(informat, outformat, width, height, data);
 142+ try {
 143+ /*
 144+ if (informat.equals("svg")) {
 145+ tr.transcodeSVG(informat, outformat, width, height, data, writer);
 146+ } else {
 147+ tr.transcodeRaster(informat, outformat, width, height, data, writer);
 148+ }
 149+ */
 150+ tr.transcode(informat, outformat, width, height, data, writer);
 151+ } catch (ImageTranscoderException e) {
 152+ String error = e.getMessage();
 153+ Throwable cause = e;
 154+ while ((cause = cause.getCause()) != null) {
 155+ error = error + ": " + cause.getMessage();
 156+ }
 157+
 158+ writer.cancel();
 159+ String status = "ERROR " + error + "\r\n";
 160+ writer.write(status.getBytes());
 161+
 162+ System.err.printf("%% [client: %s] %s\n", client.getRemoteSocketAddress().toString(),
 163+ error);
161164 }
162165
163 - String status = "OK " + out.length + "\r\n";
164 - OutputStream clout = client.getOutputStream();
165 - clout.write(status.getBytes());
166 - clout.write(out);
 166+ writer.close();
167167 reader.close();
168168 client.close();
169169 }
170170
171 - private String readLine() throws IOException {
172 - StringBuilder b = new StringBuilder();
173 - int i;
174 -
175 - while ((i = reader.read()) != -1) {
176 - char c = (char) i;
177 - if (c == '\r')
178 - continue;
179 - if (c == '\n')
180 - return b.toString();
181 - b.append(c);
182 - }
183 -
184 - throw new IOException("Unexpected end of stream looking for \\r\\n");
185 - }
186 -
187171 private int transcodepngds(int len, int width, int height)
188172 throws IOException {
189173 String inp = config.getTmpdir() + "/pngds_" + Thread.currentThread().getId()
@@ -242,134 +226,4 @@
243227
244228 return ret;
245229 }
246 -
247 - private byte[] transcodeRaster(String informat, String outformat,
248 - int width, int height,
249 - byte[] data) throws IOException {
250 - ByteArrayInputStream bis = new ByteArrayInputStream(data);
251 - Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(informat);
252 - if (!readers.hasNext()) {
253 - System.out.printf("%% No reader found for format \"%s\".\n",
254 - informat);
255 - return null;
256 - }
257 -
258 - ImageReader imgr = readers.next();
259 - imgr.setInput(new MemoryCacheImageInputStream(
260 - new ByteArrayInputStream(data)));
261 - BufferedImage img = imgr.read(0), dest;
262 -
263 - if (width != -1 || height != -1) {
264 - if (width == -1)
265 - width = (int) (img.getWidth() * ((double)height / img.getHeight()));
266 - if (height == -1)
267 - height = (int) (img.getHeight() * ((double)width / img.getWidth()));
268 - dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
269 - Graphics2D g = dest.createGraphics();
270 - AffineTransform at = AffineTransform.getScaleInstance(
271 - (double)width/img.getWidth(),
272 - (double)height/img.getHeight());
273 - g.drawRenderedImage(img, at);
274 - } else {
275 - dest = img;
276 - }
277 -
278 - Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(outformat);
279 - if (!writers.hasNext()) {
280 - System.out.printf("%% No writer found for format \"%s\".\n",
281 - outformat);
282 - return null;
283 - }
284 -
285 - ByteArrayOutputStream outs = new ByteArrayOutputStream();
286 - ImageWriter imgw = writers.next();
287 - imgw.setOutput(new MemoryCacheImageOutputStream(outs));
288 - imgw.write(dest);
289 -
290 - byte[] out = outs.toByteArray();
291 - return out;
292 - }
293 -
294 - static class NamedTranscoder {
295 - Class clas;
296 - String name;
297 -
298 - public NamedTranscoder(String name, Class clas) {
299 - this.name = name;
300 - this.clas = clas;
301 - }
302 -
303 - public Transcoder getInstance()
304 - throws InstantiationException, IllegalAccessException {
305 - return (Transcoder) clas.newInstance();
306 - }
307 -
308 - public String getName() {
309 - return name;
310 - }
311 - }
312 -
313 - static final NamedTranscoder[] namedTranscoders = {
314 - new NamedTranscoder("jpeg", JPEGTranscoder.class),
315 - new NamedTranscoder("png", PNGTranscoder.class),
316 - new NamedTranscoder("tiff", TIFFTranscoder.class),
317 - };
318 -
319 - private byte[] transcodeSVG(String informat, String outformat,
320 - int width, int height,
321 - byte[] data) throws IOException {
322 - Transcoder coder = null;
323 -
324 - for (int i = 0; i < namedTranscoders.length; ++i) {
325 - if (namedTranscoders[i].getName().equals(outformat)) {
326 - try {
327 - coder = namedTranscoders[i].getInstance();
328 - } catch (Exception e) {
329 - System.out.printf("%% Exception trying to instantiate transcoder \"%s\": %s.\n",
330 - namedTranscoders[i].getName(), e.getMessage());
331 - return null;
332 - }
333 - break;
334 - }
335 - }
336 -
337 - if (coder == null) {
338 - System.out.printf("%% No SVG transcoder found for format \"%s\".\n",
339 - outformat);
340 - return null;
341 - }
342 -
343 - if (coder instanceof JPEGTranscoder)
344 - ((JPEGTranscoder) coder).addTranscodingHint(JPEGTranscoder.KEY_QUALITY,
345 - new Float(.8));
346 -
347 - TranscodingHints.Key kwidth, kheight;
348 - try {
349 - kwidth = (TranscodingHints.Key)
350 - coder.getClass().getField("KEY_WIDTH").get(null);
351 - kheight = (TranscodingHints.Key)
352 - coder.getClass().getField("KEY_WIDTH").get(null);
353 - } catch (Exception e) {
354 - System.out.printf("%% Couldn't extract width or height keys from transcoder.\n");
355 - return null;
356 - }
357 -
358 - if (width > 0)
359 - coder.addTranscodingHint(kwidth, new Float(width));
360 - if (height > 0)
361 - coder.addTranscodingHint(kheight, new Float(height));
362 -
363 - ByteArrayOutputStream out = new ByteArrayOutputStream();
364 - TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(data));
365 - TranscoderOutput output = new TranscoderOutput(out);
366 -
367 - try {
368 - coder.transcode(input, output);
369 - } catch (Exception e) {
370 - System.out.printf("%% Exception transcoding SVG image: %s\n",
371 - e.toString());
372 - return null;
373 - }
374 - return out.toByteArray();
375 - }
376230 }
Index: trunk/imgserv/imgserv-server/src/imgservserver/MultiFormatImageWriter.java
@@ -0,0 +1,20 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.awt.image.RenderedImage;
 15+import java.io.OutputStream;
 16+
 17+public interface MultiFormatImageWriter {
 18+ public void writeImage(RenderedImage img, OutputStream out) throws ImageTranscoderException;
 19+ public int getPreferredImageType();
 20+ public boolean hasAlpha();
 21+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/ImageTranscoderException.java
@@ -0,0 +1,21 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+public class ImageTranscoderException extends Exception {
 15+ public ImageTranscoderException(String message) {
 16+ super(message);
 17+ }
 18+
 19+ public ImageTranscoderException(String message, Exception nested) {
 20+ super(message, nested);
 21+ }
 22+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/Configuration.java
@@ -0,0 +1,67 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.net.InetAddress;
 15+import java.util.Properties;
 16+
 17+public class Configuration {
 18+ static final int DEFAULT_PORT = 8765;
 19+
 20+ boolean usepngds = false;
 21+ String tmpdir = null;
 22+ int port = DEFAULT_PORT;
 23+ InetAddress address;
 24+
 25+ public Configuration(Properties p) {
 26+ String x;
 27+
 28+ if ((x = p.getProperty("tmpdir")) != null)
 29+ tmpdir = x;
 30+
 31+ if ((x = p.getProperty("usepngds")) != null)
 32+ usepngds = x.equals("true");
 33+
 34+ if ((x = p.getProperty("port")) != null)
 35+ port = Integer.parseInt(x);
 36+
 37+ if ((x = p.getProperty("bind")) != null) {
 38+ try {
 39+ address = InetAddress.getByName(x);
 40+ } catch (Exception e) {
 41+ System.err.printf("%% Invalid bind address \"%s\": %s.\n",
 42+ x);
 43+ System.exit(1);
 44+ }
 45+ }
 46+
 47+ if (tmpdir == null && usepngds) {
 48+ System.err.printf("%% tmpdir must be set when using pngds.\n");
 49+ System.exit(1);
 50+ }
 51+ }
 52+
 53+ public boolean getUsepngds() {
 54+ return usepngds;
 55+ }
 56+
 57+ public int getPort() {
 58+ return port;
 59+ }
 60+
 61+ public String getTmpdir() {
 62+ return tmpdir;
 63+ }
 64+
 65+ public InetAddress getBindAddress() {
 66+ return address;
 67+ }
 68+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/ImageTranscoder.java
@@ -0,0 +1,80 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+/* @(#) $Id$ */
 10+package imgservserver;
 11+
 12+import java.awt.Color;
 13+import java.awt.Graphics2D;
 14+import java.awt.Transparency;
 15+import java.awt.geom.AffineTransform;
 16+import java.awt.image.BufferedImage;
 17+import java.awt.image.RenderedImage;
 18+import java.io.OutputStream;
 19+
 20+public class ImageTranscoder {
 21+ public void transcode(String informat, String outformat,
 22+ int width, int height,
 23+ byte[] data, OutputStream out)
 24+ throws ImageTranscoderException {
 25+ MultiFormatImageReader reader = MultiFormatImageFactory.getReader(informat);
 26+ MultiFormatImageWriter writer = MultiFormatImageFactory.getWriter(outformat);
 27+
 28+ reader.setSizeHint(width, height);
 29+ RenderedImage img = reader.readImage(data);
 30+ RenderedImage result;
 31+
 32+ /*
 33+ * Some readers (e.g. SVG) can render the input at the size we want if
 34+ * given a size hint. If so, we don't need to scale the image here.
 35+ */
 36+ if ((width != -1 && img.getWidth() != width)
 37+ || (height != -1 && img.getHeight() != height))
 38+
 39+ {
 40+ BufferedImage dest;
 41+
 42+ if (width == -1)
 43+ width = (int) (img.getWidth() * ((double) height / img.getHeight()));
 44+
 45+ if (height == -1)
 46+ height = (int) (img.getHeight() * ((double) width / img.getWidth()));
 47+
 48+ dest = new BufferedImage(width, height, writer.getPreferredImageType());
 49+ Graphics2D g = dest.createGraphics();
 50+ AffineTransform at = AffineTransform.getScaleInstance(
 51+ (double) width / img.getWidth(),
 52+ (double) height / img.getHeight());
 53+ g.drawRenderedImage(img, at);
 54+ result = dest;
 55+ } else {
 56+ /*
 57+ * If we don't scale the image, there's no chance to convert the image
 58+ * to the preferred image type of the output format. So we remove
 59+ * any alpha channel here if it's needed.
 60+ */
 61+ if (!writer.hasAlpha() && img.getColorModel().getTransparency() != Transparency.OPAQUE)
 62+ result = removeAlpha(img);
 63+ else
 64+ result = img;
 65+ }
 66+
 67+ writer.writeImage(result, out);
 68+ }
 69+
 70+ public RenderedImage removeAlpha(RenderedImage img) {
 71+ int w = img.getWidth();
 72+ int h = img.getHeight();
 73+ BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
 74+ Graphics2D g = ret.createGraphics();
 75+ g.setColor(Color.WHITE);
 76+ g.fillRect(0,0,w,h);
 77+ g.drawRenderedImage(img, null);
 78+ g.dispose();
 79+ return ret;
 80+ }
 81+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/MultiFormatImageReader.java
@@ -0,0 +1,18 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.awt.image.RenderedImage;
 15+
 16+public interface MultiFormatImageReader {
 17+ public void setSizeHint(int width, int height);
 18+ public RenderedImage readImage(byte[] data) throws ImageTranscoderException;
 19+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/TIFFImageHandler.java
@@ -0,0 +1,65 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.awt.image.BufferedImage;
 15+import java.awt.image.RenderedImage;
 16+import java.io.ByteArrayInputStream;
 17+import java.io.OutputStream;
 18+import org.apache.batik.ext.awt.image.codec.tiff.TIFFDecodeParam;
 19+import org.apache.batik.ext.awt.image.codec.tiff.TIFFEncodeParam;
 20+import org.apache.batik.ext.awt.image.codec.tiff.TIFFImageDecoder;
 21+import org.apache.batik.ext.awt.image.codec.tiff.TIFFImageEncoder;
 22+import org.apache.batik.ext.awt.image.codec.util.MemoryCacheSeekableStream;
 23+
 24+public class TIFFImageHandler
 25+implements MultiFormatImageReader, MultiFormatImageWriter {
 26+
 27+ public TIFFImageHandler(String format) {
 28+ }
 29+
 30+ public void setSizeHint(int w, int h) {
 31+ }
 32+
 33+ public int getPreferredImageType() {
 34+ return BufferedImage.TYPE_4BYTE_ABGR;
 35+ }
 36+
 37+ public boolean hasAlpha() {
 38+ return true;
 39+ }
 40+
 41+ public RenderedImage readImage(byte[] data)
 42+ throws ImageTranscoderException {
 43+ TIFFImageDecoder coder;
 44+ TIFFDecodeParam param = new TIFFDecodeParam();
 45+ MemoryCacheSeekableStream in = new MemoryCacheSeekableStream(
 46+ new ByteArrayInputStream(data));
 47+ coder = new TIFFImageDecoder(in, param);
 48+ try {
 49+ return coder.decodeAsRenderedImage();
 50+ } catch (Exception e) {
 51+ throw new ImageTranscoderException("Cannot decode TIFF data", e);
 52+ }
 53+ }
 54+
 55+ public void writeImage(RenderedImage img, OutputStream out) throws ImageTranscoderException {
 56+ TIFFImageEncoder coder;
 57+ TIFFEncodeParam param = new TIFFEncodeParam();
 58+ param.setCompression(TIFFEncodeParam.COMPRESSION_DEFLATE);
 59+ coder = new TIFFImageEncoder(out, param);
 60+ try {
 61+ coder.encode(img);
 62+ } catch (Exception e) {
 63+ throw new ImageTranscoderException("Cannot encode TIFF data", e);
 64+ }
 65+ }
 66+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/SVGRasterizer.java
@@ -0,0 +1,234 @@
 2+/*****************************************************************************
 3+ * Copyright (C) The Apache Software Foundation. All rights reserved. *
 4+ * ------------------------------------------------------------------------- *
 5+ * This software is published under the terms of the Apache Software License *
 6+ * version 1.1, a copy of which has been included with this distribution in *
 7+ * the LICENSE file. *
 8+
 9+ *****************************************************************************/
 10+
 11+package imgservserver;
 12+
 13+import java.awt.image.BufferedImage;
 14+
 15+import java.awt.Paint;
 16+
 17+import java.io.File;
 18+import java.io.FileInputStream;
 19+import java.io.InputStream;
 20+import java.io.Reader;
 21+
 22+import java.net.URL;
 23+
 24+import java.util.Map;
 25+
 26+import org.apache.batik.transcoder.TranscoderException;
 27+import org.apache.batik.transcoder.TranscoderInput;
 28+import org.apache.batik.transcoder.TranscoderOutput;
 29+import org.apache.batik.transcoder.TranscodingHints;
 30+
 31+import org.apache.batik.transcoder.image.ImageTranscoder;
 32+
 33+import org.w3c.dom.svg.SVGDocument;
 34+
 35+/**
 36+ * This class provides a simple and method based API for converting a SVG
 37+ * document fragment to a <tt>BufferedImage</tt>.
 38+ *
 39+ * @author <a href="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
 40+ */
 41+public class SVGRasterizer {
 42+
 43+ /**
 44+ * The transcoder input.
 45+ */
 46+ protected TranscoderInput input;
 47+
 48+ /**
 49+ * The transcoder hints.
 50+ */
 51+ protected TranscodingHints hints = new TranscodingHints();
 52+
 53+ /**
 54+ * The image that represents the SVG document.
 55+ */
 56+ protected BufferedImage img;
 57+
 58+ /**
 59+ * Constructs a new SVGRasterizer.
 60+ *
 61+ * @param uri the uri of the document to rasterize
 62+ */
 63+ public SVGRasterizer(String uri) {
 64+ this.input = new TranscoderInput(uri);
 65+ }
 66+
 67+ /**
 68+ * Constructs a new SVGRasterizer.
 69+ *
 70+ * @param url the URL of the document to rasterize
 71+ */
 72+ public SVGRasterizer(URL url) {
 73+ this.input = new TranscoderInput(url.toString());
 74+ }
 75+
 76+ /**
 77+ * Constructs a new SVGRasterizer converter.
 78+ *
 79+ * @param istream the input stream that represents the SVG document to
 80+ * rasterize
 81+ */
 82+ public SVGRasterizer(InputStream istream) {
 83+ this.input = new TranscoderInput(istream);
 84+ }
 85+
 86+ /**
 87+ * Constructs a new SVGRasterizer converter.
 88+ *
 89+ * @param reader the reader that represents the SVG document to rasterize
 90+ */
 91+ public SVGRasterizer(Reader reader) {
 92+ this.input = new TranscoderInput(reader);
 93+ }
 94+
 95+ /**
 96+ * Constructs a new SVGRasterizer converter.
 97+ *
 98+ * @param document the SVG document to rasterize
 99+ */
 100+ public SVGRasterizer(SVGDocument document) {
 101+ this.input = new TranscoderInput(document);
 102+ }
 103+
 104+ /**
 105+ * Returns the image that represents the SVG document.
 106+ */
 107+ public BufferedImage createBufferedImage() throws TranscoderException {
 108+ Rasterizer r = new Rasterizer();
 109+ r.setTranscodingHints((Map)hints);
 110+ r.transcode(input, null);
 111+ return img;
 112+ }
 113+
 114+ /**
 115+ * Sets the width of the image to rasterize.
 116+ *
 117+ * @param width the image width
 118+ */
 119+ public void setImageWidth(float width) {
 120+ hints.put(ImageTranscoder.KEY_WIDTH, new Float(width));
 121+ }
 122+
 123+ /**
 124+ * Sets the height of the image to rasterize.
 125+ *
 126+ * @param width the image height
 127+ */
 128+ public void setImageHeight(float height) {
 129+ hints.put(ImageTranscoder.KEY_HEIGHT, new Float(height));
 130+ }
 131+
 132+ /**
 133+ * Sets the preferred language to use. SVG documents can provide text in
 134+ * multiple languages, this method lets you control which language to use
 135+ * if possible. e.g. "en" for english or "fr" for french.
 136+ *
 137+ * @param language the preferred language to use
 138+ */
 139+ public void setLanguages(String language) {
 140+ hints.put(ImageTranscoder.KEY_LANGUAGE, language);
 141+ }
 142+
 143+ /**
 144+ * Sets the unit conversion factor to the specified value. This method
 145+ * lets you choose how units such as 'em' are converted. e.g. 0.26458 is
 146+ * 96dpi (the default) or 0.3528 is 72dpi.
 147+ *
 148+ * @param px2mm the pixel to millimeter convertion factor.
 149+ */
 150+ public void setPixelToMMFactor(float px2mm) {
 151+ hints.put(ImageTranscoder.KEY_PIXEL_TO_MM, new Float(px2mm));
 152+ }
 153+
 154+ /**
 155+ * Sets the uri of the user stylesheet. The user stylesheet can be used to
 156+ * override styles.
 157+ *
 158+ * @param uri the uri of the user stylesheet
 159+ */
 160+ public void setUserStyleSheetURI(String uri) {
 161+ hints.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, uri);
 162+ }
 163+
 164+ /**
 165+ * Sets whether or not the XML parser used to parse SVG document should be
 166+ * validating or not, depending on the specified parameter. For futher
 167+ * details about how media work, see the
 168+ * <a href="http://www.w3.org/TR/CSS2/media.html">Media types in the CSS2
 169+ * specification</a>.
 170+ *
 171+ * @param b true means the XML parser will validate its input
 172+ */
 173+ public void setXMLParserValidating(boolean b) {
 174+ hints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING,
 175+ (b ? Boolean.TRUE : Boolean.FALSE));
 176+ }
 177+
 178+ /**
 179+ * Sets the media to rasterize. The medium should be separated by
 180+ * comma. e.g. "screen", "print" or "screen, print"
 181+ *
 182+ * @param media the media to use
 183+ */
 184+ public void setMedia(String media) {
 185+ hints.put(ImageTranscoder.KEY_MEDIA, media);
 186+ }
 187+
 188+ /**
 189+ * Sets the alternate stylesheet to use. For futher details, you can have
 190+ * a look at the <a href="http://www.w3.org/TR/xml-stylesheet/">Associating
 191+ * Style Sheets with XML documents</a>.
 192+ *
 193+ * @param alternateStylesheet the alternate stylesheet to use if possible
 194+ */
 195+ public void setAlternateStylesheet(String alternateStylesheet) {
 196+ hints.put(ImageTranscoder.KEY_ALTERNATE_STYLESHEET,
 197+ alternateStylesheet);
 198+ }
 199+
 200+ /**
 201+ * Sets the Paint to use for the background of the image.
 202+ *
 203+ * @param p the paint to use for the background
 204+ */
 205+ public void setBackgroundColor(Paint p) {
 206+ hints.put(ImageTranscoder.KEY_BACKGROUND_COLOR, p);
 207+ }
 208+
 209+ /**
 210+ * An image transcoder that stores the resulting image.
 211+ */
 212+ protected class Rasterizer extends ImageTranscoder {
 213+
 214+ public BufferedImage createImage(int w, int h) {
 215+ return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
 216+ }
 217+
 218+ public void writeImage(BufferedImage img, TranscoderOutput output)
 219+ throws TranscoderException {
 220+ SVGRasterizer.this.img = img;
 221+ }
 222+ }
 223+
 224+/* // debug
 225+ public static void main(String [] args) throws Exception {
 226+ SVGRasterizer r = new SVGRasterizer(new File(args[0]).toURL());
 227+ r.setBackgroundColor(java.awt.Color.white);
 228+ BufferedImage img = r.createBufferedImage();
 229+ javax.swing.JFrame f = new javax.swing.JFrame();
 230+ f.getContentPane().add(new javax.swing.JLabel(new javax.swing.ImageIcon(img)), java.awt.BorderLayout.CENTER);
 231+ f.pack();
 232+ f.show();
 233+ }*/
 234+}
 235+
Index: trunk/imgserv/imgserv-server/src/imgservserver/ImageIOImageHandler.java
@@ -0,0 +1,95 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.awt.image.BufferedImage;
 15+import java.awt.image.RenderedImage;
 16+import java.io.ByteArrayInputStream;
 17+import java.io.InputStream;
 18+import java.io.OutputStream;
 19+import java.util.Iterator;
 20+import javax.imageio.ImageIO;
 21+import javax.imageio.ImageReader;
 22+import javax.imageio.ImageWriter;
 23+import javax.imageio.stream.MemoryCacheImageInputStream;
 24+import javax.imageio.stream.MemoryCacheImageOutputStream;
 25+
 26+public class ImageIOImageHandler
 27+implements MultiFormatImageReader, MultiFormatImageWriter {
 28+ String format;
 29+ boolean hasAlpha = true;
 30+
 31+ public ImageIOImageHandler(String f) {
 32+ format = f;
 33+ if (format.equals("jpeg"))
 34+ hasAlpha = false;
 35+ }
 36+
 37+ public boolean hasAlpha() {
 38+ return hasAlpha;
 39+ }
 40+
 41+ public void setSizeHint(int width, int height) {
 42+ }
 43+
 44+ public int getPreferredImageType() {
 45+ if (hasAlpha)
 46+ return BufferedImage.TYPE_INT_ARGB;
 47+ else
 48+ return BufferedImage.TYPE_INT_RGB;
 49+ }
 50+
 51+ public RenderedImage readImage(byte[] data)
 52+ throws ImageTranscoderException {
 53+ ByteArrayInputStream bis = new ByteArrayInputStream(data);
 54+
 55+ Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(format);
 56+ if (!readers.hasNext()) {
 57+ throw new ImageTranscoderException(
 58+ "No reader found for format \"" + format + "\"");
 59+ }
 60+
 61+ ImageReader imgr = readers.next();
 62+ imgr.setInput(new MemoryCacheImageInputStream(
 63+ new ByteArrayInputStream(data)));
 64+
 65+ BufferedImage img;
 66+
 67+ try {
 68+ img = imgr.read(0);
 69+ return img;
 70+ } catch (Exception e) {
 71+ throw new ImageTranscoderException(
 72+ "Could not read source image", e);
 73+ }
 74+ }
 75+
 76+ public void writeImage(RenderedImage image, OutputStream out)
 77+ throws ImageTranscoderException {
 78+ Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
 79+ if (!writers.hasNext()) {
 80+ throw new ImageTranscoderException(
 81+ "No writer found for format \"" + format + "\"");
 82+ }
 83+
 84+ ImageWriter imgw = writers.next();
 85+ imgw.setOutput(new MemoryCacheImageOutputStream(out));
 86+
 87+ try {
 88+ imgw.write(image);
 89+ } catch (Exception e) {
 90+ throw new ImageTranscoderException(
 91+ "Cannot write image to client", e);
 92+ }
 93+ }
 94+
 95+
 96+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/ImageClientInputStream.java
@@ -0,0 +1,41 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.io.BufferedInputStream;
 15+import java.io.IOException;
 16+import java.io.InputStream;
 17+
 18+public class ImageClientInputStream extends BufferedInputStream {
 19+ public ImageClientInputStream(InputStream in) {
 20+ super(in);
 21+ }
 22+
 23+ public ImageClientInputStream(InputStream in, int size) {
 24+ super(in, size);
 25+ }
 26+
 27+ public String readLine() throws IOException {
 28+ StringBuilder b = new StringBuilder();
 29+ int i;
 30+
 31+ while ((i = read()) != -1) {
 32+ char c = (char) i;
 33+ if (c == '\r')
 34+ continue;
 35+ if (c == '\n')
 36+ return b.toString();
 37+ b.append(c);
 38+ }
 39+
 40+ throw new IOException("Unexpected end of stream looking for \\r\\n");
 41+ }
 42+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/ImageClientOutputStream.java
@@ -0,0 +1,109 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+import java.io.BufferedOutputStream;
 15+import java.io.IOException;
 16+import java.io.OutputStream;
 17+import java.net.Socket;
 18+
 19+public class ImageClientOutputStream extends OutputStream {
 20+ static final int BUFFER_SIZE = 8192;
 21+ static final String END_OF_CHUNKED_DATA_MARKER = "0000";
 22+
 23+ BufferedOutputStream strm;
 24+ boolean chunked = false;
 25+ byte[] buffer = new byte[BUFFER_SIZE];
 26+ int bufpos = 0;
 27+ String header = null;
 28+
 29+ public ImageClientOutputStream(Socket client) throws IOException {
 30+ strm = new BufferedOutputStream(client.getOutputStream());
 31+ }
 32+
 33+ public void cancel() throws IOException {
 34+ bufpos = 0;
 35+ chunked = false;
 36+ header = null;
 37+ }
 38+
 39+ public void setChunked(boolean c) throws IOException {
 40+ flushBuffer();
 41+ if (chunked)
 42+ strm.write(END_OF_CHUNKED_DATA_MARKER.getBytes());
 43+ chunked = c;
 44+ }
 45+
 46+ @Override
 47+ public void close() throws IOException {
 48+ flush();
 49+ if (chunked)
 50+ strm.write(END_OF_CHUNKED_DATA_MARKER.getBytes());
 51+ strm.close();
 52+ }
 53+
 54+ @Override
 55+ public void flush() throws IOException {
 56+ flushBuffer();
 57+ strm.flush();
 58+ }
 59+
 60+ @Override
 61+ public void write(byte[] b) throws IOException {
 62+ if (b.length == 0)
 63+ return;
 64+ addToBuffer(b, 0, b.length);
 65+ }
 66+
 67+ @Override
 68+ public void write(byte[] b, int off, int len) throws IOException {
 69+ if (len == 0)
 70+ return;
 71+
 72+ addToBuffer(b, off, len);
 73+ }
 74+
 75+ public void write(int b) throws IOException {
 76+ byte[] a = new byte[1];
 77+ a[0] = (byte) b;
 78+ addToBuffer(a, 0, 1);
 79+ }
 80+
 81+ private void addToBuffer(byte[] data, int offs, int len)
 82+ throws IOException {
 83+ for (int i = offs; i < len + offs; ++i) {
 84+ buffer[bufpos++] = data[i];
 85+ if (bufpos == BUFFER_SIZE)
 86+ flushBuffer();
 87+ }
 88+ }
 89+
 90+ public void setHeader(String header) {
 91+ this.header = header;
 92+ }
 93+
 94+ private void flushBuffer() throws IOException {
 95+ if (header != null) {
 96+ strm.write(header.getBytes());
 97+ header = null;
 98+ }
 99+
 100+ if (bufpos == 0)
 101+ return;
 102+
 103+ if (chunked) {
 104+ String len = String.format("%04x", bufpos);
 105+ strm.write(len.getBytes());
 106+ }
 107+ strm.write(buffer, 0, bufpos);
 108+ bufpos = 0;
 109+ }
 110+}
Index: trunk/imgserv/imgserv-server/src/imgservserver/MultiFormatImageFactory.java
@@ -0,0 +1,116 @@
 2+/* Copyright (c) 2008 River Tarnell <river@wikimedia.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* @(#) $Id$ */
 11+
 12+package imgservserver;
 13+
 14+public class MultiFormatImageFactory {
 15+ static class ImageHandler {
 16+ Class handler;
 17+ String name;
 18+
 19+ public ImageHandler(String name, Class handler) {
 20+ this.name = name;
 21+ this.handler = handler;
 22+ }
 23+
 24+ public String getName() {
 25+ return name;
 26+ }
 27+
 28+ public Class getHandler() {
 29+ return handler;
 30+ }
 31+ }
 32+
 33+ static String[][] normalisedNames = {
 34+ { "png", "png" },
 35+ { "PNG", "png" },
 36+ { "jpeg", "jpeg" },
 37+ { "JPEG", "jpeg" },
 38+ { "JPG", "jpeg" },
 39+ { "jpg", "jpeg" },
 40+ { "bmp", "bmp" },
 41+ { "BMP", "bmp" },
 42+ { "tiff", "tiff" },
 43+ { "tif", "tiff" },
 44+ { "TIFF", "tiff" },
 45+ { "TIF", "tiff" },
 46+ { "svg", "svg" },
 47+ { "SVG", "svg" },
 48+ };
 49+
 50+ static String normaliseName(String name) {
 51+ for(String[] s : normalisedNames) {
 52+ if (s[0].equals(name))
 53+ return s[1];
 54+ }
 55+
 56+ return null;
 57+ }
 58+ static ImageHandler[] readers = {
 59+ new ImageHandler("png", ImageIOImageHandler.class),
 60+ new ImageHandler("jpeg", ImageIOImageHandler.class),
 61+ new ImageHandler("bmp", ImageIOImageHandler.class),
 62+ new ImageHandler("tiff", TIFFImageHandler.class),
 63+ new ImageHandler("svg", SVGImageHandler.class),
 64+ };
 65+
 66+ static ImageHandler[] writers = {
 67+ new ImageHandler("png", ImageIOImageHandler.class),
 68+ new ImageHandler("jpeg", ImageIOImageHandler.class),
 69+ new ImageHandler("bmp", ImageIOImageHandler.class),
 70+ new ImageHandler("tiff", TIFFImageHandler.class),
 71+ };
 72+
 73+ static ImageHandler findHandler(ImageHandler[] list, String name) {
 74+ for (ImageHandler h: list)
 75+ if (h.getName().equals(name))
 76+ return h;
 77+ return null;
 78+ }
 79+
 80+ static Object getHandler(ImageHandler[] list, String format)
 81+ throws ImageTranscoderException {
 82+ format = normaliseName(format);
 83+
 84+ ImageHandler h = findHandler(list, format);
 85+ if (h == null)
 86+ throw new ImageTranscoderException(
 87+ "No handler found for type \"" + format + "\"");
 88+
 89+ Class c = h.getHandler();
 90+ try {
 91+ return c.getConstructor(String.class).newInstance(format);
 92+ } catch (Exception e) {
 93+ throw new ImageTranscoderException(
 94+ "Cannot instantiate handler for format \""+format+"\"", e);
 95+ }
 96+ }
 97+
 98+ public static MultiFormatImageReader getReader(String format)
 99+ throws ImageTranscoderException {
 100+ try {
 101+ return (MultiFormatImageReader) getHandler(readers, format);
 102+ } catch (Exception e) {
 103+ throw new ImageTranscoderException(
 104+ "Cannot instantiate handler for format \""+format+"\"", e);
 105+ }
 106+ }
 107+
 108+ public static MultiFormatImageWriter getWriter(String format)
 109+ throws ImageTranscoderException {
 110+ try {
 111+ return (MultiFormatImageWriter) getHandler(writers, format);
 112+ } catch (Exception e) {
 113+ throw new ImageTranscoderException(
 114+ "Cannot instantiate handler for format \""+format+"\"", e);
 115+ }
 116+ }
 117+}
Index: trunk/imgserv/imgserv-client/imgserv.cxx
@@ -6,6 +6,7 @@
77 #include <ios>
88 #include <sstream>
99 #include <vector>
 10+#include <iterator>
1011
1112 #include <sys/types.h>
1213 #include <sys/socket.h>
@@ -25,6 +26,7 @@
2627 void usage();
2728 int safe_write(int s, void *data, size_t n);
2829 std::string extract_extension(std::string const &);
 30+ void dump_data(char *data, std::size_t n);
2931
3032 }
3133
@@ -197,63 +199,118 @@
198200 std::cerr << "done, " << wr << " bytes\n";
199201 std::cerr << "% Waiting for reply...";
200202
201 - /*
202 - * The reply is either "OK <size>\r\n<data>", or "ERROR <message>\r\n";
203 - */
204 - int offs = 0;
205 - std::fill(v.begin(), v.end(), 0);
 203+ enum {
 204+ READING_STATUS,
 205+ READING_CHUNK_SIZE,
 206+ READING_CHUNK
 207+ } state = READING_STATUS;
 208+ int chunksize;
 209+ int bufpos = 0;
 210+ int buflen = 0;
 211+ ssize_t z;
 212+ std::string s;
 213+ std::vector<char>::iterator it, bufstart, bufend;
 214+ static std::string const rn = "\r\n";
 215+ std::size_t outsize = 0;
 216+
206217 for (;;) {
207 - ssize_t r;
208 - r = read(sock, &buf[0] + offs, buf.size() - offs - 1);
209 - if (r == -1) {
210 - std::cerr << "read error: " <<
211 - std::strerror(errno) << '\n';
212 - return 1;
213 - }
 218+ if (buflen == 0) {
 219+ z = read(sock, &buf[0], buf.size());
 220+ if (z == -1) {
 221+ std::cerr << "% Read error from server: "
 222+ << std::strerror(errno) << '\n';
 223+ return 1;
 224+ } else if (z == 0) {
 225+ std::cerr << "% Unexpected EOF from server.\n";
 226+ return 1;
 227+ }
214228
215 - if (r == 0) {
216 - std::cerr << "unexpected end of file\n";
217 - return 1;
 229+ //dump_data(&buf[0], z);
 230+ buflen = z;
 231+ bufpos = 0;
218232 }
219233
220 - offs += r;
 234+ bufstart = buf.begin() + bufpos;
 235+ bufend = buf.begin() + buflen - bufpos;
221236
222 - char *rn = std::strstr(&buf[0], "\r\n");
223 - if (rn != NULL) {
224 - if (std::memcmp(&buf[0], "ERROR ", 6) == 0) {
225 - std::cerr << "server error: "
226 - << std::string(&buf[0] + 6, rn)
227 - << '\n';
228 - return 1;
229 - } else if (std::memcmp(&buf[0], "OK ", 3) == 0) {
230 - std::cerr << "ok\n";
231 - outfile.write(rn + 2,
232 - (&buf[0] + offs) - (rn + 2));
233 - break;
 237+ switch (state) {
 238+ case READING_STATUS:
 239+ it = std::search(bufstart, bufend,
 240+ rn.begin(), rn.end());
 241+ if (it != bufend) {
 242+ int len = std::distance(bufstart, it);
 243+ s.insert(s.end(), bufstart, it);
 244+ bufpos += len + 2;
 245+ buflen -= len + 2;
 246+ if (s == "OK") {
 247+ std::cout << "ok.\n";
 248+ } else {
 249+ if (s.substr(0, 5) == "ERROR") {
 250+ std::cout << "error: " << s.substr(6) << '\n';
 251+ } else {
 252+ std::cout << "error: unknown status\n";
 253+ }
 254+ return 1;
 255+ }
 256+
 257+ state = READING_CHUNK_SIZE;
 258+ s = "";
 259+ continue;
 260+ } else {
 261+ if (s.size() + buflen > 8192) {
 262+ std::cout << "error: header too long\n";
 263+ return 1;
 264+ }
 265+
 266+ s.insert(s.end(), bufstart, bufend);
 267+ buflen = bufpos = 0;
234268 }
235 - }
 269+ break;
236270
237 - if (offs > 256) {
238 - std::cerr << "too much garbage before reply.\n";
239 - return 1;
240 - }
241 - }
 271+ case READING_CHUNK_SIZE:
 272+ i = 4 - s.size();
 273+ while (i && buflen) {
 274+ s += buf[bufpos];
 275+ bufpos++;
 276+ buflen--;
 277+ i--;
 278+ }
242279
243 - ssize_t outsize = 0;
244 - for (;;) {
245 - ssize_t n;
246 - if ((n = read(sock, &buf[0], buf.size())) == -1) {
247 - std::cerr << "read error: " <<
248 - std::strerror(errno) << '\n';
249 - return 1;
250 - }
 280+ if (i == 0) {
 281+ chunksize = std::strtol(s.c_str(), NULL, 16);
 282+ if (chunksize == 0)
 283+ goto done;
 284+ state = READING_CHUNK;
 285+ s = "";
 286+ }
 287+ continue;
251288
252 - if (n == 0)
253 - break;
 289+ case READING_CHUNK:
 290+ it = bufstart + std::min(chunksize, buflen);
 291+ std::copy(bufstart, it, std::ostream_iterator<char>(outfile));
 292+ outsize += std::distance(bufstart, it);
254293
255 - outsize += n;
256 - outfile.write(&buf[0], n);
 294+ if (buflen >= chunksize) {
 295+ /* finished this chunk */
 296+ buflen -= chunksize;
 297+ bufpos += chunksize;
 298+ state = READING_CHUNK_SIZE;
 299+ } else {
 300+ chunksize -= buflen;
 301+ bufpos = buflen = 0;
 302+
 303+ if (chunksize == 0) {
 304+ state = READING_CHUNK_SIZE;
 305+ continue;
 306+ }
 307+ }
 308+ continue;
 309+
 310+ default:
 311+ abort();
 312+ }
257313 }
 314+done:;
258315
259316 std::cerr << "% Wrote " << outsize << " bytes to " << argv[1] << '\n';
260317 }
@@ -291,4 +348,13 @@
292349 return fname.substr(n + 1);
293350 }
294351
 352+void
 353+dump_data(char *s, std::size_t n)
 354+{
 355+ while (n--) {
 356+ std::printf("%02x", (int)(unsigned char)*s);
 357+ s++;
 358+ }
295359 }
 360+
 361+}

Follow-up revisions

RevisionCommit summaryAuthorDate
r34615* Revert back to r34539 (temporary)...aaron14:42, 11 May 2008

Status & tagging log