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