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