merge the formfield patch from ooo-build
[ooovba.git] / vcl / unx / source / printergfx / printerjob.cxx
blobc1d50103c3f5e53c7a094f095e6956c55c83bf46
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: printerjob.cxx,v $
10 * $Revision: 1.47 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <pwd.h>
41 #include "psputil.hxx"
42 #include "glyphset.hxx"
44 #include "vcl/printerjob.hxx"
45 #include "vcl/ppdparser.hxx"
46 #include "vcl/strhelper.hxx"
47 #include "vcl/printerinfomanager.hxx"
48 #include "vcl/printergfx.hxx"
50 #include "rtl/ustring.hxx"
51 #include "rtl/strbuf.hxx"
52 #include "rtl/ustrbuf.hxx"
54 #include "osl/thread.h"
55 #include "sal/alloca.h"
57 #include <algorithm>
58 #include <vector>
60 using namespace psp;
61 using namespace rtl;
63 // forward declaration
65 #define nBLOCKSIZE 0x2000
67 namespace psp
70 sal_Bool
71 AppendPS (FILE* pDst, osl::File* pSrc, sal_uChar* pBuffer,
72 sal_uInt32 nBlockSize = nBLOCKSIZE)
74 if ((pDst == NULL) || (pSrc == NULL))
75 return sal_False;
77 if (nBlockSize == 0)
78 nBlockSize = nBLOCKSIZE;
79 if (pBuffer == NULL)
80 pBuffer = (sal_uChar*)alloca (nBlockSize);
82 pSrc->setPos (osl_Pos_Absolut, 0);
84 sal_uInt64 nIn = 0;
85 sal_uInt64 nOut = 0;
88 pSrc->read (pBuffer, nBlockSize, nIn);
89 if (nIn > 0)
90 nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst);
92 while ((nIn > 0) && (nIn == nOut));
94 return sal_True;
97 } // namespace psp
100 * private convenience routines for file handling
103 osl::File*
104 PrinterJob::CreateSpoolFile (const rtl::OUString& rName, const rtl::OUString& rExtension)
106 osl::File::RC nError = osl::File::E_None;
107 osl::File* pFile = NULL;
109 rtl::OUString aFile = rName + rExtension;
110 rtl::OUString aFileURL;
111 nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL );
112 if (nError != osl::File::E_None)
113 return NULL;
114 aFileURL = maSpoolDirName + rtl::OUString::createFromAscii ("/") + aFileURL;
116 pFile = new osl::File (aFileURL);
117 nError = pFile->open (OpenFlag_Read | OpenFlag_Write | OpenFlag_Create);
118 if (nError != osl::File::E_None)
120 delete pFile;
121 return NULL;
124 pFile->setAttributes (aFileURL,
125 osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead);
126 return pFile;
130 * public methods of PrinterJob: for use in PrinterGfx
133 void
134 PrinterJob::GetScale (double &rXScale, double &rYScale) const
136 rXScale = mfXScale;
137 rYScale = mfYScale;
140 sal_uInt16
141 PrinterJob::GetDepth () const
143 sal_Int32 nLevel = GetPostscriptLevel();
144 sal_Bool bColor = IsColorPrinter ();
146 return nLevel > 1 && bColor ? 24 : 8;
149 sal_uInt16
150 PrinterJob::GetPostscriptLevel (const JobData *pJobData) const
152 sal_uInt16 nPSLevel = 2;
154 if( pJobData == NULL )
155 pJobData = &m_aLastJobData;
157 if( pJobData->m_nPSLevel )
158 nPSLevel = pJobData->m_nPSLevel;
159 else
160 if( pJobData->m_pParser )
161 nPSLevel = pJobData->m_pParser->getLanguageLevel();
163 return nPSLevel;
166 sal_Bool
167 PrinterJob::IsColorPrinter () const
169 sal_Bool bColor = sal_False;
171 if( m_aLastJobData.m_nColorDevice )
172 bColor = m_aLastJobData.m_nColorDevice == -1 ? sal_False : sal_True;
173 else if( m_aLastJobData.m_pParser )
174 bColor = m_aLastJobData.m_pParser->isColorDevice() ? sal_True : sal_False;
176 return bColor;
179 osl::File*
180 PrinterJob::GetDocumentHeader ()
182 return mpJobHeader;
185 osl::File*
186 PrinterJob::GetDocumentTrailer ()
188 return mpJobTrailer;
191 osl::File*
192 PrinterJob::GetCurrentPageHeader ()
194 return maHeaderList.back();
197 osl::File*
198 PrinterJob::GetCurrentPageBody ()
200 return maPageList.back();
204 * public methods of PrinterJob: the actual job / spool handling
207 PrinterJob::PrinterJob () :
208 mpJobHeader( NULL ),
209 mpJobTrailer( NULL ),
210 m_bQuickJob( false )
214 namespace psp
217 /* check whether the given name points to a directory which is
218 usable for the user */
219 sal_Bool
220 existsTmpDir (const char* pName)
222 struct stat aFileStatus;
224 if (pName == NULL)
225 return sal_False;
226 if (stat(pName, &aFileStatus) != 0)
227 return sal_False;
228 if (! S_ISDIR(aFileStatus.st_mode))
229 return sal_False;
231 return access(pName, W_OK | R_OK) == 0 ? sal_True : sal_False;
234 /* return the username in the given buffer */
235 sal_Bool
236 getUserName (char* pName, int nSize)
238 struct passwd *pPWEntry;
239 struct passwd aPWEntry;
240 sal_Char pPWBuffer[256];
242 sal_Bool bSuccess = sal_False;
244 #ifdef FREEBSD
245 pPWEntry = getpwuid( getuid());
246 #else
247 if (getpwuid_r(getuid(), &aPWEntry, pPWBuffer, sizeof(pPWBuffer), &pPWEntry) != 0)
248 pPWEntry = NULL;
249 #endif
251 if (pPWEntry != NULL && pPWEntry->pw_name != NULL)
253 sal_Int32 nLen = strlen(pPWEntry->pw_name);
254 if (nLen > 0 && nLen < nSize)
256 memcpy (pName, pPWEntry->pw_name, nLen);
257 pName[nLen] = '\0';
259 bSuccess = sal_True;
263 // wipe the passwd off the stack
264 memset (pPWBuffer, 0, sizeof(pPWBuffer));
266 return bSuccess;
269 /* remove all our temporary files, uses external program "rm", since
270 osl functionality is inadequate */
271 void
272 removeSpoolDir (const rtl::OUString& rSpoolDir)
274 rtl::OUString aSysPath;
275 if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) )
277 // Conversion did not work, as this is quite a dangerous action,
278 // we should abort here ....
279 OSL_ENSURE( 0, "psprint: couldn't remove spool directory" );
280 return;
282 rtl::OString aSysPathByte =
283 rtl::OUStringToOString (aSysPath, osl_getThreadTextEncoding());
284 sal_Char pSystem [128];
285 sal_Int32 nChar = 0;
287 nChar = psp::appendStr ("rm -rf ", pSystem);
288 nChar += psp::appendStr (aSysPathByte.getStr(), pSystem + nChar);
290 system (pSystem);
293 /* creates a spool directory with a "pidgin random" value based on
294 current system time */
295 rtl::OUString
296 createSpoolDir ()
298 TimeValue aCur;
299 osl_getSystemTime( &aCur );
300 sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000);
302 rtl::OUString aTmpDir;
303 osl_getTempDirURL( &aTmpDir.pData );
307 rtl::OUStringBuffer aDir( aTmpDir.getLength() + 16 );
308 aDir.append( aTmpDir );
309 aDir.appendAscii( "/psp" );
310 aDir.append(nRand);
311 rtl::OUString aResult = aDir.makeStringAndClear();
312 if( osl::Directory::create( aResult ) == osl::FileBase::E_None )
314 osl::File::setAttributes( aResult,
315 osl_File_Attribute_OwnWrite
316 | osl_File_Attribute_OwnRead
317 | osl_File_Attribute_OwnExe );
318 return aResult;
320 nRand++;
321 } while( nRand );
322 return rtl::OUString();
325 } // namespace psp
327 PrinterJob::~PrinterJob ()
329 std::list< osl::File* >::iterator pPage;
330 for (pPage = maPageList.begin(); pPage != maPageList.end(); pPage++)
332 //(*pPage)->remove();
333 delete *pPage;
335 for (pPage = maHeaderList.begin(); pPage != maHeaderList.end(); pPage++)
337 //(*pPage)->remove();
338 delete *pPage;
340 // mpJobHeader->remove();
341 delete mpJobHeader;
342 // mpJobTrailer->remove();
343 delete mpJobTrailer;
345 // XXX should really call osl::remove routines
346 removeSpoolDir (maSpoolDirName);
348 // osl::Directory::remove (maSpoolDirName);
351 namespace psp
354 // get locale invariant, 7bit clean current local time string
355 sal_Char*
356 getLocalTime(sal_Char* pBuffer)
358 time_t nTime = time (NULL);
359 struct tm aTime;
360 struct tm *pLocalTime = localtime_r (&nTime, &aTime);
362 return asctime_r(pLocalTime, pBuffer);
367 static bool isAscii( const rtl::OUString& rStr )
369 const sal_Unicode* pStr = rStr;
370 sal_Int32 nLen = rStr.getLength();
371 for( sal_Int32 i = 0; i < nLen; i++ )
372 if( pStr[i] > 127 )
373 return false;
374 return true;
377 sal_Bool
378 PrinterJob::StartJob (
379 const rtl::OUString& rFileName,
380 int nMode,
381 const rtl::OUString& rJobName,
382 const rtl::OUString& rAppName,
383 const JobData& rSetupData,
384 PrinterGfx* pGraphics,
385 bool bIsQuickJob
388 m_bQuickJob = bIsQuickJob;
389 mnMaxWidthPt = mnMaxHeightPt = 0;
390 mnLandscapes = mnPortraits = 0;
391 m_pGraphics = pGraphics;
392 InitPaperSize (rSetupData);
394 // create file container for document header and trailer
395 maFileName = rFileName;
396 mnFileMode = nMode;
397 maSpoolDirName = createSpoolDir ();
398 maJobTitle = rJobName;
400 rtl::OUString aExt = rtl::OUString::createFromAscii (".ps");
401 mpJobHeader = CreateSpoolFile (rtl::OUString::createFromAscii("psp_head"), aExt);
402 mpJobTrailer = CreateSpoolFile (rtl::OUString::createFromAscii("psp_tail"), aExt);
403 if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor
404 return sal_False;
406 // write document header according to Document Structuring Conventions (DSC)
407 WritePS (mpJobHeader,
408 "%!PS-Adobe-3.0\n"
409 "%%BoundingBox: (atend)\n" );
411 rtl::OUString aFilterWS;
413 // Creator (this application)
414 aFilterWS = WhitespaceToSpace( rAppName, FALSE );
415 WritePS (mpJobHeader, "%%Creator: (");
416 WritePS (mpJobHeader, aFilterWS);
417 WritePS (mpJobHeader, ")\n");
419 // For (user name)
420 sal_Char pUserName[64];
421 if (getUserName(pUserName, sizeof(pUserName)))
423 WritePS (mpJobHeader, "%%For: (");
424 WritePS (mpJobHeader, pUserName);
425 WritePS (mpJobHeader, ")\n");
428 // Creation Date (locale independent local time)
429 sal_Char pCreationDate [256];
430 WritePS (mpJobHeader, "%%CreationDate: (");
431 getLocalTime(pCreationDate);
432 for( unsigned int i = 0; i < sizeof(pCreationDate)/sizeof(pCreationDate[0]); i++ )
434 if( pCreationDate[i] == '\n' )
436 pCreationDate[i] = 0;
437 break;
440 WritePS (mpJobHeader, pCreationDate );
441 WritePS (mpJobHeader, ")\n");
443 // Document Title
444 /* #i74335#
445 * The title should be clean ascii; rJobName however may
446 * contain any Unicode character. So implement the following
447 * algorithm:
448 * use rJobName, if it contains only ascii
449 * use the filename, if it contains only ascii
450 * else omit %%Title
452 aFilterWS = WhitespaceToSpace( rJobName, FALSE );
453 rtl::OUString aTitle( aFilterWS );
454 if( ! isAscii( aTitle ) )
456 sal_Int32 nIndex = 0;
457 while( nIndex != -1 )
458 aTitle = rFileName.getToken( 0, '/', nIndex );
459 aTitle = WhitespaceToSpace( aTitle, FALSE );
460 if( ! isAscii( aTitle ) )
461 aTitle = rtl::OUString();
464 maJobTitle = aFilterWS;
465 if( aTitle.getLength() )
467 WritePS (mpJobHeader, "%%Title: (");
468 WritePS (mpJobHeader, aTitle);
469 WritePS (mpJobHeader, ")\n");
472 // Language Level
473 sal_Char pLevel[16];
474 sal_Int32 nSz = getValueOf(GetPostscriptLevel(&rSetupData), pLevel);
475 pLevel[nSz++] = '\n';
476 pLevel[nSz ] = '\0';
477 WritePS (mpJobHeader, "%%LanguageLevel: ");
478 WritePS (mpJobHeader, pLevel);
480 // Other
481 WritePS (mpJobHeader, "%%DocumentData: Clean7Bit\n");
482 WritePS (mpJobHeader, "%%Pages: (atend)\n");
483 WritePS (mpJobHeader, "%%Orientation: (atend)\n");
484 WritePS (mpJobHeader, "%%PageOrder: Ascend\n");
485 WritePS (mpJobHeader, "%%EndComments\n");
487 // write Prolog
488 writeProlog (mpJobHeader, rSetupData);
490 // mark last job setup as not set
491 m_aLastJobData.m_pParser = NULL;
492 m_aLastJobData.m_aContext.setParser( NULL );
494 return sal_True;
497 sal_Bool
498 PrinterJob::EndJob ()
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;
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());
525 * spool the set of files to their final destination, this is U**X dependent
528 FILE* pDestFILE = NULL;
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)
534 const rtl::OString aFileName = rtl::OUStringToOString (maFileName,
535 osl_getThreadTextEncoding());
536 if( mnFileMode )
538 int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode );
539 if( nFile != -1 )
541 pDestFILE = fdopen( nFile, "w" );
542 if( pDestFILE == NULL )
544 close( nFile );
545 unlink( aFileName.getStr() );
546 return sal_False;
549 else
550 chmod( aFileName.getStr(), mnFileMode );
552 if (pDestFILE == NULL)
553 pDestFILE = fopen (aFileName.getStr(), "w");
555 if (pDestFILE == NULL)
556 return sal_False;
558 else
560 PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get ();
561 pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob );
562 if (pDestFILE == NULL)
563 return sal_False;
566 /* spool the document parts to the destination */
568 sal_uChar pBuffer[ nBLOCKSIZE ];
570 AppendPS (pDestFILE, mpJobHeader, pBuffer);
571 mpJobHeader->close();
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++)
580 if( *pPageHead )
582 osl::File::RC nError = (*pPageHead)->open(OpenFlag_Read);
583 if (nError == osl::File::E_None)
585 AppendPS (pDestFILE, *pPageHead, pBuffer);
586 (*pPageHead)->close();
589 else
590 bSuccess = sal_False;
591 if( *pPageBody )
593 osl::File::RC nError = (*pPageBody)->open(OpenFlag_Read);
594 if (nError == osl::File::E_None)
596 AppendPS (pDestFILE, *pPageBody, pBuffer);
597 (*pPageBody)->close();
600 else
601 bSuccess = sal_False;
604 AppendPS (pDestFILE, mpJobTrailer, pBuffer);
605 mpJobTrailer->close();
607 /* well done */
609 if (bSpoolToFile)
610 fclose (pDestFILE);
611 else
613 PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get();
614 if (0 == rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName,
615 maJobTitle, pDestFILE, m_aDocumentJobData ))
617 bSuccess = sal_False;
621 return bSuccess;
624 sal_Bool
625 PrinterJob::AbortJob ()
627 m_pGraphics->OnEndJob();
628 return sal_False;
631 void
632 PrinterJob::InitPaperSize (const JobData& rJobSetup)
634 int nRes = rJobSetup.m_aContext.getRenderResolution ();
636 String aPaper;
637 int nWidth, nHeight;
638 rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight);
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);
645 mnResolution = nRes;
647 mnWidthPt = nWidth;
648 mnHeightPt = nHeight;
650 if( mnWidthPt > mnMaxWidthPt )
651 mnMaxWidthPt = mnWidthPt;
652 if( mnHeightPt > mnMaxHeightPt )
653 mnMaxHeightPt = mnHeightPt;
655 mnLMarginPt = nLeft;
656 mnRMarginPt = nRight;
657 mnTMarginPt = nUpper;
658 mnBMarginPt = nLower;
660 mfXScale = (double)72.0 / (double)mnResolution;
661 mfYScale = -1.0 * (double)72.0 / (double)mnResolution;
665 sal_Bool
666 PrinterJob::StartPage (const JobData& rJobSetup)
668 InitPaperSize (rJobSetup);
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");
673 osl::File* pPageHeader = CreateSpoolFile (
674 rtl::OUString::createFromAscii("psp_pghead"), aExt);
675 osl::File* pPageBody = CreateSpoolFile (
676 rtl::OUString::createFromAscii("psp_pgbody"), aExt);
678 maHeaderList.push_back (pPageHeader);
679 maPageList.push_back (pPageBody);
681 if( ! (pPageHeader && pPageBody) )
682 return sal_False;
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");
691 if( rJobSetup.m_eOrientation == orientation::Landscape )
693 WritePS (pPageHeader, "%%PageOrientation: Landscape\n");
694 mnLandscapes++;
696 else
698 WritePS (pPageHeader, "%%PageOrientation: Portrait\n");
699 mnPortraits++;
702 sal_Char pBBox [256];
703 sal_Int32 nChar = 0;
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);
715 WritePS (pPageHeader, pBBox);
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.
722 bool bWriteFeatures = true;
723 if( 1 == maPageList.size() )
725 m_aDocumentJobData = rJobSetup;
726 bWriteFeatures = false;
729 if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) )
731 m_aLastJobData = rJobSetup;
732 return true;
735 return false;
738 sal_Bool
739 PrinterJob::EndPage ()
741 m_pGraphics->OnEndPage();
743 osl::File* pPageHeader = maHeaderList.back();
744 osl::File* pPageBody = maPageList.back();
746 if( ! (pPageBody && pPageHeader) )
747 return sal_False;
749 // copy page to paper and write page trailer according to DSC
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);
758 // this page is done for now, close it to avoid having too many open fd's
760 pPageHeader->close();
761 pPageBody->close();
763 return sal_True;
766 sal_uInt32
767 PrinterJob::GetErrorCode ()
769 /* TODO */
770 return 0;
773 struct less_ppd_key : public ::std::binary_function<double, double, bool>
775 bool operator()(const PPDKey* left, const PPDKey* right)
776 { return left->getOrderDependency() < right->getOrderDependency(); }
779 static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature )
781 if( ! pKey || ! pValue )
782 return true;
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 )
796 aFeature.append( '\n' );
797 aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) );
798 aFeature.append( "\n%%EndFeature" );
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;
806 bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup )
808 bool bSuccess = true;
809 int i;
811 // emit features ordered to OrderDependency
812 // ignore features that are set to default
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 )
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() );
826 for( i = 0; i < nKeys && bSuccess; i++ )
828 const PPDKey* pKey = aKeys[i];
829 bool bEmit = false;
830 if( bDocumentSetup )
832 if( pKey->getSetupType() == PPDKey::DocumentSetup )
833 bEmit = true;
835 if( pKey->getSetupType() == PPDKey::PageSetup ||
836 pKey->getSetupType() == PPDKey::AnySetup )
837 bEmit = true;
838 if( bEmit )
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
849 // try to avoid PS level 2 feature commands if level is set to 1
850 if( GetPostscriptLevel( &rJob ) == 1 )
852 bool bHavePS2 =
853 ( pValue->m_aValue.SearchAscii( "<<" ) != STRING_NOTFOUND )
855 ( pValue->m_aValue.SearchAscii( ">>" ) != STRING_NOTFOUND );
856 if( bHavePS2 )
857 continue;
859 bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() );
864 else
865 bSuccess = false;
867 return bSuccess;
870 bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob, bool bWriteFeatures )
872 bool bSuccess = true;
874 WritePS (pFile, "%%BeginPageSetup\n%\n");
875 if ( bWriteFeatures )
876 bSuccess = writeFeatureList( pFile, rJob, false );
877 WritePS (pFile, "%%EndPageSetup\n");
879 sal_Char pTranslate [128];
880 sal_Int32 nChar = 0;
882 if( rJob.m_eOrientation == orientation::Portrait )
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);
896 else
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);
911 WritePS (pFile, pTranslate);
913 return bSuccess;
916 void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData )
918 const PPDKey* pKey = NULL;
920 if( rJobData.m_pParser )
921 pKey = rJobData.m_pParser->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "JobPatchFile" ) ) );
922 if( ! pKey )
923 return;
925 // order the patch files
926 // according to PPD spec the JobPatchFile options must be int
927 // and should be emitted in order
928 std::list< sal_Int32 > patch_order;
929 int nValueCount = pKey->countValues();
930 for( int i = 0; i < nValueCount; i++ )
932 const PPDValue* pVal = pKey->getValue( i );
933 patch_order.push_back( pVal->m_aOption.ToInt32() );
934 if( patch_order.back() == 0 && ! pVal->m_aOption.EqualsAscii( "0" ) )
936 WritePS( pFile, "% Warning: left out JobPatchFile option \"" );
937 OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US );
938 WritePS( pFile, aOption.getStr() );
939 WritePS( pFile,
940 "\"\n% as it violates the PPD spec;\n"
941 "% JobPatchFile options need to be numbered for ordering.\n" );
945 patch_order.sort();
946 patch_order.unique();
948 while( patch_order.begin() != patch_order.end() )
950 // note: this discards patch files not adhering to the "int" scheme
951 // as there won't be a value for them
952 writeFeature( pFile, pKey, pKey->getValue( OUString::valueOf( patch_order.front() ) ), false );
953 patch_order.pop_front();
957 bool PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData )
959 WritePS( pFile, "%%BeginProlog\n" );
961 // JobPatchFile feature needs to be emitted at begin of prolog
962 writeJobPatch( pFile, rJobData );
964 static const sal_Char pProlog[] = {
965 "%%BeginResource: procset PSPrint-Prolog 1.0 0\n"
966 "/ISO1252Encoding [\n"
967 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
968 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
969 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
970 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
971 "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n"
972 "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n"
973 "/zero /one /two /three /four /five /six /seven\n"
974 "/eight /nine /colon /semicolon /less /equal /greater /question\n"
975 "/at /A /B /C /D /E /F /G\n"
976 "/H /I /J /K /L /M /N /O\n"
977 "/P /Q /R /S /T /U /V /W\n"
978 "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n"
979 "/grave /a /b /c /d /e /f /g\n"
980 "/h /i /j /k /l /m /n /o\n"
981 "/p /q /r /s /t /u /v /w\n"
982 "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n"
983 "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n"
984 "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n"
985 "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n"
986 "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n"
987 "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n"
988 "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n"
989 "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n"
990 "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n"
991 "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
992 "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n"
993 "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n"
994 "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n"
995 "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n"
996 "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
997 "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n"
998 "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n"
999 "\n"
1000 "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n"
1001 "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n"
1002 "currentdict end exch pop definefont pop } def\n"
1003 "\n"
1004 "/pathdict dup 8 dict def load begin\n"
1005 "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n"
1006 "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n"
1007 "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n"
1008 "eq 3 1 roll exch } def\n"
1009 "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n"
1010 "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n"
1011 "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n"
1012 "for 256 div exch pop exch { neg } if } def\n"
1013 "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n"
1014 "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n"
1015 "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n"
1016 "\n"
1017 "systemdict /languagelevel known not {\n"
1018 "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n"
1019 "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n"
1020 "roll show moveto 0 rmoveto } for pop pop } def\n"
1021 "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n"
1022 "rlineto closepath } def\n"
1023 "/rectfill { rectangle fill } def\n"
1024 "/rectstroke { rectangle stroke } def } if\n"
1025 "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n"
1026 "setlinewidth false charpath stroke setlinewidth } def\n"
1027 "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n"
1028 "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n"
1029 "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n"
1030 "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n"
1031 "\n"
1032 "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n"
1033 "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n"
1034 "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n"
1035 "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n"
1036 "/psp_imagedict {\n"
1037 "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n"
1038 "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n"
1039 "def 7 dict dup\n"
1040 "/ImageType 1 put dup\n"
1041 "/Width 7 -1 roll put dup\n"
1042 "/Height 5 index put dup\n"
1043 "/BitsPerComponent 4 index psp_bitspercomponent put dup\n"
1044 "/Decode 5 -1 roll psp_decodearray put dup\n"
1045 "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n"
1046 "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n"
1047 "} def\n"
1048 "%%EndResource\n"
1049 "%%EndProlog\n"
1051 static const sal_Char pSO52CompatProlog[] = {
1052 "%%BeginResource: procset PSPrint-Prolog 1.0 0\n"
1053 "/ISO1252Encoding [\n"
1054 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
1055 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
1056 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
1057 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
1058 "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n"
1059 "/parenleft /parenright /asterisk /plus /comma /minus /period /slash\n"
1060 "/zero /one /two /three /four /five /six /seven\n"
1061 "/eight /nine /colon /semicolon /less /equal /greater /question\n"
1062 "/at /A /B /C /D /E /F /G\n"
1063 "/H /I /J /K /L /M /N /O\n"
1064 "/P /Q /R /S /T /U /V /W\n"
1065 "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n"
1066 "/grave /a /b /c /d /e /f /g\n"
1067 "/h /i /j /k /l /m /n /o\n"
1068 "/p /q /r /s /t /u /v /w\n"
1069 "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n"
1070 "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n"
1071 "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n"
1072 "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n"
1073 "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n"
1074 "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n"
1075 "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n"
1076 "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n"
1077 "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n"
1078 "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
1079 "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n"
1080 "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n"
1081 "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n"
1082 "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n"
1083 "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
1084 "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n"
1085 "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n"
1086 "\n"
1087 "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n"
1088 "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n"
1089 "currentdict end exch pop definefont pop } def\n"
1090 "\n"
1091 "/pathdict dup 8 dict def load begin\n"
1092 "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n"
1093 "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n"
1094 "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n"
1095 "eq 3 1 roll exch } def\n"
1096 "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n"
1097 "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n"
1098 "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n"
1099 "for 256 div exch pop exch { neg } if } def\n"
1100 "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n"
1101 "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n"
1102 "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n"
1103 "\n"
1104 "systemdict /languagelevel known not {\n"
1105 "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n"
1106 "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n"
1107 "roll show moveto 0 rmoveto } for pop pop } def\n"
1108 "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n"
1109 "rlineto closepath } def\n"
1110 "/rectfill { rectangle fill } def\n"
1111 "/rectstroke { rectangle stroke } def } if\n"
1112 "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n"
1113 "setlinewidth false charpath stroke setlinewidth } def\n"
1114 "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n"
1115 "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n"
1116 "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n"
1117 "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n"
1118 "\n"
1119 "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n"
1120 "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n"
1121 "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n"
1122 "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n"
1123 "/psp_imagedict {\n"
1124 "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n"
1125 "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n"
1126 "def 7 dict dup\n"
1127 "/ImageType 1 put dup\n"
1128 "/Width 7 -1 roll put dup\n"
1129 "/Height 5 index put dup\n"
1130 "/BitsPerComponent 4 index psp_bitspercomponent put dup\n"
1131 "/Decode 5 -1 roll psp_decodearray put dup\n"
1132 "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n"
1133 "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n"
1134 "} def\n"
1135 "%%EndResource\n"
1136 "%%EndProlog\n"
1138 WritePS (pFile, m_pGraphics && m_pGraphics->getStrictSO52Compatibility() ? pSO52CompatProlog : pProlog);
1140 return true;
1143 bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob )
1145 WritePS (pFile, "%%BeginSetup\n%\n");
1147 // download fonts
1148 std::list< rtl::OString > aFonts[2];
1149 m_pGraphics->writeResources( pFile, aFonts[0], aFonts[1] );
1151 for( int i = 0; i < 2; i++ )
1153 if( !aFonts[i].empty() )
1155 std::list< rtl::OString >::const_iterator it = aFonts[i].begin();
1156 rtl::OStringBuffer aLine( 256 );
1157 if( i == 0 )
1158 aLine.append( "%%DocumentSuppliedResources: font " );
1159 else
1160 aLine.append( "%%DocumentNeededResources: font " );
1161 aLine.append( *it );
1162 aLine.append( "\n" );
1163 WritePS ( pFile, aLine.getStr() );
1164 while( (++it) != aFonts[i].end() )
1166 aLine.setLength(0);
1167 aLine.append( "%%+ font " );
1168 aLine.append( *it );
1169 aLine.append( "\n" );
1170 WritePS ( pFile, aLine.getStr() );
1175 bool bSuccess = true;
1176 // in case of external print dialog the number of copies is prepended
1177 // to the job, let us not complicate things by emitting our own copy count
1178 bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" );
1179 if( ! bExternalDialog && rJob.m_nCopies > 1 )
1181 // setup code
1182 ByteString aLine( "/#copies " );
1183 aLine += ByteString::CreateFromInt32( rJob.m_nCopies );
1184 aLine += " def\n";
1185 sal_uInt64 nWritten = 0;
1186 bSuccess = pFile->write( aLine.GetBuffer(), aLine.Len(), nWritten )
1187 || nWritten != aLine.Len() ? false : true;
1189 if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 )
1190 WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" );
1193 bool bFeatureSuccess = writeFeatureList( pFile, rJob, true );
1195 WritePS (pFile, "%%EndSetup\n");
1197 return bSuccess && bFeatureSuccess;