• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 cups_raster_t *ras_out = NULL;
33 cups_page_header2_t header_pwg;
34 
35 /*
36  * Write the PWG header
37  */
_write_header_pwg(int pixel_width,int pixel_height,cups_page_header2_t * h,bool monochrome)38 static 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  */
_get_pwg_media_size(pcl_job_info_t * job_info,media_size_t media_size,PCLmPageSetup * myPageInfo)100 static 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  */
_pwg_io_write(void * ctx,unsigned char * buf,size_t bytes)137 static 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 
_start_job(wJob_t job_handle,pcl_job_info_t * job_info,media_size_t media_size,media_type_t media_type,int resolution,duplex_t duplex,duplex_dry_time_t dry_time,color_space_t color_space,media_tray_t media_tray,float top_margin,float left_margin)143 static 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 
_start_page(pcl_job_info_t * job_info,int pixel_width,int pixel_height)237 static 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 
_print_swath(pcl_job_info_t * job_info,char * rgb_pixels,int start_row,int num_rows,int bytes_per_row)281 static 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  */
_generate_blank_data(int pixel_width,int pixel_height,uint8 monochrome,size_t * buffer_size)320 unsigned 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 
_end_page(pcl_job_info_t * job_info,int page_number)366 static 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 
_end_job(pcl_job_info_t * job_info)387 static 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 
_canCancelMidPage(void)394 static bool _canCancelMidPage(void) {
395     return false;
396 }
397 
398 static const ifc_pcl_t _pcl_ifc = {
399         _start_job, _end_job, _start_page, _end_page, _print_swath, _canCancelMidPage
400 };
401 
pwg_connect(void)402 ifc_pcl_t *pwg_connect(void) {
403     return ((ifc_pcl_t *) &_pcl_ifc);
404 }