You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
19 KiB
532 lines
19 KiB
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
* Copyright (C) 2016 Mopria Alliance, Inc.
|
|
* Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include "ifc_print_job.h"
|
|
#include "lib_pcl.h"
|
|
#include "wprint_image.h"
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
#ifndef __USE_UNIX98
|
|
#define __USE_UNIX98
|
|
#endif
|
|
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
|
|
#define MAX_SEND_BUFFS (BUFFERED_ROWS / STRIPE_HEIGHT)
|
|
|
|
#define TAG "plugin_pcl"
|
|
|
|
typedef enum {
|
|
MSG_START_JOB,
|
|
MSG_START_PAGE,
|
|
MSG_SEND,
|
|
MSG_END_JOB,
|
|
MSG_END_PAGE,
|
|
} msg_id_t;
|
|
|
|
typedef struct {
|
|
msg_id_t id;
|
|
|
|
union {
|
|
struct {
|
|
float extra_margin;
|
|
int width;
|
|
int height;
|
|
} start_page;
|
|
struct {
|
|
char *buffer;
|
|
int start_row;
|
|
int num_rows;
|
|
int bytes_per_row;
|
|
} send;
|
|
struct {
|
|
int page;
|
|
char *buffers[MAX_SEND_BUFFS];
|
|
int count;
|
|
} end_page;
|
|
} param;
|
|
} msgQ_msg_t;
|
|
|
|
typedef struct {
|
|
wJob_t job_handle;
|
|
msg_q_id msgQ;
|
|
pthread_t send_tid;
|
|
pcl_job_info_t job_info;
|
|
wprint_job_params_t *job_params;
|
|
sem_t buffs_sem;
|
|
ifc_pcl_t *pcl_ifc;
|
|
} plugin_data_t;
|
|
|
|
static const char *_mime_types[] = {
|
|
MIME_TYPE_PDF,
|
|
NULL};
|
|
|
|
static const char *_print_formats[] = {
|
|
PRINT_FORMAT_PCLM,
|
|
PRINT_FORMAT_PWG,
|
|
PRINT_FORMAT_PDF,
|
|
NULL};
|
|
|
|
static const char **_get_mime_types(void) {
|
|
return _mime_types;
|
|
}
|
|
|
|
static const char **_get_print_formats(void) {
|
|
return _print_formats;
|
|
}
|
|
|
|
static void _cleanup_plugin_data(plugin_data_t *priv) {
|
|
if (priv != NULL) {
|
|
if (priv->msgQ != MSG_Q_INVALID_ID) {
|
|
priv->job_info.wprint_ifc->msgQDelete(priv->msgQ);
|
|
}
|
|
sem_destroy(&priv->buffs_sem);
|
|
free(priv);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Waits to receive message from the msgQ. Handles messages and sends commands to handle jobs
|
|
*/
|
|
static void *_send_thread(void *param) {
|
|
msgQ_msg_t msg;
|
|
plugin_data_t *priv = (plugin_data_t *) param;
|
|
|
|
while (priv->job_info.wprint_ifc->msgQReceive(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t),
|
|
WAIT_FOREVER) == OK) {
|
|
if (msg.id == MSG_START_JOB) {
|
|
priv->pcl_ifc->start_job(priv->job_handle, &priv->job_info,
|
|
priv->job_params->media_size, priv->job_params->media_type,
|
|
priv->job_params->pixel_units, priv->job_params->duplex,
|
|
priv->job_params->dry_time, priv->job_params->color_space,
|
|
priv->job_params->media_tray, priv->job_params->page_top_margin,
|
|
priv->job_params->page_left_margin);
|
|
} else if (msg.id == MSG_START_PAGE) {
|
|
priv->pcl_ifc->start_page(&priv->job_info, msg.param.start_page.width,
|
|
msg.param.start_page.height);
|
|
} else if (msg.id == MSG_SEND) {
|
|
if (!priv->pcl_ifc->canCancelMidPage() || !priv->job_params->cancelled) {
|
|
priv->pcl_ifc->print_swath(&priv->job_info, msg.param.send.buffer,
|
|
msg.param.send.start_row, msg.param.send.num_rows,
|
|
msg.param.send.bytes_per_row);
|
|
}
|
|
sem_post(&priv->buffs_sem);
|
|
} else if (msg.id == MSG_END_PAGE) {
|
|
int i;
|
|
priv->pcl_ifc->end_page(&priv->job_info, msg.param.end_page.page);
|
|
for (i = 0; i < msg.param.end_page.count; i++) {
|
|
if (msg.param.end_page.buffers[i] != NULL) {
|
|
free(msg.param.end_page.buffers[i]);
|
|
}
|
|
}
|
|
} else if (msg.id == MSG_END_JOB) {
|
|
priv->pcl_ifc->end_job(&priv->job_info);
|
|
break;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Starts pcl thread
|
|
*/
|
|
static status_t _start_thread(plugin_data_t *param) {
|
|
sigset_t allsig, oldsig;
|
|
status_t result;
|
|
|
|
if (param == NULL) {
|
|
return ERROR;
|
|
}
|
|
|
|
param->send_tid = pthread_self();
|
|
|
|
result = OK;
|
|
sigfillset(&allsig);
|
|
#if CHECK_PTHREAD_SIGMASK_STATUS
|
|
result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
|
|
#else // CHECK_PTHREAD_SIGMASK_STATUS
|
|
pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
|
|
#endif // CHECK_PTHREAD_SIGMASK_STATUS
|
|
if (result == OK) {
|
|
result = (status_t) pthread_create(&(param->send_tid), 0, _send_thread, (void *) param);
|
|
if ((result == ERROR) && (param->send_tid != pthread_self())) {
|
|
#if USE_PTHREAD_CANCEL
|
|
pthread_cancel(param->send_tid);
|
|
#else // else USE_PTHREAD_CANCEL
|
|
pthread_kill(param->send_tid, SIGKILL);
|
|
#endif // USE_PTHREAD_CANCEL
|
|
param->send_tid = pthread_self();
|
|
}
|
|
}
|
|
|
|
if (result == OK) {
|
|
sched_yield();
|
|
#if CHECK_PTHREAD_SIGMASK_STATUS
|
|
result = pthread_sigmask(SIG_SETMASK, &oldsig, 0);
|
|
#else // CHECK_PTHREAD_SIGMASK_STATUS
|
|
pthread_sigmask(SIG_SETMASK, &oldsig, 0);
|
|
#endif // CHECK_PTHREAD_SIGMASK_STATUS
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Stops pcl thread
|
|
*/
|
|
static status_t _stop_thread(plugin_data_t *priv) {
|
|
status_t result = ERROR;
|
|
if (priv == NULL) {
|
|
return result;
|
|
}
|
|
if (!pthread_equal(priv->send_tid, pthread_self())) {
|
|
msgQ_msg_t msg;
|
|
msg.id = MSG_END_JOB;
|
|
|
|
priv->job_info.wprint_ifc->msgQSend(
|
|
priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
|
|
pthread_join(priv->send_tid, 0);
|
|
priv->send_tid = pthread_self();
|
|
result = OK;
|
|
}
|
|
_cleanup_plugin_data(priv);
|
|
return result;
|
|
}
|
|
|
|
static int _start_job(wJob_t job_handle, const ifc_wprint_t *wprint_ifc_p,
|
|
const ifc_print_job_t *print_ifc_p, wprint_job_params_t *job_params) {
|
|
msgQ_msg_t msg;
|
|
plugin_data_t *priv = NULL;
|
|
|
|
do {
|
|
if (job_params == NULL) continue;
|
|
|
|
job_params->plugin_data = NULL;
|
|
if ((wprint_ifc_p == NULL) || (print_ifc_p == NULL)) continue;
|
|
|
|
priv = (plugin_data_t *) malloc(sizeof(plugin_data_t));
|
|
if (priv == NULL) continue;
|
|
|
|
memset(priv, 0, sizeof(plugin_data_t));
|
|
|
|
priv->job_handle = job_handle;
|
|
priv->job_params = job_params;
|
|
priv->send_tid = pthread_self();
|
|
priv->job_info.job_handle = _WJOBH_NONE;
|
|
priv->job_info.print_ifc = (ifc_print_job_t *) print_ifc_p;
|
|
priv->job_info.wprint_ifc = (ifc_wprint_t *) wprint_ifc_p;
|
|
priv->job_info.strip_height = job_params->strip_height;
|
|
priv->job_info.useragent = job_params->useragent;
|
|
|
|
sem_init(&priv->buffs_sem, 0, MAX_SEND_BUFFS);
|
|
switch (job_params->pcl_type) {
|
|
case PCLm:
|
|
priv->pcl_ifc = pclm_connect();
|
|
break;
|
|
case PCLPWG:
|
|
priv->pcl_ifc = pwg_connect();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (priv->pcl_ifc == NULL) {
|
|
LOGE("ERROR: cannot start PCL job, no ifc found");
|
|
continue;
|
|
}
|
|
|
|
priv->msgQ = priv->job_info.wprint_ifc->msgQCreate(
|
|
(MAX_SEND_BUFFS * 2), sizeof(msgQ_msg_t));
|
|
if (priv->msgQ == MSG_Q_INVALID_ID) continue;
|
|
|
|
if (_start_thread(priv) == ERROR) continue;
|
|
|
|
job_params->plugin_data = (void *) priv;
|
|
msg.id = MSG_START_JOB;
|
|
priv->job_info.wprint_ifc->msgQSend(
|
|
priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
|
|
|
|
return OK;
|
|
} while (0);
|
|
|
|
_cleanup_plugin_data(priv);
|
|
return ERROR;
|
|
}
|
|
|
|
static status_t _print_page(wprint_job_params_t *job_params, const char *mime_type,
|
|
const char *pathname) {
|
|
wprint_image_info_t *image_info;
|
|
FILE *imgfile;
|
|
status_t result;
|
|
int num_rows, height, image_row;
|
|
int blank_data;
|
|
char *buff;
|
|
int i, buff_index, buff_size;
|
|
char *buff_pool[MAX_SEND_BUFFS];
|
|
|
|
int nbytes;
|
|
plugin_data_t *priv;
|
|
msgQ_msg_t msg;
|
|
int image_padding = PAD_PRINT;
|
|
|
|
if (job_params == NULL) return ERROR;
|
|
|
|
priv = (plugin_data_t *) job_params->plugin_data;
|
|
|
|
if (priv == NULL) return ERROR;
|
|
|
|
switch (job_params->pcl_type) {
|
|
case PCLm:
|
|
case PCLPWG:
|
|
image_padding = PAD_ALL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (pathname == NULL) {
|
|
LOGE("_print_page(): cannot print file with NULL name");
|
|
msg.param.end_page.page = -1;
|
|
msg.param.end_page.count = 0;
|
|
result = ERROR;
|
|
} else if (strlen(pathname)) {
|
|
image_info = malloc(sizeof(wprint_image_info_t));
|
|
if (image_info == NULL) return ERROR;
|
|
|
|
imgfile = fopen(pathname, "r");
|
|
if (imgfile) {
|
|
LOGD("_print_page(): fopen succeeded on %s", pathname);
|
|
wprint_image_setup(image_info, mime_type, priv->job_info.wprint_ifc,
|
|
job_params->pixel_units, job_params->pdf_render_resolution);
|
|
wprint_image_init(image_info, pathname, job_params->page_num);
|
|
|
|
// get the image_info of the input file of specified MIME type
|
|
if ((result = wprint_image_get_info(imgfile, image_info)) == OK) {
|
|
wprint_rotation_t rotation = ROT_0;
|
|
|
|
if ((job_params->render_flags & RENDER_FLAG_PORTRAIT_MODE) != 0) {
|
|
LOGI("_print_page(): portrait mode");
|
|
rotation = ROT_0;
|
|
} else if ((job_params->render_flags & RENDER_FLAG_LANDSCAPE_MODE) != 0) {
|
|
LOGI("_print_page(): landscape mode");
|
|
rotation = ROT_90;
|
|
} else if (wprint_image_is_landscape(image_info) &&
|
|
((job_params->render_flags & RENDER_FLAG_AUTO_ROTATE) != 0)) {
|
|
LOGI("_print_page(): auto mode");
|
|
rotation = ROT_90;
|
|
}
|
|
|
|
if ((job_params->render_flags & RENDER_FLAG_CENTER_ON_ORIENTATION) != 0) {
|
|
job_params->render_flags &= ~(RENDER_FLAG_CENTER_HORIZONTAL |
|
|
RENDER_FLAG_CENTER_VERTICAL);
|
|
job_params->render_flags |= ((rotation == ROT_0) ? RENDER_FLAG_CENTER_HORIZONTAL
|
|
: RENDER_FLAG_CENTER_VERTICAL);
|
|
}
|
|
|
|
if ((job_params->duplex == DUPLEX_MODE_BOOK) &&
|
|
(job_params->page_backside) &&
|
|
((job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) != 0) &&
|
|
((job_params->render_flags & RENDER_FLAG_BACK_PAGE_PREROTATED) == 0)) {
|
|
rotation = ((rotation == ROT_0) ? ROT_180 : ROT_270);
|
|
}
|
|
LOGI("_print_page(): rotation = %d", rotation);
|
|
|
|
wprint_image_set_output_properties(image_info, rotation,
|
|
job_params->printable_area_width, job_params->printable_area_height,
|
|
job_params->print_top_margin, job_params->print_left_margin,
|
|
job_params->print_right_margin, job_params->print_bottom_margin,
|
|
job_params->render_flags, job_params->strip_height, MAX_SEND_BUFFS,
|
|
image_padding);
|
|
|
|
// allocate memory for a stripe of data
|
|
for (i = 0; i < MAX_SEND_BUFFS; i++) {
|
|
buff_pool[i] = NULL;
|
|
}
|
|
|
|
blank_data = MAX_SEND_BUFFS;
|
|
buff_size = wprint_image_get_output_buff_size(image_info);
|
|
for (i = 0; i < MAX_SEND_BUFFS; i++) {
|
|
buff_pool[i] = malloc(buff_size);
|
|
if (buff_pool[i] == NULL) {
|
|
break;
|
|
}
|
|
memset(buff_pool[i], 0xff, buff_size);
|
|
}
|
|
|
|
if (i == MAX_SEND_BUFFS) {
|
|
msg.id = MSG_START_PAGE;
|
|
msg.param.start_page.extra_margin = ((job_params->duplex !=
|
|
DUPLEX_MODE_NONE) &&
|
|
((job_params->page_num & 0x1) == 0))
|
|
? job_params->page_bottom_margin : 0.0f;
|
|
msg.param.start_page.width = wprint_image_get_width(image_info);
|
|
msg.param.start_page.height = wprint_image_get_height(image_info);
|
|
priv->job_info.num_components = image_info->num_components;
|
|
priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
|
|
sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
|
|
|
|
msg.id = MSG_SEND;
|
|
msg.param.send.bytes_per_row = BYTES_PER_PIXEL(wprint_image_get_width(
|
|
image_info));
|
|
|
|
// send blank rows for any offset
|
|
buff_index = 0;
|
|
num_rows = wprint_image_get_height(image_info);
|
|
image_row = 0;
|
|
|
|
// decode and render each stripe into PCL3 raster format
|
|
while ((result != ERROR) && (num_rows > 0)) {
|
|
if (priv->pcl_ifc->canCancelMidPage() && job_params->cancelled) {
|
|
break;
|
|
}
|
|
sem_wait(&priv->buffs_sem);
|
|
|
|
buff = buff_pool[buff_index];
|
|
buff_index = ((buff_index + 1) % MAX_SEND_BUFFS);
|
|
|
|
height = MIN(num_rows, job_params->strip_height);
|
|
if (!job_params->cancelled) {
|
|
nbytes = wprint_image_decode_stripe(image_info, image_row, &height,
|
|
(unsigned char *) buff);
|
|
|
|
if (blank_data > 0) {
|
|
blank_data--;
|
|
}
|
|
} else if (blank_data < MAX_SEND_BUFFS) {
|
|
nbytes = buff_size;
|
|
memset(buff, 0xff, buff_size);
|
|
blank_data++;
|
|
}
|
|
|
|
if (nbytes > 0) {
|
|
msg.param.send.buffer = buff;
|
|
msg.param.send.start_row = image_row;
|
|
msg.param.send.num_rows = height;
|
|
|
|
result = priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
|
|
sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
|
|
if (result < 0) {
|
|
sem_post(&priv->buffs_sem);
|
|
}
|
|
|
|
image_row += height;
|
|
num_rows -= height;
|
|
} else {
|
|
sem_post(&priv->buffs_sem);
|
|
if (nbytes < 0) {
|
|
LOGE("_print_page(): ERROR: file appears to be corrupted");
|
|
result = CORRUPT;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((result == OK) && job_params->cancelled) {
|
|
result = CANCELLED;
|
|
}
|
|
|
|
LOGI("_print_page(): sends done, result: %d", result);
|
|
|
|
// free the buffer and eject the page
|
|
msg.param.end_page.page = job_params->page_num;
|
|
LOGI("_print_page(): processed %d out of"
|
|
" %d rows of page # %d from %s to printer %s %s {%s}",
|
|
image_row, wprint_image_get_height(image_info),
|
|
job_params->page_num, pathname,
|
|
(job_params->last_page) ? "- last page" : "- ",
|
|
(job_params->cancelled) ? "- job cancelled"
|
|
: ".",
|
|
(result == OK) ? "OK" : "ERROR");
|
|
} else {
|
|
msg.param.end_page.page = -1;
|
|
result = ERROR;
|
|
LOGE("_print_page(): plugin_pcl cannot allocate memory for image stripe");
|
|
}
|
|
for (i = 0; i < MAX_SEND_BUFFS; i++) {
|
|
msg.param.end_page.buffers[i] = buff_pool[i];
|
|
}
|
|
msg.param.end_page.count = MAX_SEND_BUFFS;
|
|
} else {
|
|
msg.param.end_page.page = -1;
|
|
msg.param.end_page.count = 0;
|
|
result = CORRUPT;
|
|
LOGE("_print_page(): file does not appear to be valid");
|
|
}
|
|
|
|
// send the end page message
|
|
wprint_image_cleanup(image_info);
|
|
fclose(imgfile);
|
|
} else {
|
|
msg.param.end_page.page = -1;
|
|
msg.param.end_page.count = 0;
|
|
LOGE("_print_page(): could not open %s", pathname);
|
|
result = CORRUPT;
|
|
}
|
|
free(image_info);
|
|
} else {
|
|
LOGE("_print_page(): ERROR: filename was empty");
|
|
msg.param.end_page.page = -1;
|
|
msg.param.end_page.count = 0;
|
|
result = ERROR;
|
|
}
|
|
|
|
msg.id = MSG_END_PAGE;
|
|
priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT,
|
|
MSG_Q_FIFO);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Prints a blank page
|
|
*/
|
|
static int _print_blank_page(wJob_t job_handle, wprint_job_params_t *job_params) {
|
|
msgQ_msg_t msg;
|
|
plugin_data_t *priv;
|
|
|
|
if (job_params == NULL) return ERROR;
|
|
|
|
priv = (plugin_data_t *) job_params->plugin_data;
|
|
if (priv == NULL) return ERROR;
|
|
|
|
msg.id = MSG_END_PAGE;
|
|
msg.param.end_page.page = -1;
|
|
msg.param.end_page.count = 0;
|
|
priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT,
|
|
MSG_Q_FIFO);
|
|
return OK;
|
|
}
|
|
|
|
static int _end_job(wprint_job_params_t *job_params) {
|
|
if (job_params != NULL) {
|
|
_stop_thread((plugin_data_t *) job_params->plugin_data);
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
wprint_plugin_t *libwprintplugin_pcl_reg(void) {
|
|
static const wprint_plugin_t _pcl_plugin = {.version = WPRINT_PLUGIN_VERSION(0),
|
|
.priority = PRIORITY_LOCAL, .get_mime_types = _get_mime_types,
|
|
.get_print_formats = _get_print_formats, .start_job = _start_job,
|
|
.print_page = _print_page, .print_blank_page = _print_blank_page, .end_job = _end_job,};
|
|
return ((wprint_plugin_t *) &_pcl_plugin);
|
|
} |