summaryrefslogtreecommitdiffstats
path: root/src/io/stream
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /src/io/stream
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/io/stream')
-rw-r--r--src/io/stream/Makefile.tst48
-rw-r--r--src/io/stream/README13
-rw-r--r--src/io/stream/bufferstream.cpp144
-rw-r--r--src/io/stream/bufferstream.h98
-rw-r--r--src/io/stream/gzipstream.cpp456
-rw-r--r--src/io/stream/gzipstream.h120
-rw-r--r--src/io/stream/inkscapestream.cpp800
-rw-r--r--src/io/stream/inkscapestream.h666
-rw-r--r--src/io/stream/streamtest.cpp256
-rw-r--r--src/io/stream/stringstream.cpp125
-rw-r--r--src/io/stream/stringstream.h105
-rw-r--r--src/io/stream/uristream.cpp171
-rw-r--r--src/io/stream/uristream.h101
-rw-r--r--src/io/stream/xsltstream.cpp248
-rw-r--r--src/io/stream/xsltstream.h140
15 files changed, 3491 insertions, 0 deletions
diff --git a/src/io/stream/Makefile.tst b/src/io/stream/Makefile.tst
new file mode 100644
index 0000000..2e3142d
--- /dev/null
+++ b/src/io/stream/Makefile.tst
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+##############################################
+#
+# Test makefile for InkscapeStreams
+#
+##############################################
+
+
+CC = gcc
+CXX = g++
+
+
+INC = -I. -I..
+
+XSLT_CFLAGS = `pkg-config --cflags libxslt`
+XSLT_LIBS = `pkg-config --libs libxslt`
+
+GLIBMM_CFLAGS = `pkg-config --cflags glibmm-2.4`
+GLIBMM_LIBS = `pkg-config --libs glibmm-2.4`
+
+CFLAGS = -g $(GLIBMM_CFLAGS) $(XSLT_CFLAGS)
+LIBS = $(GLIBMM_LIBS) $(XSLT_LIBS) ../uri.o -lz
+
+OBJ = \
+inkscapestream.o \
+base64stream.o \
+gzipstream.o \
+stringstream.o \
+uristream.o \
+xsltstream.o \
+ftos.o
+
+all: streamtest
+
+streamtest: inkscapestream.h libstream.a streamtest.o
+ $(CXX) -o streamtest streamtest.o libstream.a $(LIBS)
+
+libstream.a: $(OBJ)
+ ar crv libstream.a $(OBJ)
+
+
+.cpp.o:
+ $(CXX) $(CFLAGS) $(INC) -c -o $@ $<
+
+clean:
+ -$(RM) *.o *.a
+ -$(RM) streamtest
+
diff --git a/src/io/stream/README b/src/io/stream/README
new file mode 100644
index 0000000..67d8721
--- /dev/null
+++ b/src/io/stream/README
@@ -0,0 +1,13 @@
+
+This directory contains code related to streams.
+
+Base class:
+ inkscapestream.h Used by other stream handling code and odf, filter-file
+
+Derived classes:
+ bufferstream.h Used by odf
+ gzipstream.h Used by repr-io.cpp
+ stringstream.h Used by rerp-io.cpp and ui/clipboard.cpp
+ Note file with same name in src/svg
+ uristream.h Used by repr-io.cpp
+ xsltstream.h Used by none one (except testfile)
diff --git a/src/io/stream/bufferstream.cpp b/src/io/stream/bufferstream.cpp
new file mode 100644
index 0000000..1723ee1
--- /dev/null
+++ b/src/io/stream/bufferstream.cpp
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * @file
+ * Phoebe DOM Implementation.
+ *
+ * This is a C++ approximation of the W3C DOM model, which follows
+ * fairly closely the specifications in the various .idl files, copies of
+ * which are provided for reference. Most important is this one:
+ *
+ * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
+ *//*
+ * Authors:
+ * see git history
+ * Bob Jamison
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU LGPL v2.1+, read the file 'COPYING' for more information.
+ */
+
+/**
+ * This class provided buffered endpoints for input and output.
+ */
+
+#include "bufferstream.h"
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# B U F F E R I N P U T S T R E A M
+//#########################################################################
+/**
+ *
+ */
+BufferInputStream::BufferInputStream(
+ const std::vector<unsigned char> &sourceBuffer)
+ : buffer(sourceBuffer)
+{
+ position = 0;
+ closed = false;
+}
+
+/**
+ *
+ */
+BufferInputStream::~BufferInputStream()
+= default;
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this input stream without blocking by the next caller of a method for
+ * this input stream.
+ */
+int BufferInputStream::available()
+{
+ if (closed)
+ return -1;
+ return buffer.size() - position;
+}
+
+
+/**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ */
+void BufferInputStream::close()
+{
+ closed = true;
+}
+
+/**
+ * Reads the next byte of data from the input stream. -1 if EOF
+ */
+int BufferInputStream::get()
+{
+ if (closed)
+ return -1;
+ if (position >= (int)buffer.size())
+ return -1;
+ int ch = (int) buffer[position++];
+ return ch;
+}
+
+
+
+
+//#########################################################################
+//# B U F F E R O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ *
+ */
+BufferOutputStream::BufferOutputStream()
+{
+ closed = false;
+}
+
+/**
+ *
+ */
+BufferOutputStream::~BufferOutputStream()
+= default;
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+void BufferOutputStream::close()
+{
+ closed = true;
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void BufferOutputStream::flush()
+{
+ //nothing to do
+}
+
+/**
+ * Writes the specified byte to this output stream.
+ */
+int BufferOutputStream::put(char ch)
+{
+ if (closed)
+ return -1;
+ buffer.push_back(ch);
+ return 1;
+}
+
+
+
+
+} //namespace IO
+} //namespace Inkscape
+
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
diff --git a/src/io/stream/bufferstream.h b/src/io/stream/bufferstream.h
new file mode 100644
index 0000000..811ab0d
--- /dev/null
+++ b/src/io/stream/bufferstream.h
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * @file
+ * Phoebe DOM Implementation.
+ *
+ * This is a C++ approximation of the W3C DOM model, which follows
+ * fairly closely the specifications in the various .idl files, copies of
+ * which are provided for reference. Most important is this one:
+ *
+ * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
+ *//*
+ * Authors:
+ * see git history
+ * Bob Jamison
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU LGPL v2.1+, read the file 'COPYING' for more information.
+ */
+#ifndef SEEN_BUFFERSTREAM_H
+#define SEEN_BUFFERSTREAM_H
+
+
+#include <vector>
+#include "inkscapestream.h"
+
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# S T R I N G I N P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for reading character from a DOMString
+ *
+ */
+class BufferInputStream : public InputStream
+{
+
+public:
+
+ BufferInputStream(const std::vector<unsigned char> &sourceBuffer);
+ ~BufferInputStream() override;
+ int available() override;
+ void close() override;
+ int get() override;
+
+private:
+ const std::vector<unsigned char> &buffer;
+ long position;
+ bool closed;
+
+}; // class BufferInputStream
+
+
+
+
+//#########################################################################
+//# B U F F E R O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for sending a stream to a character buffer
+ *
+ */
+class BufferOutputStream : public OutputStream
+{
+
+public:
+
+ BufferOutputStream();
+ ~BufferOutputStream() override;
+ void close() override;
+ void flush() override;
+ int put(char ch) override;
+ virtual std::vector<unsigned char> &getBuffer()
+ { return buffer; }
+
+ virtual void clear()
+ { buffer.clear(); }
+
+private:
+ std::vector<unsigned char> buffer;
+ bool closed;
+
+}; // class BufferOutputStream
+
+
+
+} //namespace IO
+} //namespace Inkscape
+
+
+
+#endif // SEEN_BUFFERSTREAM_H
diff --git a/src/io/stream/gzipstream.cpp b/src/io/stream/gzipstream.cpp
new file mode 100644
index 0000000..60d17f9
--- /dev/null
+++ b/src/io/stream/gzipstream.cpp
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/** @file
+ * Zlib-enabled input and output streams
+ *//*
+ * Authors:
+ * see git history
+ * Bob Jamison <rjamison@titan.com>
+ *
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU LGPL v2.1+, read the file 'COPYING' for more information.
+ */
+/*
+ * This is a thin wrapper of libz calls, in order
+ * to provide a simple interface to our developers
+ * for gzip input and output.
+ */
+
+#include "gzipstream.h"
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# G Z I P I N P U T S T R E A M
+//#########################################################################
+
+#define OUT_SIZE 4000
+
+/**
+ *
+ */
+GzipInputStream::GzipInputStream(InputStream &sourceStream)
+ : BasicInputStream(sourceStream),
+ loaded(false),
+ outputBuf(nullptr),
+ srcBuf(nullptr),
+ crc(0),
+ srcCrc(0),
+ srcSiz(0),
+ srcLen(0),
+ outputBufPos(0),
+ outputBufLen(0)
+{
+ memset( &d_stream, 0, sizeof(d_stream) );
+}
+
+/**
+ *
+ */
+GzipInputStream::~GzipInputStream()
+{
+ close();
+ if ( srcBuf ) {
+ delete[] srcBuf;
+ srcBuf = nullptr;
+ }
+ if ( outputBuf ) {
+ delete[] outputBuf;
+ outputBuf = nullptr;
+ }
+}
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this input stream without blocking by the next caller of a method for
+ * this input stream.
+ */
+int GzipInputStream::available()
+{
+ if (closed || !outputBuf)
+ return 0;
+ return outputBufLen - outputBufPos;
+}
+
+
+/**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ */
+void GzipInputStream::close()
+{
+ if (closed)
+ return;
+
+ int zerr = inflateEnd(&d_stream);
+ if (zerr != Z_OK) {
+ printf("inflateEnd: Some kind of problem: %d\n", zerr);
+ }
+
+ if ( srcBuf ) {
+ delete[] srcBuf;
+ srcBuf = nullptr;
+ }
+ if ( outputBuf ) {
+ delete[] outputBuf;
+ outputBuf = nullptr;
+ }
+ closed = true;
+}
+
+/**
+ * Reads the next byte of data from the input stream. -1 if EOF
+ */
+int GzipInputStream::get()
+{
+ int ch = -1;
+ if (closed) {
+ // leave return value -1
+ }
+ else if (!loaded && !load()) {
+ closed=true;
+ } else {
+ loaded = true;
+
+ if ( outputBufPos >= outputBufLen ) {
+ // time to read more, if we can
+ fetchMore();
+ }
+
+ if ( outputBufPos < outputBufLen ) {
+ ch = (int)outputBuf[outputBufPos++];
+ }
+ }
+
+ return ch;
+}
+
+#define FTEXT 0x01
+#define FHCRC 0x02
+#define FEXTRA 0x04
+#define FNAME 0x08
+#define FCOMMENT 0x10
+
+bool GzipInputStream::load()
+{
+ crc = crc32(0L, Z_NULL, 0);
+
+ std::vector<Byte> inputBuf;
+ while (true)
+ {
+ int ch = source.get();
+ if (ch<0)
+ break;
+ inputBuf.push_back(static_cast<Byte>(ch & 0xff));
+ }
+ long inputBufLen = inputBuf.size();
+
+ if (inputBufLen < 19) //header + tail + 1
+ {
+ return false;
+ }
+
+ srcLen = inputBuf.size();
+ srcBuf = new (std::nothrow) Byte [srcLen];
+ if (!srcBuf) {
+ return false;
+ }
+
+ outputBuf = new (std::nothrow) unsigned char [OUT_SIZE];
+ if ( !outputBuf ) {
+ delete[] srcBuf;
+ srcBuf = nullptr;
+ return false;
+ }
+ outputBufLen = 0; // Not filled in yet
+
+ std::vector<unsigned char>::iterator iter;
+ Bytef *p = srcBuf;
+ for (iter=inputBuf.begin() ; iter != inputBuf.end() ; ++iter)
+ {
+ *p++ = *iter;
+ }
+
+ int headerLen = 10;
+
+ //Magic
+ //int val = (int)srcBuf[0];
+ ////printf("val:%x\n", val);
+ //val = (int)srcBuf[1];
+ ////printf("val:%x\n", val);
+
+ ////Method
+ //val = (int)srcBuf[2];
+ ////printf("val:%x\n", val);
+
+ //flags
+ int flags = static_cast<int>(srcBuf[3]);
+
+ ////time
+ //val = (int)srcBuf[4];
+ //val = (int)srcBuf[5];
+ //val = (int)srcBuf[6];
+ //val = (int)srcBuf[7];
+
+ ////xflags
+ //val = (int)srcBuf[8];
+ ////OS
+ //val = (int)srcBuf[9];
+
+// if ( flags & FEXTRA ) {
+// headerLen += 2;
+// int xlen =
+// TODO deal with optional header parts
+// }
+ if ( flags & FNAME ) {
+ int cur = 10;
+ while ( srcBuf[cur] )
+ {
+ cur++;
+ headerLen++;
+ }
+ headerLen++;
+ }
+
+
+ srcCrc = ((0x0ff & srcBuf[srcLen - 5]) << 24)
+ | ((0x0ff & srcBuf[srcLen - 6]) << 16)
+ | ((0x0ff & srcBuf[srcLen - 7]) << 8)
+ | ((0x0ff & srcBuf[srcLen - 8]) << 0);
+ //printf("srcCrc:%lx\n", srcCrc);
+
+ srcSiz = ((0x0ff & srcBuf[srcLen - 1]) << 24)
+ | ((0x0ff & srcBuf[srcLen - 2]) << 16)
+ | ((0x0ff & srcBuf[srcLen - 3]) << 8)
+ | ((0x0ff & srcBuf[srcLen - 4]) << 0);
+ //printf("srcSiz:%lx/%ld\n", srcSiz, srcSiz);
+
+ //outputBufLen = srcSiz + srcSiz/100 + 14;
+
+ unsigned char *data = srcBuf + headerLen;
+ unsigned long dataLen = srcLen - (headerLen + 8);
+ //printf("%x %x\n", data[0], data[dataLen-1]);
+
+ d_stream.zalloc = (alloc_func)nullptr;
+ d_stream.zfree = (free_func)nullptr;
+ d_stream.opaque = (voidpf)nullptr;
+ d_stream.next_in = data;
+ d_stream.avail_in = dataLen;
+ d_stream.next_out = outputBuf;
+ d_stream.avail_out = OUT_SIZE;
+
+ int zerr = inflateInit2(&d_stream, -MAX_WBITS);
+ if ( zerr == Z_OK )
+ {
+ zerr = fetchMore();
+ } else {
+ printf("inflateInit2: Some kind of problem: %d\n", zerr);
+ }
+
+
+ return (zerr == Z_OK) || (zerr == Z_STREAM_END);
+}
+
+
+int GzipInputStream::fetchMore()
+{
+ // TODO assumes we aren't called till the buffer is empty
+ d_stream.next_out = outputBuf;
+ d_stream.avail_out = OUT_SIZE;
+ outputBufLen = 0;
+ outputBufPos = 0;
+
+ int zerr = inflate( &d_stream, Z_SYNC_FLUSH );
+ if ( zerr == Z_OK || zerr == Z_STREAM_END ) {
+ outputBufLen = OUT_SIZE - d_stream.avail_out;
+ if ( outputBufLen ) {
+ crc = crc32(crc, const_cast<const Bytef *>(outputBuf), outputBufLen);
+ }
+ //printf("crc:%lx\n", crc);
+// } else if ( zerr != Z_STREAM_END ) {
+// // TODO check to be sure this won't happen for partial end reads
+// printf("inflate: Some kind of problem: %d\n", zerr);
+ }
+
+ return zerr;
+}
+
+//#########################################################################
+//# G Z I P O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ *
+ */
+GzipOutputStream::GzipOutputStream(OutputStream &destinationStream)
+ : BasicOutputStream(destinationStream)
+{
+
+ totalIn = 0;
+ totalOut = 0;
+ crc = crc32(0L, Z_NULL, 0);
+
+ //Gzip header
+ destination.put(0x1f);
+ destination.put(0x8b);
+
+ //Say it is compressed
+ destination.put(Z_DEFLATED);
+
+ //flags
+ destination.put(0);
+
+ //time
+ destination.put(0);
+ destination.put(0);
+ destination.put(0);
+ destination.put(0);
+
+ //xflags
+ destination.put(0);
+
+ //OS code - from zutil.h
+ //destination.put(OS_CODE);
+ //apparently, we should not explicitly include zutil.h
+ destination.put(0);
+
+}
+
+/**
+ *
+ */
+GzipOutputStream::~GzipOutputStream()
+{
+ close();
+}
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+void GzipOutputStream::close()
+{
+ if (closed)
+ return;
+
+ flush();
+
+ //# Send the CRC
+ uLong outlong = crc;
+ for (int n = 0; n < 4; n++)
+ {
+ destination.put(static_cast<char>(outlong & 0xff));
+ outlong >>= 8;
+ }
+ //# send the file length
+ outlong = totalIn & 0xffffffffL;
+ for (int n = 0; n < 4; n++)
+ {
+ destination.put(static_cast<char>(outlong & 0xff));
+ outlong >>= 8;
+ }
+
+ destination.close();
+ closed = true;
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void GzipOutputStream::flush()
+{
+ if (closed || inputBuf.empty())
+ {
+ return;
+ }
+
+ uLong srclen = inputBuf.size();
+ Bytef *srcbuf = new (std::nothrow) Bytef [srclen];
+ if (!srcbuf)
+ {
+ return;
+ }
+
+ uLong destlen = srclen;
+ Bytef *destbuf = new (std::nothrow) Bytef [(destlen + (srclen/100) + 13)];
+ if (!destbuf)
+ {
+ delete[] srcbuf;
+ return;
+ }
+
+ std::vector<unsigned char>::iterator iter;
+ Bytef *p = srcbuf;
+ for (iter=inputBuf.begin() ; iter != inputBuf.end() ; ++iter)
+ *p++ = *iter;
+
+ crc = crc32(crc, const_cast<const Bytef *>(srcbuf), srclen);
+
+ int zerr = compress(destbuf, static_cast<uLongf *>(&destlen), srcbuf, srclen);
+ if (zerr != Z_OK)
+ {
+ printf("Some kind of problem\n");
+ }
+
+ totalOut += destlen;
+ //skip the redundant zlib header and checksum
+ for (uLong i=2; i<destlen-4 ; i++)
+ {
+ destination.put((int)destbuf[i]);
+ }
+
+ destination.flush();
+
+ inputBuf.clear();
+ delete[] srcbuf;
+ delete[] destbuf;
+}
+
+
+
+/**
+ * Writes the specified byte to this output stream.
+ */
+int GzipOutputStream::put(char ch)
+{
+ if (closed)
+ {
+ //probably throw an exception here
+ return -1;
+ }
+
+
+ //Add char to buffer
+ inputBuf.push_back(ch);
+ totalIn++;
+ return 1;
+}
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/io/stream/gzipstream.h b/src/io/stream/gzipstream.h
new file mode 100644
index 0000000..202b3a1
--- /dev/null
+++ b/src/io/stream/gzipstream.h
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_INKSCAPE_IO_GZIPSTREAM_H
+#define SEEN_INKSCAPE_IO_GZIPSTREAM_H
+/**
+ * @file
+ * Zlib-enabled input and output streams.
+ *
+ * This is a thin wrapper of libz calls, in order
+ * to provide a simple interface to our developers
+ * for gzip input and output.
+ */
+/*
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ *
+ * Copyright (C) 2004 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <vector>
+#include "inkscapestream.h"
+#include <zlib.h>
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# G Z I P I N P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for deflating a gzip-compressed InputStream source
+ *
+ */
+class GzipInputStream : public BasicInputStream
+{
+
+public:
+
+ GzipInputStream(InputStream &sourceStream);
+
+ ~GzipInputStream() override;
+
+ int available() override;
+
+ void close() override;
+
+ int get() override;
+
+private:
+
+ bool load();
+ int fetchMore();
+
+ bool loaded;
+
+ unsigned char *outputBuf;
+ unsigned char *srcBuf;
+
+ unsigned long crc;
+ unsigned long srcCrc;
+ unsigned long srcSiz;
+ unsigned long srcLen;
+ long outputBufPos;
+ long outputBufLen;
+
+ z_stream d_stream;
+}; // class GzipInputStream
+
+
+
+
+//#########################################################################
+//# G Z I P O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for gzip-compressing data going to the
+ * destination OutputStream
+ *
+ */
+class GzipOutputStream : public BasicOutputStream
+{
+
+public:
+
+ GzipOutputStream(OutputStream &destinationStream);
+
+ ~GzipOutputStream() override;
+
+ void close() override;
+
+ void flush() override;
+
+ int put(char ch) override;
+
+private:
+
+ std::vector<unsigned char> inputBuf;
+
+ long totalIn;
+ long totalOut;
+ unsigned long crc;
+
+}; // class GzipOutputStream
+
+
+
+
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+#endif /* __INKSCAPE_IO_GZIPSTREAM_H__ */
diff --git a/src/io/stream/inkscapestream.cpp b/src/io/stream/inkscapestream.cpp
new file mode 100644
index 0000000..bc6dc1d
--- /dev/null
+++ b/src/io/stream/inkscapestream.cpp
@@ -0,0 +1,800 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Our base input/output stream classes. These are is directly
+ * inherited from iostreams, and includes any extra
+ * functionality that we might need.
+ *
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ *
+ * Copyright (C) 2004 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstdlib>
+#include "inkscapestream.h"
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# U T I L I T Y
+//#########################################################################
+
+void pipeStream(InputStream &source, OutputStream &dest)
+{
+ for (;;)
+ {
+ int ch = source.get();
+ if (ch<0)
+ break;
+ dest.put(ch);
+ }
+ dest.flush();
+}
+
+//#########################################################################
+//# B A S I C I N P U T S T R E A M
+//#########################################################################
+
+
+/**
+ *
+ */
+BasicInputStream::BasicInputStream(InputStream &sourceStream)
+ : source(sourceStream)
+{
+ closed = false;
+}
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this input stream without blocking by the next caller of a method for
+ * this input stream.
+ */
+int BasicInputStream::available()
+{
+ if (closed)
+ return 0;
+ return source.available();
+}
+
+
+/**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ */
+void BasicInputStream::close()
+{
+ if (closed)
+ return;
+ source.close();
+ closed = true;
+}
+
+/**
+ * Reads the next byte of data from the input stream. -1 if EOF
+ */
+int BasicInputStream::get()
+{
+ if (closed)
+ return -1;
+ return source.get();
+}
+
+
+
+//#########################################################################
+//# B A S I C O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ *
+ */
+BasicOutputStream::BasicOutputStream(OutputStream &destinationStream)
+ : destination(destinationStream)
+{
+ closed = false;
+}
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+void BasicOutputStream::close()
+{
+ if (closed)
+ return;
+ destination.close();
+ closed = true;
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void BasicOutputStream::flush()
+{
+ if (closed)
+ return;
+ destination.flush();
+}
+
+/**
+ * Writes the specified byte to this output stream.
+ */
+int BasicOutputStream::put(char ch)
+{
+ if (closed)
+ return -1;
+ destination.put(ch);
+ return 1;
+}
+
+
+
+//#########################################################################
+//# B A S I C R E A D E R
+//#########################################################################
+/**
+ *
+ */
+BasicReader::BasicReader(Reader &sourceReader)
+{
+ source = &sourceReader;
+}
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this reader without blocking by the next caller of a method for
+ * this reader.
+ */
+int BasicReader::available()
+{
+ if (source)
+ return source->available();
+ else
+ return 0;
+}
+
+
+/**
+ * Closes this reader and releases any system resources
+ * associated with the reader.
+ */
+void BasicReader::close()
+{
+ if (source)
+ source->close();
+}
+
+/**
+ * Reads the next byte of data from the reader.
+ */
+char BasicReader::get()
+{
+ if (source)
+ return source->get();
+ else
+ return (char)-1;
+}
+
+
+/**
+ * Reads a line of data from the reader.
+ */
+Glib::ustring BasicReader::readLine()
+{
+ Glib::ustring str;
+ while (available() > 0)
+ {
+ char ch = get();
+ if (ch == '\n')
+ break;
+ str.push_back(ch);
+ }
+ return str;
+}
+
+/**
+ * Reads a line of data from the reader.
+ */
+Glib::ustring BasicReader::readWord()
+{
+ Glib::ustring str;
+ while (available() > 0)
+ {
+ char ch = get();
+ if (!std::isprint(ch))
+ break;
+ str.push_back(ch);
+ }
+ return str;
+}
+
+
+static bool getLong(Glib::ustring &str, long *val)
+{
+ const char *begin = str.raw().c_str();
+ char *end;
+ long ival = strtol(begin, &end, 10);
+ if (str == end)
+ return false;
+ *val = ival;
+ return true;
+}
+
+static bool getULong(Glib::ustring &str, unsigned long *val)
+{
+ const char *begin = str.raw().c_str();
+ char *end;
+ unsigned long ival = strtoul(begin, &end, 10);
+ if (str == end)
+ return false;
+ *val = ival;
+ return true;
+}
+
+static bool getDouble(Glib::ustring &str, double *val)
+{
+ const char *begin = str.raw().c_str();
+ char *end;
+ double ival = strtod(begin, &end);
+ if (str == end)
+ return false;
+ *val = ival;
+ return true;
+}
+
+
+
+const Reader &BasicReader::readBool (bool& val )
+{
+ Glib::ustring buf = readWord();
+ if (buf == "true")
+ val = true;
+ else
+ val = false;
+ return *this;
+}
+
+const Reader &BasicReader::readShort (short& val )
+{
+ Glib::ustring buf = readWord();
+ long ival;
+ if (getLong(buf, &ival))
+ val = (short) ival;
+ return *this;
+}
+
+const Reader &BasicReader::readUnsignedShort (unsigned short& val )
+{
+ Glib::ustring buf = readWord();
+ unsigned long ival;
+ if (getULong(buf, &ival))
+ val = (unsigned short) ival;
+ return *this;
+}
+
+const Reader &BasicReader::readInt (int& val )
+{
+ Glib::ustring buf = readWord();
+ long ival;
+ if (getLong(buf, &ival))
+ val = (int) ival;
+ return *this;
+}
+
+const Reader &BasicReader::readUnsignedInt (unsigned int& val )
+{
+ Glib::ustring buf = readWord();
+ unsigned long ival;
+ if (getULong(buf, &ival))
+ val = (unsigned int) ival;
+ return *this;
+}
+
+const Reader &BasicReader::readLong (long& val )
+{
+ Glib::ustring buf = readWord();
+ long ival;
+ if (getLong(buf, &ival))
+ val = ival;
+ return *this;
+}
+
+const Reader &BasicReader::readUnsignedLong (unsigned long& val )
+{
+ Glib::ustring buf = readWord();
+ unsigned long ival;
+ if (getULong(buf, &ival))
+ val = ival;
+ return *this;
+}
+
+const Reader &BasicReader::readFloat (float& val )
+{
+ Glib::ustring buf = readWord();
+ double ival;
+ if (getDouble(buf, &ival))
+ val = (float)ival;
+ return *this;
+}
+
+const Reader &BasicReader::readDouble (double& val )
+{
+ Glib::ustring buf = readWord();
+ double ival;
+ if (getDouble(buf, &ival))
+ val = ival;
+ return *this;
+}
+
+
+
+//#########################################################################
+//# I N P U T S T R E A M R E A D E R
+//#########################################################################
+
+
+InputStreamReader::InputStreamReader(InputStream &inputStreamSource)
+ : inputStream(inputStreamSource)
+{
+}
+
+
+
+/**
+ * Close the underlying OutputStream
+ */
+void InputStreamReader::close()
+{
+ inputStream.close();
+}
+
+/**
+ * Flush the underlying OutputStream
+ */
+int InputStreamReader::available()
+{
+ return inputStream.available();
+}
+
+/**
+ * Overloaded to receive its bytes from an InputStream
+ * rather than a Reader
+ */
+char InputStreamReader::get()
+{
+ char ch = inputStream.get();
+ return ch;
+}
+
+
+
+//#########################################################################
+//# S T D R E A D E R
+//#########################################################################
+
+
+/**
+ *
+ */
+StdReader::StdReader()
+{
+ inputStream = new StdInputStream();
+}
+
+/**
+ *
+ */
+StdReader::~StdReader()
+{
+ delete inputStream;
+}
+
+
+
+/**
+ * Close the underlying OutputStream
+ */
+void StdReader::close()
+{
+ inputStream->close();
+}
+
+/**
+ * Flush the underlying OutputStream
+ */
+int StdReader::available()
+{
+ return inputStream->available();
+}
+
+/**
+ * Overloaded to receive its bytes from an InputStream
+ * rather than a Reader
+ */
+char StdReader::get()
+{
+ char ch = inputStream->get();
+ return ch;
+}
+
+
+
+
+
+//#########################################################################
+//# B A S I C W R I T E R
+//#########################################################################
+
+/**
+ *
+ */
+BasicWriter::BasicWriter(Writer &destinationWriter)
+{
+ destination = &destinationWriter;
+}
+
+/**
+ * Closes this writer and releases any system resources
+ * associated with this writer.
+ */
+void BasicWriter::close()
+{
+ if (destination)
+ destination->close();
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void BasicWriter::flush()
+{
+ if (destination)
+ destination->flush();
+}
+
+/**
+ * Writes the specified byte to this output writer.
+ */
+void BasicWriter::put(char ch)
+{
+ if (destination)
+ destination->put(ch);
+}
+
+/**
+ * Provide printf()-like formatting
+ */
+Writer &BasicWriter::printf(char const *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ gchar *buf = g_strdup_vprintf(fmt, args);
+ va_end(args);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+/**
+ * Writes the specified character to this output writer.
+ */
+Writer &BasicWriter::writeChar(char ch)
+{
+ put(ch);
+ return *this;
+}
+
+
+/**
+ * Writes the specified unicode string to this output writer.
+ */
+Writer &BasicWriter::writeUString(const Glib::ustring &str)
+{
+ writeStdString(str.raw());
+ return *this;
+}
+
+/**
+ * Writes the specified standard string to this output writer.
+ */
+Writer &BasicWriter::writeStdString(const std::string &str)
+{
+ for (char it : str) {
+ put(it);
+ }
+ return *this;
+}
+
+/**
+ * Writes the specified character string to this output writer.
+ */
+Writer &BasicWriter::writeString(const char *str)
+{
+ std::string tmp;
+ if (str)
+ tmp = str;
+ else
+ tmp = "null";
+ writeStdString(tmp);
+ return *this;
+}
+
+
+
+
+/**
+ *
+ */
+Writer &BasicWriter::writeBool (bool val )
+{
+ if (val)
+ writeString("true");
+ else
+ writeString("false");
+ return *this;
+}
+
+
+/**
+ *
+ */
+Writer &BasicWriter::writeShort (short val )
+{
+ gchar *buf = g_strdup_printf("%d", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+
+
+
+/**
+ *
+ */
+Writer &BasicWriter::writeUnsignedShort (unsigned short val )
+{
+ gchar *buf = g_strdup_printf("%u", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+
+/**
+ *
+ */
+Writer &BasicWriter::writeInt (int val)
+{
+ gchar *buf = g_strdup_printf("%d", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+
+/**
+ *
+ */
+Writer &BasicWriter::writeUnsignedInt (unsigned int val)
+{
+ gchar *buf = g_strdup_printf("%u", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+
+/**
+ *
+ */
+Writer &BasicWriter::writeLong (long val)
+{
+ gchar *buf = g_strdup_printf("%ld", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+
+/**
+ *
+ */
+Writer &BasicWriter::writeUnsignedLong(unsigned long val)
+{
+ gchar *buf = g_strdup_printf("%lu", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+ return *this;
+}
+
+/**
+ *
+ */
+Writer &BasicWriter::writeFloat(float val)
+{
+#if 1
+ gchar *buf = g_strdup_printf("%8.3f", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+#else
+ std::string tmp = ftos(val, 'g', 8, 3, 0);
+ writeStdString(tmp);
+#endif
+ return *this;
+}
+
+/**
+ *
+ */
+Writer &BasicWriter::writeDouble(double val)
+{
+#if 1
+ gchar *buf = g_strdup_printf("%8.3f", val);
+ if (buf) {
+ writeString(buf);
+ g_free(buf);
+ }
+#else
+ std::string tmp = ftos(val, 'g', 8, 3, 0);
+ writeStdString(tmp);
+#endif
+ return *this;
+}
+
+
+Writer& operator<< (Writer &writer, char val)
+ { return writer.writeChar(val); }
+
+Writer& operator<< (Writer &writer, Glib::ustring &val)
+ { return writer.writeUString(val); }
+
+Writer& operator<< (Writer &writer, std::string &val)
+ { return writer.writeStdString(val); }
+
+Writer& operator<< (Writer &writer, char const *val)
+ { return writer.writeString(val); }
+
+Writer& operator<< (Writer &writer, bool val)
+ { return writer.writeBool(val); }
+
+Writer& operator<< (Writer &writer, short val)
+ { return writer.writeShort(val); }
+
+Writer& operator<< (Writer &writer, unsigned short val)
+ { return writer.writeUnsignedShort(val); }
+
+Writer& operator<< (Writer &writer, int val)
+ { return writer.writeInt(val); }
+
+Writer& operator<< (Writer &writer, unsigned int val)
+ { return writer.writeUnsignedInt(val); }
+
+Writer& operator<< (Writer &writer, long val)
+ { return writer.writeLong(val); }
+
+Writer& operator<< (Writer &writer, unsigned long val)
+ { return writer.writeUnsignedLong(val); }
+
+Writer& operator<< (Writer &writer, float val)
+ { return writer.writeFloat(val); }
+
+Writer& operator<< (Writer &writer, double val)
+ { return writer.writeDouble(val); }
+
+
+
+//#########################################################################
+//# O U T P U T S T R E A M W R I T E R
+//#########################################################################
+
+
+OutputStreamWriter::OutputStreamWriter(OutputStream &outputStreamDest)
+ : outputStream(outputStreamDest)
+{
+}
+
+
+
+/**
+ * Close the underlying OutputStream
+ */
+void OutputStreamWriter::close()
+{
+ flush();
+ outputStream.close();
+}
+
+/**
+ * Flush the underlying OutputStream
+ */
+void OutputStreamWriter::flush()
+{
+ outputStream.flush();
+}
+
+/**
+ * Overloaded to redirect the output chars from the next Writer
+ * in the chain to an OutputStream instead.
+ */
+void OutputStreamWriter::put(char ch)
+{
+ outputStream.put(ch);
+}
+
+//#########################################################################
+//# S T D W R I T E R
+//#########################################################################
+
+
+/**
+ *
+ */
+StdWriter::StdWriter()
+{
+ outputStream = new StdOutputStream();
+}
+
+
+/**
+ *
+ */
+StdWriter::~StdWriter()
+{
+ delete outputStream;
+}
+
+
+
+/**
+ * Close the underlying OutputStream
+ */
+void StdWriter::close()
+{
+ flush();
+ outputStream->close();
+}
+
+/**
+ * Flush the underlying OutputStream
+ */
+void StdWriter::flush()
+{
+ outputStream->flush();
+}
+
+/**
+ * Overloaded to redirect the output chars from the next Writer
+ * in the chain to an OutputStream instead.
+ */
+void StdWriter::put(char ch)
+{
+ outputStream->put(ch);
+}
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
diff --git a/src/io/stream/inkscapestream.h b/src/io/stream/inkscapestream.h
new file mode 100644
index 0000000..b835a7f
--- /dev/null
+++ b/src/io/stream/inkscapestream.h
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_INKSCAPE_IO_INKSCAPESTREAM_H
+#define SEEN_INKSCAPE_IO_INKSCAPESTREAM_H
+/*
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ *
+ * Copyright (C) 2004 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstdio>
+#include <glibmm/ustring.h>
+
+namespace Inkscape
+{
+namespace IO
+{
+
+class StreamException : public std::exception
+{
+public:
+ StreamException(Glib::ustring theReason) noexcept
+ : reason(std::move(theReason))
+ {}
+ ~StreamException() noexcept override
+ = default;
+ char const *what() const noexcept override
+ { return reason.c_str(); }
+
+private:
+ Glib::ustring reason;
+
+};
+
+//#########################################################################
+//# I N P U T S T R E A M
+//#########################################################################
+
+/**
+ * This interface is the base of all input stream classes. Users who wish
+ * to make an InputStream that is part of a chain should inherit from
+ * BasicInputStream. Inherit from this class to make a source endpoint,
+ * such as a URI or buffer.
+ *
+ */
+class InputStream
+{
+
+public:
+
+ /**
+ * Constructor.
+ */
+ InputStream() = default;
+
+ /**
+ * Destructor
+ */
+ virtual ~InputStream() = default;
+
+ /**
+ * Return the number of bytes that are currently available
+ * to be read
+ */
+ virtual int available() = 0;
+
+ /**
+ * Do whatever it takes to 'close' this input stream
+ * The most likely implementation of this method will be
+ * for endpoints that use a resource for their data.
+ */
+ virtual void close() = 0;
+
+ /**
+ * Read one byte from this input stream. This is a blocking
+ * call. If no data is currently available, this call will
+ * not return until it exists. If the user does not want
+ * their code to block, then the usual solution is:
+ * if (available() > 0)
+ * myChar = get();
+ * This call returns -1 on end-of-file.
+ */
+ virtual int get() = 0;
+
+}; // class InputStream
+
+
+
+
+/**
+ * This is the class that most users should inherit, to provide
+ * their own streams.
+ *
+ */
+class BasicInputStream : public InputStream
+{
+
+public:
+
+ BasicInputStream(InputStream &sourceStream);
+
+ ~BasicInputStream() override = default;
+
+ int available() override;
+
+ void close() override;
+
+ int get() override;
+
+protected:
+
+ bool closed;
+
+ InputStream &source;
+
+private:
+
+
+}; // class BasicInputStream
+
+
+
+/**
+ * Convenience class for reading from standard input
+ */
+class StdInputStream : public InputStream
+{
+public:
+
+ int available() override
+ { return 0; }
+
+ void close() override
+ { /* do nothing */ }
+
+ int get() override
+ { return getchar(); }
+
+};
+
+
+
+
+
+
+//#########################################################################
+//# O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ * This interface is the base of all input stream classes. Users who wish
+ * to make an OutputStream that is part of a chain should inherit from
+ * BasicOutputStream. Inherit from this class to make a destination endpoint,
+ * such as a URI or buffer.
+ */
+class OutputStream
+{
+
+public:
+
+ /**
+ * Constructor.
+ */
+ OutputStream() = default;
+
+ /**
+ * Destructor
+ */
+ virtual ~OutputStream() = default;
+
+ /**
+ * This call should
+ * 1. flush itself
+ * 2. close itself
+ * 3. close the destination stream
+ */
+ virtual void close() = 0;
+
+ /**
+ * This call should push any pending data it might have to
+ * the destination stream. It should NOT call flush() on
+ * the destination stream.
+ */
+ virtual void flush() = 0;
+
+ /**
+ * Send one byte to the destination stream.
+ */
+ virtual int put(char ch) = 0;
+
+
+}; // class OutputStream
+
+
+/**
+ * This is the class that most users should inherit, to provide
+ * their own output streams.
+ */
+class BasicOutputStream : public OutputStream
+{
+
+public:
+
+ BasicOutputStream(OutputStream &destinationStream);
+
+ ~BasicOutputStream() override = default;
+
+ void close() override;
+
+ void flush() override;
+
+ int put(char ch) override;
+
+protected:
+
+ bool closed;
+
+ OutputStream &destination;
+
+
+}; // class BasicOutputStream
+
+
+
+/**
+ * Convenience class for writing to standard output
+ */
+class StdOutputStream : public OutputStream
+{
+public:
+
+ void close() override
+ { }
+
+ void flush() override
+ { }
+
+ int put(char ch) override
+ {return putchar(ch); }
+
+};
+
+
+
+
+//#########################################################################
+//# R E A D E R
+//#########################################################################
+
+
+/**
+ * This interface and its descendants are for unicode character-oriented input
+ *
+ */
+class Reader
+{
+
+public:
+
+ /**
+ * Constructor.
+ */
+ Reader() = default;
+
+ /**
+ * Destructor
+ */
+ virtual ~Reader() = default;
+
+
+ virtual int available() = 0;
+
+ virtual void close() = 0;
+
+ virtual char get() = 0;
+
+ virtual Glib::ustring readLine() = 0;
+
+ virtual Glib::ustring readWord() = 0;
+
+ /* Input formatting */
+ virtual const Reader& readBool (bool& val ) = 0;
+ virtual const Reader& operator>> (bool& val ) = 0;
+
+ virtual const Reader& readShort (short &val) = 0;
+ virtual const Reader& operator>> (short &val) = 0;
+
+ virtual const Reader& readUnsignedShort (unsigned short &val) = 0;
+ virtual const Reader& operator>> (unsigned short &val) = 0;
+
+ virtual const Reader& readInt (int &val) = 0;
+ virtual const Reader& operator>> (int &val) = 0;
+
+ virtual const Reader& readUnsignedInt (unsigned int &val) = 0;
+ virtual const Reader& operator>> (unsigned int &val) = 0;
+
+ virtual const Reader& readLong (long &val) = 0;
+ virtual const Reader& operator>> (long &val) = 0;
+
+ virtual const Reader& readUnsignedLong (unsigned long &val) = 0;
+ virtual const Reader& operator>> (unsigned long &val) = 0;
+
+ virtual const Reader& readFloat (float &val) = 0;
+ virtual const Reader& operator>> (float &val) = 0;
+
+ virtual const Reader& readDouble (double &val) = 0;
+ virtual const Reader& operator>> (double &val) = 0;
+
+}; // interface Reader
+
+
+
+/**
+ * This class and its descendants are for unicode character-oriented input
+ *
+ */
+class BasicReader : public Reader
+{
+
+public:
+
+ BasicReader(Reader &sourceStream);
+
+ ~BasicReader() override = default;
+
+ int available() override;
+
+ void close() override;
+
+ char get() override;
+
+ Glib::ustring readLine() override;
+
+ Glib::ustring readWord() override;
+
+ /* Input formatting */
+ const Reader& readBool (bool& val ) override;
+ const Reader& operator>> (bool& val ) override
+ { return readBool(val); }
+
+ const Reader& readShort (short &val) override;
+ const Reader& operator>> (short &val) override
+ { return readShort(val); }
+
+ const Reader& readUnsignedShort (unsigned short &val) override;
+ const Reader& operator>> (unsigned short &val) override
+ { return readUnsignedShort(val); }
+
+ const Reader& readInt (int &val) override;
+ const Reader& operator>> (int &val) override
+ { return readInt(val); }
+
+ const Reader& readUnsignedInt (unsigned int &val) override;
+ const Reader& operator>> (unsigned int &val) override
+ { return readUnsignedInt(val); }
+
+ const Reader& readLong (long &val) override;
+ const Reader& operator>> (long &val) override
+ { return readLong(val); }
+
+ const Reader& readUnsignedLong (unsigned long &val) override;
+ const Reader& operator>> (unsigned long &val) override
+ { return readUnsignedLong(val); }
+
+ const Reader& readFloat (float &val) override;
+ const Reader& operator>> (float &val) override
+ { return readFloat(val); }
+
+ const Reader& readDouble (double &val) override;
+ const Reader& operator>> (double &val) override
+ { return readDouble(val); }
+
+
+protected:
+
+ Reader *source;
+
+ BasicReader()
+ { source = nullptr; }
+
+private:
+
+}; // class BasicReader
+
+
+
+/**
+ * Class for placing a Reader on an open InputStream
+ *
+ */
+class InputStreamReader : public BasicReader
+{
+public:
+
+ InputStreamReader(InputStream &inputStreamSource);
+
+ /*Overload these 3 for your implementation*/
+ int available() override;
+
+ void close() override;
+
+ char get() override;
+
+
+private:
+
+ InputStream &inputStream;
+
+
+};
+
+/**
+ * Convenience class for reading formatted from standard input
+ *
+ */
+class StdReader : public BasicReader
+{
+public:
+
+ StdReader();
+
+ ~StdReader() override;
+
+ /*Overload these 3 for your implementation*/
+ int available() override;
+
+ void close() override;
+
+ char get() override;
+
+
+private:
+
+ InputStream *inputStream;
+
+
+};
+
+
+
+
+
+//#########################################################################
+//# W R I T E R
+//#########################################################################
+
+/**
+ * This interface and its descendants are for unicode character-oriented output
+ *
+ */
+class Writer
+{
+
+public:
+
+ /**
+ * Constructor.
+ */
+ Writer() = default;
+
+ /**
+ * Destructor
+ */
+ virtual ~Writer() = default;
+
+ virtual void close() = 0;
+
+ virtual void flush() = 0;
+
+ virtual void put(char ch) = 0;
+
+ /* Formatted output */
+ virtual Writer& printf(char const *fmt, ...) G_GNUC_PRINTF(2,3) = 0;
+
+ virtual Writer& writeChar(char val) = 0;
+
+ virtual Writer& writeUString(const Glib::ustring &val) = 0;
+
+ virtual Writer& writeStdString(const std::string &val) = 0;
+
+ virtual Writer& writeString(const char *str) = 0;
+
+ virtual Writer& writeBool (bool val ) = 0;
+
+ virtual Writer& writeShort (short val ) = 0;
+
+ virtual Writer& writeUnsignedShort (unsigned short val ) = 0;
+
+ virtual Writer& writeInt (int val ) = 0;
+
+ virtual Writer& writeUnsignedInt (unsigned int val ) = 0;
+
+ virtual Writer& writeLong (long val ) = 0;
+
+ virtual Writer& writeUnsignedLong (unsigned long val ) = 0;
+
+ virtual Writer& writeFloat (float val ) = 0;
+
+ virtual Writer& writeDouble (double val ) = 0;
+
+
+
+}; // interface Writer
+
+
+/**
+ * This class and its descendants are for unicode character-oriented output
+ *
+ */
+class BasicWriter : public Writer
+{
+
+public:
+
+ BasicWriter(Writer &destinationWriter);
+
+ ~BasicWriter() override = default;
+
+ /*Overload these 3 for your implementation*/
+ void close() override;
+
+ void flush() override;
+
+ void put(char ch) override;
+
+
+
+ /* Formatted output */
+ Writer &printf(char const *fmt, ...) override G_GNUC_PRINTF(2,3);
+
+ Writer& writeChar(char val) override;
+
+ Writer& writeUString(const Glib::ustring &val) override;
+
+ Writer& writeStdString(const std::string &val) override;
+
+ Writer& writeString(const char *str) override;
+
+ Writer& writeBool (bool val ) override;
+
+ Writer& writeShort (short val ) override;
+
+ Writer& writeUnsignedShort (unsigned short val ) override;
+
+ Writer& writeInt (int val ) override;
+
+ Writer& writeUnsignedInt (unsigned int val ) override;
+
+ Writer& writeLong (long val ) override;
+
+ Writer& writeUnsignedLong (unsigned long val ) override;
+
+ Writer& writeFloat (float val ) override;
+
+ Writer& writeDouble (double val ) override;
+
+
+protected:
+
+ Writer *destination;
+
+ BasicWriter()
+ { destination = nullptr; }
+
+private:
+
+}; // class BasicWriter
+
+
+
+Writer& operator<< (Writer &writer, char val);
+
+Writer& operator<< (Writer &writer, Glib::ustring &val);
+
+Writer& operator<< (Writer &writer, std::string &val);
+
+Writer& operator<< (Writer &writer, char const *val);
+
+Writer& operator<< (Writer &writer, bool val);
+
+Writer& operator<< (Writer &writer, short val);
+
+Writer& operator<< (Writer &writer, unsigned short val);
+
+Writer& operator<< (Writer &writer, int val);
+
+Writer& operator<< (Writer &writer, unsigned int val);
+
+Writer& operator<< (Writer &writer, long val);
+
+Writer& operator<< (Writer &writer, unsigned long val);
+
+Writer& operator<< (Writer &writer, float val);
+
+Writer& operator<< (Writer &writer, double val);
+
+
+
+
+/**
+ * Class for placing a Writer on an open OutputStream
+ *
+ */
+class OutputStreamWriter : public BasicWriter
+{
+public:
+
+ OutputStreamWriter(OutputStream &outputStreamDest);
+
+ /*Overload these 3 for your implementation*/
+ void close() override;
+
+ void flush() override;
+
+ void put(char ch) override;
+
+
+private:
+
+ OutputStream &outputStream;
+
+
+};
+
+
+/**
+ * Convenience class for writing to standard output
+ */
+class StdWriter : public BasicWriter
+{
+public:
+ StdWriter();
+
+ ~StdWriter() override;
+
+
+ void close() override;
+
+
+ void flush() override;
+
+
+ void put(char ch) override;
+
+
+private:
+
+ OutputStream *outputStream;
+
+};
+
+//#########################################################################
+//# U T I L I T Y
+//#########################################################################
+
+void pipeStream(InputStream &source, OutputStream &dest);
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+#endif // SEEN_INKSCAPE_IO_INKSCAPESTREAM_H
diff --git a/src/io/stream/streamtest.cpp b/src/io/stream/streamtest.cpp
new file mode 100644
index 0000000..5d27058
--- /dev/null
+++ b/src/io/stream/streamtest.cpp
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2015 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include <stdio.h>
+#include <limits.h> // realpath
+#include <stdlib.h> // mkdtemp, realpath
+#include <unistd.h> // chdir
+#include <string.h> // strlen, strncpy, strrchr
+
+#include "inkscapestream.h"
+#include "base64stream.h"
+#include "gzipstream.h"
+#include "stringstream.h"
+#include "uristream.h"
+#include "xsltstream.h"
+
+// quick way to pass the name of the executable into the test
+char myself[PATH_MAX];
+
+// names and path storage for other tests
+char *xmlname = "crystalegg.xml";
+char xmlpath[PATH_MAX];
+char *xslname = "doc2html.xsl";
+char xslpath[PATH_MAX];
+
+bool testUriStream()
+{
+ printf("######### UriStream copy ############\n");
+ Inkscape::URI inUri(myself);
+ Inkscape::IO::UriInputStream ins(inUri);
+ Inkscape::URI outUri("streamtest.copy");
+ Inkscape::IO::UriOutputStream outs(outUri);
+
+ pipeStream(ins, outs);
+
+ ins.close();
+ outs.close();
+
+ return true;
+}
+
+bool testWriter()
+{
+ printf("######### OutputStreamWriter ############\n");
+ Inkscape::IO::StdOutputStream outs;
+ Inkscape::IO::OutputStreamWriter writer(outs);
+
+ writer << "Hello, world! " << 123.45 << " times\n";
+
+ writer.printf("There are %f quick brown foxes in %d states\n", 123.45, 88);
+
+ return true;
+}
+
+bool testStdWriter()
+{
+ printf("######### StdWriter ############\n");
+ Inkscape::IO::StdWriter writer;
+
+ writer << "Hello, world! " << 123.45 << " times\n";
+
+ writer.printf("There are %f quick brown foxes in %d states\n", 123.45, 88);
+
+ return true;
+}
+
+bool testBase64()
+{
+ printf("######### Base64 Out ############\n");
+ Inkscape::URI plainInUri(xmlpath);
+ Inkscape::IO::UriInputStream ins1(plainInUri);
+
+ Inkscape::URI b64OutUri("crystalegg.xml.b64");
+ Inkscape::IO::UriOutputStream outs1(b64OutUri);
+ Inkscape::IO::Base64OutputStream b64Outs(outs1);
+
+ pipeStream(ins1, b64Outs);
+
+ ins1.close();
+ b64Outs.close();
+
+ printf("######### Base64 In ############\n");
+ Inkscape::URI b64InUri("crystalegg.xml.b64");
+ Inkscape::IO::UriInputStream ins2(b64InUri);
+ Inkscape::IO::Base64InputStream b64Ins(ins2);
+
+ Inkscape::URI plainOutUri("crystalegg.xml.b64dec");
+ Inkscape::IO::UriOutputStream outs2(plainOutUri);
+
+ pipeStream(b64Ins, outs2);
+
+ outs2.close();
+ b64Ins.close();
+
+ return true;
+}
+
+bool testXslt()
+{
+ printf("######### XSLT Sheet ############\n");
+ Inkscape::URI xsltSheetUri(xslpath);
+ Inkscape::IO::UriInputStream xsltSheetIns(xsltSheetUri);
+ Inkscape::IO::XsltStyleSheet stylesheet(xsltSheetIns);
+ xsltSheetIns.close();
+
+ Inkscape::URI sourceUri(xmlpath);
+ Inkscape::IO::UriInputStream xmlIns(sourceUri);
+
+ printf("######### XSLT Input ############\n");
+ Inkscape::URI destUri("test.html");
+ Inkscape::IO::UriOutputStream xmlOuts(destUri);
+
+ Inkscape::IO::XsltInputStream xsltIns(xmlIns, stylesheet);
+ pipeStream(xsltIns, xmlOuts);
+ xsltIns.close();
+ xmlOuts.close();
+
+
+ printf("######### XSLT Output ############\n");
+
+ Inkscape::IO::UriInputStream xmlIns2(sourceUri);
+
+ Inkscape::URI destUri2("test2.html");
+ Inkscape::IO::UriOutputStream xmlOuts2(destUri2);
+
+ Inkscape::IO::XsltOutputStream xsltOuts(xmlOuts2, stylesheet);
+ pipeStream(xmlIns2, xsltOuts);
+ xmlIns2.close();
+ xsltOuts.close();
+
+ return true;
+}
+
+bool testGzip()
+{
+
+ printf("######### Gzip Output ############\n");
+ Inkscape::URI gzUri("test.gz");
+ Inkscape::URI sourceUri(xmlpath);
+ Inkscape::IO::UriInputStream sourceIns(sourceUri);
+ Inkscape::IO::UriOutputStream gzOuts(gzUri);
+
+ Inkscape::IO::GzipOutputStream gzipOuts(gzOuts);
+ pipeStream(sourceIns, gzipOuts);
+ sourceIns.close();
+ gzipOuts.close();
+
+ printf("######### Gzip Input ############\n");
+
+ Inkscape::IO::UriInputStream gzIns(gzUri);
+ Inkscape::URI destUri("crystalegg2.xml");
+ Inkscape::IO::UriOutputStream destOuts(destUri);
+
+ Inkscape::IO::GzipInputStream gzipIns(gzIns);
+ pipeStream(gzipIns, destOuts);
+ gzipIns.close();
+ destOuts.close();
+
+ return true;
+}
+
+bool doTest()
+{
+ if (!testUriStream())
+ {
+ return false;
+ }
+ if (!testWriter())
+ {
+ return false;
+ }
+ if (!testStdWriter())
+ {
+ return false;
+ }
+ if (!testBase64())
+ {
+ return false;
+ }
+ if (!testXslt())
+ {
+ return false;
+ }
+ if (!testGzip())
+ {
+ return false;
+ }
+ return true;
+}
+
+void path_init(char *path, char *name)
+{
+ if (strlen(name)>PATH_MAX-strlen(myself))
+ {
+ printf("merging paths would be too long\n");
+ exit(1);
+ }
+ strncpy(path,myself,PATH_MAX);
+ char * ptr = strrchr(path,'/');
+ if (!ptr)
+ {
+ printf("path '%s' is missing any slashes\n",path);
+ exit(1);
+ }
+ strncpy(ptr+1,name,strlen(name)+1);
+ path[PATH_MAX-1] = '\0';
+ printf("'%s'\n",path);
+}
+
+
+int main(int argc, char **argv)
+{
+ if (!realpath(argv[0],myself))
+ {
+ perror("realpath");
+ return 1;
+ }
+ path_init(xmlpath,xmlname);
+ path_init(xslpath,xslname);
+
+ // create temp files somewhere else instead of current dir
+ // TODO: clean them up too
+ char * testpath = strdup("/tmp/streamtest-XXXXXX");
+ char * testpath2;
+ testpath2 = mkdtemp(testpath);
+ free(testpath);
+ if (!testpath2)
+ {
+ perror("mkdtemp");
+ return 1;
+ }
+ if (chdir(testpath2))
+ {
+ perror("chdir");
+ return 1;
+ }
+
+ if (!doTest())
+ {
+ printf("#### Test failed\n");
+ return 1;
+ }
+ else
+ {
+ printf("##### Test succeeded\n");
+ }
+ return 0;
+}
diff --git a/src/io/stream/stringstream.cpp b/src/io/stream/stringstream.cpp
new file mode 100644
index 0000000..0259869
--- /dev/null
+++ b/src/io/stream/stringstream.cpp
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Our base String stream classes. We implement these to
+ * be based on Glib::ustring
+ *
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ *
+ * Copyright (C) 2004 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "stringstream.h"
+
+namespace Inkscape
+{
+namespace IO
+{
+
+
+//#########################################################################
+//# S T R I N G I N P U T S T R E A M
+//#########################################################################
+
+
+/**
+ *
+ */
+StringInputStream::StringInputStream(Glib::ustring &sourceString)
+ : buffer(sourceString)
+{
+ position = 0;
+}
+
+/**
+ *
+ */
+StringInputStream::~StringInputStream()
+= default;
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this input stream without blocking by the next caller of a method for
+ * this input stream.
+ */
+int StringInputStream::available()
+{
+ return buffer.size() - position;
+}
+
+
+/**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ */
+void StringInputStream::close()
+{
+}
+
+/**
+ * Reads the next byte of data from the input stream. -1 if EOF
+ */
+int StringInputStream::get()
+{
+ if (position >= (int)buffer.size())
+ return -1;
+ int ch = (int) buffer[position++];
+ return ch;
+}
+
+
+
+
+//#########################################################################
+//# S T R I N G O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ *
+ */
+StringOutputStream::StringOutputStream()
+= default;
+
+/**
+ *
+ */
+StringOutputStream::~StringOutputStream()
+= default;
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+void StringOutputStream::close()
+{
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void StringOutputStream::flush()
+{
+ //nothing to do
+}
+
+/**
+ * Writes the specified byte to this output stream.
+ */
+int StringOutputStream::put(char ch)
+{
+ buffer.push_back(ch);
+ return 1;
+}
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
diff --git a/src/io/stream/stringstream.h b/src/io/stream/stringstream.h
new file mode 100644
index 0000000..3afb9a8
--- /dev/null
+++ b/src/io/stream/stringstream.h
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef __INKSCAPE_IO_STRINGSTREAM_H__
+#define __INKSCAPE_IO_STRINGSTREAM_H__
+
+#include <glibmm/ustring.h>
+
+#include "inkscapestream.h"
+
+
+namespace Inkscape
+{
+namespace IO
+{
+
+
+//#########################################################################
+//# S T R I N G I N P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for reading character from a Glib::ustring
+ *
+ */
+class StringInputStream : public InputStream
+{
+
+public:
+
+ StringInputStream(Glib::ustring &sourceString);
+
+ ~StringInputStream() override;
+
+ int available() override;
+
+ void close() override;
+
+ int get() override;
+
+private:
+
+ Glib::ustring &buffer;
+
+ long position;
+
+}; // class StringInputStream
+
+
+
+
+//#########################################################################
+//# S T R I N G O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for sending a stream to a Glib::ustring
+ *
+ */
+class StringOutputStream : public OutputStream
+{
+
+public:
+
+ StringOutputStream();
+
+ ~StringOutputStream() override;
+
+ void close() override;
+
+ void flush() override;
+
+ int put(char ch) override;
+
+ virtual Glib::ustring &getString()
+ { return buffer; }
+
+ virtual void clear()
+ { buffer = ""; }
+
+private:
+
+ Glib::ustring buffer;
+
+
+}; // class StringOutputStream
+
+
+
+
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+
+#endif /* __INKSCAPE_IO_STRINGSTREAM_H__ */
diff --git a/src/io/stream/uristream.cpp b/src/io/stream/uristream.cpp
new file mode 100644
index 0000000..734b8fa
--- /dev/null
+++ b/src/io/stream/uristream.cpp
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Our base String stream classes. We implement these to
+ * be based on Glib::ustring
+ *
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ *
+ * Copyright (C) 2004 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "uristream.h"
+#include "io/sys.h"
+
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# F I L E I N P U T S T R E A M
+//#########################################################################
+
+
+/**
+ *
+ */
+FileInputStream::FileInputStream(FILE *source)
+ : inf(source)
+{
+ if (!inf) {
+ Glib::ustring err = "FileInputStream passed NULL";
+ throw StreamException(err);
+ }
+}
+
+/**
+ *
+ */
+FileInputStream::~FileInputStream()
+{
+ close();
+}
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this input stream without blocking by the next caller of a method for
+ * this input stream.
+ */
+int FileInputStream::available()
+{
+ return 0;
+}
+
+
+/**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ */
+void FileInputStream::close()
+{
+ if (!inf)
+ return;
+ fflush(inf);
+ fclose(inf);
+ inf=nullptr;
+}
+
+/**
+ * Reads the next byte of data from the input stream. -1 if EOF
+ */
+int FileInputStream::get()
+{
+ int retVal = -1;
+ if (!inf || feof(inf))
+ {
+ retVal = -1;
+ }
+ else
+ {
+ retVal = fgetc(inf);
+ }
+
+ return retVal;
+}
+
+
+
+
+//#########################################################################
+//# F I L E O U T P U T S T R E A M
+//#########################################################################
+
+FileOutputStream::FileOutputStream(FILE *fp)
+ : ownsFile(false)
+ , outf(fp)
+{
+ if (!outf) {
+ Glib::ustring err = "FileOutputStream given null file ";
+ throw StreamException(err);
+ }
+}
+
+/**
+ *
+ */
+FileOutputStream::~FileOutputStream()
+{
+ close();
+}
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+void FileOutputStream::close()
+{
+ if (!outf)
+ return;
+ fflush(outf);
+ if ( ownsFile )
+ fclose(outf);
+ outf=nullptr;
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void FileOutputStream::flush()
+{
+ if (!outf)
+ return;
+ fflush(outf);
+}
+
+/**
+ * Writes the specified byte to this output stream.
+ */
+int FileOutputStream::put(char ch)
+{
+ unsigned char uch;
+
+ if (!outf)
+ return -1;
+ uch = (unsigned char)(ch & 0xff);
+ if (fputc(uch, outf) == EOF) {
+ Glib::ustring err = "ERROR writing to file ";
+ throw StreamException(err);
+ }
+
+ return 1;
+}
+
+
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
+
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/io/stream/uristream.h b/src/io/stream/uristream.h
new file mode 100644
index 0000000..f0544d8
--- /dev/null
+++ b/src/io/stream/uristream.h
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_INKSCAPE_IO_URISTREAM_H
+#define SEEN_INKSCAPE_IO_URISTREAM_H
+/**
+ * @file
+ * This should be the only way that we provide sources/sinks
+ * to any input/output stream.
+ */
+/*
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ *
+ * Copyright (C) 2004 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "object/uri.h"
+
+#include "inkscapestream.h"
+
+
+namespace Inkscape
+{
+
+class URI;
+
+namespace IO
+{
+
+//#########################################################################
+//# F I L E I N P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for receiving a stream of data from a file
+ */
+class FileInputStream : public InputStream
+{
+
+public:
+ FileInputStream(FILE *source);
+
+ ~FileInputStream() override;
+
+ int available() override;
+
+ void close() override;
+
+ int get() override;
+
+private:
+ FILE *inf; //for file: uris
+
+}; // class FileInputStream
+
+
+
+
+//#########################################################################
+//# F I L E O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for sending a stream to a destination file
+ */
+class FileOutputStream : public OutputStream
+{
+
+public:
+
+ FileOutputStream(FILE *fp);
+
+ ~FileOutputStream() override;
+
+ void close() override;
+
+ void flush() override;
+
+ int put(char ch) override;
+
+private:
+
+ bool ownsFile;
+
+ FILE *outf; //for file: uris
+
+}; // class FileOutputStream
+
+
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+#endif // SEEN_INKSCAPE_IO_URISTREAM_H
+
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/io/stream/xsltstream.cpp b/src/io/stream/xsltstream.cpp
new file mode 100644
index 0000000..882db30
--- /dev/null
+++ b/src/io/stream/xsltstream.cpp
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * XSL Transforming input and output classes
+ *
+ * Authors:
+ * Bob Jamison <ishmalius@gmail.com>
+ *
+ * Copyright (C) 2004-2008 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "xsltstream.h"
+#include "stringstream.h"
+#include <libxslt/transform.h>
+
+
+
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# X S L T S T Y L E S H E E T
+//#########################################################################
+
+/**
+ *
+ */
+XsltStyleSheet::XsltStyleSheet(InputStream &xsltSource)
+
+ : stylesheet(nullptr)
+{
+ if (!read(xsltSource)) {
+ throw StreamException("read failed");
+ }
+}
+
+/**
+ *
+ */
+XsltStyleSheet::XsltStyleSheet()
+ : stylesheet(nullptr)
+{
+}
+
+
+
+/**
+ *
+ */
+bool XsltStyleSheet::read(InputStream &xsltSource)
+{
+ StringOutputStream outs;
+ pipeStream(xsltSource, outs);
+ std::string strBuf = outs.getString().raw();
+ xmlDocPtr doc = xmlParseMemory(strBuf.c_str(), strBuf.size());
+ stylesheet = xsltParseStylesheetDoc(doc);
+ //following not necessary. handled by xsltFreeStylesheet(stylesheet);
+ //xmlFreeDoc(doc);
+ if (!stylesheet)
+ return false;
+ return true;
+}
+
+
+/**
+ *
+ */
+XsltStyleSheet::~XsltStyleSheet()
+{
+ if (stylesheet)
+ xsltFreeStylesheet(stylesheet);
+}
+
+
+
+//#########################################################################
+//# X S L T I N P U T S T R E A M
+//#########################################################################
+
+
+/**
+ *
+ */
+XsltInputStream::XsltInputStream(InputStream &xmlSource, XsltStyleSheet &sheet)
+ : BasicInputStream(xmlSource), stylesheet(sheet)
+{
+ //Load the data
+ StringOutputStream outs;
+ pipeStream(source, outs);
+ std::string strBuf = outs.getString().raw();
+
+ //Do the processing
+ const char *params[1];
+ params[0] = nullptr;
+ xmlDocPtr srcDoc = xmlParseMemory(strBuf.c_str(), strBuf.size());
+ xmlDocPtr resDoc = xsltApplyStylesheet(stylesheet.stylesheet, srcDoc, params);
+ xmlDocDumpFormatMemory(resDoc, &outbuf, &outsize, 1);
+ outpos = 0;
+
+ //Free our mem
+ xmlFreeDoc(resDoc);
+ xmlFreeDoc(srcDoc);
+}
+
+/**
+ *
+ */
+XsltInputStream::~XsltInputStream()
+{
+ xmlFree(outbuf);
+}
+
+/**
+ * Returns the number of bytes that can be read (or skipped over) from
+ * this input stream without blocking by the next caller of a method for
+ * this input stream.
+ */
+int XsltInputStream::available()
+{
+ return outsize - outpos;
+}
+
+
+/**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ */
+void XsltInputStream::close()
+{
+ closed = true;
+}
+
+/**
+ * Reads the next byte of data from the input stream. -1 if EOF
+ */
+int XsltInputStream::get()
+{
+ if (closed)
+ return -1;
+ if (outpos >= outsize)
+ return -1;
+ int ch = (int) outbuf[outpos++];
+ return ch;
+}
+
+
+
+
+
+
+//#########################################################################
+//# X S L T O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ *
+ */
+XsltOutputStream::XsltOutputStream(OutputStream &dest, XsltStyleSheet &sheet)
+ : BasicOutputStream(dest), stylesheet(sheet)
+{
+ flushed = false;
+}
+
+/**
+ *
+ */
+XsltOutputStream::~XsltOutputStream()
+{
+ //do not automatically close
+}
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+void XsltOutputStream::close()
+{
+ flush();
+ destination.close();
+}
+
+/**
+ * Flushes this output stream and forces any buffered output
+ * bytes to be written out.
+ */
+void XsltOutputStream::flush()
+{
+ if (flushed)
+ {
+ destination.flush();
+ return;
+ }
+
+ //Do the processing
+ xmlChar *resbuf;
+ int resSize;
+ const char *params[1];
+ params[0] = nullptr;
+ xmlDocPtr srcDoc = xmlParseMemory(outbuf.raw().c_str(), outbuf.size());
+ xmlDocPtr resDoc = xsltApplyStylesheet(stylesheet.stylesheet, srcDoc, params);
+ xmlDocDumpFormatMemory(resDoc, &resbuf, &resSize, 1);
+ /*
+ xmlErrorPtr err = xmlGetLastError();
+ if (err)
+ {
+ throw StreamException(err->message);
+ }
+ */
+
+ for (int i=0 ; i<resSize ; i++)
+ {
+ char ch = resbuf[i];
+ destination.put(ch);
+ }
+
+ //Free our mem
+ xmlFree(resbuf);
+ xmlFreeDoc(resDoc);
+ xmlFreeDoc(srcDoc);
+ destination.flush();
+ flushed = true;
+}
+
+/**
+ * Writes the specified byte to this output stream.
+ */
+int XsltOutputStream::put(char ch)
+{
+ outbuf.push_back(ch);
+ return 1;
+}
+
+
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
diff --git a/src/io/stream/xsltstream.h b/src/io/stream/xsltstream.h
new file mode 100644
index 0000000..7410ddc
--- /dev/null
+++ b/src/io/stream/xsltstream.h
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_INKSCAPE_IO_XSLTSTREAM_H
+#define SEEN_INKSCAPE_IO_XSLTSTREAM_H
+/**
+ * @file
+ * Xslt-enabled input and output streams.
+ */
+/*
+ * Authors:
+ * Bob Jamison <ishmalius@gmail.com>
+ *
+ * Copyright (C) 2004-2008 Inkscape.org
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "inkscapestream.h"
+
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+
+
+namespace Inkscape
+{
+namespace IO
+{
+
+//#########################################################################
+//# X S L T S T Y L E S H E E T
+//#########################################################################
+/**
+ * This is a container for reusing a loaded stylesheet
+ */
+class XsltStyleSheet
+{
+
+public:
+
+ /**
+ * Constructor with loading
+ */
+ XsltStyleSheet(InputStream &source);
+
+ /**
+ * Simple constructor, no loading
+ */
+ XsltStyleSheet();
+
+ /**
+ * Loader
+ */
+ bool read(InputStream &source);
+
+ /**
+ * Destructor
+ */
+ virtual ~XsltStyleSheet();
+
+ xsltStylesheetPtr stylesheet;
+
+
+}; // class XsltStyleSheet
+
+
+//#########################################################################
+//# X S L T I N P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for transforming stream input by a given stylesheet
+ */
+class XsltInputStream : public BasicInputStream
+{
+
+public:
+
+ XsltInputStream(InputStream &xmlSource, XsltStyleSheet &stylesheet);
+
+ ~XsltInputStream() override;
+
+ int available() override;
+
+ void close() override;
+
+ int get() override;
+
+
+private:
+
+ XsltStyleSheet &stylesheet;
+
+ xmlChar *outbuf;
+ int outsize;
+ int outpos;
+
+}; // class XsltInputStream
+
+
+
+
+//#########################################################################
+//# X S L T O U T P U T S T R E A M
+//#########################################################################
+
+/**
+ * This class is for transforming stream output by a given stylesheet
+ */
+class XsltOutputStream : public BasicOutputStream
+{
+
+public:
+
+ XsltOutputStream(OutputStream &destination, XsltStyleSheet &stylesheet);
+
+ ~XsltOutputStream() override;
+
+ void close() override;
+
+ void flush() override;
+
+ int put(char ch) override;
+
+private:
+
+ XsltStyleSheet &stylesheet;
+
+ Glib::ustring outbuf;
+
+ bool flushed;
+
+}; // class XsltOutputStream
+
+
+
+} // namespace IO
+} // namespace Inkscape
+
+
+#endif /* __INKSCAPE_IO_XSLTSTREAM_H__ */