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 <sys/types.h> 20#include <stdio.h> 21#include <stdlib.h> 22#include <math.h> 23#include <cups/raster.h> 24 25#include "lib_pcl.h" 26#include "wprint_image.h" 27 28#include "media.h" 29 30#define TAG "lib_pwg" 31#define STANDARD_SCALE_FOR_PDF 72.0 32#define _MI_TO_PIXELS(n, res) ((n)*(res)+500)/1000.0 33#define _MI_TO_POINTS(n) _MI_TO_PIXELS(n, STANDARD_SCALE_FOR_PDF) 34 35cups_raster_t *ras_out = NULL; 36cups_page_header2_t header_pwg; 37 38/* 39 * Write the PWG header 40 */ 41static void _write_header_pwg(int pixel_width, int pixel_height, cups_page_header2_t *h, 42 bool monochrome) { 43 if (h != NULL) { 44 strcpy(h->MediaClass, "PwgRaster"); 45 strcpy(h->MediaColor, ""); 46 strcpy(h->MediaType, ""); 47 strcpy(h->OutputType, ""); 48 h->AdvanceDistance = 0; 49 h->AdvanceMedia = CUPS_ADVANCE_FILE; 50 h->Collate = CUPS_FALSE; 51 h->CutMedia = CUPS_CUT_NONE; 52 h->cupsPageSize[0] = (float) ((pixel_width * STANDARD_SCALE_FOR_PDF) / h->HWResolution[0]); 53 h->cupsPageSize[1] = (float) ((pixel_height * STANDARD_SCALE_FOR_PDF) / h->HWResolution[1]); 54 55 h->ImagingBoundingBox[0] = 0; 56 h->ImagingBoundingBox[1] = 0; 57 h->ImagingBoundingBox[2] = h->cupsPageSize[0]; 58 h->ImagingBoundingBox[3] = h->cupsPageSize[1]; 59 h->cupsBorderlessScalingFactor = 1.0; 60 h->InsertSheet = CUPS_FALSE; 61 h->Jog = CUPS_JOG_NONE; 62 h->LeadingEdge = CUPS_EDGE_TOP; 63 h->Margins[0] = 0; 64 h->Margins[1] = 0; 65 h->ManualFeed = CUPS_TRUE; 66 h->MediaPosition = 0; 67 h->MediaWeight = 0; 68 h->MirrorPrint = CUPS_FALSE; 69 h->NegativePrint = CUPS_FALSE; 70 h->NumCopies = 1; 71 h->Orientation = CUPS_ORIENT_0; 72 h->PageSize[0] = (int) h->cupsPageSize[0]; 73 h->PageSize[1] = (int) h->cupsPageSize[1]; 74 h->Separations = CUPS_TRUE; 75 h->TraySwitch = CUPS_TRUE; 76 h->Tumble = CUPS_TRUE; 77 h->cupsWidth = pixel_width; 78 h->cupsHeight = pixel_height; 79 h->cupsBitsPerPixel = (monochrome ? 8 : 24); 80 h->cupsBitsPerColor = 8; 81 h->cupsColorSpace = (monochrome ? CUPS_CSPACE_SW : CUPS_CSPACE_SRGB); 82 h->cupsBytesPerLine = (h->cupsBitsPerPixel * pixel_width + 7) / 8; 83 h->cupsColorOrder = CUPS_ORDER_CHUNKED; 84 h->cupsCompression = 0; 85 h->cupsRowCount = 1; 86 h->cupsRowFeed = 1; 87 h->cupsRowStep = 1; 88 h->cupsNumColors = 0; 89 h->cupsImagingBBox[0] = 0.0; 90 h->cupsImagingBBox[1] = 0.0; 91 h->cupsImagingBBox[2] = 0.0; 92 h->cupsImagingBBox[3] = 0.0; 93 94 strcpy(h->cupsMarkerType, "Marker Type"); 95 strcpy(h->cupsRenderingIntent, "Rendering Intent"); 96 strcpy(h->cupsPageSizeName, "Letter"); 97 } 98} 99 100/* 101 * Store the supplied media size into job_info 102 */ 103static void _get_pwg_media_size(pcl_job_info_t *job_info, media_size_t media_size, 104 PCLmPageSetup *myPageInfo) { 105 int i = 0; 106 do { 107 if (myPageInfo == NULL) { 108 continue; 109 } 110 111 for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { 112 if (media_size == SupportedMediaSizes[i].media_size) { 113 strncpy(myPageInfo->mediaSizeName, SupportedMediaSizes[i].PCL6Name, 114 sizeof(myPageInfo->mediaSizeName) - 1); 115 116 myPageInfo->mediaWidth = floorf( 117 _MI_TO_POINTS(SupportedMediaSizes[i].WidthInInches)); 118 myPageInfo->mediaHeight = floorf( 119 _MI_TO_POINTS(SupportedMediaSizes[i].HeightInInches)); 120 121 LOGD(" _get_pwg_media_size(): match found: %d, %s, width=%f, height=%f", 122 media_size, SupportedMediaSizes[i].PCL6Name, myPageInfo->mediaWidth, 123 myPageInfo->mediaHeight); 124 break; // we found a match, so break out of loop 125 } 126 } 127 } 128 while (0); 129 130 if (i == SUPPORTED_MEDIA_SIZE_COUNT) { 131 // media size not found, defaulting to letter 132 LOGD("_get_pwg_media_size(): media size, %d, NOT FOUND, setting to letter", media_size); 133 _get_pwg_media_size(job_info, US_LETTER, myPageInfo); 134 } 135} 136 137/* 138 * Write a buffer to the output stream 139 */ 140static ssize_t _pwg_io_write(void *ctx, unsigned char *buf, size_t bytes) { 141 pcl_job_info_t *pwg_job_info = (pcl_job_info_t *) ctx; 142 _WRITE(pwg_job_info, (const char *) buf, bytes); 143 return bytes; 144} 145 146static wJob_t _start_job(wJob_t job_handle, pcl_job_info_t *job_info, media_size_t media_size, 147 media_type_t media_type, int resolution, duplex_t duplex, duplex_dry_time_t dry_time, 148 color_space_t color_space, media_tray_t media_tray, float top_margin, 149 float left_margin) { 150 if (job_info == NULL) { 151 return _WJOBH_NONE; 152 } 153 154 if (job_info->job_handle != _WJOBH_NONE) { 155 if (job_info->wprint_ifc != NULL) { 156 LOGE("_start_job() required cleanup"); 157 } 158 159 job_info->job_handle = _WJOBH_NONE; 160 } 161 162 if ((job_info->wprint_ifc == NULL) || (job_info->print_ifc == NULL)) { 163 return _WJOBH_NONE; 164 } 165 166 LOGD("_start_job(), media_size %d, media_type %d, dt %d, %s, media_tray %d", media_size, 167 media_type, dry_time, (duplex == DUPLEX_MODE_NONE) ? "simplex" : "duplex", 168 media_tray); 169 job_info->job_handle = job_handle; 170 171 _START_JOB(job_info, "pwg"); 172 173 header_pwg.HWResolution[0] = resolution; 174 header_pwg.HWResolution[1] = resolution; 175 176 job_info->resolution = resolution; 177 job_info->media_size = media_size; 178 job_info->standard_scale = (float) resolution / (float) 72; 179 180 // initialize static variables 181 job_info->pclm_output_buffer = NULL; 182 job_info->seed_row = job_info->pcl_buff = NULL; // unused 183 job_info->pixel_width = job_info->pixel_height = job_info->page_number = job_info->num_rows = 0; 184 185 memset((void *) &job_info->pclm_page_info, 0x0, sizeof(PCLmPageSetup)); 186 _get_pwg_media_size(job_info, media_size, &job_info->pclm_page_info); 187 188 if (left_margin < 0.0f || top_margin < 0.0f) { 189 job_info->pclm_page_info.mediaWidthOffset = 0.0f; 190 job_info->pclm_page_info.mediaHeightOffset = 0.0f; 191 } else { 192 job_info->pclm_page_info.mediaWidthOffset = left_margin; 193 job_info->pclm_page_info.mediaHeightOffset = top_margin; 194 } 195 196 header_pwg.cupsMediaType = media_size; 197 198 job_info->pclm_page_info.pageOrigin = top_left; // REVISIT 199 job_info->monochrome = (color_space == COLOR_SPACE_MONO); 200 job_info->pclm_page_info.dstColorSpaceSpefication = deviceRGB; 201 if (color_space == COLOR_SPACE_MONO) { 202 header_pwg.cupsColorSpace = CUPS_CSPACE_SW; 203 job_info->pclm_page_info.dstColorSpaceSpefication = deviceRGB; 204 } else if (color_space == COLOR_SPACE_COLOR) { 205 job_info->pclm_page_info.dstColorSpaceSpefication = deviceRGB; 206 header_pwg.cupsColorSpace = CUPS_CSPACE_SRGB; 207 } else if (color_space == COLOR_SPACE_ADOBE_RGB) { 208 job_info->pclm_page_info.dstColorSpaceSpefication = adobeRGB; 209 header_pwg.cupsColorSpace = CUPS_CSPACE_SRGB; 210 } 211 212 job_info->pclm_page_info.stripHeight = job_info->strip_height; 213 job_info->pclm_page_info.destinationResolution = res600; 214 if (resolution == 300) { 215 job_info->pclm_page_info.destinationResolution = res300; 216 } else if (resolution == 600) { 217 job_info->pclm_page_info.destinationResolution = res600; 218 } else if (resolution == 1200) { 219 job_info->pclm_page_info.destinationResolution = res1200; 220 } 221 222 if (duplex == DUPLEX_MODE_BOOK) { 223 job_info->pclm_page_info.duplexDisposition = duplex_longEdge; 224 header_pwg.Duplex = CUPS_TRUE; 225 } else if (duplex == DUPLEX_MODE_TABLET) { 226 job_info->pclm_page_info.duplexDisposition = duplex_shortEdge; 227 header_pwg.Duplex = CUPS_TRUE; 228 } else { 229 job_info->pclm_page_info.duplexDisposition = simplex; 230 header_pwg.Duplex = CUPS_FALSE; 231 } 232 233 job_info->pclm_page_info.mirrorBackside = false; 234 header_pwg.OutputFaceUp = CUPS_FALSE; 235 header_pwg.cupsBitsPerColor = BITS_PER_CHANNEL; 236 ras_out = cupsRasterOpenIO(_pwg_io_write, (void *) job_info, CUPS_RASTER_WRITE_PWG); 237 return job_info->job_handle; 238} 239 240static int _start_page(pcl_job_info_t *job_info, int pixel_width, int pixel_height) { 241 PCLmPageSetup *page_info = &job_info->pclm_page_info; 242 _START_PAGE(job_info, pixel_width, pixel_height); 243 244 page_info->sourceHeight = (float) pixel_height / job_info->standard_scale; 245 page_info->sourceWidth = (float) pixel_width / job_info->standard_scale; 246 LOGI("_start_page(), strip height=%d, image width=%d, image height=%d, scaled width=%f, " 247 "scaled height=%f", page_info->stripHeight, pixel_width, pixel_height, 248 page_info->sourceWidth, page_info->sourceHeight); 249 if (job_info->num_components == 3) { 250 page_info->colorContent = color_content; 251 page_info->srcColorSpaceSpefication = deviceRGB; 252 } else { 253 page_info->colorContent = gray_content; 254 page_info->srcColorSpaceSpefication = grayScale; 255 } 256 page_info->colorContent = color_content; 257 page_info->srcColorSpaceSpefication = deviceRGB; 258 259 // REVISIT: possibly get this value dynamically from device via IPP (ePCL) 260 // however, current ink devices report RLE as the default compression type, which compresses 261 // much worse than JPEG or FLATE 262 page_info->compTypeRequested = compressDCT; 263 264 job_info->scan_line_width = BYTES_PER_PIXEL(pixel_width); 265 266 // Fill up the pwg header 267 _write_header_pwg(pixel_width, pixel_height, &header_pwg, job_info->monochrome); 268 269 LOGI("cupsWidth = %d", header_pwg.cupsWidth); 270 LOGI("cupsHeight = %d", header_pwg.cupsHeight); 271 LOGI("cupsPageWidth = %f", header_pwg.cupsPageSize[0]); 272 LOGI("cupsPageHeight = %f", header_pwg.cupsPageSize[1]); 273 LOGI("cupsBitsPerColor = %d", header_pwg.cupsBitsPerColor); 274 LOGI("cupsBitsPerPixel = %d", header_pwg.cupsBitsPerPixel); 275 LOGI("cupsBytesPerLine = %d", header_pwg.cupsBytesPerLine); 276 LOGI("cupsColorOrder = %d", header_pwg.cupsColorOrder); 277 LOGI("cupsColorSpace = %d", header_pwg.cupsColorSpace); 278 279 cupsRasterWriteHeader2(ras_out, &header_pwg); 280 job_info->page_number++; 281 return job_info->page_number; 282} 283 284static int _print_swath(pcl_job_info_t *job_info, char *rgb_pixels, int start_row, int num_rows, 285 int bytes_per_row) { 286 int outBuffSize; 287 _PAGE_DATA(job_info, (const unsigned char *) rgb_pixels, (num_rows * bytes_per_row)); 288 289 if (job_info->monochrome) { 290 unsigned char *buff = (unsigned char *) rgb_pixels; 291 int nbytes = (num_rows * bytes_per_row); 292 int readIndex, writeIndex; 293 for (readIndex = writeIndex = 0; readIndex < nbytes; readIndex += BYTES_PER_PIXEL(1)) { 294 unsigned char gray = SP_GRAY(buff[readIndex + 0], buff[readIndex + 1], 295 buff[readIndex + 2]); 296 buff[writeIndex++] = gray; 297 } 298 outBuffSize = writeIndex; 299 } else { 300 outBuffSize = num_rows * bytes_per_row; 301 } 302 303 LOGD("_print_swath(): page #%d, buffSize=%d, rows %d - %d (%d rows), bytes per row %d", 304 job_info->page_number, job_info->strip_height * job_info->scan_line_width, 305 start_row, start_row + num_rows - 1, num_rows, bytes_per_row); 306 /* If the inBufferSize is ever used in genPCLm, change the input parameter to pass in 307 * image_info->printable_width*num_components*strip_height. it is currently pixel_width 308 * (from _start_page()) * num_components * strip_height 309 */ 310 if (ras_out != NULL) { 311 unsigned result = cupsRasterWritePixels(ras_out, (unsigned char *) rgb_pixels, outBuffSize); 312 LOGD("cupsRasterWritePixels return %d", result); 313 } else { 314 LOGD("cupsRasterWritePixels raster is null"); 315 } 316 return OK; 317} 318 319/* 320 * Allocate and fill a blank page of PackBits data. Writes size into buffer_size. The buffer 321 * must be free'd by the caller. 322 */ 323unsigned char *_generate_blank_data(int pixel_width, int pixel_height, uint8 monochrome, size_t *buffer_size) { 324 if (pixel_width == 0 || pixel_height == 0) return NULL; 325 326 /* PWG Raster's PackBits-like algorithm allows for a maximum of: 327 * 256 repeating rows and is encoded using a single octet containing (count - 1) 328 * 128 repeating color value and is run length encoded using a single octet containing (count - 1) 329 */ 330 int rows_full = pixel_height / 256; 331 int columns_full = pixel_width / 128; 332 int row_fraction = ((pixel_height % 256) != 0) ? 1 : 0; 333 int column_fraction = ((pixel_width % 128) != 0) ? 1 : 0; 334 int column_data_size = 1 + (columns_full + column_fraction) * (monochrome ? 2 : 4); 335 336 *buffer_size = (size_t) ((rows_full + row_fraction) * column_data_size); 337 unsigned char *buffer = (unsigned char *) malloc(*buffer_size); 338 if (buffer == NULL) return NULL; 339 340 int i = 0; 341 for (int y = 0; y < rows_full + row_fraction; y++) { 342 // Add row-repeat command 343 if (y < rows_full) { 344 buffer[i++] = 0xFF; 345 } else { 346 buffer[i++] = (unsigned char) ((pixel_height % 256) - 1); 347 } 348 349 for (int x = 0; x < columns_full + column_fraction; x++) { 350 // Add column-repeat command 351 if (x < columns_full) { 352 buffer[i++] = 0x7F; 353 } else { 354 buffer[i++] = (unsigned char) ((pixel_width % 128) - 1); 355 } 356 357 // Pixel color to repeat 358 buffer[i++] = 0xFF; 359 if (!monochrome) { 360 // Add rest of RGB for color output 361 buffer[i++] = 0xFF; 362 buffer[i++] = 0xFF; 363 } 364 } 365 } 366 return buffer; 367} 368 369static int _end_page(pcl_job_info_t *job_info, int page_number) { 370 if (page_number == -1) { 371 LOGD("lib_pclm: _end_page(): writing blank page"); 372 373 size_t buffer_size; 374 unsigned char *buffer; 375 _start_page(job_info, header_pwg.cupsWidth, header_pwg.cupsHeight); 376 buffer = _generate_blank_data(header_pwg.cupsWidth, header_pwg.cupsHeight, job_info->monochrome, &buffer_size); 377 if (buffer == NULL) { 378 return ERROR; 379 } else { 380 _pwg_io_write(job_info, buffer, buffer_size); 381 free(buffer); 382 } 383 } 384 LOGI("lib_pcwg: _end_page()"); 385 _END_PAGE(job_info); 386 387 return OK; 388} 389 390static int _end_job(pcl_job_info_t *job_info) { 391 LOGI("_end_job()"); 392 _END_JOB(job_info); 393 cupsRasterClose(ras_out); 394 return OK; 395} 396 397static bool _canCancelMidPage(void) { 398 return false; 399} 400 401static const ifc_pcl_t _pcl_ifc = { 402 _start_job, _end_job, _start_page, _end_page, _print_swath, _canCancelMidPage 403}; 404 405ifc_pcl_t *pwg_connect(void) { 406 return ((ifc_pcl_t *) &_pcl_ifc); 407}