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