1 /*
2 * qpdl.cpp (C) 2006-2008, Aurélien Croc (AP²C)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * $Id$
19 *
20 */
21 #include "qpdl.h"
22 #include <errno.h>
23 #include <unistd.h>
24 #include <inttypes.h>
25 #include <string.h>
26 #include "page.h"
27 #include "band.h"
28 #include "errlog.h"
29 #include "request.h"
30 #include "bandplane.h"
31
_renderBand(const Request & request,const Band * band)32 static bool _renderBand(const Request& request, const Band* band)
33 {
34 unsigned long version, subVersion, compression, size, dataSize, checkSum;
35 bool color, headerSent=false;
36 unsigned char header[0x20];
37 const BandPlane *plane;
38
39 compression = band->parent()->compression();
40 version = request.printer()->qpdlVersion();
41 color = request.printer()->color();
42 subVersion = compression == 0x13 ? 3 : 0;
43
44 for (unsigned int i=0; i < band->planesNr(); i++) {
45 bool nextBand = false;
46
47 // Get the plane
48 plane = band->plane(i);
49 if (!plane) {
50 ERRORMSG(_("Inconsistent data. Operation aborted"));
51 return false;
52 }
53 checkSum = plane->checksum();
54
55 // Check if there is a next band for that color
56 if (subVersion) {
57 const BandPlane *nextPlane;
58 const Band *next;
59
60 next = band->sibling();
61 if (next) {
62 for (unsigned int j=0; j < next->planesNr(); j++) {
63 nextPlane = next->plane(j);
64 if (nextPlane && nextPlane->colorNr() == plane->colorNr()){
65 nextBand = true;
66 break;
67 }
68 }
69 }
70 }
71
72 // Calculate the data size
73 dataSize = plane->dataSize();
74 dataSize += 4; // Data signature
75 if (version > 0) {
76 dataSize += 4; // Checksum
77 if (subVersion == 3)
78 dataSize += 7*4; // Sub-header
79 }
80
81 // Send the header
82 if (!headerSent || version == 2) {
83 header[0x0] = 0xC; // Signature
84 header[0x1] = band->bandNr(); // Band number
85 header[0x2] = band->width() >> 8; // Band width 8-15
86 header[0x3] = band->width(); // Band width 0-7
87 header[0x4] = band->height() >> 8; // Band height 8-15
88 header[0x5] = band->height(); // Band height 0-7
89 headerSent = true;
90 size = 0x6;
91 } else
92 size = 0x0;
93 // Add color information if it's a color printer
94 if (color) {
95 header[size] = plane->colorNr(); // Color number
96 size++;
97 }
98 // Append the last information and send the header
99 header[size+0] = compression; // Compression algorithm
100 header[size+1] = dataSize >> 24; // Data size 24 - 31
101 header[size+2] = dataSize >> 16; // Data size 16 - 23
102 header[size+3] = dataSize >> 8; // Data size 8 - 15
103 header[size+4] = dataSize; // Data size 0 - 7
104 if (write(STDOUT_FILENO, (unsigned char*)&header, size+5) == -1) {
105 ERRORMSG(_("Error while sending data to the printer (%u)"), errno);
106 return false;
107 }
108
109 // Send the sub-header
110 switch (plane->endian()) {
111 case BandPlane::Dependant:
112 *(uint32_t *)&header = (uint32_t)(0x09ABCDEF +
113 (subVersion << 28)); // Sub-header signature
114 break;
115 case BandPlane::BigEndian:
116 header[0x0] = 0x9 + (subVersion << 0x4);// Sub-header signature1
117 header[0x1] = 0xAB; // Sub-header signature2
118 header[0x2] = 0xCD; // Sub-header signature3
119 header[0x3] = 0xEF; // Sub-header signature4
120 break;
121 case BandPlane::LittleEndian:
122 header[0x0] = 0xEF; // Sub-header signature4
123 header[0x1] = 0xCD; // Sub-header signature3
124 header[0x2] = 0xAB; // Sub-header signature2
125 header[0x3] = 0x9 + (subVersion << 0x4);// Sub-header signature1
126 break;
127 };
128 size = 4;
129 if (subVersion == 3) {
130 uint32_t state;
131
132 checkSum += 0x39 + 0xAB + 0xCD + 0xEF;
133 if (!band->bandNr())
134 state = 0x0; // First band
135 else if (nextBand) {
136 state = 0x01000000; // Next band available
137 checkSum += 0x01;
138 } else {
139 state = 0x02000000; // Last band
140 checkSum += 0x02;
141 }
142 memset(header + size + 4, 0, 6*4);
143
144 switch (plane->endian()) {
145 case BandPlane::Dependant:
146 *(uint32_t*)(&header + size) = (uint32_t)plane->dataSize();
147 *(uint32_t*)(&header + size + 4) = (uint32_t)state;
148 break;
149 case BandPlane::BigEndian:
150 header[size+0] = plane->dataSize() >> 24; // Data size 24 - 31
151 header[size+1] = plane->dataSize() >> 16; // Data size 16 - 23
152 header[size+2] = plane->dataSize() >> 8; // Data size 8 - 15
153 header[size+3] = plane->dataSize(); // Data size 0 - 7
154 header[size+4] = state >> 24; // State 24 - 31
155 header[size+5] = state >> 16; // State 16 - 23
156 header[size+6] = state >> 8; // State 8 - 15
157 header[size+7] = state; // State 0 - 7
158 break;
159 case BandPlane::LittleEndian:
160 header[size+0] = plane->dataSize(); // Data size 0 - 7
161 header[size+1] = plane->dataSize() >> 8; // Data size 8 - 15
162 header[size+2] = plane->dataSize() >> 16; // Data size 16 - 23
163 header[size+3] = plane->dataSize() >> 24; // Data size 24 - 31
164 header[size+4] = state; // State 0 - 7
165 header[size+5] = state >> 8; // State 8 - 15
166 header[size+6] = state >> 16; // State 16 - 23
167 header[size+7] = state >> 24; // State 24 - 31
168 break;
169 }
170 for (unsigned int j=0; j < 4; j++)
171 checkSum += header[size + j];
172 size += 4 + 4 + 5*4;
173 } else
174 for (unsigned int j=0; j < 4; j++)
175 checkSum += header[size - j - 1];
176 if (write(STDOUT_FILENO, (unsigned char*)&header, size) == -1) {
177 ERRORMSG(_("Error while sending data to the printer (%u)"), errno);
178 return false;
179 }
180
181 // Send the data
182 if (write(STDOUT_FILENO, plane->data(), plane->dataSize()) == -1) {
183 ERRORMSG(_("Error while sending data to the printer (%u)"), errno);
184 return false;
185 }
186
187 // Send the checksum
188 header[0] = checkSum >> 24; // Checksum 24 - 31
189 header[1] = checkSum >> 16; // Checksum 16 - 23
190 header[2] = checkSum >> 8; // Checksum 8 - 15
191 header[3] = checkSum; // Checksum 0 - 7
192 size = 4;
193 // Close the plane if needed
194 if (color && version == 1 && (i+1) == band->planesNr()) {
195 header[4] = 0;
196 size++;
197 }
198 if (write(STDOUT_FILENO, (unsigned char*)&header, size) == -1) {
199 ERRORMSG(_("Error while sending data to the printer (%u)"), errno);
200 return false;
201 }
202 }
203
204 return true;
205 }
206
renderPage(const Request & request,Page * page,bool lastPage)207 bool renderPage(const Request& request, Page* page, bool lastPage)
208 {
209 unsigned char duplex=0, tumble=0, paperSource;
210 unsigned long width, height;
211 unsigned char header[0x11];
212 const Band* band;
213
214 if (!page) {
215 ERRORMSG(_("Try to render a NULL page"));
216 return false;
217 }
218
219 // Get the duplex values
220 paperSource = request.printer()->paperSource();
221 switch (request.duplex()) {
222 case Request::Simplex:
223 duplex = 1;
224 tumble = 0;
225 break;
226 case Request::LongEdge:
227 duplex = 1;
228 tumble = page->pageNr() % 2;
229 break;
230 case Request::ShortEdge:
231 duplex = 0;
232 tumble = page->pageNr() % 2;
233 break;
234 case Request::ManualLongEdge:
235 duplex = 0;
236 tumble = page->pageNr() % 2;
237 if (tumble && !lastPage)
238 paperSource = 3; // Multi source
239 break;
240 case Request::ManualShortEdge:
241 duplex = 1;
242 tumble = page->pageNr() % 2;
243 if (tumble && !lastPage)
244 paperSource = 3; // Multi source
245 /** @todo what about the Short edge? The page isn't rotated? */
246 break;
247 }
248
249 width = page->width();
250 height = page->height();
251
252 // Send the page header
253 header[0x0] = 0; // Signature
254 header[0x1] = page->yResolution() / 100; // Y Resolution
255 header[0x2] = page->copiesNr() >> 8; // Number of copies 8-15
256 header[0x3] = page->copiesNr(); // Number of copies 0-7
257 header[0x4] = request.printer()->paperType(); // Paper type
258 header[0x5] = width >> 8; // Printable area width
259 header[0x6] = width; // Printable area width
260 header[0x7] = height >> 8; // Printable area height
261 header[0x8] = height; // Printable area height
262 header[0x9] = paperSource; // Paper source
263 header[0xa] = request.printer()->unknownByte1();// ??? XXX
264 header[0xb] = duplex; // Duplex
265 header[0xc] = tumble; // Tumble
266 header[0xd] = request.printer()->unknownByte2();// ??? XXX
267 header[0xe] = request.printer()->qpdlVersion(); // QPDL Version
268 header[0xf] = request.printer()->unknownByte3();// ??? XXX
269 header[0x10] = page->xResolution() / 100; // X Resolution
270 if (write(STDOUT_FILENO, (unsigned char*)&header, 0x11) == -1) {
271 ERRORMSG(_("Error while sending data to the printer (%u)"), errno);
272 return false;
273 }
274
275 // Send the page bands
276 band = page->firstBand();
277 while (band) {
278 if (!_renderBand(request, band))
279 return false;
280 band = band->sibling();
281 }
282
283 // Send the page footer
284 header[0x0] = 1; // Signature
285 header[0x1] = page->copiesNr() >> 8; // Number of copies 8-15
286 header[0x2] = page->copiesNr(); // Number of copies 0-7
287 if (write(STDOUT_FILENO, (unsigned char*)&header, 0x3) == -1) {
288 ERRORMSG(_("Error while sending data to the printer (%u)"), errno);
289 return false;
290 }
291
292 return true;
293 }
294
295 /* vim: set expandtab tabstop=4 shiftwidth=4 smarttab tw=80 cin enc=utf8: */
296
297