Skip to content

Commit

Permalink
Support compression
Browse files Browse the repository at this point in the history
NOTE: Some printers need the whole file "magic" in the first
compressed chunk.
  • Loading branch information
attah committed Jan 28, 2024
1 parent 58da466 commit ec4df4a
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ccpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libpoppler-dev libpoppler-glib-dev libcurl4-openssl-dev
sudo apt-get install -y libpoppler-dev libpoppler-glib-dev libcairo2-dev libglib2.0-dev libjpeg-dev libcurl4-openssl-dev zlib1g-dev
- name: make
run: make -j
- name: make tests
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ ippdecode: bytestream.o ippmsg.o ippattr.o ippdecode.o
$(CXX) $^ -o $@

ippclient: ippmsg.o ippattr.o ippprintjob.o printparameters.o ippclient.o curlrequester.o minimime.o pdf2printable.o ppm2pwg.o baselinify.o bytestream.o
$(CXX) $^ $(shell pkg-config --libs poppler-glib) -ljpeg -lcurl -o $@
$(CXX) $^ $(shell pkg-config --libs poppler-glib) -ljpeg -lcurl -lz -o $@

minimime: minimime_main.o minimime.o bytestream.o
$(CXX) $^ -o $@
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ An IPP client that harnesses the above tools for converting files to be printed.

Install dependencies:

`sudo apt install libpoppler-dev libpoppler-glib-dev libcairo2-dev libglib2.0-dev libjpeg-dev libcurl4-openssl-dev`
`sudo apt install libpoppler-dev libpoppler-glib-dev libcairo2-dev libglib2.0-dev libjpeg-dev libcurl4-openssl-dev zlib1g-dev`

Build:

Expand Down
77 changes: 63 additions & 14 deletions lib/curlrequester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ void CurlRequester::doRun()
curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &buf);
curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, write_callback);

CURLcode res = curl_easy_perform(_curl);

_result = res;
_result = curl_easy_perform(_curl);
_resultMsg = buf;
});
}
Expand Down Expand Up @@ -75,6 +73,7 @@ bool CurlIppPosterBase::write(const void* data, size_t size)
memcpy(_data, data, size);
_size = size;
_offset = 0;
_compression = _nextCompression;
_canRead.unlock();
return true;
}
Expand Down Expand Up @@ -103,24 +102,70 @@ size_t CurlIppPosterBase::requestWrite(char* dest, size_t size)
if(!_reading)
{
_canRead.lock();
if(_done)
{
return 0;
}
_reading = true;
}

size_t actualSize = std::min(size, (_size - _offset));
size_t remaining = (_size - _offset);
size_t bytesWritten = 0;

memcpy(dest, (_data+_offset), actualSize);
_offset += actualSize;
if(_compression != Compression::None)
{
_zstrm.next_out = (Bytef*)dest;
_zstrm.avail_out = size;
_zstrm.next_in = (_data + _offset);
_zstrm.avail_in = remaining;
deflate(&_zstrm, _done ? Z_FINISH : Z_NO_FLUSH);
_offset += (remaining - _zstrm.avail_in);
bytesWritten = size - _zstrm.avail_out;
}
else // No compression = memcpy
{
bytesWritten = std::min(size, remaining);
memcpy(dest, (_data+_offset), bytesWritten);
_offset += bytesWritten;
}

if(_offset == _size)
if(!_done)
{
_reading = false;
_canWrite.unlock();
if(_offset == _size)
{ // End of input
_reading = false;
_canWrite.unlock();
}
if(bytesWritten == 0)
{ // Compression produced no output this time,
// we'll most likely have gone to write mode,
// keep waiting.
return requestWrite(dest, size);
}
}
return actualSize;

return bytesWritten;
}

void CurlIppPosterBase::setCompression(Compression compression)
{
if(_nextCompression != Compression::None)
{
throw std::logic_error("unsetting/changing compression");
}
_nextCompression = compression;

_zstrm.zalloc = Z_NULL;
_zstrm.zfree = Z_NULL;
_zstrm.opaque = Z_NULL;

int level = 11;
if(compression == Compression::Deflate)
{
level *= -1;
}
else if(compression == Compression::Gzip)
{
level += 16;
}

deflateInit2(&_zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, level, 7, Z_DEFAULT_STRATEGY);
}

CurlIppPosterBase::CurlIppPosterBase(std::string addr, bool ignoreSslErrors, bool verbose)
Expand All @@ -130,6 +175,7 @@ CurlIppPosterBase::CurlIppPosterBase(std::string addr, bool ignoreSslErrors, boo
_canRead.lock();

curl_easy_setopt(_curl, CURLOPT_POST, 1L);
curl_easy_setopt(_curl, CURLOPT_UPLOAD_BUFFERSIZE, 2*1024*1024);
curl_easy_setopt(_curl, CURLOPT_READFUNCTION, trampoline);
curl_easy_setopt(_curl, CURLOPT_READDATA, this);

Expand All @@ -146,6 +192,9 @@ CURLcode CurlIppPosterBase::await(Bytestream* data)
}

_done = true;
_data = Array<uint8_t>(0);
_size = 0;
_offset = 0;
_canRead.unlock();
return CurlRequester::await(data);
}
Expand Down
13 changes: 13 additions & 0 deletions lib/curlrequester.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
#endif

#include <curl/curl.h>
#include <zlib.h>
#include "lthread.h"
#include <array.h>
#include <bytestream.h>
#include <mutex>

enum class Compression
{
None = 0,
Deflate,
Gzip
};

class CurlRequester
{
public:
Expand Down Expand Up @@ -64,6 +72,9 @@ class CurlRequester
CURL* _curl;
struct curl_slist* _opts = NULL;

z_stream _zstrm;
Compression _nextCompression = Compression::None;
Compression _compression = Compression::None;
LThread _worker;
};

