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 }