// Copyright (C) 2004-2021 Artifex Software, Inc. // // This file is part of MuPDF. // // MuPDF is free software: you can redistribute it and/or modify it under the // terms of the GNU Affero General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // MuPDF 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 Affero General Public License for more // details. // // You should have received a copy of the GNU Affero General Public License // along with MuPDF. If not, see // // Alternative licensing terms are available from the licensor. // For commercial licensing, see or contact // Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, // CA 94945, U.S.A., +1(415)492-9861, for further information. #include "mupdf/fitz.h" /* Fax G3/G4 tables */ typedef struct { unsigned short code; unsigned short nbits; } cfe_code; typedef struct { cfe_code termination[64]; cfe_code makeup[41]; } cf_runs; /* Define the end-of-line code. */ static const cfe_code cf_run_eol = {1, 12}; /* Define the 2-D run codes. */ static const cfe_code cf2_run_pass = {0x1, 4}; static const cfe_code cf2_run_vertical[7] = { {0x3, 7}, {0x3, 6}, {0x3, 3}, {0x1, 1}, {0x2, 3}, {0x2, 6}, {0x2, 7} }; static const cfe_code cf2_run_horizontal = {1, 3}; /* White run codes. */ static const cf_runs cf_white_runs = { /* Termination codes */ { {0x35, 8}, {0x7, 6}, {0x7, 4}, {0x8, 4}, {0xb, 4}, {0xc, 4}, {0xe, 4}, {0xf, 4}, {0x13, 5}, {0x14, 5}, {0x7, 5}, {0x8, 5}, {0x8, 6}, {0x3, 6}, {0x34, 6}, {0x35, 6}, {0x2a, 6}, {0x2b, 6}, {0x27, 7}, {0xc, 7}, {0x8, 7}, {0x17, 7}, {0x3, 7}, {0x4, 7}, {0x28, 7}, {0x2b, 7}, {0x13, 7}, {0x24, 7}, {0x18, 7}, {0x2, 8}, {0x3, 8}, {0x1a, 8}, {0x1b, 8}, {0x12, 8}, {0x13, 8}, {0x14, 8}, {0x15, 8}, {0x16, 8}, {0x17, 8}, {0x28, 8}, {0x29, 8}, {0x2a, 8}, {0x2b, 8}, {0x2c, 8}, {0x2d, 8}, {0x4, 8}, {0x5, 8}, {0xa, 8}, {0xb, 8}, {0x52, 8}, {0x53, 8}, {0x54, 8}, {0x55, 8}, {0x24, 8}, {0x25, 8}, {0x58, 8}, {0x59, 8}, {0x5a, 8}, {0x5b, 8}, {0x4a, 8}, {0x4b, 8}, {0x32, 8}, {0x33, 8}, {0x34, 8} }, /* Make-up codes */ { {0, 0} /* dummy */ , {0x1b, 5}, {0x12, 5}, {0x17, 6}, {0x37, 7}, {0x36, 8}, {0x37, 8}, {0x64, 8}, {0x65, 8}, {0x68, 8}, {0x67, 8}, {0xcc, 9}, {0xcd, 9}, {0xd2, 9}, {0xd3, 9}, {0xd4, 9}, {0xd5, 9}, {0xd6, 9}, {0xd7, 9}, {0xd8, 9}, {0xd9, 9}, {0xda, 9}, {0xdb, 9}, {0x98, 9}, {0x99, 9}, {0x9a, 9}, {0x18, 6}, {0x9b, 9}, {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12}, {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12}, {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12}, {0x1f, 12} } }; /* Black run codes. */ static const cf_runs cf_black_runs = { /* Termination codes */ { {0x37, 10}, {0x2, 3}, {0x3, 2}, {0x2, 2}, {0x3, 3}, {0x3, 4}, {0x2, 4}, {0x3, 5}, {0x5, 6}, {0x4, 6}, {0x4, 7}, {0x5, 7}, {0x7, 7}, {0x4, 8}, {0x7, 8}, {0x18, 9}, {0x17, 10}, {0x18, 10}, {0x8, 10}, {0x67, 11}, {0x68, 11}, {0x6c, 11}, {0x37, 11}, {0x28, 11}, {0x17, 11}, {0x18, 11}, {0xca, 12}, {0xcb, 12}, {0xcc, 12}, {0xcd, 12}, {0x68, 12}, {0x69, 12}, {0x6a, 12}, {0x6b, 12}, {0xd2, 12}, {0xd3, 12}, {0xd4, 12}, {0xd5, 12}, {0xd6, 12}, {0xd7, 12}, {0x6c, 12}, {0x6d, 12}, {0xda, 12}, {0xdb, 12}, {0x54, 12}, {0x55, 12}, {0x56, 12}, {0x57, 12}, {0x64, 12}, {0x65, 12}, {0x52, 12}, {0x53, 12}, {0x24, 12}, {0x37, 12}, {0x38, 12}, {0x27, 12}, {0x28, 12}, {0x58, 12}, {0x59, 12}, {0x2b, 12}, {0x2c, 12}, {0x5a, 12}, {0x66, 12}, {0x67, 12} }, /* Make-up codes. */ { {0, 0} /* dummy */ , {0xf, 10}, {0xc8, 12}, {0xc9, 12}, {0x5b, 12}, {0x33, 12}, {0x34, 12}, {0x35, 12}, {0x6c, 13}, {0x6d, 13}, {0x4a, 13}, {0x4b, 13}, {0x4c, 13}, {0x4d, 13}, {0x72, 13}, {0x73, 13}, {0x74, 13}, {0x75, 13}, {0x76, 13}, {0x77, 13}, {0x52, 13}, {0x53, 13}, {0x54, 13}, {0x55, 13}, {0x5a, 13}, {0x5b, 13}, {0x64, 13}, {0x65, 13}, {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12}, {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12}, {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12}, {0x1f, 12} } }; static inline int getbit(const unsigned char *buf, int x) { /* Invert bit to handle BlackIs1=false */ return ( ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1 ) ^ 1; } static inline int find_changing(const unsigned char *line, int x, int w) { int a, b; if (!line || x >= w) return w; if (x == -1) { a = 0; x = 0; } else { a = getbit(line, x); x++; } while (x < w) { b = getbit(line, x); if (a != b) break; x++; } return x; } static inline int find_changing_color(const unsigned char *line, int x, int w, int color) { if (!line || x >= w) return w; x = find_changing(line, x, w); if (x < w && getbit(line, x) != color) x = find_changing(line, x, w); return x; } static inline int getrun(const unsigned char *line, int x, int w, int c) { int z = x; while (z < w) { int b = getbit(line, z); if (c != b) break; ++z; } return z - x; } static inline void putcode(fz_context *ctx, fz_buffer *out, const cfe_code *run) { fz_append_bits(ctx, out, run->code, run->nbits); } static void putrun(fz_context *ctx, fz_buffer *out, int run, int c) { const cf_runs *codetable = c ? &cf_black_runs : &cf_white_runs; if (run > 63) { int m = run >> 6; while (m > 40) { putcode(ctx, out, &codetable->makeup[40]); m -= 40; } if (m > 0) putcode(ctx, out, &codetable->makeup[m]); putcode(ctx, out, &codetable->termination[run & 63]); } else { putcode(ctx, out, &codetable->termination[run]); } } fz_buffer * fz_compress_ccitt_fax_g4(fz_context *ctx, const unsigned char *src, int columns, int rows) { int stride = (columns + 7) >> 3; fz_buffer *out = fz_new_buffer(ctx, (stride * rows) >> 3); const unsigned char *ref = NULL; fz_try(ctx) { while (rows-- > 0) { int a0 = -1; int c = 0; while (a0 < columns) { int a1 = find_changing(src, a0, columns); int b1 = find_changing_color(ref, a0, columns, c^1); int b2 = find_changing(ref, b1, columns); int diff = b1 - a1; if (a0 < 0) a0 = 0; /* pass mode */ if (b2 < a1) { putcode(ctx, out, &cf2_run_pass); a0 = b2; } /* vertical mode */ else if (diff >= -3 && diff <= 3) { putcode(ctx, out, &cf2_run_vertical[diff + 3]); a0 = a1; c = c^1; } /* horizontal mode */ else { int a2 = find_changing(src, a1, columns); putcode(ctx, out, &cf2_run_horizontal); putrun(ctx, out, a1 - a0, c); putrun(ctx, out, a2 - a1, c^1); a0 = a2; } } ref = src; src += stride; } putcode(ctx, out, &cf_run_eol); putcode(ctx, out, &cf_run_eol); } fz_catch(ctx) { fz_drop_buffer(ctx, out); fz_rethrow(ctx); } return out; } fz_buffer * fz_compress_ccitt_fax_g3(fz_context *ctx, const unsigned char *src, int columns, int rows) { int stride = (columns + 7) >> 3; fz_buffer *out = fz_new_buffer(ctx, (stride * rows) >> 3); int i; fz_try(ctx) { while (rows-- > 0) { int a0 = 0; int c = 0; while (a0 < columns) { int run = getrun(src, a0, columns, c); putrun(ctx, out, run, c); a0 += run; c = c^1; } src += stride; } for (i = 0; i < 6; ++i) putcode(ctx, out, &cf_run_eol); } fz_catch(ctx) { fz_drop_buffer(ctx, out); fz_rethrow(ctx); } return out; }