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