Home | History | Annotate | Download | only in plugins
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  * Copyright (C) 2016 Mopria Alliance, Inc.
      4  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *      http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <unistd.h>
     22 #include "ifc_print_job.h"
     23 #include "lib_pcl.h"
     24 #include "wprint_image.h"
     25 
     26 #ifndef _GNU_SOURCE
     27 #define _GNU_SOURCE
     28 #endif
     29 #ifndef __USE_UNIX98
     30 #define __USE_UNIX98
     31 #endif
     32 
     33 #include <pthread.h>
     34 #include <semaphore.h>
     35 
     36 #define MAX_SEND_BUFFS (BUFFERED_ROWS / STRIPE_HEIGHT)
     37 
     38 #define TAG "plugin_pcl"
     39 
     40 typedef enum {
     41     MSG_START_JOB,
     42     MSG_START_PAGE,
     43     MSG_SEND,
     44     MSG_END_JOB,
     45     MSG_END_PAGE,
     46 } msg_id_t;
     47 
     48 typedef struct {
     49     msg_id_t id;
     50 
     51     union {
     52         struct {
     53             float extra_margin;
     54             int width;
     55             int height;
     56         } start_page;
     57         struct {
     58             char *buffer;
     59             int start_row;
     60             int num_rows;
     61             int bytes_per_row;
     62         } send;
     63         struct {
     64             int page;
     65             char *buffers[MAX_SEND_BUFFS];
     66             int count;
     67         } end_page;
     68     } param;
     69 } msgQ_msg_t;
     70 
     71 typedef struct {
     72     wJob_t job_handle;
     73     msg_q_id msgQ;
     74     pthread_t send_tid;
     75     pcl_job_info_t job_info;
     76     wprint_job_params_t *job_params;
     77     sem_t buffs_sem;
     78     ifc_pcl_t *pcl_ifc;
     79 } plugin_data_t;
     80 
     81 static const char *_mime_types[] = {
     82         MIME_TYPE_PDF,
     83         NULL};
     84 
     85 static const char *_print_formats[] = {
     86         PRINT_FORMAT_PCLM,
     87         PRINT_FORMAT_PWG,
     88         PRINT_FORMAT_PDF,
     89         NULL};
     90 
     91 static const char **_get_mime_types(void) {
     92     return _mime_types;
     93 }
     94 
     95 static const char **_get_print_formats(void) {
     96     return _print_formats;
     97 }
     98 
     99 static void _cleanup_plugin_data(plugin_data_t *priv) {
    100     if (priv != NULL) {
    101         if (priv->msgQ != MSG_Q_INVALID_ID) {
    102             priv->job_info.wprint_ifc->msgQDelete(priv->msgQ);
    103         }
    104         sem_destroy(&priv->buffs_sem);
    105         free(priv);
    106     }
    107 }
    108 
    109 /*
    110  * Waits to receive message from the msgQ. Handles messages and sends commands to handle jobs
    111  */
    112 static void *_send_thread(void *param) {
    113     msgQ_msg_t msg;
    114     plugin_data_t *priv = (plugin_data_t *) param;
    115 
    116     while (priv->job_info.wprint_ifc->msgQReceive(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t),
    117             WAIT_FOREVER) == OK) {
    118         if (msg.id == MSG_START_JOB) {
    119             priv->pcl_ifc->start_job(priv->job_handle, &priv->job_info,
    120                     priv->job_params->media_size, priv->job_params->media_type,
    121                     priv->job_params->pixel_units, priv->job_params->duplex,
    122                     priv->job_params->dry_time, priv->job_params->color_space,
    123                     priv->job_params->media_tray, priv->job_params->page_top_margin,
    124                     priv->job_params->page_left_margin);
    125         } else if (msg.id == MSG_START_PAGE) {
    126             priv->pcl_ifc->start_page(&priv->job_info, msg.param.start_page.width,
    127                     msg.param.start_page.height);
    128         } else if (msg.id == MSG_SEND) {
    129             if (!priv->pcl_ifc->canCancelMidPage() || !priv->job_params->cancelled) {
    130                 priv->pcl_ifc->print_swath(&priv->job_info, msg.param.send.buffer,
    131                         msg.param.send.start_row, msg.param.send.num_rows,
    132                         msg.param.send.bytes_per_row);
    133             }
    134             sem_post(&priv->buffs_sem);
    135         } else if (msg.id == MSG_END_PAGE) {
    136             int i;
    137             priv->pcl_ifc->end_page(&priv->job_info, msg.param.end_page.page);
    138             for (i = 0; i < msg.param.end_page.count; i++) {
    139                 if (msg.param.end_page.buffers[i] != NULL) {
    140                     free(msg.param.end_page.buffers[i]);
    141                 }
    142             }
    143         } else if (msg.id == MSG_END_JOB) {
    144             priv->pcl_ifc->end_job(&priv->job_info);
    145             break;
    146         }
    147     }
    148     return NULL;
    149 }
    150 
    151 /*
    152  * Starts pcl thread
    153  */
    154 static status_t _start_thread(plugin_data_t *param) {
    155     sigset_t allsig, oldsig;
    156     status_t result;
    157 
    158     if (param == NULL) {
    159         return ERROR;
    160     }
    161 
    162     param->send_tid = pthread_self();
    163 
    164     result = OK;
    165     sigfillset(&allsig);
    166 #if CHECK_PTHREAD_SIGMASK_STATUS
    167     result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
    168 #else // CHECK_PTHREAD_SIGMASK_STATUS
    169     pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
    170 #endif // CHECK_PTHREAD_SIGMASK_STATUS
    171     if (result == OK) {
    172         result = (status_t) pthread_create(&(param->send_tid), 0, _send_thread, (void *) param);
    173         if ((result == ERROR) && (param->send_tid != pthread_self())) {
    174 #if USE_PTHREAD_CANCEL
    175             pthread_cancel(param->send_tid);
    176 #else // else USE_PTHREAD_CANCEL
    177             pthread_kill(param->send_tid, SIGKILL);
    178 #endif // USE_PTHREAD_CANCEL
    179             param->send_tid = pthread_self();
    180         }
    181     }
    182 
    183     if (result == OK) {
    184         sched_yield();
    185 #if CHECK_PTHREAD_SIGMASK_STATUS
    186         result = pthread_sigmask(SIG_SETMASK, &oldsig, 0);
    187 #else // CHECK_PTHREAD_SIGMASK_STATUS
    188         pthread_sigmask(SIG_SETMASK, &oldsig, 0);
    189 #endif // CHECK_PTHREAD_SIGMASK_STATUS
    190     }
    191     return result;
    192 }
    193 
    194 /*
    195  * Stops pcl thread
    196  */
    197 static status_t _stop_thread(plugin_data_t *priv) {
    198     status_t result = ERROR;
    199     if (priv == NULL) {
    200         return result;
    201     }
    202     if (!pthread_equal(priv->send_tid, pthread_self())) {
    203         msgQ_msg_t msg;
    204         msg.id = MSG_END_JOB;
    205 
    206         priv->job_info.wprint_ifc->msgQSend(
    207                 priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
    208         pthread_join(priv->send_tid, 0);
    209         priv->send_tid = pthread_self();
    210         result = OK;
    211     }
    212     _cleanup_plugin_data(priv);
    213     return result;
    214 }
    215 
    216 static int _start_job(wJob_t job_handle, const ifc_wprint_t *wprint_ifc_p,
    217         const ifc_print_job_t *print_ifc_p, wprint_job_params_t *job_params) {
    218     msgQ_msg_t msg;
    219     plugin_data_t *priv = NULL;
    220 
    221     do {
    222         if (job_params == NULL) continue;
    223 
    224         job_params->plugin_data = NULL;
    225         if ((wprint_ifc_p == NULL) || (print_ifc_p == NULL)) continue;
    226 
    227         priv = (plugin_data_t *) malloc(sizeof(plugin_data_t));
    228         if (priv == NULL) continue;
    229 
    230         memset(priv, 0, sizeof(plugin_data_t));
    231 
    232         priv->job_handle = job_handle;
    233         priv->job_params = job_params;
    234         priv->send_tid = pthread_self();
    235         priv->job_info.job_handle = _WJOBH_NONE;
    236         priv->job_info.print_ifc = (ifc_print_job_t *) print_ifc_p;
    237         priv->job_info.wprint_ifc = (ifc_wprint_t *) wprint_ifc_p;
    238         priv->job_info.strip_height = job_params->strip_height;
    239         priv->job_info.useragent = job_params->useragent;
    240 
    241         sem_init(&priv->buffs_sem, 0, MAX_SEND_BUFFS);
    242         switch (job_params->pcl_type) {
    243             case PCLm:
    244                 priv->pcl_ifc = pclm_connect();
    245                 break;
    246             case PCLPWG:
    247                 priv->pcl_ifc = pwg_connect();
    248                 break;
    249             default:
    250                 break;
    251         }
    252 
    253         if (priv->pcl_ifc == NULL) {
    254             LOGE("ERROR: cannot start PCL job, no ifc found");
    255             continue;
    256         }
    257 
    258         priv->msgQ = priv->job_info.wprint_ifc->msgQCreate(
    259                 (MAX_SEND_BUFFS * 2), sizeof(msgQ_msg_t));
    260         if (priv->msgQ == MSG_Q_INVALID_ID) continue;
    261 
    262         if (_start_thread(priv) == ERROR) continue;
    263 
    264         job_params->plugin_data = (void *) priv;
    265         msg.id = MSG_START_JOB;
    266         priv->job_info.wprint_ifc->msgQSend(
    267                 priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
    268 
    269         return OK;
    270     } while (0);
    271 
    272     _cleanup_plugin_data(priv);
    273     return ERROR;
    274 }
    275 
    276 static status_t _print_page(wprint_job_params_t *job_params, const char *mime_type,
    277         const char *pathname) {
    278     wprint_image_info_t *image_info;
    279     FILE *imgfile;
    280     status_t result;
    281     int num_rows, height, image_row;
    282     int blank_data;
    283     char *buff;
    284     int i, buff_index, buff_size;
    285     char *buff_pool[MAX_SEND_BUFFS];
    286 
    287     int nbytes;
    288     plugin_data_t *priv;
    289     msgQ_msg_t msg;
    290     int image_padding = PAD_PRINT;
    291 
    292     if (job_params == NULL) return ERROR;
    293 
    294     priv = (plugin_data_t *) job_params->plugin_data;
    295 
    296     if (priv == NULL) return ERROR;
    297 
    298     switch (job_params->pcl_type) {
    299         case PCLm:
    300         case PCLPWG:
    301             image_padding = PAD_ALL;
    302             break;
    303         default:
    304             break;
    305     }
    306 
    307     if (pathname == NULL) {
    308         LOGE("_print_page(): cannot print file with NULL name");
    309         msg.param.end_page.page = -1;
    310         msg.param.end_page.count = 0;
    311         result = ERROR;
    312     } else if (strlen(pathname)) {
    313         image_info = malloc(sizeof(wprint_image_info_t));
    314         if (image_info == NULL) return ERROR;
    315 
    316         imgfile = fopen(pathname, "r");
    317         if (imgfile) {
    318             LOGD("_print_page(): fopen succeeded on %s", pathname);
    319             wprint_image_setup(image_info, mime_type, priv->job_info.wprint_ifc,
    320                     job_params->pixel_units, job_params->pdf_render_resolution);
    321             wprint_image_init(image_info, pathname, job_params->page_num);
    322 
    323             // get the image_info of the input file of specified MIME type
    324             if ((result = wprint_image_get_info(imgfile, image_info)) == OK) {
    325                 wprint_rotation_t rotation = ROT_0;
    326 
    327                 if ((job_params->render_flags & RENDER_FLAG_PORTRAIT_MODE) != 0) {
    328                     LOGI("_print_page(): portrait mode");
    329                     rotation = ROT_0;
    330                 } else if ((job_params->render_flags & RENDER_FLAG_LANDSCAPE_MODE) != 0) {
    331                     LOGI("_print_page(): landscape mode");
    332                     rotation = ROT_90;
    333                 } else if (wprint_image_is_landscape(image_info) &&
    334                         ((job_params->render_flags & RENDER_FLAG_AUTO_ROTATE) != 0)) {
    335                     LOGI("_print_page(): auto mode");
    336                     rotation = ROT_90;
    337                 }
    338 
    339                 if ((job_params->render_flags & RENDER_FLAG_CENTER_ON_ORIENTATION) != 0) {
    340                     job_params->render_flags &= ~(RENDER_FLAG_CENTER_HORIZONTAL |
    341                             RENDER_FLAG_CENTER_VERTICAL);
    342                     job_params->render_flags |= ((rotation == ROT_0) ? RENDER_FLAG_CENTER_HORIZONTAL
    343                             : RENDER_FLAG_CENTER_VERTICAL);
    344                 }
    345 
    346                 if ((job_params->duplex == DUPLEX_MODE_BOOK) &&
    347                         (job_params->page_backside) &&
    348                         ((job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) != 0) &&
    349                         ((job_params->render_flags & RENDER_FLAG_BACK_PAGE_PREROTATED) == 0)) {
    350                     rotation = ((rotation == ROT_0) ? ROT_180 : ROT_270);
    351                 }
    352                 LOGI("_print_page(): rotation = %d", rotation);
    353 
    354                 wprint_image_set_output_properties(image_info, rotation,
    355                         job_params->printable_area_width, job_params->printable_area_height,
    356                         job_params->print_top_margin, job_params->print_left_margin,
    357                         job_params->print_right_margin, job_params->print_bottom_margin,
    358                         job_params->render_flags, job_params->strip_height, MAX_SEND_BUFFS,
    359                         image_padding);
    360 
    361                 // allocate memory for a stripe of data
    362                 for (i = 0; i < MAX_SEND_BUFFS; i++) {
    363                     buff_pool[i] = NULL;
    364                 }
    365 
    366                 blank_data = MAX_SEND_BUFFS;
    367                 buff_size = wprint_image_get_output_buff_size(image_info);
    368                 for (i = 0; i < MAX_SEND_BUFFS; i++) {
    369                     buff_pool[i] = malloc(buff_size);
    370                     if (buff_pool[i] == NULL) {
    371                         break;
    372                     }
    373                     memset(buff_pool[i], 0xff, buff_size);
    374                 }
    375 
    376                 if (i == MAX_SEND_BUFFS) {
    377                     msg.id = MSG_START_PAGE;
    378                     msg.param.start_page.extra_margin = ((job_params->duplex !=
    379                             DUPLEX_MODE_NONE) &&
    380                             ((job_params->page_num & 0x1) == 0))
    381                             ? job_params->page_bottom_margin : 0.0f;
    382                     msg.param.start_page.width = wprint_image_get_width(image_info);
    383                     msg.param.start_page.height = wprint_image_get_height(image_info);
    384                     priv->job_info.num_components = image_info->num_components;
    385                     priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
    386                             sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
    387 
    388                     msg.id = MSG_SEND;
    389                     msg.param.send.bytes_per_row = BYTES_PER_PIXEL(wprint_image_get_width(
    390                             image_info));
    391 
    392                     // send blank rows for any offset
    393                     buff_index = 0;
    394                     num_rows = wprint_image_get_height(image_info);
    395                     image_row = 0;
    396 
    397                     // decode and render each stripe into PCL3 raster format
    398                     while ((result != ERROR) && (num_rows > 0)) {
    399                         if (priv->pcl_ifc->canCancelMidPage() && job_params->cancelled) {
    400                             break;
    401                         }
    402                         sem_wait(&priv->buffs_sem);
    403 
    404                         buff = buff_pool[buff_index];
    405                         buff_index = ((buff_index + 1) % MAX_SEND_BUFFS);
    406 
    407                         height = MIN(num_rows, job_params->strip_height);
    408                         if (!job_params->cancelled) {
    409                             nbytes = wprint_image_decode_stripe(image_info, image_row, &height,
    410                                     (unsigned char *) buff);
    411 
    412                             if (blank_data > 0) {
    413                                 blank_data--;
    414                             }
    415                         } else if (blank_data < MAX_SEND_BUFFS) {
    416                             nbytes = buff_size;
    417                             memset(buff, 0xff, buff_size);
    418                             blank_data++;
    419                         }
    420 
    421                         if (nbytes > 0) {
    422                             msg.param.send.buffer = buff;
    423                             msg.param.send.start_row = image_row;
    424                             msg.param.send.num_rows = height;
    425 
    426                             result = priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
    427                                     sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
    428                             if (result < 0) {
    429                                 sem_post(&priv->buffs_sem);
    430                             }
    431 
    432                             image_row += height;
    433                             num_rows -= height;
    434                         } else {
    435                             sem_post(&priv->buffs_sem);
    436                             if (nbytes < 0) {
    437                                 LOGE("_print_page(): ERROR: file appears to be corrupted");
    438                                 result = CORRUPT;
    439                             }
    440                             break;
    441                         }
    442                     }
    443 
    444                     if ((result == OK) && job_params->cancelled) {
    445                         result = CANCELLED;
    446                     }
    447 
    448                     LOGI("_print_page(): sends done, result: %d", result);
    449 
    450                     // free the buffer and eject the page
    451                     msg.param.end_page.page = job_params->page_num;
    452                     LOGI("_print_page(): processed %d out of"
    453                             " %d rows of page # %d from %s to printer %s %s {%s}",
    454                             image_row, wprint_image_get_height(image_info),
    455                             job_params->page_num, pathname,
    456                             (job_params->last_page) ? "- last page" : "- ",
    457                             (job_params->cancelled) ? "- job cancelled"
    458                                     : ".",
    459                             (result == OK) ? "OK" : "ERROR");
    460                 } else {
    461                     msg.param.end_page.page = -1;
    462                     result = ERROR;
    463                     LOGE("_print_page(): plugin_pcl cannot allocate memory for image stripe");
    464                 }
    465                 for (i = 0; i < MAX_SEND_BUFFS; i++) {
    466                     msg.param.end_page.buffers[i] = buff_pool[i];
    467                 }
    468                 msg.param.end_page.count = MAX_SEND_BUFFS;
    469             } else {
    470                 msg.param.end_page.page = -1;
    471                 msg.param.end_page.count = 0;
    472                 result = CORRUPT;
    473                 LOGE("_print_page(): file does not appear to be valid");
    474             }
    475 
    476             // send the end page message
    477             wprint_image_cleanup(image_info);
    478             fclose(imgfile);
    479         } else {
    480             msg.param.end_page.page = -1;
    481             msg.param.end_page.count = 0;
    482             LOGE("_print_page(): could not open %s", pathname);
    483             result = CORRUPT;
    484         }
    485         free(image_info);
    486     } else {
    487         LOGE("_print_page(): ERROR: filename was empty");
    488         msg.param.end_page.page = -1;
    489         msg.param.end_page.count = 0;
    490         result = ERROR;
    491     }
    492 
    493     msg.id = MSG_END_PAGE;
    494     priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT,
    495             MSG_Q_FIFO);
    496     return result;
    497 }
    498 
    499 /*
    500  * Prints a blank page
    501  */
    502 static int _print_blank_page(wJob_t job_handle, wprint_job_params_t *job_params) {
    503     msgQ_msg_t msg;
    504     plugin_data_t *priv;
    505 
    506     if (job_params == NULL) return ERROR;
    507 
    508     priv = (plugin_data_t *) job_params->plugin_data;
    509     if (priv == NULL) return ERROR;
    510 
    511     msg.id = MSG_END_PAGE;
    512     msg.param.end_page.page = -1;
    513     msg.param.end_page.count = 0;
    514     priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT,
    515             MSG_Q_FIFO);
    516     return OK;
    517 }
    518 
    519 static int _end_job(wprint_job_params_t *job_params) {
    520     if (job_params != NULL) {
    521         _stop_thread((plugin_data_t *) job_params->plugin_data);
    522     }
    523     return OK;
    524 }
    525 
    526 wprint_plugin_t *libwprintplugin_pcl_reg(void) {
    527     static const wprint_plugin_t _pcl_plugin = {.version = WPRINT_PLUGIN_VERSION(0),
    528             .priority = PRIORITY_LOCAL, .get_mime_types = _get_mime_types,
    529             .get_print_formats = _get_print_formats, .start_job = _start_job,
    530             .print_page = _print_page, .print_blank_page = _print_blank_page, .end_job = _end_job,};
    531     return ((wprint_plugin_t *) &_pcl_plugin);
    532 }