1
0
mirror of https://github.com/pdewacht/brlaser synced 2025-07-19 07:19:45 +02:00

brlaser version 1

This commit is contained in:
Peter De Wachter
2013-12-21 23:40:34 +01:00
commit b7af16450c
18 changed files with 1753 additions and 0 deletions

106
src/debug.cc Normal file
View File

@ -0,0 +1,106 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h"
#include <iostream>
#include <typeinfo>
namespace {
template <typename T>
void dump(const char *name, const T &value) {
std::cerr << "DEBUG: page header: " << name << " = " << value << '\n';
}
template <typename T, int N>
void dump(const char *name, const T (&value)[N]) {
std::cerr << "DEBUG: page header: " << name << " =";
for (int i = 0; i < N; ++i) {
std::cerr << ' ' << value[i];
}
std::cerr << '\n';
}
void dump(const char *name, const char *value) {
std::cerr << "DEBUG: page header: " << name << " = \"" << value << "\"\n";
}
template <int N, int M>
void dump(const char *name, const char (&value)[N][M]) {
std::cerr << "DEBUG: page header: " << name << " =";
for (int i = 0; i < N; ++i) {
std::cerr << " \"" << value[i] << '"';
}
std::cerr << '\n';
}
} // namespace
void dump_page_header(const cups_page_header2_t &h) {
#define d(f) dump(#f, h.f)
d(MediaClass);
d(MediaColor);
d(MediaType);
d(OutputType);
d(AdvanceDistance);
d(AdvanceMedia);
d(Collate);
d(CutMedia);
d(Duplex);
d(HWResolution);
d(ImagingBoundingBox);
d(InsertSheet);
d(Jog);
d(LeadingEdge);
d(Margins);
d(ManualFeed);
d(MediaPosition);
d(MediaWeight);
d(MirrorPrint);
d(NegativePrint);
d(NumCopies);
d(Orientation);
d(OutputFaceUp);
d(PageSize);
d(Separations);
d(TraySwitch);
d(Tumble);
d(cupsWidth);
d(cupsHeight);
d(cupsMediaType);
d(cupsBitsPerColor);
d(cupsBitsPerPixel);
d(cupsBytesPerLine);
d(cupsColorOrder);
d(cupsColorSpace);
d(cupsCompression);
d(cupsRowCount);
d(cupsRowFeed);
d(cupsRowStep);
d(cupsNumColors);
d(cupsBorderlessScalingFactor);
d(cupsPageSize);
d(cupsImagingBBox);
d(cupsInteger);
d(cupsReal);
d(cupsString);
d(cupsMarkerType);
d(cupsRenderingIntent);
d(cupsPageSizeName);
#undef d
}

25
src/debug.h Normal file
View File

@ -0,0 +1,25 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#ifndef DEBUG_H
#define DEBUG_H
#include <cups/raster.h>
void dump_page_header(const cups_page_header2_t &h);
#endif

152
src/job.cc Normal file
View File

