From 296033668c8003421b1c5d10cf58a9a7f0ed57e4 Mon Sep 17 00:00:00 2001 From: Justin Gassner Date: Sun, 5 Mar 2023 19:23:26 +0100 Subject: Support and force zopfli compression Original patch developed by jarnoh [1] and ported by me. [1] https://github.com/jarnoh/qpdf --- .gitmodules | 3 +++ include/qpdf/Pl_Flate.hh | 3 +++ libqpdf/Pl_Flate.cc | 62 +++++++++++++++++++++++++++++++++++++++++++++++- zopfli | 1 + 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 zopfli diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..827f1bd3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "zopfli"] + path = zopfli + url = https://github.com/google/zopfli diff --git a/include/qpdf/Pl_Flate.hh b/include/qpdf/Pl_Flate.hh index 41ae7e4a..325c84b6 100644 --- a/include/qpdf/Pl_Flate.hh +++ b/include/qpdf/Pl_Flate.hh @@ -90,6 +90,9 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline std::function callback; }; + unsigned char* inbuf; /* additional buffer for zopfli input */ + size_t inbuf_size; + std::shared_ptr m; }; diff --git a/libqpdf/Pl_Flate.cc b/libqpdf/Pl_Flate.cc index d332e635..7fe8cc01 100644 --- a/libqpdf/Pl_Flate.cc +++ b/libqpdf/Pl_Flate.cc @@ -7,6 +7,29 @@ #include #include +#define USE_ZOPFLI 1 + +#if USE_ZOPFLI +// quick hack, link zopfli here +extern "C" { +#define adler32 zopfli_adler32 +#include "../zopfli/src/zopfli/zopfli.h" +#include "../zopfli/src/zopfli/blocksplitter.c" +#include "../zopfli/src/zopfli/cache.c" +#include "../zopfli/src/zopfli/deflate.c" +#include "../zopfli/src/zopfli/hash.c" +#include "../zopfli/src/zopfli/gzip_container.c" +#include "../zopfli/src/zopfli/katajainen.c" +#include "../zopfli/src/zopfli/lz77.c" +#include "../zopfli/src/zopfli/squeeze.c" +#include "../zopfli/src/zopfli/tree.c" +#include "../zopfli/src/zopfli/util.c" +#include "../zopfli/src/zopfli/zlib_container.c" +#include "../zopfli/src/zopfli/zopfli_lib.c" +#undef adler32 +} +#endif + int Pl_Flate::compression_level = Z_DEFAULT_COMPRESSION; Pl_Flate::Members::Members(size_t out_bufsize, action_e action) : @@ -54,13 +77,18 @@ Pl_Flate::Members::~Members() Pl_Flate::Pl_Flate( char const* identifier, Pipeline* next, action_e action, unsigned int out_bufsize_int) : Pipeline(identifier, next), - m(new Members(QIntC::to_size(out_bufsize_int), action)) + m(new Members(QIntC::to_size(out_bufsize_int), action)), + inbuf(0), inbuf_size(0) { } Pl_Flate::~Pl_Flate() // NOLINT (modernize-use-equals-default) { // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer + +#if USE_ZOPFLI + free(inbuf); inbuf = 0; +#endif } void @@ -109,6 +137,17 @@ Pl_Flate::handleData(unsigned char const* data, size_t len, int flush) zstream.next_in = const_cast(data); zstream.avail_in = QIntC::to_uint(len); +#if USE_ZOPFLI + if (this->m->action == a_deflate) + { + inbuf = static_cast(realloc(inbuf, inbuf_size+len)); + if(!inbuf) throw std::runtime_error("realloc failed"); + memcpy(inbuf+inbuf_size, data, len); + inbuf_size += len; + return; + } +#endif + if (!m->initialized) { int err = Z_OK; @@ -188,6 +227,27 @@ void Pl_Flate::finish() { try { +#if USE_ZOPFLI + if (m->action == a_deflate) + { + ZopfliOptions options; + ZopfliInitOptions(&options); + + unsigned char* out = 0; + size_t out_size = 0; + + ZopfliCompress(&options, ZOPFLI_FORMAT_ZLIB, inbuf, inbuf_size, &out, &out_size); + free(inbuf); inbuf = 0; + + if(inbuf_size!=0 && out_size==0) throw std::runtime_error("zopfli failed"); + + getNext()->write(out, out_size); + getNext()->finish(); + free(out); out = 0; + + return; + } +#endif if (m->outbuf.get()) { if (m->initialized) { z_stream& zstream = *(static_cast(m->zdata)); diff --git a/zopfli b/zopfli new file mode 160000 index 00000000..831773bc --- /dev/null +++ b/zopfli @@ -0,0 +1 @@ +Subproject commit 831773bc28e318b91a3255fa12c9fcde1606058b -- cgit v1.2.3-54-g00ecf