Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <stdio.h>
22 : #include <sys/types.h>
23 : #include <sys/stat.h>
24 : #include <fcntl.h>
25 : #include <unistd.h>
26 :
27 : #include "psputil.hxx"
28 : #include "glyphset.hxx"
29 :
30 : #include "generic/printerjob.hxx"
31 : #include "generic/printergfx.hxx"
32 : #include "vcl/ppdparser.hxx"
33 : #include "vcl/strhelper.hxx"
34 : #include "vcl/printerinfomanager.hxx"
35 :
36 : #include "rtl/ustring.hxx"
37 : #include "rtl/strbuf.hxx"
38 : #include "rtl/ustrbuf.hxx"
39 :
40 : #include <osl/thread.h>
41 : #include <osl/security.hxx>
42 : #include <sal/alloca.h>
43 : #include <sal/macros.h>
44 :
45 : #include <algorithm>
46 : #include <vector>
47 :
48 : using namespace psp;
49 :
50 : using ::rtl::OUString;
51 : using ::rtl::OUStringToOString;
52 : using ::rtl::OString;
53 : using ::rtl::OStringBuffer;
54 :
55 : // forward declaration
56 :
57 : #define nBLOCKSIZE 0x2000
58 :
59 : namespace psp
60 : {
61 :
62 : sal_Bool
63 0 : AppendPS (FILE* pDst, osl::File* pSrc, sal_uChar* pBuffer,
64 : sal_uInt32 nBlockSize = nBLOCKSIZE)
65 : {
66 0 : if ((pDst == NULL) || (pSrc == NULL))
67 0 : return sal_False;
68 :
69 0 : if (pSrc->setPos(osl_Pos_Absolut, 0) != osl::FileBase::E_None)
70 0 : return sal_False;
71 :
72 0 : if (nBlockSize == 0)
73 0 : nBlockSize = nBLOCKSIZE;
74 0 : if (pBuffer == NULL)
75 0 : pBuffer = (sal_uChar*)alloca (nBlockSize);
76 :
77 0 : sal_uInt64 nIn = 0;
78 0 : sal_uInt64 nOut = 0;
79 0 : do
80 : {
81 0 : pSrc->read (pBuffer, nBlockSize, nIn);
82 0 : if (nIn > 0)
83 0 : nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst);
84 : }
85 : while ((nIn > 0) && (nIn == nOut));
86 :
87 0 : return sal_True;
88 : }
89 :
90 : } // namespace psp
91 :
92 : /*
93 : * private convenience routines for file handling
94 : */
95 :
96 : osl::File*
97 0 : PrinterJob::CreateSpoolFile (const rtl::OUString& rName, const rtl::OUString& rExtension)
98 : {
99 0 : osl::File::RC nError = osl::File::E_None;
100 0 : osl::File* pFile = NULL;
101 :
102 0 : rtl::OUString aFile = rName + rExtension;
103 0 : rtl::OUString aFileURL;
104 0 : nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL );
105 0 : if (nError != osl::File::E_None)
106 0 : return NULL;
107 0 : aFileURL = maSpoolDirName + rtl::OUString("/") + aFileURL;
108 :
109 0 : pFile = new osl::File (aFileURL);
110 0 : nError = pFile->open (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
111 0 : if (nError != osl::File::E_None)
112 : {
113 0 : delete pFile;
114 0 : return NULL;
115 : }
116 :
117 : pFile->setAttributes (aFileURL,
118 0 : osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead);
119 0 : return pFile;
120 : }
121 :
122 : /*
123 : * public methods of PrinterJob: for use in PrinterGfx
124 : */
125 :
126 : void
127 0 : PrinterJob::GetScale (double &rXScale, double &rYScale) const
128 : {
129 0 : rXScale = mfXScale;
130 0 : rYScale = mfYScale;
131 0 : }
132 :
133 : sal_uInt16
134 0 : PrinterJob::GetDepth () const
135 : {
136 0 : sal_Int32 nLevel = GetPostscriptLevel();
137 0 : sal_Bool bColor = IsColorPrinter ();
138 :
139 0 : return nLevel > 1 && bColor ? 24 : 8;
140 : }
141 :
142 : sal_uInt16
143 0 : PrinterJob::GetPostscriptLevel (const JobData *pJobData) const
144 : {
145 0 : sal_uInt16 nPSLevel = 2;
146 :
147 0 : if( pJobData == NULL )
148 0 : pJobData = &m_aLastJobData;
149 :
150 0 : if( pJobData->m_nPSLevel )
151 0 : nPSLevel = pJobData->m_nPSLevel;
152 : else
153 0 : if( pJobData->m_pParser )
154 0 : nPSLevel = pJobData->m_pParser->getLanguageLevel();
155 :
156 0 : return nPSLevel;
157 : }
158 :
159 : sal_Bool
160 0 : PrinterJob::IsColorPrinter () const
161 : {
162 0 : sal_Bool bColor = sal_False;
163 :
164 0 : if( m_aLastJobData.m_nColorDevice )
165 0 : bColor = m_aLastJobData.m_nColorDevice == -1 ? sal_False : sal_True;
166 0 : else if( m_aLastJobData.m_pParser )
167 0 : bColor = m_aLastJobData.m_pParser->isColorDevice() ? sal_True : sal_False;
168 :
169 0 : return bColor;
170 : }
171 :
172 : osl::File*
173 0 : PrinterJob::GetCurrentPageHeader ()
174 : {
175 0 : return maHeaderList.back();
176 : }
177 :
178 : osl::File*
179 0 : PrinterJob::GetCurrentPageBody ()
180 : {
181 0 : return maPageList.back();
182 : }
183 :
184 : /*
185 : * public methods of PrinterJob: the actual job / spool handling
186 : */
187 :
188 0 : PrinterJob::PrinterJob () :
189 : mpJobHeader( NULL ),
190 : mpJobTrailer( NULL ),
191 0 : m_bQuickJob( false )
192 : {
193 0 : }
194 :
195 : /* remove all our temporary files, uses external program "rm", since
196 : osl functionality is inadequate */
197 : void
198 0 : removeSpoolDir (const rtl::OUString& rSpoolDir)
199 : {
200 0 : rtl::OUString aSysPath;
201 0 : if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) )
202 : {
203 : // Conversion did not work, as this is quite a dangerous action,
204 : // we should abort here ....
205 : OSL_FAIL( "psprint: couldn't remove spool directory" );
206 0 : return;
207 : }
208 : rtl::OString aSysPathByte =
209 0 : rtl::OUStringToOString (aSysPath, osl_getThreadTextEncoding());
210 : sal_Char pSystem [128];
211 0 : sal_Int32 nChar = 0;
212 :
213 0 : nChar = psp::appendStr ("rm -rf ", pSystem);
214 0 : nChar += psp::appendStr (aSysPathByte.getStr(), pSystem + nChar);
215 :
216 0 : if (system (pSystem) == -1)
217 0 : OSL_FAIL( "psprint: couldn't remove spool directory" );
218 : }
219 :
220 : /* creates a spool directory with a "pidgin random" value based on
221 : current system time */
222 : rtl::OUString
223 0 : createSpoolDir ()
224 : {
225 : TimeValue aCur;
226 0 : osl_getSystemTime( &aCur );
227 0 : sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000);
228 :
229 0 : rtl::OUString aTmpDir;
230 0 : osl_getTempDirURL( &aTmpDir.pData );
231 :
232 0 : do
233 : {
234 0 : rtl::OUStringBuffer aDir( aTmpDir.getLength() + 16 );
235 0 : aDir.append( aTmpDir );
236 0 : aDir.appendAscii( "/psp" );
237 0 : aDir.append(nRand);
238 0 : rtl::OUString aResult = aDir.makeStringAndClear();
239 0 : if( osl::Directory::create( aResult ) == osl::FileBase::E_None )
240 : {
241 : osl::File::setAttributes( aResult,
242 : osl_File_Attribute_OwnWrite
243 : | osl_File_Attribute_OwnRead
244 0 : | osl_File_Attribute_OwnExe );
245 0 : return aResult;
246 : }
247 0 : nRand++;
248 : } while( nRand );
249 0 : return rtl::OUString();
250 : }
251 :
252 0 : PrinterJob::~PrinterJob ()
253 : {
254 0 : std::list< osl::File* >::iterator pPage;
255 0 : for (pPage = maPageList.begin(); pPage != maPageList.end(); ++pPage)
256 : {
257 : //(*pPage)->remove();
258 0 : delete *pPage;
259 : }
260 0 : for (pPage = maHeaderList.begin(); pPage != maHeaderList.end(); ++pPage)
261 : {
262 : //(*pPage)->remove();
263 0 : delete *pPage;
264 : }
265 : // mpJobHeader->remove();
266 0 : delete mpJobHeader;
267 : // mpJobTrailer->remove();
268 0 : delete mpJobTrailer;
269 :
270 : // XXX should really call osl::remove routines
271 0 : if( !maSpoolDirName.isEmpty() )
272 0 : removeSpoolDir (maSpoolDirName);
273 :
274 : // osl::Directory::remove (maSpoolDirName);
275 0 : }
276 :
277 0 : static void WriteLocalTimePS( osl::File *rFile )
278 : {
279 : TimeValue m_start_time, tLocal;
280 : oslDateTime date_time;
281 0 : if (osl_getSystemTime( &m_start_time ) &&
282 0 : osl_getLocalTimeFromSystemTime( &m_start_time, &tLocal ) &&
283 0 : osl_getDateTimeFromTimeValue( &tLocal, &date_time ))
284 : {
285 : char ar[ 256 ];
286 : snprintf(
287 : ar, sizeof (ar),
288 : "%04d-%02d-%02d %02d:%02d:%02d ",
289 : date_time.Year, date_time.Month, date_time.Day,
290 0 : date_time.Hours, date_time.Minutes, date_time.Seconds );
291 0 : WritePS( rFile, ar );
292 : }
293 : else
294 0 : WritePS( rFile, "Unknown-Time" );
295 0 : }
296 :
297 0 : static bool isAscii( const rtl::OUString& rStr )
298 : {
299 0 : sal_Int32 nLen = rStr.getLength();
300 0 : for( sal_Int32 i = 0; i < nLen; i++ )
301 0 : if( rStr[i] > 127 )
302 0 : return false;
303 0 : return true;
304 : }
305 :
306 : sal_Bool
307 0 : PrinterJob::StartJob (
308 : const rtl::OUString& rFileName,
309 : int nMode,
310 : const rtl::OUString& rJobName,
311 : const rtl::OUString& rAppName,
312 : const JobData& rSetupData,
313 : PrinterGfx* pGraphics,
314 : bool bIsQuickJob
315 : )
316 : {
317 0 : m_bQuickJob = bIsQuickJob;
318 0 : mnMaxWidthPt = mnMaxHeightPt = 0;
319 0 : mnLandscapes = mnPortraits = 0;
320 0 : m_pGraphics = pGraphics;
321 0 : InitPaperSize (rSetupData);
322 :
323 : // create file container for document header and trailer
324 0 : maFileName = rFileName;
325 0 : mnFileMode = nMode;
326 0 : maSpoolDirName = createSpoolDir ();
327 0 : maJobTitle = rJobName;
328 :
329 0 : rtl::OUString aExt(".ps");
330 0 : mpJobHeader = CreateSpoolFile (rtl::OUString("psp_head"), aExt);
331 0 : mpJobTrailer = CreateSpoolFile (rtl::OUString("psp_tail"), aExt);
332 0 : if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor
333 0 : return sal_False;
334 :
335 : // write document header according to Document Structuring Conventions (DSC)
336 : WritePS (mpJobHeader,
337 : "%!PS-Adobe-3.0\n"
338 0 : "%%BoundingBox: (atend)\n" );
339 :
340 0 : rtl::OUString aFilterWS;
341 :
342 : // Creator (this application)
343 0 : aFilterWS = WhitespaceToSpace( rAppName, sal_False );
344 0 : WritePS (mpJobHeader, "%%Creator: (");
345 0 : WritePS (mpJobHeader, aFilterWS);
346 0 : WritePS (mpJobHeader, ")\n");
347 :
348 : // For (user name)
349 0 : osl::Security aSecurity;
350 0 : rtl::OUString aUserName;
351 0 : if( aSecurity.getUserName( aUserName ) )
352 : {
353 0 : WritePS (mpJobHeader, "%%For: (");
354 0 : WritePS (mpJobHeader, aUserName);
355 0 : WritePS (mpJobHeader, ")\n");
356 : }
357 :
358 : // Creation Date (locale independent local time)
359 0 : WritePS (mpJobHeader, "%%CreationDate: (");
360 0 : WriteLocalTimePS (mpJobHeader);
361 0 : WritePS (mpJobHeader, ")\n");
362 :
363 : // Document Title
364 : /* #i74335#
365 : * The title should be clean ascii; rJobName however may
366 : * contain any Unicode character. So implement the following
367 : * algorithm:
368 : * use rJobName, if it contains only ascii
369 : * use the filename, if it contains only ascii
370 : * else omit %%Title
371 : */
372 0 : aFilterWS = WhitespaceToSpace( rJobName, sal_False );
373 0 : rtl::OUString aTitle( aFilterWS );
374 0 : if( ! isAscii( aTitle ) )
375 : {
376 0 : sal_Int32 nIndex = 0;
377 0 : while( nIndex != -1 )
378 0 : aTitle = rFileName.getToken( 0, '/', nIndex );
379 0 : aTitle = WhitespaceToSpace( aTitle, sal_False );
380 0 : if( ! isAscii( aTitle ) )
381 0 : aTitle = rtl::OUString();
382 : }
383 :
384 0 : maJobTitle = aFilterWS;
385 0 : if( !aTitle.isEmpty() )
386 : {
387 0 : WritePS (mpJobHeader, "%%Title: (");
388 0 : WritePS (mpJobHeader, aTitle);
389 0 : WritePS (mpJobHeader, ")\n");
390 : }
391 :
392 : // Language Level
393 : sal_Char pLevel[16];
394 0 : sal_Int32 nSz = getValueOf(GetPostscriptLevel(&rSetupData), pLevel);
395 0 : pLevel[nSz++] = '\n';
396 0 : pLevel[nSz ] = '\0';
397 0 : WritePS (mpJobHeader, "%%LanguageLevel: ");
398 0 : WritePS (mpJobHeader, pLevel);
399 :
400 : // Other
401 0 : WritePS (mpJobHeader, "%%DocumentData: Clean7Bit\n");
402 0 : WritePS (mpJobHeader, "%%Pages: (atend)\n");
403 0 : WritePS (mpJobHeader, "%%Orientation: (atend)\n");
404 0 : WritePS (mpJobHeader, "%%PageOrder: Ascend\n");
405 0 : WritePS (mpJobHeader, "%%EndComments\n");
406 :
407 : // write Prolog
408 0 : writeProlog (mpJobHeader, rSetupData);
409 :
410 : // mark last job setup as not set
411 0 : m_aLastJobData.m_pParser = NULL;
412 0 : m_aLastJobData.m_aContext.setParser( NULL );
413 :
414 0 : return sal_True;
415 : }
416 :
417 : sal_Bool
418 0 : PrinterJob::EndJob ()
419 : {
420 : // no pages ? that really means no print job
421 0 : if( maPageList.empty() )
422 0 : return sal_False;
423 :
424 : // write document setup (done here because it
425 : // includes the accumulated fonts
426 0 : if( mpJobHeader )
427 0 : writeSetup( mpJobHeader, m_aDocumentJobData );
428 0 : m_pGraphics->OnEndJob();
429 0 : if( ! (mpJobHeader && mpJobTrailer) )
430 0 : return sal_False;
431 :
432 : // write document trailer according to Document Structuring Conventions (DSC)
433 0 : rtl::OStringBuffer aTrailer(512);
434 0 : aTrailer.append( "%%Trailer\n" );
435 0 : aTrailer.append( "%%BoundingBox: 0 0 " );
436 0 : aTrailer.append( (sal_Int32)mnMaxWidthPt );
437 0 : aTrailer.append( " " );
438 0 : aTrailer.append( (sal_Int32)mnMaxHeightPt );
439 0 : if( mnLandscapes > mnPortraits )
440 0 : aTrailer.append("\n%%Orientation: Landscape");
441 : else
442 0 : aTrailer.append("\n%%Orientation: Portrait");
443 0 : aTrailer.append( "\n%%Pages: " );
444 0 : aTrailer.append( (sal_Int32)maPageList.size() );
445 0 : aTrailer.append( "\n%%EOF\n" );
446 0 : WritePS (mpJobTrailer, aTrailer.getStr());
447 :
448 : /*
449 : * spool the set of files to their final destination, this is U**X dependent
450 : */
451 :
452 0 : FILE* pDestFILE = NULL;
453 :
454 : /* create a destination either as file or as a pipe */
455 0 : sal_Bool bSpoolToFile = !maFileName.isEmpty();
456 0 : if (bSpoolToFile)
457 : {
458 : const rtl::OString aFileName = rtl::OUStringToOString (maFileName,
459 0 : osl_getThreadTextEncoding());
460 0 : if( mnFileMode )
461 : {
462 0 : int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode );
463 0 : if( nFile != -1 )
464 : {
465 0 : pDestFILE = fdopen( nFile, "w" );
466 0 : if( pDestFILE == NULL )
467 : {
468 0 : close( nFile );
469 0 : unlink( aFileName.getStr() );
470 0 : return sal_False;
471 : }
472 : }
473 : else
474 0 : chmod( aFileName.getStr(), mnFileMode );
475 : }
476 0 : if (pDestFILE == NULL)
477 0 : pDestFILE = fopen (aFileName.getStr(), "w");
478 :
479 0 : if (pDestFILE == NULL)
480 0 : return sal_False;
481 : }
482 : else
483 : {
484 0 : PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get ();
485 0 : pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob );
486 0 : if (pDestFILE == NULL)
487 0 : return sal_False;
488 : }
489 :
490 : /* spool the document parts to the destination */
491 :
492 : sal_uChar pBuffer[ nBLOCKSIZE ];
493 :
494 0 : AppendPS (pDestFILE, mpJobHeader, pBuffer);
495 0 : mpJobHeader->close();
496 :
497 0 : sal_Bool bSuccess = sal_True;
498 0 : std::list< osl::File* >::iterator pPageBody;
499 0 : std::list< osl::File* >::iterator pPageHead;
500 0 : for (pPageBody = maPageList.begin(), pPageHead = maHeaderList.begin();
501 0 : pPageBody != maPageList.end() && pPageHead != maHeaderList.end();
502 : ++pPageBody, ++pPageHead)
503 : {
504 0 : if( *pPageHead )
505 : {
506 0 : osl::File::RC nError = (*pPageHead)->open(osl_File_OpenFlag_Read);
507 0 : if (nError == osl::File::E_None)
508 : {
509 0 : AppendPS (pDestFILE, *pPageHead, pBuffer);
510 0 : (*pPageHead)->close();
511 : }
512 : }
513 : else
514 0 : bSuccess = sal_False;
515 0 : if( *pPageBody )
516 : {
517 0 : osl::File::RC nError = (*pPageBody)->open(osl_File_OpenFlag_Read);
518 0 : if (nError == osl::File::E_None)
519 : {
520 0 : AppendPS (pDestFILE, *pPageBody, pBuffer);
521 0 : (*pPageBody)->close();
522 : }
523 : }
524 : else
525 0 : bSuccess = sal_False;
526 : }
527 :
528 0 : AppendPS (pDestFILE, mpJobTrailer, pBuffer);
529 0 : mpJobTrailer->close();
530 :
531 : /* well done */
532 :
533 0 : if (bSpoolToFile)
534 0 : fclose (pDestFILE);
535 : else
536 : {
537 0 : PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get();
538 0 : if (0 == rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName,
539 0 : maJobTitle, pDestFILE, m_aDocumentJobData, true ))
540 : {
541 0 : bSuccess = sal_False;
542 : }
543 : }
544 :
545 0 : return bSuccess;
546 : }
547 :
548 : sal_Bool
549 0 : PrinterJob::AbortJob ()
550 : {
551 0 : m_pGraphics->OnEndJob();
552 0 : return sal_False;
553 : }
554 :
555 : void
556 0 : PrinterJob::InitPaperSize (const JobData& rJobSetup)
557 : {
558 0 : int nRes = rJobSetup.m_aContext.getRenderResolution ();
559 :
560 0 : rtl::OUString aPaper;
561 : int nWidth, nHeight;
562 0 : rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight);
563 :
564 0 : int nLeft = 0, nRight = 0, nUpper = 0, nLower = 0;
565 0 : const PPDParser* pParser = rJobSetup.m_aContext.getParser();
566 0 : if (pParser != NULL)
567 0 : pParser->getMargins (aPaper, nLeft, nRight, nUpper, nLower);
568 :
569 0 : mnResolution = nRes;
570 :
571 0 : mnWidthPt = nWidth;
572 0 : mnHeightPt = nHeight;
573 :
574 0 : if( mnWidthPt > mnMaxWidthPt )
575 0 : mnMaxWidthPt = mnWidthPt;
576 0 : if( mnHeightPt > mnMaxHeightPt )
577 0 : mnMaxHeightPt = mnHeightPt;
578 :
579 0 : mnLMarginPt = nLeft;
580 0 : mnRMarginPt = nRight;
581 0 : mnTMarginPt = nUpper;
582 0 : mnBMarginPt = nLower;
583 :
584 0 : mfXScale = (double)72.0 / (double)mnResolution;
585 0 : mfYScale = -1.0 * (double)72.0 / (double)mnResolution;
586 0 : }
587 :
588 :
589 : sal_Bool
590 0 : PrinterJob::StartPage (const JobData& rJobSetup)
591 : {
592 0 : InitPaperSize (rJobSetup);
593 :
594 0 : rtl::OUString aPageNo = rtl::OUString::valueOf ((sal_Int32)maPageList.size()+1); // sequential page number must start with 1
595 0 : rtl::OUString aExt = aPageNo + rtl::OUString(".ps");
596 :
597 0 : osl::File* pPageHeader = CreateSpoolFile ( rtl::OUString("psp_pghead"), aExt);
598 0 : osl::File* pPageBody = CreateSpoolFile ( rtl::OUString("psp_pgbody"), aExt);
599 :
600 0 : maHeaderList.push_back (pPageHeader);
601 0 : maPageList.push_back (pPageBody);
602 :
603 0 : if( ! (pPageHeader && pPageBody) )
604 0 : return sal_False;
605 :
606 : // write page header according to Document Structuring Conventions (DSC)
607 0 : WritePS (pPageHeader, "%%Page: ");
608 0 : WritePS (pPageHeader, aPageNo);
609 0 : WritePS (pPageHeader, " ");
610 0 : WritePS (pPageHeader, aPageNo);
611 0 : WritePS (pPageHeader, "\n");
612 :
613 0 : if( rJobSetup.m_eOrientation == orientation::Landscape )
614 : {
615 0 : WritePS (pPageHeader, "%%PageOrientation: Landscape\n");
616 0 : mnLandscapes++;
617 : }
618 : else
619 : {
620 0 : WritePS (pPageHeader, "%%PageOrientation: Portrait\n");
621 0 : mnPortraits++;
622 : }
623 :
624 : sal_Char pBBox [256];
625 0 : sal_Int32 nChar = 0;
626 :
627 0 : nChar = psp::appendStr ("%%PageBoundingBox: ", pBBox);
628 0 : nChar += psp::getValueOf (mnLMarginPt, pBBox + nChar);
629 0 : nChar += psp::appendStr (" ", pBBox + nChar);
630 0 : nChar += psp::getValueOf (mnBMarginPt, pBBox + nChar);
631 0 : nChar += psp::appendStr (" ", pBBox + nChar);
632 0 : nChar += psp::getValueOf (mnWidthPt - mnRMarginPt, pBBox + nChar);
633 0 : nChar += psp::appendStr (" ", pBBox + nChar);
634 0 : nChar += psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox + nChar);
635 0 : nChar += psp::appendStr ("\n", pBBox + nChar);
636 :
637 0 : WritePS (pPageHeader, pBBox);
638 :
639 : /* #i7262# #i65491# write setup only before first page
640 : * (to %%Begin(End)Setup, instead of %%Begin(End)PageSetup)
641 : * don't do this in StartJob since the jobsetup there may be
642 : * different.
643 : */
644 0 : bool bWriteFeatures = true;
645 0 : if( 1 == maPageList.size() )
646 : {
647 0 : m_aDocumentJobData = rJobSetup;
648 0 : bWriteFeatures = false;
649 : }
650 :
651 0 : if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) )
652 : {
653 0 : m_aLastJobData = rJobSetup;
654 0 : return true;
655 : }
656 :
657 0 : return false;
658 : }
659 :
660 : sal_Bool
661 0 : PrinterJob::EndPage ()
662 : {
663 0 : m_pGraphics->OnEndPage();
664 :
665 0 : osl::File* pPageHeader = maHeaderList.back();
666 0 : osl::File* pPageBody = maPageList.back();
667 :
668 0 : if( ! (pPageBody && pPageHeader) )
669 0 : return sal_False;
670 :
671 : // copy page to paper and write page trailer according to DSC
672 :
673 : sal_Char pTrailer[256];
674 0 : sal_Int32 nChar = 0;
675 0 : nChar = psp::appendStr ("grestore grestore\n", pTrailer);
676 0 : nChar += psp::appendStr ("showpage\n", pTrailer + nChar);
677 0 : nChar += psp::appendStr ("%%PageTrailer\n\n", pTrailer + nChar);
678 0 : WritePS (pPageBody, pTrailer);
679 :
680 : // this page is done for now, close it to avoid having too many open fd's
681 :
682 0 : pPageHeader->close();
683 0 : pPageBody->close();
684 :
685 0 : return sal_True;
686 : }
687 :
688 : struct less_ppd_key : public ::std::binary_function<double, double, bool>
689 : {
690 0 : bool operator()(const PPDKey* left, const PPDKey* right)
691 0 : { return left->getOrderDependency() < right->getOrderDependency(); }
692 : };
693 :
694 0 : static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature )
695 : {
696 0 : if( ! pKey || ! pValue )
697 0 : return true;
698 :
699 0 : OStringBuffer aFeature(256);
700 0 : aFeature.append( "[{\n" );
701 0 : if( bUseIncluseFeature )
702 0 : aFeature.append( "%%IncludeFeature:" );
703 : else
704 0 : aFeature.append( "%%BeginFeature:" );
705 0 : aFeature.append( " *" );
706 0 : aFeature.append( OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ) );
707 0 : aFeature.append( ' ' );
708 0 : aFeature.append( OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) );
709 0 : if( !bUseIncluseFeature )
710 : {
711 0 : aFeature.append( '\n' );
712 0 : aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) );
713 0 : aFeature.append( "\n%%EndFeature" );
714 : }
715 0 : aFeature.append( "\n} stopped cleartomark\n" );
716 0 : sal_uInt64 nWritten = 0;
717 0 : return pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten )
718 0 : || nWritten != (sal_uInt64)aFeature.getLength() ? false : true;
719 : }
720 :
721 0 : bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup )
722 : {
723 0 : bool bSuccess = true;
724 :
725 : // emit features ordered to OrderDependency
726 : // ignore features that are set to default
727 :
728 : // sanity check
729 0 : if( rJob.m_pParser == rJob.m_aContext.getParser() &&
730 : rJob.m_pParser &&
731 : ( m_aLastJobData.m_pParser == rJob.m_pParser || m_aLastJobData.m_pParser == NULL )
732 : )
733 : {
734 : int i;
735 0 : int nKeys = rJob.m_aContext.countValuesModified();
736 0 : ::std::vector< const PPDKey* > aKeys( nKeys );
737 0 : for( i = 0; i < nKeys; i++ )
738 0 : aKeys[i] = rJob.m_aContext.getModifiedKey( i );
739 0 : ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
740 :
741 0 : for( i = 0; i < nKeys && bSuccess; i++ )
742 : {
743 0 : const PPDKey* pKey = aKeys[i];
744 0 : bool bEmit = false;
745 0 : if( bDocumentSetup )
746 : {
747 0 : if( pKey->getSetupType() == PPDKey::DocumentSetup )
748 0 : bEmit = true;
749 : }
750 0 : if( pKey->getSetupType() == PPDKey::PageSetup ||
751 0 : pKey->getSetupType() == PPDKey::AnySetup )
752 0 : bEmit = true;
753 0 : if( bEmit )
754 : {
755 0 : const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
756 0 : if( pValue
757 : && pValue->m_eType == eInvocation
758 : && ( m_aLastJobData.m_pParser == NULL
759 0 : || m_aLastJobData.m_aContext.getValue( pKey ) != pValue
760 : || bDocumentSetup
761 : )
762 : )
763 : {
764 : // try to avoid PS level 2 feature commands if level is set to 1
765 0 : if( GetPostscriptLevel( &rJob ) == 1 )
766 : {
767 : bool bHavePS2 =
768 0 : ( pValue->m_aValue.SearchAscii( "<<" ) != STRING_NOTFOUND )
769 : ||
770 0 : ( pValue->m_aValue.SearchAscii( ">>" ) != STRING_NOTFOUND );
771 0 : if( bHavePS2 )
772 0 : continue;
773 : }
774 0 : bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() );
775 : }
776 : }
777 0 : }
778 : }
779 : else
780 0 : bSuccess = false;
781 :
782 0 : return bSuccess;
783 : }
784 :
785 0 : bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob, bool bWriteFeatures )
786 : {
787 0 : bool bSuccess = true;
788 :
789 0 : WritePS (pFile, "%%BeginPageSetup\n%\n");
790 0 : if ( bWriteFeatures )
791 0 : bSuccess = writeFeatureList( pFile, rJob, false );
792 0 : WritePS (pFile, "%%EndPageSetup\n");
793 :
794 : sal_Char pTranslate [128];
795 0 : sal_Int32 nChar = 0;
796 :
797 0 : if( rJob.m_eOrientation == orientation::Portrait )
798 : {
799 0 : nChar = psp::appendStr ("gsave\n[", pTranslate);
800 0 : nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5);
801 0 : nChar += psp::appendStr (" 0 0 ", pTranslate + nChar);
802 0 : nChar += psp::getValueOfDouble ( pTranslate + nChar, mfYScale, 5);
803 0 : nChar += psp::appendStr (" ", pTranslate + nChar);
804 0 : nChar += psp::getValueOf (mnRMarginPt, pTranslate + nChar);
805 0 : nChar += psp::appendStr (" ", pTranslate + nChar);
806 : nChar += psp::getValueOf (mnHeightPt-mnTMarginPt,
807 0 : pTranslate + nChar);
808 : nChar += psp::appendStr ("] concat\ngsave\n",
809 0 : pTranslate + nChar);
810 : }
811 : else
812 : {
813 0 : nChar = psp::appendStr ("gsave\n", pTranslate);
814 0 : nChar += psp::appendStr ("[ 0 ", pTranslate + nChar);
815 0 : nChar += psp::getValueOfDouble ( pTranslate + nChar, -mfYScale, 5);
816 0 : nChar += psp::appendStr (" ", pTranslate + nChar);
817 0 : nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5);
818 0 : nChar += psp::appendStr (" 0 ", pTranslate + nChar );
819 0 : nChar += psp::getValueOfDouble ( pTranslate + nChar, mnLMarginPt, 5 );
820 0 : nChar += psp::appendStr (" ", pTranslate + nChar);
821 0 : nChar += psp::getValueOf (mnBMarginPt, pTranslate + nChar );
822 : nChar += psp::appendStr ("] concat\ngsave\n",
823 0 : pTranslate + nChar);
824 : }
825 :
826 0 : WritePS (pFile, pTranslate);
827 :
828 0 : return bSuccess;
829 : }
830 :
831 0 : void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData )
832 : {
833 0 : if( ! PrinterInfoManager::get().getUseJobPatch() )
834 : return;
835 :
836 0 : const PPDKey* pKey = NULL;
837 :
838 0 : if( rJobData.m_pParser )
839 0 : pKey = rJobData.m_pParser->getKey( OUString( "JobPatchFile" ) );
840 0 : if( ! pKey )
841 : return;
842 :
843 : // order the patch files
844 : // according to PPD spec the JobPatchFile options must be int
845 : // and should be emitted in order
846 0 : std::list< sal_Int32 > patch_order;
847 0 : int nValueCount = pKey->countValues();
848 0 : for( int i = 0; i < nValueCount; i++ )
849 : {
850 0 : const PPDValue* pVal = pKey->getValue( i );
851 0 : patch_order.push_back( pVal->m_aOption.ToInt32() );
852 0 : if( patch_order.back() == 0 && ! pVal->m_aOption.EqualsAscii( "0" ) )
853 : {
854 0 : WritePS( pFile, "% Warning: left out JobPatchFile option \"" );
855 0 : OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US );
856 0 : WritePS( pFile, aOption.getStr() );
857 : WritePS( pFile,
858 : "\"\n% as it violates the PPD spec;\n"
859 0 : "% JobPatchFile options need to be numbered for ordering.\n" );
860 : }
861 : }
862 :
863 0 : patch_order.sort();
864 0 : patch_order.unique();
865 :
866 0 : while( patch_order.begin() != patch_order.end() )
867 : {
868 : // note: this discards patch files not adhering to the "int" scheme
869 : // as there won't be a value for them
870 0 : writeFeature( pFile, pKey, pKey->getValue( OUString::valueOf( patch_order.front() ) ), false );
871 0 : patch_order.pop_front();
872 0 : }
873 : }
874 :
875 0 : bool PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData )
876 : {
877 0 : WritePS( pFile, "%%BeginProlog\n" );
878 :
879 : // JobPatchFile feature needs to be emitted at begin of prolog
880 0 : writeJobPatch( pFile, rJobData );
881 :
882 : static const sal_Char pProlog[] = {
883 : "%%BeginResource: procset PSPrint-Prolog 1.0 0\n"
884 : "/ISO1252Encoding [\n"
885 : "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
886 : "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
887 : "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
888 : "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
889 : "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n"
890 : "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n"
891 : "/zero /one /two /three /four /five /six /seven\n"
892 : "/eight /nine /colon /semicolon /less /equal /greater /question\n"
893 : "/at /A /B /C /D /E /F /G\n"
894 : "/H /I /J /K /L /M /N /O\n"
895 : "/P /Q /R /S /T /U /V /W\n"
896 : "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n"
897 : "/grave /a /b /c /d /e /f /g\n"
898 : "/h /i /j /k /l /m /n /o\n"
899 : "/p /q /r /s /t /u /v /w\n"
900 : "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n"
901 : "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n"
902 : "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n"
903 : "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n"
904 : "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n"
905 : "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n"
906 : "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n"
907 : "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n"
908 : "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n"
909 : "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
910 : "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n"
911 : "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n"
912 : "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n"
913 : "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n"
914 : "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
915 : "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n"
916 : "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n"
917 : "\n"
918 : "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n"
919 : "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n"
920 : "currentdict end exch pop definefont pop } def\n"
921 : "\n"
922 : "/pathdict dup 8 dict def load begin\n"
923 : "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n"
924 : "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n"
925 : "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n"
926 : "eq 3 1 roll exch } def\n"
927 : "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n"
928 : "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n"
929 : "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n"
930 : "for 256 div exch pop exch { neg } if } def\n"
931 : "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n"
932 : "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n"
933 : "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n"
934 : "\n"
935 : "systemdict /languagelevel known not {\n"
936 : "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n"
937 : "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n"
938 : "roll show moveto 0 rmoveto } for pop pop } def\n"
939 : "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n"
940 : "rlineto closepath } def\n"
941 : "/rectfill { rectangle fill } def\n"
942 : "/rectstroke { rectangle stroke } def } if\n"
943 : "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n"
944 : "setlinewidth false charpath stroke setlinewidth } def\n"
945 : "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n"
946 : "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n"
947 : "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n"
948 : "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n"
949 : "\n"
950 : "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n"
951 : "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n"
952 : "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n"
953 : "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n"
954 : "/psp_imagedict {\n"
955 : "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n"
956 : "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n"
957 : "def 7 dict dup\n"
958 : "/ImageType 1 put dup\n"
959 : "/Width 7 -1 roll put dup\n"
960 : "/Height 5 index put dup\n"
961 : "/BitsPerComponent 4 index psp_bitspercomponent put dup\n"
962 : "/Decode 5 -1 roll psp_decodearray put dup\n"
963 : "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n"
964 : "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n"
965 : "} def\n"
966 : "%%EndResource\n"
967 : "%%EndProlog\n"
968 : };
969 0 : WritePS (pFile, pProlog);
970 :
971 0 : return true;
972 : }
973 :
974 0 : bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob )
975 : {
976 0 : WritePS (pFile, "%%BeginSetup\n%\n");
977 :
978 : // download fonts
979 0 : std::list< rtl::OString > aFonts[2];
980 0 : m_pGraphics->writeResources( pFile, aFonts[0], aFonts[1] );
981 :
982 0 : for( int i = 0; i < 2; i++ )
983 : {
984 0 : if( !aFonts[i].empty() )
985 : {
986 0 : std::list< rtl::OString >::const_iterator it = aFonts[i].begin();
987 0 : rtl::OStringBuffer aLine( 256 );
988 0 : if( i == 0 )
989 0 : aLine.append( "%%DocumentSuppliedResources: font " );
990 : else
991 0 : aLine.append( "%%DocumentNeededResources: font " );
992 0 : aLine.append( *it );
993 0 : aLine.append( "\n" );
994 0 : WritePS ( pFile, aLine.getStr() );
995 0 : while( (++it) != aFonts[i].end() )
996 : {
997 0 : aLine.setLength(0);
998 0 : aLine.append( "%%+ font " );
999 0 : aLine.append( *it );
1000 0 : aLine.append( "\n" );
1001 0 : WritePS ( pFile, aLine.getStr() );
1002 0 : }
1003 : }
1004 : }
1005 :
1006 0 : bool bSuccess = true;
1007 : // in case of external print dialog the number of copies is prepended
1008 : // to the job, let us not complicate things by emitting our own copy count
1009 0 : bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" );
1010 0 : if( ! bExternalDialog && rJob.m_nCopies > 1 )
1011 : {
1012 : // setup code
1013 0 : rtl::OStringBuffer aLine(RTL_CONSTASCII_STRINGPARAM("/#copies "));
1014 0 : aLine.append(static_cast<sal_Int32>(rJob.m_nCopies));
1015 0 : aLine.append(RTL_CONSTASCII_STRINGPARAM(" def\n"));
1016 0 : sal_uInt64 nWritten = 0;
1017 0 : bSuccess = pFile->write(aLine.getStr(), aLine.getLength(), nWritten)
1018 0 : || nWritten != static_cast<sal_uInt64>(aLine.getLength()) ?
1019 0 : false : true;
1020 :
1021 0 : if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 )
1022 0 : WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" );
1023 : }
1024 :
1025 0 : bool bFeatureSuccess = writeFeatureList( pFile, rJob, true );
1026 :
1027 0 : WritePS (pFile, "%%EndSetup\n");
1028 :
1029 0 : return bSuccess && bFeatureSuccess;
1030 : }
1031 :
1032 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|