@ -0,0 +1,152 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#include "job.h"
#include <assert.h>
#include <algorithm>
#include <vector>
#include "line.h"
using std::vector;
namespace {
class block {
public:
void add_line(vector<uint8_t> &&line) {
assert(!line.empty());
line_bytes_ += line.size();
lines_.push_back(line);
}
bool line_fits(unsigned size) {
return lines_.size() != max_lines_per_block_
&& line_bytes_ + size < max_block_size_;
}
void flush(FILE *f) {
if (line_bytes_) {
fprintf(f, "%dw%c%c",
line_bytes_ + 2, 0,
static_cast<int>(lines_.size()));
for (auto &line : lines_) {
fwrite(line.data(), 1, line.size(), f);
}
line_bytes_ = 0;
lines_.clear();
}
}
private:
const unsigned max_block_size_ = 16350;
const unsigned max_lines_per_block_ = 128;
vector<vector<uint8_t>> lines_;
int line_bytes_ = 0;
};
} // namespace
job::job(FILE *out, const std::string &job_name)
: out_(out),
job_name_(job_name),
page_params_() {
// Delete dubious characters from job name
std::replace_if(job_name_.begin(), job_name_.end(), [](char c) {
return c < 32 || c >= 127 || c == '"' || c == '\\';
}, ' ');
begin_job();
}
job::~job() {
end_job();
}
void job::begin_job() {
for (int i = 0; i < 128; ++i) {
putc(0, out_);
}
fprintf(out_, "\033%%-12345X@PJL\n");
fprintf(out_, "@PJL JOB NAME=\"%s\"\n", job_name_.c_str());
}
void job::end_job() {
fprintf(out_, "\033%%-12345X@PJL\n");
fprintf(out_, "@PJL EOJ NAME=\"%s\"\n", job_name_.c_str());
fprintf(out_, "\033%%-12345X\n");
}
void job::write_page_header() {
fprintf(out_, "\033%%-12345X@PJL\n");
if (page_params_.resolution != 1200) {
fprintf(out_, "@PJL SET RAS1200MODE = FALSE\n");
fprintf(out_, "@PJL SET RESOLUTION = %d\n", page_params_.resolution);
} else {
fprintf(out_, "@PJL SET RAS1200MODE = TRUE\n");
fprintf(out_, "@PJL SET RESOLUTION = 600\n");
}
fprintf(out_, "@PJL SET ECONOMODE = %s\n",
page_params_.economode ? "ON" : "OFF");
fprintf(out_, "@PJL SET SOURCETRAY = %s\n",
page_params_.sourcetray.c_str());
fprintf(out_, "@PJL SET MEDIATYPE = %s\n",
page_params_.mediatype.c_str());
fprintf(out_, "@PJL SET PAPER = %s\n",
page_params_.papersize.c_str());
fprintf(out_, "@PJL SET PAGEPROTECT = AUTO\n");
fprintf(out_, "@PJL SET ORIENTATION = PORTRAIT\n");
fprintf(out_, "@PJL ENTER LANGUAGE = PCL\n");
}
void job::encode_page(const page_params &page_params,
int num_copies,
int lines,
int linesize,
nextline_fn nextline) {
if (!(page_params_ == page_params)) {
page_params_ = page_params;
write_page_header();
}
vector<uint8_t> line(linesize);
vector<uint8_t> reference(linesize);
block block;
if (!nextline(line.data())) {
return;
}
block.add_line(encode_line(line));
std::swap(line, reference);
fputs("\033E", out_);
fprintf(out_, "\033&l%dX", std::max(1, num_copies));
fputs("\033*b1030m", out_);
for (int i = 1; i < lines && nextline(line.data()); ++i) {
vector<uint8_t> encoded = encode_line(line, reference);
if (!block.line_fits(encoded.size())) {
block.flush(out_);
}
block.add_line(std::move(encoded));
std::swap(line, reference);
}
block.flush(out_);
fputs("1030M\f", out_);
fflush(out_);
}

64
src/job.h Normal file
View File

@ -0,0 +1,64 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#ifndef JOB_H
#define JOB_H
#include <stdint.h>
#include <stdio.h>
#include <string>
struct page_params {
int resolution;
bool economode;
std::string sourcetray;
std::string mediatype;
std::string papersize;
bool operator==(const page_params &o) const {
return resolution == o.resolution
&& economode == o.economode
&& sourcetray == o.sourcetray
&& mediatype == o.mediatype
&& papersize == o.papersize;
}
};
class job {
public:
typedef bool (*nextline_fn)(uint8_t *buf);
explicit job(FILE *out, const std::string &job_name);
~job();
void encode_page(const page_params &params,
int num_copies,
int lines,
int linesize,
nextline_fn nextline);
private:
void begin_job();
void end_job();
void write_page_header();
FILE *out_;
std::string job_name_;
page_params page_params_;
};
#endif // JOB_H

191
src/line.cc Normal file
View File

