1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
46 #define nBLOCKSIZE 0x2000
52 AppendPS (FILE* pDst
, osl::File
* pSrc
, unsigned char* pBuffer
)
55 if ((pDst
== nullptr) || (pSrc
== nullptr))
58 if (pSrc
->setPos(osl_Pos_Absolut
, 0) != osl::FileBase::E_None
)
65 pSrc
->read (pBuffer
, nBLOCKSIZE
, nIn
);
67 nOut
= fwrite (pBuffer
, 1, sal::static_int_cast
<sal_uInt32
>(nIn
), pDst
);
69 while ((nIn
> 0) && (nIn
== nOut
));
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
;
85 osl::File::RC nError
= osl::File::getFileURLFromSystemPath( aFile
, aFileURL
);
86 if (nError
!= osl::File::E_None
)
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
)
97 osl::File::setAttributes (aFileURL
,
98 osl_File_Attribute_OwnWrite
| osl_File_Attribute_OwnRead
);
103 * public methods of PrinterJob: for use in PrinterGfx
107 PrinterJob::GetScale (double &rXScale
, double &rYScale
) const
114 PrinterJob::GetDepth () const
116 sal_Int32 nLevel
= GetPostscriptLevel();
117 bool bColor
= IsColorPrinter ();
119 return nLevel
> 1 && bColor
? 24 : 8;
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
;
133 if( pJobData
->m_pParser
)
134 nPSLevel
= pJobData
->m_pParser
->getLanguageLevel();
140 PrinterJob::IsColorPrinter () const
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();
153 PrinterJob::GetCurrentPageBody ()
155 return maPageVector
.back().get();
159 * public methods of PrinterJob: the actual job / spool handling
161 PrinterJob::PrinterJob()
163 , m_pGraphics(nullptr)
181 /* remove all our temporary files, uses external program "rm", since
182 osl functionality is inadequate */
184 removeSpoolDir (const OUString
& rSpoolDir
)
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" );
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 */
206 osl_getSystemTime( &aCur
);
207 sal_Int32 nRand
= aCur
.Seconds
^ (aCur
.Nanosec
/1000);
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
);
228 PrinterJob::~PrinterJob ()
230 maPageVector
.clear();
231 maHeaderVector
.clear();
233 // mpJobHeader->remove();
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
))
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
);
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
++ )
275 PrinterJob::StartJob (
276 const OUString
& rFileName
,
278 const OUString
& rJobName
,
279 const OUString
& rAppName
,
280 const JobData
& rSetupData
,
281 PrinterGfx
* pGraphics
,
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
;
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
303 // write document header according to Document Structuring Conventions (DSC)
304 WritePS (mpJobHeader
.get(),
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");
315 osl::Security aSecurity
;
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");
331 * The title should be clean ascii; rJobName however may
332 * contain any Unicode character. So implement the following
334 * use rJobName, if it contains only ascii
335 * use the filename, if it contains only ascii
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
) )
347 maJobTitle
= aFilterWS
;
348 if( !aTitle
.isEmpty() )
350 WritePS (mpJobHeader
.get(), "%%Title: (");
351 WritePS (mpJobHeader
.get(), aTitle
);
352 WritePS (mpJobHeader
.get(), ")\n");
356 OStringBuffer pLevel
;
357 getValueOf(GetPostscriptLevel(&rSetupData
), pLevel
);
359 WritePS (mpJobHeader
.get(), "%%LanguageLevel: ");
360 WritePS (mpJobHeader
.get(), pLevel
.makeStringAndClear());
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");
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 );
382 // no pages ? that really means no print job
383 if( maPageVector
.empty() )
386 // write document setup (done here because it
387 // includes the accumulated fonts
389 writeSetup( mpJobHeader
.get(), m_aDocumentJobData
);
390 m_pGraphics
->OnEndJob();
391 if( ! (mpJobHeader
&& mpJobTrailer
) )
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");
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();
420 const OString aFileName
= OUStringToOString (maFileName
,
421 osl_getThreadTextEncoding());
424 int nFile
= open( aFileName
.getStr(), O_CREAT
| O_EXCL
| O_RDWR
, mnFileMode
);
427 pDestFILE
= fdopen( nFile
, "w" );
428 if( pDestFILE
== nullptr )
431 unlink( aFileName
.getStr() );
437 (void)chmod( aFileName
.getStr(), mnFileMode
);
440 if (pDestFILE
== nullptr)
441 pDestFILE
= fopen (aFileName
.getStr(), "w");
443 if (pDestFILE
== nullptr)
448 PrinterInfoManager
& rPrinterInfoManager
= PrinterInfoManager::get ();
449 pDestFILE
= rPrinterInfoManager
.startSpool( m_aLastJobData
.m_aPrinterName
, m_bQuickJob
);
450 if (pDestFILE
== nullptr)
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
)
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();
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();
492 AppendPS (pDestFILE
, mpJobTrailer
.get(), pBuffer
);
493 mpJobTrailer
->close();
501 PrinterInfoManager
& rPrinterInfoManager
= PrinterInfoManager::get();
502 if (!rPrinterInfoManager
.endSpool( m_aLastJobData
.m_aPrinterName
,
503 maJobTitle
, pDestFILE
, m_aDocumentJobData
, true, OUString()))
513 PrinterJob::InitPaperSize (const JobData
& rJobSetup
)
515 int nRes
= rJobSetup
.m_aContext
.getRenderResolution ();
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
);
529 mnHeightPt
= nHeight
;
531 if( mnWidthPt
> mnMaxWidthPt
)
532 mnMaxWidthPt
= mnWidthPt
;
533 if( mnHeightPt
> mnMaxHeightPt
)
534 mnMaxHeightPt
= mnHeightPt
;
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
);
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
) )
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");
576 WritePS (pPageHeader
, "%%PageOrientation: Portrait\n");
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
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
;
613 PrinterJob::EndPage ()
615 osl::File
* pPageHeader
= maHeaderVector
.back().get();
616 osl::File
* pPageBody
= maPageVector
.back().get();
618 if( ! (pPageBody
&& pPageHeader
) )
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();
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
)
646 OStringBuffer
aFeature(256);
647 aFeature
.append( "[{\n" );
648 if( bUseIncluseFeature
)
649 aFeature
.append( "%%IncludeFeature:" );
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
676 if( rJob
.m_pParser
== rJob
.m_aContext
.getParser() &&
678 ( m_aLastJobData
.m_pParser
== rJob
.m_pParser
|| m_aLastJobData
.m_pParser
== nullptr )
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
];
694 if( pKey
->getSetupType() == PPDKey::SetupType::DocumentSetup
)
697 if( pKey
->getSetupType() == PPDKey::SetupType::PageSetup
||
698 pKey
->getSetupType() == PPDKey::SetupType::AnySetup
)
702 const PPDValue
* pValue
= rJob
.m_aContext
.getValue( pKey
);
704 && pValue
->m_eType
== eInvocation
705 && ( m_aLastJobData
.m_pParser
== nullptr
706 || m_aLastJobData
.m_aContext
.getValue( pKey
) != pValue
711 // try to avoid PS level 2 feature commands if level is set to 1
712 if( GetPostscriptLevel( &rJob
) == 1 )
715 ( pValue
->m_aValue
.indexOf( "<<" ) != -1 )
717 ( pValue
->m_aValue
.indexOf( ">>" ) != -1 );
721 bSuccess
= writeFeature( pFile
, pKey
, pValue
, PrinterInfoManager::get().getUseIncludeFeature() );
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
,
754 psp::appendStr ("] concat\ngsave\n",
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",
772 WritePS (pFile
, pTranslate
.makeStringAndClear());
777 void PrinterJob::writeJobPatch( osl::File
* pFile
, const JobData
& rJobData
)
779 if( ! PrinterInfoManager::get().getUseJobPatch() )
782 const PPDKey
* pKey
= nullptr;
784 if( rJobData
.m_pParser
)
785 pKey
= rJobData
.m_pParser
->getKey( "JobPatchFile" );
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() );
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"
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"
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"
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"
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"
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"
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"
914 WritePS (pFile
, pProlog
);
917 bool PrinterJob::writeSetup( osl::File
* pFile
, const JobData
& rJob
)
919 WritePS (pFile
, "%%BeginSetup\n%\n");
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 " );
931 aLine
.append( "\n" );
932 WritePS ( pFile
, aLine
.getStr() );
933 while( (++it
) != aFonts
.end() )
936 aLine
.append( "%%+ font " );
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 )
950 OString aLine
= "/#copies " +
951 OString::number(static_cast<sal_Int32
>(rJob
.m_nCopies
)) +
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: */