Expand All @@ -76,6 +87,8 @@ class CurlIppPosterBase : public CurlRequester
bool give(Bytestream& bts);
size_t requestWrite(char* dest, size_t size);

void setCompression(Compression compression);

static size_t trampoline(char* dest, size_t size, size_t nmemb, void* userp)
{
CurlIppPosterBase* cid = (CurlIppPosterBase*)userp;
Expand Down
32 changes: 27 additions & 5 deletions lib/ippprintjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ Error IppPrintJob::finalize(std::string inputFormat, int pages)
return Error("Failed to determine traget format");
}

if(MiniMime::isPrinterRaster(targetFormat))
{
List<std::string> compressionSupported = compression.getSupported();
if(compressionSupported.contains("gzip"))
{
compression.set("gzip");
}
else if(compressionSupported.contains("deflate"))
{
compression.set("deflate");
}
}

if(!printParams.setPaperSize(media.get(printParams.paperSizeName)))
{
return Error("Invalid paper size name");
Expand Down Expand Up @@ -437,12 +450,21 @@ Error IppPrintJob::doPrint(std::string addr, std::string inFile, ConvertFun conv
CurlIppStreamer cr(addr, true, verbose);
cr.write((char*)(hdr.raw()), hdr.size());

if(compression.get() == "gzip")
{
cr.setCompression(Compression::Gzip);
}
else if(compression.get() == "deflate")
{
cr.setCompression(Compression::Deflate);
}

WriteFun writeFun([&cr](unsigned char const* buf, unsigned int len) -> bool
{
if(len == 0)
return true;
return cr.write((const char*)buf, len);
});
{
if(len == 0)
return true;
return cr.write((const char*)buf, len);
});

ProgressFun progressFun([verbose](size_t page, size_t total) -> void
{
Expand Down
1 change: 1 addition & 0 deletions lib/ippprintjob.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class IppPrintJob
ChoiceSetting<std::string> scaling = ChoiceSetting<std::string>(&_printerAttrs, &jobAttrs, IppMsg::Keyword, "print-scaling");

ChoiceSetting<std::string> documentFormat = ChoiceSetting<std::string>(&_printerAttrs, &opAttrs, IppMsg::MimeMediaType, "document-format");
ChoiceSetting<std::string> compression = ChoiceSetting<std::string>(&_printerAttrs, &opAttrs, IppMsg::Keyword, "compression");

ChoiceSetting<std::string> mediaType = ChoiceSetting<std::string>(&_printerAttrs, &jobAttrs, IppMsg::Keyword, "media-type", "media-col");
ChoiceSetting<std::string> mediaSource = ChoiceSetting<std::string>(&_printerAttrs, &jobAttrs, IppMsg::Keyword, "media-source", "media-col");
Expand Down
6 changes: 2 additions & 4 deletions lib/pdf2printable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,14 @@ Error pdf_to_printable(std::string inFile, WriteFun writeFun, const PrintParamet
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
params.getPaperSizeWInPixels(),
params.getPaperSizeHInPixels());
Bytestream fileHdr;
if(params.format == PrintParameters::URF)
{
fileHdr = make_urf_file_hdr(seq.size());
outBts = make_urf_file_hdr(seq.size());
}
else
{
fileHdr = make_pwg_file_hdr();
outBts = make_pwg_file_hdr();
}
CHECK(writeFun(fileHdr.raw(), fileHdr.size()));
}
else if(params.format == PrintParameters::PDF)
{
Expand Down
2 changes: 1 addition & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
LDFLAGS = -ldl -Wl,--export-dynamic -lcurl -ljpeg $(shell pkg-config --libs poppler-glib)
LDFLAGS = -ldl -Wl,--export-dynamic -lcurl -ljpeg $(shell pkg-config --libs poppler-glib) -lz
CXXFLAGS = -std=c++17 -g -pedantic -Wall -Werror -Wextra -I ../lib -I ../bytestream \
$(shell pkg-config --cflags poppler-glib)
TEST_INCLUDES = -I ../lib -I ../bytestream/minitest -I ../bytestream/minitest/tests
Expand Down
9 changes: 9 additions & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,7 @@ TEST(finalize)
ASSERT(ip.printParams.duplexMode == PrintParameters::TwoSidedLongEdge);
ASSERT(ip.printParams.quality == PrintParameters::HighQuality);
ASSERT(ip.printParams.colorMode == PrintParameters::Gray8);
ASSERT(ip.compression.get() == "");

// Forget choices
ip = IppPrintJob(printerAttrs);
Expand Down Expand Up @@ -1810,6 +1811,7 @@ TEST(finalize)
ASSERT(ip.printParams.hwResH == 600);
ASSERT(ip.printParams.backXformMode == PrintParameters::Rotated);
ASSERT(ip.printParams.copies == 2);
ASSERT(ip.compression.get() == "");

// Add Postscript, forget settings
printerAttrs.set("document-format-supported",
Expand Down Expand Up @@ -1916,6 +1918,13 @@ TEST(finalize)
ASSERT(ip.printParams.format == PrintParameters::PWG);
ASSERT(ip.printParams.colorMode == PrintParameters::Gray8); // Since we don't support color

// Support compression, forget choices
printerAttrs.set("compression-supported", IppAttr(IppMsg::Keyword, IppOneSetOf {"none", "deflate", "gzip"}));
ip = IppPrintJob(printerAttrs);
ip.finalize("application/pdf", 0);
// gzip has higher priority
ASSERT(ip.compression.get() == "gzip");

}

TEST(additional_formats)
Expand Down

0 comments on commit ec4df4a

Please sign in to comment.