@ -0,0 +1,191 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#include "line.h"
#include <assert.h>
#include <algorithm>
using std::vector;
namespace {
void write_overflow(int value, vector<uint8_t> *out) {
if (value >= 0) {
if (value < 255) {
out->push_back(value);
} else {
out->insert(out->end(), value / 255, 255);
out->push_back(value % 255);
}
}
}
template <typename Iterator>
void write_substitute(int offset,
Iterator first,
Iterator last,
vector<uint8_t> *out) {
assert(offset >= 0);
assert(offset < 10000);
assert(first != last);
const int offset_max = 15;
const int count_max = 7;
int count = std::distance(first, last) - 1;
int offset_low = std::min(offset, offset_max);
int count_low = std::min(count, count_max);
out->push_back((offset_low << 3) | count_low);
write_overflow(offset - offset_max, out);
write_overflow(count - count_max, out);
out->insert(out->end(), first, last);
}
void write_repeat(int offset, int count, int value, vector<uint8_t> *out) {
assert(offset >= 0);
assert(offset < 10000);
assert(count >= 2);
assert(count < 10000);
const int offset_max = 3;
const int count_max = 31;
count -= 2;
int offset_low = std::min(offset, offset_max);
int count_low = std::min(count, count_max);
out->push_back(128 | (offset_low << 5) | count_low);
write_overflow(offset - offset_max, out);
write_overflow(count - count_max, out);
out->push_back(value);
}
bool all_zeros(const vector<uint8_t> &buf) {
return std::none_of(buf.begin(), buf.end(), [](uint8_t b) { return b; });
}
template <typename Iterator1, typename Iterator2>
int skip_to_next_mismatch(Iterator1 *first1,
Iterator1 last1,
Iterator2 *first2) {
auto mismatch_it = std::mismatch(*first1, last1, *first2);
int skipped = std::distance(*first1, mismatch_it.first);
*first1 = mismatch_it.first;
*first2 = mismatch_it.second;
return skipped;
}
template <typename Iterator>
int repeat_length(Iterator first, Iterator last) {
if (first != last) {
auto k = *first;
auto mismatch = std::find_if(std::next(first), last,
[=](decltype(k) x) { return x != k; });
return std::distance(first, mismatch);
}
return 0;
}
template <typename Iterator1, typename Iterator2>
int substitute_length(Iterator1 first1, Iterator1 last1, Iterator2 first2) {
if (first1 != last1) {
Iterator1 it1 = first1;
Iterator2 it2 = first2;
Iterator1 next1 = std::next(first1);
Iterator2 next2 = std::next(first2);
Iterator1 prev1 = first1;
while (next1 != last1) {
if ((*it1 == *it2 && *next1 == *next2)) {
return std::distance(first1, it1);
}
if (*it1 == *next1 && *it1 == *prev1) {
return std::distance(first1, prev1);
}
prev1 = it1;
it1 = next1; it2 = next2;
++next1; ++next2;
}
}
return std::distance(first1, last1);
}
size_t reserve_size(const vector<uint8_t> &line) {
// Big enough to store the line uncompressed together with an Substitute
// command with many overflow bytes.
return line.size() + 16;
}
} // namespace
vector<uint8_t> encode_line(const vector<uint8_t> &line,
const vector<uint8_t> &reference) {
assert(line.size() == reference.size());
if (all_zeros(line)) {
return vector<uint8_t>(1, 0xFF);
}
vector<uint8_t> output;
output.reserve(reserve_size(line));
output.push_back(0); // first byte is the edit count
const uint8_t max_edits = 254;
int num_edits = 0;
auto line_it = line.begin();
auto ref_it = reference.begin();
while (1) {
int offset = skip_to_next_mismatch(&line_it, line.end(), &ref_it);
if (line_it == line.end()) {
// No more differences, we're done.
break;
}
if (++num_edits == max_edits) {
// We've run out of edits. Just output the rest of the line in a big
// substitute command.
write_substitute(offset, line_it, line.end(), &output);
break;
}
int s = substitute_length(line_it, line.end(), ref_it);
if (s > 0) {
write_substitute(offset, line_it, std::next(line_it, s), &output);
line_it += s;
ref_it += s;
} else {
int r = repeat_length(line_it, line.end());
assert(r >= 2);
write_repeat(offset, r, *line_it, &output);
line_it += r;
ref_it += r;
}
}
assert(num_edits <= max_edits);
output[0] = num_edits;
return output;
}
vector<uint8_t> encode_line(const vector<uint8_t> &line) {
if (all_zeros(line)) {
return vector<uint8_t>(1, 0xFF);
}
vector<uint8_t> buf;
buf.reserve(reserve_size(line));
buf.push_back(1);
write_substitute(0, line.begin(), line.end(), &buf);
return buf;
}

31
src/line.h Normal file
View File

@ -0,0 +1,31 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#ifndef LINE_H
#define LINE_H
#include <stdint.h>
#include <vector>
std::vector<uint8_t> encode_line(
const std::vector<uint8_t> &line,
const std::vector<uint8_t> &reference);
std::vector<uint8_t> encode_line(
const std::vector<uint8_t> &line);
#endif // LINE_H

194
src/main.cc Normal file
View File

