mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 06:22:44 +08:00
619 lines
18 KiB
C++
619 lines
18 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
#include <allheaders.h>
|
|
#include <pix.h>
|
|
|
|
#include <math.h>
|
|
#include "additionaltypes.h"
|
|
|
|
#define u64 uint64_t
|
|
#define u32 uint32_t
|
|
#define u16 uint16_t
|
|
#define u8 uint8_t
|
|
|
|
#include "jbig2arith.h"
|
|
#include "jbig2sym.h"
|
|
#include "jbig2structs.h"
|
|
#include "jbig2segments.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Removes spots which are less than size x size pixels
|
|
//
|
|
// Note, this has a side-effect of removing a few pixels
|
|
// that from components you want to keep.
|
|
//
|
|
// If that's a problem, you do a binary reconstruction
|
|
// (from seedfill.c):
|
|
// -----------------------------------------------------------------------------
|
|
static PIX *
|
|
remove_flyspecks(PIX *const source, const int size) {
|
|
Sel *sel_5h = selCreateBrick(1, size, 0, 2, SEL_HIT);
|
|
Sel *sel_5v = selCreateBrick(size, 1, 2, 0, SEL_HIT);
|
|
|
|
Pix *pixt = pixOpen(NULL, source, sel_5h);
|
|
Pix *pixd = pixOpen(NULL, source, sel_5v);
|
|
pixOr(pixd, pixd, pixt);
|
|
pixDestroy(&pixt);
|
|
selDestroy(&sel_5h);
|
|
selDestroy(&sel_5v);
|
|
|
|
return pixd;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Returns the number of bits needed to encode v symbols
|
|
// -----------------------------------------------------------------------------
|
|
static unsigned
|
|
log2up(int v) {
|
|
unsigned r = 0;
|
|
const bool is_pow_of_2 = (v & (v - 1)) == 0;
|
|
|
|
while (v >>= 1) r++;
|
|
if (is_pow_of_2) return r;
|
|
|
|
return r + 1;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// This is the context for a multi-page JBIG2 document.
|
|
// -----------------------------------------------------------------------------
|
|
struct jbig2ctx {
|
|
struct JbClasser *classer; // the leptonica classifier
|
|
int xres, yres; // ppi for the X and Y direction
|
|
bool full_headers; // true if we are producing a full JBIG2 file
|
|
bool pdf_page_numbering; // true if all text pages are page "1" (pdf mode)
|
|
int segnum; // current segment number
|
|
int symtab_segment; // the segment number of the symbol table
|
|
// a map from page number a list of components for that page
|
|
std::map<int, std::vector<int> > pagecomps;
|
|
// for each page, the list of symbols which are only used on that page
|
|
std::map<int, std::vector<unsigned> > single_use_symbols;
|
|
// the number of symbols in the global symbol table
|
|
int num_global_symbols;
|
|
std::vector<int> page_width, page_height;
|
|
// Used to store the mapping from symbol number to the index in the global
|
|
// symbol dictionary.
|
|
std::map<int, int> symmap;
|
|
bool refinement;
|
|
PIXA *avg_templates; // grayed templates
|
|
int refine_level;
|
|
// only used when using refinement
|
|
// the number of the first symbol of each page
|
|
std::vector<int> baseindexes;
|
|
};
|
|
|
|
// see comments in .h file
|
|
struct jbig2ctx *
|
|
jbig2_init(float thresh, float weight, int xres, int yres, bool full_headers,
|
|
int refine_level) {
|
|
struct jbig2ctx *ctx = new jbig2ctx;
|
|
ctx->xres = xres;
|
|
ctx->yres = yres;
|
|
ctx->full_headers = full_headers;
|
|
ctx->pdf_page_numbering = !full_headers;
|
|
ctx->segnum = 0;
|
|
ctx->symtab_segment = -1;
|
|
ctx->refinement = refine_level >= 0;
|
|
ctx->refine_level = refine_level;
|
|
ctx->avg_templates = NULL;
|
|
|
|
ctx->classer = jbCorrelationInitWithoutComponents(JB_CONN_COMPS, 9999, 9999,
|
|
thresh, weight);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
// see comments in .h file
|
|
void
|
|
jbig2_destroy(struct jbig2ctx *ctx) {
|
|
if (ctx->avg_templates) pixaDestroy(&ctx->avg_templates);
|
|
jbClasserDestroy(&ctx->classer);
|
|
delete ctx;
|
|
}
|
|
|
|
// see comments in .h file
|
|
void
|
|
jbig2_add_page(struct jbig2ctx *ctx, struct Pix *input) {
|
|
PIX *bw;
|
|
|
|
if (false /*ctx->xres >= 300*/) {
|
|
bw = remove_flyspecks(input, (int) (0.0084*ctx->xres));
|
|
} else {
|
|
bw = pixClone(input);
|
|
}
|
|
|
|
if (ctx->refinement) {
|
|
ctx->baseindexes.push_back(ctx->classer->baseindex);
|
|
}
|
|
|
|
jbAddPage(ctx->classer, bw);
|
|
ctx->page_width.push_back(bw->w);
|
|
ctx->page_height.push_back(bw->h);
|
|
|
|
if (ctx->refinement) {
|
|
// This code is broken by (my) recent changes to Leptonica. Needs to be
|
|
// fixed at some point, but not too important at the moment since we don't
|
|
// use refinement.
|
|
|
|
/*BOXA *boxes = boxaCopy(ctx->classer->boxas, L_CLONE);
|
|
ctx->boxes.push_back(boxes);
|
|
PIXA *comps = pixaCopy(ctx->classer->pixas, L_CLONE);
|
|
ctx->comps.push_back(comps);*/
|
|
}
|
|
|
|
pixDestroy(&bw);
|
|
}
|
|
|
|
#define F(x) memcpy(ret + offset, &x, sizeof(x)) ; offset += sizeof(x)
|
|
#define G(x, y) memcpy(ret + offset, x, y); offset += y;
|
|
#define SEGMENT(x) x.write(ret + offset); offset += x.size();
|
|
|
|
// see comments in .h file
|
|
uint8_t *
|
|
jbig2_pages_complete(struct jbig2ctx *ctx, int *const length) {
|
|
/*
|
|
Graying support - disabled.
|
|
It's not very clear that graying actaully buys you much extra quality
|
|
above pick-the-first. Also, aligning the gray glyphs requires the
|
|
original source image.
|
|
|
|
Remember that you need the Init without WithoutComponents to use this */
|
|
|
|
|
|
/*NUMA *samples_per_composition;
|
|
PTA *grayed_centroids;
|
|
PIXA *grayed;
|
|
|
|
grayed = jbAccumulateComposites(ctx->classer->pixaa, &samples_per_composition,
|
|
&grayed_centroids);
|
|
|
|
if (!grayed || grayed->n != ctx->classer->pixaa->n) {
|
|
fprintf(stderr, "Graying failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
ctx->avg_templates = pixaCreate(0);
|
|
for (int i = 0; i < grayed->n; ++i) {
|
|
int samples;
|
|
numaGetIValue(samples_per_composition, i, &samples);
|
|
PIX *avg = pixFinalAccumulateThreshold(grayed->pix[i], 0,
|
|
(samples + 1) >> 1);
|
|
pixaAddPix(ctx->avg_templates, avg, L_INSERT);
|
|
//char b[512];
|
|
//sprintf(b, "gray-%d/th.png", i);
|
|
//pixWrite(b, avg, IFF_PNG);
|
|
}
|
|
|
|
pixaDestroy(&grayed);
|
|
numaDestroy(&samples_per_composition);*/
|
|
|
|
// We find the symbols which only appear on a single page and encode them in
|
|
// a symbol dictionary just for that page. This is because we want to keep
|
|
// the size of the global dictionary down as some PDF readers appear to
|
|
// decode it for every page (!)
|
|
|
|
// (as a short cut, we just pick the symbols which are only used once since,
|
|
// in testing, all the symbols which appear on only one page appear only once
|
|
// on that page)
|
|
|
|
const bool single_page = ctx->classer->npages == 1;
|
|
|
|
// maps symbol number to the number of times it has been used
|
|
// pixat->n is the number of symbols
|
|
// naclass->n is the number of connected components
|
|
|
|
std::vector<unsigned> symbol_used(ctx->classer->pixat->n);
|
|
for (int i = 0; i < ctx->classer->naclass->n; ++i) {
|
|
int n;
|
|
numaGetIValue(ctx->classer->naclass, i, &n);
|
|
symbol_used[n]++;
|
|
}
|
|
|
|
// the multiuse symbols are the ones which go into the global dictionary
|
|
std::vector<unsigned> multiuse_symbols;
|
|
for (int i = 0; i < ctx->classer->pixat->n; ++i) {
|
|
if (symbol_used[i] == 0) abort();
|
|
if (symbol_used[i] > 1 || single_page) multiuse_symbols.push_back(i);
|
|
}
|
|
ctx->num_global_symbols = multiuse_symbols.size();
|
|
|
|
// build the pagecomps map: a map from page number to the list of connected
|
|
// components for that page. The classer gives us an array from connected
|
|
// component number to page number - we just have to reverse it
|
|
for (int i = 0; i < ctx->classer->napage->n; ++i) {
|
|
int page_num;
|
|
numaGetIValue(ctx->classer->napage, i, &page_num);
|
|
ctx->pagecomps[page_num].push_back(i);
|
|
int symbol;
|
|
numaGetIValue(ctx->classer->naclass, i, &symbol);
|
|
if (symbol_used[symbol] == 1 && !single_page) {
|
|
ctx->single_use_symbols[page_num].push_back(symbol);
|
|
}
|
|
}
|
|
|
|
#ifdef DUMP_SYMBOL_GRAPH
|
|
for (int p = 0; p < ctx->classer->npages; ++p) {
|
|
for (std::vector<int>::const_iterator i = ctx->pagecomps[p].begin();
|
|
i != ctx->pagecomps[p].end(); ++i) {
|
|
const int sym = (int) ctx->classer->naclass->array[*i];
|
|
fprintf(stderr, "S: %d %d\n", p, sym);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef SYMBOL_COMPRESSION_DEBUGGING
|
|
std::map<int, int> usecount;
|
|
for (int i = 0; i < ctx->classer->naclass->n; ++i) {
|
|
usecount[(int)ctx->classer->naclass->array[i]]++;
|
|
}
|
|
|
|
for (int p = 0; p < ctx->classer->npages; ++p) {
|
|
const int numcomps = ctx->pagecomps[p].size();
|
|
int unique_in_doc = 0;
|
|
std::map<int, int> symcount;
|
|
for (std::vector<int>::const_iterator i = ctx->pagecomps[p].begin();
|
|
i != ctx->pagecomps[p].end(); ++i) {
|
|
const int sym = (int) ctx->classer->naclass->array[*i];
|
|
symcount[sym]++;
|
|
if (usecount[sym] == 1) unique_in_doc++;
|
|
}
|
|
int unique_this_page = 0;
|
|
for (std::map<int, int>::const_iterator i = symcount.begin();
|
|
i != symcount.end(); ++i) {
|
|
if (i->second == 1) unique_this_page++;
|
|
}
|
|
|
|
fprintf(stderr, "Page %d %d/%d/%d\n", p, numcomps, unique_this_page, unique_in_doc);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DUMP_ALL_SYMBOLS
|
|
char filenamebuf[128];
|
|
for (int i = 0; i < ctx->classer->pixat->n; ++i) {
|
|
sprintf(filenamebuf, "sym-%d.png", i);
|
|
pixWrite(filenamebuf, ctx->classer->pixat->pix[i], IFF_PNG);
|
|
}
|
|
#endif
|
|
fprintf(stderr, "JBIG2 compression complete. pages:%d symbols:%d log2:%d\n",
|
|
ctx->classer->npages, ctx->classer->pixat->n,
|
|
log2up(ctx->classer->pixat->n));
|
|
|
|
jbGetLLCorners(ctx->classer);
|
|
|
|
struct jbig2enc_ctx ectx;
|
|
jbig2enc_init(&ectx);
|
|
|
|
struct jbig2_file_header header;
|
|
if (ctx->full_headers) {
|
|
memset(&header, 0, sizeof(header));
|
|
header.n_pages = htonl(ctx->classer->npages);
|
|
header.organisation_type = 1;
|
|
memcpy(&header.id, JBIG2_FILE_MAGIC, 8);
|
|
}
|
|
|
|
Segment seg;
|
|
struct jbig2_symbol_dict symtab;
|
|
memset(&symtab, 0, sizeof(symtab));
|
|
|
|
jbig2enc_symboltable
|
|
(&ectx, ctx->avg_templates ? ctx->avg_templates : ctx->classer->pixat,
|
|
&multiuse_symbols, &ctx->symmap, ctx->avg_templates == NULL);
|
|
const int symdatasize = jbig2enc_datasize(&ectx);
|
|
|
|
symtab.a1x = 3;
|
|
symtab.a1y = -1;
|
|
symtab.a2x = -3;
|
|
symtab.a2y = -1;
|
|
symtab.a3x = 2;
|
|
symtab.a3y = -2;
|
|
symtab.a4x = -2;
|
|
symtab.a4y = -2;
|
|
symtab.exsyms = symtab.newsyms = htonl(multiuse_symbols.size());
|
|
|
|
ctx->symtab_segment = ctx->segnum;
|
|
seg.number = ctx->segnum;
|
|
ctx->segnum++;
|
|
seg.type = segment_symbol_table;
|
|
seg.len = sizeof(symtab) + symdatasize;
|
|
seg.page = 0;
|
|
seg.retain_bits = 1;
|
|
|
|
u8 *const ret = (u8 *) malloc((ctx->full_headers ? sizeof(header) : 0) +
|
|
seg.size() + sizeof(symtab) + symdatasize);
|
|
int offset = 0;
|
|
if (ctx->full_headers) {
|
|
F(header);
|
|
}
|
|
SEGMENT(seg);
|
|
F(symtab);
|
|
jbig2enc_tobuffer(&ectx, ret + offset);
|
|
jbig2enc_dealloc(&ectx);
|
|
offset += symdatasize;
|
|
|
|
*length = offset;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// see comments in .h file
|
|
uint8_t *
|
|
jbig2_produce_page(struct jbig2ctx *ctx, int page_no,
|
|
int xres, int yres, int *const length) {
|
|
const bool last_page = page_no == ctx->classer->npages;
|
|
const bool include_trailer = last_page && ctx->full_headers;
|
|
|
|
struct jbig2enc_ctx ectx;
|
|
jbig2enc_init(&ectx);
|
|
|
|
Segment seg, symseg;
|
|
Segment endseg, trailerseg;
|
|
struct jbig2_page_info pageinfo;
|
|
memset(&pageinfo, 0, sizeof(pageinfo));
|
|
struct jbig2_text_region textreg;
|
|
memset(&textreg, 0, sizeof(textreg));
|
|
struct jbig2_text_region_syminsts textreg_syminsts;
|
|
memset(&textreg_syminsts, 0, sizeof(textreg_syminsts));
|
|
struct jbig2_text_region_atflags textreg_atflags;
|
|
memset(&textreg_atflags, 0, sizeof(textreg_atflags));
|
|
Segment segr;
|
|
|
|
// page information segment
|
|
seg.number = ctx->segnum;
|
|
ctx->segnum++;
|
|
seg.type = segment_page_information;
|
|
seg.page = ctx->pdf_page_numbering ? 1 : 1 + page_no;
|
|
seg.len = sizeof(struct jbig2_page_info);
|
|
pageinfo.width = htonl(ctx->page_width[page_no]);
|
|
pageinfo.height = htonl(ctx->page_height[page_no]);
|
|
pageinfo.xres = htonl(xres == -1 ? ctx->xres : xres);
|
|
pageinfo.yres = htonl(yres == -1 ? ctx->yres : yres);
|
|
pageinfo.is_lossless = ctx->refinement;
|
|
|
|
std::map<int, int> second_symbol_map;
|
|
// If we have single-use symbols on this page we make a new symbol table
|
|
// containing just them.
|
|
const bool extrasymtab = ctx->single_use_symbols[page_no].size() > 0;
|
|
struct jbig2enc_ctx extrasymtab_ctx;
|
|
|
|
struct jbig2_symbol_dict symtab;
|
|
memset(&symtab, 0, sizeof(symtab));
|
|
|
|
if (extrasymtab) {
|
|
jbig2enc_init(&extrasymtab_ctx);
|
|
symseg.number = ctx->segnum++;
|
|
symseg.type = segment_symbol_table;
|
|
symseg.page = ctx->pdf_page_numbering ? 1 : 1 + page_no;
|
|
|
|
jbig2enc_symboltable
|
|
(&extrasymtab_ctx,
|
|
ctx->avg_templates ? ctx->avg_templates : ctx->classer->pixat,
|
|
&ctx->single_use_symbols[page_no], &second_symbol_map,
|
|
ctx->avg_templates == NULL);
|
|
symtab.a1x = 3;
|
|
symtab.a1y = -1;
|
|
symtab.a2x = -3;
|
|
symtab.a2y = -1;
|
|
symtab.a3x = 2;
|
|
symtab.a3y = -2;
|
|
symtab.a4x = -2;
|
|
symtab.a4y = -2;
|
|
symtab.exsyms = symtab.newsyms =
|
|
htonl(ctx->single_use_symbols[page_no].size());
|
|
|
|
symseg.len = jbig2enc_datasize(&extrasymtab_ctx) + sizeof(symtab);
|
|
}
|
|
|
|
const int numsyms = ctx->num_global_symbols +
|
|
ctx->single_use_symbols[page_no].size();
|
|
//BOXA *const boxes = ctx->refinement ? ctx->boxes[page_no] : NULL;
|
|
int baseindex = ctx->refinement ? ctx->baseindexes[page_no] : 0;
|
|
jbig2enc_textregion(&ectx, ctx->symmap, second_symbol_map,
|
|
ctx->pagecomps[page_no],
|
|
ctx->classer->ptall,
|
|
ctx->avg_templates ? ctx->avg_templates : ctx->classer->pixat,
|
|
ctx->classer->naclass, 1,
|
|
log2up(numsyms),
|
|
//ctx->refinement ? ctx->comps[page_no] : NULL,
|
|
NULL,
|
|
/* boxes */ NULL, baseindex, ctx->refine_level,
|
|
ctx->avg_templates == NULL);
|
|
const int textdatasize = jbig2enc_datasize(&ectx);
|
|
textreg.width = htonl(ctx->page_width[page_no]);
|
|
textreg.height = htonl(ctx->page_height[page_no]);
|
|
textreg.logsbstrips = 0;
|
|
textreg.sbrefine = ctx->refinement;
|
|
// refcorner = 0 -> bot left
|
|
textreg_syminsts.sbnuminstances = htonl(ctx->pagecomps[page_no].size());
|
|
|
|
textreg_atflags.a1x = -1;
|
|
textreg_atflags.a1y = -1;
|
|
textreg_atflags.a2x = -1;
|
|
textreg_atflags.a2y = -1;
|
|
|
|
segr.number = ctx->segnum;
|
|
ctx->segnum++;
|
|
segr.type = segment_imm_text_region;
|
|
segr.referred_to.push_back(ctx->symtab_segment);
|
|
if (extrasymtab) segr.referred_to.push_back(symseg.number);
|
|
if (ctx->refinement) {
|
|
segr.len = sizeof(textreg) + sizeof(textreg_syminsts) +
|
|
sizeof(textreg_atflags) + textdatasize;
|
|
} else {
|
|
segr.len = sizeof(textreg) + sizeof(textreg_syminsts) + textdatasize;
|
|
}
|
|
|
|
segr.retain_bits = 2;
|
|
segr.page = ctx->pdf_page_numbering ? 1 : 1 + page_no;
|
|
|
|
const int extrasymtab_size = extrasymtab ?
|
|
jbig2enc_datasize(&extrasymtab_ctx) : 0;
|
|
|
|
if (ctx->full_headers) {
|
|
endseg.number = ctx->segnum;
|
|
ctx->segnum++;
|
|
endseg.type = segment_end_of_page;
|
|
endseg.page = ctx->pdf_page_numbering ? 1 : 1 + page_no;
|
|
}
|
|
|
|
if (include_trailer) {
|
|
trailerseg.number = ctx->segnum;
|
|
ctx->segnum++;
|
|
trailerseg.type = segment_end_of_file;
|
|
trailerseg.page = 0;
|
|
}
|
|
|
|
const int totalsize = seg.size() + sizeof(pageinfo) +
|
|
(extrasymtab ? (extrasymtab_size + symseg.size() +
|
|
sizeof(symtab)) : 0) +
|
|
segr.size() +
|
|
sizeof(textreg) + sizeof(textreg_syminsts) +
|
|
(ctx->refinement ? sizeof(textreg_atflags) : 0) +
|
|
textdatasize +
|
|
(ctx->full_headers ? endseg.size() : 0) +
|
|
(include_trailer ? trailerseg.size() : 0);
|
|
u8 *ret = (u8 *) malloc(totalsize);
|
|
int offset = 0;
|
|
|
|
SEGMENT(seg);
|
|
F(pageinfo);
|
|
if (extrasymtab) {
|
|
SEGMENT(symseg);
|
|
F(symtab);
|
|
jbig2enc_tobuffer(&extrasymtab_ctx, ret + offset);
|
|
offset += extrasymtab_size;
|
|
}
|
|
SEGMENT(segr);
|
|
F(textreg);
|
|
if (ctx->refinement) {
|
|
F(textreg_atflags);
|
|
}
|
|
F(textreg_syminsts);
|
|
jbig2enc_tobuffer(&ectx, ret + offset); offset += textdatasize;
|
|
if (ctx->full_headers) {
|
|
SEGMENT(endseg);
|
|
}
|
|
if (include_trailer) {
|
|
SEGMENT(trailerseg);
|
|
}
|
|
|
|
if (totalsize != offset) abort();
|
|
|
|
jbig2enc_dealloc(&ectx);
|
|
if (extrasymtab) jbig2enc_dealloc(&extrasymtab_ctx);
|
|
|
|
*length = offset;
|
|
return ret;
|
|
}
|
|
|
|
#undef F
|
|
#undef G
|
|
|
|
// see comments in .h file
|
|
u8 *
|
|
jbig2_encode_generic(struct Pix *const bw, const bool full_headers, const int xres,
|
|
const int yres, const bool duplicate_line_removal,
|
|
int *const length) {
|
|
int segnum = 0;
|
|
|
|
if (!bw) return NULL;
|
|
pixSetPadBits(bw, 0);
|
|
|
|
struct jbig2_file_header header;
|
|
if (full_headers) {
|
|
memset(&header, 0, sizeof(header));
|
|
header.n_pages = htonl(1);
|
|
header.organisation_type = 1;
|
|
memcpy(&header.id, JBIG2_FILE_MAGIC, 8);
|
|
}
|
|
|
|
// setup compression
|
|
struct jbig2enc_ctx ctx;
|
|
jbig2enc_init(&ctx);
|
|
|
|
Segment seg, seg2, endseg;
|
|
jbig2_page_info pageinfo;
|
|
memset(&pageinfo, 0, sizeof(pageinfo));
|
|
jbig2_generic_region genreg;
|
|
memset(&genreg, 0, sizeof(genreg));
|
|
|
|
seg.number = segnum;
|
|
segnum++;
|
|
seg.type = segment_page_information;
|
|
seg.page = 1;
|
|
seg.len = sizeof(struct jbig2_page_info);
|
|
pageinfo.width = htonl(bw->w);
|
|
pageinfo.height = htonl(bw->h);
|
|
pageinfo.xres = htonl(xres ? xres : bw->xres);
|
|
pageinfo.yres = htonl(yres ? yres : bw->yres);
|
|
pageinfo.is_lossless = 1;
|
|
|
|
#ifdef SURPRISE_MAP
|
|
dprintf(3, "P5\n%d %d 255\n", bw->w, bw->h);
|
|
#endif
|
|
|
|
jbig2enc_bitimage(&ctx, (u8 *) bw->data, bw->w, bw->h, duplicate_line_removal);
|
|
jbig2enc_final(&ctx);
|
|
const int datasize = jbig2enc_datasize(&ctx);
|
|
|
|
seg2.number = segnum;
|
|
segnum++;
|
|
seg2.type = segment_imm_generic_region;
|
|
seg2.page = 1;
|
|
seg2.len = sizeof(genreg) + datasize;
|
|
|
|
endseg.number = segnum;
|
|
segnum++;
|
|
endseg.page = 1;
|
|
|
|
genreg.width = htonl(bw->w);
|
|
genreg.height = htonl(bw->h);
|
|
if (duplicate_line_removal) {
|
|
genreg.tpgdon = true;
|
|
}
|
|
genreg.a1x = 3;
|
|
genreg.a1y = -1;
|
|
genreg.a2x = -3;
|
|
genreg.a2y = -1;
|
|
genreg.a3x = 2;
|
|
genreg.a3y = -2;
|
|
genreg.a4x = -2;
|
|
genreg.a4y = -2;
|
|
|
|
const int totalsize = seg.size() + sizeof(pageinfo) + seg2.size() +
|
|
sizeof(genreg) + datasize +
|
|
(full_headers ? (sizeof(header) + 2*endseg.size()) : 0);
|
|
u8 *const ret = (u8 *) malloc(totalsize);
|
|
int offset = 0;
|
|
|
|
#define F(x) memcpy(ret + offset, &x, sizeof(x)) ; offset += sizeof(x)
|
|
if (full_headers) {
|
|
F(header);
|
|
}
|
|
SEGMENT(seg);
|
|
F(pageinfo);
|
|
SEGMENT(seg2);
|
|
F(genreg);
|
|
jbig2enc_tobuffer(&ctx, ret + offset);
|
|
offset += datasize;
|
|
|
|
if (full_headers) {
|
|
endseg.type = segment_end_of_page;
|
|
SEGMENT(endseg);
|
|
endseg.type = segment_end_of_file;
|
|
SEGMENT(endseg);
|
|
}
|
|
|
|
if (totalsize != offset) abort();
|
|
|
|
jbig2enc_dealloc(&ctx);
|
|
|
|
*length = offset;
|
|
|
|
return ret;
|
|
}
|
|
|