| /* |
| * 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); |
| } |