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 
_get_mime_types(void)91 static const char **_get_mime_types(void) {
92     return _mime_types;
93 }
94 
_get_print_formats(void)95 static const char **_get_print_formats(void) {
96     return _print_formats;
97 }
98 
_cleanup_plugin_data(plugin_data_t * priv)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  */
_send_thread(void * param)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  */
_start_thread(plugin_data_t * param)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  */
_stop_thread(plugin_data_t * priv)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 
_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)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 
_print_page(wprint_job_params_t * job_params,const char * mime_type,const char * pathname)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  */
_print_blank_page(wJob_t job_handle,wprint_job_params_t * job_params)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 
_end_job(wprint_job_params_t * job_params)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 
libwprintplugin_pcl_reg(void)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 }