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 <string.h>
23 #include <math.h>
24 
25 #include "lib_pcl.h"
26 #include "wprint_image.h"
27 
28 #include "pclm_wrapper_api.h"
29 
30 #include "media.h"
31 #include "wprint_debug.h"
32 
33 #define TAG "lib_pclm"
34 
35 /*
36  * Store a valid media_size name into media_name
37  */
_get_pclm_media_size_name(pcl_job_info_t * job_info,media_size_t media_size,char * media_name)38 static void _get_pclm_media_size_name(pcl_job_info_t *job_info, media_size_t media_size,
39         char *media_name) {
40     int i = 0;
41     for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) {
42         if (media_size == SupportedMediaSizes[i].media_size) {
43             strncpy(media_name, SupportedMediaSizes[i].PCL6Name,
44                     strlen(SupportedMediaSizes[i].PCL6Name));
45             LOGD("_get_pclm_media_size_name(): match found: %d, %s", media_size, media_name);
46             break;  // we found a match, so break out of loop
47         }
48     }
49 
50     if (i == SUPPORTED_MEDIA_SIZE_COUNT) {
51         // media size not found, defaulting to letter
52         LOGD("_get_pclm_media_size_name(): media size, %d, NOT FOUND, setting to letter",
53                 media_size);
54         _get_pclm_media_size_name(job_info, US_LETTER, media_name);
55     }
56 }
57 
58 /*
59  * Write a valid media_size into myPageInfo
60  */
_get_pclm_media_size(pcl_job_info_t * job_info,media_size_t media_size,PCLmPageSetup * myPageInfo)61 static void _get_pclm_media_size(pcl_job_info_t *job_info, media_size_t media_size,
62         PCLmPageSetup *myPageInfo) {
63     int i = SUPPORTED_MEDIA_SIZE_COUNT;
64 
65     if (myPageInfo != NULL) {
66         for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) {
67             if (media_size == SupportedMediaSizes[i].media_size) {
68                 strncpy(myPageInfo->mediaSizeName, SupportedMediaSizes[i].PCL6Name,
69                         sizeof(myPageInfo->mediaSizeName) - 1);
70 
71                 myPageInfo->mediaWidth = floorf(
72                         _MI_TO_POINTS(SupportedMediaSizes[i].WidthInInches));
73                 myPageInfo->mediaHeight = floorf(
74                         _MI_TO_POINTS(SupportedMediaSizes[i].HeightInInches));
75 
76                 LOGD("_get_pclm_media_size(): match found: %d, %s, width=%f, height=%f",
77                         media_size, SupportedMediaSizes[i].PCL6Name, myPageInfo->mediaWidth,
78                         myPageInfo->mediaHeight);
79                 break;  // we found a match, so break out of loop
80             }
81         }
82     }
83 
84     if (i == SUPPORTED_MEDIA_SIZE_COUNT) {
85         // media size not found, defaulting to letter
86         LOGD("_get_pclm_media_size(): media size, %d, NOT FOUND, setting to letter", media_size);
87         _get_pclm_media_size(job_info, US_LETTER, myPageInfo);
88     }
89 }
90 
_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)91 static wJob_t _start_job(wJob_t job_handle, pcl_job_info_t *job_info, media_size_t media_size,
92         media_type_t media_type, int resolution, duplex_t duplex, duplex_dry_time_t dry_time,
93         color_space_t color_space, media_tray_t media_tray, float top_margin,
94         float left_margin) {
95     int outBuffSize = 0;
96 
97     if (job_info == NULL) {
98         return _WJOBH_NONE;
99     }
100 
101     if (job_info->job_handle != _WJOBH_NONE) {
102         if (job_info->wprint_ifc != NULL) {
103             LOGD("_start_job() required cleanup");
104         }
105         job_info->job_handle = _WJOBH_NONE;
106     }
107 
108     if ((job_info->wprint_ifc == NULL) || (job_info->print_ifc == NULL)) {
109         return _WJOBH_NONE;
110     }
111 
112     LOGD("_start_job(), media_size %d, media_type %d, dt %d, %s, media_tray %d margins T %f L %f",
113             media_size, media_type, dry_time,
114             (duplex == DUPLEX_MODE_NONE) ? "simplex" : "duplex",
115             media_tray, top_margin, left_margin);
116 
117     job_info->job_handle = job_handle;
118 
119     _START_JOB(job_info, "pdf");
120 
121     job_info->resolution = resolution;
122     job_info->media_size = media_size;
123     job_info->standard_scale = (float) resolution / (float) STANDARD_SCALE_FOR_PDF;
124 
125     //  initialize static variables
126     job_info->pclm_output_buffer = NULL;
127     job_info->seed_row = job_info->pcl_buff = NULL;    // unused
128     job_info->pixel_width = job_info->pixel_height = job_info->page_number = job_info->num_rows = 0;
129 
130     memset((void *) &job_info->pclm_page_info, 0x0, sizeof(PCLmPageSetup));
131     _get_pclm_media_size_name(job_info, media_size, &job_info->pclm_page_info.mediaSizeName[0]);
132 
133     if ((left_margin < 0.0f) || (top_margin < 0.0f)) {
134         job_info->pclm_page_info.mediaWidthOffset = 0.0f;
135         job_info->pclm_page_info.mediaHeightOffset = 0.0f;
136     } else {
137         job_info->pclm_page_info.mediaWidthOffset = (left_margin * (float) STANDARD_SCALE_FOR_PDF);
138         job_info->pclm_page_info.mediaHeightOffset = (top_margin * (float) STANDARD_SCALE_FOR_PDF);
139     }
140 
141     LOGI("_start_job(), mediaHeightOffsets W %f H %f", job_info->pclm_page_info.mediaWidthOffset,
142             job_info->pclm_page_info.mediaHeightOffset);
143 
144     job_info->pclm_page_info.pageOrigin = top_left;    // REVISIT
145 
146     job_info->monochrome = (color_space == COLOR_SPACE_MONO);
147     job_info->pclm_page_info.dstColorSpaceSpefication = deviceRGB;
148     if (color_space == COLOR_SPACE_MONO) {
149         job_info->pclm_page_info.dstColorSpaceSpefication = grayScale;
150     } else if (color_space == COLOR_SPACE_COLOR) {
151         job_info->pclm_page_info.dstColorSpaceSpefication = deviceRGB;
152     } else if (color_space == COLOR_SPACE_ADOBE_RGB) {
153         job_info->pclm_page_info.dstColorSpaceSpefication = adobeRGB;
154     }
155 
156     job_info->pclm_page_info.stripHeight = job_info->strip_height;
157     job_info->pclm_page_info.destinationResolution = res600;
158     if (resolution == 300) {
159         job_info->pclm_page_info.destinationResolution = res300;
160     } else if (resolution == 600) {
161         job_info->pclm_page_info.destinationResolution = res600;
162     } else if (resolution == 1200) {
163         job_info->pclm_page_info.destinationResolution = res1200;
164     }
165 
166     if (duplex == DUPLEX_MODE_BOOK) {
167         job_info->pclm_page_info.duplexDisposition = duplex_longEdge;
168     } else if (duplex == DUPLEX_MODE_TABLET) {
169         job_info->pclm_page_info.duplexDisposition = duplex_shortEdge;
170     } else {
171         job_info->pclm_page_info.duplexDisposition = simplex;
172     }
173 
174     job_info->pclm_page_info.mirrorBackside = false;
175     job_info->pclmgen_obj = CreatePCLmGen();
176     PCLmStartJob(job_info->pclmgen_obj, (void **) &job_info->pclm_output_buffer, &outBuffSize);
177     _WRITE(job_info, (const char *) job_info->pclm_output_buffer, outBuffSize);
178     return job_info->job_handle;
179 }
180 
_start_page(pcl_job_info_t * job_info,int pixel_width,int pixel_height)181 static int _start_page(pcl_job_info_t *job_info, int pixel_width, int pixel_height) {
182     PCLmPageSetup *page_info = &job_info->pclm_page_info;
183     ubyte *pclm_output_buff = job_info->pclm_output_buffer;
184     int outBuffSize = 0;
185 
186     _START_PAGE(job_info, pixel_width, pixel_height);
187     job_info->pixel_width = pixel_width;
188 
189     page_info->sourceHeight = (float) pixel_height / job_info->standard_scale;
190     page_info->sourceWidth = (float) pixel_width / job_info->standard_scale;
191     LOGI("_start_page(), strip height=%d, image width=%d, image height=%d, scaled width=%f, "
192             "scaled height=%f", page_info->stripHeight, pixel_width, pixel_height,
193             page_info->sourceWidth, page_info->sourceHeight);
194 
195     if (job_info->num_components == 3) {
196         page_info->colorContent = color_content;
197         page_info->srcColorSpaceSpefication = deviceRGB;
198     } else {
199         page_info->colorContent = gray_content;
200         page_info->srcColorSpaceSpefication = grayScale;
201     }
202 
203     /* Note: we could possibly get this value dynamically from device via IPP (ePCL) however,
204      * current ink devices report RLE as the default compression type, which compresses much
205      * worse than JPEG or FLATE
206      */
207     page_info->compTypeRequested = compressDCT;
208     job_info->scan_line_width = pixel_width * job_info->num_components;
209     int res1 = PCLmGetMediaDimensions(job_info->pclmgen_obj, page_info->mediaSizeName, page_info);
210     page_info->SourceWidthPixels = MIN(pixel_width, job_info->pclm_page_info.mediaWidthInPixels);
211     page_info->SourceHeightPixels = pixel_height;
212     job_info->pclm_scan_line_width =
213             job_info->pclm_page_info.mediaWidthInPixels * job_info->num_components;
214 
215     LOGD("PCLmGetMediaDimensions(%d), mediaSizeName=%s, mediaWidth=%f, mediaHeight=%f, "
216             "widthPixels=%d, heightPixels=%d", res1, job_info->pclm_page_info.mediaSizeName,
217             job_info->pclm_page_info.mediaWidth, job_info->pclm_page_info.mediaHeight,
218             job_info->pclm_page_info.mediaWidthInPixels,
219             job_info->pclm_page_info.mediaHeightInPixels);
220 
221     PCLmStartPage(job_info->pclmgen_obj, page_info, (void **) &pclm_output_buff, &outBuffSize);
222     _WRITE(job_info, (const char *) pclm_output_buff, outBuffSize);
223 
224     job_info->page_number++;
225     return job_info->page_number;
226 }
227 
_print_swath(pcl_job_info_t * job_info,char * rgb_pixels,int start_row,int num_rows,int bytes_per_row)228 static int _print_swath(pcl_job_info_t *job_info, char *rgb_pixels, int start_row, int num_rows,
229         int bytes_per_row) {
230     int outBuffSize = 0;
231     _PAGE_DATA(job_info, (const unsigned char *) rgb_pixels, (num_rows * bytes_per_row));
232 
233     if (job_info->monochrome) {
234         unsigned char *buff = (unsigned char *) rgb_pixels;
235         int nbytes = (num_rows * bytes_per_row);
236         int readIndex, writeIndex;
237         for (readIndex = writeIndex = 0; readIndex < nbytes; readIndex += BYTES_PER_PIXEL(1)) {
238             unsigned char gray = SP_GRAY(buff[readIndex + 0], buff[readIndex + 1],
239                     buff[readIndex + 2]);
240             buff[writeIndex++] = gray;
241             buff[writeIndex++] = gray;
242             buff[writeIndex++] = gray;
243         }
244     }
245 
246     LOGD("_print_swath(): page #%d, buffSize=%d, rows %d - %d (%d rows), bytes per row %d",
247             job_info->page_number, job_info->strip_height * job_info->scan_line_width, start_row,
248             start_row + num_rows - 1, num_rows, bytes_per_row);
249 
250     if (job_info->scan_line_width > job_info->pclm_scan_line_width) {
251         int i;
252         char *src_pixels = rgb_pixels + job_info->scan_line_width;
253         char *dest_pixels = rgb_pixels + job_info->pclm_scan_line_width;
254         for (i = 1; i < num_rows; i++, src_pixels += job_info->scan_line_width,
255                 dest_pixels += job_info->pclm_scan_line_width) {
256             memmove(dest_pixels, src_pixels, job_info->pclm_scan_line_width);
257         }
258     }
259 
260     /* if the inBufferSize is ever used in genPCLm, change the input parameter to pass in
261      * image_info->printable_width*num_components*strip_height
262      * it is currently pixel_width (from _start_page()) * num_components * strip_height
263      */
264     PCLmEncapsulate(job_info->pclmgen_obj, rgb_pixels,
265             job_info->strip_height * MIN(job_info->scan_line_width, job_info->pclm_scan_line_width),
266             num_rows, (void **) &job_info->pclm_output_buffer, &outBuffSize);
267     _WRITE(job_info, (const char *) job_info->pclm_output_buffer, outBuffSize);
268 
269     return OK;
270 }
271 
_end_page(pcl_job_info_t * job_info,int page_number)272 static int _end_page(pcl_job_info_t *job_info, int page_number) {
273     int outBuffSize = 0;
274 
275     if (page_number == -1) {
276         LOGI("_end_page(): writing blank page");
277         _start_page(job_info, job_info->pixel_width, job_info->strip_height);
278         size_t blank_data_size = (size_t) job_info->strip_height * job_info->pixel_width *
279                 job_info->num_components;
280         void *blank_data = malloc(blank_data_size);
281         memset(blank_data, 0xff, blank_data_size);
282         PCLmEncapsulate(job_info->pclmgen_obj, blank_data, blank_data_size, job_info->strip_height,
283                 (void **) &job_info->pclm_output_buffer, &outBuffSize);
284         free(blank_data);
285         _WRITE(job_info, (const char *) job_info->pclm_output_buffer, outBuffSize);
286     }
287     LOGI("_end_page()");
288     PCLmEndPage(job_info->pclmgen_obj, (void **) &job_info->pclm_output_buffer, &outBuffSize);
289     _WRITE(job_info, (const char *) job_info->pclm_output_buffer, outBuffSize);
290     _END_PAGE(job_info);
291 
292     return OK;
293 }
294 
_end_job(pcl_job_info_t * job_info)295 static int _end_job(pcl_job_info_t *job_info) {
296     int outBuffSize = 0;
297 
298     LOGI("_end_job()");
299     PCLmEndJob(job_info->pclmgen_obj, (void **) &job_info->pclm_output_buffer, &outBuffSize);
300     _WRITE(job_info, (const char *) job_info->pclm_output_buffer, outBuffSize);
301     PCLmFreeBuffer(job_info->pclmgen_obj, job_info->pclm_output_buffer);
302     DestroyPCLmGen(job_info->pclmgen_obj);
303     _END_JOB(job_info);
304     return OK;
305 }
306 
_canCancelMidPage(void)307 static bool _canCancelMidPage(void) {
308     return false;
309 }
310 
311 static const ifc_pcl_t _pcl_ifc = {
312     _start_job, _end_job, _start_page, _end_page, _print_swath, _canCancelMidPage
313 };
314 
pclm_connect(void)315 ifc_pcl_t *pclm_connect(void) {
316     return ((ifc_pcl_t *) &_pcl_ifc);
317 }
318