Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / generic / print / printerjob.cxx
blob7d805ef942e5228533dc8321d383a666fc6793bc
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 .
20 #include <stdio.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
25 #include "psputil.hxx"
27 #include <unx/printerjob.hxx>
28 #include <unx/printergfx.hxx>
29 #include <vcl/ppdparser.hxx>
30 #include <vcl/strhelper.hxx>
31 #include <printerinfomanager.hxx>
33 #include <rtl/ustring.hxx>
34 #include <rtl/strbuf.hxx>
35 #include <rtl/ustrbuf.hxx>
37 #include <osl/thread.h>
38 #include <osl/security.hxx>
40 #include <algorithm>
41 #include <deque>
42 #include <vector>
44 using namespace psp;
46 #define nBLOCKSIZE 0x2000
48 namespace psp
51 static bool
52 AppendPS (FILE* pDst, osl::File* pSrc, unsigned char* pBuffer)
54 assert(pBuffer);
55 if ((pDst == nullptr) || (pSrc == nullptr))
56 return false;
58 if (pSrc->setPos(osl_Pos_Absolut, 0) != osl::FileBase::E_None)
59 return false;
61 sal_uInt64 nIn = 0;
62 sal_uInt64 nOut = 0;
65 pSrc->read (pBuffer, nBLOCKSIZE, nIn);
66 if (nIn > 0)
67 nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst);
69 while ((nIn > 0) && (nIn == nOut));
71 return true;
74 } // namespace psp
77 * private convenience routines for file handling
80 std::unique_ptr<osl::File>
81 PrinterJob::CreateSpoolFile (const OUString& rName, const OUString& rExtension)
83 OUString aFile = rName + rExtension;
84 OUString aFileURL;
85 osl::File::RC nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL );
86 if (nError != osl::File::E_None)
87 return nullptr;
88 aFileURL = maSpoolDirName + "/" + aFileURL;
90 std::unique_ptr<osl::File> pFile( new osl::File (aFileURL) );
91 nError = pFile->open (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
92 if (nError != osl::File::E_None)
94 return nullptr;
97 osl::File::setAttributes (aFileURL,
98 osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead);
99 return pFile;
103 * public methods of PrinterJob: for use in PrinterGfx
106 void
107 PrinterJob::GetScale (double &rXScale, double &rYScale) const
109 rXScale = mfXScale;
110 rYScale = mfYScale;
113 sal_uInt16
114 PrinterJob::GetDepth () const
116 sal_Int32 nLevel = GetPostscriptLevel();
117 bool bColor = IsColorPrinter ();
119 return nLevel > 1 && bColor ? 24 : 8;
122 sal_uInt16
123 PrinterJob::GetPostscriptLevel (const JobData *pJobData) const
125 sal_uInt16 nPSLevel = 2;
127 if( pJobData == nullptr )
128 pJobData = &m_aLastJobData;
130 if( pJobData->m_nPSLevel )
131 nPSLevel = pJobData->m_nPSLevel;
132 else
133 if( pJobData->m_pParser )
134 nPSLevel = pJobData->m_pParser->getLanguageLevel();
136 return nPSLevel;
139 bool
140 PrinterJob::IsColorPrinter () const
142 bool bColor = false;
144 if( m_aLastJobData.m_nColorDevice )
145 bColor = m_aLastJobData.m_nColorDevice != -1;
146 else if( m_aLastJobData.m_pParser )
147 bColor = m_aLastJobData.m_pParser->isColorDevice();
149 return bColor;
152 osl::File*
153 PrinterJob::GetCurrentPageBody ()
155 return maPageVector.back().get();
159 * public methods of PrinterJob: the actual job / spool handling
161 PrinterJob::PrinterJob()
162 : mnFileMode(0)
163 , m_pGraphics(nullptr)
164 , mnResolution(96)
165 , mnWidthPt(0)
166 , mnHeightPt(0)
167 , mnMaxWidthPt(0)
168 , mnMaxHeightPt(0)
169 , mnLandscapes(0)
170 , mnPortraits(0)
171 , mnLMarginPt(0)
172 , mnRMarginPt(0)
173 , mnTMarginPt(0)
174 , mnBMarginPt(0)
175 , mfXScale(1)
176 , mfYScale(1)
177 , m_bQuickJob(false)
181 /* remove all our temporary files, uses external program "rm", since
182 osl functionality is inadequate */
183 static void
184 removeSpoolDir (const OUString& rSpoolDir)
186 OUString aSysPath;
187 if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) )
189 // Conversion did not work, as this is quite a dangerous action,
190 // we should abort here...
191 OSL_FAIL( "psprint: couldn't remove spool directory" );
192 return;
194 OString aSysPathByte =
195 OUStringToOString (aSysPath, osl_getThreadTextEncoding());
196 if (system (OString("rm -rf " + aSysPathByte).getStr()) == -1)
197 OSL_FAIL( "psprint: couldn't remove spool directory" );
200 /* creates a spool directory with a "pidgin random" value based on
201 current system time */
202 static OUString
203 createSpoolDir ()
205 TimeValue aCur;
206 osl_getSystemTime( &aCur );
207 sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000);
209 OUString aTmpDir;
210 osl_getTempDirURL( &aTmpDir.pData );
214 OUString aDir = aTmpDir + "/psp" + OUString::number(nRand);
215 if( osl::Directory::create( aDir ) == osl::FileBase::E_None )
217 osl::File::setAttributes( aDir,
218 osl_File_Attribute_OwnWrite
219 | osl_File_Attribute_OwnRead
220 | osl_File_Attribute_OwnExe );
221 return aDir;
223 nRand++;
224 } while( nRand );
225 return OUString();
228 PrinterJob::~PrinterJob ()
230 maPageVector.clear();
231 maHeaderVector.clear();
233 // mpJobHeader->remove();
234 mpJobHeader.reset();
235 // mpJobTrailer->remove();
236 mpJobTrailer.reset();
238 // XXX should really call osl::remove routines
239 if( !maSpoolDirName.isEmpty() )
240 removeSpoolDir (maSpoolDirName);
242 // osl::Directory::remove (maSpoolDirName);
245 static void WriteLocalTimePS( osl::File *rFile )
247 TimeValue aStartTime, tLocal;
248 oslDateTime date_time;
249 if (osl_getSystemTime( &aStartTime ) &&
250 osl_getLocalTimeFromSystemTime( &aStartTime, &tLocal ) &&
251 osl_getDateTimeFromTimeValue( &tLocal, &date_time ))
253 char ar[ 256 ];
254 snprintf(
255 ar, sizeof (ar),
256 "%04d-%02d-%02d %02d:%02d:%02d ",
257 date_time.Year, date_time.Month, date_time.Day,
258 date_time.Hours, date_time.Minutes, date_time.Seconds );
259 WritePS( rFile, ar );
261 else
262 WritePS( rFile, "Unknown-Time" );
265 static bool isAscii( const OUString& rStr )
267 sal_Int32 nLen = rStr.getLength();
268 for( sal_Int32 i = 0; i < nLen; i++ )
269 if( rStr[i] > 127 )
270 return false;
271 return true;
274 bool
275 PrinterJob::StartJob (
276 const OUString& rFileName,
277 int nMode,
278 const OUString& rJobName,
279 const OUString& rAppName,
280 const JobData& rSetupData,
281 PrinterGfx* pGraphics,
282 bool bIsQuickJob
285 m_bQuickJob = bIsQuickJob;
286 mnMaxWidthPt = mnMaxHeightPt = 0;
287 mnLandscapes = mnPortraits = 0;
288 m_pGraphics = pGraphics;
289 InitPaperSize (rSetupData);
291 // create file container for document header and trailer
292 maFileName = rFileName;
293 mnFileMode = nMode;
294 maSpoolDirName = createSpoolDir ();
295 maJobTitle = rJobName;
297 OUString aExt(".ps");
298 mpJobHeader = CreateSpoolFile ("psp_head", aExt);
299 mpJobTrailer = CreateSpoolFile ("psp_tail", aExt);
300 if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor
301 return false;
303 // write document header according to Document Structuring Conventions (DSC)
304 WritePS (mpJobHeader.get(),
305 "%!PS-Adobe-3.0\n"
306 "%%BoundingBox: (atend)\n" );
308 // Creator (this application)
309 OUString aFilterWS = WhitespaceToSpace( rAppName, false );
310 WritePS (mpJobHeader.get(), "%%Creator: (");
311 WritePS (mpJobHeader.get(), aFilterWS);
312 WritePS (mpJobHeader.get(), ")\n");
314 // For (user name)
315 osl::Security aSecurity;
316 OUString aUserName;
317 if( aSecurity.getUserName( aUserName ) )
319 WritePS (mpJobHeader.get(), "%%For: (");
320 WritePS (mpJobHeader.get(), aUserName);
321 WritePS (mpJobHeader.get(), ")\n");
324 // Creation Date (locale independent local time)
325 WritePS (mpJobHeader.get(), "%%CreationDate: (");
326 WriteLocalTimePS (mpJobHeader.get());
327 WritePS (mpJobHeader.get(), ")\n");
329 // Document Title
330 /* #i74335#
331 * The title should be clean ascii; rJobName however may
332 * contain any Unicode character. So implement the following
333 * algorithm:
334 * use rJobName, if it contains only ascii
335 * use the filename, if it contains only ascii
336 * else omit %%Title
338 aFilterWS = WhitespaceToSpace( rJobName, false );
339 OUString aTitle( aFilterWS );
340 if( ! isAscii( aTitle ) )
342 aTitle = WhitespaceToSpace( rFileName.copy(rFileName.lastIndexOf('/')+1), false );
343 if( ! isAscii( aTitle ) )
344 aTitle.clear();
347 maJobTitle = aFilterWS;
348 if( !aTitle.isEmpty() )
350 WritePS (mpJobHeader.get(), "%%Title: (");
351 WritePS (mpJobHeader.get(), aTitle);
352 WritePS (mpJobHeader.get(), ")\n");
355 // Language Level
356 OStringBuffer pLevel;
357 getValueOf(GetPostscriptLevel(&rSetupData), pLevel);
358 pLevel.append('\n');
359 WritePS (mpJobHeader.get(), "%%LanguageLevel: ");
360 WritePS (mpJobHeader.get(), pLevel.makeStringAndClear());
362 // Other
363 WritePS (mpJobHeader.get(), "%%DocumentData: Clean7Bit\n");
364 WritePS (mpJobHeader.get(), "%%Pages: (atend)\n");
365 WritePS (mpJobHeader.get(), "%%Orientation: (atend)\n");
366 WritePS (mpJobHeader.get(), "%%PageOrder: Ascend\n");
367 WritePS (mpJobHeader.get(), "%%EndComments\n");
369 // write Prolog
370 writeProlog (mpJobHeader.get(), rSetupData);
372 // mark last job setup as not set
373 m_aLastJobData.m_pParser = nullptr;
374 m_aLastJobData.m_aContext.setParser( nullptr );
376 return true;
379 bool
380 PrinterJob::EndJob()
382 // no pages ? that really means no print job
383 if( maPageVector.empty() )
384 return false;
386 // write document setup (done here because it
387 // includes the accumulated fonts
388 if( mpJobHeader )
389 writeSetup( mpJobHeader.get(), m_aDocumentJobData );
390 m_pGraphics->OnEndJob();
391 if( ! (mpJobHeader && mpJobTrailer) )
392 return false;
394 // write document trailer according to Document Structuring Conventions (DSC)
395 OStringBuffer aTrailer(512);
396 aTrailer.append( "%%Trailer\n" );
397 aTrailer.append( "%%BoundingBox: 0 0 " );
398 aTrailer.append( static_cast<sal_Int32>(mnMaxWidthPt) );
399 aTrailer.append( " " );
400 aTrailer.append( static_cast<sal_Int32>(mnMaxHeightPt) );
401 if( mnLandscapes > mnPortraits )
402 aTrailer.append("\n%%Orientation: Landscape");
403 else
404 aTrailer.append("\n%%Orientation: Portrait");
405 aTrailer.append( "\n%%Pages: " );
406 aTrailer.append( static_cast<sal_Int32>(maPageVector.size()) );
407 aTrailer.append( "\n%%EOF\n" );
408 WritePS (mpJobTrailer.get(), aTrailer.getStr());
411 * spool the set of files to their final destination, this is U**X dependent
414 FILE* pDestFILE = nullptr;
416 /* create a destination either as file or as a pipe */
417 bool bSpoolToFile = !maFileName.isEmpty();
418 if (bSpoolToFile)
420 const OString aFileName = OUStringToOString (maFileName,
421 osl_getThreadTextEncoding());
422 if( mnFileMode )
424 int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode );
425 if( nFile != -1 )
427 pDestFILE = fdopen( nFile, "w" );
428 if( pDestFILE == nullptr )
430 close( nFile );
431 unlink( aFileName.getStr() );
432 return false;
435 else
437 (void)chmod( aFileName.getStr(), mnFileMode );
440 if (pDestFILE == nullptr)
441 pDestFILE = fopen (aFileName.getStr(), "w");
443 if (pDestFILE == nullptr)
444 return false;
446 else
448 PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get ();
449 pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob );
450 if (pDestFILE == nullptr)
451 return false;
454 /* spool the document parts to the destination */
456 unsigned char pBuffer[ nBLOCKSIZE ];
458 AppendPS (pDestFILE, mpJobHeader.get(), pBuffer);
459 mpJobHeader->close();
461 bool bSuccess = true;
462 std::vector< std::unique_ptr<osl::File> >::iterator pPageBody;
463 std::vector< std::unique_ptr<osl::File> >::iterator pPageHead;
464 for (pPageBody = maPageVector.begin(), pPageHead = maHeaderVector.begin();
465 pPageBody != maPageVector.end() && pPageHead != maHeaderVector.end();
466 ++pPageBody, ++pPageHead)
468 if( *pPageHead )
470 osl::File::RC nError = (*pPageHead)->open(osl_File_OpenFlag_Read);
471 if (nError == osl::File::E_None)
473 AppendPS (pDestFILE, pPageHead->get(), pBuffer);
474 (*pPageHead)->close();
477 else
478 bSuccess = false;
479 if( *pPageBody )
481 osl::File::RC nError = (*pPageBody)->open(osl_File_OpenFlag_Read);
482 if (nError == osl::File::E_None)
484 AppendPS (pDestFILE, pPageBody->get(), pBuffer);
485 (*pPageBody)->close();
488 else
489 bSuccess = false;
492 AppendPS (pDestFILE, mpJobTrailer.get(), pBuffer);
493 mpJobTrailer->close();
495 /* well done */
497 if (bSpoolToFile)
498 fclose (pDestFILE);
499 else
501 PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get();
502 if (!rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName,
503 maJobTitle, pDestFILE, m_aDocumentJobData, true, OUString()))
505 bSuccess = false;
509 return bSuccess;
512 void
513 PrinterJob::InitPaperSize (const JobData& rJobSetup)
515 int nRes = rJobSetup.m_aContext.getRenderResolution ();
517 OUString aPaper;
518 int nWidth, nHeight;
519 rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight);
521 int nLeft = 0, nRight = 0, nUpper = 0, nLower = 0;
522 const PPDParser* pParser = rJobSetup.m_aContext.getParser();
523 if (pParser != nullptr)
524 pParser->getMargins (aPaper, nLeft, nRight, nUpper, nLower);
526 mnResolution = nRes;
528 mnWidthPt = nWidth;
529 mnHeightPt = nHeight;
531 if( mnWidthPt > mnMaxWidthPt )
532 mnMaxWidthPt = mnWidthPt;
533 if( mnHeightPt > mnMaxHeightPt )
534 mnMaxHeightPt = mnHeightPt;
536 mnLMarginPt = nLeft;
537 mnRMarginPt = nRight;
538 mnTMarginPt = nUpper;
539 mnBMarginPt = nLower;
541 mfXScale = 72.0 / static_cast<double>(mnResolution);
542 mfYScale = -1.0 * 72.0 / static_cast<double>(mnResolution);
545 void
546 PrinterJob::StartPage (const JobData& rJobSetup)
548 InitPaperSize (rJobSetup);
550 OUString aPageNo = OUString::number (static_cast<sal_Int32>(maPageVector.size())+1); // sequential page number must start with 1
551 OUString aExt = aPageNo + ".ps";
553 maHeaderVector.push_back( CreateSpoolFile ( "psp_pghead", aExt) );
554 maPageVector.push_back( CreateSpoolFile ( "psp_pgbody", aExt) );
556 osl::File* pPageHeader = maHeaderVector.back().get();
557 osl::File* pPageBody = maPageVector.back().get();
559 if( ! (pPageHeader && pPageBody) )
560 return;
562 // write page header according to Document Structuring Conventions (DSC)
563 WritePS (pPageHeader, "%%Page: ");
564 WritePS (pPageHeader, aPageNo);
565 WritePS (pPageHeader, " ");
566 WritePS (pPageHeader, aPageNo);
567 WritePS (pPageHeader, "\n");
569 if( rJobSetup.m_eOrientation == orientation::Landscape )
571 WritePS (pPageHeader, "%%PageOrientation: Landscape\n");
572 mnLandscapes++;
574 else
576 WritePS (pPageHeader, "%%PageOrientation: Portrait\n");
577 mnPortraits++;
580 OStringBuffer pBBox;
582 psp::appendStr ("%%PageBoundingBox: ", pBBox);
583 psp::getValueOf (mnLMarginPt, pBBox);
584 psp::appendStr (" ", pBBox);
585 psp::getValueOf (mnBMarginPt, pBBox);
586 psp::appendStr (" ", pBBox);
587 psp::getValueOf (mnWidthPt - mnRMarginPt, pBBox);
588 psp::appendStr (" ", pBBox);
589 psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox);
590 psp::appendStr ("\n", pBBox);
592 WritePS (pPageHeader, pBBox.makeStringAndClear());
594 /* #i7262# #i65491# write setup only before first page
595 * (to %%Begin(End)Setup, instead of %%Begin(End)PageSetup)
596 * don't do this in StartJob since the jobsetup there may be
597 * different.
599 bool bWriteFeatures = true;
600 if( 1 == maPageVector.size() )
602 m_aDocumentJobData = rJobSetup;
603 bWriteFeatures = false;
606 if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) )
608 m_aLastJobData = rJobSetup;
612 void
613 PrinterJob::EndPage ()
615 osl::File* pPageHeader = maHeaderVector.back().get();
616 osl::File* pPageBody = maPageVector.back().get();
618 if( ! (pPageBody && pPageHeader) )
619 return;
621 // copy page to paper and write page trailer according to DSC
623 OStringBuffer pTrailer;
624 psp::appendStr ("grestore grestore\n", pTrailer);
625 psp::appendStr ("showpage\n", pTrailer);
626 psp::appendStr ("%%PageTrailer\n\n", pTrailer);
627 WritePS (pPageBody, pTrailer.makeStringAndClear());
629 // this page is done for now, close it to avoid having too many open fd's
631 pPageHeader->close();
632 pPageBody->close();
635 struct less_ppd_key
637 bool operator()(const PPDKey* left, const PPDKey* right)
638 { return left->getOrderDependency() < right->getOrderDependency(); }
641 static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature )
643 if( ! pKey || ! pValue )
644 return true;
646 OStringBuffer aFeature(256);
647 aFeature.append( "[{\n" );
648 if( bUseIncluseFeature )
649 aFeature.append( "%%IncludeFeature:" );
650 else
651 aFeature.append( "%%BeginFeature:" );
652 aFeature.append( " *" );
653 aFeature.append( OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ) );
654 aFeature.append( ' ' );
655 aFeature.append( OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) );
656 if( !bUseIncluseFeature )
658 aFeature.append( '\n' );
659 aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) );
660 aFeature.append( "\n%%EndFeature" );
662 aFeature.append( "\n} stopped cleartomark\n" );
663 sal_uInt64 nWritten = 0;
664 return !(pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten )
665 || nWritten != static_cast<sal_uInt64>(aFeature.getLength()));
668 bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup )
670 bool bSuccess = true;
672 // emit features ordered to OrderDependency
673 // ignore features that are set to default
675 // sanity check
676 if( rJob.m_pParser == rJob.m_aContext.getParser() &&
677 rJob.m_pParser &&
678 ( m_aLastJobData.m_pParser == rJob.m_pParser || m_aLastJobData.m_pParser == nullptr )
681 int i;
682 int nKeys = rJob.m_aContext.countValuesModified();
683 ::std::vector< const PPDKey* > aKeys( nKeys );
684 for( i = 0; i < nKeys; i++ )
685 aKeys[i] = rJob.m_aContext.getModifiedKey( i );
686 ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
688 for( i = 0; i < nKeys && bSuccess; i++ )
690 const PPDKey* pKey = aKeys[i];
691 bool bEmit = false;
692 if( bDocumentSetup )
694 if( pKey->getSetupType() == PPDKey::SetupType::DocumentSetup )
695 bEmit = true;
697 if( pKey->getSetupType() == PPDKey::SetupType::PageSetup ||
698 pKey->getSetupType() == PPDKey::SetupType::AnySetup )
699 bEmit = true;
700 if( bEmit )
702 const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
703 if( pValue
704 && pValue->m_eType == eInvocation
705 && ( m_aLastJobData.m_pParser == nullptr
706 || m_aLastJobData.m_aContext.getValue( pKey ) != pValue
707 || bDocumentSetup
711 // try to avoid PS level 2 feature commands if level is set to 1
712 if( GetPostscriptLevel( &rJob ) == 1 )
714 bool bHavePS2 =
715 ( pValue->m_aValue.indexOf( "<<" ) != -1 )
717 ( pValue->m_aValue.indexOf( ">>" ) != -1 );
718 if( bHavePS2 )
719 continue;
721 bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() );
726 else
727 bSuccess = false;
729 return bSuccess;
732 bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob, bool bWriteFeatures )
734 bool bSuccess = true;
736 WritePS (pFile, "%%BeginPageSetup\n%\n");
737 if ( bWriteFeatures )
738 bSuccess = writeFeatureList( pFile, rJob, false );
739 WritePS (pFile, "%%EndPageSetup\n");
741 OStringBuffer pTranslate;
743 if( rJob.m_eOrientation == orientation::Portrait )
745 psp::appendStr ("gsave\n[", pTranslate);
746 psp::getValueOfDouble ( pTranslate, mfXScale, 5);
747 psp::appendStr (" 0 0 ", pTranslate);
748 psp::getValueOfDouble ( pTranslate, mfYScale, 5);
749 psp::appendStr (" ", pTranslate);
750 psp::getValueOf (mnRMarginPt, pTranslate);
751 psp::appendStr (" ", pTranslate);
752 psp::getValueOf (mnHeightPt-mnTMarginPt,
753 pTranslate);
754 psp::appendStr ("] concat\ngsave\n",
755 pTranslate);
757 else
759 psp::appendStr ("gsave\n", pTranslate);
760 psp::appendStr ("[ 0 ", pTranslate);
761 psp::getValueOfDouble ( pTranslate, -mfYScale, 5);
762 psp::appendStr (" ", pTranslate);
763 psp::getValueOfDouble ( pTranslate, mfXScale, 5);
764 psp::appendStr (" 0 ", pTranslate );
765 psp::getValueOfDouble ( pTranslate, mnLMarginPt, 5 );
766 psp::appendStr (" ", pTranslate);
767 psp::getValueOf (mnBMarginPt, pTranslate );
768 psp::appendStr ("] concat\ngsave\n",
769 pTranslate);
772 WritePS (pFile, pTranslate.makeStringAndClear());
774 return bSuccess;
777 void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData )
779 if( ! PrinterInfoManager::get().getUseJobPatch() )
780 return;
782 const PPDKey* pKey = nullptr;
784 if( rJobData.m_pParser )
785 pKey = rJobData.m_pParser->getKey( "JobPatchFile" );
786 if( ! pKey )
787 return;
789 // order the patch files
790 // according to PPD spec the JobPatchFile options must be int
791 // and should be emitted in order
792 std::deque< sal_Int32 > patch_order;
793 int nValueCount = pKey->countValues();
794 for( int i = 0; i < nValueCount; i++ )
796 const PPDValue* pVal = pKey->getValue( i );
797 patch_order.push_back( pVal->m_aOption.toInt32() );
798 if( patch_order.back() == 0 && pVal->m_aOption != "0" )
800 WritePS( pFile, "% Warning: left out JobPatchFile option \"" );
801 OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US );
802 WritePS( pFile, aOption.getStr() );
803 WritePS( pFile,
804 "\"\n% as it violates the PPD spec;\n"
805 "% JobPatchFile options need to be numbered for ordering.\n" );
809 std::sort(patch_order.begin(), patch_order.end());
810 patch_order.erase(std::unique(patch_order.begin(), patch_order.end()), patch_order.end());
812 for (auto const& elem : patch_order)
814 // note: this discards patch files not adhering to the "int" scheme
815 // as there won't be a value for them
816 writeFeature( pFile, pKey, pKey->getValue( OUString::number(elem) ), false );
820 void PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData )
822 WritePS( pFile, "%%BeginProlog\n" );
824 // JobPatchFile feature needs to be emitted at begin of prolog
825 writeJobPatch( pFile, rJobData );
827 static const sal_Char pProlog[] = {
828 "%%BeginResource: procset PSPrint-Prolog 1.0 0\n"
829 "/ISO1252Encoding [\n"
830 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
831 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
832 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
833 "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
834 "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n"
835 "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n"
836 "/zero /one /two /three /four /five /six /seven\n"
837 "/eight /nine /colon /semicolon /less /equal /greater /question\n"
838 "/at /A /B /C /D /E /F /G\n"
839 "/H /I /J /K /L /M /N /O\n"
840 "/P /Q /R /S /T /U /V /W\n"
841 "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n"
842 "/grave /a /b /c /d /e /f /g\n"
843 "/h /i /j /k /l /m /n /o\n"
844 "/p /q /r /s /t /u /v /w\n"
845 "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n"
846 "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n"
847 "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n"
848 "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n"
849 "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n"
850 "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n"
851 "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n"
852 "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n"
853 "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n"
854 "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
855 "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n"
856 "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n"
857 "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n"
858 "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n"
859 "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
860 "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n"
861 "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n"
862 "\n"
863 "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n"
864 "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n"
865 "currentdict end exch pop definefont pop } def\n"
866 "\n"
867 "/pathdict dup 8 dict def load begin\n"
868 "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n"
869 "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n"
870 "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n"
871 "eq 3 1 roll exch } def\n"
872 "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n"
873 "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n"
874 "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n"
875 "for 256 div exch pop exch { neg } if } def\n"
876 "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n"
877 "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n"
878 "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n"
879 "\n"
880 "systemdict /languagelevel known not {\n"
881 "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n"
882 "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n"
883 "roll show moveto 0 rmoveto } for pop pop } def\n"
884 "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n"
885 "rlineto closepath } def\n"
886 "/rectfill { rectangle fill } def\n"
887 "/rectstroke { rectangle stroke } def } if\n"
888 "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n"
889 "setlinewidth false charpath stroke setlinewidth } def\n"
890 "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n"
891 "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n"
892 "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n"
893 "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n"
894 "\n"
895 "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n"
896 "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n"
897 "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n"
898 "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n"
899 "/psp_imagedict {\n"
900 "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n"
901 "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n"
902 "def 7 dict dup\n"
903 "/ImageType 1 put dup\n"
904 "/Width 7 -1 roll put dup\n"
905 "/Height 5 index put dup\n"
906 "/BitsPerComponent 4 index psp_bitspercomponent put dup\n"
907 "/Decode 5 -1 roll psp_decodearray put dup\n"
908 "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n"
909 "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n"
910 "} def\n"
911 "%%EndResource\n"
912 "%%EndProlog\n"
914 WritePS (pFile, pProlog);
917 bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob )
919 WritePS (pFile, "%%BeginSetup\n%\n");
921 // download fonts
922 std::vector< OString > aFonts;
923 m_pGraphics->writeResources( pFile, aFonts );
925 if( !aFonts.empty() )
927 std::vector< OString >::const_iterator it = aFonts.begin();
928 OStringBuffer aLine( 256 );
929 aLine.append( "%%DocumentSuppliedResources: font " );
930 aLine.append( *it );
931 aLine.append( "\n" );
932 WritePS ( pFile, aLine.getStr() );
933 while( (++it) != aFonts.end() )
935 aLine.setLength(0);
936 aLine.append( "%%+ font " );
937 aLine.append( *it );
938 aLine.append( "\n" );
939 WritePS ( pFile, aLine.getStr() );
943 bool bSuccess = true;
944 // in case of external print dialog the number of copies is prepended
945 // to the job, let us not complicate things by emitting our own copy count
946 bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" );
947 if( ! bExternalDialog && rJob.m_nCopies > 1 )
949 // setup code
950 OString aLine = "/#copies " +
951 OString::number(static_cast<sal_Int32>(rJob.m_nCopies)) +
952 " def\n";
953 sal_uInt64 nWritten = 0;
954 bSuccess = !(pFile->write(aLine.getStr(), aLine.getLength(), nWritten)
955 || nWritten != static_cast<sal_uInt64>(aLine.getLength()));
957 if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 )
958 WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" );
961 bool bFeatureSuccess = writeFeatureList( pFile, rJob, true );
963 WritePS (pFile, "%%EndSetup\n");
965 return bSuccess && bFeatureSuccess;
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */