1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy. 2141cc406Sopenharmony_ci 3141cc406Sopenharmony_ci Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com> 4141cc406Sopenharmony_ci 5141cc406Sopenharmony_ci This file is part of the SANE package. 6141cc406Sopenharmony_ci 7141cc406Sopenharmony_ci SANE is free software; you can redistribute it and/or modify it under 8141cc406Sopenharmony_ci the terms of the GNU General Public License as published by the Free 9141cc406Sopenharmony_ci Software Foundation; either version 3 of the License, or (at your 10141cc406Sopenharmony_ci option) any later version. 11141cc406Sopenharmony_ci 12141cc406Sopenharmony_ci SANE is distributed in the hope that it will be useful, but WITHOUT 13141cc406Sopenharmony_ci ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14141cc406Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15141cc406Sopenharmony_ci for more details. 16141cc406Sopenharmony_ci 17141cc406Sopenharmony_ci You should have received a copy of the GNU General Public License 18141cc406Sopenharmony_ci along with sane; see the file COPYING. 19141cc406Sopenharmony_ci If not, see <https://www.gnu.org/licenses/>. 20141cc406Sopenharmony_ci 21141cc406Sopenharmony_ci This file implements a SANE backend for eSCL scanners. */ 22141cc406Sopenharmony_ci 23141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY 24141cc406Sopenharmony_ci#include "../include/sane/config.h" 25141cc406Sopenharmony_ci 26141cc406Sopenharmony_ci#include "escl.h" 27141cc406Sopenharmony_ci 28141cc406Sopenharmony_ci#include "../include/sane/sanei.h" 29141cc406Sopenharmony_ci 30141cc406Sopenharmony_ci#include <stdio.h> 31141cc406Sopenharmony_ci#include <stdlib.h> 32141cc406Sopenharmony_ci#include <string.h> 33141cc406Sopenharmony_ci 34141cc406Sopenharmony_ci#include <errno.h> 35141cc406Sopenharmony_ci 36141cc406Sopenharmony_ci#if(defined HAVE_MUPDF) 37141cc406Sopenharmony_ci#include <mupdf/fitz.h> 38141cc406Sopenharmony_ci#endif 39141cc406Sopenharmony_ci 40141cc406Sopenharmony_ci#include <setjmp.h> 41141cc406Sopenharmony_ci 42141cc406Sopenharmony_ci 43141cc406Sopenharmony_ci#if(defined HAVE_MUPDF) 44141cc406Sopenharmony_ci 45141cc406Sopenharmony_ci// TODO: WIN32: HANDLE CreateFileW(), etc. 46141cc406Sopenharmony_ci// TODO: POSIX: int creat(), read(), write(), lseeko, etc. 47141cc406Sopenharmony_ci 48141cc406Sopenharmony_citypedef struct fz_file_stream_escl_s 49141cc406Sopenharmony_ci{ 50141cc406Sopenharmony_ci FILE *file; 51141cc406Sopenharmony_ci unsigned char buffer[4096]; 52141cc406Sopenharmony_ci} fz_file_stream_escl; 53141cc406Sopenharmony_ci 54141cc406Sopenharmony_cistatic int 55141cc406Sopenharmony_cinext_file_escl(fz_context *ctx, fz_stream *stm, size_t n) 56141cc406Sopenharmony_ci{ 57141cc406Sopenharmony_ci fz_file_stream_escl *state = stm->state; 58141cc406Sopenharmony_ci 59141cc406Sopenharmony_ci /* n is only a hint, that we can safely ignore */ 60141cc406Sopenharmony_ci n = fread(state->buffer, 1, sizeof(state->buffer), state->file); 61141cc406Sopenharmony_ci if (n < sizeof(state->buffer) && ferror(state->file)) 62141cc406Sopenharmony_ci fz_throw(ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno)); 63141cc406Sopenharmony_ci stm->rp = state->buffer; 64141cc406Sopenharmony_ci stm->wp = state->buffer + n; 65141cc406Sopenharmony_ci stm->pos += (int64_t)n; 66141cc406Sopenharmony_ci 67141cc406Sopenharmony_ci if (n == 0) 68141cc406Sopenharmony_ci return EOF; 69141cc406Sopenharmony_ci return *stm->rp++; 70141cc406Sopenharmony_ci} 71141cc406Sopenharmony_ci 72141cc406Sopenharmony_cistatic void 73141cc406Sopenharmony_cidrop_file_escl(fz_context *ctx, void *state_) 74141cc406Sopenharmony_ci{ 75141cc406Sopenharmony_ci fz_file_stream_escl *state = state_; 76141cc406Sopenharmony_ci int n = fclose(state->file); 77141cc406Sopenharmony_ci if (n < 0) 78141cc406Sopenharmony_ci fz_warn(ctx, "close error: %s", strerror(errno)); 79141cc406Sopenharmony_ci fz_free(ctx, state); 80141cc406Sopenharmony_ci} 81141cc406Sopenharmony_ci 82141cc406Sopenharmony_cistatic void 83141cc406Sopenharmony_ciseek_file_escl(fz_context *ctx, fz_stream *stm, int64_t offset, int whence) 84141cc406Sopenharmony_ci{ 85141cc406Sopenharmony_ci fz_file_stream_escl *state = stm->state; 86141cc406Sopenharmony_ci#ifdef _WIN32 87141cc406Sopenharmony_ci int64_t n = _fseeki64(state->file, offset, whence); 88141cc406Sopenharmony_ci#else 89141cc406Sopenharmony_ci int64_t n = fseeko(state->file, offset, whence); 90141cc406Sopenharmony_ci#endif 91141cc406Sopenharmony_ci if (n < 0) 92141cc406Sopenharmony_ci fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno)); 93141cc406Sopenharmony_ci#ifdef _WIN32 94141cc406Sopenharmony_ci stm->pos = _ftelli64(state->file); 95141cc406Sopenharmony_ci#else 96141cc406Sopenharmony_ci stm->pos = ftello(state->file); 97141cc406Sopenharmony_ci#endif 98141cc406Sopenharmony_ci stm->rp = state->buffer; 99141cc406Sopenharmony_ci stm->wp = state->buffer; 100141cc406Sopenharmony_ci} 101141cc406Sopenharmony_ci 102141cc406Sopenharmony_cistatic fz_stream * 103141cc406Sopenharmony_cifz_open_file_ptr_escl(fz_context *ctx, FILE *file) 104141cc406Sopenharmony_ci{ 105141cc406Sopenharmony_ci fz_stream *stm; 106141cc406Sopenharmony_ci fz_file_stream_escl *state = fz_malloc_struct(ctx, fz_file_stream_escl); 107141cc406Sopenharmony_ci state->file = file; 108141cc406Sopenharmony_ci 109141cc406Sopenharmony_ci stm = fz_new_stream(ctx, state, next_file_escl, drop_file_escl); 110141cc406Sopenharmony_ci stm->seek = seek_file_escl; 111141cc406Sopenharmony_ci 112141cc406Sopenharmony_ci return stm; 113141cc406Sopenharmony_ci} 114141cc406Sopenharmony_ci 115141cc406Sopenharmony_ci/** 116141cc406Sopenharmony_ci * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler) 117141cc406Sopenharmony_ci * \brief Function that aims to decompress the pdf image to SANE be able 118141cc406Sopenharmony_ci * to read the image. 119141cc406Sopenharmony_ci * This function is called in the "sane_read" function. 120141cc406Sopenharmony_ci * 121141cc406Sopenharmony_ci * \return SANE_STATUS_GOOD (if everything is OK, otherwise, 122141cc406Sopenharmony_ci * SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) 123141cc406Sopenharmony_ci */ 124141cc406Sopenharmony_ciSANE_Status 125141cc406Sopenharmony_ciget_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps) 126141cc406Sopenharmony_ci{ 127141cc406Sopenharmony_ci int page_number = -1, page_count = -2; 128141cc406Sopenharmony_ci fz_context *ctx; 129141cc406Sopenharmony_ci fz_document *doc; 130141cc406Sopenharmony_ci fz_pixmap *pix; 131141cc406Sopenharmony_ci fz_matrix ctm; 132141cc406Sopenharmony_ci fz_stream *stream; 133141cc406Sopenharmony_ci unsigned char *surface = NULL; /* Image data */ 134141cc406Sopenharmony_ci SANE_Status status = SANE_STATUS_GOOD; 135141cc406Sopenharmony_ci 136141cc406Sopenharmony_ci /* Create a context to hold the exception stack and various caches. */ 137141cc406Sopenharmony_ci ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); 138141cc406Sopenharmony_ci if (!ctx) 139141cc406Sopenharmony_ci { 140141cc406Sopenharmony_ci DBG(1, "cannot create mupdf context\n"); 141141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 142141cc406Sopenharmony_ci goto close_file; 143141cc406Sopenharmony_ci } 144141cc406Sopenharmony_ci 145141cc406Sopenharmony_ci /* Register the default file types to handle. */ 146141cc406Sopenharmony_ci fz_try(ctx) 147141cc406Sopenharmony_ci fz_register_document_handlers(ctx); 148141cc406Sopenharmony_ci fz_catch(ctx) 149141cc406Sopenharmony_ci { 150141cc406Sopenharmony_ci DBG(1, "cannot register document handlers: %s\n", fz_caught_message(ctx)); 151141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 152141cc406Sopenharmony_ci goto drop_context; 153141cc406Sopenharmony_ci } 154141cc406Sopenharmony_ci 155141cc406Sopenharmony_ci /* Open the stream. */ 156141cc406Sopenharmony_ci fz_try(ctx) 157141cc406Sopenharmony_ci stream = fz_open_file_ptr_escl(ctx, scanner->tmp); 158141cc406Sopenharmony_ci fz_catch(ctx) 159141cc406Sopenharmony_ci { 160141cc406Sopenharmony_ci DBG(1, "cannot open stream: %s\n", fz_caught_message(ctx)); 161141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 162141cc406Sopenharmony_ci goto drop_context; 163141cc406Sopenharmony_ci } 164141cc406Sopenharmony_ci 165141cc406Sopenharmony_ci /* Seek stream. */ 166141cc406Sopenharmony_ci fz_try(ctx) 167141cc406Sopenharmony_ci fz_seek(ctx, stream, 0, SEEK_SET); 168141cc406Sopenharmony_ci fz_catch(ctx) 169141cc406Sopenharmony_ci { 170141cc406Sopenharmony_ci DBG(1, "cannot seek stream: %s\n", fz_caught_message(ctx)); 171141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 172141cc406Sopenharmony_ci goto drop_stream; 173141cc406Sopenharmony_ci } 174141cc406Sopenharmony_ci 175141cc406Sopenharmony_ci /* Open the document. */ 176141cc406Sopenharmony_ci fz_try(ctx) 177141cc406Sopenharmony_ci doc = fz_open_document_with_stream(ctx, "filename.pdf", stream); 178141cc406Sopenharmony_ci fz_catch(ctx) 179141cc406Sopenharmony_ci { 180141cc406Sopenharmony_ci DBG(1, "cannot open document: %s\n", fz_caught_message(ctx)); 181141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 182141cc406Sopenharmony_ci goto drop_stream; 183141cc406Sopenharmony_ci } 184141cc406Sopenharmony_ci 185141cc406Sopenharmony_ci /* Count the number of pages. */ 186141cc406Sopenharmony_ci fz_try(ctx) 187141cc406Sopenharmony_ci page_count = fz_count_pages(ctx, doc); 188141cc406Sopenharmony_ci fz_catch(ctx) 189141cc406Sopenharmony_ci { 190141cc406Sopenharmony_ci DBG(1, "cannot count number of pages: %s\n", fz_caught_message(ctx)); 191141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 192141cc406Sopenharmony_ci goto drop_document; 193141cc406Sopenharmony_ci } 194141cc406Sopenharmony_ci 195141cc406Sopenharmony_ci if (page_number < 0 || page_number >= page_count) 196141cc406Sopenharmony_ci { 197141cc406Sopenharmony_ci DBG(1, "page number out of range: %d (page count %d)\n", page_number + 1, page_count); 198141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 199141cc406Sopenharmony_ci goto drop_document; 200141cc406Sopenharmony_ci } 201141cc406Sopenharmony_ci 202141cc406Sopenharmony_ci /* Compute a transformation matrix for the zoom and rotation desired. */ 203141cc406Sopenharmony_ci /* The default resolution without scaling is 72 dpi. */ 204141cc406Sopenharmony_ci fz_scale(&ctm, (float)1.0, (float)1.0); 205141cc406Sopenharmony_ci fz_pre_rotate(&ctm, (float)0.0); 206141cc406Sopenharmony_ci 207141cc406Sopenharmony_ci /* Render page to an RGB pixmap. */ 208141cc406Sopenharmony_ci fz_try(ctx) 209141cc406Sopenharmony_ci pix = fz_new_pixmap_from_page_number(ctx, doc, 0, &ctm, fz_device_rgb(ctx), 0); 210141cc406Sopenharmony_ci fz_catch(ctx) 211141cc406Sopenharmony_ci { 212141cc406Sopenharmony_ci DBG(1, "cannot render page: %s\n", fz_caught_message(ctx)); 213141cc406Sopenharmony_ci status = SANE_STATUS_INVAL; 214141cc406Sopenharmony_ci goto drop_document; 215141cc406Sopenharmony_ci } 216141cc406Sopenharmony_ci 217141cc406Sopenharmony_ci surface = malloc(pix->h * pix->stride); 218141cc406Sopenharmony_ci memcpy(surface, pix->samples, (pix->h * pix->stride)); 219141cc406Sopenharmony_ci 220141cc406Sopenharmony_ci // If necessary, trim the image. 221141cc406Sopenharmony_ci surface = escl_crop_surface(scanner, surface, pix->w, pix->h, pix->n, width, height); 222141cc406Sopenharmony_ci if (!surface) { 223141cc406Sopenharmony_ci DBG( 1, "Escl Pdf : Surface Memory allocation problem\n"); 224141cc406Sopenharmony_ci status = SANE_STATUS_NO_MEM; 225141cc406Sopenharmony_ci goto drop_pix; 226141cc406Sopenharmony_ci } 227141cc406Sopenharmony_ci *bps = pix->n; 228141cc406Sopenharmony_ci 229141cc406Sopenharmony_ci /* Clean up. */ 230141cc406Sopenharmony_cidrop_pix: 231141cc406Sopenharmony_ci fz_drop_pixmap(ctx, pix); 232141cc406Sopenharmony_cidrop_document: 233141cc406Sopenharmony_ci fz_drop_document(ctx, doc); 234141cc406Sopenharmony_cidrop_stream: 235141cc406Sopenharmony_ci fz_drop_stream(ctx, stream); 236141cc406Sopenharmony_cidrop_context: 237141cc406Sopenharmony_ci fz_drop_context(ctx); 238141cc406Sopenharmony_ci 239141cc406Sopenharmony_ciclose_file: 240141cc406Sopenharmony_ci if (scanner->tmp) 241141cc406Sopenharmony_ci fclose(scanner->tmp); 242141cc406Sopenharmony_ci scanner->tmp = NULL; 243141cc406Sopenharmony_ci return status; 244141cc406Sopenharmony_ci} 245141cc406Sopenharmony_ci#else 246141cc406Sopenharmony_ci 247141cc406Sopenharmony_ciSANE_Status 248141cc406Sopenharmony_ciget_PDF_data(capabilities_t __sane_unused__ *scanner, 249141cc406Sopenharmony_ci int __sane_unused__ *width, 250141cc406Sopenharmony_ci int __sane_unused__ *height, 251141cc406Sopenharmony_ci int __sane_unused__ *bps) 252141cc406Sopenharmony_ci{ 253141cc406Sopenharmony_ci return (SANE_STATUS_INVAL); 254141cc406Sopenharmony_ci} 255141cc406Sopenharmony_ci 256141cc406Sopenharmony_ci#endif 257