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 }