@ -0,0 +1,194 @@
// This file is part of the brlaser printer driver.
//
// Copyright 2013 Peter De Wachter
//
// brlaser is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// brlaser 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with brlaser. If not, see <http://www.gnu.org/licenses/>.
#include <stdio.h>
#include <signal.h>
#include <locale.h>
#include <iconv.h>
#include <unistd.h>
#include <fcntl.h>
#include <cups/raster.h>
#include <algorithm>
#include <functional>
#include <string>
#include <array>
#include <map>
#include "config.h"
#include "job.h"
#include "debug.h"
namespace {
volatile sig_atomic_t interrupted = 0;
void sigterm_handler(int sig) {
interrupted = 1;
}
cups_raster_t *ras;
cups_page_header2_t header;
bool next_line(uint8_t *buf) {
if (interrupted) {
return false;
}
unsigned bytes = header.cupsBytesPerLine;
return cupsRasterReadPixels(ras, buf, bytes) == bytes;
}
// POSIX says the second argument of iconv has type 'char **', but
// some systems have 'const char **'. This class is used to work
// around this incompatibility.
class sloppy_ptr {
public:
explicit sloppy_ptr(const char **ptr): ptr_(ptr) { }
operator const char **() { return ptr_; }
operator char **() { return const_cast<char **>(ptr_); }
private:
const char **ptr_;
};
std::string ascii_job_name(const char *job_name, const char *charset) {
if (job_name && charset) {
iconv_t cd = iconv_open("ASCII//TRANSLIT//IGNORE", charset);
if (cd != (iconv_t) -1) {
char ascii[80];
const char *in_ptr = job_name;
size_t in_left = strlen(job_name);
char *out_ptr = ascii;
size_t out_left = sizeof(ascii) - 1;
while (in_left > 0) {
size_t err = iconv(cd,
sloppy_ptr(&in_ptr), &in_left,
&out_ptr, &out_left);
if (err == (size_t) -1) {
break;
}
}
*out_ptr = 0;
iconv_close(cd);
return ascii;
}
}
return "CUPS";
}
page_params build_page_params() {
static const std::array<std::string, 6> sources = {
"AUTO", "T1", "T2", "T3", "MP", "MANUAL"
};
static const std::map<std::string, std::string> sizes = {
{ "A4", "A4" },
{ "A5", "A5" },
{ "A6", "A6" },
{ "B5", "B5" },
{ "B6", "B6" },
{ "EnvC5", "C5" },
{ "EnvMonarch", "MONARCH" },
{ "EnvPRC5", "DL" },
{ "Executive", "EXECUTIVE" },
{ "Legal", "LEGAL" },
{ "Letter", "LETTER" }
};
page_params p = { };
p.resolution = header.HWResolution[0];
p.economode = header.cupsInteger[10];
p.mediatype = header.MediaType;
if (header.MediaPosition < sources.size())
p.sourcetray = sources[header.MediaPosition];
else
p.sourcetray = sources[0];
auto size_it = sizes.find(header.cupsPageSizeName);
if (size_it != sizes.end())
p.papersize = size_it->second;
else
p.papersize = "A4";
return p;
}
} // namespace
int main(int argc, char *argv[]) {
fprintf(stderr, "INFO: %s version %s\n", PACKAGE, VERSION);
if (argc != 6 && argc != 7) {
fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n", argv[0]);
fprintf(stderr, "INFO: This program is a CUPS filter. It is not intended to be run manually.\n");
return 1;
}
// const char *job_id = argv[1];
// const char *job_user = argv[2];
const char *job_name = argv[3];
// const int job_copies = atoi(argv[4]);
// const char *job_options = argv[5];
const char *job_filename = argv[6];
const char *job_charset = getenv("CHARSET");
setlocale(LC_ALL, "");
signal(SIGTERM, sigterm_handler);
signal(SIGPIPE, SIG_IGN);
int fd = 0;
if (job_filename) {
fd = open(job_filename, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "ERROR: Unable to open raster file\n");
return 1;
}
}
ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
if (!ras) {
fprintf(stderr, "ERROR: Can't read raster data\n");
return 1;
}
int pages = 0;
{
job job(stdout, ascii_job_name(job_name, job_charset));
while (!interrupted && cupsRasterReadHeader2(ras, &header)) {
if (pages == 0) {
dump_page_header(header);
}
job.encode_page(build_page_params(),
header.NumCopies,
header.cupsHeight,
header.cupsBytesPerLine,
next_line);
fprintf(stderr, "PAGE: %d %d\n", ++pages, header.NumCopies);
}
}
if (pages == 0) {
fprintf(stderr, "ERROR: No pages were found.");
return 1;
}
fflush(stdout);
if (ferror(stdout)) {
fprintf(stderr, "ERROR: Could not write print data\n");
return 1;
}
return 0;
}