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