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