1
0
mirror of https://github.com/nzeemin/escparser.git synced 2025-04-19 17:42:16 +03:00
escparser/Drivers.cpp
2022-11-13 00:07:09 +03:00

262 lines
8.9 KiB
C++

/* This file is part of UKNCBTL.
UKNCBTL is free software: you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
UKNCBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with
UKNCBTL. If not, see <http://www.gnu.org/licenses/>. */
#include "ESCParser.h"
#include <iomanip>
#include "zlib/zlib.h"
//////////////////////////////////////////////////////////////////////
// SVG driver
//NOTE: The most recent SVG standard is 1.2 tiny. Multipage support appears in 1.2 full.
// So, currently SVG does not have multipage support, and browsers can't interpret multipage SVGs.
void OutputDriverSvg::WriteBeginning(int pagestotal)
{
m_output << "<?xml version=\"1.0\"?>" << std::endl;
m_output << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.0\">" << std::endl;
}
void OutputDriverSvg::WriteEnding()
{
m_output << "</svg>" << std::endl;
}
void OutputDriverSvg::WriteStrike(float x, float y, float r)
{
float cx = x / 10.0f;
float cy = y / 10.0f;
float cr = r / 10.0f;
m_output << "<circle cx=\"" << cx << "\" cy=\"" << cy << "\" r=\"" << cr << "\" />" << std::endl;
}
//////////////////////////////////////////////////////////////////////
// PostScript driver
void OutputDriverPostScript::WriteBeginning(int pagestotal)
{
m_output << "%!PS-Adobe-2.0" << std::endl;
m_output << "%%Creator: ESCParser" << std::endl;
m_output << "%%Pages: " << pagestotal << std::endl;
// PS procedure used to simplify WriteStrike output
m_output << "/dotxyr { newpath 0 360 arc fill } def" << std::endl;
}
void OutputDriverPostScript::WriteEnding()
{
m_output << "%%EOF" << std::endl;
}
void OutputDriverPostScript::WritePageBeginning(int pageno)
{
m_output << "%%Page: " << pageno << " " << pageno << std::endl;
m_output << "0 850 translate 1 -1 scale" << std::endl;
m_output << "0 setgray" << std::endl;
}
void OutputDriverPostScript::WritePageEnding()
{
m_output << "showpage" << std::endl;
}
void OutputDriverPostScript::WriteStrike(float x, float y, float r)
{
float cx = x / 10.0f;
float cy = y / 10.0f;
float cr = r / 10.0f;
char buffer[24];
sprintf_s(buffer, sizeof(buffer), "%.2f %.2f %.1f", cx, cy, cr);
m_output << buffer << " dotxyr" << std::endl;
}
//////////////////////////////////////////////////////////////////////
// PDF driver
// See below
void ascii85_encode_tuple(const unsigned char* src, char* dst);
const float PdfPageSizeX = 595.0f; // A4 210mm / 25.4 * 72, rounded
const float PdfPageSizeY = 842.0f; // A4 297mm / 25.4 * 72, rounded
void OutputDriverPdf::WriteBeginning(int pagestotal)
{
xref.push_back(PdfXrefItem(0, 65535, 'f'));
m_output << "%PDF-1.3" << std::endl;
xref.push_back(PdfXrefItem(m_output.tellp(), 0, 'n'));
m_output << "1 0 obj <<";
m_output << "/Producer (ESCParser utility by Nikita Zimin)";
m_output << ">>" << std::endl << "endobj" << std::endl;
xref.push_back(PdfXrefItem(m_output.tellp(), 0, 'n'));
m_output << "2 0 obj <</Type /Catalog /Pages 3 0 R>>" << std::endl;
m_output << "endobj" << std::endl;
xref.push_back(PdfXrefItem(m_output.tellp(), 0, 'n'));
m_output << "3 0 obj <</Type /Pages /Kids [";
for (int i = 0; i < pagestotal; i++)
{
if (i > 0)
m_output << " ";
m_output << i * 2 + 4 << " 0 R"; // Page objects: 4, 6, 8, etc.
}
m_output << "] /Count " << pagestotal << ">>" << std::endl;
m_output << "endobj" << std::endl;
}
void OutputDriverPdf::WriteEnding()
{
std::streamoff startxref = m_output.tellp();
m_output << "xref" << std::endl;
m_output << "0 " << xref.size() << std::endl;
for (std::vector<PdfXrefItem>::iterator it = xref.begin(); it != xref.end(); ++it)
{
m_output << std::setw(10) << std::setfill('0') << (*it).offset << " ";
m_output << std::setw(5) << (*it).size << " ";
m_output << (*it).flag << std::endl;
}
m_output << "trailer" << std::endl;
m_output << "<</Size " << xref.size() << " /Root 2 0 R /Info 1 0 R>>" << std::endl;
m_output << "startxref" << std::endl;
m_output << startxref << std::endl;
m_output << "%%EOF" << std::endl;
}
void OutputDriverPdf::WritePageBeginning(int pageno)
{
xref.push_back(PdfXrefItem(m_output.tellp(), 0, 'n'));
int objnopage = pageno * 2 + 2; // 4, 6, 8, etc.
int objnostream = pageno * 2 + 3; // 5, 7, 9, etc.
m_output << objnopage << " 0 obj<</Type /Page /Parent 3 0 R ";
m_output << "/MediaBox [0 0 " << PdfPageSizeX << " " << PdfPageSizeY << "] "; // Page bounds
m_output << "/Contents " << objnostream << " 0 R ";
m_output << "/Resources<< >> >>" << std::endl; // Resources is required key
m_output << "endobj" << std::endl;
xref.push_back(PdfXrefItem(m_output.tellp(), 0, 'n'));
m_output << objnostream << " 0 obj<<";
strikesize = 0.0f;
pagebuf.clear();
pagebuf.append("1 J"); // Round cap
}
void OutputDriverPdf::WritePageEnding()
{
// Preparing for inflate
size_t outsize = pagebuf.length() + pagebuf.length() / 2 + 200;
outsize = (outsize + 3) / 4 * 4; // Make sure we have 4-byte aligned size
Bytef* zbuffer = new Bytef[outsize]; memset(zbuffer, 0, outsize);
z_stream zstrm; memset(&zstrm, 0, sizeof(zstrm));
zstrm.avail_in = pagebuf.length();
zstrm.avail_out = outsize;
zstrm.next_in = (Bytef*) pagebuf.c_str();
zstrm.next_out = zbuffer;
// Trying to inflate
bool inflatedok = false;
int rsti = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION);
if (rsti == Z_OK)
{
int rst2 = deflate(&zstrm, Z_FINISH);
if (rst2 >= 0)
inflatedok = true;
}
size_t inflatesize = zstrm.total_out;
if (inflatedok)
{
std::string pagebufz;
char buffer[6]; memset(buffer, 0, sizeof(buffer));
for (size_t i = 0; i < inflatesize; i += 4)
{
unsigned char * bytes = zbuffer + i;
ascii85_encode_tuple(bytes, buffer);
pagebufz.append(buffer);
}
pagebufz.append("~>");
m_output << "/Length " << pagebufz.length() << " /Filter [/ASCII85Decode /FlateDecode]>>stream" << std::endl;
m_output << pagebufz.c_str() << std::endl;
m_output << "endstream" << std::endl << "endobj" << std::endl;
}
else
{
m_output << "/Length " << pagebuf.length() << ">>stream" << std::endl;
m_output << pagebuf.c_str() << std::endl;
m_output << "endstream" << std::endl << "endobj" << std::endl;
}
deflateEnd(&zstrm);
delete[] zbuffer; zbuffer = 0;
}
void OutputDriverPdf::WriteStrike(float x, float y, float r)
{
char buffer[80];
if (strikesize != r / 5.0f)
{
strikesize = r / 5.0f;
sprintf_s(buffer, sizeof(buffer), " %g w", strikesize); // Line width
pagebuf.append(buffer);
}
float cx = x / 10.0f;
float cy = PdfPageSizeY - y / 10.0f;
sprintf_s(buffer, sizeof(buffer), " %g %g m %g %g l s", cx, cy, cx, cy);
pagebuf.append(buffer);
}
//////////////////////////////////////////////////////////////////////
// ASCII85 encoding for PDF
typedef unsigned int uint32_t;
// make sure uint32_t is 32-bit
typedef char Z85_uint32_t_static_assert[(sizeof(uint32_t) * 8 == 32) * 2 - 1];
#define DIV85_MAGIC 3233857729ULL
// make sure magic constant is 64-bit
typedef char Z85_div85_magic_static_assert[(sizeof(DIV85_MAGIC) * 8 == 64) * 2 - 1];
#define DIV85(number) ((uint32_t)((DIV85_MAGIC * (number)) >> 32) >> 6)
static const char* base85 =
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu";
void ascii85_encode_tuple(const unsigned char* src, char* dst)
{
// unpack big-endian frame
uint32_t value = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
if (value == 0) // Special case for zero
{
dst[0] = 'z';
dst[2] = dst[3] = dst[4] = dst[5] = 0;
}
else
{
uint32_t value2;
value2 = DIV85(value); dst[4] = base85[value - value2 * 85]; value = value2;
value2 = DIV85(value); dst[3] = base85[value - value2 * 85]; value = value2;
value2 = DIV85(value); dst[2] = base85[value - value2 * 85]; value = value2;
value2 = DIV85(value); dst[1] = base85[value - value2 * 85];
dst[0] = base85[value2];
}
}
//////////////////////////////////////////////////////////////////////