diff options
author | Oliver Bolte <obo@openoffice.org> | 2009-03-06 09:33:41 +0000 |
---|---|---|
committer | Oliver Bolte <obo@openoffice.org> | 2009-03-06 09:33:41 +0000 |
commit | 659af2052b0471ff4507f1742a4f0c0681d66e8e (patch) | |
tree | d047328a27e779c050e18e63766fed55f759bdd1 /vcl/unx | |
parent | 3315021862137d58fd8d57c035d397474a9df644 (diff) |
CWS-TOOLING: integrate CWS movepsprint
2009-02-27 10:42:24 +0100 pl r268559 : #i99633# remove X11 build for mac
2009-02-26 16:38:01 +0100 pl r268535 : #i97898# move psprint to vcl: no more linking psprint
2009-02-26 16:36:35 +0100 pl r268534 : #i97898# move psprint to vcl: tentative os2 changes
2009-02-26 16:20:36 +0100 pl r268532 : #i97898# move psprint to vcl: make compile again, round 5
2009-02-26 15:53:12 +0100 pl r268530 : #i97898# psprint removed
2009-02-26 13:43:40 +0100 pl r268513 : #i97898# use proper X display in PrintFontManager::initialize
2009-02-26 12:39:05 +0100 pl r268496 : #i99633# remove unsused header
2009-02-25 19:12:54 +0100 pl r268467 : #i99633# get rid of special casing for the now discontinued Mac X11 port
2009-02-25 18:25:37 +0100 pl r268466 : #i97898# move psprint to vcl: make compile again, round 4
2009-02-25 17:38:55 +0100 pl r268458 : #i97898# move psprint to vcl: make compile again, round 3
2009-02-25 16:55:15 +0100 pl r268452 : #i97898# move psprint to vcl: remove psp lib from install set
2009-02-25 16:50:25 +0100 pl r268451 : #i97898# move psprint to vcl: make compile again, round 2
2009-02-25 16:10:50 +0100 pl r268448 : #i97898# move psprint to vcl: make compile again, round 1
2009-02-25 14:38:12 +0100 pl r268440 : #i97898# move psprint to vcl: remove references to psprint in solenv
2009-02-25 14:22:22 +0100 pl r268437 : #i97898# move psprint to vcl, first step: move in svn
Diffstat (limited to 'vcl/unx')
61 files changed, 20741 insertions, 1641 deletions
diff --git a/vcl/unx/gtk/window/gtkframe.cxx b/vcl/unx/gtk/window/gtkframe.cxx index 630b034733a4..8e280675866a 100644 --- a/vcl/unx/gtk/window/gtkframe.cxx +++ b/vcl/unx/gtk/window/gtkframe.cxx @@ -84,20 +84,10 @@ static USHORT GetKeyModCode( guint state ) USHORT nCode = 0; if( (state & GDK_SHIFT_MASK) ) nCode |= KEY_SHIFT; - if( (state & GDK_CONTROL_MASK) -#ifdef MACOSX - || (state & GDK_MOD2_MASK) // map Meta (aka Command key) to Ctrl -#endif - ) + if( (state & GDK_CONTROL_MASK) ) nCode |= KEY_MOD1; if( (state & GDK_MOD1_MASK) ) - { nCode |= KEY_MOD2; -#ifdef MACOSX - if( ! (nCode & KEY_MOD1) ) - nCode |= KEY_MOD3; -#endif - } return nCode; } @@ -2951,35 +2941,21 @@ gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame // The modifier mode therefore has to be adapted manually. switch( pEvent->keyval ) { -#ifdef MACOSX - case GDK_Meta_L: // map Meta (aka Command key) to Ctrl -#endif case GDK_Control_L: nExtModMask = MODKEY_LMOD1; nModMask = KEY_MOD1; break; -#ifdef MACOSX - case GDK_Meta_R: // map Meta (aka Command key) to Ctrl -#endif case GDK_Control_R: nExtModMask = MODKEY_RMOD1; nModMask = KEY_MOD1; break; case GDK_Alt_L: nExtModMask = MODKEY_LMOD2; -#ifdef MACOSX - nModMask = KEY_MOD3; -#else nModMask = KEY_MOD2; -#endif break; case GDK_Alt_R: nExtModMask = MODKEY_RMOD2; -#ifdef MACOSX - nModMask = KEY_MOD2 | (pEvent->type == GDK_KEY_RELEASE ? KEY_MOD3 : 0); -#else nModMask = KEY_MOD2; -#endif break; case GDK_Shift_L: nExtModMask = MODKEY_LSHIFT; diff --git a/vcl/unx/headless/svpprn.cxx b/vcl/unx/headless/svpprn.cxx index 0ac79afeec88..e9d726464921 100644 --- a/vcl/unx/headless/svpprn.cxx +++ b/vcl/unx/headless/svpprn.cxx @@ -28,17 +28,17 @@ * ************************************************************************/ -#include <vcl/svapp.hxx> -#include <vcl/jobset.h> +#include "vcl/svapp.hxx" +#include "vcl/jobset.h" +#include "vcl/print.h" +#include "vcl/salptype.hxx" +#include "vcl/timer.hxx" +#include "vcl/printerinfomanager.hxx" + #include "svpprn.hxx" -#include <vcl/print.h> -#include <vcl/salptype.hxx> -#include <vcl/timer.hxx> #include "svppspgraphics.hxx" #include "svpinst.hxx" -#include <psprint/printerinfomanager.hxx> - #include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> @@ -623,58 +623,13 @@ BOOL PspSalInfoPrinter::SetData( } String aPaper; -#ifdef MACOSX - // For Mac OS X, many printers are directly attached - // USB/Serial printers with a stripped-down PPD that gives us - // problems. We need to do PS->PDF conversion for these printers - // but they are not able to handle multiple page sizes in the same - // document at all, since we must pass -o media=... to them to get - // a good printout. - // So, we must find a match between the paper size from OOo and what - // the PPD of the printer has, and pass that paper size to -o media=... - // If a match cannot be found (ie the paper size from Format->Page is - // nowhere near anything in the PPD), we default to what has been - // chosen in File->Print->Properties. - // - // For printers capable of directly accepting PostScript data, none - // of this occurs and we default to the normal OOo behavior. - const PPDKey *pCupsFilterKey; - const PPDValue *pCupsFilterValue; - BOOL bIsCUPSPrinter = TRUE; - - // Printers that need PS->PDF conversion have a "cupsFilter" key and - // a value of "application/pdf" in that key - pCupsFilterKey = aData.m_pParser->getKey( String(RTL_CONSTASCII_USTRINGPARAM("cupsFilter")) ); - pCupsFilterValue = pCupsFilterKey != NULL ? aData.m_aContext.getValue( pCupsFilterKey ) : NULL; - if ( pCupsFilterValue ) - { - // PPD had a cupsFilter key, check for PS->PDF conversion requirement - ByteString aCupsFilterString( pCupsFilterValue->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ); - if ( aCupsFilterString.Search("application/pdf") == 0 ) - bIsCUPSPrinter = FALSE; - } + if( pJobSetup->mePaperFormat == PAPER_USER ) + aPaper = aData.m_pParser->matchPaper( + TenMuToPt( pJobSetup->mnPaperWidth ), + TenMuToPt( pJobSetup->mnPaperHeight ) ); else - bIsCUPSPrinter = FALSE; + aPaper = String( ByteString( aPaperTab[ pJobSetup->mePaperFormat ].name ), RTL_TEXTENCODING_ISO_8859_1 ); - if ( TRUE == bIsCUPSPrinter ) - { - // If its a directly attached printer, with a - // stripped down PPD (most OS X printers are) always - // match the paper size. - aPaper = aData.m_pParser->matchPaper( - TenMuToPt( pJobSetup->mnPaperWidth ), - TenMuToPt( pJobSetup->mnPaperHeight ) ); - } - else -#endif - { - if( pJobSetup->mePaperFormat == PAPER_USER ) - aPaper = aData.m_pParser->matchPaper( - TenMuToPt( pJobSetup->mnPaperWidth ), - TenMuToPt( pJobSetup->mnPaperHeight ) ); - else - aPaper = String( ByteString( aPaperTab[ pJobSetup->mePaperFormat ].name ), RTL_TEXTENCODING_ISO_8859_1 ); - } pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); pValue = pKey ? pKey->getValue( aPaper ) : NULL; if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) ) diff --git a/vcl/unx/headless/svpprn.hxx b/vcl/unx/headless/svpprn.hxx index eef8865fb56b..c2d85c054fce 100644 --- a/vcl/unx/headless/svpprn.hxx +++ b/vcl/unx/headless/svpprn.hxx @@ -31,10 +31,10 @@ #ifndef _SVP_SVPPRN_HXX #define _SVP_SVPPRN_HXX -#include <psprint/jobdata.hxx> -#include <psprint/printergfx.hxx> -#include <psprint/printerjob.hxx> -#include <vcl/salprn.hxx> +#include "vcl/jobdata.hxx" +#include "vcl/printergfx.hxx" +#include "vcl/printerjob.hxx" +#include "vcl/salprn.hxx" class PspGraphics; diff --git a/vcl/unx/headless/svppspgraphics.cxx b/vcl/unx/headless/svppspgraphics.cxx index 12302c1bbae2..2ff48966c765 100644 --- a/vcl/unx/headless/svppspgraphics.cxx +++ b/vcl/unx/headless/svppspgraphics.cxx @@ -29,22 +29,24 @@ ************************************************************************/ #include "svppspgraphics.hxx" -#include <psprint/jobdata.hxx> -#include <psprint/printergfx.hxx> -#include <psprint/printerinfomanager.hxx> -#include <vcl/bmpacc.hxx> -#include <vcl/salbmp.hxx> -#include <vcl/glyphcache.hxx> -#include <vcl/impfont.hxx> -#include <vcl/outfont.hxx> -#include <vcl/svapp.hxx> -#include <vcl/salprn.hxx> -#include <vcl/sysdata.hxx> -#include <basegfx/vector/b2ivector.hxx> -#include <basegfx/point/b2ipoint.hxx> -#include <basebmp/color.hxx> #include "svpbmp.hxx" +#include "vcl/jobdata.hxx" +#include "vcl/printergfx.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/bmpacc.hxx" +#include "vcl/salbmp.hxx" +#include "vcl/glyphcache.hxx" +#include "vcl/impfont.hxx" +#include "vcl/outfont.hxx" +#include "vcl/svapp.hxx" +#include "vcl/salprn.hxx" +#include "vcl/sysdata.hxx" + +#include "basegfx/vector/b2ivector.hxx" +#include "basegfx/point/b2ipoint.hxx" +#include "basebmp/color.hxx" + #include <stdlib.h> #include <unistd.h> #include <fcntl.h> diff --git a/vcl/unx/headless/svppspgraphics.hxx b/vcl/unx/headless/svppspgraphics.hxx index 9cbbac446477..ba7d690a9f90 100644 --- a/vcl/unx/headless/svppspgraphics.hxx +++ b/vcl/unx/headless/svppspgraphics.hxx @@ -32,11 +32,9 @@ #define _SVP_PSPGRAPHICS_HXX -#ifndef _PSPRINT_FONTMANAGER_HXX -#include <psprint/fontmanager.hxx> -#endif -#include <vcl/sallayout.hxx> -#include <vcl/salgdi.hxx> +#include "vcl/fontmanager.hxx" +#include "vcl/sallayout.hxx" +#include "vcl/salgdi.hxx" namespace psp { struct JobData; class PrinterGfx; } diff --git a/vcl/unx/inc/dtint.hxx b/vcl/unx/inc/dtint.hxx index b7cc8941219f..e65e41a73af5 100644 --- a/vcl/unx/inc/dtint.hxx +++ b/vcl/unx/inc/dtint.hxx @@ -49,8 +49,7 @@ struct XEvent; enum DtType { DtGeneric, - DtCDE, - DtMACOSX + DtCDE }; class DtIntegrator diff --git a/vcl/unx/inc/macosxint.hxx b/vcl/unx/inc/macosxint.hxx deleted file mode 100644 index 86b5f34840df..000000000000 --- a/vcl/unx/inc/macosxint.hxx +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************* - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright 2008 by Sun Microsystems, Inc. - * - * OpenOffice.org - a multi-platform office productivity suite - * - * $RCSfile: macosxint.hxx,v $ - * $Revision: 1.3 $ - * - * This file is part of OpenOffice.org. - * - * OpenOffice.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 - * only, as published by the Free Software Foundation. - * - * OpenOffice.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3 for more details - * (a copy is included in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with OpenOffice.org. If not, see - * <http://www.openoffice.org/license.html> - * for a copy of the LGPLv3 License. - * - ************************************************************************/ -#ifndef _SV_MACOSXINT_HXX -#define _SV_MACOSXINT_HXX - -#include <dtint.hxx> -#include <tools/list.hxx> - -class MACOSXIntegrator : public DtIntegrator -{ - friend DtIntegrator* DtIntegrator::CreateDtIntegrator(); -private: - MACOSXIntegrator(); - -public: - virtual ~MACOSXIntegrator(); - - virtual void GetSystemLook( AllSettings& rSettings ); -}; - -#endif diff --git a/vcl/unx/inc/prex.h b/vcl/unx/inc/prex.h index 705e33ca5188..131e628efe2e 100644 --- a/vcl/unx/inc/prex.h +++ b/vcl/unx/inc/prex.h @@ -50,7 +50,7 @@ extern "C" { #endif -#if defined(LINUX) || defined(FREEBSD) || defined(MACOSX) // should really check for xfree86 or for X11R6.1 and higher +#if defined(LINUX) || defined(FREEBSD) // should really check for xfree86 or for X11R6.1 and higher #define __XKeyboardExtension__ 1 #else #define __XKeyboardExtension__ 0 diff --git a/vcl/unx/inc/pspgraphics.h b/vcl/unx/inc/pspgraphics.h index c8c0abf29fd6..5c31d889453c 100644 --- a/vcl/unx/inc/pspgraphics.h +++ b/vcl/unx/inc/pspgraphics.h @@ -32,16 +32,12 @@ #define _VCL_PSPGRAPHICS_H -#include <vcl/salgdi.hxx> -#ifndef _PSPRINT_FONTMANAGER_HXX -#include <psprint/fontmanager.hxx> -#endif -#include <vcl/sallayout.hxx> -#include <vcl/dllapi.h> - -#ifndef _USE_PRINT_EXTENSION_ +#include "vcl/fontmanager.hxx" +#include "vcl/salgdi.hxx" +#include "vcl/sallayout.hxx" +#include "vcl/dllapi.h" + namespace psp { struct JobData; class PrinterGfx; } -#endif class ServerFont; class ImplDevFontAttributes; diff --git a/vcl/unx/inc/saldata.hxx b/vcl/unx/inc/saldata.hxx index e62b00952663..a4326990c464 100644 --- a/vcl/unx/inc/saldata.hxx +++ b/vcl/unx/inc/saldata.hxx @@ -51,7 +51,7 @@ class SalPrinter; DECLARE_LIST( SalDisplays, SalDisplay* ) -#if defined SCO || defined LINUX || defined NETBSD || defined AIX || defined HPUX || defined FREEBSD || defined MACOSX +#if defined SCO || defined LINUX || defined NETBSD || defined AIX || defined HPUX || defined FREEBSD #include <pthread.h> #else typedef unsigned int pthread_t; diff --git a/vcl/unx/inc/saldisp.hxx b/vcl/unx/inc/saldisp.hxx index dbb8b7bd7ec4..9f9383106615 100644 --- a/vcl/unx/inc/saldisp.hxx +++ b/vcl/unx/inc/saldisp.hxx @@ -384,9 +384,6 @@ protected: KeySym nShiftKeySym_; // first shift modifier KeySym nCtrlKeySym_; // first control modifier KeySym nMod1KeySym_; // first mod1 modifier -#ifdef MACOSX - KeySym nMod2KeySym_; //first mod2 modifier -#endif ByteString m_aKeyboardName; vcl_sal::WMAdaptor* m_pWMAdaptor; diff --git a/vcl/unx/inc/salinst.h b/vcl/unx/inc/salinst.h index e35774b01f02..c0614a78af9b 100644 --- a/vcl/unx/inc/salinst.h +++ b/vcl/unx/inc/salinst.h @@ -113,7 +113,8 @@ public: virtual SalMenuItem* CreateMenuItem( const SalItemParams* pItemData ); virtual void DestroyMenuItem( SalMenuItem* pItem ); - virtual void* GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ); + virtual void* GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ); + void FillFontPathList( std::list< rtl::OString >& o_rFontPaths ); bool isPrinterInit() const diff --git a/vcl/unx/inc/salprn.h b/vcl/unx/inc/salprn.h index 51e22dbbe589..452fa5a89387 100644 --- a/vcl/unx/inc/salprn.h +++ b/vcl/unx/inc/salprn.h @@ -31,10 +31,10 @@ #ifndef _SV_SALPRN_H #define _SV_SALPRN_H -#include <psprint/jobdata.hxx> -#include <psprint/printergfx.hxx> -#include <psprint/printerjob.hxx> -#include <vcl/salprn.hxx> +#include "vcl/jobdata.hxx" +#include "vcl/printergfx.hxx" +#include "vcl/printerjob.hxx" +#include "vcl/salprn.hxx" class PspGraphics; diff --git a/vcl/unx/inc/salunx.h b/vcl/unx/inc/salunx.h index ed9368895f0c..cdf45fd30867 100644 --- a/vcl/unx/inc/salunx.h +++ b/vcl/unx/inc/salunx.h @@ -32,7 +32,7 @@ #define _SALUNX_H // -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -#if defined SCO || defined LINUX || defined HPUX || defined FREEBSD || defined NETBSD || defined MACOSX +#if defined SCO || defined LINUX || defined HPUX || defined FREEBSD || defined NETBSD #include <sys/time.h> #elif defined AIX #include <time.h> diff --git a/vcl/unx/inc/xsalprn.h b/vcl/unx/inc/xsalprn.h deleted file mode 100644 index ac72454c70ab..000000000000 --- a/vcl/unx/inc/xsalprn.h +++ /dev/null @@ -1,192 +0,0 @@ -/************************************************************************* - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright 2008 by Sun Microsystems, Inc. - * - * OpenOffice.org - a multi-platform office productivity suite - * - * $RCSfile: xsalprn.h,v $ - * $Revision: 1.4 $ - * - * This file is part of OpenOffice.org. - * - * OpenOffice.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 - * only, as published by the Free Software Foundation. - * - * OpenOffice.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3 for more details - * (a copy is included in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with OpenOffice.org. If not, see - * <http://www.openoffice.org/license.html> - * for a copy of the LGPLv3 License. - * - ************************************************************************/ -#ifndef __salprint_h -#define __salprint_h - - -class String; - -#ifdef __cplusplus -extern "C" { -#endif - -/* printer interface */ -extern int XSalIsDisplay( const Display * ); -extern int XSalIsPrinter( const Display * ); - -/* error handling */ -typedef int (*XSalPrinterErrorHandler)( XErrorEvent * ); - -extern XSalPrinterErrorHandler XSalSetPrinterErrorHandler( XSalPrinterErrorHandler ); - -/* common callbacks */ -typedef void* XSalPointer; -typedef int (*XSalPrinterCallback)( XSalPointer cb_data, XSalPointer client_data ); - -#ifndef _SV_SV_H -#define _SV_SV_H -#define USHORT unsigned short -#include <vcl/prntypes.hxx> -#undef USHORT -#undef _SV_SV_H -#else -#include <vcl/prntypes.hxx> -#endif - -typedef enum Orientation Orientation; - -/* initialize before first use */ -extern void XSalPrinterInit( const String& installPath ); - -typedef unsigned short XSalEnum; -typedef unsigned char XSalBool; - -#define COLOR_SPACE_COLOR 1 -#define COLOR_SPACE_GRAY 0 - -typedef struct -{ - unsigned int nMagic; /* internal data */ - unsigned short nVersion; /* internal data */ - unsigned short nFlags; /* internal data */ - XSalEnum eDriver; /* PostScript, PCL, ... */ - unsigned short nCopies; /* number of copies */ - unsigned short nPaperBin; /* paper bin to use */ - XSalEnum ePaper; /* A4, A5, ... */ - unsigned int nPaperWidth; /* paper width if PAPER_USER */ - unsigned int nPaperHeight; /* paper height if PAPER_USER */ - XSalEnum eOrientation; /* portrait / landscape */ - unsigned int nScale; /* [%] ( 100 => 1:1 ) */ - unsigned short nResolutionX; /* [dots per inch] */ - unsigned short nResolutionY; /* [dots per inch] */ - char sCompatCommand[256];/* current shell command */ - char sPort[256]; /* default shell command */ - char cDriverName[32]; /* Druckertreibername */ - unsigned int nTrailingBytes; /* trailing bytes appended to this structure */ -} XSalPrinterSetup; - -#define XSAL_PRINTER_SETUP_MAGIC 0x0000ede1 -#define WRONG_ENDIANESS_MAGIC 0xe1ed0000 - -/* definition for XSalPrinterSetup.nFlags */ -#define XSALPRINTERSETUP_FLAG_LEVEL_DEFAULT 0x0000 -#define XSALPRINTERSETUP_FLAG_LEVEL 0x000f -#define XSALPRINTERSETUP_FLAG_LEVEL_SHIFT 0 -#define XSALPRINTERSETUP_FLAG_COLOR 0x0010 /* colored bitmaps */ -#define XSALPRINTERSETUP_FLAG_COLOR_DEFAULT 0x0020 -#define XSALPRINTERSETUP_FLAG_COMPRESS 0x0040 /* compress bitmaps */ -#define XSALPRINTERSETUP_FLAG_COMPRESS_DEFAULT 0x0080 /* compress bitmaps */ -#define XSALPRINTERSETUP_FLAG_DEPTH_DEFAULT 0x0700 -#define XSALPRINTERSETUP_FLAG_DEPTH 0x0700 /* depth n = depth 2^n, 6 = 24Bit, 7 = default */ -#define XSALPRINTERSETUP_FLAG_DEPTH_SHIFT 8 - -#define XSALPRINTERSETUP_FLAG_DEFAULT\ - (XSALPRINTERSETUP_FLAG_LEVEL_DEFAULT | \ - XSALPRINTERSETUP_FLAG_COMPRESS_DEFAULT | \ - XSALPRINTERSETUP_FLAG_COLOR_DEFAULT | \ - XSALPRINTERSETUP_FLAG_DEPTH_DEFAULT ) - - -/* XSalPrinterSetup access */ -extern unsigned short XSalGetPrinterDriverId( const char* driverName ); -extern const char* XSalGetPrinterDriverName( unsigned short driverId ); - -extern unsigned short XSalGetLanguageLevel( const XSalPrinterSetup* pSetup ); -extern void XSalGetLanguageLevels( - const XSalPrinterSetup* pSetup, - unsigned short* minLevel, - unsigned short* maxLevel ); -extern void XSalSetLanguageLevel( XSalPrinterSetup* pSetup, unsigned short); - -extern unsigned short XSalGetDepth( const XSalPrinterSetup* pSetup ); -extern void XSalSetDepth( XSalPrinterSetup* pSetup, unsigned short depth ); - -extern XSalEnum XSalGetColorSpace( const XSalPrinterSetup* pSetup ); -extern void XSalSetColorSpace( XSalPrinterSetup* pSetup, XSalEnum space ); - -extern XSalBool XSalGetBmpCompression( const XSalPrinterSetup* pSetup ); -extern void XSalSetBmpCompression( XSalPrinterSetup* pSetup, XSalBool compress ); - -extern XSalEnum XSalGetOrientation( const char* string ); -extern const char* XSalGetOrientationName( XSalEnum eOrientation ); - -extern XSalEnum XSalGetPaper( const char* sPaperName ); -extern const char* XSalGetPaperName( XSalEnum ePaper ); - -/* use XSalInitPrinterSetup to initialize internal data */ -extern void XSalInitPrinterSetup( XSalPrinterSetup* ); -extern void XSalCorrectEndianess( XSalPrinterSetup* ); -extern void XSalSetupPrinterSetup( XSalPrinterSetup*, Display* display, XLIB_Window parent); - - -/* the following two functions set defaults of the profile */ -extern void XSalReadPrinterSetup( XSalPrinterSetup*, const String& rPrinter ); -extern void XSalReadPrinterSetupDefaults( XSalPrinterSetup* ); - - -typedef Display XSalPrinter; /* an XSalPrinter is a Display. Draw into RootWindow */ - - -/* open, change setup and close printer */ -extern XSalPrinter* XSalOpenPrinter( const XSalPrinterSetup * pSetup, const String& rPrinterName, const String& rPrintFile ); -/* XSalSetupPrinter() can setup: Orientation, Copies, Page, PaperBin */ -extern void XSalSetupPrinter( XSalPrinter *, const XSalPrinterSetup * pSetup ); -extern void XSalClosePrinter( XSalPrinter * ); - -typedef struct -{ - int nWidth; /* [dots] drawable area */ - int nHeight; /* [dots] drawable area */ - int nMarginLeft; /* [dots] left margin */ - int nMarginTop; /* [dots] top margin */ - int nMarginRight; /* [dots] right margin */ - int nMarginBottom; /* [dots] bottom margin */ - int nResolutionX; /* [dpi] resolution x */ - int nResolutionY; /* [dpi] resolution y */ -} XSalPageInfo; - -extern void XSalGetPageInfo( - const XSalPrinter* printer, - const XSalPrinterSetup* pSetup, - XSalPageInfo* pPageInfo ); - - -/* printer job control */ -extern int XSalStartDoc( XSalPrinter * printer, const String& jobName ); -extern int XSalStartPage( XSalPrinter * printer ); -extern int XSalEndPage( XSalPrinter * printer ); -extern int XSalEndDoc( XSalPrinter * printer ); -extern int XSalAbortDoc( XSalPrinter * printer ); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/vcl/unx/source/app/i18n_ic.cxx b/vcl/unx/source/app/i18n_ic.cxx index 9fba669f338d..817fc2781e8c 100644 --- a/vcl/unx/source/app/i18n_ic.cxx +++ b/vcl/unx/source/app/i18n_ic.cxx @@ -340,7 +340,7 @@ SalI18N_InputContext::SalI18N_InputContext ( SalFrame *pFrame ) : if ( mnPreeditStyle != XIMPreeditNone ) { -#if defined LINUX || defined FREEBSD || defined NETBSD || defined IRIX || defined MACOSX +#if defined LINUX || defined FREEBSD || defined NETBSD || defined IRIX if ( mpPreeditAttributes != NULL ) #endif mpAttributes = XVaAddToNestedList( mpAttributes, @@ -348,7 +348,7 @@ SalI18N_InputContext::SalI18N_InputContext ( SalFrame *pFrame ) : } if ( mnStatusStyle != XIMStatusNone ) { -#if defined LINUX || defined FREEBSD || defined NETBSD || defined IRIX || defined MACOSX +#if defined LINUX || defined FREEBSD || defined NETBSD || defined IRIX if ( mpStatusAttributes != NULL ) #endif mpAttributes = XVaAddToNestedList( mpAttributes, diff --git a/vcl/unx/source/app/i18n_im.cxx b/vcl/unx/source/app/i18n_im.cxx index d4ff59e87864..a47cefcef7dd 100644 --- a/vcl/unx/source/app/i18n_im.cxx +++ b/vcl/unx/source/app/i18n_im.cxx @@ -55,10 +55,6 @@ #include "i18n_im.hxx" #include <i18n_status.hxx> -#ifdef MACOSX -#include <osl/process.h> -#include <tools/string.hxx> -#endif #include <osl/thread.h> using namespace vcl; @@ -253,13 +249,8 @@ SalI18N_InputMethod::SetLocale( const char* pLocale ) char *locale = SetSystemLocale( pLocale ); if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) ) { - #ifdef MACOSX // MacOS X always uses UTF-8 for the filesystem - osl_setThreadTextEncoding (RTL_TEXTENCODING_UTF8); - locale = SetSystemLocale( "en_US.UTF-8" ); - #else osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1); locale = SetSystemLocale( "en_US" ); - #endif #ifdef SOLARIS SetSystemEnvironment( "en_US" ); #endif diff --git a/vcl/unx/source/app/i18n_wrp.cxx b/vcl/unx/source/app/i18n_wrp.cxx index f561e18e0ff0..b3a3ebc8e101 100644 --- a/vcl/unx/source/app/i18n_wrp.cxx +++ b/vcl/unx/source/app/i18n_wrp.cxx @@ -45,10 +45,7 @@ struct XIMArg #include <sal/alloca.h> #include <string.h> -#if !defined(MACOSX) -/* MacOS X doesn't yet support XIM... FIXME */ #include <dlfcn.h> -#endif #include <X11/Xlib.h> #include <X11/Xlibint.h> #include "XIM.h" @@ -66,10 +63,8 @@ typedef XIM (*OpenFunction)(Display*, XrmDatabase, char*, char*, XIMArg*); } /* global variables */ -#if !defined(MACOSX) static void *g_dlmodule = 0; static OpenFunction g_open_im = (OpenFunction)NULL; -#endif /* utility function to transform vararg list into an array of XIMArg */ @@ -213,8 +208,6 @@ XvaOpenIM(Display *display, XrmDatabase rdb, XvaGetArgs( variable, args ); va_end(variable); - /* MacOS X doesn't yet support XIM... FIXME */ -#if !defined(MACOSX) if (!g_dlmodule) { g_dlmodule = dlopen(XIIIMP_LIB, RTLD_LAZY); @@ -235,13 +228,10 @@ XvaOpenIM(Display *display, XrmDatabase rdb, { goto legacy_XIM; } -#endif } // in #if to prevent warning "warning: label 'legacy_XIM' defined but not used" -#if !defined(MACOSX) legacy_XIM: -#endif if (!xim) xim = XOpenIM(display, rdb, res_name, res_class); @@ -257,8 +247,6 @@ Status XvaCloseIM(XIM) { Status s = False; - /* MacOS X doesn't yet support XIM... FIXME */ -#if !defined(MACOSX) if (!g_dlmodule) { /* assuming one XvaOpenIM call */ @@ -267,7 +255,6 @@ Status XvaCloseIM(XIM) g_open_im = (OpenFunction)NULL; s = True; } -#endif return (s); } diff --git a/vcl/unx/source/app/keysymnames.cxx b/vcl/unx/source/app/keysymnames.cxx index 2a78110c35fa..4a2bf13af3e0 100644 --- a/vcl/unx/source/app/keysymnames.cxx +++ b/vcl/unx/source/app/keysymnames.cxx @@ -649,18 +649,7 @@ const char* SalDisplay::GetKeyboardName( BOOL bRefresh ) { XkbDescPtr pXkbDesc = NULL; // try X keyboard extension - #ifdef MACOSX - // FIXME - // XDarwin doesn't yet have very good support for the Xkeyboard extension. - // When we call XkbGetKeyboard(), the XServer throws a message up in the - // console about xkbcomp and files for geometry include. The side effect of - // this is _very_ noticable lag when drawing menus. The file menu, for example, - // takes about 1s to come down on my G4/450 DP and you can see it draw. Therefore - // we are disabling it for the moment until better XDarwin support exists. - if ( 0 ) - #else if( (pXkbDesc = XkbGetKeyboard( GetDisplay(), XkbAllComponentsMask, XkbUseCoreKbd )) ) - #endif { const char* pAtom = NULL; if( pXkbDesc->names->groups[0] ) diff --git a/vcl/unx/source/app/makefile.mk b/vcl/unx/source/app/makefile.mk index 948c2b2de5be..a7f790fd67ab 100644 --- a/vcl/unx/source/app/makefile.mk +++ b/vcl/unx/source/app/makefile.mk @@ -71,6 +71,7 @@ SLOFILES=\ EXCEPTIONSFILES=\ $(SLO)$/wmadaptor.obj \ $(SLO)$/saldata.obj \ + $(SLO)$/salinst.obj \ $(SLO)$/saldisp.obj \ $(SLO)$/i18n_status.obj \ $(SLO)$/i18n_cb.obj \ diff --git a/vcl/unx/source/app/saldisp.cxx b/vcl/unx/source/app/saldisp.cxx index e09346d6cfff..95679d77fdfb 100644 --- a/vcl/unx/source/app/saldisp.cxx +++ b/vcl/unx/source/app/saldisp.cxx @@ -65,7 +65,7 @@ #ifdef USE_XINERAMA #ifdef USE_XINERAMA_XORG -#if defined(X86) || defined(X86_64) || defined(MACOSX) +#if defined(X86) || defined(X86_64) #include <X11/extensions/Xinerama.h> #endif #elif defined USE_XINERAMA_XSUN @@ -893,7 +893,7 @@ void SalDisplay::Init() sscanf( pProperties, "%li", &nProperties_ ); else { -#if defined DBG_UTIL || defined SUN || defined LINUX || defined FREEBSD || defined IRIX || defined MACOSX +#if defined DBG_UTIL || defined SUN || defined LINUX || defined FREEBSD || defined IRIX nProperties_ |= PROPERTY_FEATURE_Maximize; #endif // Server Bugs & Properties @@ -919,7 +919,7 @@ void SalDisplay::Init() if( GetServerVendor() == vendor_xfree ) { nProperties_ |= PROPERTY_BUG_XCopyArea_GXxor; -#if defined LINUX || defined FREEBSD || defined MACOSX +#if defined LINUX || defined FREEBSD // otherwm and olwm are a kind of default, which are not detected // carefully. if we are running linux (i.e. not netbsd) on an xfree // display, fvwm is most probable the wm to choose, confusing with mwm @@ -1085,9 +1085,6 @@ void SalDisplay::ModifierMapping() nShiftKeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, ShiftMapIndex ); nCtrlKeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, ControlMapIndex ); nMod1KeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, Mod1MapIndex ); -#ifdef MACOSX - nMod2KeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, Mod2MapIndex ); -#endif // Auf Sun-Servern und SCO-Severn beruecksichtigt XLookupString // nicht den NumLock Modifier. if( (GetServerVendor() == vendor_sun) @@ -1116,25 +1113,12 @@ XubString SalDisplay::GetKeyName( USHORT nKeyCode ) const String aStrMap; if( nKeyCode & KEY_MOD1 ) - { - if( aStrMap.Len() ) - aStrMap += '+'; aStrMap += GetKeyNameFromKeySym( nCtrlKeySym_ ); - } -#ifdef MACOSX - if( nKeyCode & KEY_MOD3 ) - { - aStrMap += GetKeyNameFromKeySym( nMod2KeySym_ ); - } if( nKeyCode & KEY_MOD2 ) { - if ( aStrMap.Len() ) - aStrMap += '+' ; -#else - if( nKeyCode & KEY_MOD2 ) - { -#endif + if( aStrMap.Len() ) + aStrMap += '+'; aStrMap += GetKeyNameFromKeySym( nMod1KeySym_ ); } @@ -2646,7 +2630,7 @@ void SalDisplay::InitXinerama() } } #elif defined(USE_XINERAMA_XORG) -#if defined( X86 ) || defined( X86_64 ) || defined( MACOSX ) +#if defined( X86 ) || defined( X86_64 ) if( XineramaIsActive( pDisp_ ) ) { int nFramebuffers = 1; diff --git a/vcl/unx/source/app/salinst.cxx b/vcl/unx/source/app/salinst.cxx index 18b24e2cb2f3..c160ea4c2fa5 100644 --- a/vcl/unx/source/app/salinst.cxx +++ b/vcl/unx/source/app/salinst.cxx @@ -35,20 +35,21 @@ #include <stdio.h> #include <stdlib.h> -#include <salunx.h> +#include "salunx.h" -#ifndef _VOS_MUTEX_HXX -#include <vos/mutex.hxx> -#endif -#include <saldata.hxx> -#include <saldisp.hxx> -#include <salinst.h> -#include <salframe.h> -#include <vcl/salwtype.hxx> -#include <vcl/salatype.hxx> -#include <dtint.hxx> -#include <salprn.h> -#include <sm.hxx> +#include "saldata.hxx" +#include "saldisp.hxx" +#include "salinst.h" +#include "salframe.h" +#include "dtint.hxx" +#include "salprn.h" +#include "sm.hxx" + +#include "vcl/salwtype.hxx" +#include "vcl/salatype.hxx" +#include "vcl/helper.hxx" + +#include "vos/mutex.hxx" // ------------------------------------------------------------------------- // @@ -276,3 +277,129 @@ void X11SalInstance::DestroyFrame( SalFrame* pFrame ) { delete pFrame; } + +static void getServerDirectories( std::list< rtl::OString >& o_rFontPaths ) +{ +#ifdef LINUX + /* + * chkfontpath exists on some (RH derived) Linux distributions + */ + static const char* pCommands[] = { + "/usr/sbin/chkfontpath 2>/dev/null", "chkfontpath 2>/dev/null" + }; + ::std::list< ByteString > aLines; + + for( unsigned int i = 0; i < sizeof(pCommands)/sizeof(pCommands[0]); i++ ) + { + FILE* pPipe = popen( pCommands[i], "r" ); + aLines.clear(); + if( pPipe ) + { + char line[1024]; + char* pSearch; + while( fgets( line, sizeof(line), pPipe ) ) + { + int nLen = strlen( line ); + if( line[nLen-1] == '\n' ) + line[nLen-1] = 0; + pSearch = strstr( line, ": " ); + if( pSearch ) + aLines.push_back( pSearch+2 ); + } + if( ! pclose( pPipe ) ) + break; + } + } + + for( ::std::list< ByteString >::iterator it = aLines.begin(); it != aLines.end(); ++it ) + { + if( ! access( it->GetBuffer(), F_OK ) ) + { + o_rFontPaths.push_back( *it ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding fs dir %s\n", it->GetBuffer() ); +#endif + } + } +#else + (void)o_rFontPaths; +#endif +} + + + +void X11SalInstance::FillFontPathList( std::list< rtl::OString >& o_rFontPaths ) +{ + Display *pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + + DBG_ASSERT( pDisplay, "No Display !" ); + if( pDisplay ) + { + // get font paths to look for fonts + int nPaths = 0, i; + char** pPaths = XGetFontPath( pDisplay, &nPaths ); + + bool bServerDirs = false; + for( i = 0; i < nPaths; i++ ) + { + OString aPath( pPaths[i] ); + sal_Int32 nPos = 0; + if( ! bServerDirs + && ( nPos = aPath.indexOf( ':' ) ) > 0 + && ( !aPath.copy(nPos).equals( ":unscaled" ) ) ) + { + bServerDirs = true; + getServerDirectories( o_rFontPaths ); + } + else + { + psp::normPath( aPath ); + o_rFontPaths.push_back( aPath ); + } + } + + if( nPaths ) + XFreeFontPath( pPaths ); + } + + // insert some standard directories + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/TrueType" ); + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/Type1" ); + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/Type1/sun" ); + o_rFontPaths.push_back( "/usr/X11R6/lib/X11/fonts/truetype" ); + o_rFontPaths.push_back( "/usr/X11R6/lib/X11/fonts/Type1" ); + + #ifdef SOLARIS + /* cde specials, from /usr/dt/bin/Xsession: here are the good fonts, + the OWfontpath file may contain as well multiple lines as a comma + separated list of fonts in each line. to make it even more weird + environment variables are allowed as well */ + + const char* lang = getenv("LANG"); + if ( lang != NULL ) + { + String aOpenWinDir( String::CreateFromAscii( "/usr/openwin/lib/locale/" ) ); + aOpenWinDir.AppendAscii( lang ); + aOpenWinDir.AppendAscii( "/OWfontpath" ); + + SvFileStream aStream( aOpenWinDir, STREAM_READ ); + + // TODO: replace environment variables + while( aStream.IsOpen() && ! aStream.IsEof() ) + { + ByteString aLine; + aStream.ReadLine( aLine ); + // need an OString for normpath + OString aNLine( aLine ); + psp::normPath( aNLine ); + aLine = aNLine; + // try to avoid bad fonts in some cases + static bool bAvoid = (strncasecmp( lang, "ar", 2 ) == 0) || (strncasecmp( lang, "he", 2 ) == 0) || strncasecmp( lang, "iw", 2 ) == 0 || (strncasecmp( lang, "hi", 2 ) == 0); + if( bAvoid && aLine.Search( "iso_8859" ) != STRING_NOTFOUND ) + continue; + o_rFontPaths.push_back( aLine ); + } + } + #endif /* SOLARIS */ +} + diff --git a/vcl/unx/source/app/wmadaptor.cxx b/vcl/unx/source/app/wmadaptor.cxx index e099af0de0fb..37015b6e58d6 100644 --- a/vcl/unx/source/app/wmadaptor.cxx +++ b/vcl/unx/source/app/wmadaptor.cxx @@ -405,12 +405,6 @@ WMAdaptor::WMAdaptor( SalDisplay* pDisplay ) : XFree( pProperty ); } } - -#ifdef MACOSX - /* Apple's X11 needs NW gravity with OOo 1.1 */ - m_nWinGravity = NorthWestGravity; - m_nInitWinGravity = NorthWestGravity; -#endif } /* diff --git a/vcl/unx/source/fontmanager/adobeenc.tab b/vcl/unx/source/fontmanager/adobeenc.tab new file mode 100644 index 000000000000..e4005a87849f --- /dev/null +++ b/vcl/unx/source/fontmanager/adobeenc.tab @@ -0,0 +1,1090 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: adobeenc.tab,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +struct AdobeEncEntry { + sal_Unicode aUnicode; + sal_uInt8 aAdobeStandardCode; + const char* const pAdobename; +}; + +static const AdobeEncEntry aAdobeCodes[]= +{ + { 0x0041, 0101, "A" }, + { 0x00C6, 0341, "AE" }, + { 0x01FC, 0, "AEacute" }, + { 0xF7E6, 0, "AEsmall" }, + { 0x00C1, 0, "Aacute" }, + { 0xF7E1, 0, "Aacutesmall" }, + { 0x0102, 0, "Abreve" }, + { 0x00C2, 0, "Acircumflex" }, + { 0xF7E2, 0, "Acircumflexsmall" }, + { 0xF6C9, 0, "Acute" }, + { 0xF7B4, 0, "Acutesmall" }, + { 0x00C4, 0, "Adieresis" }, + { 0xF7E4, 0, "Adieresissmall" }, + { 0x00C0, 0, "Agrave" }, + { 0xF7E0, 0, "Agravesmall" }, + { 0x0391, 0, "Alpha" }, + { 0x0386, 0, "Alphatonos" }, + { 0x0100, 0, "Amacron" }, + { 0x0104, 0, "Aogonek" }, + { 0x00C5, 0, "Aring" }, + { 0x01FA, 0, "Aringacute" }, + { 0xF7E5, 0, "Aringsmall" }, + { 0xF761, 0, "Asmall" }, + { 0x00C3, 0, "Atilde" }, + { 0xF7E3, 0, "Atildesmall" }, + { 0x0042, 0102, "B" }, + { 0x0392, 0, "Beta" }, + { 0xF6F4, 0, "Brevesmall" }, + { 0xF762, 0, "Bsmall" }, + { 0x0043, 0103, "C" }, + { 0x0106, 0, "Cacute" }, + { 0xF6CA, 0, "Caron" }, + { 0xF6F5, 0, "Caronsmall" }, + { 0x010C, 0, "Ccaron" }, + { 0x00C7, 0, "Ccedilla" }, + { 0xF7E7, 0, "Ccedillasmall" }, + { 0x0108, 0, "Ccircumflex" }, + { 0x010A, 0, "Cdotaccent" }, + { 0xF7B8, 0, "Cedillasmall" }, + { 0x03A7, 0, "Chi" }, + { 0xF6F6, 0, "Circumflexsmall" }, + { 0xF763, 0, "Csmall" }, + { 0x0044, 0104, "D" }, + { 0x010E, 0, "Dcaron" }, + { 0x0110, 0, "Dcroat" }, + { 0x2206, 0, "Delta" }, + { 0x0394, 0, "Delta" }, + { 0xF6CB, 0, "Dieresis" }, + { 0xF6CC, 0, "DieresisAcute" }, + { 0xF6CD, 0, "DieresisGrave" }, + { 0xF7A8, 0, "Dieresissmall" }, + { 0xF6F7, 0, "Dotaccentsmall" }, + { 0xF764, 0, "Dsmall" }, + { 0x0045, 0105, "E" }, + { 0x00C9, 0, "Eacute" }, + { 0xF7E9, 0, "Eacutesmall" }, + { 0x0114, 0, "Ebreve" }, + { 0x011A, 0, "Ecaron" }, + { 0x00CA, 0, "Ecircumflex" }, + { 0xF7EA, 0, "Ecircumflexsmall" }, + { 0x00CB, 0, "Edieresis" }, + { 0xF7EB, 0, "Edieresissmall" }, + { 0x0116, 0, "Edotaccent" }, + { 0x00C8, 0, "Egrave" }, + { 0xF7E8, 0, "Egravesmall" }, + { 0x0112, 0, "Emacron" }, + { 0x014A, 0, "Eng" }, + { 0x0118, 0, "Eogonek" }, + { 0x0395, 0, "Epsilon" }, + { 0x0388, 0, "Epsilontonos" }, + { 0xF765, 0, "Esmall" }, + { 0x0397, 0, "Eta" }, + { 0x0389, 0, "Etatonos" }, + { 0x00D0, 0, "Eth" }, + { 0xF7F0, 0, "Ethsmall" }, + { 0x20AC, 0, "Euro" }, + { 0x0046, 0106, "F" }, + { 0xF766, 0, "Fsmall" }, + { 0x0047, 0107, "G" }, + { 0x0393, 0, "Gamma" }, + { 0x011E, 0, "Gbreve" }, + { 0x01E6, 0, "Gcaron" }, + { 0x011C, 0, "Gcircumflex" }, + { 0x0122, 0, "Gcommaaccent" }, + { 0x0120, 0, "Gdotaccent" }, + { 0xF6CE, 0, "Grave" }, + { 0xF760, 0, "Gravesmall" }, + { 0xF767, 0, "Gsmall" }, + { 0x0048, 0110, "H" }, + { 0x25CF, 0, "H18533" }, + { 0x25AA, 0, "H18543" }, + { 0x25AB, 0, "H18551" }, + { 0x25A1, 0, "H22073" }, + { 0x0126, 0, "Hbar" }, + { 0x0124, 0, "Hcircumflex" }, + { 0xF768, 0, "Hsmall" }, + { 0xF6CF, 0, "Hungarumlaut" }, + { 0xF6F8, 0, "Hungarumlautsmall" }, + { 0x0049, 0111, "I" }, + { 0x0132, 0, "IJ" }, + { 0x00CD, 0, "Iacute" }, + { 0xF7ED, 0, "Iacutesmall" }, + { 0x012C, 0, "Ibreve" }, + { 0x00CE, 0, "Icircumflex" }, + { 0xF7EE, 0, "Icircumflexsmall" }, + { 0x00CF, 0, "Idieresis" }, + { 0xF7EF, 0, "Idieresissmall" }, + { 0x0130, 0, "Idotaccent" }, + { 0x2111, 0, "Ifraktur" }, + { 0x00CC, 0, "Igrave" }, + { 0xF7EC, 0, "Igravesmall" }, + { 0x012A, 0, "Imacron" }, + { 0x012E, 0, "Iogonek" }, + { 0x0399, 0, "Iota" }, + { 0x03AA, 0, "Iotadieresis" }, + { 0x038A, 0, "Iotatonos" }, + { 0xF769, 0, "Ismall" }, + { 0x0128, 0, "Itilde" }, + { 0x004A, 0112, "J" }, + { 0x0134, 0, "Jcircumflex" }, + { 0xF76A, 0, "Jsmall" }, + { 0x004B, 0113, "K" }, + { 0x039A, 0, "Kappa" }, + { 0x0136, 0, "Kcommaaccent" }, + { 0xF76B, 0, "Ksmall" }, + { 0x004C, 0114, "L" }, + { 0xF6BF, 0, "LL" }, + { 0x0139, 0, "Lacute" }, + { 0x039B, 0, "Lambda" }, + { 0x013D, 0, "Lcaron" }, + { 0x013B, 0, "Lcommaaccent" }, + { 0x013F, 0, "Ldot" }, + { 0x0141, 0350, "Lslash" }, + { 0xF6F9, 0, "Lslashsmall" }, + { 0xF76C, 0, "Lsmall" }, + { 0x004D, 0115, "M" }, + { 0xF6D0, 0, "Macron" }, + { 0xF7AF, 0, "Macronsmall" }, + { 0xF76D, 0, "Msmall" }, + { 0x039C, 0, "Mu" }, + { 0x004E, 0116, "N" }, + { 0x0143, 0, "Nacute" }, + { 0x0147, 0, "Ncaron" }, + { 0x0145, 0, "Ncommaaccent" }, + { 0xF76E, 0, "Nsmall" }, + { 0x00D1, 0, "Ntilde" }, + { 0xF7F1, 0, "Ntildesmall" }, + { 0x039D, 0, "Nu" }, + { 0x004F, 0117, "O" }, + { 0x0152, 0, "OE" }, + { 0xF6FA, 0, "OEsmall" }, + { 0x00D3, 0, "Oacute" }, + { 0xF7F3, 0, "Oacutesmall" }, + { 0x014E, 0, "Obreve" }, + { 0x00D4, 0, "Ocircumflex" }, + { 0xF7F4, 0, "Ocircumflexsmall" }, + { 0x00D6, 0, "Odieresis" }, + { 0xF7F6, 0, "Odieresissmall" }, + { 0xF6FB, 0, "Ogoneksmall" }, + { 0x00D2, 0, "Ograve" }, + { 0xF7F2, 0, "Ogravesmall" }, + { 0x01A0, 0, "Ohorn" }, + { 0x0150, 0, "Ohungarumlaut" }, + { 0x014C, 0, "Omacron" }, + { 0x2126, 0, "Omega" }, + { 0x03A9, 0, "Omega" }, + { 0x038F, 0, "Omegatonos" }, + { 0x039F, 0, "Omicron" }, + { 0x038C, 0, "Omicrontonos" }, + { 0x00D8, 0351, "Oslash" }, + { 0x01FE, 0, "Oslashacute" }, + { 0xF7F8, 0, "Oslashsmall" }, + { 0xF76F, 0, "Osmall" }, + { 0x00D5, 0, "Otilde" }, + { 0xF7F5, 0, "Otildesmall" }, + { 0x0050, 0120, "P" }, + { 0x03A6, 0, "Phi" }, + { 0x03A0, 0, "Pi" }, + { 0x03A8, 0, "Psi" }, + { 0xF770, 0, "Psmall" }, + { 0x0051, 0121, "Q" }, + { 0xF771, 0, "Qsmall" }, + { 0x0052, 0122, "R" }, + { 0x0154, 0, "Racute" }, + { 0x0158, 0, "Rcaron" }, + { 0x0156, 0, "Rcommaaccent" }, + { 0x211C, 0, "Rfraktur" }, + { 0x03A1, 0, "Rho" }, + { 0xF6FC, 0, "Ringsmall" }, + { 0xF772, 0, "Rsmall" }, + { 0x0053, 0123, "S" }, + { 0x250C, 0, "SF010000" }, + { 0x2514, 0, "SF020000" }, + { 0x2510, 0, "SF030000" }, + { 0x2518, 0, "SF040000" }, + { 0x253C, 0, "SF050000" }, + { 0x252C, 0, "SF060000" }, + { 0x2534, 0, "SF070000" }, + { 0x251C, 0, "SF080000" }, + { 0x2524, 0, "SF090000" }, + { 0x2500, 0, "SF100000" }, + { 0x2502, 0, "SF110000" }, + { 0x2561, 0, "SF190000" }, + { 0x2562, 0, "SF200000" }, + { 0x2556, 0, "SF210000" }, + { 0x2555, 0, "SF220000" }, + { 0x2563, 0, "SF230000" }, + { 0x2551, 0, "SF240000" }, + { 0x2557, 0, "SF250000" }, + { 0x255D, 0, "SF260000" }, + { 0x255C, 0, "SF270000" }, + { 0x255B, 0, "SF280000" }, + { 0x255E, 0, "SF360000" }, + { 0x255F, 0, "SF370000" }, + { 0x255A, 0, "SF380000" }, + { 0x2554, 0, "SF390000" }, + { 0x2569, 0, "SF400000" }, + { 0x2566, 0, "SF410000" }, + { 0x2560, 0, "SF420000" }, + { 0x2550, 0, "SF430000" }, + { 0x256C, 0, "SF440000" }, + { 0x2567, 0, "SF450000" }, + { 0x2568, 0, "SF460000" }, + { 0x2564, 0, "SF470000" }, + { 0x2565, 0, "SF480000" }, + { 0x2559, 0, "SF490000" }, + { 0x2558, 0, "SF500000" }, + { 0x2552, 0, "SF510000" }, + { 0x2553, 0, "SF520000" }, + { 0x256B, 0, "SF530000" }, + { 0x256A, 0, "SF540000" }, + { 0x015A, 0, "Sacute" }, + { 0x0160, 0, "Scaron" }, + { 0xF6FD, 0, "Scaronsmall" }, + { 0x015E, 0, "Scedilla" }, + { 0xF6C1, 0, "Scedilla" }, + { 0x015C, 0, "Scircumflex" }, + { 0x0218, 0, "Scommaaccent" }, + { 0x03A3, 0, "Sigma" }, + { 0xF773, 0, "Ssmall" }, + { 0x0054, 0124, "T" }, + { 0x03A4, 0, "Tau" }, + { 0x0166, 0, "Tbar" }, + { 0x0164, 0, "Tcaron" }, + { 0x0162, 0, "Tcommaaccent" }, + { 0x021A, 0, "Tcommaaccent" }, + { 0x0398, 0, "Theta" }, + { 0x00DE, 0, "Thorn" }, + { 0xF7FE, 0, "Thornsmall" }, + { 0xF6FE, 0, "Tildesmall" }, + { 0xF774, 0, "Tsmall" }, + { 0x0055, 0125, "U" }, + { 0x00DA, 0, "Uacute" }, + { 0xF7FA, 0, "Uacutesmall" }, + { 0x016C, 0, "Ubreve" }, + { 0x00DB, 0, "Ucircumflex" }, + { 0xF7FB, 0, "Ucircumflexsmall" }, + { 0x00DC, 0, "Udieresis" }, + { 0xF7FC, 0, "Udieresissmall" }, + { 0x00D9, 0, "Ugrave" }, + { 0xF7F9, 0, "Ugravesmall" }, + { 0x01AF, 0, "Uhorn" }, + { 0x0170, 0, "Uhungarumlaut" }, + { 0x016A, 0, "Umacron" }, + { 0x0172, 0, "Uogonek" }, + { 0x03A5, 0, "Upsilon" }, + { 0x03D2, 0, "Upsilon1" }, + { 0x03AB, 0, "Upsilondieresis" }, + { 0x038E, 0, "Upsilontonos" }, + { 0x016E, 0, "Uring" }, + { 0xF775, 0, "Usmall" }, + { 0x0168, 0, "Utilde" }, + { 0x0056, 0126, "V" }, + { 0xF776, 0, "Vsmall" }, + { 0x0057, 0127, "W" }, + { 0x1E82, 0, "Wacute" }, + { 0x0174, 0, "Wcircumflex" }, + { 0x1E84, 0, "Wdieresis" }, + { 0x1E80, 0, "Wgrave" }, + { 0xF777, 0, "Wsmall" }, + { 0x0058, 0130, "X" }, + { 0x039E, 0, "Xi" }, + { 0xF778, 0, "Xsmall" }, + { 0x0059, 0131, "Y" }, + { 0x00DD, 0, "Yacute" }, + { 0xF7FD, 0, "Yacutesmall" }, + { 0x0176, 0, "Ycircumflex" }, + { 0x0178, 0, "Ydieresis" }, + { 0xF7FF, 0, "Ydieresissmall" }, + { 0x1EF2, 0, "Ygrave" }, + { 0xF779, 0, "Ysmall" }, + { 0x005A, 0132, "Z" }, + { 0x0179, 0, "Zacute" }, + { 0x017D, 0, "Zcaron" }, + { 0xF6FF, 0, "Zcaronsmall" }, + { 0x017B, 0, "Zdotaccent" }, + { 0x0396, 0, "Zeta" }, + { 0xF77A, 0, "Zsmall" }, + { 0x0061, 0141, "a" }, + { 0x00E1, 0, "aacute" }, + { 0x0103, 0, "abreve" }, + { 0x00E2, 0, "acircumflex" }, + { 0x00B4, 0302, "acute" }, + { 0x0301, 0, "acutecomb" }, + { 0x00E4, 0, "adieresis" }, + { 0x00E6, 0361, "ae" }, + { 0x01FD, 0, "aeacute" }, + { 0x2015, 0, "afii00208" }, + { 0x0410, 0, "afii10017" }, + { 0x0411, 0, "afii10018" }, + { 0x0412, 0, "afii10019" }, + { 0x0413, 0, "afii10020" }, + { 0x0414, 0, "afii10021" }, + { 0x0415, 0, "afii10022" }, + { 0x0401, 0, "afii10023" }, + { 0x0416, 0, "afii10024" }, + { 0x0417, 0, "afii10025" }, + { 0x0418, 0, "afii10026" }, + { 0x0419, 0, "afii10027" }, + { 0x041A, 0, "afii10028" }, + { 0x041B, 0, "afii10029" }, + { 0x041C, 0, "afii10030" }, + { 0x041D, 0, "afii10031" }, + { 0x041E, 0, "afii10032" }, + { 0x041F, 0, "afii10033" }, + { 0x0420, 0, "afii10034" }, + { 0x0421, 0, "afii10035" }, + { 0x0422, 0, "afii10036" }, + { 0x0423, 0, "afii10037" }, + { 0x0424, 0, "afii10038" }, + { 0x0425, 0, "afii10039" }, + { 0x0426, 0, "afii10040" }, + { 0x0427, 0, "afii10041" }, + { 0x0428, 0, "afii10042" }, + { 0x0429, 0, "afii10043" }, + { 0x042A, 0, "afii10044" }, + { 0x042B, 0, "afii10045" }, + { 0x042C, 0, "afii10046" }, + { 0x042D, 0, "afii10047" }, + { 0x042E, 0, "afii10048" }, + { 0x042F, 0, "afii10049" }, + { 0x0490, 0, "afii10050" }, + { 0x0402, 0, "afii10051" }, + { 0x0403, 0, "afii10052" }, + { 0x0404, 0, "afii10053" }, + { 0x0405, 0, "afii10054" }, + { 0x0406, 0, "afii10055" }, + { 0x0407, 0, "afii10056" }, + { 0x0408, 0, "afii10057" }, + { 0x0409, 0, "afii10058" }, + { 0x040A, 0, "afii10059" }, + { 0x040B, 0, "afii10060" }, + { 0x040C, 0, "afii10061" }, + { 0x040E, 0, "afii10062" }, + { 0xF6C4, 0, "afii10063" }, + { 0xF6C5, 0, "afii10064" }, + { 0x0430, 0, "afii10065" }, + { 0x0431, 0, "afii10066" }, + { 0x0432, 0, "afii10067" }, + { 0x0433, 0, "afii10068" }, + { 0x0434, 0, "afii10069" }, + { 0x0435, 0, "afii10070" }, + { 0x0451, 0, "afii10071" }, + { 0x0436, 0, "afii10072" }, + { 0x0437, 0, "afii10073" }, + { 0x0438, 0, "afii10074" }, + { 0x0439, 0, "afii10075" }, + { 0x043A, 0, "afii10076" }, + { 0x043B, 0, "afii10077" }, + { 0x043C, 0, "afii10078" }, + { 0x043D, 0, "afii10079" }, + { 0x043E, 0, "afii10080" }, + { 0x043F, 0, "afii10081" }, + { 0x0440, 0, "afii10082" }, + { 0x0441, 0, "afii10083" }, + { 0x0442, 0, "afii10084" }, + { 0x0443, 0, "afii10085" }, + { 0x0444, 0, "afii10086" }, + { 0x0445, 0, "afii10087" }, + { 0x0446, 0, "afii10088" }, + { 0x0447, 0, "afii10089" }, + { 0x0448, 0, "afii10090" }, + { 0x0449, 0, "afii10091" }, + { 0x044A, 0, "afii10092" }, + { 0x044B, 0, "afii10093" }, + { 0x044C, 0, "afii10094" }, + { 0x044D, 0, "afii10095" }, + { 0x044E, 0, "afii10096" }, + { 0x044F, 0, "afii10097" }, + { 0x0491, 0, "afii10098" }, + { 0x0452, 0, "afii10099" }, + { 0x0453, 0, "afii10100" }, + { 0x0454, 0, "afii10101" }, + { 0x0455, 0, "afii10102" }, + { 0x0456, 0, "afii10103" }, + { 0x0457, 0, "afii10104" }, + { 0x0458, 0, "afii10105" }, + { 0x0459, 0, "afii10106" }, + { 0x045A, 0, "afii10107" }, + { 0x045B, 0, "afii10108" }, + { 0x045C, 0, "afii10109" }, + { 0x045E, 0, "afii10110" }, + { 0x040F, 0, "afii10145" }, + { 0x0462, 0, "afii10146" }, + { 0x0472, 0, "afii10147" }, + { 0x0474, 0, "afii10148" }, + { 0xF6C6, 0, "afii10192" }, + { 0x045F, 0, "afii10193" }, + { 0x0463, 0, "afii10194" }, + { 0x0473, 0, "afii10195" }, + { 0x0475, 0, "afii10196" }, + { 0xF6C7, 0, "afii10831" }, + { 0xF6C8, 0, "afii10832" }, + { 0x04D9, 0, "afii10846" }, + { 0x200E, 0, "afii299" }, + { 0x200F, 0, "afii300" }, + { 0x200D, 0, "afii301" }, + { 0x066A, 0, "afii57381" }, + { 0x060C, 0, "afii57388" }, + { 0x0660, 0, "afii57392" }, + { 0x0661, 0, "afii57393" }, + { 0x0662, 0, "afii57394" }, + { 0x0663, 0, "afii57395" }, + { 0x0664, 0, "afii57396" }, + { 0x0665, 0, "afii57397" }, + { 0x0666, 0, "afii57398" }, + { 0x0667, 0, "afii57399" }, + { 0x0668, 0, "afii57400" }, + { 0x0669, 0, "afii57401" }, + { 0x061B, 0, "afii57403" }, + { 0x061F, 0, "afii57407" }, + { 0x0621, 0, "afii57409" }, + { 0x0622, 0, "afii57410" }, + { 0x0623, 0, "afii57411" }, + { 0x0624, 0, "afii57412" }, + { 0x0625, 0, "afii57413" }, + { 0x0626, 0, "afii57414" }, + { 0x0627, 0, "afii57415" }, + { 0x0628, 0, "afii57416" }, + { 0x0629, 0, "afii57417" }, + { 0x062A, 0, "afii57418" }, + { 0x062B, 0, "afii57419" }, + { 0x062C, 0, "afii57420" }, + { 0x062D, 0, "afii57421" }, + { 0x062E, 0, "afii57422" }, + { 0x062F, 0, "afii57423" }, + { 0x0630, 0, "afii57424" }, + { 0x0631, 0, "afii57425" }, + { 0x0632, 0, "afii57426" }, + { 0x0633, 0, "afii57427" }, + { 0x0634, 0, "afii57428" }, + { 0x0635, 0, "afii57429" }, + { 0x0636, 0, "afii57430" }, + { 0x0637, 0, "afii57431" }, + { 0x0638, 0, "afii57432" }, + { 0x0639, 0, "afii57433" }, + { 0x063A, 0, "afii57434" }, + { 0x0640, 0, "afii57440" }, + { 0x0641, 0, "afii57441" }, + { 0x0642, 0, "afii57442" }, + { 0x0643, 0, "afii57443" }, + { 0x0644, 0, "afii57444" }, + { 0x0645, 0, "afii57445" }, + { 0x0646, 0, "afii57446" }, + { 0x0648, 0, "afii57448" }, + { 0x0649, 0, "afii57449" }, + { 0x064A, 0, "afii57450" }, + { 0x064B, 0, "afii57451" }, + { 0x064C, 0, "afii57452" }, + { 0x064D, 0, "afii57453" }, + { 0x064E, 0, "afii57454" }, + { 0x064F, 0, "afii57455" }, + { 0x0650, 0, "afii57456" }, + { 0x0651, 0, "afii57457" }, + { 0x0652, 0, "afii57458" }, + { 0x0647, 0, "afii57470" }, + { 0x06A4, 0, "afii57505" }, + { 0x067E, 0, "afii57506" }, + { 0x0686, 0, "afii57507" }, + { 0x0698, 0, "afii57508" }, + { 0x06AF, 0, "afii57509" }, + { 0x0679, 0, "afii57511" }, + { 0x0688, 0, "afii57512" }, + { 0x0691, 0, "afii57513" }, + { 0x06BA, 0, "afii57514" }, + { 0x06D2, 0, "afii57519" }, + { 0x06D5, 0, "afii57534" }, + { 0x20AA, 0, "afii57636" }, + { 0x05BE, 0, "afii57645" }, + { 0x05C3, 0, "afii57658" }, + { 0x05D0, 0, "afii57664" }, + { 0x05D1, 0, "afii57665" }, + { 0x05D2, 0, "afii57666" }, + { 0x05D3, 0, "afii57667" }, + { 0x05D4, 0, "afii57668" }, + { 0x05D5, 0, "afii57669" }, + { 0x05D6, 0, "afii57670" }, + { 0x05D7, 0, "afii57671" }, + { 0x05D8, 0, "afii57672" }, + { 0x05D9, 0, "afii57673" }, + { 0x05DA, 0, "afii57674" }, + { 0x05DB, 0, "afii57675" }, + { 0x05DC, 0, "afii57676" }, + { 0x05DD, 0, "afii57677" }, + { 0x05DE, 0, "afii57678" }, + { 0x05DF, 0, "afii57679" }, + { 0x05E0, 0, "afii57680" }, + { 0x05E1, 0, "afii57681" }, + { 0x05E2, 0, "afii57682" }, + { 0x05E3, 0, "afii57683" }, + { 0x05E4, 0, "afii57684" }, + { 0x05E5, 0, "afii57685" }, + { 0x05E6, 0, "afii57686" }, + { 0x05E7, 0, "afii57687" }, + { 0x05E8, 0, "afii57688" }, + { 0x05E9, 0, "afii57689" }, + { 0x05EA, 0, "afii57690" }, + { 0xFB2A, 0, "afii57694" }, + { 0xFB2B, 0, "afii57695" }, + { 0xFB4B, 0, "afii57700" }, + { 0xFB1F, 0, "afii57705" }, + { 0x05F0, 0, "afii57716" }, + { 0x05F1, 0, "afii57717" }, + { 0x05F2, 0, "afii57718" }, + { 0xFB35, 0, "afii57723" }, + { 0x05B4, 0, "afii57793" }, + { 0x05B5, 0, "afii57794" }, + { 0x05B6, 0, "afii57795" }, + { 0x05BB, 0, "afii57796" }, + { 0x05B8, 0, "afii57797" }, + { 0x05B7, 0, "afii57798" }, + { 0x05B0, 0, "afii57799" }, + { 0x05B2, 0, "afii57800" }, + { 0x05B1, 0, "afii57801" }, + { 0x05B3, 0, "afii57802" }, + { 0x05C2, 0, "afii57803" }, + { 0x05C1, 0, "afii57804" }, + { 0x05B9, 0, "afii57806" }, + { 0x05BC, 0, "afii57807" }, + { 0x05BD, 0, "afii57839" }, + { 0x05BF, 0, "afii57841" }, + { 0x05C0, 0, "afii57842" }, + { 0x02BC, 0, "afii57929" }, + { 0x2105, 0, "afii61248" }, + { 0x2113, 0, "afii61289" }, + { 0x2116, 0, "afii61352" }, + { 0x202C, 0, "afii61573" }, + { 0x202D, 0, "afii61574" }, + { 0x202E, 0, "afii61575" }, + { 0x200C, 0, "afii61664" }, + { 0x066D, 0, "afii63167" }, + { 0x02BD, 0, "afii64937" }, + { 0x00E0, 0, "agrave" }, + { 0x2135, 0, "aleph" }, + { 0x03B1, 0, "alpha" }, + { 0x03AC, 0, "alphatonos" }, + { 0x0101, 0, "amacron" }, + { 0x0026, 046, "ampersand" }, + { 0xF726, 0, "ampersandsmall" }, + { 0x2220, 0, "angle" }, + { 0x2329, 0, "angleleft" }, + { 0x232A, 0, "angleright" }, + { 0x0387, 0, "anoteleia" }, + { 0x0105, 0, "aogonek" }, + { 0x2248, 0, "approxequal" }, + { 0x00E5, 0, "aring" }, + { 0x01FB, 0, "aringacute" }, + { 0x2194, 0, "arrowboth" }, + { 0x21D4, 0, "arrowdblboth" }, + { 0x21D3, 0, "arrowdbldown" }, + { 0x21D0, 0, "arrowdblleft" }, + { 0x21D2, 0, "arrowdblright" }, + { 0x21D1, 0, "arrowdblup" }, + { 0x2193, 0, "arrowdown" }, + { 0xF8E7, 0, "arrowhorizex" }, + { 0x2190, 0, "arrowleft" }, + { 0x2192, 0, "arrowright" }, + { 0x2191, 0, "arrowup" }, + { 0x2195, 0, "arrowupdn" }, + { 0x21A8, 0, "arrowupdnbse" }, + { 0xF8E6, 0, "arrowvertex" }, + { 0x005E, 0136, "asciicircum" }, + { 0x007E, 0176, "asciitilde" }, + { 0x002A, 052, "asterisk" }, + { 0x2217, 0, "asteriskmath" }, + { 0xF6E9, 0, "asuperior" }, + { 0x0040, 0100, "at" }, + { 0x00E3, 0, "atilde" }, + { 0x0062, 0142, "b" }, + { 0x005C, 0134, "backslash" }, + { 0x007C, 0174, "bar" }, + { 0x03B2, 0, "beta" }, + { 0x2588, 0, "block" }, + { 0xF8F4, 0, "braceex" }, + { 0x007B, 0173, "braceleft" }, + { 0xF8F3, 0, "braceleftbt" }, + { 0xF8F2, 0, "braceleftmid" }, + { 0xF8F1, 0, "bracelefttp" }, + { 0x007D, 0175, "braceright" }, + { 0xF8FE, 0, "bracerightbt" }, + { 0xF8FD, 0, "bracerightmid" }, + { 0xF8FC, 0, "bracerighttp" }, + { 0x005B, 0133, "bracketleft" }, + { 0xF8F0, 0, "bracketleftbt" }, + { 0xF8EF, 0, "bracketleftex" }, + { 0xF8EE, 0, "bracketlefttp" }, + { 0x005D, 0135, "bracketright" }, + { 0xF8FB, 0, "bracketrightbt" }, + { 0xF8FA, 0, "bracketrightex" }, + { 0xF8F9, 0, "bracketrighttp" }, + { 0x02D8, 0306, "breve" }, + { 0x00A6, 0, "brokenbar" }, + { 0xF6EA, 0, "bsuperior" }, + { 0x2022, 0267, "bullet" }, + { 0x0063, 0143, "c" }, + { 0x0107, 0, "cacute" }, + { 0x02C7, 0317, "caron" }, + { 0x21B5, 0, "carriagereturn" }, + { 0x010D, 0, "ccaron" }, + { 0x00E7, 0, "ccedilla" }, + { 0x0109, 0, "ccircumflex" }, + { 0x010B, 0, "cdotaccent" }, + { 0x00B8, 0313, "cedilla" }, + { 0x00A2, 0242, "cent" }, + { 0xF6DF, 0, "centinferior" }, + { 0xF7A2, 0, "centoldstyle" }, + { 0xF6E0, 0, "centsuperior" }, + { 0x03C7, 0, "chi" }, + { 0x25CB, 0, "circle" }, + { 0x2297, 0, "circlemultiply" }, + { 0x2295, 0, "circleplus" }, + { 0x02C6, 0303, "circumflex" }, + { 0x2663, 0, "club" }, + { 0x003A, 072, "colon" }, + { 0x20A1, 0, "colonmonetary" }, + { 0x002C, 054, "comma" }, + { 0xF6C3, 0, "commaaccent" }, + { 0xF6E1, 0, "commainferior" }, + { 0xF6E2, 0, "commasuperior" }, + { 0x2245, 0, "congruent" }, + { 0x00A9, 0, "copyright" }, + { 0xF8E9, 0, "copyrightsans" }, + { 0xF6D9, 0, "copyrightserif" }, + { 0x00A4, 0250, "currency" }, + { 0xF6D1, 0, "cyrBreve" }, + { 0xF6D2, 0, "cyrFlex" }, + { 0xF6D4, 0, "cyrbreve" }, + { 0xF6D5, 0, "cyrflex" }, + { 0x0064, 0144, "d" }, + { 0x2020, 0262, "dagger" }, + { 0x2021, 0263, "daggerdbl" }, + { 0xF6D3, 0, "dblGrave" }, + { 0xF6D6, 0, "dblgrave" }, + { 0x010F, 0, "dcaron" }, + { 0x0111, 0, "dcroat" }, + { 0x00B0, 0, "degree" }, + { 0x03B4, 0, "delta" }, + { 0x2666, 0, "diamond" }, + { 0x00A8, 0310, "dieresis" }, + { 0xF6D7, 0, "dieresisacute" }, + { 0xF6D8, 0, "dieresisgrave" }, + { 0x0385, 0, "dieresistonos" }, + { 0x00F7, 0, "divide" }, + { 0x2593, 0, "dkshade" }, + { 0x2584, 0, "dnblock" }, + { 0x0024, 044, "dollar" }, + { 0xF6E3, 0, "dollarinferior" }, + { 0xF724, 0, "dollaroldstyle" }, + { 0xF6E4, 0, "dollarsuperior" }, + { 0x20AB, 0, "dong" }, + { 0x02D9, 0307, "dotaccent" }, + { 0x0323, 0, "dotbelowcomb" }, + { 0x0131, 0365, "dotlessi" }, + { 0xF6BE, 0, "dotlessj" }, + { 0x22C5, 0, "dotmath" }, + { 0xF6EB, 0, "dsuperior" }, + { 0x0065, 0145, "e" }, + { 0x00E9, 0, "eacute" }, + { 0x0115, 0, "ebreve" }, + { 0x011B, 0, "ecaron" }, + { 0x00EA, 0, "ecircumflex" }, + { 0x00EB, 0, "edieresis" }, + { 0x0117, 0, "edotaccent" }, + { 0x00E8, 0, "egrave" }, + { 0x0038, 070, "eight" }, + { 0x2088, 0, "eightinferior" }, + { 0xF738, 0, "eightoldstyle" }, + { 0x2078, 0, "eightsuperior" }, + { 0x2208, 0, "element" }, + { 0x2026, 0274, "ellipsis" }, + { 0x0113, 0, "emacron" }, + { 0x2014, 0320, "emdash" }, + { 0x2205, 0, "emptyset" }, + { 0x2013, 0261, "endash" }, + { 0x014B, 0, "eng" }, + { 0x0119, 0, "eogonek" }, + { 0x03B5, 0, "epsilon" }, + { 0x03AD, 0, "epsilontonos" }, + { 0x003D, 075, "equal" }, + { 0x2261, 0, "equivalence" }, + { 0x212E, 0, "estimated" }, + { 0xF6EC, 0, "esuperior" }, + { 0x03B7, 0, "eta" }, + { 0x03AE, 0, "etatonos" }, + { 0x00F0, 0, "eth" }, + { 0x0021, 041, "exclam" }, + { 0x203C, 0, "exclamdbl" }, + { 0x00A1, 0241, "exclamdown" }, + { 0xF7A1, 0, "exclamdownsmall" }, + { 0xF721, 0, "exclamsmall" }, + { 0x2203, 0, "existential" }, + { 0x0066, 0146, "f" }, + { 0x2640, 0, "female" }, + { 0xFB00, 0, "ff" }, + { 0xFB03, 0, "ffi" }, + { 0xFB04, 0, "ffl" }, + { 0xFB01, 0256, "fi" }, + { 0x2012, 0, "figuredash" }, + { 0x25A0, 0, "filledbox" }, + { 0x25AC, 0, "filledrect" }, + { 0x0035, 065, "five" }, + { 0x215D, 0, "fiveeighths" }, + { 0x2085, 0, "fiveinferior" }, + { 0xF735, 0, "fiveoldstyle" }, + { 0x2075, 0, "fivesuperior" }, + { 0xFB02, 0257, "fl" }, + { 0x0192, 0246, "florin" }, + { 0x0034, 064, "four" }, + { 0x2084, 0, "fourinferior" }, + { 0xF734, 0, "fouroldstyle" }, + { 0x2074, 0, "foursuperior" }, + { 0x2044, 0244, "fraction" }, + { 0x2215, 0244, "fraction" }, + { 0x20A3, 0, "franc" }, + { 0x0067, 0147, "g" }, + { 0x03B3, 0, "gamma" }, + { 0x011F, 0, "gbreve" }, + { 0x01E7, 0, "gcaron" }, + { 0x011D, 0, "gcircumflex" }, + { 0x0123, 0, "gcommaaccent" }, + { 0x0121, 0, "gdotaccent" }, + { 0x00DF, 0373, "germandbls" }, + { 0x2207, 0, "gradient" }, + { 0x0060, 0301, "grave" }, + { 0x0300, 0, "gravecomb" }, + { 0x003E, 076, "greater" }, + { 0x2265, 0, "greaterequal" }, + { 0x00AB, 0253, "guillemotleft" }, + { 0x00BB, 0273, "guillemotright" }, + { 0x2039, 0254, "guilsinglleft" }, + { 0x203A, 0255, "guilsinglright" }, + { 0x0068, 0150, "h" }, + { 0x0127, 0, "hbar" }, + { 0x0125, 0, "hcircumflex" }, + { 0x2665, 0, "heart" }, + { 0x0309, 0, "hookabovecomb" }, + { 0x2302, 0, "house" }, + { 0x02DD, 0315, "hungarumlaut" }, + { 0x002D, 055, "hyphen" }, + { 0x00AD, 0, "hyphen" }, + { 0xF6E5, 0, "hypheninferior" }, + { 0xF6E6, 0, "hyphensuperior" }, + { 0x0069, 0151, "i" }, + { 0x00ED, 0, "iacute" }, + { 0x012D, 0, "ibreve" }, + { 0x00EE, 0, "icircumflex" }, + { 0x00EF, 0, "idieresis" }, + { 0x00EC, 0, "igrave" }, + { 0x0133, 0, "ij" }, + { 0x012B, 0, "imacron" }, + { 0x221E, 0, "infinity" }, + { 0x222B, 0, "integral" }, + { 0x2321, 0, "integralbt" }, + { 0xF8F5, 0, "integralex" }, + { 0x2320, 0, "integraltp" }, + { 0x2229, 0, "intersection" }, + { 0x25D8, 0, "invbullet" }, + { 0x25D9, 0, "invcircle" }, + { 0x263B, 0, "invsmileface" }, + { 0x012F, 0, "iogonek" }, + { 0x03B9, 0, "iota" }, + { 0x03CA, 0, "iotadieresis" }, + { 0x0390, 0, "iotadieresistonos" }, + { 0x03AF, 0, "iotatonos" }, + { 0xF6ED, 0, "isuperior" }, + { 0x0129, 0, "itilde" }, + { 0x006A, 0152, "j" }, + { 0x0135, 0, "jcircumflex" }, + { 0x006B, 0153, "k" }, + { 0x03BA, 0, "kappa" }, + { 0x0137, 0, "kcommaaccent" }, + { 0x0138, 0, "kgreenlandic" }, + { 0x006C, 0154, "l" }, + { 0x013A, 0, "lacute" }, + { 0x03BB, 0, "lambda" }, + { 0x013E, 0, "lcaron" }, + { 0x013C, 0, "lcommaaccent" }, + { 0x0140, 0, "ldot" }, + { 0x003C, 074, "less" }, + { 0x2264, 0, "lessequal" }, + { 0x258C, 0, "lfblock" }, + { 0x20A4, 0, "lira" }, + { 0xF6C0, 0, "ll" }, + { 0x2227, 0, "logicaland" }, + { 0x00AC, 0, "logicalnot" }, + { 0x2228, 0, "logicalor" }, + { 0x017F, 0, "longs" }, + { 0x25CA, 0, "lozenge" }, + { 0x0142, 0370, "lslash" }, + { 0xF6EE, 0, "lsuperior" }, + { 0x2591, 0, "ltshade" }, + { 0x006D, 0155, "m" }, + { 0x00AF, 0305, "macron" }, + { 0x02C9, 0305, "macron" }, + { 0x2642, 0, "male" }, + { 0x2212, 0, "minus" }, + { 0x2032, 0, "minute" }, + { 0xF6EF, 0, "msuperior" }, + { 0x00B5, 0, "mu" }, + { 0x03BC, 0, "mu" }, + { 0x00D7, 0, "multiply" }, + { 0x266A, 0, "musicalnote" }, + { 0x266B, 0, "musicalnotedbl" }, + { 0x006E, 0156, "n" }, + { 0x0144, 0, "nacute" }, + { 0x0149, 0, "napostrophe" }, + { 0x0148, 0, "ncaron" }, + { 0x0146, 0, "ncommaaccent" }, + { 0x0039, 071, "nine" }, + { 0x2089, 0, "nineinferior" }, + { 0xF739, 0, "nineoldstyle" }, + { 0x2079, 0, "ninesuperior" }, + { 0x2209, 0, "notelement" }, + { 0x2260, 0, "notequal" }, + { 0x2284, 0, "notsubset" }, + { 0x207F, 0, "nsuperior" }, + { 0x00F1, 0, "ntilde" }, + { 0x03BD, 0, "nu" }, + { 0x0023, 043, "numbersign" }, + { 0x006F, 0157, "o" }, + { 0x00F3, 0, "oacute" }, + { 0x014F, 0, "obreve" }, + { 0x00F4, 0, "ocircumflex" }, + { 0x00F6, 0, "odieresis" }, + { 0x0153, 0372, "oe" }, + { 0x02DB, 0316, "ogonek" }, + { 0x00F2, 0, "ograve" }, + { 0x01A1, 0, "ohorn" }, + { 0x0151, 0, "ohungarumlaut" }, + { 0x014D, 0, "omacron" }, + { 0x03C9, 0, "omega" }, + { 0x03D6, 0, "omega1" }, + { 0x03CE, 0, "omegatonos" }, + { 0x03BF, 0, "omicron" }, + { 0x03CC, 0, "omicrontonos" }, + { 0x0031, 061, "one" }, + { 0x2024, 0, "onedotenleader" }, + { 0x215B, 0, "oneeighth" }, + { 0xF6DC, 0, "onefitted" }, + { 0x00BD, 0, "onehalf" }, + { 0x2081, 0, "oneinferior" }, + { 0xF731, 0, "oneoldstyle" }, + { 0x00BC, 0, "onequarter" }, + { 0x00B9, 0, "onesuperior" }, + { 0x2153, 0, "onethird" }, + { 0x25E6, 0, "openbullet" }, + { 0x00AA, 0343, "ordfeminine" }, + { 0x00BA, 0353, "ordmasculine" }, + { 0x221F, 0, "orthogonal" }, + { 0x00F8, 0371, "oslash" }, + { 0x01FF, 0, "oslashacute" }, + { 0xF6F0, 0, "osuperior" }, + { 0x00F5, 0, "otilde" }, + { 0x0070, 0160, "p" }, + { 0x00B6, 0266, "paragraph" }, + { 0x0028, 050, "parenleft" }, + { 0xF8ED, 0, "parenleftbt" }, + { 0xF8EC, 0, "parenleftex" }, + { 0x208D, 0, "parenleftinferior" }, + { 0x207D, 0, "parenleftsuperior" }, + { 0xF8EB, 0, "parenlefttp" }, + { 0x0029, 051, "parenright" }, + { 0xF8F8, 0, "parenrightbt" }, + { 0xF8F7, 0, "parenrightex" }, + { 0x208E, 0, "parenrightinferior" }, + { 0x207E, 0, "parenrightsuperior" }, + { 0xF8F6, 0, "parenrighttp" }, + { 0x2202, 0, "partialdiff" }, + { 0x0025, 045, "percent" }, + { 0x002E, 056, "period" }, + { 0x00B7, 0264, "periodcentered" }, + { 0x2219, 0, "periodcentered" }, + { 0xF6E7, 0, "periodinferior" }, + { 0xF6E8, 0, "periodsuperior" }, + { 0x22A5, 0, "perpendicular" }, + { 0x2030, 0275, "perthousand" }, + { 0x20A7, 0, "peseta" }, + { 0x03C6, 0, "phi" }, + { 0x03D5, 0, "phi1" }, + { 0x03C0, 0, "pi" }, + { 0x002B, 053, "plus" }, + { 0x00B1, 0, "plusminus" }, + { 0x211E, 0, "prescription" }, + { 0x220F, 0, "product" }, + { 0x2282, 0, "propersubset" }, + { 0x2283, 0, "propersuperset" }, + { 0x221D, 0, "proportional" }, + { 0x03C8, 0, "psi" }, + { 0x0071, 0161, "q" }, + { 0x003F, 077, "question" }, + { 0x00BF, 0277, "questiondown" }, + { 0xF7BF, 0, "questiondownsmall" }, + { 0xF73F, 0, "questionsmall" }, + { 0x0022, 042, "quotedbl" }, + { 0x201E, 0271, "quotedblbase" }, + { 0x201C, 0252, "quotedblleft" }, + { 0x201D, 0272, "quotedblright" }, + { 0x2018, 0140, "quoteleft" }, + { 0x201B, 0, "quotereversed" }, + { 0x2019, 047, "quoteright" }, + { 0x201A, 0270, "quotesinglbase" }, + { 0x0027, 0251, "quotesingle" }, + { 0x0072, 0162, "r" }, + { 0x0155, 0, "racute" }, + { 0x221A, 0, "radical" }, + { 0xF8E5, 0, "radicalex" }, + { 0x0159, 0, "rcaron" }, + { 0x0157, 0, "rcommaaccent" }, + { 0x2286, 0, "reflexsubset" }, + { 0x2287, 0, "reflexsuperset" }, + { 0x00AE, 0, "registered" }, + { 0xF8E8, 0, "registersans" }, + { 0xF6DA, 0, "registerserif" }, + { 0x2310, 0, "revlogicalnot" }, + { 0x03C1, 0, "rho" }, + { 0x02DA, 0312, "ring" }, + { 0xF6F1, 0, "rsuperior" }, + { 0x2590, 0, "rtblock" }, + { 0xF6DD, 0, "rupiah" }, + { 0x0073, 0163, "s" }, + { 0x015B, 0, "sacute" }, + { 0x0161, 0, "scaron" }, + { 0x015F, 0, "scedilla" }, + { 0xF6C2, 0, "scedilla" }, + { 0x015D, 0, "scircumflex" }, + { 0x0219, 0, "scommaaccent" }, + { 0x2033, 0, "second" }, + { 0x00A7, 0247, "section" }, + { 0x003B, 073, "semicolon" }, + { 0x0037, 067, "seven" }, + { 0x215E, 0, "seveneighths" }, + { 0x2087, 0, "seveninferior" }, + { 0xF737, 0, "sevenoldstyle" }, + { 0x2077, 0, "sevensuperior" }, + { 0x2592, 0, "shade" }, + { 0x03C3, 0, "sigma" }, + { 0x03C2, 0, "sigma1" }, + { 0x223C, 0, "similar" }, + { 0x0036, 066, "six" }, + { 0x2086, 0, "sixinferior" }, + { 0xF736, 0, "sixoldstyle" }, + { 0x2076, 0, "sixsuperior" }, + { 0x002F, 057, "slash" }, + { 0x263A, 0, "smileface" }, + { 0x0020, 040, "space" }, + { 0x00A0, 040, "space" }, + { 0x2660, 0, "spade" }, + { 0xF6F2, 0, "ssuperior" }, + { 0x00A3, 0243, "sterling" }, + { 0x220B, 0, "suchthat" }, + { 0x2211, 0, "summation" }, + { 0x263C, 0, "sun" }, + { 0x0074, 0164, "t" }, + { 0x03C4, 0, "tau" }, + { 0x0167, 0, "tbar" }, + { 0x0165, 0, "tcaron" }, + { 0x0163, 0, "tcommaaccent" }, + { 0x021B, 0, "tcommaaccent" }, + { 0x2234, 0, "therefore" }, + { 0x03B8, 0, "theta" }, + { 0x03D1, 0, "theta1" }, + { 0x00FE, 0, "thorn" }, + { 0x0033, 063, "three" }, + { 0x215C, 0, "threeeighths" }, + { 0x2083, 0, "threeinferior" }, + { 0xF733, 0, "threeoldstyle" }, + { 0x00BE, 0, "threequarters" }, + { 0xF6DE, 0, "threequartersemdash" }, + { 0x00B3, 0, "threesuperior" }, + { 0x02DC, 0304, "tilde" }, + { 0x0303, 0, "tildecomb" }, + { 0x0384, 0, "tonos" }, + { 0x2122, 0, "trademark" }, + { 0xF8EA, 0, "trademarksans" }, + { 0xF6DB, 0, "trademarkserif" }, + { 0x25BC, 0, "triagdn" }, + { 0x25C4, 0, "triaglf" }, + { 0x25BA, 0, "triagrt" }, + { 0x25B2, 0, "triagup" }, + { 0xF6F3, 0, "tsuperior" }, + { 0x0032, 062, "two" }, + { 0x2025, 0, "twodotenleader" }, + { 0x2082, 0, "twoinferior" }, + { 0xF732, 0, "twooldstyle" }, + { 0x00B2, 0, "twosuperior" }, + { 0x2154, 0, "twothirds" }, + { 0x0075, 0165, "u" }, + { 0x00FA, 0, "uacute" }, + { 0x016D, 0, "ubreve" }, + { 0x00FB, 0, "ucircumflex" }, + { 0x00FC, 0, "udieresis" }, + { 0x00F9, 0, "ugrave" }, + { 0x01B0, 0, "uhorn" }, + { 0x0171, 0, "uhungarumlaut" }, + { 0x016B, 0, "umacron" }, + { 0x005F, 0137, "underscore" }, + { 0x2017, 0, "underscoredbl" }, + { 0x222A, 0, "union" }, + { 0x2200, 0, "universal" }, + { 0x0173, 0, "uogonek" }, + { 0x2580, 0, "upblock" }, + { 0x03C5, 0, "upsilon" }, + { 0x03CB, 0, "upsilondieresis" }, + { 0x03B0, 0, "upsilondieresistonos" }, + { 0x03CD, 0, "upsilontonos" }, + { 0x016F, 0, "uring" }, + { 0x0169, 0, "utilde" }, + { 0x0076, 0166, "v" }, + { 0x0077, 0167, "w" }, + { 0x1E83, 0, "wacute" }, + { 0x0175, 0, "wcircumflex" }, + { 0x1E85, 0, "wdieresis" }, + { 0x2118, 0, "weierstrass" }, + { 0x1E81, 0, "wgrave" }, + { 0x0078, 0170, "x" }, + { 0x03BE, 0, "xi" }, + { 0x0079, 0171, "y" }, + { 0x00FD, 0, "yacute" }, + { 0x0177, 0, "ycircumflex" }, + { 0x00FF, 0, "ydieresis" }, + { 0x00A5, 0245, "yen" }, + { 0x1EF3, 0, "ygrave" }, + { 0x007A, 0172, "z" }, + { 0x017A, 0, "zacute" }, + { 0x017E, 0, "zcaron" }, + { 0x017C, 0, "zdotaccent" }, + { 0x0030, 060, "zero" }, + { 0x2080, 0, "zeroinferior" }, + { 0xF730, 0, "zerooldstyle" }, + { 0x2070, 0, "zerosuperior" }, + { 0x03B6, 0, "zeta" } +}; diff --git a/vcl/unx/source/fontmanager/afm_hash.cpp b/vcl/unx/source/fontmanager/afm_hash.cpp new file mode 100755 index 000000000000..de01d8cd0434 --- /dev/null +++ b/vcl/unx/source/fontmanager/afm_hash.cpp @@ -0,0 +1,245 @@ +/* C++ code produced by gperf version 3.0.1 */ +/* Command-line: gperf -C -t -l -L C++ -m 20 -Z AfmKeywordHash afm_keyword_list */ +/* Computed positions: -k'1,4,6,$' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." +#endif + +#line 1 "afm_keyword_list" +struct hash_entry { const char* name; enum parseKey eKey; }; + +#define TOTAL_KEYWORDS 56 +#define MIN_WORD_LENGTH 1 +#define MAX_WORD_LENGTH 18 +#define MIN_HASH_VALUE 1 +#define MAX_HASH_VALUE 57 +/* maximum key range = 57, duplicates = 0 */ + +class AfmKeywordHash +{ +private: + static inline unsigned int hash (const char *str, unsigned int len); +public: + static const struct hash_entry *in_word_set (const char *str, unsigned int len); +}; + +inline unsigned int +AfmKeywordHash::hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 28, 1, 0, 9, 0, + 19, 58, 2, 10, 58, 0, 28, 0, 20, 58, + 44, 58, 58, 0, 16, 10, 24, 2, 3, 58, + 58, 58, 58, 58, 58, 58, 58, 6, 58, 0, + 19, 0, 58, 25, 14, 6, 58, 58, 17, 11, + 0, 17, 39, 58, 0, 0, 10, 58, 58, 58, + 13, 4, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[5]]; + /*FALLTHROUGH*/ + case 5: + case 4: + hval += asso_values[(unsigned char)str[3]]; + /*FALLTHROUGH*/ + case 3: + case 2: + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval + asso_values[(unsigned char)str[len - 1]]; +} + +const struct hash_entry * +AfmKeywordHash::in_word_set (register const char *str, register unsigned int len) +{ + static const unsigned char lengthtable[] = + { + 0, 1, 2, 1, 2, 1, 3, 2, 3, 5, 10, 11, 12, 2, + 14, 15, 16, 11, 9, 13, 14, 12, 12, 14, 13, 9, 7, 9, + 7, 9, 14, 5, 6, 14, 12, 16, 10, 14, 11, 10, 7, 1, + 12, 8, 17, 18, 2, 3, 7, 1, 8, 8, 13, 6, 6, 8, + 0, 1 + }; + static const struct hash_entry wordlist[] = + { + {"",NOPE}, +#line 6 "afm_keyword_list" + {"C",CODE}, +#line 7 "afm_keyword_list" + {"CC",COMPCHAR}, +#line 5 "afm_keyword_list" + {"B",CHARBBOX}, +#line 8 "afm_keyword_list" + {"CH",CODEHEX}, +#line 54 "afm_keyword_list" + {"W",XYWIDTH}, +#line 33 "afm_keyword_list" + {"KPX",KERNPAIRXAMT}, +#line 56 "afm_keyword_list" + {"WX",XWIDTH}, +#line 55 "afm_keyword_list" + {"W0X",X0WIDTH}, +#line 47 "afm_keyword_list" + {"StdHW",STDHW}, +#line 12 "afm_keyword_list" + {"Characters",CHARACTERS}, +#line 36 "afm_keyword_list" + {"MetricsSets",METRICSSETS}, +#line 23 "afm_keyword_list" + {"EndKernPairs",ENDKERNPAIRS}, +#line 16 "afm_keyword_list" + {"Em",EM}, +#line 45 "afm_keyword_list" + {"StartKernPairs",STARTKERNPAIRS}, +#line 41 "afm_keyword_list" + {"StartComposites",STARTCOMPOSITES}, +#line 40 "afm_keyword_list" + {"StartCharMetrics",STARTCHARMETRICS}, +#line 22 "afm_keyword_list" + {"EndKernData",ENDKERNDATA}, +#line 14 "afm_keyword_list" + {"Descender",DESCENDER}, +#line 44 "afm_keyword_list" + {"StartKernData",STARTKERNDATA}, +#line 18 "afm_keyword_list" + {"EndCharMetrics",ENDCHARMETRICS}, +#line 20 "afm_keyword_list" + {"EndDirection",ENDDIRECTION}, +#line 11 "afm_keyword_list" + {"CharacterSet",CHARACTERSET}, +#line 42 "afm_keyword_list" + {"StartDirection",STARTDIRECTION}, +#line 19 "afm_keyword_list" + {"EndComposites",ENDCOMPOSITES}, +#line 49 "afm_keyword_list" + {"TrackKern",TRACKKERN}, +#line 15 "afm_keyword_list" + {"Descent",DESCENT}, +#line 9 "afm_keyword_list" + {"CapHeight",CAPHEIGHT}, +#line 13 "afm_keyword_list" + {"Comment",COMMENT}, +#line 10 "afm_keyword_list" + {"CharWidth",CHARWIDTH}, +#line 46 "afm_keyword_list" + {"StartTrackKern",STARTTRACKKERN}, +#line 48 "afm_keyword_list" + {"StdVW",STDVW}, +#line 38 "afm_keyword_list" + {"Notice",NOTICE}, +#line 21 "afm_keyword_list" + {"EndFontMetrics",ENDFONTMETRICS}, +#line 24 "afm_keyword_list" + {"EndTrackKern",ENDTRACKKERN}, +#line 43 "afm_keyword_list" + {"StartFontMetrics",STARTFONTMETRICS}, +#line 29 "afm_keyword_list" + {"IsBaseFont",ISBASEFONT}, +#line 17 "afm_keyword_list" + {"EncodingScheme",ENCODINGSCHEME}, +#line 31 "afm_keyword_list" + {"ItalicAngle",ITALICANGLE}, +#line 25 "afm_keyword_list" + {"FamilyName",FAMILYNAME}, +#line 58 "afm_keyword_list" + {"XHeight",XHEIGHT}, +#line 37 "afm_keyword_list" + {"N",CHARNAME}, +#line 30 "afm_keyword_list" + {"IsFixedPitch",ISFIXEDPITCH}, +#line 27 "afm_keyword_list" + {"FontName",FONTNAME}, +#line 50 "afm_keyword_list" + {"UnderlinePosition",UNDERLINEPOSITION}, +#line 51 "afm_keyword_list" + {"UnderlineThickness",UNDERLINETHICKNESS}, +#line 32 "afm_keyword_list" + {"KP",KERNPAIR}, +#line 39 "afm_keyword_list" + {"PCC",COMPCHARPIECE}, +#line 53 "afm_keyword_list" + {"Version",VERSION}, +#line 52 "afm_keyword_list" + {"V",VVECTOR}, +#line 28 "afm_keyword_list" + {"FullName",FULLNAME}, +#line 26 "afm_keyword_list" + {"FontBBox",FONTBBOX}, +#line 35 "afm_keyword_list" + {"MappingScheme",MAPPINGSCHEME}, +#line 57 "afm_keyword_list" + {"Weight",WEIGHT}, +#line 4 "afm_keyword_list" + {"Ascent",ASCENT}, +#line 3 "afm_keyword_list" + {"Ascender",ASCENDER}, + {"",NOPE}, +#line 34 "afm_keyword_list" + {"L",LIGATURE} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + if (len == lengthtable[key]) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !memcmp (str + 1, s + 1, len - 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/vcl/unx/source/fontmanager/afm_keyword_list b/vcl/unx/source/fontmanager/afm_keyword_list new file mode 100755 index 000000000000..263d76bca4d3 --- /dev/null +++ b/vcl/unx/source/fontmanager/afm_keyword_list @@ -0,0 +1,58 @@ +struct hash_entry { char* name; enum parseKey eKey; }; +%% +Ascender,ASCENDER +Ascent,ASCENT +B,CHARBBOX +C,CODE +CC,COMPCHAR +CH,CODEHEX +CapHeight,CAPHEIGHT +CharWidth,CHARWIDTH +CharacterSet,CHARACTERSET +Characters,CHARACTERS +Comment,COMMENT +Descender,DESCENDER +Descent,DESCENT +Em,EM +EncodingScheme,ENCODINGSCHEME +EndCharMetrics,ENDCHARMETRICS +EndComposites,ENDCOMPOSITES +EndDirection,ENDDIRECTION +EndFontMetrics,ENDFONTMETRICS +EndKernData,ENDKERNDATA +EndKernPairs,ENDKERNPAIRS +EndTrackKern,ENDTRACKKERN +FamilyName,FAMILYNAME +FontBBox,FONTBBOX +FontName,FONTNAME +FullName,FULLNAME +IsBaseFont,ISBASEFONT +IsFixedPitch,ISFIXEDPITCH +ItalicAngle,ITALICANGLE +KP,KERNPAIR +KPX,KERNPAIRXAMT +L,LIGATURE +MappingScheme,MAPPINGSCHEME +MetricsSets,METRICSSETS +N,CHARNAME +Notice,NOTICE +PCC,COMPCHARPIECE +StartCharMetrics,STARTCHARMETRICS +StartComposites,STARTCOMPOSITES +StartDirection,STARTDIRECTION +StartFontMetrics,STARTFONTMETRICS +StartKernData,STARTKERNDATA +StartKernPairs,STARTKERNPAIRS +StartTrackKern,STARTTRACKKERN +StdHW,STDHW +StdVW,STDVW +TrackKern,TRACKKERN +UnderlinePosition,UNDERLINEPOSITION +UnderlineThickness,UNDERLINETHICKNESS +V,VVECTOR +Version,VERSION +W,XYWIDTH +W0X,X0WIDTH +WX,XWIDTH +Weight,WEIGHT +XHeight,XHEIGHT diff --git a/vcl/unx/source/fontmanager/fontcache.cxx b/vcl/unx/source/fontmanager/fontcache.cxx new file mode 100644 index 000000000000..4932f7a771e0 --- /dev/null +++ b/vcl/unx/source/fontmanager/fontcache.cxx @@ -0,0 +1,821 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: fontcache.cxx,v $ + * $Revision: 1.26 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <cstdlib> +#include <cstring> + +#include "vcl/fontcache.hxx" + +#include "osl/thread.h" + +#include "unotools/atom.hxx" + +#include "tools/stream.hxx" + +#include <unistd.h> +#include <sys/stat.h> + +#if OSL_DEBUG_LEVEL >1 +#include <cstdio> +#endif + +#define FONTCACHEFILE "/user/psprint/pspfontcache" +#define CACHE_MAGIC "PspFontCacheFile format 3" + +using namespace std; +using namespace rtl; +using namespace psp; +using namespace utl; + +/* + * static helpers + */ + +/* + * FontCache constructor + */ + +FontCache::FontCache() +{ + m_bDoFlush = false; + m_aCacheFile = getOfficePath( UserPath ); + if( m_aCacheFile.Len() ) + { + m_aCacheFile.AppendAscii( FONTCACHEFILE ); + read(); + } +} + +/* + * FontCache destructor + */ + +FontCache::~FontCache() +{ + clearCache(); +} + +/* + * FontCache::clearCache + */ +void FontCache::clearCache() +{ + for( FontCacheData::iterator dir_it = m_aCache.begin(); dir_it != m_aCache.end(); ++dir_it ) + { + for( FontDirMap::iterator entry_it = dir_it->second.m_aEntries.begin(); entry_it != dir_it->second.m_aEntries.end(); ++entry_it ) + { + for( FontCacheEntry::iterator font_it = entry_it->second.m_aEntry.begin(); font_it != entry_it->second.m_aEntry.end(); ++font_it ) + delete *font_it; + } + } + m_aCache.clear(); +} + +/* + * FontCache::Commit + */ + +void FontCache::flush() +{ + if( ! m_bDoFlush || ! m_aCacheFile.Len() ) + return; + + SvFileStream aStream; + aStream.Open( m_aCacheFile, STREAM_WRITE | STREAM_TRUNC ); + if( ! (aStream.IsOpen() && aStream.IsWritable()) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "FontCache::flush: opening cache file %s failed\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() ); +#endif + return; + } + + aStream.SetLineDelimiter( LINEEND_LF ); + aStream.WriteLine( ByteString( CACHE_MAGIC ) ); + + PrintFontManager& rManager( PrintFontManager::get() ); + MultiAtomProvider* pAtoms = rManager.m_pAtoms; + + for( FontCacheData::const_iterator dir_it = m_aCache.begin(); dir_it != m_aCache.end(); ++ dir_it ) + { + const FontDirMap& rDir( dir_it->second.m_aEntries ); + + ByteString aDirectory( rManager.getDirectory( dir_it->first ) ); + ByteString aLine( "FontCacheDirectory:" ); + aLine.Append( ByteString::CreateFromInt64( dir_it->second.m_nTimestamp ) ); + aLine.Append( ':' ); + aLine.Append( aDirectory ); + if( rDir.empty() && dir_it->second.m_bNoFiles ) + aLine.Insert( "Empty", 0 ); + aStream.WriteLine( aLine ); + + for( FontDirMap::const_iterator entry_it = rDir.begin(); entry_it != rDir.end(); ++entry_it ) + { + // insert cache entries + const FontCacheEntry& rEntry( entry_it->second.m_aEntry ); + if( rEntry.begin() == rEntry.end() ) + continue; + + aLine = "File:"; + aLine.Append( ByteString( entry_it->first ) ); + aStream.WriteLine( aLine ); + + int nEntrySize = entry_it->second.m_aEntry.size(); + // write: type;nfonts + aLine = ByteString::CreateFromInt32( rEntry.front()->m_eType ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( nEntrySize ) ); + aStream.WriteLine( aLine ); + + sal_Int32 nSubEntry = 0; + for( FontCacheEntry::const_iterator it = rEntry.begin(); it != rEntry.end(); ++it, nSubEntry++ ) + { + /* + * for each font entry write: + * name[;name[;name]] + * fontnr;PSName;italic;weight;width;pitch;encoding;ascend;descend;leading;vsubst;gxw;gxh;gyw;gyh;useroverrride;embed;antialias[;{metricfile,typeflags}][;stylename] + */ + if( nEntrySize > 1 ) + nSubEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nCollectionEntry; + else + nSubEntry = -1; + + aLine = OUStringToOString( pAtoms->getString( ATOM_FAMILYNAME, (*it)->m_nFamilyName ), RTL_TEXTENCODING_UTF8 ); + for( ::std::list< int >::const_iterator name_it = (*it)->m_aAliases.begin(); name_it != (*it)->m_aAliases.end(); ++name_it ) + { + const OUString& rAdd( pAtoms->getString( ATOM_FAMILYNAME, *name_it ) ); + if( rAdd.getLength() ) + { + aLine.Append( ';' ); + aLine.Append( ByteString( String( rAdd ), RTL_TEXTENCODING_UTF8 ) ); + } + } + aStream.WriteLine( aLine ); + + const OUString& rPSName( pAtoms->getString( ATOM_PSNAME, (*it)->m_nPSName ) ); + aLine = ByteString::CreateFromInt32( nSubEntry ); + aLine.Append( ';' ); + aLine.Append( ByteString( String( rPSName ), RTL_TEXTENCODING_UTF8 ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eItalic ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eWeight ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eWidth ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_ePitch ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aEncoding ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_nAscend ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_nDescend ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_nLeading ) ); + aLine.Append( ';' ); + aLine.Append( (*it)->m_bHaveVerticalSubstitutedGlyphs ? "1" : "0" ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricX.width ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricX.height ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricY.width ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricY.height ) ); + aLine.Append( ';' ); + aLine.Append( (*it)->m_bUserOverride ? "1" : "0" ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eEmbeddedbitmap ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eAntialias ) ); + + switch( (*it)->m_eType ) + { + case fonttype::Type1: + aLine.Append( ';' ); + aLine.Append( ByteString( static_cast<const PrintFontManager::Type1FontFile*>(*it)->m_aMetricFile ) ); + break; + case fonttype::TrueType: + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nTypeFlags ) ); + break; + default: break; + } + if( (*it)->m_aStyleName.getLength() ) + { + aLine.Append( ';' ); + aLine.Append( ByteString( String( (*it)->m_aStyleName ), RTL_TEXTENCODING_UTF8 ) ); + } + aStream.WriteLine( aLine ); + } + aStream.WriteLine( ByteString() ); + } + } + m_bDoFlush = false; +} + +/* + * FontCache::read + */ + +void FontCache::read() +{ + PrintFontManager& rManager( PrintFontManager::get() ); + MultiAtomProvider* pAtoms = rManager.m_pAtoms; + + SvFileStream aStream( m_aCacheFile, STREAM_READ ); + if( ! aStream.IsOpen() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "FontCache::read: opening cache file %s failed\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() ); +#endif + return; + } + + + ByteString aLine; + aStream.ReadLine( aLine ); + if( !aLine.Equals( CACHE_MAGIC ) ) + { + #if OSL_DEBUG_LEVEL >1 + fprintf( stderr, "FontCache::read: cache file %s fails magic test\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() ); + #endif + return; + } + + int nDir = 0; + FontDirMap* pDir = NULL; + xub_StrLen nIndex; + bool bKeepOnlyUserOverridden = false; + do + { + aStream.ReadLine( aLine ); + if( aLine.CompareTo( "FontCacheDirectory:", 19 ) == COMPARE_EQUAL || + aLine.CompareTo( "EmptyFontCacheDirectory:", 24 ) == COMPARE_EQUAL ) + { + bool bEmpty = (aLine.CompareTo( "Empty", 5 ) == COMPARE_EQUAL); + xub_StrLen nSearchIndex = bEmpty ? 24 : 19; + + OString aDir; + sal_Int64 nTimestamp = 0; + xub_StrLen nTEnd = aLine.Search( ':', nSearchIndex ); + if( nTEnd != STRING_NOTFOUND ) + { + nTimestamp = aLine.Copy( nSearchIndex, nTEnd - nSearchIndex ).ToInt64(); + aDir = aLine.Copy( nTEnd+1 ); + } + else + { + // invalid format, remove + pDir = NULL; + nDir = 0; + m_bDoFlush = true; + continue; + } + + // is the directory modified ? + struct stat aStat; + if( stat( aDir.getStr(), &aStat ) || + ! S_ISDIR(aStat.st_mode) ) + { + // remove outdated cache data + pDir = NULL; + nDir = 0; + m_bDoFlush = true; + continue; + } + else + { + nDir = rManager.getDirectoryAtom( aDir, true ); + m_aCache[ nDir ].m_nTimestamp = (sal_Int64)aStat.st_mtime; + m_aCache[ nDir ].m_bNoFiles = bEmpty; + pDir = bEmpty ? NULL : &m_aCache[ nDir ].m_aEntries; + bKeepOnlyUserOverridden = ((sal_Int64)aStat.st_mtime != nTimestamp); + m_aCache[ nDir ].m_bUserOverrideOnly = bKeepOnlyUserOverridden; + } + } + else if( pDir && aLine.CompareTo( "File:", 5 ) == COMPARE_EQUAL ) + { + OString aFile( aLine.Copy( 5 ) ); + aStream.ReadLine( aLine ); + + const char* pLine = aLine.GetBuffer(); + + fonttype::type eType = (fonttype::type)atoi( pLine ); + if( eType != fonttype::TrueType && + eType != fonttype::Type1 && + eType != fonttype::Builtin + ) + continue; + while( *pLine && *pLine != ';' ) + pLine++; + if( *pLine != ';' ) + continue; + + pLine++; + sal_Int32 nFonts = atoi( pLine ); + for( int n = 0; n < nFonts; n++ ) + { + aStream.ReadLine( aLine ); + pLine = aLine.GetBuffer(); + int nLen = aLine.Len(); + + PrintFontManager::PrintFont* pFont = NULL; + switch( eType ) + { + case fonttype::TrueType: + pFont = new PrintFontManager::TrueTypeFontFile(); + break; + case fonttype::Type1: + pFont = new PrintFontManager::Type1FontFile(); + break; + case fonttype::Builtin: + pFont = new PrintFontManager::BuiltinFont(); + break; + default: break; + } + + for( nIndex = 0; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ ) + ; + + pFont->m_nFamilyName = pAtoms->getAtom( ATOM_FAMILYNAME, + OUString( pLine, nIndex, RTL_TEXTENCODING_UTF8 ), + sal_True ); + while( nIndex < nLen ) + { + xub_StrLen nLastIndex = nIndex+1; + for( nIndex = nLastIndex ; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ ) + ; + if( nIndex - nLastIndex > 1 ) + { + OUString aAlias( pLine+nLastIndex, nIndex-nLastIndex-1, RTL_TEXTENCODING_UTF8 ); + pFont->m_aAliases.push_back( pAtoms->getAtom( ATOM_FAMILYNAME, aAlias, sal_True ) ); + } + } + aStream.ReadLine( aLine ); + pLine = aLine.GetBuffer(); + nLen = aLine.Len(); + + // get up to 20 token positions + const int nMaxTokens = 20; + int nTokenPos[nMaxTokens]; + nTokenPos[0] = 0; + int nTokens = 1; + for( int i = 0; i < nLen; i++ ) + { + if( pLine[i] == ';' ) + { + nTokenPos[nTokens++] = i+1; + if( nTokens == nMaxTokens ) + break; + } + } + if( nTokens < 18 ) + { + delete pFont; + continue; + } + int nCollEntry = atoi( pLine ); + pFont->m_nPSName = pAtoms->getAtom( ATOM_PSNAME, OUString( pLine + nTokenPos[1], nTokenPos[2]-nTokenPos[1]-1, RTL_TEXTENCODING_UTF8 ), sal_True ); + pFont->m_eItalic = (italic::type)atoi( pLine+nTokenPos[2] ); + pFont->m_eWeight = (weight::type)atoi( pLine+nTokenPos[3] ); + pFont->m_eWidth = (width::type)atoi( pLine+nTokenPos[4] ); + pFont->m_ePitch = (pitch::type)atoi( pLine+nTokenPos[5] ); + pFont->m_aEncoding = (rtl_TextEncoding)atoi( pLine+nTokenPos[6] ); + pFont->m_nAscend = atoi( pLine + nTokenPos[7] ); + pFont->m_nDescend = atoi( pLine + nTokenPos[8] ); + pFont->m_nLeading = atoi( pLine + nTokenPos[9] ); + pFont->m_bHaveVerticalSubstitutedGlyphs + = (atoi( pLine + nTokenPos[10] ) != 0); + pFont->m_aGlobalMetricX.width + = atoi( pLine + nTokenPos[11] ); + pFont->m_aGlobalMetricX.height + = atoi( pLine + nTokenPos[12] ); + pFont->m_aGlobalMetricY.width + = atoi( pLine + nTokenPos[13] ); + pFont->m_aGlobalMetricY.height + = atoi( pLine + nTokenPos[14] ); + pFont->m_bUserOverride + = (atoi( pLine + nTokenPos[15] ) != 0); + pFont->m_eEmbeddedbitmap + = (fcstatus::type)atoi(pLine+nTokenPos[16]); + pFont->m_eAntialias = (fcstatus::type)atoi(pLine+nTokenPos[17]); + int nStyleTokenNr = 18; + switch( eType ) + { + case fonttype::TrueType: + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nTypeFlags = atoi( pLine + nTokenPos[18] ); + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nCollectionEntry = nCollEntry; + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nDirectory = nDir; + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_aFontFile = aFile; + nStyleTokenNr++; + break; + case fonttype::Type1: + { + int nTokLen = (nTokens > 19 ) ? nTokenPos[19]-nTokenPos[18]-1 : nLen - nTokenPos[18]; + static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_aMetricFile = OString( pLine + nTokenPos[18], nTokLen ); + static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_nDirectory = nDir; + static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_aFontFile = aFile; + nStyleTokenNr++; + } + break; + case fonttype::Builtin: + static_cast<PrintFontManager::BuiltinFont*>(pFont)->m_nDirectory = nDir; + static_cast<PrintFontManager::BuiltinFont*>(pFont)->m_aMetricFile = aFile; + break; + default: break; + } + if( nTokens > nStyleTokenNr ) + pFont->m_aStyleName = OUString::intern( pLine + nTokenPos[nStyleTokenNr], + nLen - nTokenPos[nStyleTokenNr], + RTL_TEXTENCODING_UTF8 ); + + bool bObsolete = false; + if( bKeepOnlyUserOverridden ) + { + if( pFont->m_bUserOverride ) + { + ByteString aFilePath = rManager.getDirectory( nDir ); + aFilePath.Append( '/' ); + aFilePath.Append( ByteString(aFile) ); + struct stat aStat; + if( stat( aFilePath.GetBuffer(), &aStat ) || + ! S_ISREG( aStat.st_mode ) || + aStat.st_size < 16 ) + { + bObsolete = true; + } + #if OSL_DEBUG_LEVEL > 2 + else + fprintf( stderr, "keeping file %s in outdated cache entry due to user override\n", + aFilePath.GetBuffer() ); + #endif + } + else + bObsolete = true; + } + if( bObsolete ) + { + m_bDoFlush = true; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "removing obsolete font %s\n", aFile.getStr() ); +#endif + delete pFont; + continue; + } + + FontCacheEntry& rEntry = (*pDir)[aFile].m_aEntry; + rEntry.push_back( pFont ); + } + } + } while( ! aStream.IsEof() ); +} + +/* + * FontCache::updateDirTimestamp + */ +void FontCache::updateDirTimestamp( int nDirID ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + const OString& rDir = rManager.getDirectory( nDirID ); + + struct stat aStat; + if( ! stat( rDir.getStr(), &aStat ) ) + m_aCache[ nDirID ].m_nTimestamp = (sal_Int64)aStat.st_mtime; +} + + +/* + * FontCache::copyPrintFont + */ +void FontCache::copyPrintFont( const PrintFontManager::PrintFont* pFrom, PrintFontManager::PrintFont* pTo ) const +{ + if( pFrom->m_eType != pTo->m_eType ) + return; + switch( pFrom->m_eType ) + { + case fonttype::TrueType: + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nDirectory; + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_aFontFile = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_aFontFile; + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nCollectionEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nCollectionEntry; + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nTypeFlags = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nTypeFlags; + break; + case fonttype::Type1: + static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_nDirectory; + static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_aFontFile = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_aFontFile; + static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_aMetricFile = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_aMetricFile; + break; + case fonttype::Builtin: + static_cast<PrintFontManager::BuiltinFont*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::BuiltinFont*>(pFrom)->m_nDirectory; + static_cast<PrintFontManager::BuiltinFont*>(pTo)->m_aMetricFile = static_cast<const PrintFontManager::BuiltinFont*>(pFrom)->m_aMetricFile; + break; + default: break; + } + pTo->m_nFamilyName = pFrom->m_nFamilyName; + pTo->m_aStyleName = pFrom->m_aStyleName; + pTo->m_aAliases = pFrom->m_aAliases; + pTo->m_nPSName = pFrom->m_nPSName; + pTo->m_eItalic = pFrom->m_eItalic; + pTo->m_eWeight = pFrom->m_eWeight; + pTo->m_eWidth = pFrom->m_eWidth; + pTo->m_ePitch = pFrom->m_ePitch; + pTo->m_aEncoding = pFrom->m_aEncoding; + pTo->m_aGlobalMetricX = pFrom->m_aGlobalMetricX; + pTo->m_aGlobalMetricY = pFrom->m_aGlobalMetricY; + pTo->m_nAscend = pFrom->m_nAscend; + pTo->m_nDescend = pFrom->m_nDescend; + pTo->m_nLeading = pFrom->m_nLeading; + pTo->m_nXMin = pFrom->m_nXMin; + pTo->m_nYMin = pFrom->m_nYMin; + pTo->m_nXMax = pFrom->m_nXMax; + pTo->m_nYMax = pFrom->m_nYMax; + pTo->m_bHaveVerticalSubstitutedGlyphs = pFrom->m_bHaveVerticalSubstitutedGlyphs; + pTo->m_bUserOverride = pFrom->m_bUserOverride; + pTo->m_eEmbeddedbitmap = pFrom->m_eEmbeddedbitmap; + pTo->m_eAntialias = pFrom->m_eAntialias; +} + +/* + * FontCache::equalsPrintFont + */ +bool FontCache::equalsPrintFont( const PrintFontManager::PrintFont* pLeft, PrintFontManager::PrintFont* pRight ) const +{ + if( pLeft->m_eType != pRight->m_eType ) + return false; + switch( pLeft->m_eType ) + { + case fonttype::TrueType: + { + const PrintFontManager::TrueTypeFontFile* pLT = static_cast<const PrintFontManager::TrueTypeFontFile*>(pLeft); + const PrintFontManager::TrueTypeFontFile* pRT = static_cast<const PrintFontManager::TrueTypeFontFile*>(pRight); + if( pRT->m_nDirectory != pLT->m_nDirectory || + pRT->m_aFontFile != pLT->m_aFontFile || + pRT->m_nCollectionEntry != pLT->m_nCollectionEntry || + pRT->m_nTypeFlags != pLT->m_nTypeFlags ) + return false; + } + break; + case fonttype::Type1: + { + const PrintFontManager::Type1FontFile* pLT = static_cast<const PrintFontManager::Type1FontFile*>(pLeft); + const PrintFontManager::Type1FontFile* pRT = static_cast<const PrintFontManager::Type1FontFile*>(pRight); + if( pRT->m_nDirectory != pLT->m_nDirectory || + pRT->m_aFontFile != pLT->m_aFontFile || + pRT->m_aMetricFile != pLT->m_aMetricFile ) + return false; + } + break; + case fonttype::Builtin: + { + const PrintFontManager::BuiltinFont* pLT = static_cast<const PrintFontManager::BuiltinFont*>(pLeft); + const PrintFontManager::BuiltinFont* pRT = static_cast<const PrintFontManager::BuiltinFont*>(pRight); + if( pRT->m_nDirectory != pLT->m_nDirectory || + pRT->m_aMetricFile != pLT->m_aMetricFile ) + return false; + } + break; + default: break; + } + if( pRight->m_nFamilyName != pLeft->m_nFamilyName || + pRight->m_aStyleName != pLeft->m_aStyleName || + pRight->m_nPSName != pLeft->m_nPSName || + pRight->m_eItalic != pLeft->m_eItalic || + pRight->m_eWeight != pLeft->m_eWeight || + pRight->m_eWidth != pLeft->m_eWidth || + pRight->m_ePitch != pLeft->m_ePitch || + pRight->m_aEncoding != pLeft->m_aEncoding || + pRight->m_aGlobalMetricX != pLeft->m_aGlobalMetricX || + pRight->m_aGlobalMetricY != pLeft->m_aGlobalMetricY || + pRight->m_nAscend != pLeft->m_nAscend || + pRight->m_nDescend != pLeft->m_nDescend || + pRight->m_nLeading != pLeft->m_nLeading || + pRight->m_nXMin != pLeft->m_nXMin || + pRight->m_nYMin != pLeft->m_nYMin || + pRight->m_nXMax != pLeft->m_nXMax || + pRight->m_nYMax != pLeft->m_nYMax || + pRight->m_bHaveVerticalSubstitutedGlyphs != pLeft->m_bHaveVerticalSubstitutedGlyphs || + pRight->m_bUserOverride != pLeft->m_bUserOverride || + pRight->m_eEmbeddedbitmap != pLeft->m_eEmbeddedbitmap || + pRight->m_eAntialias != pLeft->m_eAntialias + ) + return false; + std::list< int >::const_iterator lit, rit; + for( lit = pLeft->m_aAliases.begin(), rit = pRight->m_aAliases.begin(); + lit != pLeft->m_aAliases.end() && rit != pRight->m_aAliases.end() && (*lit) == (*rit); + ++lit, ++rit ) + ; + return lit == pLeft->m_aAliases.end() && rit == pRight->m_aAliases.end(); +} + +/* + * FontCache::clonePrintFont + */ +PrintFontManager::PrintFont* FontCache::clonePrintFont( const PrintFontManager::PrintFont* pOldFont ) const +{ + PrintFontManager::PrintFont* pFont = NULL; + switch( pOldFont->m_eType ) + { + case fonttype::TrueType: + pFont = new PrintFontManager::TrueTypeFontFile(); + break; + case fonttype::Type1: + pFont = new PrintFontManager::Type1FontFile(); + break; + case fonttype::Builtin: + pFont = new PrintFontManager::BuiltinFont(); + break; + default: break; + } + if( pFont ) + { + copyPrintFont( pOldFont, pFont ); + } + return pFont; + } + +/* + * FontCache::getFontCacheFile + */ +bool FontCache::getFontCacheFile( int nDirID, const OString& rFile, list< PrintFontManager::PrintFont* >& rNewFonts ) const +{ + bool bSuccess = false; + + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + if( dir != m_aCache.end() ) + { + FontDirMap::const_iterator entry = dir->second.m_aEntries.find( rFile ); + if( entry != dir->second.m_aEntries.end() ) + { + for( FontCacheEntry::const_iterator font = entry->second.m_aEntry.begin(); font != entry->second.m_aEntry.end(); ++font ) + { + bSuccess = true; + PrintFontManager::PrintFont* pFont = clonePrintFont( *font ); + rNewFonts.push_back( pFont ); + } + } + } + return bSuccess; +} + +/* + * FontCache::updateFontCacheEntry + */ +void FontCache::updateFontCacheEntry( const PrintFontManager::PrintFont* pFont, bool bFlush ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + + OString aFile; + int nDirID = 0; + switch( pFont->m_eType ) + { + case fonttype::TrueType: + nDirID = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_nDirectory; + aFile = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_aFontFile; + break; + case fonttype::Type1: + nDirID = static_cast<const PrintFontManager::Type1FontFile*>(pFont)->m_nDirectory; + aFile = static_cast<const PrintFontManager::Type1FontFile*>(pFont)->m_aFontFile; + break; + case fonttype::Builtin: + nDirID = static_cast<const PrintFontManager::BuiltinFont*>(pFont)->m_nDirectory; + aFile = static_cast<const PrintFontManager::BuiltinFont*>(pFont)->m_aMetricFile; + break; + default: + return; + } + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + FontDirMap::const_iterator entry; + FontCacheEntry::const_iterator font; + PrintFontManager::PrintFont* pCacheFont = NULL; + + if( dir != m_aCache.end() ) + { + entry = dir->second.m_aEntries.find( aFile ); + if( entry != dir->second.m_aEntries.end() ) + { + for( font = entry->second.m_aEntry.begin(); font != entry->second.m_aEntry.end(); ++font ) + { + if( (*font)->m_eType == pFont->m_eType && + ( (*font)->m_eType != fonttype::TrueType || + static_cast<const PrintFontManager::TrueTypeFontFile*>(*font)->m_nCollectionEntry == static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_nCollectionEntry + ) ) + break; + } + if( font != entry->second.m_aEntry.end() ) + pCacheFont = *font; + } + } + else + createCacheDir( nDirID ); + + if( pCacheFont ) + { + if( ! equalsPrintFont( pFont, pCacheFont ) ) + { + copyPrintFont( pFont, pCacheFont ); + m_bDoFlush = true; + } + } + else + { + pCacheFont = clonePrintFont( pFont ); + m_aCache[nDirID].m_aEntries[aFile].m_aEntry.push_back( pCacheFont ); + + ByteString aPath = rManager.getDirectory( nDirID ); + aPath.Append( '/' ); + aPath.Append( ByteString( aFile ) ); + m_bDoFlush = true; + } + if( bFlush ) + flush(); +} + +/* + * FontCache::listDirectory + */ +bool FontCache::listDirectory( const OString& rDir, std::list< PrintFontManager::PrintFont* >& rNewFonts ) const +{ + PrintFontManager& rManager( PrintFontManager::get() ); + int nDirID = rManager.getDirectoryAtom( rDir ); + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + bool bFound = (dir != m_aCache.end()); + + if( bFound && !dir->second.m_bNoFiles ) + { + for( FontDirMap::const_iterator file = dir->second.m_aEntries.begin(); file != dir->second.m_aEntries.end(); ++file ) + { + for( FontCacheEntry::const_iterator font = file->second.m_aEntry.begin(); font != file->second.m_aEntry.end(); ++font ) + { + PrintFontManager::PrintFont* pFont = clonePrintFont( *font ); + rNewFonts.push_back( pFont ); + } + } + } + return bFound; +} + +/* + * FontCache::listDirectory + */ +bool FontCache::scanAdditionalFiles( const OString& rDir ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + int nDirID = rManager.getDirectoryAtom( rDir ); + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + bool bFound = (dir != m_aCache.end()); + + return (bFound && dir->second.m_bUserOverrideOnly); +} + +/* + * FontCache::createCacheDir + */ +void FontCache::createCacheDir( int nDirID ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + + const OString& rDir = rManager.getDirectory( nDirID ); + struct stat aStat; + if( ! stat( rDir.getStr(), &aStat ) ) + m_aCache[nDirID].m_nTimestamp = (sal_Int64)aStat.st_mtime; +} + +/* + * FontCache::markEmptyDir + */ +void FontCache::markEmptyDir( int nDirID, bool bNoFiles ) +{ + createCacheDir( nDirID ); + m_aCache[nDirID].m_bNoFiles = bNoFiles; + m_bDoFlush = true; +} diff --git a/vcl/unx/source/fontmanager/fontconfig.cxx b/vcl/unx/source/fontmanager/fontconfig.cxx new file mode 100644 index 000000000000..c44e082f91bd --- /dev/null +++ b/vcl/unx/source/fontmanager/fontconfig.cxx @@ -0,0 +1,1078 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: fontconfig.cxx,v $ + * $Revision: 1.30.24.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vcl/fontmanager.hxx" +#include "vcl/fontcache.hxx" + +using namespace psp; + +#ifdef ENABLE_FONTCONFIG +#include <fontconfig/fontconfig.h> +#include <ft2build.h> +#include <fontconfig/fcfreetype.h> +// be compatible with fontconfig 2.2.0 release +#ifndef FC_WEIGHT_BOOK + #define FC_WEIGHT_BOOK 75 +#endif +#ifndef FC_EMBEDDED_BITMAP + #define FC_EMBEDDED_BITMAP "embeddedbitmap" +#endif +#ifndef FC_FAMILYLANG + #define FC_FAMILYLANG "familylang" +#endif +#else +typedef void FcConfig; +typedef void FcObjectSet; +typedef void FcPattern; +typedef void FcFontSet; +typedef void FcCharSet; +typedef int FcResult; +typedef int FcBool; +typedef int FcMatchKind; +typedef char FcChar8; +typedef int FcChar32; +typedef unsigned int FT_UInt; +typedef void* FT_Face; +typedef int FcSetName; +#endif + +#include <cstdio> +#include <cstdarg> + +#include "unotools/atom.hxx" + +#include "osl/module.h" +#include "osl/thread.h" +#include "osl/process.h" + +#include "rtl/ustrbuf.hxx" +#include "rtl/locale.hxx" + +#include "sal/alloca.h" + +#include <utility> +#include <algorithm> + +using namespace osl; +using namespace rtl; + +class FontCfgWrapper +{ + oslModule m_pLib; + FcFontSet* m_pOutlineSet; + + FcBool (*m_pFcInit)(); + int (*m_pFcGetVersion)(); + FcConfig* (*m_pFcConfigGetCurrent)(); + FcObjectSet* (*m_pFcObjectSetVaBuild)(const char*,va_list); + void (*m_pFcObjectSetDestroy)(FcObjectSet* pSet); + FcPattern* (*m_pFcPatternCreate)(); + void (*m_pFcPatternDestroy)(FcPattern*); + FcFontSet* (*m_pFcFontList)(FcConfig*,FcPattern*,FcObjectSet*); + FcFontSet* (*m_pFcConfigGetFonts)(FcConfig*,FcSetName); + FcFontSet* (*m_pFcFontSetCreate)(); + FcCharSet* (*m_pFcCharSetCreate)(); + FcBool (*m_pFcCharSetAddChar)(FcCharSet *, FcChar32); + FcBool (*m_pFcCharSetHasChar)(FcCharSet *, FcChar32); + void (*m_pFcCharSetDestroy)(FcCharSet*); + void (*m_pFcFontSetDestroy)(FcFontSet*); + FcBool (*m_pFcFontSetAdd)(FcFontSet*,FcPattern*); + void (*m_pFcPatternReference)(FcPattern*); + FcResult (*m_pFcPatternGetCharSet)(const FcPattern*,const char*,int,FcCharSet**); + FcResult (*m_pFcPatternGetString)(const FcPattern*,const char*,int,FcChar8**); + FcResult (*m_pFcPatternGetInteger)(const FcPattern*,const char*,int,int*); + FcResult (*m_pFcPatternGetDouble)(const FcPattern*,const char*,int,double*); + FcResult (*m_pFcPatternGetBool)(const FcPattern*,const char*,int,FcBool*); + void (*m_pFcDefaultSubstitute)(FcPattern *); + FcPattern* (*m_pFcFontSetMatch)(FcConfig*,FcFontSet**, int, FcPattern*,FcResult*); + FcBool (*m_pFcConfigAppFontAddFile)(FcConfig*, const FcChar8*); + FcBool (*m_pFcConfigAppFontAddDir)(FcConfig*, const FcChar8*); + FcBool (*m_pFcConfigSubstitute)(FcConfig*,FcPattern*,FcMatchKind); + FcBool (*m_pFcPatternAddInteger)(FcPattern*,const char*,int); + FcBool (*m_pFcPatternAddBool)(FcPattern*,const char*,FcBool); + FcBool (*m_pFcPatternAddCharSet)(FcPattern*,const char*,const FcCharSet*); + FcBool (*m_pFcPatternAddString)(FcPattern*,const char*,const FcChar8*); + FT_UInt (*m_pFcFreeTypeCharIndex)(FT_Face,FcChar32); + + oslGenericFunction loadSymbol( const char* ); + void addFontSet( FcSetName ); + + FontCfgWrapper(); + ~FontCfgWrapper(); + +public: + static FontCfgWrapper& get(); + static void release(); + + bool isValid() const + { return m_pLib != NULL;} + + FcFontSet* getFontSet(); + + FcBool FcInit() + { return m_pFcInit(); } + + int FcGetVersion() + { return m_pFcGetVersion(); } + + FcConfig* FcConfigGetCurrent() + { return m_pFcConfigGetCurrent(); } + + FcObjectSet* FcObjectSetBuild( const char* first, ... ) + { + va_list ap; + va_start( ap, first ); + FcObjectSet* pSet = m_pFcObjectSetVaBuild( first, ap ); + va_end( ap ); + return pSet; + } + + void FcObjectSetDestroy( FcObjectSet* pSet ) + { m_pFcObjectSetDestroy( pSet ); } + + FcPattern* FcPatternCreate() + { return m_pFcPatternCreate(); } + + void FcPatternDestroy( FcPattern* pPattern ) + { m_pFcPatternDestroy( pPattern ); } + + FcFontSet* FcFontList( FcConfig* pConfig, FcPattern* pPattern, FcObjectSet* pSet ) + { return m_pFcFontList( pConfig, pPattern, pSet ); } + + FcFontSet* FcConfigGetFonts( FcConfig* pConfig, FcSetName eSet) + { return m_pFcConfigGetFonts( pConfig, eSet ); } + + FcFontSet* FcFontSetCreate() + { return m_pFcFontSetCreate(); } + + FcCharSet* FcCharSetCreate() + { return m_pFcCharSetCreate(); } + + FcBool FcCharSetAddChar(FcCharSet *fcs, FcChar32 ucs4) + { return m_pFcCharSetAddChar(fcs, ucs4); } + + FcBool FcCharSetHasChar(FcCharSet *fcs, FcChar32 ucs4) + { return m_pFcCharSetHasChar(fcs, ucs4); } + + void FcCharSetDestroy( FcCharSet* pSet ) + { m_pFcCharSetDestroy( pSet );} + + void FcFontSetDestroy( FcFontSet* pSet ) + { m_pFcFontSetDestroy( pSet );} + + FcBool FcFontSetAdd( FcFontSet* pSet, FcPattern* pPattern ) + { return m_pFcFontSetAdd( pSet, pPattern ); } + + void FcPatternReference( FcPattern* pPattern ) + { m_pFcPatternReference( pPattern ); } + + FcResult FcPatternGetCharSet( const FcPattern* pPattern, const char* object, int n, FcCharSet** s ) + { return m_pFcPatternGetCharSet( pPattern, object, n, s ); } + + FcResult FcPatternGetString( const FcPattern* pPattern, const char* object, int n, FcChar8** s ) + { return m_pFcPatternGetString( pPattern, object, n, s ); } + + FcResult FcPatternGetInteger( const FcPattern* pPattern, const char* object, int n, int* s ) + { return m_pFcPatternGetInteger( pPattern, object, n, s ); } + + FcResult FcPatternGetDouble( const FcPattern* pPattern, const char* object, int n, double* s ) + { return m_pFcPatternGetDouble( pPattern, object, n, s ); } + + FcResult FcPatternGetBool( const FcPattern* pPattern, const char* object, int n, FcBool* s ) + { return m_pFcPatternGetBool( pPattern, object, n, s ); } + FcBool FcConfigAppFontAddFile( FcConfig* pConfig, const FcChar8* pFileName ) + { return m_pFcConfigAppFontAddFile( pConfig, pFileName ); } + FcBool FcConfigAppFontAddDir(FcConfig* pConfig, const FcChar8* pDirName ) + { return m_pFcConfigAppFontAddDir( pConfig, pDirName ); } + void FcDefaultSubstitute( FcPattern* pPattern ) + { m_pFcDefaultSubstitute( pPattern ); } + FcPattern* FcFontSetMatch( FcConfig* pConfig, FcFontSet **ppFontSet, int nset, FcPattern* pPattern, FcResult* pResult ) + { return m_pFcFontSetMatch ? m_pFcFontSetMatch( pConfig, ppFontSet, nset, pPattern, pResult ) : 0; } + FcBool FcConfigSubstitute( FcConfig* pConfig, FcPattern* pPattern, FcMatchKind eKind ) + { return m_pFcConfigSubstitute( pConfig, pPattern, eKind ); } + FcBool FcPatternAddInteger( FcPattern* pPattern, const char* pObject, int nValue ) + { return m_pFcPatternAddInteger( pPattern, pObject, nValue ); } + FcBool FcPatternAddString( FcPattern* pPattern, const char* pObject, const FcChar8* pString ) + { return m_pFcPatternAddString( pPattern, pObject, pString ); } + FcBool FcPatternAddBool( FcPattern* pPattern, const char* pObject, bool nValue ) + { return m_pFcPatternAddBool( pPattern, pObject, nValue ); } + FcBool FcPatternAddCharSet(FcPattern* pPattern,const char* pObject,const FcCharSet*pCharSet) + { return m_pFcPatternAddCharSet(pPattern,pObject,pCharSet); } + + FT_UInt FcFreeTypeCharIndex( FT_Face face, FcChar32 ucs4 ) + { return m_pFcFreeTypeCharIndex ? m_pFcFreeTypeCharIndex( face, ucs4 ) : 0; } + +public: // TODO: cleanup + std::hash_map< rtl::OString, rtl::OString, rtl::OStringHash > m_aFontconfigNameToLocalized; +}; + +oslGenericFunction FontCfgWrapper::loadSymbol( const char* pSymbol ) +{ + OUString aSym( OUString::createFromAscii( pSymbol ) ); + oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" ); +#endif + return pSym; +} + +FontCfgWrapper::FontCfgWrapper() + : m_pLib( NULL ), + m_pOutlineSet( NULL ) +{ + OUString aLib( RTL_CONSTASCII_USTRINGPARAM( "libfontconfig.so.1" ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + if( !m_pLib ) + { + aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( "libfontconfig.so" ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + } + + if( ! m_pLib ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "no libfontconfig\n" ); +#endif + return; + } + + m_pFcInit = (FcBool(*)()) + loadSymbol( "FcInit" ); + m_pFcGetVersion = (int(*)()) + loadSymbol( "FcGetVersion" ); + m_pFcConfigGetCurrent = (FcConfig *(*)()) + loadSymbol( "FcConfigGetCurrent" ); + m_pFcObjectSetVaBuild = (FcObjectSet*(*)(const char*,va_list)) + loadSymbol( "FcObjectSetVaBuild" ); + m_pFcObjectSetDestroy = (void(*)(FcObjectSet*)) + loadSymbol( "FcObjectSetDestroy" ); + m_pFcPatternCreate = (FcPattern*(*)()) + loadSymbol( "FcPatternCreate" ); + m_pFcPatternDestroy = (void(*)(FcPattern*)) + loadSymbol( "FcPatternDestroy" ); + m_pFcFontList = (FcFontSet*(*)(FcConfig*,FcPattern*,FcObjectSet*)) + loadSymbol( "FcFontList" ); + m_pFcConfigGetFonts = (FcFontSet*(*)(FcConfig*,FcSetName)) + loadSymbol( "FcConfigGetFonts" ); + m_pFcFontSetCreate = (FcFontSet*(*)()) + loadSymbol( "FcFontSetCreate" ); + m_pFcCharSetCreate = (FcCharSet*(*)()) + loadSymbol( "FcCharSetCreate" ); + m_pFcCharSetAddChar = (FcBool(*)(FcCharSet*, FcChar32)) + loadSymbol( "FcCharSetAddChar" ); + m_pFcCharSetHasChar = (FcBool(*)(FcCharSet*, FcChar32)) + loadSymbol( "FcCharSetHasChar" ); + m_pFcCharSetDestroy = (void(*)(FcCharSet*)) + loadSymbol( "FcCharSetDestroy" ); + m_pFcFontSetDestroy = (void(*)(FcFontSet*)) + loadSymbol( "FcFontSetDestroy" ); + m_pFcFontSetAdd = (FcBool(*)(FcFontSet*,FcPattern*)) + loadSymbol( "FcFontSetAdd" ); + m_pFcPatternReference = (void(*)(FcPattern*)) + loadSymbol( "FcPatternReference" ); + m_pFcPatternGetCharSet = (FcResult(*)(const FcPattern*,const char*,int,FcCharSet**)) + loadSymbol( "FcPatternGetCharSet" ); + m_pFcPatternGetString = (FcResult(*)(const FcPattern*,const char*,int,FcChar8**)) + loadSymbol( "FcPatternGetString" ); + m_pFcPatternGetInteger = (FcResult(*)(const FcPattern*,const char*,int,int*)) + loadSymbol( "FcPatternGetInteger" ); + m_pFcPatternGetDouble = (FcResult(*)(const FcPattern*,const char*,int,double*)) + loadSymbol( "FcPatternGetDouble" ); + m_pFcPatternGetBool = (FcResult(*)(const FcPattern*,const char*,int,FcBool*)) + loadSymbol( "FcPatternGetBool" ); + m_pFcConfigAppFontAddFile = (FcBool(*)(FcConfig*, const FcChar8*)) + loadSymbol( "FcConfigAppFontAddFile" ); + m_pFcConfigAppFontAddDir = (FcBool(*)(FcConfig*, const FcChar8*)) + loadSymbol( "FcConfigAppFontAddDir" ); + m_pFcDefaultSubstitute = (void(*)(FcPattern *)) + loadSymbol( "FcDefaultSubstitute" ); + m_pFcFontSetMatch = (FcPattern*(*)(FcConfig*,FcFontSet**,int,FcPattern*,FcResult*)) + loadSymbol( "FcFontSetMatch" ); + m_pFcConfigSubstitute = (FcBool(*)(FcConfig*,FcPattern*,FcMatchKind)) + loadSymbol( "FcConfigSubstitute" ); + m_pFcPatternAddInteger = (FcBool(*)(FcPattern*,const char*,int)) + loadSymbol( "FcPatternAddInteger" ); + m_pFcPatternAddBool = (FcBool(*)(FcPattern*,const char*,FcBool)) + loadSymbol( "FcPatternAddBool" ); + m_pFcPatternAddCharSet = (FcBool(*)(FcPattern*,const char*,const FcCharSet *)) + loadSymbol( "FcPatternAddCharSet" ); + m_pFcPatternAddString = (FcBool(*)(FcPattern*,const char*,const FcChar8*)) + loadSymbol( "FcPatternAddString" ); + m_pFcFreeTypeCharIndex = (FT_UInt(*)(FT_Face,FcChar32)) + loadSymbol( "FcFreeTypeCharIndex" ); + + if( ! ( + m_pFcInit && + m_pFcGetVersion && + m_pFcConfigGetCurrent && + m_pFcObjectSetVaBuild && + m_pFcObjectSetDestroy && + m_pFcPatternCreate && + m_pFcPatternDestroy && + m_pFcFontList && + m_pFcConfigGetFonts && + m_pFcFontSetCreate && + m_pFcCharSetCreate && + m_pFcCharSetAddChar && + m_pFcCharSetHasChar && + m_pFcCharSetDestroy && + m_pFcFontSetDestroy && + m_pFcFontSetAdd && + m_pFcPatternReference && + m_pFcPatternGetCharSet && + m_pFcPatternGetString && + m_pFcPatternGetInteger && + m_pFcPatternGetDouble && + m_pFcPatternGetBool && + m_pFcConfigAppFontAddFile && + m_pFcConfigAppFontAddDir && + m_pFcDefaultSubstitute && + m_pFcConfigSubstitute && + m_pFcPatternAddInteger && + m_pFcPatternAddCharSet && + m_pFcPatternAddBool && + m_pFcPatternAddString + ) ) + { + osl_unloadModule( (oslModule)m_pLib ); + m_pLib = NULL; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "not all needed symbols were found in libfontconfig\n" ); +#endif + return; + } + + + FcInit(); + if( ! FcConfigGetCurrent() ) + { + osl_unloadModule( (oslModule)m_pLib ); + m_pLib = NULL; + } +} + +void FontCfgWrapper::addFontSet( FcSetName eSetName ) +{ + #ifdef ENABLE_FONTCONFIG + /* + add only acceptable outlined fonts to our config, + for future fontconfig use + */ + FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName ); + if( !pOrig ) + return; + + for( int i = 0; i < pOrig->nfont; ++i ) + { + FcBool outline = false; + FcPattern *pOutlinePattern = pOrig->fonts[i]; + FcResult eOutRes = + FcPatternGetBool( pOutlinePattern, FC_OUTLINE, 0, &outline ); + if( (eOutRes != FcResultMatch) || (outline != FcTrue) ) + continue; + FcPatternReference(pOutlinePattern); + FcFontSetAdd(m_pOutlineSet, pOutlinePattern); + } + // TODO: FcFontSetDestroy( pOrig ); + #else + (void)eSetName; // prevent compiler warning about unused parameter + #endif +} + +FcFontSet* FontCfgWrapper::getFontSet() +{ + #ifdef ENABLE_FONTCONFIG + if( !m_pOutlineSet ) + { + m_pOutlineSet = FcFontSetCreate(); + addFontSet( FcSetSystem ); + const int nVersion = FcGetVersion(); + if( nVersion > 20400 ) + addFontSet( FcSetApplication ); + } + #endif + + return m_pOutlineSet; +} + +FontCfgWrapper::~FontCfgWrapper() +{ + if( m_pOutlineSet ) + FcFontSetDestroy( m_pOutlineSet ); + if( m_pLib ) + osl_unloadModule( (oslModule)m_pLib ); +} + +static FontCfgWrapper* pOneInstance = NULL; + +FontCfgWrapper& FontCfgWrapper::get() +{ + if( ! pOneInstance ) + pOneInstance = new FontCfgWrapper(); + return *pOneInstance; +} + +void FontCfgWrapper::release() +{ + if( pOneInstance ) + { + delete pOneInstance; + pOneInstance = NULL; + } +} + +#ifdef ENABLE_FONTCONFIG +namespace +{ + typedef std::pair<FcChar8*, FcChar8*> lang_and_family; + + class localizedsorter + { + rtl::OLocale maLoc; + public: + localizedsorter(rtl_Locale* pLoc) : maLoc(pLoc) {} + FcChar8* bestname(const std::vector<lang_and_family> &families); + }; + + FcChar8* localizedsorter::bestname(const std::vector<lang_and_family> &families) + { + FcChar8* candidate = families.begin()->second; + rtl::OString sLangMatch(rtl::OUStringToOString(maLoc.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8)); + rtl::OString sFullMatch = sLangMatch; + sFullMatch += OString('-'); + sFullMatch += rtl::OUStringToOString(maLoc.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8); + + std::vector<lang_and_family>::const_iterator aEnd = families.end(); + bool alreadyclosematch = false; + for (std::vector<lang_and_family>::const_iterator aIter = families.begin(); aIter != aEnd; ++aIter) + { + const char *pLang = (const char*)aIter->first; + //perfect + if( rtl_str_compare(pLang,sFullMatch.getStr() ) == 0) + { + candidate = aIter->second; + break; + } + else if( (rtl_str_compare(pLang,sLangMatch.getStr()) == 0) && (!alreadyclosematch)) + { + candidate = aIter->second; + alreadyclosematch = true; + } + } + + return candidate; + } + + + FcResult lcl_FamilyFromPattern(FontCfgWrapper& rWrapper, FcPattern* pPattern, FcChar8 **family, + std::hash_map< rtl::OString, rtl::OString, rtl::OStringHash > &aFontconfigNameToLocalized) + { + FcChar8 *origfamily; + FcResult eFamilyRes = rWrapper.FcPatternGetString( pPattern, FC_FAMILY, 0, &origfamily ); + *family = origfamily; + + if( eFamilyRes == FcResultMatch) + { + FcChar8* familylang = NULL; + if (rWrapper.FcPatternGetString( pPattern, FC_FAMILYLANG, 0, &familylang ) == FcResultMatch) + { + std::vector< lang_and_family > lang_and_families; + lang_and_families.push_back(lang_and_family(familylang, *family)); + int k = 1; + while (1) + { + if (rWrapper.FcPatternGetString( pPattern, FC_FAMILYLANG, k, &familylang ) != FcResultMatch) + break; + if (rWrapper.FcPatternGetString( pPattern, FC_FAMILY, k, family ) != FcResultMatch) + break; + lang_and_families.push_back(lang_and_family(familylang, *family)); + ++k; + } + + //possible to-do, sort by UILocale instead of process locale + rtl_Locale* pLoc; + osl_getProcessLocale(&pLoc); + localizedsorter aSorter(pLoc); + *family = aSorter.bestname(lang_and_families); + + std::vector<lang_and_family>::const_iterator aEnd = lang_and_families.end(); + for (std::vector<lang_and_family>::const_iterator aIter = lang_and_families.begin(); aIter != aEnd; ++aIter) + { + const char *candidate = (const char*)(aIter->second); + if (rtl_str_compare(candidate, (const char*)(*family)) != 0) + aFontconfigNameToLocalized[OString(candidate)] = OString((const char*)(*family)); + } + } + } + + return eFamilyRes; + } +} + + +/* + * PrintFontManager::initFontconfig + */ +bool PrintFontManager::initFontconfig() +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return false; + return true; +} + +int PrintFontManager::countFontconfigFonts() +{ + int nFonts = 0; + + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( !rWrapper.isValid() ) + return 0; + + FcFontSet* pFSet = rWrapper.getFontSet(); + if( pFSet ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %d entries in fontconfig fontset\n", pFSet->nfont ); +#endif + for( int i = 0; i < pFSet->nfont; i++ ) + { + FcChar8* file = NULL; + FcChar8* family = NULL; + FcChar8* style = NULL; + int slant = 0; + int weight = 0; + int spacing = 0; + int nCollectionEntry = -1; + FcBool outline = false, embitmap = true, antialias = true; + + FcResult eFileRes = rWrapper.FcPatternGetString( pFSet->fonts[i], FC_FILE, 0, &file ); + FcResult eFamilyRes = lcl_FamilyFromPattern(rWrapper, pFSet->fonts[i], &family, rWrapper.m_aFontconfigNameToLocalized ); + FcResult eStyleRes = rWrapper.FcPatternGetString( pFSet->fonts[i], FC_STYLE, 0, &style ); + FcResult eSlantRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_SLANT, 0, &slant ); + FcResult eWeightRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_WEIGHT, 0, &weight ); + FcResult eSpacRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_SPACING, 0, &spacing ); + FcResult eOutRes = rWrapper.FcPatternGetBool( pFSet->fonts[i], FC_OUTLINE, 0, &outline ); + FcResult eIndexRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry ); + FcResult eEmbeddedBitmap = rWrapper.FcPatternGetBool( pFSet->fonts[i], FC_EMBEDDED_BITMAP, 0, &embitmap ); + FcResult eAntialias = rWrapper.FcPatternGetBool( pFSet->fonts[i], FC_ANTIALIAS, 0, &antialias ); + + if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch ) + continue; + +#if (OSL_DEBUG_LEVEL > 2) + fprintf( stderr, "found font \"%s\" in file %s\n" + " weight = %d, slant = %d, style = \"%s\"\n" + " spacing = %d, outline = %d\n" + , family, file + , eWeightRes == FcResultMatch ? weight : -1 + , eSpacRes == FcResultMatch ? slant : -1 + , eStyleRes == FcResultMatch ? (const char*) style : "<nil>" + , eSpacRes == FcResultMatch ? spacing : -1 + , eOutRes == FcResultMatch ? outline : -1 + ); +#endif + + OSL_ASSERT(eOutRes != FcResultMatch || outline); + + // only outline fonts are usable to psprint anyway + if( eOutRes == FcResultMatch && ! outline ) + continue; + + // see if this font is already cached + // update attributes + std::list< PrintFont* > aFonts; + OString aDir, aBase, aOrgPath( (sal_Char*)file ); + splitPath( aOrgPath, aDir, aBase ); + int nDirID = getDirectoryAtom( aDir, true ); + if( ! m_pFontCache->getFontCacheFile( nDirID, aBase, aFonts ) ) + { +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "file %s not cached\n", aBase.getStr() ); +#endif + // not known, analyze font file to get attributes + // not described by fontconfig (e.g. alias names, PSName) + std::list< OString > aDummy; + analyzeFontFile( nDirID, aBase, aDummy, aFonts ); +#if OSL_DEBUG_LEVEL > 1 + if( aFonts.empty() ) + fprintf( stderr, "Warning: file \"%s\" is unusable to psprint\n", aOrgPath.getStr() ); +#endif + } + if( aFonts.empty() ) + continue; + + int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( (sal_Char*)family ), RTL_TEXTENCODING_UTF8 ), sal_True ); + PrintFont* pUpdate = aFonts.front(); + std::list<PrintFont*>::const_iterator second_font = aFonts.begin(); + ++second_font; + if( second_font != aFonts.end() ) // more than one font + { + // a collection entry, get the correct index + if( eIndexRes == FcResultMatch && nCollectionEntry != -1 ) + { + for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it ) + { + if( (*it)->m_eType == fonttype::TrueType && + static_cast<TrueTypeFontFile*>(*it)->m_nCollectionEntry == nCollectionEntry ) + { + pUpdate = *it; + break; + } + } + // update collection entry + // additional entries will be created in the cache + // if this is a new index (that is if the loop above + // ran to the end of the list) + if( pUpdate->m_eType == fonttype::TrueType ) // sanity check, this should always be the case here + static_cast<TrueTypeFontFile*>(pUpdate)->m_nCollectionEntry = nCollectionEntry; + } + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "multiple fonts for file, but no index in fontconfig pattern ! (index res = %d collection entry = %d\nfile will not be used\n", eIndexRes, nCollectionEntry ); +#endif + // we have found more than one font in this file + // but fontconfig will not tell us which index is meant + // -> something is in disorder, do not use this font + pUpdate = NULL; + } + } + + if( pUpdate ) + { + // set family name + if( pUpdate->m_nFamilyName != nFamilyName ) + { + pUpdate->m_aAliases.remove( pUpdate->m_nFamilyName ); + pUpdate->m_aAliases.push_back( pUpdate->m_nFamilyName ); + pUpdate->m_aAliases.remove( nFamilyName ); + pUpdate->m_nFamilyName = nFamilyName; + } + if( eWeightRes == FcResultMatch ) + { + // set weight + if( weight <= FC_WEIGHT_THIN ) + pUpdate->m_eWeight = weight::Thin; + else if( weight <= FC_WEIGHT_ULTRALIGHT ) + pUpdate->m_eWeight = weight::UltraLight; + else if( weight <= FC_WEIGHT_LIGHT ) + pUpdate->m_eWeight = weight::Light; + else if( weight <= FC_WEIGHT_BOOK ) + pUpdate->m_eWeight = weight::SemiLight; + else if( weight <= FC_WEIGHT_NORMAL ) + pUpdate->m_eWeight = weight::Normal; + else if( weight <= FC_WEIGHT_MEDIUM ) + pUpdate->m_eWeight = weight::Medium; + else if( weight <= FC_WEIGHT_SEMIBOLD ) + pUpdate->m_eWeight = weight::SemiBold; + else if( weight <= FC_WEIGHT_BOLD ) + pUpdate->m_eWeight = weight::Bold; + else if( weight <= FC_WEIGHT_ULTRABOLD ) + pUpdate->m_eWeight = weight::UltraBold; + else + pUpdate->m_eWeight = weight::Black; + } + if( eSpacRes == FcResultMatch ) + { + // set pitch + if( spacing == FC_PROPORTIONAL ) + pUpdate->m_ePitch = pitch::Variable; + else if( spacing == FC_MONO || spacing == FC_CHARCELL ) + pUpdate->m_ePitch = pitch::Fixed; + } + if( eSlantRes == FcResultMatch ) + { + // set italic + if( slant == FC_SLANT_ROMAN ) + pUpdate->m_eItalic = italic::Upright; + else if( slant == FC_SLANT_ITALIC ) + pUpdate->m_eItalic = italic::Italic; + else if( slant == FC_SLANT_OBLIQUE ) + pUpdate->m_eItalic = italic::Oblique; + } + if( eStyleRes == FcResultMatch ) + { + pUpdate->m_aStyleName = OStringToOUString( OString( (sal_Char*)style ), RTL_TEXTENCODING_UTF8 ); + } + if( eEmbeddedBitmap == FcResultMatch ) + { + pUpdate->m_eEmbeddedbitmap = embitmap ? fcstatus::istrue : fcstatus::isfalse; + } + if( eAntialias == FcResultMatch ) + { + pUpdate->m_eAntialias = antialias ? fcstatus::istrue : fcstatus::isfalse; + } + + + // update font cache + m_pFontCache->updateFontCacheEntry( pUpdate, false ); + // sort into known fonts + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = pUpdate; + m_aFontFileToFontID[ aBase ].insert( aFont ); + nFonts++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "inserted font %s as fontID %d\n", family, aFont ); +#endif + } + // clean up the fonts we did not put into the list + for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it ) + { + if( *it != pUpdate ) + { + m_pFontCache->updateFontCacheEntry( *it, false ); // prepare a cache entry for a collection item + delete *it; + } + } + } + } + + // how does one get rid of the config ? +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "inserted %d fonts from fontconfig\n", nFonts ); +#endif + return nFonts; +} + +void PrintFontManager::deinitFontconfig() +{ + FontCfgWrapper::release(); +} + +int PrintFontManager::FreeTypeCharIndex( void *pFace, sal_uInt32 aChar ) +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + return rWrapper.isValid() ? rWrapper.FcFreeTypeCharIndex( (FT_Face)pFace, aChar ) : 0; +} + +bool PrintFontManager::addFontconfigDir( const rtl::OString& rDirName ) +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return false; + + // workaround for a stability problems in older FC versions + // when handling application specifc fonts + const int nVersion = rWrapper.FcGetVersion(); + if( nVersion <= 20400 ) + return false; + const char* pDirName = (const char*)rDirName.getStr(); + bool bRet = (rWrapper.FcConfigAppFontAddDir( rWrapper.FcConfigGetCurrent(), (FcChar8*)pDirName ) == FcTrue); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "FcConfigAppFontAddDir( \"%s\") => %d\n", pDirName, bRet ); +#endif + + return bRet; +} + +static void addtopattern(FontCfgWrapper& rWrapper, FcPattern *pPattern, + italic::type eItalic, weight::type eWeight, width::type eWidth, pitch::type ePitch) +{ + if( eItalic != italic::Unknown ) + { + int nSlant = FC_SLANT_ROMAN; + switch( eItalic ) + { + case italic::Italic: nSlant = FC_SLANT_ITALIC;break; + case italic::Oblique: nSlant = FC_SLANT_OBLIQUE;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_SLANT, nSlant ); + } + if( eWeight != weight::Unknown ) + { + int nWeight = FC_WEIGHT_NORMAL; + switch( eWeight ) + { + case weight::Thin: nWeight = FC_WEIGHT_THIN;break; + case weight::UltraLight: nWeight = FC_WEIGHT_ULTRALIGHT;break; + case weight::Light: nWeight = FC_WEIGHT_LIGHT;break; + case weight::SemiLight: nWeight = FC_WEIGHT_BOOK;break; + case weight::Normal: nWeight = FC_WEIGHT_NORMAL;break; + case weight::Medium: nWeight = FC_WEIGHT_MEDIUM;break; + case weight::SemiBold: nWeight = FC_WEIGHT_SEMIBOLD;break; + case weight::Bold: nWeight = FC_WEIGHT_BOLD;break; + case weight::UltraBold: nWeight = FC_WEIGHT_ULTRABOLD;break; + case weight::Black: nWeight = FC_WEIGHT_BLACK;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_WEIGHT, nWeight ); + } + if( eWidth != width::Unknown ) + { + int nWidth = FC_WIDTH_NORMAL; + switch( eWidth ) + { + case width::UltraCondensed: nWidth = FC_WIDTH_ULTRACONDENSED;break; + case width::ExtraCondensed: nWidth = FC_WIDTH_EXTRACONDENSED;break; + case width::Condensed: nWidth = FC_WIDTH_CONDENSED;break; + case width::SemiCondensed: nWidth = FC_WIDTH_SEMICONDENSED;break; + case width::Normal: nWidth = FC_WIDTH_NORMAL;break; + case width::SemiExpanded: nWidth = FC_WIDTH_SEMIEXPANDED;break; + case width::Expanded: nWidth = FC_WIDTH_EXPANDED;break; + case width::ExtraExpanded: nWidth = FC_WIDTH_EXTRAEXPANDED;break; + case width::UltraExpanded: nWidth = FC_WIDTH_ULTRACONDENSED;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_WIDTH, nWidth ); + } + if( ePitch != pitch::Unknown ) + { + int nSpacing = FC_PROPORTIONAL; + switch( ePitch ) + { + case pitch::Fixed: nSpacing = FC_MONO;break; + case pitch::Variable: nSpacing = FC_PROPORTIONAL;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_SPACING, nSpacing ); + if (nSpacing == FC_MONO) + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, (FcChar8*)"monospace"); + } +} + +rtl::OUString PrintFontManager::Substitute(const rtl::OUString& rFontName, + rtl::OUString& rMissingCodes, const rtl::OString &rLangAttrib, + italic::type eItalic, weight::type eWeight, + width::type eWidth, pitch::type ePitch) const +{ + rtl::OUString aName; + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return aName; + + // build pattern argument for fontconfig query + FcPattern* pPattern = rWrapper.FcPatternCreate(); + + // Prefer scalable fonts + rWrapper.FcPatternAddBool( pPattern, FC_SCALABLE, FcTrue ); + + const rtl::OString aTargetName = rtl::OUStringToOString( rFontName, RTL_TEXTENCODING_UTF8 ); + const FcChar8* pTargetNameUtf8 = (FcChar8*)aTargetName.getStr(); + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, pTargetNameUtf8 ); + + const FcChar8* pLangAttribUtf8 = (FcChar8*)rLangAttrib.getStr(); + if( rLangAttrib.getLength() ) + rWrapper.FcPatternAddString( pPattern, FC_LANG, pLangAttribUtf8 ); + + // Add required Unicode characters, if any + if ( rMissingCodes.getLength() ) + { + FcCharSet *unicodes = rWrapper.FcCharSetCreate(); + for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) + { + // also handle unicode surrogates + const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex ); + rWrapper.FcCharSetAddChar( unicodes, nCode ); + } + rWrapper.FcPatternAddCharSet( pPattern, FC_CHARSET, unicodes); + rWrapper.FcCharSetDestroy( unicodes ); + } + + addtopattern(rWrapper, pPattern, eItalic, eWeight, eWidth, ePitch); + + // query fontconfig for a substitute + rWrapper.FcConfigSubstitute( rWrapper.FcConfigGetCurrent(), pPattern, FcMatchPattern ); + rWrapper.FcDefaultSubstitute( pPattern ); + + // process the result of the fontconfig query + FcResult eResult = FcResultNoMatch; + FcFontSet* pFontSet = rWrapper.getFontSet(); + FcPattern* pResult = rWrapper.FcFontSetMatch( rWrapper.FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult ); + rWrapper.FcPatternDestroy( pPattern ); + + FcFontSet* pSet = NULL; + if( pResult ) + { + pSet = rWrapper.FcFontSetCreate(); + // info: destroying the pSet destroys pResult implicitly + // since pResult was "added" to pSet + rWrapper.FcFontSetAdd( pSet, pResult ); + } + + if( pSet ) + { + if( pSet->nfont > 0 ) + { + //extract the closest match + FcChar8* family = NULL; + FcResult eFileRes = rWrapper.FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family ); + + // get the family name + if( eFileRes == FcResultMatch ) + { + OString sFamily((sal_Char*)family); + std::hash_map< rtl::OString, rtl::OString, rtl::OStringHash >::const_iterator aI = rWrapper.m_aFontconfigNameToLocalized.find(sFamily); + if (aI != rWrapper.m_aFontconfigNameToLocalized.end()) + sFamily = aI->second; + aName = rtl::OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 ); + } + + // update rMissingCodes by removing resolved unicodes + if( rMissingCodes.getLength() > 0 ) + { + sal_uInt32* pRemainingCodes = (sal_uInt32*)alloca( rMissingCodes.getLength() * sizeof(sal_uInt32) ); + int nRemainingLen = 0; + FcCharSet* unicodes; + if( !rWrapper.FcPatternGetCharSet( pSet->fonts[0], FC_CHARSET, 0, &unicodes ) ) + { + for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) + { + // also handle unicode surrogates + const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex ); + if( rWrapper.FcCharSetHasChar( unicodes, nCode ) != FcTrue ) + pRemainingCodes[ nRemainingLen++ ] = nCode; + } + } + rMissingCodes = OUString( pRemainingCodes, nRemainingLen ); + } + } + + rWrapper.FcFontSetDestroy( pSet ); + } + + return aName; +} + +bool PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const com::sun::star::lang::Locale& rLocale ) +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return false; + + FcConfig* pConfig = rWrapper.FcConfigGetCurrent(); + FcPattern* pPattern = rWrapper.FcPatternCreate(); + + OString aLangAttrib; + // populate pattern with font characteristics + if( rLocale.Language.getLength() ) + { + OUStringBuffer aLang(6); + aLang.append( rLocale.Language ); + if( rLocale.Country.getLength() ) + { + aLang.append( sal_Unicode('-') ); + aLang.append( rLocale.Country ); + } + aLangAttrib = OUStringToOString( aLang.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); + } + if( aLangAttrib.getLength() ) + rWrapper.FcPatternAddString( pPattern, FC_LANG, (FcChar8*)aLangAttrib.getStr() ); + + OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 ); + if( aFamily.getLength() ) + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, (FcChar8*)aFamily.getStr() ); + + addtopattern(rWrapper, pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch); + + rWrapper.FcConfigSubstitute( pConfig, pPattern, FcMatchPattern ); + rWrapper.FcDefaultSubstitute( pPattern ); + FcResult eResult = FcResultNoMatch; + FcFontSet *pFontSet = rWrapper.getFontSet(); + FcPattern* pResult = rWrapper.FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult ); + bool bSuccess = false; + if( pResult ) + { + FcFontSet* pSet = rWrapper.FcFontSetCreate(); + rWrapper.FcFontSetAdd( pSet, pResult ); + if( pSet->nfont > 0 ) + { + //extract the closest match + FcChar8* file = NULL; + FcResult eFileRes = rWrapper.FcPatternGetString( pSet->fonts[0], FC_FILE, 0, &file ); + if( eFileRes == FcResultMatch ) + { + OString aDir, aBase, aOrgPath( (sal_Char*)file ); + splitPath( aOrgPath, aDir, aBase ); + int nDirID = getDirectoryAtom( aDir, true ); + fontID aFont = findFontFileID( nDirID, aBase ); + if( aFont > 0 ) + bSuccess = getFontFastInfo( aFont, rInfo ); + } + } + // info: destroying the pSet destroys pResult implicitly + // since pResult was "added" to pSet + rWrapper.FcFontSetDestroy( pSet ); + } + + // cleanup + rWrapper.FcPatternDestroy( pPattern ); + + return bSuccess; +} + +#else // ENABLE_FONTCONFIG not defined + +bool PrintFontManager::initFontconfig() +{ + return false; +} + +int PrintFontManager::countFontconfigFonts() +{ + return 0; +} + +void PrintFontManager::deinitFontconfig() +{} + +bool PrintFontManager::addFontconfigDir( const rtl::OString& ) +{ + return false; +} + +bool PrintFontManager::matchFont( FastPrintFontInfo&, const com::sun::star::lang::Locale& ) +{ + return false; +} + +int PrintFontManager::FreeTypeCharIndex( void*, sal_uInt32 ) +{ + return 0; +} + +rtl::OUString PrintFontManager::Substitute( const rtl::OUString&, + rtl::OUString&, const rtl::OString&, italic::type, weight::type, width::type, pitch::type) const +{ + rtl::OUString aName; + return aName; +} + +#endif // ENABLE_FONTCONFIG + diff --git a/vcl/unx/source/fontmanager/fontmanager.cxx b/vcl/unx/source/fontmanager/fontmanager.cxx new file mode 100644 index 000000000000..73e117550a14 --- /dev/null +++ b/vcl/unx/source/fontmanager/fontmanager.cxx @@ -0,0 +1,4008 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: fontmanager.cxx,v $ + * $Revision: 1.81.22.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unistd.h> +#include <sys/stat.h> +#include <dirent.h> +#include <stdlib.h> +#include <osl/thread.h> + +#include "unotools/atom.hxx" + +#include "vcl/fontmanager.hxx" +#include "vcl/fontcache.hxx" +#include "vcl/helper.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/ppdparser.hxx" +#include "vcl/svdata.hxx" +#include "vcl/salinst.hxx" + +#include "tools/urlobj.hxx" +#include "tools/stream.hxx" +#include "tools/debug.hxx" +#include "tools/config.hxx" + +#include "osl/file.hxx" +#include "osl/process.h" + +#include "rtl/tencinfo.h" +#include "rtl/ustrbuf.hxx" +#include "rtl/strbuf.hxx" + +#include "i18npool/mslangid.hxx" + + +#include "parseAFM.hxx" +#define NO_LIST +#include "sft.h" +#undef NO_LIST + +#if OSL_DEBUG_LEVEL > 1 +#include <sys/times.h> +#include <stdio.h> +#endif + +#include "sal/alloca.h" + +#include <set> +#include <hash_set> +#include <algorithm> + +#include "adobeenc.tab" // get encoding table for AFM metrics + +#ifdef CALLGRIND_COMPILE +#include <valgrind/callgrind.h> +#endif + +#include "comphelper/processfactory.hxx" +#include "com/sun/star/beans/XMaterialHolder.hpp" +#include "com/sun/star/beans/NamedValue.hpp" + +#define PRINTER_METRICDIR "fontmetric" + +using namespace utl; +using namespace psp; +using namespace osl; +using namespace rtl; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; + +/* + * static helpers + */ + +inline sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer ) +{ + sal_uInt16 nRet = (sal_uInt16)pBuffer[1] | + (((sal_uInt16)pBuffer[0]) << 8); + pBuffer+=2; + return nRet; +} + +inline sal_uInt32 getUInt32BE( const sal_uInt8*& pBuffer ) +{ + sal_uInt32 nRet = (((sal_uInt32)pBuffer[0]) << 24) | + (((sal_uInt32)pBuffer[1]) << 16) | + (((sal_uInt32)pBuffer[2]) << 8) | + (((sal_uInt32)pBuffer[3]) ); + pBuffer += 4; + return nRet; +} + +static italic::type parseItalic( const ByteString& rItalic ) +{ + italic::type eItalic = italic::Unknown; + if( rItalic.EqualsIgnoreCaseAscii( "i" ) ) + eItalic = italic::Italic; + else if( rItalic.EqualsIgnoreCaseAscii( "o" ) ) + eItalic = italic::Oblique; + else + eItalic = italic::Upright; + return eItalic; +} + +// ------------------------------------------------------------------------- + +static weight::type parseWeight( const ByteString& rWeight ) +{ + weight::type eWeight = weight::Unknown; + if( rWeight.Search( "bold" ) != STRING_NOTFOUND ) + { + if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi + eWeight = weight::SemiBold; + else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND ) + eWeight = weight::UltraBold; + else + eWeight = weight::Bold; + } + else if( rWeight.Search( "heavy" ) != STRING_NOTFOUND ) + eWeight = weight::Bold; + else if( rWeight.Search( "light" ) != STRING_NOTFOUND ) + { + if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi + eWeight = weight::SemiLight; + else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND ) + eWeight = weight::UltraLight; + else + eWeight = weight::Light; + } + else if( rWeight.Search( "black" ) != STRING_NOTFOUND ) + eWeight = weight::Black; + else if( rWeight.Equals( "demi" ) ) + eWeight = weight::SemiBold; + else if( rWeight.Equals( "book" ) || + rWeight.Equals( "semicondensed" ) ) + eWeight = weight::Light; + else if( rWeight.Equals( "medium" ) || rWeight.Equals( "roman" ) ) + eWeight = weight::Medium; + else + eWeight = weight::Normal; + return eWeight; +} + +// ------------------------------------------------------------------------- + +static width::type parseWidth( const ByteString& rWidth ) +{ + width::type eWidth = width::Unknown; + if( rWidth.Equals( "bold" ) || + rWidth.Equals( "semiexpanded" ) ) + eWidth = width::SemiExpanded; + else if( rWidth.Equals( "condensed" ) || + rWidth.Equals( "narrow" ) ) + eWidth = width::Condensed; + else if( rWidth.Equals( "double wide" ) || + rWidth.Equals( "extraexpanded" ) || + rWidth.Equals( "ultraexpanded" ) ) + eWidth = width::UltraExpanded; + else if( rWidth.Equals( "expanded" ) || + rWidth.Equals( "wide" ) ) + eWidth = width::Expanded; + else if( rWidth.Equals( "extracondensed" ) ) + eWidth = width::ExtraCondensed; + else if( rWidth.Equals( "semicondensed" ) ) + eWidth = width::SemiCondensed; + else if( rWidth.Equals( "ultracondensed" ) ) + eWidth = width::UltraCondensed; + else + eWidth = width::Normal; + + return eWidth; +} + +// ------------------------------------------------------------------------- +bool PrintFontManager::XLFDEntry::operator<(const PrintFontManager::XLFDEntry& rRight) const +{ + sal_Int32 nCmp = 0; + if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer, + aFamily.pData->length, + rRight.aFamily.pData->buffer, + rRight.aFamily.pData->length ); + if( nCmp != 0 ) + return nCmp < 0; + } + + if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer, + aFoundry.pData->length, + rRight.aFoundry.pData->buffer, + rRight.aFoundry.pData->length ); + if( nCmp != 0 ) + return nCmp < 0; + } + + if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) ) + { + if( eItalic != rRight.eItalic ) + return (int)eItalic < (int)rRight.eItalic; + } + + if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) ) + { + if( eWeight != rRight.eWeight ) + return (int)eWeight < (int)rRight.eWeight; + } + + if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) ) + { + if( eWidth != rRight.eWidth ) + return (int)eWidth < (int)rRight.eWidth; + } + + if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) ) + { + if( ePitch != rRight.ePitch ) + return (int)ePitch < (int)rRight.ePitch; + } + + if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer, + aAddStyle.pData->length, + rRight.aAddStyle.pData->buffer, + rRight.aAddStyle.pData->length ); + if( nCmp != 0 ) + return nCmp < 0; + } + + if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) ) + { + if( aEncoding != rRight.aEncoding ) + return aEncoding < rRight.aEncoding; + } + + return false; +} + +bool PrintFontManager::XLFDEntry::operator==(const PrintFontManager::XLFDEntry& rRight) const +{ + sal_Int32 nCmp = 0; + if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer, + aFamily.pData->length, + rRight.aFamily.pData->buffer, + rRight.aFamily.pData->length ); + if( nCmp != 0 ) + return false; + } + + if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer, + aFoundry.pData->length, + rRight.aFoundry.pData->buffer, + rRight.aFoundry.pData->length ); + if( nCmp != 0 ) + return false; + } + + if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) ) + { + if( eItalic != rRight.eItalic ) + return false; + } + + if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) ) + { + if( eWeight != rRight.eWeight ) + return false; + } + + if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) ) + { + if( eWidth != rRight.eWidth ) + return false; + } + + if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) ) + { + if( ePitch != rRight.ePitch ) + return false; + } + + if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer, + aAddStyle.pData->length, + rRight.aAddStyle.pData->buffer, + rRight.aAddStyle.pData->length ); + if( nCmp != 0 ) + return false; + } + + if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) ) + { + if( aEncoding != rRight.aEncoding ) + return false; + } + + return true; +} + +/* + * PrintFont implementations + */ +PrintFontManager::PrintFont::PrintFont( fonttype::type eType ) : + m_eType( eType ), + m_nFamilyName( 0 ), + m_nPSName( 0 ), + m_eItalic( italic::Unknown ), + m_eWidth( width::Unknown ), + m_eWeight( weight::Unknown ), + m_ePitch( pitch::Unknown ), + m_aEncoding( RTL_TEXTENCODING_DONTKNOW ), + m_bFontEncodingOnly( false ), + m_pMetrics( NULL ), + m_nAscend( 0 ), + m_nDescend( 0 ), + m_nLeading( 0 ), + m_nXMin( 0 ), + m_nYMin( 0 ), + m_nXMax( 0 ), + m_nYMax( 0 ), + m_bHaveVerticalSubstitutedGlyphs( false ), + m_bUserOverride( false ), + m_eEmbeddedbitmap( fcstatus::isunset ), + m_eAntialias( fcstatus::isunset ) +{ +} + +// ------------------------------------------------------------------------- + +PrintFontManager::PrintFont::~PrintFont() +{ + if( m_pMetrics ) + delete m_pMetrics; +} + +// ------------------------------------------------------------------------- + +PrintFontManager::Type1FontFile::~Type1FontFile() +{ +} + +// ------------------------------------------------------------------------- + +PrintFontManager::TrueTypeFontFile::~TrueTypeFontFile() +{ +} + +// ------------------------------------------------------------------------- + +PrintFontManager::BuiltinFont::~BuiltinFont() +{ +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::Type1FontFile::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider ) +{ + return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false ); +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::BuiltinFont::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider ) +{ + return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false ); +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::TrueTypeFontFile::queryMetricPage( int nPage, MultiAtomProvider* pProvider ) +{ + bool bSuccess = false; + + ByteString aFile( PrintFontManager::get().getFontFile( this ) ); + + TrueTypeFont* pTTFont = NULL; + + if( OpenTTFontFile( aFile.GetBuffer(), m_nCollectionEntry < 0 ? 0 : m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + if( ! m_pMetrics ) + { + m_pMetrics = new PrintFontMetrics; + memset (m_pMetrics->m_aPages, 0, sizeof(m_pMetrics->m_aPages)); + } + m_pMetrics->m_aPages[ nPage/8 ] |= (1 << ( nPage & 7 )); + int i; + sal_uInt16 table[256], table_vert[256]; + + for( i = 0; i < 256; i++ ) + table[ i ] = 256*nPage + i; + + int nCharacters = nPage < 255 ? 256 : 254; + MapString( pTTFont, table, nCharacters, NULL, 0 ); + TTSimpleGlyphMetrics* pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 0 ); + if( pMetrics ) + { + for( i = 0; i < nCharacters; i++ ) + { + if( table[i] ) + { + CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i ]; + rChar.width = pMetrics[ i ].adv; + rChar.height = m_aGlobalMetricX.height; + } + } + + free( pMetrics ); + } + + for( i = 0; i < 256; i++ ) + table_vert[ i ] = 256*nPage + i; + MapString( pTTFont, table_vert, nCharacters, NULL, 1 ); + pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 1 ); + if( pMetrics ) + { + for( i = 0; i < nCharacters; i++ ) + { + if( table_vert[i] ) + { + CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i + ( 1 << 16 ) ]; + rChar.width = m_aGlobalMetricY.width; + rChar.height = pMetrics[ i ].adv; + if( table_vert[i] != table[i] ) + m_pMetrics->m_bVerticalSubstitutions[ nPage*256 + i ] = 1; + } + } + free( pMetrics ); + } + + if( ! m_pMetrics->m_bKernPairsQueried ) + { + m_pMetrics->m_bKernPairsQueried = true; + // this is really a hack + // in future MapString/KernGlyphs should be used + // but vcl is not in a state where that could be used + // so currently we get kernpairs by accessing the raw data + struct _TrueTypeFont* pImplTTFont = (struct _TrueTypeFont*)pTTFont; + + //----------------------------------------------------------------- + // Kerning: KT_MICROSOFT + //----------------------------------------------------------------- + if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_MICROSOFT ) + { + // create a glyph -> character mapping + ::std::hash_map< sal_uInt16, sal_Unicode > aGlyphMap; + ::std::hash_map< sal_uInt16, sal_Unicode >::iterator left, right; + for( i = 21; i < 0xfffd; i++ ) + { + sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only + if( nGlyph != 0 ) + aGlyphMap[ nGlyph ] = (sal_Unicode)i; + } + + + KernPair aPair; + for( i = 0; i < (int)pImplTTFont->nkern; i++ ) + { + const sal_uInt8* pTable = pImplTTFont->kerntables[i]; + + /*sal_uInt16 nVersion =*/ getUInt16BE( pTable ); + /*sal_uInt16 nLength =*/ getUInt16BE( pTable ); + sal_uInt16 nCoverage = getUInt16BE( pTable ); + + aPair.kern_x = 0; + aPair.kern_y = 0; + switch( nCoverage >> 8 ) + { + case 0: + { + sal_uInt16 nPairs = getUInt16BE( pTable ); + pTable += 6; + for( int n = 0; n < nPairs; n++ ) + { + sal_uInt16 nLeftGlyph = getUInt16BE( pTable ); + sal_uInt16 nRightGlyph = getUInt16BE( pTable ); + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable ); + + left = aGlyphMap.find( nLeftGlyph ); + right = aGlyphMap.find( nRightGlyph ); + if( left != aGlyphMap.end() && right != aGlyphMap.end() ) + { + aPair.first = left->second; + aPair.second = right->second; + switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } + } + } + } + break; + + case 2: + { + const sal_uInt8* pSubTable = pTable; + /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable ); + sal_uInt16 nOfLeft = getUInt16BE( pTable ); + sal_uInt16 nOfRight = getUInt16BE( pTable ); + /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable ); + const sal_uInt8* pTmp = pSubTable + nOfLeft; + sal_uInt16 nFirstLeft = getUInt16BE( pTmp ); + sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1; + pTmp = pSubTable + nOfRight; + sal_uInt16 nFirstRight = getUInt16BE( pTmp ); + sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1; + + // int nPairs = (int)(nLastLeft-nFirstLeft+1)*(int)(nLastRight-nFirstRight+1); + for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ ) + { + for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ ) + { + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp ); + switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } + } + } + } + break; + } + } + } + + //----------------------------------------------------------------- + // Kerning: KT_APPLE_NEW + //----------------------------------------------------------------- + if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_APPLE_NEW ) + { + // create a glyph -> character mapping + ::std::hash_map< sal_uInt16, sal_Unicode > aGlyphMap; + ::std::hash_map< sal_uInt16, sal_Unicode >::iterator left, right; + for( i = 21; i < 0xfffd; i++ ) + { + sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only + if( nGlyph != 0 ) + aGlyphMap[ nGlyph ] = (sal_Unicode)i; + } + + // Loop through each of the 'kern' subtables + KernPair aPair; + for( i = 0; (unsigned int)i < pImplTTFont->nkern; i++ ) + { + const sal_uInt8* pTable = pImplTTFont->kerntables[i]; + + /*sal_uInt32 nLength =*/ getUInt32BE( pTable ); + sal_uInt16 nCoverage = getUInt16BE( pTable ); + /*sal_uInt16 nTupleIndex =*/ getUInt16BE( pTable ); + + // Get kerning type + // sal_Bool bKernVertical = nCoverage & 0x8000; + // sal_Bool bKernCrossStream = nCoverage & 0x4000; + // sal_Bool bKernVariation = nCoverage & 0x2000; + + // Kerning sub-table format, 0 through 3 + sal_uInt8 nSubTableFormat = nCoverage & 0x00FF; + + aPair.kern_x = 0; + aPair.kern_y = 0; + switch( nSubTableFormat ) + { + case 0: + { + // Grab the # of kern pairs but skip over the: + // searchRange + // entrySelector + // rangeShift + sal_uInt16 nPairs = getUInt16BE( pTable ); + pTable += 6; + + for( int n = 0; n < nPairs; n++ ) + { + sal_uInt16 nLeftGlyph = getUInt16BE( pTable ); + sal_uInt16 nRightGlyph = getUInt16BE( pTable ); + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable ); + + left = aGlyphMap.find( nLeftGlyph ); + right = aGlyphMap.find( nRightGlyph ); + if( left != aGlyphMap.end() && right != aGlyphMap.end() ) + { + aPair.first = left->second; + aPair.second = right->second; + + // Only support horizontal kerning for now + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + aPair.kern_y = 0; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + +/* switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } +*/ + } + } + } + break; + + case 2: + { + const sal_uInt8* pSubTable = pTable; + /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable ); + sal_uInt16 nOfLeft = getUInt16BE( pTable ); + sal_uInt16 nOfRight = getUInt16BE( pTable ); + /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable ); + const sal_uInt8* pTmp = pSubTable + nOfLeft; + sal_uInt16 nFirstLeft = getUInt16BE( pTmp ); + sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1; + pTmp = pSubTable + nOfRight; + sal_uInt16 nFirstRight = getUInt16BE( pTmp ); + sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1; + + for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ ) + { + for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ ) + { + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp ); + switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } + } + } + } + break; + + default: + fprintf( stderr, "Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat ); + break; + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %d/%d kern pairs for %s\n", + m_pMetrics->m_aXKernPairs.size(), + m_pMetrics->m_aYKernPairs.size(), + OUStringToOString( pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ), RTL_TEXTENCODING_MS_1252 ).getStr() ); +#else + (void) pProvider; /* avoid warnings */ +#endif + } + + CloseTTFont( pTTFont ); + bSuccess = true; + } + return bSuccess; +} + +// ------------------------------------------------------------------------- + +/* #i73387# There seem to be fonts with a rather unwell chosen family name +* consider e.g. "Helvetica Narrow" which defines its family as "Helvetica" +* It can really only be distinguished by its PSName and FullName. Both of +* which are not user presentable in OOo. So replace it by something sensible. +* +* If other fonts feature this behaviour, insert them to the map. +*/ +static bool familyNameOverride( const OUString& i_rPSname, OUString& o_rFamilyName ) +{ + static std::hash_map< OUString, OUString, OUStringHash > aPSNameToFamily( 16 ); + if( aPSNameToFamily.empty() ) // initialization + { + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Bold" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-BoldOblique" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Oblique" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + } + std::hash_map<OUString,OUString,OUStringHash>::const_iterator it = + aPSNameToFamily.find( i_rPSname ); + bool bReplaced = (it != aPSNameToFamily.end() ); + if( bReplaced ) + o_rFamilyName = it->second; + return bReplaced; +}; + +bool PrintFontManager::PrintFont::readAfmMetrics( const OString& rFileName, MultiAtomProvider* pProvider, bool bFillEncodingvector, bool bOnlyGlobalAttributes ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + + int i; + FontInfo* pInfo = NULL; + parseFile( rFileName.getStr(), &pInfo, P_ALL ); + if( ! pInfo || ! pInfo->numOfChars ) + { + if( pInfo ) + freeFontInfo( pInfo ); + return false; + } + + m_aEncodingVector.clear(); + // fill in global info + + // PSName + OUString aPSName( OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ) ); + m_nPSName = pProvider->getAtom( ATOM_PSNAME, aPSName, sal_True ); + + // family name (if not already set) + OUString aFamily; + if( ! m_nFamilyName ) + { + aFamily = OStringToOUString( pInfo->gfi->familyName, RTL_TEXTENCODING_ISO_8859_1 ); + if( ! aFamily.getLength() ) + { + aFamily = OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ); + sal_Int32 nIndex = 0; + aFamily = aFamily.getToken( 0, '-', nIndex ); + } + familyNameOverride( aPSName, aFamily ); + m_nFamilyName = pProvider->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ); + } + else + aFamily = pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ); + + // style name: if fullname begins with family name + // interpret the rest of fullname as style + if( ! m_aStyleName.getLength() && pInfo->gfi->fullName && *pInfo->gfi->fullName ) + { + OUString aFullName( OStringToOUString( pInfo->gfi->fullName, RTL_TEXTENCODING_ISO_8859_1 ) ); + if( aFullName.indexOf( aFamily ) == 0 ) + m_aStyleName = WhitespaceToSpace( aFullName.copy( aFamily.getLength() ) ); + } + + // italic + if( pInfo->gfi->italicAngle > 0 ) + m_eItalic = italic::Oblique; + else if( pInfo->gfi->italicAngle < 0 ) + m_eItalic = italic::Italic; + else + m_eItalic = italic::Upright; + + // weight + ByteString aLowerWeight( pInfo->gfi->weight ); + aLowerWeight.ToLowerAscii(); + m_eWeight = parseWeight( aLowerWeight ); + + // pitch + m_ePitch = pInfo->gfi->isFixedPitch ? pitch::Fixed : pitch::Variable; + + // encoding - only set if unknown + int nAdobeEncoding = 0; + if( pInfo->gfi->encodingScheme ) + { + if( !strcmp( pInfo->gfi->encodingScheme, "AdobeStandardEncoding" ) ) + nAdobeEncoding = 1; + else if( !strcmp( pInfo->gfi->encodingScheme, "ISO10646-1" ) ) + { + nAdobeEncoding = 1; + m_aEncoding = RTL_TEXTENCODING_UNICODE; + } + else if( !strcmp( pInfo->gfi->encodingScheme, "Symbol") ) + nAdobeEncoding = 2; + else if( !strcmp( pInfo->gfi->encodingScheme, "FontSpecific") ) + nAdobeEncoding = 3; + + if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) + m_aEncoding = nAdobeEncoding == 1 ? + RTL_TEXTENCODING_ADOBE_STANDARD : RTL_TEXTENCODING_SYMBOL; + } + else if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) + m_aEncoding = RTL_TEXTENCODING_ADOBE_STANDARD; + + // try to parse the font name and decide wether it might be a + // japanese font. Who invented this PITA ? + OUString aPSNameLastToken( aPSName.copy( aPSName.lastIndexOf( '-' )+1 ) ); + if( ! aPSNameLastToken.compareToAscii( "H" ) || + ! aPSNameLastToken.compareToAscii( "V" ) ) + { + static const char* pEncs[] = + { + "EUC", + "RKSJ", + "SJ" + }; + static const rtl_TextEncoding aEncs[] = + { + RTL_TEXTENCODING_EUC_JP, + RTL_TEXTENCODING_SHIFT_JIS, + RTL_TEXTENCODING_JIS_X_0208 + }; + + for( unsigned int enc = 0; enc < sizeof( aEncs )/sizeof(aEncs[0]) && m_aEncoding == RTL_TEXTENCODING_DONTKNOW; enc++ ) + { + sal_Int32 nIndex = 0, nOffset = 1; + do + { + OUString aToken( aPSName.getToken( nOffset, '-', nIndex ) ); + if( nIndex == -1 ) + break; + nOffset = 0; + if( ! aToken.compareToAscii( pEncs[enc] ) ) + { + m_aEncoding = aEncs[ enc ]; + m_bFontEncodingOnly = true; + } + } while( nIndex != -1 ); + } + + // default is jis + if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) + m_aEncoding = RTL_TEXTENCODING_JIS_X_0208; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Encoding %d for %s\n", m_aEncoding, pInfo->gfi->fontName ); +#endif + } + + // hack for GB encoded builtin fonts posing as FontSpecific + if( m_eType == fonttype::Builtin && ( nAdobeEncoding == 3 || nAdobeEncoding == 0 ) ) + { + int nLen = aFamily.getLength(); + if( nLen > 2 && + aFamily.getStr()[ nLen-2 ] == 'G' && + aFamily.getStr()[ nLen-1 ] == 'B' && + pInfo->numOfChars > 255 ) + { + m_aEncoding = RTL_TEXTENCODING_GBK; + m_bFontEncodingOnly = true; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found builtin font %s with GBK encoding\n", pInfo->gfi->fontName ); +#endif + } + } + + // #i37313# check if Fontspecific is not rather some character encoding + if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL ) + { + bool bYFound = false; + bool bQFound = false; + CharMetricInfo* pChar = pInfo->cmi; + for( int j = 0; j < pInfo->numOfChars && ! (bYFound && bQFound); j++ ) + { + if( pChar[j].name ) + { + if( pChar[j].name[0] == 'Y' && pChar[j].name[1] == 0 ) + bYFound = true; + else if( pChar[j].name[0] == 'Q' && pChar[j].name[1] == 0 ) + bQFound = true; + } + } + if( bQFound && bYFound ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "setting FontSpecific font %s (file %s) to unicode\n", + pInfo->gfi->fontName, + rFileName.getStr() + ); + #endif + nAdobeEncoding = 4; + m_aEncoding = RTL_TEXTENCODING_UNICODE; + bFillEncodingvector = false; // will be filled anyway, don't do the work twice + } + } + + // ascend + m_nAscend = pInfo->gfi->fontBBox.ury; + + // descend + // descends have opposite sign of our definition + m_nDescend = -pInfo->gfi->fontBBox.lly; + + // fallback to ascender, descender + // interesting: the BBox seems to describe Ascender and Descender better + // as we understand it + if( m_nAscend == 0 ) + m_nAscend = pInfo->gfi->ascender; + if( m_nDescend == 0) + m_nDescend = -pInfo->gfi->descender; + + m_nLeading = m_nAscend + m_nDescend - 1000; + + if( m_pMetrics ) + delete m_pMetrics; + m_pMetrics = new PrintFontMetrics; + // mark all pages as queried (or clear if only global font info queiried) + memset( m_pMetrics->m_aPages, bOnlyGlobalAttributes ? 0 : 0xff, sizeof( m_pMetrics->m_aPages ) ); + + m_aGlobalMetricX.width = m_aGlobalMetricY.width = + pInfo->gfi->charwidth ? pInfo->gfi->charwidth : pInfo->gfi->fontBBox.urx; + m_aGlobalMetricX.height = m_aGlobalMetricY.height = + pInfo->gfi->capHeight ? pInfo->gfi->capHeight : pInfo->gfi->fontBBox.ury; + + m_nXMin = pInfo->gfi->fontBBox.llx; + m_nYMin = pInfo->gfi->fontBBox.lly; + m_nXMax = pInfo->gfi->fontBBox.urx; + m_nYMax = pInfo->gfi->fontBBox.ury; + + if( bFillEncodingvector || !bOnlyGlobalAttributes ) + { + // fill in character metrics + + // first transform the character codes to unicode + // note: this only works with single byte encodings + sal_Unicode* pUnicodes = (sal_Unicode*)alloca( pInfo->numOfChars * sizeof(sal_Unicode)); + CharMetricInfo* pChar = pInfo->cmi; + + for( i = 0; i < pInfo->numOfChars; i++, pChar++ ) + { + if( nAdobeEncoding == 4 ) + { + if( pChar->name ) + { + pUnicodes[i] = 0; + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + { + m_aEncodingVector[ *it ] = pChar->code; + if( pChar->code == -1 ) + m_aNonEncoded[ *it ] = pChar->name; + if( ! pUnicodes[i] ) // map the first + pUnicodes[i] = *it; + } + } + } + } + else if( pChar->code != -1 ) + { + if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL ) + { + pUnicodes[i] = pChar->code + 0xf000; + if( bFillEncodingvector ) + m_aEncodingVector[ pUnicodes[i] ] = pChar->code; + continue; + } + + if( m_aEncoding == RTL_TEXTENCODING_UNICODE ) + { + pUnicodes[i] = (sal_Unicode)pChar->code; + continue; + } + + ByteString aTranslate; + if( pChar->code & 0xff000000 ) + aTranslate += (char)(pChar->code >> 24 ); + if( pChar->code & 0xffff0000 ) + aTranslate += (char)((pChar->code & 0x00ff0000) >> 16 ); + if( pChar->code & 0xffffff00 ) + aTranslate += (char)((pChar->code & 0x0000ff00) >> 8 ); + aTranslate += (char)(pChar->code & 0xff); + String aUni( aTranslate, m_aEncoding ); + pUnicodes[i] = *aUni.GetBuffer(); + } + else + pUnicodes[i] = 0; + } + + // now fill in the character metrics + // parseAFM.cxx effectively only supports direction 0 (horizontal) + pChar = pInfo->cmi; + CharacterMetric aMetric; + for( i = 0; i < pInfo->numOfChars; i++, pChar++ ) + { + if( pChar->code == -1 && ! pChar->name ) + continue; + + if( bFillEncodingvector && pChar->name ) + { + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + { + m_aEncodingVector[ *it ] = pChar->code; + if( pChar->code == -1 ) + m_aNonEncoded[ *it ] = pChar->name; + } + } + } + + aMetric.width = pChar->wx ? pChar->wx : pChar->charBBox.urx; + aMetric.height = pChar->wy ? pChar->wy : pChar->charBBox.ury - pChar->charBBox.lly; + if( aMetric.width == 0 && aMetric.height == 0 ) + // guess something for e.g. space + aMetric.width = m_aGlobalMetricX.width/4; + + if( ( nAdobeEncoding == 0 ) || + ( ( nAdobeEncoding == 3 ) && ( m_aEncoding != RTL_TEXTENCODING_SYMBOL ) ) ) + { + if( pChar->code != -1 ) + { + m_pMetrics->m_aMetrics[ pUnicodes[i] ] = aMetric; + if( bFillEncodingvector ) + m_aEncodingVector[ pUnicodes[i] ] = pChar->code; + } + else if( pChar->name ) + { + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + m_pMetrics->m_aMetrics[ *it ] = aMetric; + } + } + } + else if( nAdobeEncoding == 1 || nAdobeEncoding == 2 || nAdobeEncoding == 4) + { + if( pChar->name ) + { + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + m_pMetrics->m_aMetrics[ *it ] = aMetric; + } + } + else if( pChar->code != -1 ) + { + ::std::pair< ::std::hash_multimap< sal_uInt8, sal_Unicode >::const_iterator, + ::std::hash_multimap< sal_uInt8, sal_Unicode >::const_iterator > + aCodes = rManager.getUnicodeFromAdobeCode( pChar->code ); + while( aCodes.first != aCodes.second ) + { + if( (*aCodes.first).second != 0 ) + { + m_pMetrics->m_aMetrics[ (*aCodes.first).second ] = aMetric; + if( bFillEncodingvector ) + m_aEncodingVector[ (*aCodes.first).second ] = pChar->code; + } + ++aCodes.first; + } + } + } + else if( nAdobeEncoding == 3 ) + { + if( pChar->code != -1 ) + { + sal_Unicode code = 0xf000 + pChar->code; + m_pMetrics->m_aMetrics[ code ] = aMetric; + // maybe should try to find the name in the convtabs ? + if( bFillEncodingvector ) + m_aEncodingVector[ code ] = pChar->code; + } + } + } + + m_pMetrics->m_aXKernPairs.clear(); + m_pMetrics->m_aYKernPairs.clear(); + + // now fill in the kern pairs + // parseAFM.cxx effectively only supports direction 0 (horizontal) + PairKernData* pKern = pInfo->pkd; + KernPair aPair; + for( i = 0; i < pInfo->numOfPairs; i++, pKern++ ) + { + // #i37703# broken kern table + if( ! pKern->name1 || ! pKern->name2 ) + continue; + + aPair.first = 0; + aPair.second = 0; + // currently we have to find the adobe character names + // in the already parsed character metrics to find + // the corresponding UCS2 code which is a bit dangerous + // since the character names are not required + // in the metric descriptions + pChar = pInfo->cmi; + for( int j = 0; + j < pInfo->numOfChars && ( aPair.first == 0 || aPair.second == 0 ); + j++, pChar++ ) + { + if( pChar->code != -1 ) + { + if( ! strcmp( pKern->name1, pChar->name ? pChar->name : "" ) ) + aPair.first = pUnicodes[ j ]; + if( ! strcmp( pKern->name2, pChar->name ? pChar->name : "" ) ) + aPair.second = pUnicodes[ j ]; + } + } + if( aPair.first && aPair.second ) + { + aPair.kern_x = pKern->xamt; + aPair.kern_y = pKern->yamt; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + } + } + m_pMetrics->m_bKernPairsQueried = true; + } + + freeFontInfo( pInfo ); + return true; +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::s_aEmptyOString; + +/* + * one instance only + */ +PrintFontManager& PrintFontManager::get() +{ + static PrintFontManager* theManager = NULL; + if( ! theManager ) + { + theManager = new PrintFontManager(); + theManager->initialize(); + } + return *theManager; +} + +// ------------------------------------------------------------------------- + +/* + * the PrintFontManager + */ + +PrintFontManager::PrintFontManager() : + m_nNextFontID( 1 ), + m_pAtoms( new MultiAtomProvider() ), + m_nNextDirAtom( 1 ), + m_pFontCache( NULL ), + m_bFontconfigSuccess( false ) +{ + for( unsigned int i = 0; i < sizeof( aAdobeCodes )/sizeof( aAdobeCodes[0] ); i++ ) + { + m_aUnicodeToAdobename.insert( ::std::hash_multimap< sal_Unicode, ::rtl::OString >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].pAdobename ) ); + m_aAdobenameToUnicode.insert( ::std::hash_multimap< ::rtl::OString, sal_Unicode, ::rtl::OStringHash >::value_type( aAdobeCodes[i].pAdobename, aAdobeCodes[i].aUnicode ) ); + if( aAdobeCodes[i].aAdobeStandardCode ) + { + m_aUnicodeToAdobecode.insert( ::std::hash_multimap< sal_Unicode, sal_uInt8 >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].aAdobeStandardCode ) ); + m_aAdobecodeToUnicode.insert( ::std::hash_multimap< sal_uInt8, sal_Unicode >::value_type( aAdobeCodes[i].aAdobeStandardCode, aAdobeCodes[i].aUnicode ) ); + } +#if 0 + m_aUnicodeToAdobename[ aAdobeCodes[i].aUnicode ] = aAdobeCodes[i].pAdobename; + m_aAdobenameToUnicode[ aAdobeCodes[i].pAdobename ] = aAdobeCodes[i].aUnicode; + if( aAdobeCodes[i].aAdobeStandardCode ) + { + m_aUnicodeToAdobecode[ aAdobeCodes[i].aUnicode ] = aAdobeCodes[i].aAdobeStandardCode; + m_aAdobecodeToUnicode[ aAdobeCodes[i].aAdobeStandardCode ] = aAdobeCodes[i].aUnicode; + } +#endif + } +} + +// ------------------------------------------------------------------------- + +PrintFontManager::~PrintFontManager() +{ + deinitFontconfig(); + for( ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + delete (*it).second; + delete m_pAtoms; + if( m_pFontCache ) + delete m_pFontCache; +} + +// ------------------------------------------------------------------------- + +const OString& PrintFontManager::getDirectory( int nAtom ) const +{ + ::std::hash_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) ); + return it != m_aAtomToDir.end() ? it->second : s_aEmptyOString; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getDirectoryAtom( const OString& rDirectory, bool bCreate ) +{ + int nAtom = 0; + ::std::hash_map< OString, int, OStringHash >::const_iterator it + ( m_aDirToAtom.find( rDirectory ) ); + if( it != m_aDirToAtom.end() ) + nAtom = it->second; + else if( bCreate ) + { + nAtom = m_nNextDirAtom++; + m_aDirToAtom[ rDirectory ] = nAtom; + m_aAtomToDir[ nAtom ] = rDirectory; + } + return nAtom; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::addFontFile( const ::rtl::OString& rFileName, int /*nFaceNum*/ ) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + INetURLObject aPath( OStringToOUString( rFileName, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + OString aName( OUStringToOString( aPath.GetName(), aEncoding ) ); + OString aDir( OUStringToOString( aPath.GetPath(), aEncoding ) ); + + int nDirID = getDirectoryAtom( aDir, true ); + fontID nFontId = findFontFileID( nDirID, aName ); + if( !nFontId ) + { + ::std::list< PrintFont* > aNewFonts; + if( analyzeFontFile( nDirID, aName, ::std::list<OString>(), aNewFonts ) ) + { + for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); + it != aNewFonts.end(); ++it ) + { + m_aFonts[ nFontId = m_nNextFontID++ ] = *it; + m_aFontFileToFontID[ aName ].insert( nFontId ); + m_pFontCache->updateFontCacheEntry( *it, true ); + } + } + } + return nFontId; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, const ::std::list<OString>& rXLFDs, ::std::list< PrintFontManager::PrintFont* >& rNewFonts ) const +{ + rNewFonts.clear(); + + OString aDir( getDirectory( nDirID ) ); + + OString aFullPath( aDir ); + aFullPath += "/"; + aFullPath += rFontFile; + + // #i1872# reject unreadable files + if( access( aFullPath.getStr(), R_OK ) ) + return false; + + ByteString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) ); + if( aExt.EqualsIgnoreCaseAscii( "pfb" ) || aExt.EqualsIgnoreCaseAscii( "pfa" ) ) + { + // check for corresponding afm metric + // first look for an adjacent file + static const char* pSuffix[] = { ".afm", ".AFM" }; + + for( unsigned int i = 0; i < sizeof(pSuffix)/sizeof(pSuffix[0]); i++ ) + { + ByteString aName( rFontFile ); + aName.Erase( aName.Len()-4 ); + aName.Append( pSuffix[i] ); + + ByteString aFilePath( aDir ); + aFilePath.Append( '/' ); + aFilePath.Append( aName ); + + ByteString aAfmFile; + if( access( aFilePath.GetBuffer(), R_OK ) ) + { + // try in subdirectory afm instead + aFilePath = aDir; + aFilePath.Append( "/afm/" ); + aFilePath.Append( aName ); + + if( ! access( aFilePath.GetBuffer(), R_OK ) ) + { + aAfmFile = "afm/"; + aAfmFile += aName; + } + } + else + aAfmFile = aName; + + if( aAfmFile.Len() ) + { + Type1FontFile* pFont = new Type1FontFile(); + pFont->m_nDirectory = nDirID; + + pFont->m_aFontFile = rFontFile; + pFont->m_aMetricFile = aAfmFile; + + if( ! pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ) ) + { + delete pFont; + pFont = NULL; + } + if( pFont && rXLFDs.size() ) + getFontAttributesFromXLFD( pFont, rXLFDs ); + if( pFont ) + rNewFonts.push_back( pFont ); + break; + } + } + } + else if( aExt.EqualsIgnoreCaseAscii( "afm" ) ) + { + ByteString aFilePath( aDir ); + aFilePath.Append( '/' ); + aFilePath.Append( ByteString( rFontFile ) ); + BuiltinFont* pFont = new BuiltinFont(); + pFont->m_nDirectory = nDirID; + pFont->m_aMetricFile = rFontFile; + if( pFont->readAfmMetrics( aFilePath, m_pAtoms, false, true ) ) + rNewFonts.push_back( pFont ); + else + delete pFont; + } + else if( aExt.EqualsIgnoreCaseAscii( "ttf" ) + || aExt.EqualsIgnoreCaseAscii( "tte" ) // #i33947# for Gaiji support + || aExt.EqualsIgnoreCaseAscii( "otf" ) ) // #112957# allow GLYF-OTF + { + TrueTypeFontFile* pFont = new TrueTypeFontFile(); + pFont->m_nDirectory = nDirID; + pFont->m_aFontFile = rFontFile; + pFont->m_nCollectionEntry = -1; + + if( rXLFDs.size() ) + getFontAttributesFromXLFD( pFont, rXLFDs ); + // need to read the font anyway to get aliases inside the font file + if( ! analyzeTrueTypeFile( pFont ) ) + { + delete pFont; + pFont = NULL; + } + else + rNewFonts.push_back( pFont ); + } + else if( aExt.EqualsIgnoreCaseAscii( "ttc" ) ) + { + // get number of ttc entries + int nLength = CountTTCFonts( aFullPath.getStr() ); + if( nLength ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s contains %d fonts\n", aFullPath.getStr(), nLength ); +#endif + for( int i = 0; i < nLength; i++ ) + { + TrueTypeFontFile* pFont = new TrueTypeFontFile(); + pFont->m_nDirectory = nDirID; + pFont->m_aFontFile = rFontFile; + pFont->m_nCollectionEntry = i; + if( nLength == 1 ) + getFontAttributesFromXLFD( pFont, rXLFDs ); + if( ! analyzeTrueTypeFile( pFont ) ) + { + delete pFont; + pFont = NULL; + } + else + rNewFonts.push_back( pFont ); + } + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "CountTTCFonts( \"%s/%s\" ) failed\n", getDirectory(nDirID).getStr(), rFontFile.getStr() ); +#endif + } + return ! rNewFonts.empty(); +} + +// ------------------------------------------------------------------------- + +fontID PrintFontManager::findFontBuiltinID( int nPSNameAtom ) const +{ + fontID nID = 0; + ::std::hash_map< fontID, PrintFont* >::const_iterator it; + for( it = m_aFonts.begin(); nID == 0 && it != m_aFonts.end(); ++it ) + { + if( it->second->m_eType == fonttype::Builtin && + it->second->m_nPSName == nPSNameAtom ) + nID = it->first; + } + return nID; +} + +// ------------------------------------------------------------------------- + +fontID PrintFontManager::findFontFileID( int nDirID, const OString& rFontFile ) const +{ + fontID nID = 0; + + ::std::hash_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile ); + if( set_it != m_aFontFileToFontID.end() ) + { + for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end() && ! nID; ++font_it ) + { + ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it ); + if( it != m_aFonts.end() ) + { + switch( it->second->m_eType ) + { + case fonttype::Type1: + { + Type1FontFile* const pFont = static_cast< Type1FontFile* const >((*it).second); + if( pFont->m_nDirectory == nDirID && + pFont->m_aFontFile == rFontFile ) + nID = it->first; + } + break; + case fonttype::TrueType: + { + TrueTypeFontFile* const pFont = static_cast< TrueTypeFontFile* const >((*it).second); + if( pFont->m_nDirectory == nDirID && + pFont->m_aFontFile == rFontFile ) + nID = it->first; + } + break; + case fonttype::Builtin: + if( static_cast<const BuiltinFont*>((*it).second)->m_nDirectory == nDirID && + static_cast<const BuiltinFont*>((*it).second)->m_aMetricFile == rFontFile ) + nID = it->first; + break; + default: + break; + } + } + } + } + return nID; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::parseXLFD( const OString& rXLFD, XLFDEntry& rEntry ) +{ + sal_Int32 nIndex = 0; + OString aFoundry = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ) ); + if( nIndex < 0 ) return false; + OString aFamilyXLFD = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ) ); + if( nIndex < 0 ) return false; + OString aWeight = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aSlant = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aWidth = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aAddStyle = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aPitch = rXLFD.getToken( 4, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aRegEnc = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ).toAsciiLowerCase() ); + if( nIndex < 0 ) return false; + OString aEnc = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase() ); + + // capitalize words + sal_Int32 nFamIndex = 0; + OStringBuffer aFamilyName; + while( nFamIndex >= 0 ) + { + OString aToken = aFamilyXLFD.getToken( 0, ' ', nFamIndex ); + sal_Char aFirst = aToken.toChar(); + if( aFirst >= 'a' && aFirst <= 'z' ) + aFirst = aFirst - 'a' + 'A'; + OStringBuffer aNewToken( aToken.getLength() ); + aNewToken.append( aToken ); + aNewToken.setCharAt( 0, aFirst ); + if( aFamilyName.getLength() > 0 ) + aFamilyName.append( ' ' ); + aFamilyName.append( aNewToken.makeStringAndClear() ); + } + + rEntry.aFoundry = aFoundry; + rEntry.aFamily = aFamilyName.makeStringAndClear(); + rEntry.aAddStyle = aAddStyle; + // evaluate weight + rEntry.eWeight = parseWeight( aWeight ); + // evaluate slant + rEntry.eItalic = parseItalic( aSlant ); + // evaluate width + rEntry.eWidth = parseWidth( aWidth ); + + // evaluate pitch + if( aPitch.toChar() == 'c' || aPitch.toChar() == 'm' ) + rEntry.ePitch = pitch::Fixed; + else + rEntry.ePitch = pitch::Variable; + + OString aToken = aEnc.toAsciiLowerCase(); + // get encoding + if( aAddStyle.indexOf( "symbol" ) != -1 ) + rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL; + else + { + if( aToken.equals( "symbol" ) ) + rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL; + else + { + OStringBuffer aCharset( aRegEnc.getLength() + aEnc.getLength() + 1 ); + aCharset.append( aRegEnc ); + aCharset.append( '-' ); + aCharset.append( aEnc ); + rEntry.aEncoding = rtl_getTextEncodingFromUnixCharset( aCharset.getStr() ); + } + } + + // set correct mask flags + rEntry.nMask = 0; + if( rEntry.aFoundry != "*" ) rEntry.nMask |= XLFDEntry::MaskFoundry; + if( rEntry.aFamily != "*" ) rEntry.nMask |= XLFDEntry::MaskFamily; + if( rEntry.aAddStyle != "*" ) rEntry.nMask |= XLFDEntry::MaskAddStyle; + if( aWeight != "*" ) rEntry.nMask |= XLFDEntry::MaskWeight; + if( aSlant != "*" ) rEntry.nMask |= XLFDEntry::MaskItalic; + if( aWidth != "*" ) rEntry.nMask |= XLFDEntry::MaskWidth; + if( aPitch != "*" ) rEntry.nMask |= XLFDEntry::MaskPitch; + if( aRegEnc != "*" && aEnc != "*" ) rEntry.nMask |= XLFDEntry::MaskEncoding; + + return true; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::parseXLFD_appendAliases( const std::list< OString >& rXLFDs, std::list< XLFDEntry >& rEntries ) const +{ + for( std::list< OString >::const_iterator it = rXLFDs.begin(); it != rXLFDs.end(); ++it ) + { + XLFDEntry aEntry; + if( ! parseXLFD(*it, aEntry) ) + continue; + rEntries.push_back( aEntry ); + std::map< XLFDEntry, std::list< XLFDEntry > >::const_iterator alias_it = + m_aXLFD_Aliases.find( aEntry ); + if( alias_it != m_aXLFD_Aliases.end() ) + { + rEntries.insert( rEntries.end(), alias_it->second.begin(), alias_it->second.end() ); + } + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::getFontAttributesFromXLFD( PrintFont* pFont, const std::list< OString >& rXLFDs ) const +{ + bool bFamilyName = false; + + std::list< XLFDEntry > aXLFDs; + + parseXLFD_appendAliases( rXLFDs, aXLFDs ); + + for( std::list< XLFDEntry >::const_iterator it = aXLFDs.begin(); + it != aXLFDs.end(); ++it ) + { + // set family name or alias + int nFam = + m_pAtoms->getAtom( ATOM_FAMILYNAME, + OStringToOUString( it->aFamily, it->aAddStyle.indexOf( "utf8" ) != -1 ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1 ), + sal_True ); + if( ! bFamilyName ) + { + bFamilyName = true; + pFont->m_nFamilyName = nFam; + switch( pFont->m_eType ) + { + case fonttype::Type1: + static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + case fonttype::TrueType: + static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + default: + break; + } + } + else + { + // make sure that aliases are unique + if( nFam != pFont->m_nFamilyName ) + { + std::list< int >::const_iterator al_it; + for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nFam; ++al_it ) + ; + if( al_it == pFont->m_aAliases.end() ) + pFont->m_aAliases.push_back( nFam ); + + } + // for the rest of the attributes there can only be one value; + // we'll trust the first one + continue; + } + + // fill in weight + pFont->m_eWeight = it->eWeight; + // fill in slant + pFont->m_eItalic = it->eItalic; + // fill in width + pFont->m_eWidth = it->eWidth; + // fill in pitch + pFont->m_ePitch = it->ePitch; + // fill in encoding + pFont->m_aEncoding = it->aEncoding; + } + + // handle iso8859-1 as ms1252 to fill the "gap" starting at 0x80 + if( pFont->m_aEncoding == RTL_TEXTENCODING_ISO_8859_1 ) + pFont->m_aEncoding = RTL_TEXTENCODING_MS_1252; + if( rXLFDs.begin() != rXLFDs.end() ) + { + switch( pFont->m_eType ) + { + case fonttype::Type1: + static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + case fonttype::TrueType: + static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + default: break; + } + } +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::getXLFD( PrintFont* pFont ) const +{ + if( pFont->m_eType == fonttype::Type1 ) + { + if( static_cast<Type1FontFile*>(pFont)->m_aXLFD.getLength() ) + return static_cast<Type1FontFile*>(pFont)->m_aXLFD; + } + if( pFont->m_eType == fonttype::TrueType ) + { + if( static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD.getLength() ) + return static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD; + } + + OStringBuffer aXLFD( 128 ); + + aXLFD.append( "-misc-" ); + ByteString aFamily( String( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ) ), RTL_TEXTENCODING_UTF8 ); + aFamily.SearchAndReplaceAll( '-',' ' ); + aFamily.SearchAndReplaceAll( '?',' ' ); + aFamily.SearchAndReplaceAll( '*',' ' ); + aXLFD.append( OString( aFamily ) ); + aXLFD.append( '-' ); + switch( pFont->m_eWeight ) + { + case weight::Thin: aXLFD.append("thin");break; + case weight::UltraLight: aXLFD.append("ultralight");break; + case weight::Light: aXLFD.append("light");break; + case weight::SemiLight: aXLFD.append("semilight");break; + case weight::Normal: aXLFD.append("normal");break; + case weight::Medium: aXLFD.append("medium");break; + case weight::SemiBold: aXLFD.append("semibold");break; + case weight::Bold: aXLFD.append("bold");break; + case weight::UltraBold: aXLFD.append("ultrabold");break; + case weight::Black: aXLFD.append("black");break; + default: break; + } + aXLFD.append('-'); + switch( pFont->m_eItalic ) + { + case italic::Upright: aXLFD.append('r');break; + case italic::Oblique: aXLFD.append('o');break; + case italic::Italic: aXLFD.append('i');break; + default: break; + } + aXLFD.append('-'); + switch( pFont->m_eWidth ) + { + case width::UltraCondensed: aXLFD.append("ultracondensed");break; + case width::ExtraCondensed: aXLFD.append("extracondensed");break; + case width::Condensed: aXLFD.append("condensed");break; + case width::SemiCondensed: aXLFD.append("semicondensed");break; + case width::Normal: aXLFD.append("normal");break; + case width::SemiExpanded: aXLFD.append("semiexpanded");break; + case width::Expanded: aXLFD.append("expanded");break; + case width::ExtraExpanded: aXLFD.append("extraexpanded");break; + case width::UltraExpanded: aXLFD.append("ultraexpanded");break; + default: break; + } + aXLFD.append("-utf8-0-0-0-0-"); + aXLFD.append( pFont->m_ePitch == pitch::Fixed ? "m" : "p" ); + aXLFD.append("-0-"); + const char* pEnc = rtl_getBestUnixCharsetFromTextEncoding( pFont->m_aEncoding ); + if( ! pEnc ) + { + if( pFont->m_aEncoding == RTL_TEXTENCODING_ADOBE_STANDARD ) + pEnc = "adobe-standard"; + else + pEnc = "iso8859-1"; + } + aXLFD .append( pEnc ); + + return aXLFD.makeStringAndClear(); +} + +// ------------------------------------------------------------------------- + +OUString PrintFontManager::convertTrueTypeName( void* pRecord ) const +{ + NameRecord* pNameRecord = (NameRecord*)pRecord; + OUString aValue; + if( + ( pNameRecord->platformID == 3 && ( pNameRecord->encodingID == 0 || pNameRecord->encodingID == 1 ) ) // MS, Unicode + || + ( pNameRecord->platformID == 0 ) // Apple, Unicode + ) + { + OUStringBuffer aName( pNameRecord->slen/2 ); + const sal_uInt8* pNameBuffer = pNameRecord->sptr; + for(int n = 0; n < pNameRecord->slen/2; n++ ) + aName.append( (sal_Unicode)getUInt16BE( pNameBuffer ) ); + aValue = aName.makeStringAndClear(); + } + else if( pNameRecord->platformID == 3 ) + { + if( pNameRecord->encodingID >= 2 && pNameRecord->encodingID <= 6 ) + { + /* + * and now for a special kind of madness: + * some fonts encode their byte value string as BE uint16 + * (leading to stray zero bytes in the string) + * while others code two bytes as a uint16 and swap to BE + */ + OStringBuffer aName; + const sal_uInt8* pNameBuffer = pNameRecord->sptr; + for(int n = 0; n < pNameRecord->slen/2; n++ ) + { + sal_Unicode aCode = (sal_Unicode)getUInt16BE( pNameBuffer ); + sal_Char aChar = aCode >> 8; + if( aChar ) + aName.append( aChar ); + aChar = aCode & 0x00ff; + if( aChar ) + aName.append( aChar ); + } + switch( pNameRecord->encodingID ) + { + case 2: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_932 ); + break; + case 3: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_936 ); + break; + case 4: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_950 ); + break; + case 5: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_949 ); + break; + case 6: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_1361 ); + break; + } + } + } + return aValue; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::analyzeTrueTypeFamilyName( void* pTTFont, ::std::list< OUString >& rNames ) const +{ + OUString aFamily; + + rNames.clear(); + ::std::set< OUString > aSet; + + NameRecord* pNameRecords = NULL; + int nNameRecords = GetTTNameRecords( (TrueTypeFont*)pTTFont, &pNameRecords ); + if( nNameRecords && pNameRecords ) + { + LanguageType aLang = MsLangId::getSystemLanguage(); + int nLastMatch = -1; + for( int i = 0; i < nNameRecords; i++ ) + { + if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == NULL ) + continue; + int nMatch = -1; + if( pNameRecords[i].platformID == 0 ) // Unicode + nMatch = 4000; + else if( pNameRecords[i].platformID == 3 ) + { + // this bases on the LanguageType actually being a Win LCID + if( pNameRecords[i].languageID == aLang ) + nMatch = 8000; + else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH_US ) + nMatch = 2000; + else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH || + pNameRecords[i].languageID == LANGUAGE_ENGLISH_UK ) + nMatch = 1500; + else + nMatch = 1000; + } + OUString aName = convertTrueTypeName( pNameRecords + i ); + aSet.insert( aName ); + if( nMatch > nLastMatch ) + { + nLastMatch = nMatch; + aFamily = aName; + } + } + DisposeNameRecords( pNameRecords, nNameRecords ); + } + if( aFamily.getLength() ) + { + rNames.push_front( aFamily ); + for( ::std::set< OUString >::const_iterator it = aSet.begin(); it != aSet.end(); ++it ) + if( *it != aFamily ) + rNames.push_back( *it ); + } + return; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::analyzeTrueTypeFile( PrintFont* pFont ) const +{ + bool bSuccess = false; + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + ByteString aFile = getFontFile( pFont ); + TrueTypeFont* pTTFont = NULL; + + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + TTGlobalFontInfo aInfo; + GetTTGlobalFontInfo( pTTFont, & aInfo ); + + ::std::list< OUString > aNames; + analyzeTrueTypeFamilyName( pTTFont, aNames ); + + // set family name from XLFD if possible + if( ! pFont->m_nFamilyName ) + { + if( aNames.begin() != aNames.end() ) + { + pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, aNames.front(), sal_True ); + aNames.pop_front(); + } + else + { + sal_Int32 dotIndex; + + // poor font does not have a family name + // name it to file name minus the extension + dotIndex = pTTFontFile->m_aFontFile.lastIndexOf( '.' ); + if ( dotIndex == -1 ) + dotIndex = pTTFontFile->m_aFontFile.getLength(); + + pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( pTTFontFile->m_aFontFile.copy( 0, dotIndex ), aEncoding ), sal_True ); + } + } + for( ::std::list< OUString >::iterator it = aNames.begin(); it != aNames.end(); ++it ) + { + if( it->getLength() ) + { + int nAlias = m_pAtoms->getAtom( ATOM_FAMILYNAME, *it, sal_True ); + if( nAlias != pFont->m_nFamilyName ) + { + std::list< int >::const_iterator al_it; + for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nAlias; ++al_it ) + ; + if( al_it == pFont->m_aAliases.end() ) + pFont->m_aAliases.push_back( nAlias ); + } + } + } + + if( aInfo.usubfamily ) + pFont->m_aStyleName = OUString( aInfo.usubfamily ); + + pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, String( ByteString( aInfo.psname ), aEncoding ), sal_True ); + switch( aInfo.weight ) + { + case FW_THIN: pFont->m_eWeight = weight::Thin; break; + case FW_EXTRALIGHT: pFont->m_eWeight = weight::UltraLight; break; + case FW_LIGHT: pFont->m_eWeight = weight::Light; break; + case FW_MEDIUM: pFont->m_eWeight = weight::Medium; break; + case FW_SEMIBOLD: pFont->m_eWeight = weight::SemiBold; break; + case FW_BOLD: pFont->m_eWeight = weight::Bold; break; + case FW_EXTRABOLD: pFont->m_eWeight = weight::UltraBold; break; + case FW_BLACK: pFont->m_eWeight = weight::Black; break; + + case FW_NORMAL: + default: pFont->m_eWeight = weight::Normal; break; + } + + switch( aInfo.width ) + { + case FWIDTH_ULTRA_CONDENSED: pFont->m_eWidth = width::UltraCondensed; break; + case FWIDTH_EXTRA_CONDENSED: pFont->m_eWidth = width::ExtraCondensed; break; + case FWIDTH_CONDENSED: pFont->m_eWidth = width::Condensed; break; + case FWIDTH_SEMI_CONDENSED: pFont->m_eWidth = width::SemiCondensed; break; + case FWIDTH_SEMI_EXPANDED: pFont->m_eWidth = width::SemiExpanded; break; + case FWIDTH_EXPANDED: pFont->m_eWidth = width::Expanded; break; + case FWIDTH_EXTRA_EXPANDED: pFont->m_eWidth = width::ExtraExpanded; break; + case FWIDTH_ULTRA_EXPANDED: pFont->m_eWidth = width::UltraExpanded; break; + + case FWIDTH_NORMAL: + default: pFont->m_eWidth = width::Normal; break; + } + + pFont->m_ePitch = aInfo.pitch ? pitch::Fixed : pitch::Variable; + pFont->m_eItalic = aInfo.italicAngle == 0 ? italic::Upright : ( aInfo.italicAngle < 0 ? italic::Italic : italic::Oblique ); + // #104264# there are fonts that set italic angle 0 although they are + // italic; use macstyle bit here + if( aInfo.italicAngle == 0 && (aInfo.macStyle & 2) ) + pFont->m_eItalic = italic::Italic; + + pFont->m_aEncoding = aInfo.symbolEncoded ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UCS2; + + pFont->m_aGlobalMetricY.width = pFont->m_aGlobalMetricX.width = aInfo.xMax - aInfo.xMin; + pFont->m_aGlobalMetricY.height = pFont->m_aGlobalMetricX.height = aInfo.yMax - aInfo.yMin; + + if( aInfo.winAscent && aInfo.winDescent ) + { + pFont->m_nAscend = aInfo.winAscent; + pFont->m_nDescend = aInfo.winDescent; + pFont->m_nLeading = pFont->m_nAscend + pFont->m_nDescend - 1000; + } + else if( aInfo.typoAscender && aInfo.typoDescender ) + { + pFont->m_nLeading = aInfo.typoLineGap; + pFont->m_nAscend = aInfo.typoAscender; + pFont->m_nDescend = -aInfo.typoDescender; + } + else + { + pFont->m_nLeading = aInfo.linegap; + pFont->m_nAscend = aInfo.ascender; + pFont->m_nDescend = -aInfo.descender; + } + + // last try: font bounding box + if( pFont->m_nAscend == 0 ) + pFont->m_nAscend = aInfo.yMax; + if( pFont->m_nDescend == 0 ) + pFont->m_nDescend = -aInfo.yMin; + if( pFont->m_nLeading == 0 ) + pFont->m_nLeading = 15 * (pFont->m_nAscend+pFont->m_nDescend) / 100; + + if( pFont->m_nAscend ) + pFont->m_aGlobalMetricX.height = pFont->m_aGlobalMetricY.height = pFont->m_nAscend + pFont->m_nDescend; + + // get bounding box + pFont->m_nXMin = aInfo.xMin; + pFont->m_nYMin = aInfo.yMin; + pFont->m_nXMax = aInfo.xMax; + pFont->m_nYMax = aInfo.yMax; + + // get type flags + pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags; + + // get vertical substitutions flag + pFont->m_bHaveVerticalSubstitutedGlyphs = DoesVerticalSubstitution( pTTFont, 1 ); + + CloseTTFont( pTTFont ); + bSuccess = true; + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "could not OpenTTFont \"%s\"\n", aFile.GetBuffer() ); +#endif + + return bSuccess; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::initFontsAlias() +{ + m_aXLFD_Aliases.clear(); + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + for( std::list< OString >::const_iterator dir_it = m_aFontDirectories.begin(); + dir_it != m_aFontDirectories.end(); ++dir_it ) + { + OStringBuffer aDirName(512); + aDirName.append( *dir_it ); + aDirName.append( "/fonts.alias" ); + SvFileStream aStream( OStringToOUString( aDirName.makeStringAndClear(), aEnc ), STREAM_READ ); + if( ! aStream.IsOpen() ) + continue; + + do + { + ByteString aLine; + aStream.ReadLine( aLine ); + + // get the alias and the pattern it gets translated to + ByteString aAlias = GetCommandLineToken( 0, aLine ); + ByteString aMap = GetCommandLineToken( 1, aLine ); + + // remove eventual quotes + aAlias.EraseLeadingChars( '"' ); + aAlias.EraseTrailingChars( '"' ); + aMap.EraseLeadingChars( '"' ); + aMap.EraseTrailingChars( '"' ); + + XLFDEntry aAliasEntry, aMapEntry; + parseXLFD( aAlias, aAliasEntry ); + parseXLFD( aMap, aMapEntry ); + + if( aAliasEntry.nMask && aMapEntry.nMask ) + m_aXLFD_Aliases[ aMapEntry ].push_back( aAliasEntry ); + } while( ! aStream.IsEof() ); + } +} + +// code stolen from vcl's RegisterFontSubstitutors() +// TODO: use that method once psprint gets merged into vcl +static bool AreFCSubstitutionsEnabled() +{ + // init font substitution defaults + int nDisableBits = 0; +#ifdef SOLARIS + // TODO: check the OS version and fc-data maintenance level + nDisableBits = 1; // disable "font fallback" here on default +#endif + // apply the environment variable if any + const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); + if( pEnvStr ) + { + // + if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) + nDisableBits = (*pEnvStr - '0'); + else + nDisableBits = ~0U; // no specific bits set: disable all + } + + return ((nDisableBits & 3) == 0); +} + +void PrintFontManager::initialize() +{ + #ifdef CALLGRIND_COMPILE + CALLGRIND_TOGGLE_COLLECT(); + CALLGRIND_ZERO_STATS(); + #endif + + long aDirEntBuffer[ (sizeof(struct dirent)+_PC_NAME_MAX)+1 ]; + + if( ! m_pFontCache ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "creating font cache ... " ); + clock_t aStart; + struct tms tms; + aStart = times( &tms ); +#endif + m_pFontCache = new FontCache(); +#if OSL_DEBUG_LEVEL > 1 + clock_t aStop = times( &tms ); + fprintf( stderr, "done in %lf s\n", (double)(aStop - aStart)/(double)sysconf( _SC_CLK_TCK ) ); +#endif + } + + // initialize may be called twice in the future + { + for( ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + delete (*it).second; + m_nNextFontID = 1; + m_aFonts.clear(); + m_aFontDirectories.clear(); + m_aPrivateFontDirectories.clear(); + m_aOverrideFonts.clear(); + } + +#if OSL_DEBUG_LEVEL > 1 + clock_t aStart; + clock_t aStep1; + clock_t aStep2; + clock_t aStep3; + int nBuiltinFonts = 0; + int nCached = 0; + + struct tms tms; + + aStart = times( &tms ); +#endif + + // first try fontconfig + m_bFontconfigSuccess = initFontconfig(); + + // part one - look for downloadable fonts + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + const ::rtl::OUString &rSalPrivatePath = psp::getFontPath(); + + // search for the fonts in SAL_PRIVATE_FONTPATH first; those are + // the TrueType fonts installed with the office + if( rSalPrivatePath.getLength() ) + { + OString aPath = rtl::OUStringToOString( rSalPrivatePath, aEncoding ); + const bool bAreFCSubstitutionsEnabled = AreFCSubstitutionsEnabled(); + sal_Int32 nIndex = 0; + do + { + OString aToken = aPath.getToken( 0, ';', nIndex ); + normPath( aToken ); + // if registering an app-specific fontdir with fontconfig fails + // and fontconfig-based substitutions are enabled + // then trying to use these app-specific fonts doesn't make sense + if( m_bFontconfigSuccess && !addFontconfigDir( aToken ) ) + if( bAreFCSubstitutionsEnabled ) + continue; + m_aFontDirectories.push_back( aToken ); + m_aPrivateFontDirectories.push_back( getDirectoryAtom( aToken, true ) ); + } while( nIndex >= 0 ); + } + + // now that all global and local font dirs are known to fontconfig + // check that there are fonts actually managed by fontconfig + if( m_bFontconfigSuccess ) + m_bFontconfigSuccess = (countFontconfigFonts() > 0); + + // don't search through many directories fontconfig already told us about + if( ! m_bFontconfigSuccess ) + ImplGetSVData()->mpDefInst->FillFontPathList( m_aFontDirectories ); + + // fill XLFD aliases from fonts.alias files + initFontsAlias(); + + // search for font files in each path + std::list< OString >::iterator dir_it; + // protect against duplicate paths + std::hash_map< OString, int, OStringHash > visited_dirs; + for( dir_it = m_aFontDirectories.begin(); dir_it != m_aFontDirectories.end(); ++dir_it ) + { + OString aPath( *dir_it ); + // see if we were here already + if( visited_dirs.find( aPath ) != visited_dirs.end() ) + continue; + visited_dirs[ aPath ] = 1; + + // there may be ":unscaled" directories (see XFree86) + // it should be safe to ignore them since they should not + // contain any of our recognizeable fonts + + // ask the font cache whether it handles this directory + std::list< PrintFont* > aCacheFonts; + if( m_pFontCache->listDirectory( aPath, aCacheFonts ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding cache directory: %s\n", aPath.getStr() ); +#endif + for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it ) + { + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = *it; + if( (*it)->m_eType == fonttype::Type1 ) + m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::TrueType ) + m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::Builtin ) + m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont ); +#if OSL_DEBUG_LEVEL > 1 + if( (*it)->m_eType == fonttype::Builtin ) + nBuiltinFonts++; + nCached++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont, + OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), + getFontFileSysPath( aFont ).getStr() ); +#endif +#endif + } + if( ! m_pFontCache->scanAdditionalFiles( aPath ) ) + continue; + } + + DIR* pDIR = opendir( aPath.getStr() ); + struct dirent* pEntry = (struct dirent*)aDirEntBuffer; + if( pDIR ) + { + // read fonts.dir if possible + ::std::hash_map< OString, ::std::list<OString>, OStringHash > aFontsDir; + int nDirID = getDirectoryAtom( aPath, true ); + // #i38367# no fonts.dir in our own directories anymore + std::list< int >::const_iterator priv_dir; + for( priv_dir = m_aPrivateFontDirectories.begin(); + priv_dir != m_aPrivateFontDirectories.end() && *priv_dir != nDirID; + ++priv_dir ) + ; + + if( priv_dir == m_aPrivateFontDirectories.end() ) + { + ByteString aGccDummy( aPath ); + String aFontsDirPath( aGccDummy, aEncoding ); + aFontsDirPath.AppendAscii( "/fonts.dir" ); + SvFileStream aStream( aFontsDirPath, STREAM_READ ); + if( aStream.IsOpen() ) + { + ByteString aLine; + while( ! aStream.IsEof() ) + { + aStream.ReadLine( aLine ); + ByteString aFileName( GetCommandLineToken( 0, aLine ) ); + ByteString aXLFD( aLine.Copy( aFileName.Len() ) ); + if( aFileName.Len() && aXLFD.Len() ) + aFontsDir[ aFileName ].push_back(aXLFD); + } + } + } + + int nDirFonts = 0; + while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pEntry ) && pEntry ) + { + OString aFileName( pEntry->d_name ); + // ignore .afm files here + if( aFileName.getLength() > 3 && + aFileName.lastIndexOf( ".afm" ) == aFileName.getLength()-4 ) + continue; + + struct stat aStat; + ByteString aFilePath( aPath ); + aFilePath.Append( '/' ); + aFilePath.Append( ByteString( aFileName ) ); + if( ! stat( aFilePath.GetBuffer(), &aStat ) && + S_ISREG( aStat.st_mode ) ) + { + if( findFontFileID( nDirID, aFileName ) == 0 ) + { + ::std::list<OString> aXLFDs; + ::std::hash_map< OString, ::std::list<OString>, OStringHash >::const_iterator it = + aFontsDir.find( aFileName ); + if( it != aFontsDir.end() ) + aXLFDs = (*it).second; + + // fill in font attributes from XLFD rather + // than reading every file + ::std::list< PrintFont* > aNewFonts; + if( analyzeFontFile( nDirID, aFileName, aXLFDs, aNewFonts ) ) + { + for( ::std::list< PrintFont* >::iterator font_it = aNewFonts.begin(); font_it != aNewFonts.end(); ++font_it ) + { + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = *font_it; + m_aFontFileToFontID[ aFileName ].insert( aFont ); + m_pFontCache->updateFontCacheEntry( *font_it, false ); + nDirFonts++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "adding font %d: \"%s\" from %s\n", aFont, + OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), + getFontFileSysPath( aFont ).getStr() ); +#endif + } + } + } + } + } + closedir( pDIR ); + m_pFontCache->updateDirTimestamp( nDirID ); + if( ! nDirFonts ) + m_pFontCache->markEmptyDir( nDirID ); + } + } + +#if OSL_DEBUG_LEVEL > 1 + aStep1 = times( &tms ); +#endif + + // part two - look for metrics for builtin printer fonts + std::list< OUString > aMetricDirs; + psp::getPrinterPathList( aMetricDirs, PRINTER_METRICDIR ); + + std::list< OString > aEmptyFontsDir; + for( std::list< OUString >::const_iterator met_dir_it = aMetricDirs.begin(); met_dir_it != aMetricDirs.end(); ++met_dir_it ) + { + OString aDir = OUStringToOString( *met_dir_it, aEncoding ); + + // ask the font cache whether it handles this directory + std::list< PrintFont* > aCacheFonts; + + if( m_pFontCache->listDirectory( aDir, aCacheFonts ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding cache directory: %s\n", aDir.getStr() ); +#endif + for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it ) + { + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = *it; + if( (*it)->m_eType == fonttype::Type1 ) + m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::TrueType ) + m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::Builtin ) + m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont ); +#if OSL_DEBUG_LEVEL > 1 + if( (*it)->m_eType == fonttype::Builtin ) + nBuiltinFonts++; + nCached++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont, + OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), + getFontFileSysPath( aFont ).getStr() ); +#endif +#endif + } + continue; + } + + DIR* pDIR = opendir( aDir.getStr() ); + if( pDIR ) + { + struct dirent* pDirEntry = (struct dirent*)aDirEntBuffer; + int nDirID = getDirectoryAtom( aDir, true ); + int nDirFonts = 0; + + while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pDirEntry ) && pDirEntry ) + { + ByteString aFile( aDir ); + aFile += '/'; + aFile += pDirEntry->d_name; + struct stat aStat; + if( ! stat( aFile.GetBuffer(), &aStat ) + && S_ISREG( aStat.st_mode ) + ) + { + OString aFileName( pDirEntry->d_name, strlen( pDirEntry->d_name ) ); + OString aExt( aFileName.copy( aFileName.lastIndexOf( '.' )+1 ) ); + if( aExt.equalsIgnoreAsciiCase( "afm" ) ) + { + ::std::list< PrintFont* > aNewFonts; + + analyzeFontFile( nDirID, aFileName, aEmptyFontsDir, aNewFonts ); + for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) + { + if( findFontBuiltinID( (*it)->m_nPSName ) == 0 ) + { + m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID ); + m_aFonts[ m_nNextFontID++ ] = *it; + m_pFontCache->updateFontCacheEntry( *it, false ); +#if OSL_DEBUG_LEVEL > 2 + nBuiltinFonts++; +#endif + } + else + delete *it; + } + } + } + } + closedir( pDIR ); + if( ! nDirFonts ) + m_pFontCache->markEmptyDir( nDirID ); + } + } + +#if OSL_DEBUG_LEVEL > 1 + aStep2 = times( &tms ); +#endif + + // part three - fill in family styles + ::std::hash_map< fontID, PrintFont* >::iterator font_it; + for (font_it = m_aFonts.begin(); font_it != m_aFonts.end(); ++font_it) + { + ::std::hash_map< int, family::type >::const_iterator it = + m_aFamilyTypes.find( font_it->second->m_nFamilyName ); + if (it != m_aFamilyTypes.end()) + continue; + const ::rtl::OUString& rFamily = + m_pAtoms->getString( ATOM_FAMILYNAME, font_it->second->m_nFamilyName); + family::type eType = matchFamilyName( rFamily ); + m_aFamilyTypes[ font_it->second->m_nFamilyName ] = eType; + } + +#if OSL_DEBUG_LEVEL > 1 + aStep3 = times( &tms ); + fprintf( stderr, "PrintFontManager::initialize: collected %d fonts (%d builtin, %d cached)\n", m_aFonts.size(), nBuiltinFonts, nCached ); + double fTick = (double)sysconf( _SC_CLK_TCK ); + fprintf( stderr, "Step 1 took %lf seconds\n", (double)(aStep1 - aStart)/fTick ); + fprintf( stderr, "Step 2 took %lf seconds\n", (double)(aStep2 - aStep1)/fTick ); + fprintf( stderr, "Step 3 took %lf seconds\n", (double)(aStep3 - aStep2)/fTick ); +#endif + + m_pFontCache->flush(); + + #ifdef CALLGRIND_COMPILE + CALLGRIND_DUMP_STATS(); + CALLGRIND_TOGGLE_COLLECT(); + #endif +} + +// ------------------------------------------------------------------------- +inline bool +equalPitch (psp::pitch::type from, psp::pitch::type to) +{ + return from == to; +} + +inline bool +equalWeight (psp::weight::type from, psp::weight::type to) +{ + return from > to ? (from - to) <= 3 : (to - from) <= 3; +} + +inline bool +equalItalic (psp::italic::type from, psp::italic::type to) +{ + if ( (from == psp::italic::Italic) || (from == psp::italic::Oblique) ) + return (to == psp::italic::Italic) || (to == psp::italic::Oblique); + return to == from; +} +inline bool +equalEncoding (rtl_TextEncoding from, rtl_TextEncoding to) +{ + if ((from == RTL_TEXTENCODING_ISO_8859_1) || (from == RTL_TEXTENCODING_MS_1252)) + return (to == RTL_TEXTENCODING_ISO_8859_1) || (to == RTL_TEXTENCODING_MS_1252); + return from == to; +} + +namespace { + struct BuiltinFontIdentifier + { + OUString aFamily; + italic::type eItalic; + weight::type eWeight; + pitch::type ePitch; + rtl_TextEncoding aEncoding; + + BuiltinFontIdentifier( const OUString& rFam, + italic::type eIt, + weight::type eWg, + pitch::type ePt, + rtl_TextEncoding enc ) : + aFamily( rFam ), + eItalic( eIt ), + eWeight( eWg ), + ePitch( ePt ), + aEncoding( enc ) + {} + + bool operator==( const BuiltinFontIdentifier& rRight ) const + { + return equalItalic( eItalic, rRight.eItalic ) && + equalWeight( eWeight, rRight.eWeight ) && + equalPitch( ePitch, rRight.ePitch ) && + equalEncoding( aEncoding, rRight.aEncoding ) && + aFamily.equalsIgnoreAsciiCase( rRight.aFamily ); + } + }; + + struct BuiltinFontIdentifierHash + { + size_t operator()( const BuiltinFontIdentifier& rFont ) const + { + return rFont.aFamily.hashCode() ^ rFont.eItalic ^ rFont.eWeight ^ rFont.ePitch ^ rFont.aEncoding; + } + }; +} + +void PrintFontManager::getFontList( ::std::list< fontID >& rFontIDs, const PPDParser* pParser, bool bUseOverrideMetrics ) +{ + rFontIDs.clear(); + std::hash_map< fontID, PrintFont* >::const_iterator it; + + /* + * Note: there are two easy steps making this faster: + * first: insert the printer builtins first, then the not builtins, + * if they do not match. + * drawback: this would change the sequence of fonts; this could have + * subtle, unknown consequences in vcl font matching + * second: instead of comparing attributes to see whether a softfont + * is duplicate to a builtin one could simply compare the PSName (which is + * supposed to be unique), which at this point is just an int. + * drawback: this could change which fonts are listed; especially TrueType + * fonts often have a rather dubious PSName, so this could change the + * font list not so subtle. + * Until getFontList for a printer becomes a performance issue (which is + * currently not the case), best stay with the current algorithm. + */ + + // fill sets of printer supported fonts + if( pParser ) + { + std::set<int> aBuiltinPSNames; + std::hash_set< BuiltinFontIdentifier, + BuiltinFontIdentifierHash + > aBuiltinFonts; + + std::map<int, fontID > aOverridePSNames; + if( bUseOverrideMetrics ) + { + readOverrideMetrics(); + for( std::vector<fontID>::const_iterator over = m_aOverrideFonts.begin(); + over != m_aOverrideFonts.end(); ++over ) + { + std::hash_map<fontID,PrintFont*>::const_iterator font_it = m_aFonts.find( *over ); + DBG_ASSERT( font_it != m_aFonts.end(), "override to nonexistant font" ); + if( font_it != m_aFonts.end() ) + aOverridePSNames[ font_it->second->m_nPSName ] = *over; + } + } + + int nFonts = pParser->getFonts(); + for( int i = 0; i < nFonts; i++ ) + aBuiltinPSNames.insert( m_pAtoms->getAtom( ATOM_PSNAME, pParser->getFont( i ) ) ); + for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + { + PrintFont* pFont = it->second; + if( it->second->m_eType == fonttype::Builtin && + aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() ) + { + bool bInsert = true; + if( bUseOverrideMetrics ) + { + // in override case only use the override fonts, not their counterparts + std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName ); + if( over != aOverridePSNames.end() && over->second != it->first ) + bInsert = false; + } + else + { + // do not insert override fonts in non override case + if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() ) + bInsert = false; + } + if( bInsert ) + { + aBuiltinFonts.insert( BuiltinFontIdentifier( + m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ), + pFont->m_eItalic, + pFont->m_eWeight, + pFont->m_ePitch, + pFont->m_aEncoding + ) ); + } + } + } + for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + { + PrintFont* pFont = it->second; + if( it->second->m_eType == fonttype::Builtin ) + { + if( aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() ) + { + bool bInsert = true; + if( bUseOverrideMetrics ) + { + // in override case only use the override fonts, not their counterparts + std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName ); + if( over != aOverridePSNames.end() && over->second != it->first ) + bInsert = false; + } + else + { + // do not insert override fonts in non override case + if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() ) + bInsert = false; + } + if( bInsert ) + rFontIDs.push_back( it->first ); + } + } + else if( aBuiltinFonts.find( BuiltinFontIdentifier( + m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ), + pFont->m_eItalic, + pFont->m_eWeight, + pFont->m_ePitch, + pFont->m_aEncoding + ) ) == aBuiltinFonts.end() ) + { + rFontIDs.push_back( it->first ); + } + } + } + else // no specific printer + { + for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + rFontIDs.push_back( it->first ); + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, FastPrintFontInfo& rInfo ) const +{ + ::std::hash_map< int, family::type >::const_iterator style_it = + m_aFamilyTypes.find( pFont->m_nFamilyName ); + rInfo.m_eType = pFont->m_eType; + rInfo.m_aFamilyName = m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ); + rInfo.m_aStyleName = pFont->m_aStyleName; + rInfo.m_eFamilyStyle = style_it != m_aFamilyTypes.end() ? style_it->second : family::Unknown; + rInfo.m_eItalic = pFont->m_eItalic; + rInfo.m_eWidth = pFont->m_eWidth; + rInfo.m_eWeight = pFont->m_eWeight; + rInfo.m_ePitch = pFont->m_ePitch; + rInfo.m_aEncoding = pFont->m_aEncoding; + rInfo.m_eEmbeddedbitmap = pFont->m_eEmbeddedbitmap; + rInfo.m_eAntialias = pFont->m_eAntialias; + rInfo.m_aAliases.clear(); + for( ::std::list< int >::iterator it = pFont->m_aAliases.begin(); it != pFont->m_aAliases.end(); ++it ) + rInfo.m_aAliases.push_back( m_pAtoms->getString( ATOM_FAMILYNAME, *it ) ); +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, PrintFontInfo& rInfo ) const +{ + if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) || + ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() + ) + { + // might be a truetype font not analyzed or type1 without metrics read + if( pFont->m_eType == fonttype::Type1 ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + fillPrintFontInfo( pFont, static_cast< FastPrintFontInfo& >( rInfo ) ); + + rInfo.m_nAscend = pFont->m_nAscend; + rInfo.m_nDescend = pFont->m_nDescend; + rInfo.m_nLeading = pFont->m_nLeading; + rInfo.m_nWidth = pFont->m_aGlobalMetricX.width < pFont->m_aGlobalMetricY.width ? pFont->m_aGlobalMetricY.width : pFont->m_aGlobalMetricX.width; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::getFontListWithInfo( ::std::list< PrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics ) +{ + rFonts.clear(); + ::std::list< fontID > aFontList; + getFontList( aFontList, pParser, bUseOverrideMetrics ); + + ::std::list< fontID >::iterator it; + for( it = aFontList.begin(); it != aFontList.end(); ++it ) + { + PrintFontInfo aInfo; + aInfo.m_nID = *it; + fillPrintFontInfo( getFont( *it ), aInfo ); + rFonts.push_back( aInfo ); + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::getFontListWithFastInfo( ::std::list< FastPrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics ) +{ + rFonts.clear(); + ::std::list< fontID > aFontList; + getFontList( aFontList, pParser, bUseOverrideMetrics ); + + ::std::list< fontID >::iterator it; + for( it = aFontList.begin(); it != aFontList.end(); ++it ) + { + FastPrintFontInfo aInfo; + aInfo.m_nID = *it; + fillPrintFontInfo( getFont( *it ), aInfo ); + rFonts.push_back( aInfo ); + } +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont ) + { + rInfo.m_nID = nFontID; + fillPrintFontInfo( pFont, rInfo ); + } + return pFont ? true : false; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont ) + { + rInfo.m_nID = nFontID; + fillPrintFontInfo( pFont, rInfo ); + } + return pFont ? true : false; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFontBoundingBox( fontID nFontID, int& xMin, int& yMin, int& xMax, int& yMax ) +{ + bool bSuccess = false; + PrintFont* pFont = getFont( nFontID ); + if( pFont ) + { + if( pFont->m_nXMin == 0 && pFont->m_nYMin == 0 && pFont->m_nXMax == 0 && pFont->m_nYMax == 0 ) + { + // might be a truetype font not analyzed or type1 without metrics read + if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + bSuccess = true; + xMin = pFont->m_nXMin; + yMin = pFont->m_nYMin; + xMax = pFont->m_nXMax; + yMax = pFont->m_nYMax; + } + return bSuccess; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontFaceNumber( fontID nFontID ) const +{ + int nRet = -1; + PrintFont* pFont = getFont( nFontID ); + if( pFont && pFont->m_eType == fonttype::TrueType ) + nRet = static_cast< TrueTypeFontFile* >(pFont)->m_nCollectionEntry; + return nRet; +} + +// ------------------------------------------------------------------------- + + +family::type PrintFontManager::matchFamilyName( const ::rtl::OUString& rFamily ) const +{ + typedef struct { + const char* mpName; + sal_uInt16 mnLength; + family::type meType; + } family_t; + +#define InitializeClass( p, a ) p, sizeof(p) - 1, a + const family_t pFamilyMatch[] = { + { InitializeClass( "arial", family::Swiss ) }, + { InitializeClass( "arioso", family::Script ) }, + { InitializeClass( "avant garde", family::Swiss ) }, + { InitializeClass( "avantgarde", family::Swiss ) }, + { InitializeClass( "bembo", family::Roman ) }, + { InitializeClass( "bookman", family::Roman ) }, + { InitializeClass( "conga", family::Roman ) }, + { InitializeClass( "courier", family::Modern ) }, + { InitializeClass( "curl", family::Script ) }, + { InitializeClass( "fixed", family::Modern ) }, + { InitializeClass( "gill", family::Swiss ) }, + { InitializeClass( "helmet", family::Modern ) }, + { InitializeClass( "helvetica", family::Swiss ) }, + { InitializeClass( "international", family::Modern ) }, + { InitializeClass( "lucida", family::Swiss ) }, + { InitializeClass( "new century schoolbook", family::Roman ) }, + { InitializeClass( "palatino", family::Roman ) }, + { InitializeClass( "roman", family::Roman ) }, + { InitializeClass( "sans serif", family::Swiss ) }, + { InitializeClass( "sansserif", family::Swiss ) }, + { InitializeClass( "serf", family::Roman ) }, + { InitializeClass( "serif", family::Roman ) }, + { InitializeClass( "times", family::Roman ) }, + { InitializeClass( "utopia", family::Roman ) }, + { InitializeClass( "zapf chancery", family::Script ) }, + { InitializeClass( "zapfchancery", family::Script ) } + }; + + rtl::OString aFamily = rtl::OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US ); + sal_uInt32 nLower = 0; + sal_uInt32 nUpper = sizeof(pFamilyMatch) / sizeof(pFamilyMatch[0]); + + while( nLower < nUpper ) + { + sal_uInt32 nCurrent = (nLower + nUpper) / 2; + const family_t* pHaystack = pFamilyMatch + nCurrent; + sal_Int32 nComparison = + rtl_str_compareIgnoreAsciiCase_WithLength + ( + aFamily.getStr(), aFamily.getLength(), + pHaystack->mpName, pHaystack->mnLength + ); + + if( nComparison < 0 ) + nUpper = nCurrent; + else + if( nComparison > 0 ) + nLower = nCurrent + 1; + else + return pHaystack->meType; + } + + return family::Unknown; +} + +// ------------------------------------------------------------------------- + +family::type PrintFontManager::getFontFamilyType( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( !pFont ) + return family::Unknown; + + ::std::hash_map< int, family::type >::const_iterator it = + m_aFamilyTypes.find( pFont->m_nFamilyName ); + return (it != m_aFamilyTypes.end()) ? it->second : family::Unknown; +} + + +// ------------------------------------------------------------------------- + +const ::rtl::OUString& PrintFontManager::getFontFamily( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + return m_pAtoms->getString( ATOM_FAMILYNAME, pFont ? pFont->m_nFamilyName : INVALID_ATOM ); +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::getAfmFile( PrintFont* pFont ) const +{ + OString aMetricPath; + if( pFont ) + { + switch( pFont->m_eType ) + { + case fonttype::Type1: + { + Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont); + aMetricPath = getDirectory( pPSFont->m_nDirectory ); + aMetricPath += "/"; + aMetricPath += pPSFont->m_aMetricFile; + } + break; + case fonttype::Builtin: + { + BuiltinFont* pBuiltinFont = static_cast< BuiltinFont* >(pFont); + aMetricPath = getDirectory( pBuiltinFont->m_nDirectory ); + aMetricPath += "/"; + aMetricPath += pBuiltinFont->m_aMetricFile; + } + break; + default: break; + } + } + return aMetricPath; +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::getFontFile( PrintFont* pFont ) const +{ + OString aPath; + + if( pFont && pFont->m_eType == fonttype::Type1 ) + { + Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont); + ::std::hash_map< int, OString >::const_iterator it = m_aAtomToDir.find( pPSFont->m_nDirectory ); + aPath = it->second; + aPath += "/"; + aPath += pPSFont->m_aFontFile; + } + else if( pFont && pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFontFile* pTTFont = static_cast< TrueTypeFontFile* >(pFont); + ::std::hash_map< int, OString >::const_iterator it = m_aAtomToDir.find( pTTFont->m_nDirectory ); + aPath = it->second; + aPath += "/"; + aPath += pTTFont->m_aFontFile; + } + return aPath; +} + +// ------------------------------------------------------------------------- + +const ::rtl::OUString& PrintFontManager::getPSName( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont && pFont->m_nPSName == 0 ) + { + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + return m_pAtoms->getString( ATOM_PSNAME, pFont ? pFont->m_nPSName : INVALID_ATOM ); +} + +// ------------------------------------------------------------------------- + +const CharacterMetric& PrintFontManager::getGlobalFontMetric( fontID nFontID, bool bHorizontal ) const +{ + static CharacterMetric aMetric; + PrintFont* pFont = getFont( nFontID ); + return pFont ? ( bHorizontal ? pFont->m_aGlobalMetricX : pFont->m_aGlobalMetricY ) : aMetric; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontAscend( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); + } + return pFont->m_nAscend; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontDescend( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); + } + return pFont->m_nDescend; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontLeading( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + return pFont->m_nLeading; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::hasVerticalSubstitutions( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + return pFont->m_bHaveVerticalSubstitutedGlyphs; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::hasVerticalSubstitutions( fontID nFontID, + const sal_Unicode* pCharacters, int nCharacters, bool* pHasSubst ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + if( ! pFont->m_bHaveVerticalSubstitutedGlyphs ) + memset( pHasSubst, 0, sizeof(bool)*nCharacters ); + else + { + for( int i = 0; i < nCharacters; i++ ) + { + sal_Unicode code = pCharacters[i]; + if( ! pFont->m_pMetrics || + ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) ) + pFont->queryMetricPage( code >> 8, m_pAtoms ); + ::std::hash_map< sal_Unicode, bool >::const_iterator it = pFont->m_pMetrics->m_bVerticalSubstitutions.find( code ); + pHasSubst[i] = it != pFont->m_pMetrics->m_bVerticalSubstitutions.end(); + } + } +} + +// ------------------------------------------------------------------------- + +OUString PrintFontManager::getFontXLFD( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + OUString aRet; + if( pFont ) + { + ByteString aXLFD( getXLFD( pFont ) ); + rtl_TextEncoding aEncoding = aXLFD.GetToken( 6, '-' ).Search( "utf8" ) != STRING_NOTFOUND ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1; + aRet = OStringToOUString( aXLFD, aEncoding ); + } + return aRet; +} + +// ------------------------------------------------------------------------- + +const ::std::list< KernPair >& PrintFontManager::getKernPairs( fontID nFontID, bool bVertical ) const +{ + static ::std::list< KernPair > aEmpty; + + PrintFont* pFont = getFont( nFontID ); + if( ! pFont ) + return aEmpty; + + if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried ) + pFont->queryMetricPage( 0, m_pAtoms ); + if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried ) + return aEmpty; + return bVertical ? pFont->m_pMetrics->m_aYKernPairs : pFont->m_pMetrics->m_aXKernPairs; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::isFontDownloadingAllowed( fontID nFont ) const +{ + static const char* pEnable = getenv( "PSPRINT_ENABLE_TTF_COPYRIGHTAWARENESS" ); + bool bRet = true; + + if( pEnable && *pEnable ) + { + PrintFont* pFont = getFont( nFont ); + if( pFont && pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFontFile* pTTFontFile = static_cast<TrueTypeFontFile*>(pFont); + if( pTTFontFile->m_nTypeFlags & 0x80000000 ) + { + TrueTypeFont* pTTFont = NULL; + ByteString aFile = getFontFile( pFont ); + if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + // get type flags + TTGlobalFontInfo aInfo; + GetTTGlobalFontInfo( pTTFont, & aInfo ); + pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags; + CloseTTFont( pTTFont ); + } + } + + unsigned int nCopyrightFlags = pTTFontFile->m_nTypeFlags & 0x0e; + + // font embedding is allowed if either + // no restriction at all (bit 1 clear) + // printing allowed (bit 1 set, bit 2 set ) + bRet = ! ( nCopyrightFlags & 0x02 ) || ( nCopyrightFlags & 0x04 ); + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getMetrics( fontID nFontID, const sal_Unicode* pString, int nLen, CharacterMetric* pArray, bool bVertical ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( ! pFont ) + return false; + + if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() + ) + { + // might be a font not yet analyzed + if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + for( int i = 0; i < nLen; i++ ) + { + if( ! pFont->m_pMetrics || + ! ( pFont->m_pMetrics->m_aPages[ pString[i] >> 11 ] & ( 1 << ( ( pString[i] >> 8 ) & 7 ) ) ) ) + pFont->queryMetricPage( pString[i] >> 8, m_pAtoms ); + pArray[i].width = pArray[i].height = -1; + if( pFont->m_pMetrics ) + { + int effectiveCode = pString[i]; + effectiveCode |= bVertical ? 1 << 16 : 0; + ::std::hash_map< int, CharacterMetric >::const_iterator it = + pFont->m_pMetrics->m_aMetrics.find( effectiveCode ); + // if no vertical metrics are available assume rotated horizontal metrics + if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) ) + it = pFont->m_pMetrics->m_aMetrics.find( pString[i] ); + // the character metrics are in it->second + if( it != pFont->m_pMetrics->m_aMetrics.end() ) + pArray[ i ] = it->second; + } + } + + return true; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getMetrics( fontID nFontID, sal_Unicode minCharacter, sal_Unicode maxCharacter, CharacterMetric* pArray, bool bVertical ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( ! pFont ) + return false; + + if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() + ) + { + // might be a font not yet analyzed + if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + sal_Unicode code = minCharacter; + do + { + if( ! pFont->m_pMetrics || + ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) ) + pFont->queryMetricPage( code >> 8, m_pAtoms ); + pArray[ code - minCharacter ].width = -1; + pArray[ code - minCharacter ].height = -1; + if( pFont->m_pMetrics ) + { + int effectiveCode = code; + effectiveCode |= bVertical ? 1 << 16 : 0; + ::std::hash_map< int, CharacterMetric >::const_iterator it = + pFont->m_pMetrics->m_aMetrics.find( effectiveCode ); + // if no vertical metrics are available assume rotated horizontal metrics + if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) ) + it = pFont->m_pMetrics->m_aMetrics.find( code ); + // the character metrics are in it->second + if( it != pFont->m_pMetrics->m_aMetrics.end() ) + pArray[ code - minCharacter ] = it->second; + } + } while( code++ != maxCharacter ); + + return true; +} + +// ------------------------------------------------------------------------- + +static bool createWriteablePath( const ByteString& rPath ) +{ + bool bSuccess = false; + + if( access( rPath.GetBuffer(), W_OK ) ) + { + int nPos = rPath.SearchBackward( '/' ); + if( nPos != STRING_NOTFOUND ) + while( nPos > 0 && rPath.GetChar( nPos ) == '/' ) + nPos--; + + if( nPos != STRING_NOTFOUND && nPos != 0 && createWriteablePath( rPath.Copy( 0, nPos+1 ) ) ) + { + bSuccess = mkdir( rPath.GetBuffer(), 0777 ) ? false : true; + } + } + else + bSuccess = true; + + return bSuccess; +} + + +// ------------------------------------------------------------------------- + +int PrintFontManager::importFonts( const ::std::list< OString >& rFiles, bool bLinkOnly, ImportFontCallback* pCallback ) +{ + int nSuccess = 0; + + // find a directory with write access + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + bool bCanWrite = false; + int nDirID = 0; + INetURLObject aDir; + for( ::std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin(); + ! bCanWrite && dir_it != m_aPrivateFontDirectories.end(); ++dir_it ) + { + // check if we can create files in that directory + ByteString aDirPath = getDirectory( *dir_it ); + if( createWriteablePath( aDirPath ) ) + { + aDir = INetURLObject( OStringToOUString( aDirPath, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + nDirID = *dir_it; + bCanWrite = true; + } + } + if( bCanWrite ) + { + for( ::std::list< OString >::const_iterator font_it = rFiles.begin(); + font_it != rFiles.end(); ++font_it ) + { + INetURLObject aFrom( OStringToOUString( *font_it, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + INetURLObject aTo( aDir ); + aTo.Append( aFrom.GetName() ); + + if( pCallback ) + pCallback->progress( aTo.PathToFileName() ); + + if( pCallback && pCallback->isCanceled() ) + break; + + if( ! access( ByteString( String(aTo.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + if( ! ( pCallback ? pCallback->queryOverwriteFile( aTo.PathToFileName() ) : false ) ) + continue; + } + // look for afm if necessary + OUString aAfmCopied; + FileBase::RC nError; + if( aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfa" ) || + aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfb" ) ) + { + INetURLObject aFromAfm( aFrom ); + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + aFromAfm.removeSegment(); + aFromAfm.Append( String( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + aFromAfm.Append( aTo.GetName() ); + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + // give up + if( pCallback ) + pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::NoAfmMetric ); + continue; + } + } + } + } + INetURLObject aToAfm( aTo ); + aToAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + OUString aFromPath, aToPath; + if( bLinkOnly ) + { + ByteString aLinkFromPath( String(aFromAfm.PathToFileName()), + aEncoding ); + ByteString aLinkToPath( String(aToAfm.PathToFileName()), + aEncoding ); + nError = (FileBase::RC)symlink( aLinkFromPath.GetBuffer(), aLinkToPath.GetBuffer() ); + } + else + nError = File::copy( aFromAfm.GetMainURL(INetURLObject::DECODE_TO_IURI), aToAfm.GetMainURL(INetURLObject::DECODE_TO_IURI) ); + if( nError ) + { + if( pCallback ) + pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::AfmCopyFailed ); + continue; + } + aAfmCopied = aToPath; + } + if( bLinkOnly ) + { + ByteString aFromPath( String(aFrom.PathToFileName()), + aEncoding ); + ByteString aToPath( String(aTo.PathToFileName()), aEncoding ); + nError = (FileBase::RC)symlink( aFromPath.GetBuffer(), + aToPath.GetBuffer() ); + } + else + nError = File::copy( aFrom.GetMainURL(INetURLObject::DECODE_TO_IURI), aTo.GetMainURL(INetURLObject::DECODE_TO_IURI) ); + // copy font file + if( nError ) + { + if( aAfmCopied.getLength() ) + File::remove( aAfmCopied ); + if( pCallback ) + pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::FontCopyFailed ); + continue; + } + + ::std::list< PrintFont* > aNewFonts; + ::std::list< PrintFont* >::iterator it; + if( analyzeFontFile( nDirID, OUStringToOString( aTo.GetName(), aEncoding ), ::std::list<OString>(), aNewFonts ) ) + { + // remove all fonts for the same file + // discarding their font ids + ::std::hash_map< fontID, PrintFont* >::iterator current, next; + current = m_aFonts.begin(); + OString aFileName( OUStringToOString( aTo.GetName(), aEncoding ) ); + while( current != m_aFonts.end() ) + { + bool bRemove = false; + switch( current->second->m_eType ) + { + case fonttype::Type1: + if( static_cast<Type1FontFile*>(current->second)->m_aFontFile == aFileName ) + bRemove = true; + break; + case fonttype::TrueType: + if( static_cast<TrueTypeFontFile*>(current->second)->m_aFontFile == aFileName ) + bRemove = true; + break; + default: break; + } + if( bRemove ) + { + next = current; + ++next; + m_aFontFileToFontID[ aFileName ].erase( current->first ); + delete current->second; + m_aFonts.erase( current ); + current = next; + } + else + ++current; + } + + DBG_ASSERT( !findFontFileID( nDirID, aFileName ), "not all fonts removed for file" ); + + nSuccess++; + for( it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) + { + m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID ); + m_aFonts[ m_nNextFontID++ ] = *it; + m_pFontCache->updateFontCacheEntry( *it, false ); + } + } + } + + m_pFontCache->updateDirTimestamp( nDirID ); + m_pFontCache->flush(); + } + else if( pCallback ) + pCallback->importFontsFailed( ImportFontCallback::NoWritableDirectory ); + + return nSuccess; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::checkImportPossible() const +{ + bool bSuccess = false; + + // find a directory with write access + ByteString aDir; + for( std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin(); + dir_it != m_aPrivateFontDirectories.end(); ++dir_it ) + { + aDir = getDirectory( *dir_it ); + if( createWriteablePath( aDir ) ) + { + bSuccess = true; + break; + } + } + +#if OSL_DEBUG_LEVEL > 1 + if( bSuccess ) + fprintf( stderr, "found writable %s\n", aDir.GetBuffer() ); +#endif + + return bSuccess; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::checkChangeFontPropertiesPossible( fontID /*nFontID*/ ) const +{ + // since font properties are changed in the font cache file only nowadays + // they can always be changed + return true; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::changeFontProperties( fontID nFontID, const ::rtl::OUString& rXLFD ) +{ + ByteString aXLFD( OUStringToOString( rXLFD, RTL_TEXTENCODING_UTF8 ) ); + ByteString aAddStyle = aXLFD.GetToken( '-', 6 ); + if( aAddStyle.Search( "utf8" ) == STRING_NOTFOUND ) + { + aAddStyle.Append( aAddStyle.Len() ? ";utf8" : "utf8" ); + aXLFD.SetToken( 6, ';', aAddStyle ); + } + PrintFont* pFont = getFont( nFontID ); + std::list< OString > aDummyList; + aDummyList.push_back( aXLFD ); + getFontAttributesFromXLFD( pFont, aDummyList ); + pFont->m_bUserOverride = true; + m_pFontCache->updateFontCacheEntry( pFont, true ); + + return true; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager:: +getImportableFontProperties( + const OString& rFile, + ::std::list< FastPrintFontInfo >& rFontProps + ) +{ + rFontProps.clear(); + int nIndex = rFile.lastIndexOf( '/' ); + OString aDir, aFile( rFile.copy( nIndex+1 ) ); + if( nIndex != -1 ) + aDir = rFile.copy( 0, nIndex ); + int nDirID = getDirectoryAtom( aDir, true ); + ::std::list< PrintFont* > aFonts; + bool bRet = analyzeFontFile( nDirID, aFile, ::std::list<OString>(), aFonts ); + while( aFonts.begin() != aFonts.end() ) + { + PrintFont* pFont = aFonts.front(); + aFonts.pop_front(); + FastPrintFontInfo aInfo; + fillPrintFontInfo( pFont, aInfo ); + rFontProps.push_back( aInfo ); + delete pFont; + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFileDuplicates( fontID nFont, ::std::list< fontID >& rFonts ) const +{ + bool bRet = false; + + rFonts.clear(); + + PrintFont* pSearchFont = getFont( nFont ); + if( ! pSearchFont || + pSearchFont->m_eType != fonttype::TrueType || + static_cast<TrueTypeFontFile*>(pSearchFont)->m_nCollectionEntry == -1 + ) + return false; + + OString aFile( getFontFileSysPath( nFont ) ); + if( ! aFile.getLength() ) + return false; + + for( ::std::hash_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + { + if( nFont != it->first ) + { + OString aCompFile( getFontFile( it->second ) ); + if( aCompFile == aFile ) + { + rFonts.push_back( it->first ); + bRet = true; + } + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::removeFonts( const ::std::list< fontID >& rFonts ) +{ + bool bRet = true; + ::std::list< fontID > aDuplicates; + for( ::std::list< fontID >::const_iterator it = rFonts.begin(); it != rFonts.end(); ++it ) + { + ::std::hash_map< fontID, PrintFont* >::const_iterator haveFont = m_aFonts.find( *it ); + if( haveFont == m_aFonts.end() ) + continue; + + PrintFont* pFont = haveFont->second; + bool bRemoveDuplicates = getFileDuplicates( *it, aDuplicates ); + ByteString aFile( getFontFile( pFont ) ); + if( aFile.Len() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "try unlink( \"%s\" ) ... ", aFile.GetBuffer() ); +#endif + if( unlink( aFile.GetBuffer() ) ) + { + bRet = false; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "failed\n" ); +#endif + continue; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "succeeded\n" ); +#endif + OString aAfm( getAfmFile( pFont ) ); + if( aAfm.getLength() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "unlink( \"%s\" )\n", aAfm.getStr() ); +#endif + unlink( aAfm.getStr() ); + } + m_aFonts.erase( *it ); + delete pFont; + if( bRemoveDuplicates ) + { + for( ::std::list< fontID >::iterator dup = aDuplicates.begin(); dup != aDuplicates.end(); ++dup ) + { + m_aFontFileToFontID[ aFile ].erase( *dup ); + PrintFont* pDup = m_aFonts[ *dup ]; + m_aFonts.erase( *dup ); + delete pDup; + } + } + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::isPrivateFontFile( fontID nFont ) const +{ + bool bRet = false; + int nDirID = -1; + PrintFont* pFont = getFont( nFont ); + if( pFont ) + { + switch( pFont->m_eType ) + { + case fonttype::Type1: nDirID = static_cast< Type1FontFile* >(pFont)->m_nDirectory;break; + case fonttype::TrueType: nDirID = static_cast< TrueTypeFontFile* >(pFont)->m_nDirectory;break; + default: break; + } + } + if( nDirID != -1 ) + { + for( ::std::list< int >::const_iterator it = m_aPrivateFontDirectories.begin(); it != m_aPrivateFontDirectories.end(); ++it ) + { + if( nDirID == *it ) + { + bRet = true; + break; + } + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getAlternativeFamilyNames( fontID nFont, ::std::list< OUString >& rNames ) const +{ + rNames.clear(); + + PrintFont* pFont = getFont( nFont ); + if( pFont && pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + ByteString aFile( getFontFile( pFont ) ); + TrueTypeFont* pTTFont; + if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + NameRecord* pNameRecords = NULL; + int nNameRecords = GetTTNameRecords( pTTFont, &pNameRecords ); + for( int i = 0; i < nNameRecords; i++ ) + { + if( pNameRecords[i].nameID != 1 ) // family name + continue; + + OUString aFamily( convertTrueTypeName( pNameRecords+i ) ); + if( aFamily.getLength() + && + m_pAtoms->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ) != pFont->m_nFamilyName + ) + { + rNames.push_back( aFamily ); + } + } + + if( nNameRecords ) + DisposeNameRecords( pNameRecords, nNameRecords ); + CloseTTFont( pTTFont ); + } + } + return rNames.begin() != rNames.end(); +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::createFontSubset( + fontID nFont, + const OUString& rOutFile, + sal_Int32* pGlyphIDs, + sal_uInt8* pNewEncoding, + sal_Int32* pWidths, + int nGlyphs, + bool bVertical + ) +{ + PrintFont* pFont = getFont( nFont ); + if( !pFont || pFont->m_eType != fonttype::TrueType ) + return false; + + OUString aSysPath; + if( osl_File_E_None != osl_getSystemPathFromFileURL( rOutFile.pData, &aSysPath.pData ) ) + return false; + + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + ByteString aFromFile = getFontFile( pFont ); + ByteString aToFile( OUStringToOString( aSysPath, aEncoding ) ); + + sal_uInt8 pEnc[256]; + sal_uInt16 pGID[256]; + sal_uInt8 pOldIndex[256]; + + memset( pEnc, 0, sizeof( pEnc ) ); + memset( pGID, 0, sizeof( pGID ) ); + memset( pOldIndex, 0, sizeof( pOldIndex ) ); + int nChar = 1; + int i; + for( i = 0; i < nGlyphs; i++ ) + { + if( pNewEncoding[i] == 0 ) + { + pOldIndex[ 0 ] = i; + } + else + { + DBG_ASSERT( !(pGlyphIDs[i] & 0x007f0000), "overlong glyph id" ); + DBG_ASSERT( (int)pNewEncoding[i] < nGlyphs, "encoding wrong" ); + DBG_ASSERT( pEnc[pNewEncoding[i]] == 0 && pGID[pNewEncoding[i]] == 0, "duplicate encoded glyph" ); + pEnc[ pNewEncoding[i] ] = pNewEncoding[i]; + pGID[ pNewEncoding[i] ] = (sal_uInt16)pGlyphIDs[ i ]; + pOldIndex[ pNewEncoding[i] ] = i; + nChar++; + } + } + nGlyphs = nChar; // either input value or increased by one + + if( nGlyphs > 256 ) + return false; + + TrueTypeFont* pTTFont = NULL; + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK ) + return false; + + TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont, + pGID, + nGlyphs, + bVertical ? 1 : 0 ); + if( pMetrics ) + { + for( i = 0; i < nGlyphs; i++ ) + pWidths[pOldIndex[i]] = pMetrics[i].adv; + free( pMetrics ); + } + else + { + CloseTTFont( pTTFont ); + return false; + } + + bool bSuccess = ( SF_OK == CreateTTFromTTGlyphs( pTTFont, + aToFile.GetBuffer(), + pGID, + pEnc, + nGlyphs, + 0, + NULL, + 0 ) ); + CloseTTFont( pTTFont ); + + return bSuccess; +} + +void PrintFontManager::getGlyphWidths( fontID nFont, + bool bVertical, + std::vector< sal_Int32 >& rWidths, + std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc ) +{ + PrintFont* pFont = getFont( nFont ); + if( !pFont || + (pFont->m_eType != fonttype::TrueType && pFont->m_eType != fonttype::Type1) ) + return; + if( pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFont* pTTFont = NULL; + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + ByteString aFromFile = getFontFile( pFont ); + if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK ) + return; + int nGlyphs = GetTTGlyphCount( pTTFont ); + if( nGlyphs > 0 ) + { + rWidths.resize(nGlyphs); + std::vector<sal_uInt16> aGlyphIds(nGlyphs); + for( int i = 0; i < nGlyphs; i++ ) + aGlyphIds[i] = sal_uInt16(i); + TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont, + &aGlyphIds[0], + nGlyphs, + bVertical ? 1 : 0 ); + if( pMetrics ) + { + for( int i = 0; i< nGlyphs; i++ ) + rWidths[i] = pMetrics[i].adv; + free( pMetrics ); + rUnicodeEnc.clear(); + } + } + CloseTTFont( pTTFont ); + } + else if( pFont->m_eType == fonttype::Type1 ) + { + if( ! pFont->m_aEncodingVector.size() ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true ); + if( pFont->m_pMetrics ) + { + rUnicodeEnc.clear(); + rWidths.clear(); + rWidths.reserve( pFont->m_pMetrics->m_aMetrics.size() ); + for( std::hash_map< int, CharacterMetric >::const_iterator it = + pFont->m_pMetrics->m_aMetrics.begin(); + it != pFont->m_pMetrics->m_aMetrics.end(); ++it ) + { + if( (it->first & 0x00010000) == 0 || bVertical ) + { + rUnicodeEnc[ sal_Unicode(it->first & 0x0000ffff) ] = sal_uInt32(rWidths.size()); + rWidths.push_back( it->second.width ); + } + } + } + } +} + +// ------------------------------------------------------------------------- + +const std::map< sal_Unicode, sal_Int32 >* PrintFontManager::getEncodingMap( fontID nFont, const std::map< sal_Unicode, rtl::OString >** pNonEncoded ) const +{ + PrintFont* pFont = getFont( nFont ); + if( !pFont || + (pFont->m_eType != fonttype::Type1 && pFont->m_eType != fonttype::Builtin) + ) + return NULL; + + if( ! pFont->m_aEncodingVector.size() ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true ); + + if( pNonEncoded ) + *pNonEncoded = pFont->m_aNonEncoded.size() ? &pFont->m_aNonEncoded : NULL; + + return pFont->m_aEncodingVector.size() ? &pFont->m_aEncodingVector : NULL; +} + +// ------------------------------------------------------------------------- + +std::list< OString > PrintFontManager::getAdobeNameFromUnicode( sal_Unicode aChar ) const +{ + std::pair< std::hash_multimap< sal_Unicode, rtl::OString >::const_iterator, + std::hash_multimap< sal_Unicode, rtl::OString >::const_iterator > range + = m_aUnicodeToAdobename.equal_range( aChar ); + + std::list< OString > aRet; + for( ; range.first != range.second; ++range.first ) + aRet.push_back( range.first->second ); + + if( aRet.begin() == aRet.end() && aChar != 0 ) + { + sal_Char aBuf[8]; + sal_Int32 nChars = snprintf( (char*)aBuf, sizeof( aBuf ), "uni%.4hX", aChar ); + aRet.push_back( OString( aBuf, nChars ) ); + } + + return aRet; +} + +// ------------------------------------------------------------------------- +std::list< sal_Unicode > PrintFontManager::getUnicodeFromAdobeName( const rtl::OString& rName ) const +{ + std::pair< std::hash_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator, + std::hash_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator > range + = m_aAdobenameToUnicode.equal_range( rName ); + + std::list< sal_Unicode > aRet; + for( ; range.first != range.second; ++range.first ) + aRet.push_back( range.first->second ); + + if( aRet.begin() == aRet.end() ) + { + if( rName.getLength() == 7 && rName.indexOf( "uni" ) == 0 ) + { + sal_Unicode aCode = (sal_Unicode)rName.copy( 3 ).toInt32( 16 ); + aRet.push_back( aCode ); + } + } + + return aRet; +} + +// ------------------------------------------------------------------------- +namespace +{ + OUString getString( const Any& rAny ) + { + OUString aStr; + rAny >>= aStr; + return aStr; + } + bool getBool( const Any& rAny ) + { + sal_Bool bBool = sal_False; + rAny >>= bBool; + return static_cast<bool>(bBool); + } + sal_Int32 getInt( const Any& rAny ) + { + sal_Int32 n = 0; + rAny >>= n; + return n; + } +} +bool PrintFontManager::readOverrideMetrics() +{ + if( ! m_aOverrideFonts.empty() ) + return false; + + Reference< XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); + if( !xFact.is() ) + return false; + Reference< XMaterialHolder > xMat( + xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.psprint.CompatMetricOverride" ) ) ), + UNO_QUERY ); + if( !xMat.is() ) + return false; + + Any aAny( xMat->getMaterial() ); + Sequence< Any > aOverrideFonts; + if( ! (aAny >>= aOverrideFonts ) ) + return false; + sal_Int32 nFonts = aOverrideFonts.getLength(); + for( sal_Int32 i = 0; i < nFonts; i++ ) + { + Sequence< NamedValue > aMetrics; + if( ! (aOverrideFonts.getConstArray()[i] >>= aMetrics) ) + continue; + BuiltinFont* pFont = new BuiltinFont(); + pFont->m_nDirectory = 0; + pFont->m_bUserOverride = false; + pFont->m_eEmbeddedbitmap = fcstatus::isunset; + pFont->m_eAntialias = fcstatus::isunset; + pFont->m_pMetrics = new PrintFontMetrics; + memset( pFont->m_pMetrics->m_aPages, 0xff, sizeof( pFont->m_pMetrics->m_aPages ) ); + pFont->m_pMetrics->m_bKernPairsQueried = true; + sal_Int32 nProps = aMetrics.getLength(); + const NamedValue* pProps = aMetrics.getConstArray(); + for( sal_Int32 n = 0; n < nProps; n++ ) + { + if( pProps[n].Name.equalsAscii( "FamilyName" ) ) + pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, + getString(pProps[n].Value), + sal_True ); + else if( pProps[n].Name.equalsAscii( "PSName" ) ) + pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, + getString(pProps[n].Value), + sal_True ); + else if( pProps[n].Name.equalsAscii( "StyleName" ) ) + pFont->m_aStyleName = getString(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "Italic" ) ) + pFont->m_eItalic = static_cast<italic::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAscii( "Width" ) ) + pFont->m_eWidth = static_cast<width::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAscii( "Weight" ) ) + pFont->m_eWeight = static_cast<weight::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAscii( "Pitch" ) ) + pFont->m_ePitch = static_cast<pitch::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAscii( "Encoding" ) ) + pFont->m_aEncoding = static_cast<rtl_TextEncoding>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAscii( "FontEncodingOnly" ) ) + pFont->m_bFontEncodingOnly = getBool(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "GlobalMetricXWidth" ) ) + pFont->m_aGlobalMetricX.width = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "GlobalMetricXHeight" ) ) + pFont->m_aGlobalMetricX.height = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "GlobalMetricYWidth" ) ) + pFont->m_aGlobalMetricY.width = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "GlobalMetricYHeight" ) ) + pFont->m_aGlobalMetricY.height = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "Ascend" ) ) + pFont->m_nAscend = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "Descend" ) ) + pFont->m_nDescend = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "Leading" ) ) + pFont->m_nLeading = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "XMin" ) ) + pFont->m_nXMin = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "YMin" ) ) + pFont->m_nYMin = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "XMax" ) ) + pFont->m_nXMax = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "YMax" ) ) + pFont->m_nYMax = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "VerticalSubstitutes" ) ) + pFont->m_bHaveVerticalSubstitutedGlyphs = getBool(pProps[n].Value); + else if( pProps[n].Name.equalsAscii( "EncodingVector" ) ) + { + Sequence< NamedValue > aEncoding; + pProps[n].Value >>= aEncoding; + sal_Int32 nEnc = aEncoding.getLength(); + const NamedValue* pEnc = aEncoding.getConstArray(); + for( sal_Int32 m = 0; m < nEnc; m++ ) + { + sal_Unicode cCode = *pEnc[m].Name.getStr(); + sal_Int32 nGlyph = getInt(pEnc[m].Value); + pFont->m_aEncodingVector[ cCode ] = nGlyph; + } + } + else if( pProps[n].Name.equalsAscii( "NonEncoded" ) ) + { + Sequence< NamedValue > aEncoding; + pProps[n].Value >>= aEncoding; + sal_Int32 nEnc = aEncoding.getLength(); + const NamedValue* pEnc = aEncoding.getConstArray(); + for( sal_Int32 m = 0; m < nEnc; m++ ) + { + sal_Unicode cCode = *pEnc[m].Name.getStr(); + OUString aGlyphName( getString(pEnc[m].Value) ); + pFont->m_aNonEncoded[ cCode ] = OUStringToOString(aGlyphName,RTL_TEXTENCODING_ASCII_US); + } + } + else if( pProps[n].Name.equalsAscii( "CharacterMetrics" ) ) + { + // fill pFont->m_pMetrics->m_aMetrics + // expect triples of int: int -> CharacterMetric.{ width, height } + Sequence< sal_Int32 > aSeq; + pProps[n].Value >>= aSeq; + sal_Int32 nInts = aSeq.getLength(); + const sal_Int32* pInts = aSeq.getConstArray(); + for( sal_Int32 m = 0; m < nInts; m+=3 ) + { + pFont->m_pMetrics->m_aMetrics[ pInts[m] ].width = static_cast<short int>(pInts[m+1]); + pFont->m_pMetrics->m_aMetrics[ pInts[m] ].height = static_cast<short int>(pInts[m+2]); + } + } + else if( pProps[n].Name.equalsAscii( "XKernPairs" ) ) + { + // fill pFont->m_pMetrics->m_aXKernPairs + // expection name: <unicode1><unicode2> value: ((height << 16)| width) + Sequence< NamedValue > aKern; + pProps[n].Value >>= aKern; + KernPair aPair; + const NamedValue* pVals = aKern.getConstArray(); + int nPairs = aKern.getLength(); + for( int m = 0; m < nPairs; m++ ) + { + if( pVals[m].Name.getLength() == 2 ) + { + aPair.first = pVals[m].Name.getStr()[0]; + aPair.second = pVals[m].Name.getStr()[1]; + sal_Int32 nKern = getInt( pVals[m].Value ); + aPair.kern_x = static_cast<short int>(nKern & 0xffff); + aPair.kern_y = static_cast<short int>((sal_uInt32(nKern) >> 16) & 0xffff); + pFont->m_pMetrics->m_aXKernPairs.push_back( aPair ); + } + } + } + } + // sanity check + if( pFont->m_nPSName && + pFont->m_nFamilyName && + ! pFont->m_pMetrics->m_aMetrics.empty() ) + { + m_aOverrideFonts.push_back( m_nNextFontID ); + m_aFonts[ m_nNextFontID++ ] = pFont; + } + else + { + DBG_ASSERT( 0, "override font failed" ); + delete pFont; + } + } + + return true; +} diff --git a/vcl/unx/source/fontmanager/helper.cxx b/vcl/unx/source/fontmanager/helper.cxx new file mode 100644 index 000000000000..2f3821eac7d1 --- /dev/null +++ b/vcl/unx/source/fontmanager/helper.cxx @@ -0,0 +1,407 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: helper.cxx,v $ + * $Revision: 1.35 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <cstring> +#include <sys/stat.h> +#include <unistd.h> +#include <limits.h> + +#include "vcl/helper.hxx" +#include "vcl/ppdparser.hxx" +#include "tools/string.hxx" +#include "tools/urlobj.hxx" +#include "osl/file.hxx" +#include "osl/process.h" +#include "rtl/bootstrap.hxx" + +using namespace rtl; + +namespace psp { + +OUString getOfficePath( enum whichOfficePath ePath ) +{ + static OUString aNetPath; + static OUString aUserPath; + static OUString aConfigPath; + static OUString aEmpty; + static bool bOnce = false; + + if( ! bOnce ) + { + bOnce = true; + OUString aIni; + Bootstrap::get( OUString( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aIni ); + aIni += OUString( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) ); + Bootstrap aBootstrap( aIni ); + aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "CustomDataUrl" ) ), aConfigPath ); + aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "BaseInstallation" ) ), aNetPath ); + aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "UserInstallation" ) ), aUserPath ); + OUString aUPath = aUserPath; + + if( ! aConfigPath.compareToAscii( "file://", 7 ) ) + { + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aConfigPath.pData, &aSysPath.pData ) == osl_File_E_None ) + aConfigPath = aSysPath; + } + if( ! aNetPath.compareToAscii( "file://", 7 ) ) + { + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aNetPath.pData, &aSysPath.pData ) == osl_File_E_None ) + aNetPath = aSysPath; + } + if( ! aUserPath.compareToAscii( "file://", 7 ) ) + { + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aUserPath.pData, &aSysPath.pData ) == osl_File_E_None ) + aUserPath = aSysPath; + } + // ensure user path exists + aUPath += OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/psprint" ) ); + #if OSL_DEBUG_LEVEL > 1 + oslFileError eErr = + #endif + osl_createDirectoryPath( aUPath.pData, NULL, NULL ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "try to create \"%s\" = %d\n", OUStringToOString( aUPath, RTL_TEXTENCODING_UTF8 ).getStr(), eErr ); + #endif + } + + switch( ePath ) + { + case ConfigPath: return aConfigPath; + case NetPath: return aNetPath; + case UserPath: return aUserPath; + } + return aEmpty; +} + +static OString getEnvironmentPath( const char* pKey ) +{ + OString aPath; + + const char* pValue = getenv( pKey ); + if( pValue && *pValue ) + { + aPath = OString( pValue ); + } + return aPath; +} + +} // namespace psp + +void psp::getPrinterPathList( std::list< OUString >& rPathList, const char* pSubDir ) +{ + rPathList.clear(); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + + OUStringBuffer aPathBuffer( 256 ); + + // append net path + aPathBuffer.append( getOfficePath( psp::NetPath ) ); + if( aPathBuffer.getLength() ) + { + aPathBuffer.appendAscii( "/share/psprint" ); + if( pSubDir ) + { + aPathBuffer.append( sal_Unicode('/') ); + aPathBuffer.appendAscii( pSubDir ); + } + rPathList.push_back( aPathBuffer.makeStringAndClear() ); + } + // append user path + aPathBuffer.append( getOfficePath( psp::UserPath ) ); + if( aPathBuffer.getLength() ) + { + aPathBuffer.appendAscii( "/user/psprint" ); + if( pSubDir ) + { + aPathBuffer.append( sal_Unicode('/') ); + aPathBuffer.appendAscii( pSubDir ); + } + rPathList.push_back( aPathBuffer.makeStringAndClear() ); + } + + OString aPath( getEnvironmentPath("SAL_PSPRINT") ); + sal_Int32 nIndex = 0; + do + { + OString aDir( aPath.getToken( 0, ':', nIndex ) ); + if( ! aDir.getLength() ) + continue; + + if( pSubDir ) + { + aDir += "/"; + aDir += pSubDir; + } + struct stat aStat; + if( stat( aDir.getStr(), &aStat ) || ! S_ISDIR( aStat.st_mode ) ) + continue; + + rPathList.push_back( OStringToOUString( aDir, aEncoding ) ); + } while( nIndex != -1 ); + + #ifdef SYSTEM_PPD_DIR + if( pSubDir && rtl_str_compare( pSubDir, PRINTER_PPDDIR ) == 0 ) + { + rPathList.push_back( rtl::OStringToOUString( rtl::OString( SYSTEM_PPD_DIR ), RTL_TEXTENCODING_UTF8 ) ); + } + #endif + + if( rPathList.empty() ) + { + // last resort: next to program file (mainly for setup) + OUString aExe; + if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None ) + { + INetURLObject aDir( aExe ); + aDir.removeSegment(); + aExe = aDir.GetMainURL( INetURLObject::NO_DECODE ); + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aExe.pData, &aSysPath.pData ) == osl_File_E_None ) + { + rPathList.push_back( aSysPath ); + } + } + } +} + +OUString psp::getFontPath() +{ + static OUString aPath; + + if( ! aPath.getLength() ) + { + OUStringBuffer aPathBuffer( 512 ); + + OUString aConfigPath( getOfficePath( psp::ConfigPath ) ); + OUString aNetPath( getOfficePath( psp::NetPath ) ); + OUString aUserPath( getOfficePath( psp::UserPath ) ); + if( aConfigPath.getLength() ) + { + // #i53530# Path from CustomDataUrl will completely + // replace net and user paths if the path exists + aPathBuffer.append(aConfigPath); + aPathBuffer.appendAscii("/share/fonts"); + // check existance of config path + struct stat aStat; + if( 0 != stat( OUStringToOString( aPathBuffer.makeStringAndClear(), osl_getThreadTextEncoding() ).getStr(), &aStat ) + || ! S_ISDIR( aStat.st_mode ) ) + aConfigPath = OUString(); + else + { + aPathBuffer.append(aConfigPath); + aPathBuffer.appendAscii("/share/fonts"); + } + } + if( aConfigPath.getLength() == 0 ) + { + if( aNetPath.getLength() ) + { + aPathBuffer.append( aNetPath ); + aPathBuffer.appendAscii( "/share/fonts/truetype;"); + aPathBuffer.append( aNetPath ); + aPathBuffer.appendAscii( "/share/fonts/type1;" ); + } + if( aUserPath.getLength() ) + { + aPathBuffer.append( aUserPath ); + aPathBuffer.appendAscii( "/user/fonts" ); + } + } + OString aEnvPath( getEnvironmentPath( "SAL_FONTPATH_PRIVATE" ) ); + if( aEnvPath.getLength() ) + { + aPathBuffer.append( sal_Unicode(';') ); + aPathBuffer.append( OStringToOUString( aEnvPath, osl_getThreadTextEncoding() ) ); + } + + aPath = aPathBuffer.makeStringAndClear(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "initializing font path to \"%s\"\n", OUStringToOString( aPath, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + } + return aPath; +} + +bool psp::convertPfbToPfa( ::osl::File& rInFile, ::osl::File& rOutFile ) +{ + static unsigned char hexDigits[] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + bool bSuccess = true; + bool bEof = false; + unsigned char buffer[256]; + sal_uInt64 nRead; + sal_uInt64 nOrgPos = 0; + rInFile.getPos( nOrgPos ); + + while( bSuccess && ! bEof ) + { + // read leading bytes + bEof = ! rInFile.read( buffer, 6, nRead ) && nRead == 6 ? false : true; + unsigned int nType = buffer[ 1 ]; + unsigned int nBytesToRead = buffer[2] | buffer[3] << 8 | buffer[4] << 16 | buffer[5] << 24; + if( buffer[0] != 0x80 ) // test for pfb m_agic number + { + // this migt be a pfa font already + sal_uInt64 nWrite = 0; + if( ! rInFile.read( buffer+6, 9, nRead ) && nRead == 9 && + ( ! std::strncmp( (char*)buffer, "%!FontType1-", 12 ) || + ! std::strncmp( (char*)buffer, "%!PS-AdobeFont-", 15 ) ) ) + { + if( rOutFile.write( buffer, 15, nWrite ) || nWrite != 15 ) + bSuccess = false; + while( bSuccess && + ! rInFile.read( buffer, sizeof( buffer ), nRead ) && + nRead != 0 ) + { + if( rOutFile.write( buffer, nRead, nWrite ) || + nWrite != nRead ) + bSuccess = false; + } + bEof = true; + } + else + bSuccess = false; + } + else if( nType == 1 || nType == 2 ) + { + unsigned char* pBuffer = new unsigned char[ nBytesToRead+1 ]; + + if( ! rInFile.read( pBuffer, nBytesToRead, nRead ) && nRead == nBytesToRead ) + { + if( nType == 1 ) + { + // ascii data, convert dos lineends( \r\n ) and + // m_ac lineends( \r ) to \n + unsigned char * pWriteBuffer = new unsigned char[ nBytesToRead ]; + unsigned int nBytesToWrite = 0; + for( unsigned int i = 0; i < nBytesToRead; i++ ) + { + if( pBuffer[i] != '\r' ) + pWriteBuffer[ nBytesToWrite++ ] = pBuffer[i]; + else if( pBuffer[ i+1 ] == '\n' ) + { + i++; + pWriteBuffer[ nBytesToWrite++ ] = '\n'; + } + else + pWriteBuffer[ nBytesToWrite++ ] = '\n'; + } + if( rOutFile.write( pWriteBuffer, nBytesToWrite, nRead ) || nRead != nBytesToWrite ) + bSuccess = false; + + delete [] pWriteBuffer; + } + else + { + // binary data + unsigned int nBuffer = 0; + for( unsigned int i = 0; i < nBytesToRead && bSuccess; i++ ) + { + buffer[ nBuffer++ ] = hexDigits[ pBuffer[ i ] >> 4 ]; + buffer[ nBuffer++ ] = hexDigits[ pBuffer[ i ] & 15 ]; + if( nBuffer >= 80 ) + { + buffer[ nBuffer++ ] = '\n'; + if( rOutFile.write( buffer, nBuffer, nRead ) || nRead != nBuffer ) + bSuccess = false; + nBuffer = 0; + } + } + if( nBuffer > 0 && bSuccess ) + { + buffer[ nBuffer++ ] = '\n'; + if( rOutFile.write( buffer, nBuffer, nRead ) || nRead != nBuffer ) + bSuccess = false; + } + } + } + else + bSuccess = false; + + delete [] pBuffer; + } + else if( nType == 3 ) + bEof = true; + else + bSuccess = false; + } + + return bSuccess; +} + +void psp::normPath( OString& rPath ) +{ + char buf[PATH_MAX]; + + ByteString aPath( rPath ); + + // double slashes and slash at end are probably + // removed by realpath anyway, but since this runs + // on many different platforms let's play it safe + while( aPath.SearchAndReplace( "//", "/" ) != STRING_NOTFOUND ) + ; + if( aPath.Len() > 0 && aPath.GetChar( aPath.Len()-1 ) == '/' ) + aPath.Erase( aPath.Len()-1 ); + + if( ( aPath.Search( "./" ) != STRING_NOTFOUND || + aPath.Search( "~" ) != STRING_NOTFOUND ) + && realpath( aPath.GetBuffer(), buf ) ) + { + rPath = buf; + } + else + { + rPath = aPath; + } +} + +void psp::splitPath( OString& rPath, OString& rDir, OString& rBase ) +{ + normPath( rPath ); + sal_Int32 nIndex = rPath.lastIndexOf( '/' ); + if( nIndex > 0 ) + rDir = rPath.copy( 0, nIndex ); + else if( nIndex == 0 ) // root dir + rDir = rPath.copy( 0, 1 ); + if( rPath.getLength() > nIndex+1 ) + rBase = rPath.copy( nIndex+1 ); +} + + diff --git a/vcl/unx/source/fontmanager/makefile.mk b/vcl/unx/source/fontmanager/makefile.mk new file mode 100644 index 000000000000..c1d1fde15de3 --- /dev/null +++ b/vcl/unx/source/fontmanager/makefile.mk @@ -0,0 +1,76 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.11 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +ENABLE_EXCEPTIONS=TRUE +PRJNAME=vcl +TARGET=fontman + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +CFLAGS+= -I..$/fontsubset +INCDEPN+= -I..$/fontsubset + +.IF "$(ENABLE_FONTCONFIG)" != "" +CDEFS += -DENABLE_FONTCONFIG +.ENDIF + +CFLAGS+=$(FREETYPE_CFLAGS) + + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"=="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"=="aqua" + +SLOFILES=\ + $(SLO)$/fontmanager.obj \ + $(SLO)$/fontcache.obj \ + $(SLO)$/fontconfig.obj \ + $(SLO)$/helper.obj \ + $(SLO)$/parseAFM.obj + +.IF "$(OS)$(CPU)"=="SOLARISI" +NOOPTFILES=$(SLO)$/fontmanager.obj +.ENDIF + +.ENDIF + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/vcl/unx/source/fontmanager/parseAFM.cxx b/vcl/unx/source/fontmanager/parseAFM.cxx new file mode 100644 index 000000000000..0ac4754d4bd5 --- /dev/null +++ b/vcl/unx/source/fontmanager/parseAFM.cxx @@ -0,0 +1,1569 @@ +/* + * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* + * Changes made for OpenOffice.org + * + * 10/24/2000 pl - changed code to compile with c++-compilers + * - added namespace to avoid symbol clashes + * - replaced BOOL by bool + * - added function to free space allocated by parseFile + * 10/26/2000 pl - added additional keys + * - added ability to parse slightly broken files + * - added charwidth member to GlobalFontInfo + * 04/26/2001 pl - added OpenOffice header + * 10/19/2005 pl - performance increase: + * - fread file in one pass + * - replace file io by buffer access + * 10/20/2005 pl - performance increase: + * - use one table lookup in token() routine + * instead of many conditions + * - return token length in toke() routine + * - use hash lookup instead of binary search + * in recognize() routine + */ + +/************************************************************************* + * + * $RCSfile: parseAFM.cxx,v $ + * + * $Revision: 1.11 $ + * + * last change: $Author: rt $ $Date: 2008-01-29 16:08:31 $ + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +/* parseAFM.c + * + * This file is used in conjuction with the parseAFM.h header file. + * This file contains several procedures that are used to parse AFM + * files. It is intended to work with an application program that needs + * font metric information. The program can be used as is by making a + * procedure call to "parseFile" (passing in the expected parameters) + * and having it fill in a data structure with the data from the + * AFM file, or an application developer may wish to customize this + * code. + * + * There is also a file, parseAFMclient.c, that is a sample application + * showing how to call the "parseFile" procedure and how to use the data + * after "parseFile" has returned. + * + * Please read the comments in parseAFM.h and parseAFMclient.c. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed bug of not allocating extra byte for string duplication + * - fixed typos + * modified: DSM Tue Apr 3 11:18:34 PDT 1990 + * - added free(ident) at end of parseFile routine + * modified: DSM Tue Jun 19 10:16:29 PDT 1990 + * - changed (width == 250) to (width = 250) in initializeArray + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <math.h> + +#include "parseAFM.hxx" +#include "vcl/strhelper.hxx" + +#include "rtl/alloc.h" + +#define lineterm EOL /* line terminating character */ +#define normalEOF 1 /* return code from parsing routines used only */ +/* in this module */ +#define Space "space" /* used in string comparison to look for the width */ +/* of the space character to init the widths array */ +#define False "false" /* used in string comparison to check the value of */ +/* boolean keys (e.g. IsFixedPitch) */ + +#define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0) + +namespace psp { + +class FileInputStream +{ + char* m_pMemory; + unsigned int m_nPos; + unsigned int m_nLen; + public: + FileInputStream( const char* pFilename ); + ~FileInputStream(); + + int getChar() { return (m_nPos < m_nLen) ? int(m_pMemory[m_nPos++]) : -1; } + void ungetChar() + { + if( m_nPos > 0 ) + m_nPos--; + } + unsigned int tell() const { return m_nPos; } + void seek( unsigned int nPos ) + // NOTE: do not check input data since only results of tell() + // get seek()ed in this file + { m_nPos = nPos; } +}; + +FileInputStream::FileInputStream( const char* pFilename ) : + m_pMemory( NULL ), + m_nPos( 0 ), + m_nLen( 0 ) +{ + struct stat aStat; + if( ! stat( pFilename, &aStat ) && + S_ISREG( aStat.st_mode ) && + aStat.st_size > 0 + ) + { + FILE* fp = fopen( pFilename, "r" ); + if( fp ) + { + m_pMemory = (char*)rtl_allocateMemory( aStat.st_size ); + m_nLen = (unsigned int)fread( m_pMemory, 1, aStat.st_size, fp ); + fclose( fp ); + } + } +} + +FileInputStream::~FileInputStream() +{ + rtl_freeMemory( m_pMemory ); +} + +/*************************** GLOBALS ***********************/ +/* "shorts" for fast case statement + * The values of each of these enumerated items correspond to an entry in the + * table of strings defined below. Therefore, if you add a new string as + * new keyword into the keyStrings table, you must also add a corresponding + * parseKey AND it MUST be in the same position! + * + * IMPORTANT: since the sorting algorithm is a binary search, the strings of + * keywords must be placed in lexicographical order, below. [Therefore, the + * enumerated items are not necessarily in lexicographical order, depending + * on the name chosen. BUT, they must be placed in the same position as the + * corresponding key string.] The NOPE shall remain in the last position, + * since it does not correspond to any key string, and it is used in the + * "recognize" procedure to calculate how many possible keys there are. + */ + +// some metrics have Ascent, Descent instead Ascender, Descender or Em +// which is not allowed per afm spcification, but let us handle +// this gently +enum parseKey { + ASCENDER, ASCENT, CHARBBOX, CODE, COMPCHAR, CODEHEX, CAPHEIGHT, CHARWIDTH, CHARACTERSET, CHARACTERS, COMMENT, + DESCENDER, DESCENT, EM, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, ENDDIRECTION, + ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, + FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISBASEFONT, ISFIXEDPITCH, + ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, MAPPINGSCHEME, METRICSSETS, CHARNAME, + NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, STARTDIRECTION, + STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, + STARTTRACKKERN, STDHW, STDVW, TRACKKERN, UNDERLINEPOSITION, + UNDERLINETHICKNESS, VVECTOR, VERSION, XYWIDTH, X0WIDTH, XWIDTH, WEIGHT, XHEIGHT, + NOPE +}; + +/*************************** PARSING ROUTINES **************/ + +/*************************** token *************************/ + +/* A "AFM file Conventions" tokenizer. That means that it will + * return the next token delimited by white space. See also + * the `linetoken' routine, which does a similar thing but + * reads all tokens until the next end-of-line. + */ + +// token white space is ' ', '\n', '\r', ',', '\t', ';' +static const bool is_white_Array[ 256 ] = +{ false, false, false, false, false, false, false, false, // 0-7 + false, true, true, false, false, true, false, false, // 8-15 + false, false, false, false, false, false, false, false, // 16-23 + false, false, false, false, false, false, false, false, // 24-31 + true, false, false, false, false, false, false, false, // 32-39 + false, false, false, false, true, false, false, false, // 40-47 + false, false, false, false, false, false, false, false, // 48-55 + false, false, false, true, false, false, false, false, // 56-63 + + false, false, false, false, false, false, false, false, // 64 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 127 + + false, false, false, false, false, false, false, false, // 128 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 191 + + false, false, false, false, false, false, false, false, // 192 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 255 +}; +// token delimiters are ' ', '\n', '\r', '\t', ':', ';' +static const bool is_delimiter_Array[ 256 ] = +{ false, false, false, false, false, false, false, false, // 0-7 + false, true, true, false, false, true, false, false, // 8-15 + false, false, false, false, false, false, false, false, // 16-23 + false, false, false, false, false, false, false, false, // 24-31 + true, false, false, false, false, false, false, false, // 32-39 + false, false, false, false, false, false, false, false, // 40-47 + false, false, false, false, false, false, false, false, // 48-55 + false, false, true, true, false, false, false, false, // 56-63 + + false, false, false, false, false, false, false, false, // 64 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 127 + + false, false, false, false, false, false, false, false, // 128 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 191 + + false, false, false, false, false, false, false, false, // 192 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 255 +}; +static char *token( FileInputStream* stream, int& rLen ) +{ + static char ident[MAX_NAME]; /* storage buffer for keywords */ + + int ch, idx; + + /* skip over white space */ + // relies on EOF = -1 + while( is_white_Array[ (ch = stream->getChar()) & 255 ] ) + ; + + idx = 0; + while( ch != -1 && ! is_delimiter_Array[ ch & 255 ] && idx < MAX_NAME-1 ) + { + ident[idx++] = ch; + ch = stream->getChar(); + } + + if (ch == -1 && idx < 1) return ((char *)NULL); + if (idx >= 1 && ch != ':' && ch != -1) stream->ungetChar(); + if (idx < 1 ) ident[idx++] = ch; /* single-character token */ + ident[idx] = 0; + rLen = idx; + + return(ident); /* returns pointer to the token */ + +} /* token */ + + +/*************************** linetoken *************************/ + +/* "linetoken" will get read all tokens until the EOL character from + * the given stream. This is used to get any arguments that can be + * more than one word (like Comment lines and FullName). + */ + +static char *linetoken( FileInputStream* stream ) +{ + static char ident[MAX_NAME]; /* storage buffer for keywords */ + int ch, idx; + + while ((ch = stream->getChar()) == ' ' || ch == '\t' ) ; + + idx = 0; + while (ch != -1 && ch != lineterm && ch != '\r' && idx < MAX_NAME-1 ) + { + ident[idx++] = ch; + ch = stream->getChar(); + } /* while */ + + stream->ungetChar(); + ident[idx] = 0; + + return(ident); /* returns pointer to the token */ + +} /* linetoken */ + + +/*************************** recognize *************************/ + +/* This function tries to match a string to a known list of + * valid AFM entries (check the keyStrings array above). + * "ident" contains everything from white space through the + * next space, tab, or ":" character. + * + * The algorithm is a standard Knuth binary search. + */ +#include "afm_hash.cpp" + +static inline enum parseKey recognize( register char* ident, int len) +{ + const hash_entry* pEntry = AfmKeywordHash::in_word_set( ident, len ); + return pEntry ? pEntry->eKey : NOPE; + +} /* recognize */ + + +/************************* parseGlobals *****************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "StartCharMetrics" keyword, which essentially marks the + * end of the Global Font Information and the beginning of the character + * metrics information. + * + * If the caller of "parseFile" specified that it wanted the Global + * Font Information (as defined by the "AFM file Specification" + * document), then that information will be stored in the returned + * data structure. + * + * Any Global Font Information entries that are not found in a + * given file, will have the usual default initialization value + * for its type (i.e. entries of type int will be 0, etc). + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseGlobals( FileInputStream* fp, register GlobalFontInfo* gfi ) +{ + bool cont = true, save = (gfi != NULL); + int error = ok; + register char *keyword; + int direction = -1; + int tokenlen; + + while (cont) + { + keyword = token(fp, tokenlen); + + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Global Font info section */ + /* without saving any of the data */ + switch (recognize(keyword, tokenlen)) + { + case STARTCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire global font info section, */ + /* saving the data */ + switch(recognize(keyword, tokenlen)) + { + case STARTFONTMETRICS: + keyword = token(fp,tokenlen); + gfi->afmVersion = strdup( keyword ); + break; + case COMMENT: + keyword = linetoken(fp); + break; + case FONTNAME: + keyword = token(fp, tokenlen); + gfi->fontName = strdup( keyword ); + break; + case ENCODINGSCHEME: + keyword = token(fp, tokenlen); + gfi->encodingScheme = strdup( keyword ); + break; + case FULLNAME: + keyword = linetoken(fp); + gfi->fullName = strdup( keyword ); + break; + case FAMILYNAME: + keyword = linetoken(fp); + gfi->familyName = strdup( keyword ); + break; + case WEIGHT: + keyword = token(fp, tokenlen); + gfi->weight = strdup( keyword ); + break; + case ITALICANGLE: + keyword = token(fp,tokenlen); + gfi->italicAngle = StringToDouble( keyword ); + break; + case ISFIXEDPITCH: + keyword = token(fp,tokenlen); + if (MATCH(keyword, False)) + gfi->isFixedPitch = 0; + else + gfi->isFixedPitch = 1; + break; + case UNDERLINEPOSITION: + keyword = token(fp,tokenlen); + gfi->underlinePosition = atoi(keyword); + break; + case UNDERLINETHICKNESS: + keyword = token(fp,tokenlen); + gfi->underlineThickness = atoi(keyword); + break; + case VERSION: + keyword = token(fp,tokenlen); + gfi->version = strdup( keyword ); + break; + case NOTICE: + keyword = linetoken(fp); + gfi->notice = strdup( keyword ); + break; + case FONTBBOX: + keyword = token(fp,tokenlen); + gfi->fontBBox.llx = atoi(keyword); + keyword = token(fp,tokenlen); + gfi->fontBBox.lly = atoi(keyword); + keyword = token(fp,tokenlen); + gfi->fontBBox.urx = atoi(keyword); + keyword = token(fp,tokenlen); + gfi->fontBBox.ury = atoi(keyword); + break; + case CAPHEIGHT: + keyword = token(fp,tokenlen); + gfi->capHeight = atoi(keyword); + break; + case XHEIGHT: + keyword = token(fp,tokenlen); + gfi->xHeight = atoi(keyword); + break; + case DESCENT: + keyword = token(fp,tokenlen); + gfi->descender = -atoi(keyword); + break; + case DESCENDER: + keyword = token(fp,tokenlen); + gfi->descender = atoi(keyword); + break; + case ASCENT: + case ASCENDER: + keyword = token(fp,tokenlen); + gfi->ascender = atoi(keyword); + break; + case STARTCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case EM: + // skip one token + keyword = token(fp,tokenlen); + break; + case STARTDIRECTION: + keyword = token(fp,tokenlen); + direction = atoi(keyword); + break; /* ignore this for now */ + case ENDDIRECTION: + break; /* ignore this for now */ + case MAPPINGSCHEME: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case CHARACTERS: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case ISBASEFONT: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case CHARACTERSET: + keyword=token(fp,tokenlen); //ignore + break; + case STDHW: + keyword=token(fp,tokenlen); //ignore + break; + case STDVW: + keyword=token(fp,tokenlen); //ignore + break; + case CHARWIDTH: + keyword = token(fp,tokenlen); + if (direction == 0) + gfi->charwidth = atoi(keyword); + keyword = token(fp,tokenlen); + /* ignore y-width for now */ + break; + case METRICSSETS: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseGlobals */ + + +#if 0 +/************************* initializeArray ************************/ + +/* Unmapped character codes are (at Adobe Systems) assigned the + * width of the space character (if one exists) else they get the + * value of 250 ems. This function initializes all entries in the + * char widths array to have this value. Then any mapped character + * codes will be replaced with the width of the appropriate character + * when parsing the character metric section. + + * This function parses the Character Metrics Section looking + * for a space character (by comparing character names). If found, + * the width of the space character will be used to initialize the + * values in the array of character widths. + * + * Before returning, the position of the read/write pointer of the + * FileInputStream is reset to be where it was upon entering this function. + */ + +static int initializeArray( FileInputStream* fp, register int* cwi) +{ + bool cont = true, found = false; + unsigned int opos = fp->tell(); + int code = 0, width = 0, i = 0, error = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + code = atoi(token(fp,tokenlen)); + break; + case CODEHEX: + sscanf(token(fp,tokenlen),"<%x>", &code); + break; + case XWIDTH: + width = atoi(token(fp,tokenlen)); + break; + case X0WIDTH: + (void) token(fp,tokenlen); + break; + case CHARNAME: + keyword = token(fp,tokenlen); + if (MATCH(keyword, Space)) + { + cont = false; + found = true; + } + break; + case ENDCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (!found) + width = 250; + + for (i = 0; i < 256; ++i) + cwi[i] = width; + + fp->seek(opos); + + return(error); + +} /* initializeArray */ +#endif + +/************************* parseCharWidths **************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndCharMetrics" keyword. It will save the character + * width info (as opposed to all of the character metric information) + * if requested by the caller of parseFile. Otherwise, it will just + * parse through the section without saving any information. + * + * If data is to be saved, parseCharWidths is passed in a pointer + * to an array of widths that has already been initialized by the + * standard value for unmapped character codes. This function parses + * the Character Metrics section only storing the width information + * for the encoded characters into the array using the character code + * as the index into that array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharWidths( FileInputStream* fp, register int* cwi) +{ + bool cont = true, save = (cwi != NULL); + int pos = 0, error = ok, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Char Metrics section without */ + /* saving any of the data*/ + switch (recognize(keyword,tokenlen)) + { + case ENDCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire char metrics section, saving */ + /* only the char x-width info */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + keyword = token(fp,tokenlen); + pos = atoi(keyword); + break; + case XYWIDTH: + /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */ + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); /* eat values */ + error = parseError; + break; + case CODEHEX: + keyword = token(fp,tokenlen); + sscanf(keyword, "<%x>", &pos); + break; + case X0WIDTH: + (void) token(fp,tokenlen); + break; + case XWIDTH: + keyword = token(fp,tokenlen); + if (pos >= 0) /* ignore unmapped chars */ + cwi[pos] = atoi(keyword); + break; + case ENDCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case CHARNAME: /* eat values (so doesn't cause parseError) */ + keyword = token(fp,tokenlen); + break; + case CHARBBOX: + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); + break; + case LIGATURE: + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); + break; + case VVECTOR: + keyword = token(fp,tokenlen); + keyword = token(fp,tokenlen); + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseCharWidths */ + + +/* + * number of char metrics is almost allways inaccurate, so be gentle and try to + * adapt our internal storage by adjusting the allocated list + */ + +static int +reallocFontMetrics( void **pp_fontmetrics, int *p_oldcount, int n_newcount, unsigned int n_size ) +{ + char *p_tmpmetrics = NULL; + + if ((pp_fontmetrics == NULL) || (*pp_fontmetrics == NULL)) + return storageProblem; + + if (*p_oldcount == n_newcount) + return ok; + + p_tmpmetrics = (char*)realloc(*pp_fontmetrics, n_newcount * n_size); + if (p_tmpmetrics == NULL) + return storageProblem; + + if ( n_newcount > *p_oldcount ) + { + char *p_inimetrics = p_tmpmetrics + n_size * *p_oldcount; + int n_inimetrics = n_size * (n_newcount - *p_oldcount); + memset( p_inimetrics, 0, n_inimetrics ); + } + + *pp_fontmetrics = p_tmpmetrics; + *p_oldcount = n_newcount; + + return ok; +} + +static unsigned int +enlargeCount( unsigned int n_oldcount ) +{ + unsigned int n_newcount = n_oldcount + n_oldcount / 5; + if (n_oldcount == n_newcount ) + n_newcount = n_oldcount + 5; + + return n_newcount; +} + +/************************* parseCharMetrics ************************/ + +/* This function is called by parseFile if the caller of parseFile + * requested that all character metric information be saved + * (as opposed to only the character width information). + * + * parseCharMetrics is passed in a pointer to an array of records + * to hold information on a per character basis. This function + * parses the Character Metrics section storing all character + * metric information for the ALL characters (mapped and unmapped) + * into the array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharMetrics( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, firstTime = true; + int error = ok, count = 0, tokenlen; + register CharMetricInfo *temp = fi->cmi; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + if (!(count < fi->numOfChars)) + { + reallocFontMetrics( (void**)&(fi->cmi), + &(fi->numOfChars), enlargeCount(fi->numOfChars), + sizeof(CharMetricInfo) ); + temp = &(fi->cmi[ count - 1 ]); + } + if (count < fi->numOfChars) + { + if (firstTime) firstTime = false; + else temp++; + temp->code = atoi(token(fp,tokenlen)); + if (fi->gfi && fi->gfi->charwidth) + temp->wx = fi->gfi->charwidth; + count++; + } + else + { + error = parseError; + cont = false; + } + break; + case CODEHEX: + if (!(count < fi->numOfChars )) + { + reallocFontMetrics( (void**)&(fi->cmi), + &(fi->numOfChars), enlargeCount(fi->numOfChars), + sizeof(CharMetricInfo) ); + temp = &(fi->cmi[ count - 1 ]); + } + if (count < fi->numOfChars) { + if (firstTime) + firstTime = false; + else + temp++; + sscanf(token(fp,tokenlen),"<%x>", &temp->code); + if (fi->gfi && fi->gfi->charwidth) + temp->wx = fi->gfi->charwidth; + count++; + } + else { + error = parseError; + cont = false; + } + break; + case XYWIDTH: + temp->wx = atoi(token(fp,tokenlen)); + temp->wy = atoi(token(fp,tokenlen)); + break; + case X0WIDTH: + temp->wx = atoi(token(fp,tokenlen)); + break; + case XWIDTH: + temp->wx = atoi(token(fp,tokenlen)); + break; + case CHARNAME: + keyword = token(fp,tokenlen); + temp->name = (char *)strdup(keyword); + break; + case CHARBBOX: + temp->charBBox.llx = atoi(token(fp,tokenlen)); + temp->charBBox.lly = atoi(token(fp,tokenlen)); + temp->charBBox.urx = atoi(token(fp,tokenlen)); + temp->charBBox.ury = atoi(token(fp,tokenlen)); + break; + case LIGATURE: { + Ligature **tail = &(temp->ligs); + Ligature *node = *tail; + + if (*tail != NULL) + { + while (node->next != NULL) + node = node->next; + tail = &(node->next); + } + + *tail = (Ligature *) calloc(1, sizeof(Ligature)); + keyword = token(fp,tokenlen); + (*tail)->succ = (char *)strdup(keyword); + keyword = token(fp,tokenlen); + (*tail)->lig = (char *)strdup(keyword); + break; } + case ENDCHARMETRICS: + cont = false;; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case VVECTOR: + keyword = token(fp,tokenlen); + keyword = token(fp,tokenlen); + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if ((error == ok) && (count != fi->numOfChars)) + error = reallocFontMetrics( (void**)&(fi->cmi), &(fi->numOfChars), + count, sizeof(CharMetricInfo) ); + + if ((error == ok) && (count != fi->numOfChars)) + error = parseError; + + return(error); + +} /* parseCharMetrics */ + + + +/************************* parseTrackKernData ***********************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndTrackKern" or "EndKernData" keywords. It will save the + * track kerning data if requested by the caller of parseFile. + * + * parseTrackKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the track kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseTrackKernData( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, save = (fi->tkd != NULL); + int pos = 0, error = ok, tcount = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Track Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword,tokenlen)) + { + case ENDTRACKKERN: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Track Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case TRACKKERN: + if (!(tcount < fi->numOfTracks)) + { + reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks), + enlargeCount(fi->numOfTracks), sizeof(TrackKernData) ); + } + + if (tcount < fi->numOfTracks) + { + keyword = token(fp,tokenlen); + fi->tkd[pos].degree = atoi(keyword); + keyword = token(fp,tokenlen); + fi->tkd[pos].minPtSize = StringToDouble(keyword); + keyword = token(fp,tokenlen); + fi->tkd[pos].minKernAmt = StringToDouble(keyword); + keyword = token(fp,tokenlen); + fi->tkd[pos].maxPtSize = StringToDouble(keyword); + keyword = token(fp,tokenlen); + fi->tkd[pos++].maxKernAmt = StringToDouble(keyword); + tcount++; + } + else + { + error = parseError; + cont = false; + } + break; + case ENDTRACKKERN: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && tcount != fi->numOfTracks) + error = reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks), + tcount, sizeof(TrackKernData) ); + + if (error == ok && tcount != fi->numOfTracks) + error = parseError; + + return(error); + +} /* parseTrackKernData */ + + +/************************* parsePairKernData ************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndKernPairs" or "EndKernData" keywords. It will save + * the pair kerning data if requested by the caller of parseFile. + * + * parsePairKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the pair kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parsePairKernData( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, save = (fi->pkd != NULL); + int pos = 0, error = ok, pcount = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Pair Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword,tokenlen)) + { + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Pair Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case KERNPAIR: + if (!(pcount < fi->numOfPairs)) + { + reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), + enlargeCount(fi->numOfPairs), sizeof(PairKernData) ); + } + if (pcount < fi->numOfPairs) + { + keyword = token(fp,tokenlen); + fi->pkd[pos].name1 = strdup( keyword ); + keyword = token(fp,tokenlen); + fi->pkd[pos].name2 = strdup( keyword ); + keyword = token(fp,tokenlen); + fi->pkd[pos].xamt = atoi(keyword); + keyword = token(fp,tokenlen); + fi->pkd[pos++].yamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = false; + } + break; + case KERNPAIRXAMT: + if (!(pcount < fi->numOfPairs)) + { + reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), + enlargeCount(fi->numOfPairs), sizeof(PairKernData) ); + } + if (pcount < fi->numOfPairs) + { + keyword = token(fp,tokenlen); + fi->pkd[pos].name1 = strdup( keyword ); + keyword = token(fp,tokenlen); + fi->pkd[pos].name2 = strdup( keyword ); + keyword = token(fp,tokenlen); + fi->pkd[pos++].xamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = false; + } + break; + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if ((error == ok) && (pcount != fi->numOfPairs)) + error = reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), + pcount, sizeof(PairKernData) ); + + if (error == ok && pcount != fi->numOfPairs) + error = parseError; + + return(error); + +} /* parsePairKernData */ + + +/************************* parseCompCharData **************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndComposites" keyword. It will save the composite + * character data if requested by the caller of parseFile. + * + * parseCompCharData is passed in a pointer to the FontInfo record, and + * a boolean representing if the data should be saved. + * + * This function will create the appropriate amount of storage for + * the composite character data and store a pointer to the storage + * in the FontInfo record. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCompCharData( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, firstTime = true, save = (fi->ccd != NULL); + int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (ccount > fi->numOfComps) + { + reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), + enlargeCount(fi->numOfComps), sizeof(CompCharData) ); + } + if (ccount > fi->numOfComps) + { + error = parseError; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Composite Character info */ + /* section without saving any of the data */ + switch(recognize(keyword,tokenlen)) + { + case ENDCOMPOSITES: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case COMMENT: + case COMPCHAR: + keyword = linetoken(fp); + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Composite Character info section, */ + /* saving the data */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case COMPCHAR: + if (!(ccount < fi->numOfComps)) + { + reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), + enlargeCount(fi->numOfComps), sizeof(CompCharData) ); + } + if (ccount < fi->numOfComps) + { + keyword = token(fp,tokenlen); + if (pcount != fi->ccd[pos].numOfPieces) + error = parseError; + pcount = 0; + if (firstTime) firstTime = false; + else pos++; + fi->ccd[pos].ccName = strdup( keyword ); + keyword = token(fp,tokenlen); + fi->ccd[pos].numOfPieces = atoi(keyword); + fi->ccd[pos].pieces = (Pcc *) + calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)); + j = 0; + ccount++; + } + else + { + error = parseError; + cont = false; + } + break; + case COMPCHARPIECE: + if (pcount < fi->ccd[pos].numOfPieces) + { + keyword = token(fp,tokenlen); + fi->ccd[pos].pieces[j].pccName = strdup( keyword ); + keyword = token(fp,tokenlen); + fi->ccd[pos].pieces[j].deltax = atoi(keyword); + keyword = token(fp,tokenlen); + fi->ccd[pos].pieces[j++].deltay = atoi(keyword); + pcount++; + } + else + error = parseError; + break; + case ENDCOMPOSITES: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && ccount != fi->numOfComps) + reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), + ccount, sizeof(CompCharData) ); + + if (error == ok && ccount != fi->numOfComps) + error = parseError; + + return(error); + +} /* parseCompCharData */ + + + + +/*************************** 'PUBLIC' FUNCTION ********************/ + + +/*************************** parseFile *****************************/ + +/* parseFile is the only 'public' procedure available. It is called + * from an application wishing to get information from an AFM file. + * The caller of this function is responsible for locating and opening + * an AFM file and handling all errors associated with that task. + * + * parseFile expects 3 parameters: a filename pointer, a pointer + * to a (FontInfo *) variable (for which storage will be allocated and + * the data requested filled in), and a mask specifying which + * data from the AFM file should be saved in the FontInfo structure. + * + * The file will be parsed and the requested data will be stored in + * a record of type FontInfo (refer to ParseAFM.h). + * + * parseFile returns an error code as defined in parseAFM.h. + * + * The position of the read/write pointer associated with the file + * pointer upon return of this function is undefined. + */ + +int parseFile( const char* pFilename, FontInfo** fi, FLAGS flags) +{ + FileInputStream aFile( pFilename ); + + int code = ok; /* return code from each of the parsing routines */ + int error = ok; /* used as the return code from this function */ + int tokenlen; + + register char *keyword; /* used to store a token */ + + + (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo)); + if ((*fi) == NULL) {error = storageProblem; return(error);} + + if (flags & P_G) + { + (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo)); + if ((*fi)->gfi == NULL) {error = storageProblem; return(error);} + } + + /* The AFM file begins with Global Font Information. This section */ + /* will be parsed whether or not information should be saved. */ + code = parseGlobals(&aFile, (*fi)->gfi); + + if (code < 0) error = code; + + /* The Global Font Information is followed by the Character Metrics */ + /* section. Which procedure is used to parse this section depends on */ + /* how much information should be saved. If all of the metrics info */ + /* is wanted, parseCharMetrics is called. If only the character widths */ + /* is wanted, parseCharWidths is called. parseCharWidths will also */ + /* be called in the case that no character data is to be saved, just */ + /* to parse through the section. */ + + if ((code != normalEOF) && (code != earlyEOF)) + { + (*fi)->numOfChars = atoi(token(&aFile,tokenlen)); + if (flags & (P_M ^ P_W)) + { + (*fi)->cmi = (CharMetricInfo *) + calloc((*fi)->numOfChars, sizeof(CharMetricInfo)); + if ((*fi)->cmi == NULL) {error = storageProblem; return(error);} + code = parseCharMetrics(&aFile, *fi); + } + else + { + if (flags & P_W) + { + (*fi)->cwi = (int *) calloc(256, sizeof(int)); + if ((*fi)->cwi == NULL) + { + error = storageProblem; + return(error); + } + } + /* parse section regardless */ + code = parseCharWidths(&aFile, (*fi)->cwi); + } /* else */ + } /* if */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + /* The remaining sections of the AFM are optional. This code will */ + /* look at the next keyword in the file to determine what section */ + /* is next, and then allocate the appropriate amount of storage */ + /* for the data (if the data is to be saved) and call the */ + /* appropriate parsing routine to parse the section. */ + + while ((code != normalEOF) && (code != earlyEOF)) + { + keyword = token(&aFile,tokenlen); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + code = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword,tokenlen)) + { + case STARTKERNDATA: + break; + case ENDKERNDATA: + break; + case STARTTRACKKERN: + keyword = token(&aFile,tokenlen); + if (flags & P_T) + { + (*fi)->numOfTracks = atoi(keyword); + (*fi)->tkd = (TrackKernData *) + calloc((*fi)->numOfTracks, sizeof(TrackKernData)); + if ((*fi)->tkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseTrackKernData(&aFile, *fi); + break; + case STARTKERNPAIRS: + keyword = token(&aFile,tokenlen); + if (flags & P_P) + { + (*fi)->numOfPairs = atoi(keyword); + (*fi)->pkd = (PairKernData *) + calloc((*fi)->numOfPairs, sizeof(PairKernData)); + if ((*fi)->pkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parsePairKernData(&aFile, *fi); + break; + case STARTCOMPOSITES: + keyword = token(&aFile,tokenlen); + if (flags & P_C) + { + (*fi)->numOfComps = atoi(keyword); + (*fi)->ccd = (CompCharData *) + calloc((*fi)->numOfComps, sizeof(CompCharData)); + if ((*fi)->ccd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseCompCharData(&aFile, *fi); + break; + case ENDFONTMETRICS: + code = normalEOF; + break; + case COMMENT: + linetoken(&aFile); + break; + case NOPE: + default: + code = parseError; + break; + } /* switch */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + } /* while */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + return(error); + +} /* parseFile */ + +void +freeFontInfo (FontInfo *fi) +{ + int i, j; + + if (fi->gfi) + { + free (fi->gfi->afmVersion); + free (fi->gfi->fontName); + free (fi->gfi->fullName); + free (fi->gfi->familyName); + free (fi->gfi->weight); + free (fi->gfi->version); + free (fi->gfi->notice); + free (fi->gfi->encodingScheme); + free (fi->gfi); + } + + free (fi->cwi); + + if (fi->cmi) + { + for (i = 0; i < fi->numOfChars; i++) + { + Ligature *ligs; + free (fi->cmi[i].name); + ligs = fi->cmi[i].ligs; + while (ligs) + { + Ligature *tmp; + tmp = ligs; + ligs = ligs->next; + free (tmp->succ); + free (tmp->lig); + free (tmp); + } + } + free (fi->cmi); + } + + free (fi->tkd); + + if (fi->pkd) + { + for ( i = 0; i < fi->numOfPairs; i++) + { + free (fi->pkd[i].name1); + free (fi->pkd[i].name2); + } + free (fi->pkd); + } + + if (fi->ccd) + { + for (i = 0; i < fi->numOfComps; i++) + { + free (fi->ccd[i].ccName); + for (j = 0; j < fi->ccd[i].numOfPieces; j++) + free (fi->ccd[i].pieces[j].pccName); + + free (fi->ccd[i].pieces); + } + free (fi->ccd); + } + + free (fi); +} + +} // namspace diff --git a/vcl/unx/source/fontmanager/parseAFM.hxx b/vcl/unx/source/fontmanager/parseAFM.hxx new file mode 100644 index 000000000000..ad0c32e4b51b --- /dev/null +++ b/vcl/unx/source/fontmanager/parseAFM.hxx @@ -0,0 +1,344 @@ +/* + * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* + * Changes made for OpenOffice.org + * + * 10/24/2000 pl - changed code to compile with c++-compilers + * - added namespace to avoid symbol clashes + * - replaced BOOL by bool + * - added function to free space allocated by parseFile + * 10/26/2000 pl - added additional keys + * - added ability to parse slightly broken files + * - added charwidth member to GlobalFontInfo + * 04/26/2001 pl - added OpenOffice header + * 10/19/2005 pl - changed parseFile to accept a file name instead of a stream + */ + +/************************************************************************* + * + * $RCSfile: parseAFM.hxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: hr $ $Date: 2005-12-28 17:08:50 $ + * + ************************************************************************/ + +/* ParseAFM.h + * + * This header file is used in conjuction with the parseAFM.c file. + * Together these files provide the functionality to parse Adobe Font + * Metrics files and store the information in predefined data structures. + * It is intended to work with an application program that needs font metric + * information. The program can be used as is by making a procedure call to + * parse an AFM file and have the data stored, or an application developer + * may wish to customize the code. + * + * This header file defines the data structures used as well as the key + * strings that are currently recognized by this version of the AFM parser. + * This program is based on the document "Adobe Font Metrics Files, + * Specification Version 2.0". + * + * AFM files are separated into distinct sections of different data. Because + * of this, the parseAFM program can parse a specified file to only save + * certain sections of information based on the application's needs. A record + * containing the requested information will be returned to the application. + * + * AFM files are divided into five sections of data: + * 1) The Global Font Information + * 2) The Character Metrics Information + * 3) The Track Kerning Data + * 4) The Pair-Wise Kerning Data + * 5) The Composite Character Data + * + * Basically, the application can request any of these sections independent + * of what other sections are requested. In addition, in recognizing that + * many applications will want ONLY the x-width of characters and not all + * of the other character metrics information, there is a way to receive + * only the width information so as not to pay the storage cost for the + * unwanted data. An application should never request both the + * "quick and dirty" char metrics (widths only) and the Character Metrics + * Information since the Character Metrics Information will contain all + * of the character widths as well. + * + * There is a procedure in parseAFM.c, called parseFile, that can be + * called from any application wishing to get information from the AFM File. + * This procedure expects 3 parameters: a vaild file descriptor, a pointer + * to a (FontInfo *) variable (for which space will be allocated and then + * will be filled in with the data requested), and a mask specifying + * which data from the AFM File should be saved in the FontInfo structure. + * + * The flags that can be used to set the appropriate mask are defined below. + * In addition, several commonly used masks have already been defined. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed typos + */ + +#include <stdio.h> + +namespace psp { + +/* your basic constants */ +#define EOL '\n' /* end-of-line indicator */ +#define MAX_NAME 4096 /* max length for identifiers */ +#define FLAGS int + + + +/* Flags that can be AND'ed together to specify exactly what + * information from the AFM file should be saved. + */ +#define P_G 0x01 /* 0000 0001 */ /* Global Font Info */ +#define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */ +#define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */ +#define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */ +#define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */ +#define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */ + + +/* Commonly used flags + */ +#define P_GW (P_G | P_W) +#define P_GM (P_G | P_M) +#define P_GMP (P_G | P_M | P_P) +#define P_GMK (P_G | P_M | P_P | P_T) +#define P_ALL (P_G | P_M | P_P | P_T | P_C) + + + +/* Possible return codes from the parseFile procedure. + * + * ok means there were no problems parsing the file. + * + * parseError means that there was some kind of parsing error, but the + * parser went on. This could include problems like the count for any given + * section does not add up to how many entries there actually were, or + * there was a key that was not recognized. The return record may contain + * vaild data or it may not. + * + * earlyEOF means that an End of File was encountered before expected. This + * may mean that the AFM file had been truncated, or improperly formed. + * + * storageProblem means that there were problems allocating storage for + * the data structures that would have contained the AFM data. + */ + +enum afmError { ok = 0, parseError = -1, earlyEOF = -2, storageProblem = -3 }; + + +/************************* TYPES *********************************/ +/* Below are all of the data structure definitions. These structures + * try to map as closely as possible to grouping and naming of data + * in the AFM Files. + */ + + +/* Bounding box definition. Used for the Font BBox as well as the + * Character BBox. + */ +typedef struct +{ + int llx; /* lower left x-position */ + int lly; /* lower left y-position */ + int urx; /* upper right x-position */ + int ury; /* upper right y-position */ +} BBox; + + +/* Global Font information. + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the AFM + * documentation (full title & version given above). + */ +typedef struct +{ + char *afmVersion; /* key: StartFontMetrics */ + char *fontName; /* key: FontName */ + char *fullName; /* key: FullName */ + char *familyName; /* key: FamilyName */ + char *weight; /* key: Weight */ + float italicAngle; /* key: ItalicAngle */ + bool isFixedPitch; /* key: IsFixedPitch */ + BBox fontBBox; /* key: FontBBox */ + int underlinePosition; /* key: UnderlinePosition */ + int underlineThickness; /* key: UnderlineThickness */ + char *version; /* key: Version */ + char *notice; /* key: Notice */ + char *encodingScheme; /* key: EncodingScheme */ + int capHeight; /* key: CapHeight */ + int xHeight; /* key: XHeight */ + int ascender; /* key: Ascender */ + int descender; /* key: Descender */ + int charwidth; /* key: CharWidth */ +} GlobalFontInfo; + + +/* Ligature definition is a linked list since any character can have + * any number of ligatures. + */ +typedef struct _t_ligature +{ + char *succ, *lig; + struct _t_ligature *next; +} Ligature; + + +/* Character Metric Information. This structure is used only if ALL + * character metric information is requested. If only the character + * widths is requested, then only an array of the character x-widths + * is returned. + * + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the + * Character Metrics section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int code, /* key: C */ + wx, /* key: WX */ + w0x, /* key: W0X */ + wy; /* together wx and wy are associated with key: W */ + char *name; /* key: N */ + BBox charBBox; /* key: B */ + Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */ +} CharMetricInfo; + + +/* Track kerning data structure. + * The fields of this record are the five values associated with every + * TrackKern entry. + * + * For an explanation about each value please refer to the + * Track Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int degree; + float minPtSize, + minKernAmt, + maxPtSize, + maxKernAmt; +} TrackKernData; + + +/* Pair Kerning data structure. + * The fields of this record are the four values associated with every + * KP entry. For KPX entries, the yamt will be zero. + * + * For an explanation about each value please refer to the + * Pair Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *name1; + char *name2; + int xamt, + yamt; +} PairKernData; + + +/* PCC is a piece of a composite character. This is a sub structure of a + * compCharData described below. + * These fields will be filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *pccName; + int deltax, + deltay; +} Pcc; + + +/* Composite Character Information data structure. + * The fields ccName and numOfPieces are filled with the values associated + * with the key CC. The field pieces points to an array (size = numOfPieces) + * of information about each of the parts of the composite character. That + * array is filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *ccName; + int numOfPieces; + Pcc *pieces; +} CompCharData; + + +/* FontInfo + * Record type containing pointers to all of the other data + * structures containing information about a font. + * A a record of this type is filled with data by the + * parseFile function. + */ +typedef struct +{ + GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */ + int *cwi; /* ptr to 256 element array of just char widths */ + int numOfChars; /* number of entries in char metrics array */ + CharMetricInfo *cmi; /* ptr to char metrics array */ + int numOfTracks; /* number to entries in track kerning array */ + TrackKernData *tkd; /* ptr to track kerning array */ + int numOfPairs; /* number to entries in pair kerning array */ + PairKernData *pkd; /* ptr to pair kerning array */ + int numOfComps; /* number to entries in comp char array */ + CompCharData *ccd; /* ptr to comp char array */ +} FontInfo; + + + +/************************* PROCEDURES ****************************/ + +/* Call this procedure to do the grunt work of parsing an AFM file. + * + * "fp" should be a valid file pointer to an AFM file. + * + * "fi" is a pointer to a pointer to a FontInfo record sturcture + * (defined above). Storage for the FontInfo structure will be + * allocated in parseFile and the structure will be filled in + * with the requested data from the AFM File. + * + * "flags" is a mask with bits set representing what data should + * be saved. Defined above are valid flags that can be used to set + * the mask, as well as a few commonly used masks. + * + * The possible return codes from parseFile are defined above. + */ + +int parseFile( const char* pFilename, FontInfo **fi, FLAGS flags ); +void freeFontInfo(FontInfo *fi); + +} // namespace diff --git a/vcl/unx/source/gdi/dtint.cxx b/vcl/unx/source/gdi/dtint.cxx index 8a67dbe6fcc7..96d78b4f006d 100644 --- a/vcl/unx/source/gdi/dtint.cxx +++ b/vcl/unx/source/gdi/dtint.cxx @@ -44,9 +44,6 @@ #include <cdeint.hxx> #endif #include <dtint.hxx> -#ifdef MACOSX -#include <macosxint.hxx> -#endif #include <saldisp.hxx> #include <saldata.hxx> #include <wmadaptor.hxx> @@ -98,9 +95,6 @@ DtIntegrator::~DtIntegrator() DtIntegrator* DtIntegrator::CreateDtIntegrator() { -#ifdef MACOSX - return new MACOSXIntegrator(); -#endif /* * #i22061# override desktop detection * if environment variable OOO_FORCE_DESKTOP is set diff --git a/vcl/unx/source/gdi/gcach_xpeer.cxx b/vcl/unx/source/gdi/gcach_xpeer.cxx index 77e8ccc09a91..85466c532ff2 100644 --- a/vcl/unx/source/gdi/gcach_xpeer.cxx +++ b/vcl/unx/source/gdi/gcach_xpeer.cxx @@ -647,9 +647,6 @@ Glyph X11GlyphPeer::GetGlyphId( ServerFont& rServerFont, int nGlyphIndex ) X11GlyphCache::X11GlyphCache( X11GlyphPeer& rPeer ) : GlyphCache( rPeer ) { -#ifdef MACOSX - LoadFonts(); -#endif } // --------------------------------------------------------------------------- diff --git a/vcl/unx/source/gdi/macosxint.cxx b/vcl/unx/source/gdi/macosxint.cxx deleted file mode 100644 index 5e2a2838ae7f..000000000000 --- a/vcl/unx/source/gdi/macosxint.cxx +++ /dev/null @@ -1,250 +0,0 @@ -/************************************************************************* - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright 2008 by Sun Microsystems, Inc. - * - * OpenOffice.org - a multi-platform office productivity suite - * - * $RCSfile: macosxint.cxx,v $ - * $Revision: 1.5 $ - * - * This file is part of OpenOffice.org. - * - * OpenOffice.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 - * only, as published by the Free Software Foundation. - * - * OpenOffice.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3 for more details - * (a copy is included in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with OpenOffice.org. If not, see - * <http://www.openoffice.org/license.html> - * for a copy of the LGPLv3 License. - * - ************************************************************************/ - -// MARKER(update_precomp.py): autogen include statement, do not remove -#include "precompiled_vcl.hxx" - -#include <macosxint.hxx> -#include <tools/config.hxx> -#include <vcl/settings.hxx> -#include <osl/thread.h> -#include <osl/file.hxx> -#include <rtl/bootstrap.hxx> -#include <unistd.h> -#include <cstdio> - -using namespace rtl; -using namespace osl; - -MACOSXIntegrator::MACOSXIntegrator() -{ - meType = DtMACOSX; -} - -MACOSXIntegrator::~MACOSXIntegrator() -{ -} - -void MACOSXIntegrator::GetSystemLook( AllSettings& rSettings ) -{ - rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); - - StyleSettings aStyleSettings( rSettings.GetStyleSettings() ); - // #i48001# set a default blink rate - aStyleSettings.SetCursorBlinkTime( 500 ); - -// #i61174# aquacolors -// aUserConfigFile : string containing the user install directory completed with "/user/macosxrc.txt" -// currently : ~/Library/Application Support/OpenOffice.org 2.0/user/macosxrc.txt -// aDefaultConfigFile : string containing the OpenOffice.org install directory + presets/macosxrc.txt -// default should be /Applications/OpenOffice.org 2.0/Contents/openoffice.org2/presets/macosxrc.txt - - - rtl::OUString aUserConfigFile; - rtl::OUString aDefaultConfigFile; - rtl::OUString aTryFiles[2]; - -// read the content of bootstraprc is necessary to find the path to the user configuration file -// ~/Library/Application Support/OpenOffice.org 2.0/user/macosxrc.txt - - rtl::Bootstrap aBootstrap( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("bootstraprc") ) ); - if( aBootstrap.getFrom( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("MacOSXIntegrationUserFile") ), aUserConfigFile ) ) - { - rtl::OUString aFile = aUserConfigFile ; - osl::FileBase::getSystemPathFromFileURL(aFile, aTryFiles[0]); - } - -// if macosxrc.txt is not found in user install dir, fallback to the second macosxrc.txt (with default values), located in <install_dir>/presets - - if( aBootstrap.getFrom( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("MacOSXIntegrationDefaultFile") ), aDefaultConfigFile ) ) - { - rtl::OUString aFile = aDefaultConfigFile ; - osl::FileBase::getSystemPathFromFileURL(aFile, aTryFiles[1]); - } - - for( unsigned int i = 0; (i < sizeof(aTryFiles) / sizeof(aTryFiles[0])); i++ ) - { - - #if OSL_DEBUG_LEVEL > 1 - fprintf(stderr, "try accessing %d, %s\n", i, rtl::OUStringToOString( aTryFiles[i], aEncoding ).getStr()); - #endif - if( access( rtl::OUStringToOString( aTryFiles[i], aEncoding ).getStr(), R_OK ) ) - continue; - - #if OSL_DEBUG_LEVEL > 1 - fprintf( stderr, "using %s for style settings\n", rtl::OUStringToOString( aTryFiles[i], aEncoding ).getStr() ); - #endif - - Config aConfig( aTryFiles[i] ); - ByteString aLine; - - if( aConfig.HasGroup( "General" ) ) - { - aConfig.SetGroup( "General" ); - - aLine = aConfig.ReadKey( "foreground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) - { - Color aFore( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - aStyleSettings.SetDialogTextColor( aFore ); - aStyleSettings.SetMenuTextColor( aFore ); - aStyleSettings.SetButtonTextColor( aFore ); - aStyleSettings.SetRadioCheckTextColor( aFore ); - aStyleSettings.SetGroupTextColor( aFore ); - aStyleSettings.SetLabelTextColor( aFore ); - aStyleSettings.SetInfoTextColor( aFore ); - aStyleSettings.SetFieldTextColor( aFore ); - } - - aLine = aConfig.ReadKey( "background" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) - { - Color aBack( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - aStyleSettings.Set3DColors( aBack ); - aStyleSettings.SetFaceColor( aBack ); - aStyleSettings.SetDialogColor( aBack ); - aStyleSettings.SetMenuColor( aBack ); - aStyleSettings.SetMenuBarColor( aBack ); - aStyleSettings.SetLightBorderColor( aBack ); - if( aBack == COL_LIGHTGRAY ) - aStyleSettings.SetCheckedColor( Color( 0xCC, 0xCC, 0xCC ) ); - else - { - Color aColor2 = aStyleSettings.GetLightColor(); - aStyleSettings.SetCheckedColor( - Color( (BYTE)(((USHORT)aBack.GetRed()+(USHORT)aColor2.GetRed())/2), - (BYTE)(((USHORT)aBack.GetGreen()+(USHORT)aColor2.GetGreen())/2), - (BYTE)(((USHORT)aBack.GetBlue()+(USHORT)aColor2.GetBlue())/2) - ) ); - } - } - - aLine = aConfig.ReadKey( "selectForeground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) - { - Color aSelectFore( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32()); - - aStyleSettings.SetHighlightTextColor( aSelectFore ); - aStyleSettings.SetMenuHighlightTextColor( aSelectFore ); - } - aLine = aConfig.ReadKey( "selectBackground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) - { - Color aSelectBack( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - - aStyleSettings.SetHighlightColor( aSelectBack ); - aStyleSettings.SetMenuHighlightColor( aSelectBack ); - } - aLine = aConfig.ReadKey( "activeForeground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) { - Color aActiveFore( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - - aStyleSettings.SetActiveTextColor( aActiveFore ); - } - aLine = aConfig.ReadKey( "activeBackground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) { - Color aActiveBack( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - - aStyleSettings.SetActiveColor( aActiveBack ); - aStyleSettings.SetActiveColor2( aActiveBack ); - } - aLine = aConfig.ReadKey( "deactiveForeground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) { - Color aDeactiveFore( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - - aStyleSettings.SetDeactiveTextColor( aDeactiveFore ); - aStyleSettings.SetDisableColor( aDeactiveFore ); - } - aLine = aConfig.ReadKey( "deactiveBackground" ); - if( aLine.GetTokenCount( ',' ) >= 3 ) { - Color aDeactiveBack( aLine.GetToken( 0, ',' ).ToInt32(), - aLine.GetToken( 1, ',' ).ToInt32(), - aLine.GetToken( 2, ',' ).ToInt32() ); - - aStyleSettings.SetDeactiveColor( aDeactiveBack ); - aStyleSettings.SetDeactiveColor2( aDeactiveBack ); - aStyleSettings.SetDeactiveBorderColor( aDeactiveBack ); - aStyleSettings.SetActiveBorderColor( aDeactiveBack ); - } - - aLine = aConfig.ReadKey( "font" ); - if( aLine.Len() ) - { - Font aFont = aStyleSettings.GetAppFont(); - String aFontName( aLine, RTL_TEXTENCODING_UTF8 ); - if( aFontName.GetTokenCount( ',' ) > 0 ) - aFontName = aFontName.GetToken( 0, ',' ); - aFont.SetName( aFontName ); - - aStyleSettings.SetAppFont( aFont ); - aStyleSettings.SetHelpFont( aFont ); - aStyleSettings.SetTitleFont( aFont ); - aStyleSettings.SetFloatTitleFont( aFont ); - aStyleSettings.SetMenuFont( aFont ); - aStyleSettings.SetToolFont( aFont ); - aStyleSettings.SetLabelFont( aFont ); - aStyleSettings.SetInfoFont( aFont ); - aStyleSettings.SetRadioCheckFont( aFont ); - aStyleSettings.SetPushButtonFont( aFont ); - aStyleSettings.SetFieldFont( aFont ); - aStyleSettings.SetIconFont( aFont ); - aStyleSettings.SetGroupFont( aFont ); - } - - aLine = aConfig.ReadKey( "cursorFlashTime" ); - if( aLine.Len() ) - { - sal_Int32 nTime = aLine.ToInt32() / 2; - if( nTime == 0 ) - nTime = STYLE_CURSOR_NOBLINKTIME; - aStyleSettings.SetCursorBlinkTime( nTime ); - } - } - - break; - } - - rSettings.SetStyleSettings( aStyleSettings ); -} - diff --git a/vcl/unx/source/gdi/macosxrc.txt b/vcl/unx/source/gdi/macosxrc.txt deleted file mode 100644 index 55c2c40f67cc..000000000000 --- a/vcl/unx/source/gdi/macosxrc.txt +++ /dev/null @@ -1,33 +0,0 @@ -# -# Configure file for UI colors in OpenOffice.org Mac OS X port -# Created by Mox 2006/01/27 -# This file follows the format of .kderc files -# -# The settings under label [General] are used. Modify it according to -# the other labels, if you want to change it. -# - -[Aqua] -background=244,244,244 -foreground=0,0,0 -selectBackground=52,112,204 -selectForeground=255,255,255 - -[Graphite] -background=244,244,244 -foreground=0,0,0 -selectBackground=94,106,121 -selectForeground=255,255,255 - -[General] -background=244,244,244 -foreground=0,0,0 -selectBackground=52,112,204 -selectForeground=255,255,255 -#comment out if you want to use a customized font -# only one font can be choosen -#font=Andale Mono,Andale Sans UI -# another nice font example -#font=Futura, Futura Sans UI -#Apple recommandation is Lucida Grande -font=Lucida Grande, Futura Sans UI diff --git a/vcl/unx/source/gdi/makefile.mk b/vcl/unx/source/gdi/makefile.mk index 1516cd8ad5a5..8f0faf863af2 100644 --- a/vcl/unx/source/gdi/makefile.mk +++ b/vcl/unx/source/gdi/makefile.mk @@ -73,12 +73,6 @@ EXCEPTIONSFILES=\ $(SLO)$/salgdi3.obj \ $(SLO)$/salcvt.obj - -.IF "$(OS)"=="MACOSX" -SLOFILES += $(SLO)$/macosxint.obj -MACOSXRC = $(MISC)$/macosxrc.txt -.ENDIF # "$(OS)"=="MACOSX" - .IF "$(USE_XPRINT)" == "TRUE" CFLAGS+=-D_USE_PRINT_EXTENSION_=1 SLOFILES+=$(SLO)$/xprintext.obj @@ -115,5 +109,3 @@ $(INCCOM)$/rtsname.hxx: $(SLO)$/salpimpl.obj : $(INCCOM)$/rtsname.hxx $(SLO)$/salprnpsp.obj : $(INCCOM)$/rtsname.hxx -$(MISC)$/macosxrc.txt : $$(@:f) - $(COPY) $< $@ diff --git a/vcl/unx/source/gdi/pspgraphics.cxx b/vcl/unx/source/gdi/pspgraphics.cxx index 7b8a0f173707..4a4bccd86d2a 100644 --- a/vcl/unx/source/gdi/pspgraphics.cxx +++ b/vcl/unx/source/gdi/pspgraphics.cxx @@ -31,18 +31,18 @@ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" -#include <pspgraphics.h> -#include <psprint/jobdata.hxx> -#include <psprint/printergfx.hxx> -#include <psprint/printerinfomanager.hxx> -#include <vcl/bmpacc.hxx> -#include <vcl/salbmp.hxx> -#include <vcl/glyphcache.hxx> -#include <vcl/impfont.hxx> -#include <vcl/outfont.hxx> -#include <vcl/svapp.hxx> -#include <vcl/salprn.hxx> -#include <vcl/sysdata.hxx> +#include "pspgraphics.h" +#include "vcl/jobdata.hxx" +#include "vcl/printergfx.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/bmpacc.hxx" +#include "vcl/salbmp.hxx" +#include "vcl/glyphcache.hxx" +#include "vcl/impfont.hxx" +#include "vcl/outfont.hxx" +#include "vcl/svapp.hxx" +#include "vcl/salprn.hxx" +#include "vcl/sysdata.hxx" #include <stdlib.h> #include <unistd.h> diff --git a/vcl/unx/source/gdi/salgdi.cxx b/vcl/unx/source/gdi/salgdi.cxx index dabce7c59b9e..1d61dbe214a2 100644 --- a/vcl/unx/source/gdi/salgdi.cxx +++ b/vcl/unx/source/gdi/salgdi.cxx @@ -33,31 +33,28 @@ #include "Xproto.h" -#include <salunx.h> -#include <saldata.hxx> -#include <saldisp.hxx> -#ifndef _SV_SALGDI_HXX -#include <salgdi.h> -#endif -#include <salframe.h> -#include <salvd.h> -#include <tools/debug.hxx> +#include "salunx.h" +#include "saldata.hxx" +#include "saldisp.hxx" +#include "salgdi.h" +#include "salframe.h" +#include "salvd.h" +#include "xrender_peer.hxx" -#ifndef _USE_PRINT_EXTENSION_ -#include <psprint/printergfx.hxx> -#include <psprint/jobdata.hxx> -#endif +#include "vcl/printergfx.hxx" +#include "vcl/jobdata.hxx" -#include <basegfx/polygon/b2dpolygon.hxx> -#include <basegfx/polygon/b2dpolypolygon.hxx> -#include <basegfx/polygon/b2dpolypolygontools.hxx> -#include <basegfx/polygon/b2dpolygontools.hxx> -#include <basegfx/polygon/b2dpolygonclipper.hxx> -#include <basegfx/polygon/b2dlinegeometry.hxx> -#include <basegfx/matrix/b2dhommatrix.hxx> -#include <basegfx/polygon/b2dpolypolygoncutter.hxx> +#include "tools/debug.hxx" + +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/polygon/b2dpolypolygon.hxx" +#include "basegfx/polygon/b2dpolypolygontools.hxx" +#include "basegfx/polygon/b2dpolygontools.hxx" +#include "basegfx/polygon/b2dpolygonclipper.hxx" +#include "basegfx/polygon/b2dlinegeometry.hxx" +#include "basegfx/matrix/b2dhommatrix.hxx" +#include "basegfx/polygon/b2dpolypolygoncutter.hxx" -#include "xrender_peer.hxx" #include <vector> #include <queue> #include <set> diff --git a/vcl/unx/source/gdi/salgdi2.cxx b/vcl/unx/source/gdi/salgdi2.cxx index c10abac60bb0..7192a417f96c 100644 --- a/vcl/unx/source/gdi/salgdi2.cxx +++ b/vcl/unx/source/gdi/salgdi2.cxx @@ -32,22 +32,20 @@ #include "precompiled_vcl.hxx" #include <stdio.h> - -#include <salunx.h> #include <poll.h> -#include <saldata.hxx> -#include <saldisp.hxx> -#include <salbmp.h> -#include <vcl/salbtype.hxx> -#include <salgdi.h> -#include <salframe.h> -#include <salvd.h> -#include <xrender_peer.hxx> - -#ifndef _USE_PRINT_EXTENSION_ -#include <psprint/printergfx.hxx> -#include <vcl/bmpacc.hxx> -#endif + +#include "salunx.h" +#include "saldata.hxx" +#include "saldisp.hxx" +#include "salbmp.h" +#include "salgdi.h" +#include "salframe.h" +#include "salvd.h" +#include "xrender_peer.hxx" + +#include "vcl/salbtype.hxx" +#include "vcl/printergfx.hxx" +#include "vcl/bmpacc.hxx" #undef SALGDI2_TESTTRANS diff --git a/vcl/unx/source/gdi/salgdi3.cxx b/vcl/unx/source/gdi/salgdi3.cxx index 47b5f5263ce5..e2c41b52006b 100644 --- a/vcl/unx/source/gdi/salgdi3.cxx +++ b/vcl/unx/source/gdi/salgdi3.cxx @@ -40,42 +40,44 @@ #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> -#include <sal/alloca.h> - -#include <gcach_xpeer.hxx> -#include <xrender_peer.hxx> -#include <sal/types.h> - -#include <salunx.h> -#include <saldata.hxx> -#include <saldisp.hxx> -#include <salgdi.h> -#include <pspgraphics.h> -#include <vcl/salframe.hxx> -#include <salvd.h> -#include <vcl/outdev.h> -#include <tools/string.hxx> -#include <basegfx/polygon/b2dpolypolygon.hxx> -#include <rtl/tencinfo.h> -#include <osl/file.hxx> -#include "xfont.hxx" -#include <vcl/impfont.hxx> - - -#include <tools/debug.hxx> -#include <tools/stream.hxx> -#include <psprint/printergfx.hxx> -#include <psprint/fontmanager.hxx> -#include <psprint/jobdata.hxx> -#include <psprint/printerinfomanager.hxx> -#include <vcl/svapp.hxx> +#include "gcach_xpeer.hxx" +#include "xrender_peer.hxx" +#include "salunx.h" +#include "saldata.hxx" +#include "saldisp.hxx" +#include "salgdi.h" +#include "pspgraphics.h" +#include "salvd.h" +#include "xfont.hxx" #include "xlfd_attr.hxx" #include "xlfd_smpl.hxx" #include "xlfd_extd.hxx" #include "salcvt.hxx" -#include <i18npool/mslangid.hxx> +#include "vcl/printergfx.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/jobdata.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/svapp.hxx" +#include "vcl/impfont.hxx" +#include "vcl/salframe.hxx" +#include "vcl/outdev.h" + +#include "sal/alloca.h" +#include "sal/types.h" + +#include "rtl/tencinfo.h" + +#include "osl/file.hxx" + +#include "tools/string.hxx" +#include "tools/debug.hxx" +#include "tools/stream.hxx" + +#include "basegfx/polygon/b2dpolypolygon.hxx" + +#include "i18npool/mslangid.hxx" #include <hash_set> @@ -795,11 +797,7 @@ CairoWrapper::CairoWrapper() if( !XQueryExtension( GetX11SalData()->GetDisplay()->GetDisplay(), "RENDER", &nDummy, &nDummy, &nDummy ) ) return; -#ifdef MACOSX - OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libcairo.2.dylib" )); -#else OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libcairo.so.2" )); -#endif mpCairoLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT ); if( !mpCairoLib ) return; @@ -1392,10 +1390,8 @@ void X11SalGraphics::DrawServerFontLayout( const ServerFontLayout& rLayout ) X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); if( rGlyphPeer.GetGlyphSet( rFont, m_nScreen ) ) DrawServerAAFontString( rLayout ); -#ifndef MACOSX /* ignore X11 fonts on MACOSX */ else if( !rGlyphPeer.ForcedAntialiasing( rFont, m_nScreen ) ) DrawServerSimpleFontString( rLayout ); -#endif // MACOSX else DrawServerAAForcedString( rLayout ); } diff --git a/vcl/unx/source/gdi/salprnpsp.cxx b/vcl/unx/source/gdi/salprnpsp.cxx index 965fb2f10209..b3fdfaef56ce 100644 --- a/vcl/unx/source/gdi/salprnpsp.cxx +++ b/vcl/unx/source/gdi/salprnpsp.cxx @@ -47,20 +47,22 @@ #include <unistd.h> #include <sys/wait.h> #include <sys/stat.h> -#include <vcl/svapp.hxx> -#include <vcl/jobset.h> -#include <saldisp.hxx> -#include <salinst.h> -#include <salprn.h> -#include <vcl/print.h> -#include <vcl/salptype.hxx> -#include <salframe.h> -#include <pspgraphics.h> -#include <saldata.hxx> - -#include <rtl/ustring.hxx> -#include <osl/module.h> -#include <psprint/printerinfomanager.hxx> + +#include "saldisp.hxx" +#include "salinst.h" +#include "salprn.h" +#include "salframe.h" +#include "pspgraphics.h" +#include "saldata.hxx" +#include "vcl/svapp.hxx" +#include "vcl/jobset.h" +#include "vcl/print.h" +#include "vcl/salptype.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "rtl/ustring.hxx" + +#include "osl/module.h" using namespace psp; using namespace rtl; @@ -69,7 +71,7 @@ using namespace rtl; * static helpers */ -#include <rtsname.hxx> +#include "rtsname.hxx" static oslModule driverLib = NULL; extern "C" @@ -723,58 +725,13 @@ BOOL PspSalInfoPrinter::SetData( } String aPaper; -#ifdef MACOSX - // For Mac OS X, many printers are directly attached - // USB/Serial printers with a stripped-down PPD that gives us - // problems. We need to do PS->PDF conversion for these printers - // but they are not able to handle multiple page sizes in the same - // document at all, since we must pass -o media=... to them to get - // a good printout. - // So, we must find a match between the paper size from OOo and what - // the PPD of the printer has, and pass that paper size to -o media=... - // If a match cannot be found (ie the paper size from Format->Page is - // nowhere near anything in the PPD), we default to what has been - // chosen in File->Print->Properties. - // - // For printers capable of directly accepting PostScript data, none - // of this occurs and we default to the normal OOo behavior. - const PPDKey *pCupsFilterKey; - const PPDValue *pCupsFilterValue; - BOOL bIsCUPSPrinter = TRUE; - - // Printers that need PS->PDF conversion have a "cupsFilter" key and - // a value of "application/pdf" in that key - pCupsFilterKey = aData.m_pParser->getKey( String(RTL_CONSTASCII_USTRINGPARAM("cupsFilter")) ); - pCupsFilterValue = pCupsFilterKey != NULL ? aData.m_aContext.getValue( pCupsFilterKey ) : NULL; - if ( pCupsFilterValue ) - { - // PPD had a cupsFilter key, check for PS->PDF conversion requirement - ByteString aCupsFilterString( pCupsFilterValue->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ); - if ( aCupsFilterString.Search("application/pdf") == 0 ) - bIsCUPSPrinter = FALSE; - } + if( pJobSetup->mePaperFormat == PAPER_USER ) + aPaper = aData.m_pParser->matchPaper( + TenMuToPt( pJobSetup->mnPaperWidth ), + TenMuToPt( pJobSetup->mnPaperHeight ) ); else - bIsCUPSPrinter = FALSE; + aPaper = String( ByteString( aPaperTab[ pJobSetup->mePaperFormat ].name ), RTL_TEXTENCODING_ISO_8859_1 ); - if ( TRUE == bIsCUPSPrinter ) - { - // If its a directly attached printer, with a - // stripped down PPD (most OS X printers are) always - // match the paper size. - aPaper = aData.m_pParser->matchPaper( - TenMuToPt( pJobSetup->mnPaperWidth ), - TenMuToPt( pJobSetup->mnPaperHeight ) ); - } - else -#endif - { - if( pJobSetup->mePaperFormat == PAPER_USER ) - aPaper = aData.m_pParser->matchPaper( - TenMuToPt( pJobSetup->mnPaperWidth ), - TenMuToPt( pJobSetup->mnPaperHeight ) ); - else - aPaper = String( ByteString( aPaperTab[ pJobSetup->mePaperFormat ].name ), RTL_TEXTENCODING_ISO_8859_1 ); - } pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL; if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) ) diff --git a/vcl/unx/source/gdi/xprintext.cxx b/vcl/unx/source/gdi/xprintext.cxx deleted file mode 100644 index d43185e34dea..000000000000 --- a/vcl/unx/source/gdi/xprintext.cxx +++ /dev/null @@ -1,656 +0,0 @@ -/************************************************************************ - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright 2008 by Sun Microsystems, Inc. - * - * OpenOffice.org - a multi-platform office productivity suite - * - * $RCSfile: xprintext.cxx,v $ - * $Revision: 1.12 $ - * - * This file is part of OpenOffice.org. - * - * OpenOffice.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 3 - * only, as published by the Free Software Foundation. - * - * OpenOffice.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3 for more details - * (a copy is included in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU Lesser General Public License - * version 3 along with OpenOffice.org. If not, see - * <http://www.openoffice.org/license.html> - * for a copy of the LGPLv3 License. - * - ************************************************************************/ - -// MARKER(update_precomp.py): autogen include statement, do not remove -#include "precompiled_vcl.hxx" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#if OSL_DEBUG_LEVEL == 0 -#define NDEBUG -#endif -#include <assert.h> - -#include <prex.h> -#include <X11/extensions/Print.h> -#include <postx.h> - -#include <salunx.h> -#include <saldata.hxx> -#include <saldisp.hxx> -#include <vcl/salinst.hxx> -#include <vcl/salprn.hxx> -#include <vcl/salgdi.hxx> -#include <salprn.h> -#include <vcl/print.h> -#include <vcl/jobset.h> -#include "i18n_im.hxx" -#include "i18n_xkb.hxx" - -// ======================================================================= -// -// ImplSalPrinterData -// -// ======================================================================= - - -class ImplSalPrinterData -{ - -private: - - SalDisplay* mpDisplay; - SalGraphics* mpGraphics; - char* mpPrinterName; - Display* mpXDisplay; - XPContext maContext; - Bool XprtConnectStatus; - - -private: - - ImplSalPrinterData( ImplSalPrinterData& rData ); - -public: - - ImplSalPrinterData(); - ~ImplSalPrinterData(); - - void Init( const SalPrinterQueueInfo* pQueueInfo, - ImplJobSetup* pJobSetup ); - - SalGraphics* GetGraphics(); - void ReleaseGraphics( SalGraphics* pGraphics = NULL ); - XLIB_Window GetDrawable() const { return mpDisplay->GetRootWindow(); } - SalColormap& GetColormap() const { return mpDisplay->GetColormap(); } - Display* GetXDisplay() const { return mpXDisplay; } - XPContext GetXContext() const { return maContext; } - const char* GetPrinter() const { return mpPrinterName; } - XPContext GetContext() const { return maContext; } - Bool GetStatus() const { return XprtConnectStatus; } -}; - -ImplSalPrinterData::ImplSalPrinterData() : - mpDisplay( NULL ), - mpGraphics( NULL ), - mpXDisplay( NULL ), - maContext( NULL ), - mpPrinterName( NULL ), - XprtConnectStatus( FALSE ) -{ - Init(NULL, NULL); -} - -void ImplSalPrinterData::Init( const SalPrinterQueueInfo* pQueueInfo, - ImplJobSetup* pJobSetup ) -{ - const char *printername = NULL; - - if (mpPrinterName == NULL || strcmp(mpPrinterName,printername)) { - int nCount; - XPContext aContext = NULL; - char *Xprinter = getenv("XPRINTER"); - char *XpDisplayIndex; - if (mpXDisplay == NULL && !XprtConnectStatus) { - - if (Xprinter && (XpDisplayIndex = strchr(Xprinter,'@'))) { - if (Xprinter != XpDisplayIndex && printername == NULL) { - char *defprinter = new char [XpDisplayIndex - Xprinter + 1]; - strncpy(defprinter, Xprinter, XpDisplayIndex - Xprinter); - defprinter[XpDisplayIndex - Xprinter] = '\0'; - printername = defprinter; - } - } - mpXDisplay = GetXpDisplay(); - // If GetXpDisplay() returns NULL (i.e. cannot connect to Xprint server) set XprtConnectStatus to FALSE. - if (mpXDisplay == NULL) { - fprintf(stderr, "Could not connect to Xprint server. Xprinting disabled.\n"); - XprtConnectStatus = FALSE; - } - else { - //fprintf(stderr, "Connected to Xprint server.\n"); - if( getenv( "SAL_SYNCHRONIZE" ) ) - XSynchronize( mpXDisplay, True ); - - if (printername == NULL || mpPrinterName == NULL - || strcmp(mpPrinterName,printername) || maContext == NULL) { - XpRehashPrinterList(mpXDisplay); - XPPrinterList pList = XpGetPrinterList (mpXDisplay, NULL, &nCount); - - for ( int i = 0; i < nCount; i++ ) { - //fprintf (stderr, "Printer %s: %s\n", - //pList[i].name ? pList[i].name : "(null)", - //pList[i].desc ? pList[i].desc : "(null)" ); - if(pList[i].name) - if (printername == NULL || strcmp (pList[i].name, printername) == 0) { - mpPrinterName = strdup( pList[i].name ); - maContext = XpCreateContext ( mpXDisplay, mpPrinterName ); - } - } - XpFreePrinterList (pList); - } - assert(maContext); - XpSetContext (mpXDisplay, maContext); - - // New Sal - if (mpDisplay == NULL) { - mpDisplay = new SalDisplay( mpXDisplay, NULL ); - SalI18N_InputMethod* pInputMethod = new SalI18N_InputMethod; - pInputMethod->Invalidate(); - mpDisplay->SetInputMethod( pInputMethod ); - SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( mpXDisplay ); - mpDisplay->SetKbdExtension( pKbdExtension ); - - } - // Connection to Xprint server successful so set XprtConnectStatus to TRUE. - XprtConnectStatus = TRUE; - } - mpGraphics = NULL; - } -} -} - -SalGraphics* -ImplSalPrinterData::GetGraphics() -{ - //If no Xprinter or mpGraphics already set then return NULL. - if ( mpGraphics || !XprtConnectStatus) { - return NULL; - } - mpGraphics = new SalGraphics; - mpGraphics->maGraphicsData.Init( this ); - - return mpGraphics; -} - -void -ImplSalPrinterData::ReleaseGraphics( SalGraphics* pGraphics ) -{ - if ( mpGraphics ) - { - assert( !(pGraphics && pGraphics != mpGraphics) ); - delete mpGraphics; - mpGraphics = NULL; - } -} - -ImplSalPrinterData::~ImplSalPrinterData() -{ - - if ( mpPrinterName != NULL ) - free( mpPrinterName ); - XpDestroyContext(mpXDisplay, maContext); - - delete mpGraphics; - delete mpDisplay; - - if ( mpXDisplay != NULL ) - XCloseDisplay( mpXDisplay ); -} - -// ======================================================================= -// -// SalInfoPrinterData -// -// ======================================================================= - -SalInfoPrinterData::SalInfoPrinterData() -{ - mpImplData = NULL; -} - -SalInfoPrinterData::~SalInfoPrinterData() -{ - delete mpImplData; -} - -void -SalInfoPrinterData::Init( - SalPrinterQueueInfo *pQueueInfo, - ImplJobSetup* pJobSetup ) -{ - mpImplData = new ImplSalPrinterData(); -} - -// ======================================================================= -// -// SalPrinterData -// -// ======================================================================= - -SalPrinterData::SalPrinterData() -{ - mpImplData = NULL; -} - -SalPrinterData::~SalPrinterData() -{ - delete mpImplData; -} - -void -SalPrinterData::Init( SalInfoPrinter *pInfoPrinter ) -{ - mpImplData = new ImplSalPrinterData(); -} - -// ======================================================================= -// -// SalInfoPrinter -// -// ======================================================================= - -SalInfoPrinter::SalInfoPrinter() -{ -} - -SalInfoPrinter::~SalInfoPrinter() -{ -} - -SalGraphics* -SalInfoPrinter::GetGraphics() -{ - return maPrinterData.mpImplData->GetGraphics(); -} - -void -SalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics ) -{ - maPrinterData.mpImplData->ReleaseGraphics( pGraphics ); -} - -BOOL -SalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pJobSetup ) -{ - pJobSetup->mePaperFormat = PAPER_A4; - pJobSetup->mnPaperWidth = 21000; - pJobSetup->mnPaperHeight = 29700; - pJobSetup->meOrientation = ORIENTATION_PORTRAIT; - return TRUE; -} - -BOOL -SalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup ) -{ - pJobSetup->mePaperFormat = PAPER_A4; - pJobSetup->mnPaperWidth = 21000; - pJobSetup->mnPaperHeight = 29700; - pJobSetup->meOrientation = ORIENTATION_PORTRAIT; - return TRUE; -} - -BOOL -SalInfoPrinter::SetData( ULONG nSetDataFlags, ImplJobSetup* pJobSetup ) -{ - pJobSetup->mePaperFormat = PAPER_A4; - pJobSetup->mnPaperWidth = 21000; - pJobSetup->mnPaperHeight = 29700; - pJobSetup->meOrientation = ORIENTATION_PORTRAIT; - return TRUE; -} - -void -SalInfoPrinter::GetPageInfo( const ImplJobSetup* pImplJobSetup, - long& rOutWidth, long& rOutHeight, - long& rPageOffX, long& rPageOffY, - long& rPageWidth, long& rPageHeight ) -{ - rPageWidth = 2550; - rPageHeight = 3300; - rPageOffX = 75; - rPageOffY = 75; - rOutWidth = rPageWidth - rPageOffX - 75; - rOutHeight = rPageHeight- rPageOffY - 75; -} - -ULONG -SalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup ) -{ - return 1; -} - -XubString -SalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, - ULONG nPaperBin ) -{ - return(XubString(RTL_CONSTASCII_USTRINGPARAM("PaperBinName"))); - // return "PaperBinName"; -} - -ULONG -SalInfoPrinter::GetCapabilities( const ImplJobSetup* pSetupData, USHORT nType ) -{ - return 0; -} - -// ======================================================================= -// -// SalPrinter -// -// ======================================================================= - -SalPrinter::SalPrinter() -{ -} - -SalPrinter::~SalPrinter() -{ -} - -BOOL -SalPrinter::StartJob( - const XubString* pFileName, - const XubString& rJobName, - const XubString& rAppName, - ULONG nCopies, BOOL bCollate, - ImplJobSetup* pJobSetup ) -{ - Display *pDisplay = maPrinterData.mpImplData->GetXDisplay(); - XPContext aContext = maPrinterData.mpImplData->GetXContext(); - const char* pPrinterName = maPrinterData.mpImplData->GetPrinter(); - - XpSelectInput (pDisplay, aContext, XPPrintMask); - - char pJobName[ 64 ]; - snprintf (pJobName, sizeof(pJobName), "%s.job-name: XPrint%d", pPrinterName, getpid() ); - - XpStartJob( pDisplay, XPSpool ); - - return TRUE; -} - -BOOL -SalPrinter::EndJob() -{ - Display *pDisplay = maPrinterData.mpImplData->GetXDisplay(); - XEvent aEvent;; - - XpEndJob( pDisplay ); - XSync( pDisplay, False ); - // Wait until printing is done - do - { - // XNextEvent (pDisplay, &aEvent); - } - while ( 0 ); - // aEvent.type != XPPrintNotify - // && ((XPPrintEvent *) (&aEvent))->detail != XPEndJobNotify); - - return TRUE; -} - -BOOL -SalPrinter::AbortJob() -{ - return FALSE; -} - -SalGraphics* -SalPrinter::StartPage( ImplJobSetup* pJobSetup, BOOL bNewJobData ) -{ - Display *pDisplay = maPrinterData.mpImplData->GetXDisplay(); - SalGraphics *pGraphics = maPrinterData.mpImplData->GetGraphics(); - - Drawable aDrawable = pGraphics->maGraphicsData.GetDrawable(); - XPContext nContext = maPrinterData.mpImplData->GetContext(); - - unsigned short nWidth, nHeight; - XRectangle aArea; - Status nState = XpGetPageDimensions( pDisplay, nContext, - &nWidth, &nHeight, &aArea ); - //fprintf(stderr, "PageSize = %ix%i (%i,%i %ix%i)\n", nWidth, nHeight, - // aArea.x,aArea.y, aArea.width, aArea.height ); - XResizeWindow( pDisplay, aDrawable, nWidth, nHeight ); - XpStartPage ( pDisplay, aDrawable ); - - return pGraphics; -} - -BOOL -SalPrinter::EndPage() -{ - Display *pDisplay = maPrinterData.mpImplData->GetXDisplay(); - XpEndPage ( pDisplay ); - - maPrinterData.mpImplData->ReleaseGraphics(); - - return TRUE; -} - -ULONG -SalPrinter::GetErrorCode() -{ - return 0; -} - -// ======================================================================= -// -// SalInstance -// -// ======================================================================= - -SalInfoPrinter* -SalInstance::CreateInfoPrinter( - SalPrinterQueueInfo* pQueueInfo, - ImplJobSetup* pSetup ) -{ - // create and initialize SalInfoPrinter - SalInfoPrinter* pPrinter = new SalInfoPrinter; - pPrinter->maPrinterData.Init( pQueueInfo, pSetup ); - - pSetup->mePaperFormat = PAPER_A4; // Papierformat - pSetup->mnPaperWidth = 21000; // Papierbreite in 100tel mm - pSetup->mnPaperHeight = 29700; // Papierhoehe in 100tel mm - - return pPrinter; -} - -void -SalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) -{ - delete pPrinter; -} - -SalPrinter* -SalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) -{ - // create and initialize SalPrinter - SalPrinter* pPrinter = new SalPrinter; - pPrinter->maPrinterData.Init( pInfoPrinter ); - - return pPrinter; -} - -void -SalInstance::DestroyPrinter( SalPrinter* pPrinter ) -{ - delete pPrinter; -} - - -void -SalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) -{ - // Neuen Eintrag anlegen - int nCount; - Display *XprtDisp = GetXpDisplay(); - if (XprtDisp == NULL) { - fprintf(stderr, "Could not connect to Xprint server. Xprinting disabled.\n"); - return; - } - else { - XpRehashPrinterList(XprtDisp); - XPPrinterList XpList = XpGetPrinterList(XprtDisp, NULL, &nCount); - - SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; - String Name(XpList[0].name, RTL_TEXTENCODING_UTF8); - pInfo->maPrinterName = XubString(Name); - pInfo->maDriver = XubString(RTL_CONSTASCII_USTRINGPARAM("X Printer")); - pInfo->maLocation = XubString(RTL_CONSTASCII_USTRINGPARAM("X Printer")); - pInfo->maComment = XubString(RTL_CONSTASCII_USTRINGPARAM("X Printer")); - pInfo->mpSysData = NULL; - pList->Add( pInfo ); - - XpFreePrinterList(XpList); - } -} - -void -SalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo ) -{ - return; -} - -void -SalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) -{ - delete pInfo; -} - -XubString -SalInstance::GetDefaultPrinter() -{ - Display *XprtDisp = GetXpDisplay(); - int nCount; - if (XprtDisp == NULL) - return XubString(RTL_CONSTASCII_USTRINGPARAM("No Default")); - else { - XpRehashPrinterList(XprtDisp); - XPPrinterList XpList = XpGetPrinterList(XprtDisp, NULL, &nCount); - - String Name( XpList[0].name, RTL_TEXTENCODING_UTF8 ); - XpFreePrinterList(XpList); - return (XubString(Name)); - } - - //return(XubString(RTL_CONSTASCII_USTRINGPARAM("X Printer"))); - // return "X Printer"; -} - -// ======================================================================= -// -// SalGraphicsData -// -// ======================================================================= - -void SalGraphicsData::Init(ImplSalPrinterData *pPrinter) -{ -if (pPrinter->GetStatus()) { - xColormap_ = &(pPrinter->GetColormap()); - hDrawable_ = pPrinter->GetDrawable(); - //pGCCache_ = pPrinter->GetGCCache(); - - bPrinter_ = TRUE; - - nPenPixel_ = GetPixel( nPenColor_ ); - nTextPixel_ = GetPixel( nTextColor_ ); - nBrushPixel_ = GetPixel( nBrushColor_ ); -} -else - bPrinter_ = FALSE; - -} - -// ======================================================================= -// -// Utility Functions -// -// ======================================================================= - -// GetXpDisplay(). -// Finds and returns the Xprint display. First looks at environment variable XPRINTER -// which should be in the form <printername>@<host>:<display number>. If not defined, -// then environment variable XPDISPLAY is checked. It should be in the form -// <host>:<display number>. If it is not defined it is set by default to ":1". If an -// Xprint server is found then a pointer to Display is returned, otherwise NULL. This -// function can be used by other functions to determine the current Xprint server display. - -// [ed] 6/15/02 We've got some linkage errors with this function on OS X, -// perhaps due to mismatched prototypes. Let's take a quick route to the finish -// line and declare it C linkage! +++ FIXME -#ifdef MACOSX -extern "C" -#endif -Display* -GetXpDisplay() -{ - char *XpDisplayName=NULL; - Display *XpDisplay; - if (getenv("XPRINTER")) { - XpDisplayName=strchr(getenv("XPRINTER"),'@'); - if (XpDisplayName != NULL) { - XpDisplayName++; - } - } - else { - if (!getenv("XPDISPLAY")) - putenv("XPDISPLAY=:1"); - XpDisplayName=getenv("XPDISPLAY"); - } - XpDisplay=XOpenDisplay(XpDisplayName); - if (XpDisplay==NULL || !XSalIsPrinter(XpDisplay)) { - return NULL; - } - else { - return XpDisplay; - } -} - -// [ed] 6/15/02 We've got some linkage errors with this function on OS X, -// perhaps due to mismatched prototypes. Let's take a quick route to the finish -// line and declare it C linkage! +++ FIXME -#ifdef MACOSX -extern "C" -#endif -Bool -XSalIsPrinter( Display * display ) -{ - int nEventBase; - int nErrorBase; - - Bool bPrinter = XpQueryExtension( display, &nEventBase, &nErrorBase ); - return bPrinter; -} - -// [ed] 6/15/02 We've got some linkage errors with this function on OS X, -// perhaps due to mismatched prototypes. Let's take a quick route to the finish -// line and declare it C linkage! +++ FIXME -#ifdef MACOSX -extern "C" -#endif -Bool -XSalIsDisplay( Display * display ) -{ - return !XSalIsPrinter( display ); -} - diff --git a/vcl/unx/source/gdi/xrender_peer.cxx b/vcl/unx/source/gdi/xrender_peer.cxx index 9f6e583ec723..861bf0e454aa 100644 --- a/vcl/unx/source/gdi/xrender_peer.cxx +++ b/vcl/unx/source/gdi/xrender_peer.cxx @@ -82,11 +82,7 @@ void XRenderPeer::InitRenderLib() // we don't know if we are running on a system with xrender library // we don't want to install system libraries ourselves // => load them dynamically when they are there -#ifdef MACOSX - OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libXrender.dylib" )); -#else OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libXrender.so.1" )); -#endif mpRenderLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT ); if( !mpRenderLib ) { #ifdef DEBUG diff --git a/vcl/unx/source/printer/cupsmgr.cxx b/vcl/unx/source/printer/cupsmgr.cxx new file mode 100644 index 000000000000..d0c7f184fb06 --- /dev/null +++ b/vcl/unx/source/printer/cupsmgr.cxx @@ -0,0 +1,1140 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: cupsmgr.cxx,v $ + * $Revision: 1.28 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#ifdef ENABLE_CUPS +#include <cups/cups.h> +#include <cups/ppd.h> + +#else // !ENABLE_CUPS +typedef void ppd_file_t; +typedef void cups_dest_t; +typedef void cups_option_t; +#endif + +#include <unistd.h> + +#include "cupsmgr.hxx" + +#include "osl/thread.h" +#include "osl/diagnose.h" +#include "osl/conditn.hxx" + +#include "rtl/ustrbuf.hxx" + +#include <algorithm> +#include <setjmp.h> +#include <signal.h> + +#define CUPS_LIB_NAME "libcups.so.2" + +namespace psp +{ +class CUPSWrapper +{ + oslModule m_pLib; + osl::Mutex m_aGetPPDMutex; + bool m_bPPDThreadRunning; + + int (*m_pcupsPrintFile)(const char*, const char*, const char*, int, cups_option_t*); + int (*m_pcupsGetDests)(cups_dest_t**); + void (*m_pcupsSetDests)(int,cups_dest_t*); + void (*m_pcupsFreeDests)(int,cups_dest_t*); + const char* (*m_pcupsGetPPD)(const char*); + int (*m_pcupsMarkOptions)(ppd_file_t*,int,cups_option_t*); + int (*m_pcupsAddOption)(const char*,const char*,int,cups_option_t**); + void (*m_pcupsFreeOptions)(int,cups_option_t*); + ppd_file_t* (*m_pppdOpenFile)(const char* pFile); + void (*m_pppdClose)(ppd_file_t*); + const char* (*m_pcupsServer)(); + void (*m_pcupsSetPasswordCB)(const char*(cb)(const char*)); + const char* (*m_pcupsUser)(); + void (*m_pcupsSetUser)(const char*); + const char* (*m_pcupsGetOption)(const char*,int,cups_option_t*); + + oslGenericFunction loadSymbol( const char* ); +public: + CUPSWrapper(); + ~CUPSWrapper(); + + bool isValid(); + + int cupsGetDests(cups_dest_t** pDests) + { return m_pcupsGetDests(pDests); } + + void cupsSetDests( int nDests, cups_dest_t* pDests ) + { m_pcupsSetDests( nDests, pDests ); } + + void cupsFreeDests(int nDests, cups_dest_t* pDests) + { m_pcupsFreeDests(nDests, pDests); } + + int cupsPrintFile( const char* pPrinter, + const char* pFileName, + const char* pTitle, + int nOptions, + cups_option_t* pOptions ) + { return m_pcupsPrintFile( pPrinter, pFileName, pTitle, nOptions, pOptions ); } + + rtl::OString cupsGetPPD( const char* pPrinter ); + + int cupsMarkOptions(ppd_file_t* pPPD, int nOptions, cups_option_t* pOptions ) + { return m_pcupsMarkOptions(pPPD, nOptions, pOptions); } + + int cupsAddOption( const char* pName, const char* pValue, int nOptions, cups_option_t** pOptions ) + { return m_pcupsAddOption( pName, pValue, nOptions, pOptions ); } + + void cupsFreeOptions( int nOptions, cups_option_t* pOptions ) + { m_pcupsFreeOptions( nOptions, pOptions ); } + + ppd_file_t* ppdOpenFile( const char* pFileName ) + { return m_pppdOpenFile( pFileName ); } + + void ppdClose( ppd_file_t* pPPD ) + { m_pppdClose( pPPD ); } + + const char *cupsServer(void) + { return m_pcupsServer(); } + + const char *cupsUser(void) + { return m_pcupsUser(); } + + void cupsSetPasswordCB(const char *(*cb)(const char *)) + { m_pcupsSetPasswordCB( cb ); } + + void cupsSetUser(const char *user) + { m_pcupsSetUser( user ); } + + const char* cupsGetOption(const char* name, int num_options, cups_option_t* options) + { return m_pcupsGetOption( name, num_options, options ); } + +}; +} + +using namespace psp; +using namespace osl; +using namespace rtl; + +/* + * CUPSWrapper class + */ + +oslGenericFunction CUPSWrapper::loadSymbol( const char* pSymbol ) +{ + OUString aSym( OUString::createFromAscii( pSymbol ) ); + oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" ); +#endif + return pSym; +} + +CUPSWrapper::CUPSWrapper() + : m_pLib( NULL ), + m_bPPDThreadRunning( false ) +{ +#ifdef ENABLE_CUPS + OUString aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + if( ! m_pLib ) + { + aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + } +#endif + + if( ! m_pLib ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "no cups library found\n" ); +#endif + return; + } + + m_pcupsPrintFile = (int(*)(const char*,const char*,const char*,int,cups_option_t*)) + loadSymbol( "cupsPrintFile" ); + m_pcupsGetDests = (int(*)(cups_dest_t**)) + loadSymbol( "cupsGetDests" ); + m_pcupsSetDests = (void(*)(int,cups_dest_t*)) + loadSymbol( "cupsSetDests" ); + m_pcupsFreeDests = (void(*)(int,cups_dest_t*)) + loadSymbol( "cupsFreeDests" ); + m_pcupsGetPPD = (const char*(*)(const char*)) + loadSymbol( "cupsGetPPD" ); + m_pcupsMarkOptions = (int(*)(ppd_file_t*,int,cups_option_t*)) + loadSymbol( "cupsMarkOptions" ); + m_pcupsAddOption = (int(*)(const char*,const char*,int,cups_option_t**)) + loadSymbol( "cupsAddOption" ); + m_pcupsFreeOptions = (void(*)(int,cups_option_t*)) + loadSymbol( "cupsFreeOptions" ); + m_pppdOpenFile = (ppd_file_t*(*)(const char*)) + loadSymbol( "ppdOpenFile" ); + m_pppdClose = (void(*)(ppd_file_t*)) + loadSymbol( "ppdClose" ); + m_pcupsServer = (const char*(*)()) + loadSymbol( "cupsServer" ); + m_pcupsUser = (const char*(*)()) + loadSymbol( "cupsUser" ); + m_pcupsSetPasswordCB = (void(*)(const char*(*)(const char*))) + loadSymbol( "cupsSetPasswordCB" ); + m_pcupsSetUser = (void(*)(const char*)) + loadSymbol( "cupsSetUser" ); + m_pcupsGetOption = (const char*(*)(const char*,int,cups_option_t*)) + loadSymbol( "cupsGetOption" ); + + if( ! ( + m_pcupsPrintFile && + m_pcupsGetDests && + m_pcupsSetDests && + m_pcupsFreeDests && + m_pcupsGetPPD && + m_pcupsMarkOptions && + m_pcupsAddOption && + m_pcupsServer && + m_pcupsUser && + m_pcupsSetPasswordCB && + m_pcupsSetUser && + m_pcupsFreeOptions && + m_pppdOpenFile && + m_pppdClose && + m_pcupsGetOption + ) ) + { + osl_unloadModule( m_pLib ); + m_pLib = NULL; + } +} + +CUPSWrapper::~CUPSWrapper() +{ + if( m_pLib ) + osl_unloadModule( m_pLib ); +} + +bool CUPSWrapper::isValid() +{ + return m_pLib != NULL; +} + +typedef const char*(*PPDFunction)(const char*); +struct GetPPDAttribs +{ + PPDFunction m_pFunction; + osl::Condition m_aCondition; + OString m_aParameter; + OString m_aResult; + oslThread m_aThread; + int m_nRefs; + bool* m_pResetRunning; + osl::Mutex* m_pSyncMutex; + + GetPPDAttribs( PPDFunction pFn, const char * m_pParameter, + bool* pResetRunning, osl::Mutex* pSyncMutex ) + : m_pFunction( pFn ), + m_aParameter( m_pParameter ), + m_pResetRunning( pResetRunning ), + m_pSyncMutex( pSyncMutex ) + { + m_nRefs = 2; + m_aCondition.reset(); + } + + ~GetPPDAttribs() + { + if( m_aResult.getLength() ) + unlink( m_aResult.getStr() ); + } + + void unref() + { + if( --m_nRefs == 0 ) + { + *m_pResetRunning = false; + delete this; + } + } + + void executeCall() + { + // This CUPS method is not at all thread-safe we need + // to dup the pointer to a static buffer it returns ASAP + OString aResult = m_pFunction( m_aParameter ); + MutexGuard aGuard( *m_pSyncMutex ); + m_aResult = aResult; + m_aCondition.set(); + unref(); + } + + OString waitResult( TimeValue *pDelay ) + { + m_pSyncMutex->release(); + + if (m_aCondition.wait( pDelay ) != Condition::result_ok + ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cupsGetPPD %s timed out\n", + (const sal_Char *) m_aParameter + ); + #endif + } + m_pSyncMutex->acquire(); + + OString aRetval = m_aResult; + m_aResult = OString(); + unref(); + + return aRetval; + } +}; + +extern "C" { + static void getPPDWorker(void* pData) + { + GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData; + pAttribs->executeCall(); + } +} + +OString CUPSWrapper::cupsGetPPD( const char* pPrinter ) +{ + OString aResult; + + m_aGetPPDMutex.acquire(); + // if one thread hangs in cupsGetPPD already, don't start another + if( ! m_bPPDThreadRunning ) + { + m_bPPDThreadRunning = true; + GetPPDAttribs* pAttribs = new GetPPDAttribs( m_pcupsGetPPD, + pPrinter, + &m_bPPDThreadRunning, + &m_aGetPPDMutex ); + + oslThread aThread = osl_createThread( getPPDWorker, pAttribs ); + + TimeValue aValue; + aValue.Seconds = 5; + aValue.Nanosec = 0; + + // NOTE: waitResult release and acquires the GetPPD mutex + aResult = pAttribs->waitResult( &aValue ); + osl_destroyThread( aThread ); + } + m_aGetPPDMutex.release(); + + return aResult; +} + +#ifdef ENABLE_CUPS +static const char* setPasswordCallback( const char* pIn ) +{ + const char* pRet = NULL; + + PrinterInfoManager& rMgr = PrinterInfoManager::get(); + if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check + pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn ); + return pRet; +} +#endif + +/* + * CUPSManager class + */ + +CUPSManager* CUPSManager::tryLoadCUPS() +{ + CUPSManager* pManager = NULL; +#ifdef ENABLE_CUPS + static const char* pEnv = getenv( "SAL_DISABLE_CUPS" ); + + if( ! pEnv || ! *pEnv ) + { + // try to load CUPS + CUPSWrapper* pWrapper = new CUPSWrapper(); + if( pWrapper->isValid() ) + pManager = new CUPSManager( pWrapper ); + else + delete pWrapper; + } +#endif + return pManager; +} + +extern "C" +{ +static void run_dest_thread_stub( void* pThis ) +{ + CUPSManager::runDestThread( pThis ); +} +} + +CUPSManager::CUPSManager( CUPSWrapper* pWrapper ) : + PrinterInfoManager( CUPS ), + m_pCUPSWrapper( pWrapper ), + m_nDests( 0 ), + m_pDests( NULL ), + m_bNewDests( false ) +{ + m_aDestThread = osl_createThread( run_dest_thread_stub, this ); +} + +CUPSManager::~CUPSManager() +{ + if( m_aDestThread ) + { + // if the thread is still running here, then + // cupsGetDests is hung; terminate the thread instead of joining + osl_terminateThread( m_aDestThread ); + osl_destroyThread( m_aDestThread ); + } + + if( m_nDests && m_pDests ) + m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); + delete m_pCUPSWrapper; +} + +void CUPSManager::runDestThread( void* pThis ) +{ + ((CUPSManager*)pThis)->runDests(); +} + +static sigjmp_buf aViolationBuffer; + +extern "C" +{ + static void lcl_signal_action(int nSignal) + { + fprintf( stderr, "Signal %d during fontconfig initialization called, ignoring fontconfig\n", nSignal ); + siglongjmp( aViolationBuffer, 1 ); + } +} + +void CUPSManager::runDests() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "starting cupsGetDests\n" ); +#endif + int nDests = 0; + cups_dest_t* pDests = NULL; + + // #i86306# prepare against really broken CUPS installations / missing servers + + // install signal handler for SEGV, BUS and ABRT + struct sigaction act; + struct sigaction oact[3]; + + act.sa_handler = lcl_signal_action; + act.sa_flags = 0; + sigemptyset(&(act.sa_mask)); + + int nSegvSignalInstalled = sigaction(SIGSEGV, &act, &oact[0]); + int nBusSignalInstalled = sigaction(SIGBUS, &act, &oact[1]); + int nAbortSignalInstalled = sigaction(SIGABRT, &act, &oact[2]); + + // prepare against a signal during FcInit or FcConfigGetCurrent + if( sigsetjmp( aViolationBuffer, ~0 ) == 0 ) + { + nDests = m_pCUPSWrapper->cupsGetDests( &pDests ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "came out of cupsGetDests\n" ); + #endif + + osl::MutexGuard aGuard( m_aCUPSMutex ); + m_nDests = nDests; + m_pDests = pDests; + m_bNewDests = true; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "finished cupsGetDests\n" ); + #endif + } + else + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cupsGetDests crashed, not using CUPS\n" ); + #endif + } + + // restore old signal handlers + if( nSegvSignalInstalled == 0 ) + sigaction( SIGSEGV, &oact[0], NULL ); + if( nBusSignalInstalled == 0 ) + sigaction( SIGBUS, &oact[1], NULL ); + if( nAbortSignalInstalled == 0 ) + sigaction( SIGABRT, &oact[2], NULL ); +} + +void CUPSManager::initialize() +{ + // get normal printers, clear printer list + PrinterInfoManager::initialize(); + +#ifdef ENABLE_CUPS + // check whether thread has completed + // if not behave like old printing system + osl::MutexGuard aGuard( m_aCUPSMutex ); + + if( ! m_bNewDests ) + return; + + // dest thread has run, clean up + if( m_aDestThread ) + { + osl_joinWithThread( m_aDestThread ); + osl_destroyThread( m_aDestThread ); + m_aDestThread = NULL; + } + m_bNewDests = false; + + // clear old stuff + m_aCUPSDestMap.clear(); + + if( ! (m_nDests && m_pDests ) ) + return; + + if( isCUPSDisabled() ) + return; + + // check for CUPS server(?) > 1.2 + // since there is no API to query, check for options that were + // introduced in dests with 1.2 + // this is needed to check for %%IncludeFeature support + // (#i65684#, #i65491#) + cups_dest_t* pDest = ((cups_dest_t*)m_pDests); + const char* pOpt = m_pCUPSWrapper->cupsGetOption( "printer-info", + pDest->num_options, + pDest->options ); + if( pOpt ) + m_bUseIncludeFeature = true; + + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + int nPrinter = m_nDests; + + // reset global default PPD options; these are queried on demand from CUPS + m_aGlobalDefaults.m_pParser = NULL; + m_aGlobalDefaults.m_aContext = PPDContext(); + + // add CUPS printers, should there be a printer + // with the same name as a CUPS printer, overwrite it + while( nPrinter-- ) + { + pDest = ((cups_dest_t*)m_pDests)+nPrinter; + OUString aPrinterName = OStringToOUString( pDest->name, aEncoding ); + if( pDest->instance && *pDest->instance ) + { + OUStringBuffer aBuf( 256 ); + aBuf.append( aPrinterName ); + aBuf.append( sal_Unicode( '/' ) ); + aBuf.append( OStringToOUString( pDest->instance, aEncoding ) ); + aPrinterName = aBuf.makeStringAndClear(); + } + + // initialize printer with possible configuration from psprint.conf + bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end(); + Printer aPrinter = m_aPrinters[ aPrinterName ]; + if( bSetToGlobalDefaults ) + aPrinter.m_aInfo = m_aGlobalDefaults; + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + if( pDest->is_default ) + m_aDefaultPrinter = aPrinterName; + + for( int k = 0; k < pDest->num_options; k++ ) + { + if(!strcmp(pDest->options[k].name, "printer-info")) + aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding); + if(!strcmp(pDest->options[k].name, "printer-location")) + aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding); + } + + + OUStringBuffer aBuf( 256 ); + aBuf.appendAscii( "CUPS:" ); + aBuf.append( aPrinterName ); + // note: the parser that goes with the PrinterInfo + // is created implicitly by the JobData::operator=() + // when it detects the NULL ptr m_pParser. + // if we wanted to fill in the parser here this + // would mean we'd have to download PPDs for each and + // every printer - which would be really bad runtime + // behaviour + aPrinter.m_aInfo.m_pParser = NULL; + aPrinter.m_aInfo.m_aContext.setParser( NULL ); + std::hash_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName ); + if( c_it != m_aDefaultContexts.end() ) + { + aPrinter.m_aInfo.m_pParser = c_it->second.getParser(); + aPrinter.m_aInfo.m_aContext = c_it->second; + } + aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear(); + aPrinter.m_bModified = false; + + m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter; + m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter; + } + + // remove everything that is not a CUPS printer and not + // a special purpose printer (PDF, Fax) + std::list< OUString > aRemovePrinters; + for( std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin(); + it != m_aPrinters.end(); ++it ) + { + if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() ) + continue; + + if( it->second.m_aInfo.m_aFeatures.getLength() > 0 ) + continue; + aRemovePrinters.push_back( it->first ); + } + while( aRemovePrinters.begin() != aRemovePrinters.end() ) + { + m_aPrinters.erase( aRemovePrinters.front() ); + aRemovePrinters.pop_front(); + } + + m_pCUPSWrapper->cupsSetPasswordCB( setPasswordCallback ); +#endif // ENABLE_CUPS +} + +#ifdef ENABLE_CUPS +static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext ) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + for( int i = 0; i < pPPDGroup->num_options; i++ ) + { + ppd_option_t* pOption = pPPDGroup->options + i; + for( int n = 0; n < pOption->num_choices; n++ ) + { + ppd_choice_t* pChoice = pOption->choices + n; + if( pChoice->marked ) + { + const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) ); + if( pKey ) + { + const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) ); + if( pValue ) + { + if( pValue != pKey->getDefaultValue() ) + { + rContext.setValue( pKey, pValue, true ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "key %s is set to %s\n", pOption->keyword, pChoice->choice ); +#endif + + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword ); +#endif + } + } + } + + // recurse through subgroups + for( int g = 0; g < pPPDGroup->num_subgroups; g++ ) + { + updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext ); + } +} +#endif // ENABLE_CUPS + +const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter ) +{ + const PPDParser* pNewParser = NULL; + OUString aPrinter; + + if( rPrinter.compareToAscii( "CUPS:", 5 ) == 0 ) + aPrinter = rPrinter.copy( 5 ); + else + aPrinter = rPrinter; + +#ifdef ENABLE_CUPS + if( m_aCUPSMutex.tryToAcquire() ) + { + if( m_nDests && m_pDests && ! isCUPSDisabled() ) + { + std::hash_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( aPrinter ); + if( dest_it != m_aCUPSDestMap.end() ) + { + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; + OString aPPDFile = m_pCUPSWrapper->cupsGetPPD( pDest->name ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PPD for %s is %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr(), aPPDFile.getStr() ); + #endif + if( aPPDFile.getLength() ) + { + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) ); + // update the printer info with context information + ppd_file_t* pPPD = m_pCUPSWrapper->ppdOpenFile( aPPDFile.getStr() ); + if( pPPD ) + { + // create the new parser + PPDParser* pCUPSParser = new PPDParser( aFileName ); + pCUPSParser->m_aFile = rPrinter; + pNewParser = pCUPSParser; + + /*int nConflicts =*/ m_pCUPSWrapper->cupsMarkOptions( pPPD, pDest->num_options, pDest->options ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "processing the following options for printer %s (instance %s):\n", + pDest->name, pDest->instance ); + for( int k = 0; k < pDest->num_options; k++ ) + fprintf( stderr, " \"%s\" = \"%s\"\n", + pDest->options[k].name, + pDest->options[k].value ); + #endif + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + + // remember the default context for later use + PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; + rContext.setParser( pNewParser ); + // set system default paper; printer CUPS PPD options + // may overwrite it + setDefaultPaper( rContext ); + for( int i = 0; i < pPPD->num_groups; i++ ) + updatePrinterContextInfo( pPPD->groups + i, rContext ); + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext = rContext; + + // clean up the mess + m_pCUPSWrapper->ppdClose( pPPD ); + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" ); + #endif + + // remove temporary PPD file + unlink( aPPDFile.getStr() ); + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" ); + #endif + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() ); + #endif + } + m_aCUPSMutex.release(); + } + #if OSL_DEBUG_LEVEL >1 + else + fprintf( stderr, "could not acquire CUPS mutex !!!\n" ); + #endif + #endif // ENABLE_CUPS + + if( ! pNewParser ) + { + // get the default PPD + pNewParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ); + + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext.setParser( pNewParser ); + } + + return pNewParser; +} + +void CUPSManager::setupJobContextData( + JobData& +#ifdef ENABLE_CUPS + rData +#endif +) +{ +#ifdef ENABLE_CUPS + std::hash_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( rData.m_aPrinterName ); + + if( dest_it == m_aCUPSDestMap.end() ) + return PrinterInfoManager::setupJobContextData( rData ); + + std::hash_map< OUString, Printer, OUStringHash >::iterator p_it = + m_aPrinters.find( rData.m_aPrinterName ); + if( p_it == m_aPrinters.end() ) // huh ? + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData.m_aPrinterName, osl_getThreadTextEncoding() ).getStr() ); +#endif + return; + } + + if( p_it->second.m_aInfo.m_pParser == NULL ) + { + // in turn calls createCUPSParser + // which updates the printer info + p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName ); + } + if( p_it->second.m_aInfo.m_aContext.getParser() == NULL ) + { + OUString aPrinter; + if( p_it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) == 0 ) + aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 ); + else + aPrinter = p_it->second.m_aInfo.m_aDriverName; + + p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ]; + } + + rData.m_pParser = p_it->second.m_aInfo.m_pParser; + rData.m_aContext = p_it->second.m_aInfo.m_aContext; +#endif +} + +FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) +{ + if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() ) + return PrinterInfoManager::startSpool( rPrintername, bQuickCommand ); + +#ifdef ENABLE_CUPS + OUString aTmpURL, aTmpFile; + osl_createTempFile( NULL, NULL, &aTmpURL.pData ); + osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData ); + OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() ); + FILE* fp = fopen( aSysFile.getStr(), "w" ); + if( fp ) + m_aSpoolFiles[fp] = aSysFile; + + return fp; +#else + return NULL; +#endif +} + +struct less_ppd_key : public ::std::binary_function<double, double, bool> +{ + bool operator()(const PPDKey* left, const PPDKey* right) + { return left->getOrderDependency() < right->getOrderDependency(); } +}; + +void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, int& rNumOptions, void** rOptions ) const +{ + rNumOptions = 0; + *rOptions = NULL; + int i; + + // emit features ordered to OrderDependency + // ignore features that are set to default + + // sanity check + if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser ) + { + int nKeys = rJob.m_aContext.countValuesModified(); + ::std::vector< const PPDKey* > aKeys( nKeys ); + for( i = 0; i < nKeys; i++ ) + aKeys[i] = rJob.m_aContext.getModifiedKey( i ); + ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); + + for( i = 0; i < nKeys; i++ ) + { + const PPDKey* pKey = aKeys[i]; + const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); + if(pValue && pValue->m_eType == eInvocation && pValue->m_aValue.Len() ) + { + OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ); + OString aValue = OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ); + rNumOptions = m_pCUPSWrapper->cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, (cups_option_t**)rOptions ); + } + } + } +} + +int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData ) +{ + int nJobID = 0; + + osl::MutexGuard aGuard( m_aCUPSMutex ); + + std::hash_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( rPrintername ); + if( dest_it == m_aCUPSDestMap.end() ) + return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData ); + + #ifdef ENABLE_CUPS + std::hash_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); + if( it != m_aSpoolFiles.end() ) + { + fclose( pFile ); + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + + // setup cups options + int nNumOptions = 0; + cups_option_t* pOptions = NULL; + getOptionsFromDocumentSetup( rDocumentJobData, nNumOptions, (void**)&pOptions ); + + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; + nJobID = m_pCUPSWrapper->cupsPrintFile( pDest->name, + it->second.getStr(), + OUStringToOString( rJobTitle, aEnc ).getStr(), + nNumOptions, pOptions ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n", + pDest->name, + it->second.getStr(), + OUStringToOString( rJobTitle, aEnc ).getStr(), + nNumOptions, + pOptions, + nJobID + ); + for( int n = 0; n < nNumOptions; n++ ) + fprintf( stderr, " option %s=%s\n", pOptions[n].name, pOptions[n].value ); + OString aCmd( "cp " ); + aCmd = aCmd + it->second; + aCmd = aCmd + OString( " $HOME/cupsprint.ps" ); + system( aCmd.getStr() ); +#endif + + unlink( it->second.getStr() ); + m_aSpoolFiles.erase( pFile ); + if( pOptions ) + m_pCUPSWrapper->cupsFreeOptions( nNumOptions, pOptions ); + } +#endif // ENABLE_CUPS + + return nJobID; +} + + +void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ) +{ + PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo ); +} + +bool CUPSManager::checkPrintersChanged( bool bWait ) +{ + bool bChanged = false; + if( bWait ) + { + if( m_aDestThread ) + { + // initial asynchronous detection still running + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "syncing cups discovery thread\n" ); + #endif + osl_joinWithThread( m_aDestThread ); + osl_destroyThread( m_aDestThread ); + m_aDestThread = NULL; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "done: syncing cups discovery thread\n" ); + #endif + } + else + { + // #i82321# check for cups printer updates + // with this change the whole asynchronous detection in a thread is + // almost useless. The only relevance left is for some stalled systems + // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION + // (see vcl/unx/source/gdi/salprnpsp.cxx) + // so that checkPrintersChanged( true ) will never be called + + // there is no way to query CUPS whether the printer list has changed + // so get the dest list anew + if( m_nDests && m_pDests ) + m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); + m_nDests = 0; + m_pDests = NULL; + runDests(); + } + } + if( m_aCUPSMutex.tryToAcquire() ) + { + bChanged = m_bNewDests; + m_aCUPSMutex.release(); + } + + if( ! bChanged ) + { + bChanged = PrinterInfoManager::checkPrintersChanged( bWait ); + // #i54375# ensure new merging with CUPS list in :initialize + if( bChanged ) + m_bNewDests = true; + } + + if( bChanged ) + initialize(); + + return bChanged; +} + +bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver ) +{ + // don't touch the CUPS printers + if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() || + rDriver.compareToAscii( "CUPS:", 5 ) == 0 + ) + return false; + return PrinterInfoManager::addPrinter( rName, rDriver ); +} + +bool CUPSManager::removePrinter( const OUString& rName, bool bCheck ) +{ + // don't touch the CUPS printers + if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ) + return false; + return PrinterInfoManager::removePrinter( rName, bCheck ); +} + +bool CUPSManager::setDefaultPrinter( const OUString& rName ) +{ + bool bSuccess = false; +#ifdef ENABLE_CUPS + std::hash_map< OUString, int, OUStringHash >::iterator nit = + m_aCUPSDestMap.find( rName ); + if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() ) + { + cups_dest_t* pDests = (cups_dest_t*)m_pDests; + for( int i = 0; i < m_nDests; i++ ) + pDests[i].is_default = 0; + pDests[ nit->second ].is_default = 1; + m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); + m_aDefaultPrinter = rName; + m_aCUPSMutex.release(); + bSuccess = true; + } + else +#endif + bSuccess = PrinterInfoManager::setDefaultPrinter( rName ); + + return bSuccess; +} + +bool CUPSManager::writePrinterConfig() +{ +#ifdef ENABLE_CUPS + bool bDestModified = false; + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + + for( std::hash_map< OUString, Printer, OUStringHash >::iterator prt = + m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt ) + { + std::hash_map< OUString, int, OUStringHash >::iterator nit = + m_aCUPSDestMap.find( prt->first ); + if( nit == m_aCUPSDestMap.end() ) + continue; + + if( ! prt->second.m_bModified ) + continue; + + if( m_aCUPSMutex.tryToAcquire() ) + { + bDestModified = true; + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + nit->second; + PrinterInfo& rInfo = prt->second.m_aInfo; + + // create new option list + int nNewOptions = 0; + cups_option_t* pNewOptions = NULL; + int nValues = rInfo.m_aContext.countValuesModified(); + for( int i = 0; i < nValues; i++ ) + { + const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i ); + const PPDValue* pValue = rInfo.m_aContext.getValue( pKey ); + if( pKey && pValue ) // sanity check + { + OString aName = OUStringToOString( pKey->getKey(), aEncoding ); + OString aValue = OUStringToOString( pValue->m_aOption, aEncoding ); + nNewOptions = m_pCUPSWrapper->cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions ); + } + } + // set PPD options on CUPS dest + m_pCUPSWrapper->cupsFreeOptions( pDest->num_options, pDest->options ); + pDest->num_options = nNewOptions; + pDest->options = pNewOptions; + m_aCUPSMutex.release(); + } + } + if( bDestModified && m_aCUPSMutex.tryToAcquire() ) + { + m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); + m_aCUPSMutex.release(); + } +#endif // ENABLE_CUPS + + return PrinterInfoManager::writePrinterConfig(); +} + +bool CUPSManager::addOrRemovePossible() const +{ + return (m_nDests && m_pDests && ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible(); +} + +#include <rtsname.hxx> + +const char* CUPSManager::authenticateUser( const char* /*pIn*/ ) +{ + const char* pRet = NULL; + +#ifdef ENABLE_CUPS + OUString aLib = OUString::createFromAscii( _XSALSET_LIBNAME ); + oslModule pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + if( pLib ) + { + OUString aSym( RTL_CONSTASCII_USTRINGPARAM( "Sal_authenticateQuery" ) ); + bool (*getpw)( const OString& rServer, OString& rUser, OString& rPw) = + (bool(*)(const OString&,OString&,OString&))osl_getFunctionSymbol( pLib, aSym.pData ); + if( getpw ) + { + osl::MutexGuard aGuard( m_aCUPSMutex ); + + OString aUser = m_pCUPSWrapper->cupsUser(); + OString aServer = m_pCUPSWrapper->cupsServer(); + OString aPassword; + if( getpw( aServer, aUser, aPassword ) ) + { + m_aPassword = aPassword; + m_aUser = aUser; + m_pCUPSWrapper->cupsSetUser( m_aUser.getStr() ); + pRet = m_aPassword.getStr(); + } + } + osl_unloadModule( pLib ); + } +#if OSL_DEBUG_LEVEL > 1 + else fprintf( stderr, "loading of module %s failed\n", OUStringToOString( aLib, osl_getThreadTextEncoding() ).getStr() ); +#endif +#endif // ENABLE_CUPS + + return pRet; +} diff --git a/vcl/unx/source/printer/jobdata.cxx b/vcl/unx/source/printer/jobdata.cxx new file mode 100644 index 000000000000..51e171d578d9 --- /dev/null +++ b/vcl/unx/source/printer/jobdata.cxx @@ -0,0 +1,207 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: jobdata.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vcl/jobdata.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "tools/stream.hxx" + +#include "sal/alloca.h" + +using namespace psp; +using namespace rtl; + +JobData& JobData::operator=(const JobData& rRight) +{ + m_nCopies = rRight.m_nCopies; + m_nLeftMarginAdjust = rRight.m_nLeftMarginAdjust; + m_nRightMarginAdjust = rRight.m_nRightMarginAdjust; + m_nTopMarginAdjust = rRight.m_nTopMarginAdjust; + m_nBottomMarginAdjust = rRight.m_nBottomMarginAdjust; + m_nColorDepth = rRight.m_nColorDepth; + m_eOrientation = rRight.m_eOrientation; + m_aPrinterName = rRight.m_aPrinterName; + m_pParser = rRight.m_pParser; + m_aContext = rRight.m_aContext; + m_nPSLevel = rRight.m_nPSLevel; + m_nColorDevice = rRight.m_nColorDevice; + + if( ! m_pParser && m_aPrinterName.getLength() ) + { + PrinterInfoManager& rMgr = PrinterInfoManager::get(); + rMgr.setupJobContextData( *this ); + } + return *this; +} + +bool JobData::getStreamBuffer( void*& pData, int& bytes ) +{ + // consistency checks + if( ! m_pParser ) + m_pParser = m_aContext.getParser(); + if( m_pParser != m_aContext.getParser() || + ! m_pParser ) + return false; + + SvMemoryStream aStream; + ByteString aLine; + + // write header job data + aStream.WriteLine( "JobData 1" ); + + aLine = "printer="; + aLine += ByteString( String( m_aPrinterName ), RTL_TEXTENCODING_UTF8 ); + aStream.WriteLine( aLine ); + + aLine = "orientation="; + aLine += m_eOrientation == orientation::Landscape ? "Landscape" : "Portrait"; + aStream.WriteLine( aLine ); + + aLine = "copies="; + aLine += ByteString::CreateFromInt32( m_nCopies ); + aStream.WriteLine( aLine ); + + aLine = "margindajustment="; + aLine += ByteString::CreateFromInt32( m_nLeftMarginAdjust ); + aLine += ','; + aLine += ByteString::CreateFromInt32( m_nRightMarginAdjust ); + aLine += ','; + aLine += ByteString::CreateFromInt32( m_nTopMarginAdjust ); + aLine += ','; + aLine += ByteString::CreateFromInt32( m_nBottomMarginAdjust ); + aStream.WriteLine( aLine ); + + aLine = "colordepth="; + aLine += ByteString::CreateFromInt32( m_nColorDepth ); + aStream.WriteLine( aLine ); + + aLine = "pslevel="; + aLine += ByteString::CreateFromInt32( m_nPSLevel ); + aStream.WriteLine( aLine ); + + aLine = "colordevice="; + aLine += ByteString::CreateFromInt32( m_nColorDevice ); + aStream.WriteLine( aLine ); + + // now append the PPDContext stream buffer + aStream.WriteLine( "PPDContexData" ); + ULONG nBytes; + void* pContextBuffer = m_aContext.getStreamableBuffer( nBytes ); + if( nBytes ) + aStream.Write( pContextBuffer, nBytes ); + + // success + pData = rtl_allocateMemory( bytes = aStream.Tell() ); + memcpy( pData, aStream.GetData(), bytes ); + return true; +} + +bool JobData::constructFromStreamBuffer( void* pData, int bytes, JobData& rJobData ) +{ + SvMemoryStream aStream( pData, bytes, STREAM_READ ); + ByteString aLine; + bool bVersion = false; + bool bPrinter = false; + bool bOrientation = false; + bool bCopies = false; + bool bContext = false; + bool bMargin = false; + bool bColorDepth = false; + bool bColorDevice = false; + bool bPSLevel = false; + while( ! aStream.IsEof() ) + { + aStream.ReadLine( aLine ); + if( aLine.CompareTo( "JobData", 7 ) == COMPARE_EQUAL ) + bVersion = true; + else if( aLine.CompareTo( "printer=", 8 ) == COMPARE_EQUAL ) + { + bPrinter = true; + rJobData.m_aPrinterName = String( aLine.Copy( 8 ), RTL_TEXTENCODING_UTF8 ); + } + else if( aLine.CompareTo( "orientation=", 12 ) == COMPARE_EQUAL ) + { + bOrientation = true; + rJobData.m_eOrientation = aLine.Copy( 12 ).EqualsIgnoreCaseAscii( "landscape" ) ? orientation::Landscape : orientation::Portrait; + } + else if( aLine.CompareTo( "copies=", 7 ) == COMPARE_EQUAL ) + { + bCopies = true; + rJobData.m_nCopies = aLine.Copy( 7 ).ToInt32(); + } + else if( aLine.CompareTo( "margindajustment=",17 ) == COMPARE_EQUAL ) + { + bMargin = true; + ByteString aValues( aLine.Copy( 17 ) ); + rJobData.m_nLeftMarginAdjust = aValues.GetToken( 0, ',' ).ToInt32(); + rJobData.m_nRightMarginAdjust = aValues.GetToken( 1, ',' ).ToInt32(); + rJobData.m_nTopMarginAdjust = aValues.GetToken( 2, ',' ).ToInt32(); + rJobData.m_nBottomMarginAdjust = aValues.GetToken( 3, ',' ).ToInt32(); + } + else if( aLine.CompareTo( "colordepth=", 11 ) == COMPARE_EQUAL ) + { + bColorDepth = true; + rJobData.m_nColorDepth = aLine.Copy( 11 ).ToInt32(); + } + else if( aLine.CompareTo( "colordevice=", 12 ) == COMPARE_EQUAL ) + { + bColorDevice = true; + rJobData.m_nColorDevice = aLine.Copy( 12 ).ToInt32(); + } + else if( aLine.CompareTo( "pslevel=", 8 ) == COMPARE_EQUAL ) + { + bPSLevel = true; + rJobData.m_nPSLevel = aLine.Copy( 8 ).ToInt32(); + } + else if( aLine.Equals( "PPDContexData" ) ) + { + if( bPrinter ) + { + PrinterInfoManager& rManager = PrinterInfoManager::get(); + const PrinterInfo& rInfo = rManager.getPrinterInfo( rJobData.m_aPrinterName ); + rJobData.m_pParser = PPDParser::getParser( rInfo.m_aDriverName ); + if( rJobData.m_pParser ) + { + rJobData.m_aContext.setParser( rJobData.m_pParser ); + int nBytes = bytes - aStream.Tell(); + void* pRemain = alloca( bytes - aStream.Tell() ); + aStream.Read( pRemain, nBytes ); + rJobData.m_aContext.rebuildFromStreamBuffer( pRemain, nBytes ); + bContext = true; + } + } + } + } + + return bVersion && bPrinter && bOrientation && bCopies && bContext && bMargin && bPSLevel && bColorDevice && bColorDepth; +} diff --git a/vcl/unx/source/printer/makefile.mk b/vcl/unx/source/printer/makefile.mk new file mode 100644 index 000000000000..df184adc00fa --- /dev/null +++ b/vcl/unx/source/printer/makefile.mk @@ -0,0 +1,74 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.9 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +ENABLE_EXCEPTIONS=TRUE +PRJNAME=vcl +TARGET=printer + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +.IF "$(ENABLE_CUPS)" != "" +CDEFS += -DENABLE_CUPS +.ENDIF + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"=="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"=="aqua" + +SLOFILES=\ + $(SLO)$/ppdparser.obj \ + $(SLO)$/printerinfomanager.obj \ + $(SLO)$/jobdata.obj \ + $(SLO)$/cupsmgr.obj + +.ENDIF # GUIBASE = aqua + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +XSALSETLIBNAME=$(DLLPRE)spa$(DLLPOSTFIX)$(DLLPOST) + +$(INCCOM)$/rtsname.hxx: + rm -f $(INCCOM)$/rtsname.hxx ; \ + echo "#define _XSALSET_LIBNAME "\"$(XSALSETLIBNAME)\" > $(INCCOM)$/rtsname.hxx + +$(SLO)$/cupsmgr.obj : $(INCCOM)$/rtsname.hxx + diff --git a/vcl/unx/source/printer/ppdparser.cxx b/vcl/unx/source/printer/ppdparser.cxx new file mode 100644 index 000000000000..1caf64ef7e2c --- /dev/null +++ b/vcl/unx/source/printer/ppdparser.cxx @@ -0,0 +1,1876 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ppdparser.cxx,v $ + * $Revision: 1.27 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <stdio.h> + +#include <hash_map> + +#include "vcl/ppdparser.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/helper.hxx" +#include "cupsmgr.hxx" +#include "tools/debug.hxx" +#include "tools/urlobj.hxx" +#include "tools/stream.hxx" +#include "tools/zcodec.hxx" +#include "osl/mutex.hxx" +#include "osl/file.hxx" +#include "osl/process.h" +#include "osl/thread.h" +#include "rtl/strbuf.hxx" +#include "rtl/ustrbuf.hxx" + +using namespace psp; +using namespace rtl; + +#undef DBG_ASSERT +#if defined DBG_UTIL || (OSL_DEBUG_LEVEL > 1) +#define BSTRING(x) ByteString( x, osl_getThreadTextEncoding() ) +#define DBG_ASSERT( x, y ) { if( ! (x) ) fprintf( stderr, (y) ); } +#else +#define DBG_ASSERT( x, y ) +#endif + +std::list< PPDParser* > PPDParser::aAllParsers; +std::hash_map< OUString, OUString, OUStringHash >* PPDParser::pAllPPDFiles = NULL; +static String aEmptyString; + +class PPDDecompressStream +{ + SvFileStream* mpFileStream; + SvMemoryStream* mpMemStream; + rtl::OUString maFileName; + + // forbid copying + PPDDecompressStream( const PPDDecompressStream& ); + PPDDecompressStream& operator=(const PPDDecompressStream& ); + + public: + PPDDecompressStream( const rtl::OUString& rFile ); + ~PPDDecompressStream(); + + bool IsOpen() const; + bool IsEof() const; + void ReadLine( ByteString& o_rLine); + void Open( const rtl::OUString& i_rFile ); + void Close(); + const rtl::OUString& GetFileName() const { return maFileName; } +}; + +PPDDecompressStream::PPDDecompressStream( const rtl::OUString& i_rFile ) : + mpFileStream( NULL ), + mpMemStream( NULL ) +{ + Open( i_rFile ); +} + +PPDDecompressStream::~PPDDecompressStream() +{ + Close(); +} + +void PPDDecompressStream::Open( const rtl::OUString& i_rFile ) +{ + Close(); + + mpFileStream = new SvFileStream( i_rFile, STREAM_READ ); + maFileName = mpFileStream->GetFileName(); + + if( ! mpFileStream->IsOpen() ) + { + Close(); + return; + } + + ByteString aLine; + mpFileStream->ReadLine( aLine ); + mpFileStream->Seek( 0 ); + + // check for compress'ed or gzip'ed file + ULONG nCompressMethod = 0; + if( aLine.Len() > 1 && static_cast<unsigned char>(aLine.GetChar( 0 )) == 0x1f ) + { + if( static_cast<unsigned char>(aLine.GetChar( 1 )) == 0x8b ) // check for gzip + nCompressMethod = ZCODEC_DEFAULT | ZCODEC_GZ_LIB; + } + + if( nCompressMethod != 0 ) + { + // so let's try to decompress the stream + mpMemStream = new SvMemoryStream( 4096, 4096 ); + ZCodec aCodec; + aCodec.BeginCompression( nCompressMethod ); + long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream ); + aCodec.EndCompression(); + if( nComp < 0 ) + { + // decompression failed, must be an uncompressed stream after all + delete mpMemStream, mpMemStream = NULL; + mpFileStream->Seek( 0 ); + } + else + { + // compression successfull, can get rid of file stream + delete mpFileStream, mpFileStream = NULL; + mpMemStream->Seek( 0 ); + } + } +} + +void PPDDecompressStream::Close() +{ + delete mpMemStream, mpMemStream = NULL; + delete mpFileStream, mpFileStream = NULL; +} + +bool PPDDecompressStream::IsOpen() const +{ + return (mpMemStream || (mpFileStream && mpFileStream->IsOpen())); +} + +bool PPDDecompressStream::IsEof() const +{ + return ( mpMemStream ? mpMemStream->IsEof() : ( mpFileStream ? mpFileStream->IsEof() : true ) ); +} + +void PPDDecompressStream::ReadLine( ByteString& o_rLine ) +{ + if( mpMemStream ) + mpMemStream->ReadLine( o_rLine ); + else if( mpFileStream ) + mpFileStream->ReadLine( o_rLine ); +} + +static osl::FileBase::RC resolveLink( const rtl::OUString& i_rURL, rtl::OUString& o_rResolvedURL, rtl::OUString& o_rBaseName, osl::FileStatus::Type& o_rType, int nLinkLevel = 10 ) +{ + osl::DirectoryItem aLinkItem; + osl::FileBase::RC aRet = osl::FileBase::E_None; + + if( ( aRet = osl::DirectoryItem::get( i_rURL, aLinkItem ) ) == osl::FileBase::E_None ) + { + osl::FileStatus aStatus( FileStatusMask_FileName | FileStatusMask_Type | FileStatusMask_LinkTargetURL ); + if( ( aRet = aLinkItem.getFileStatus( aStatus ) ) == osl::FileBase::E_None ) + { + if( aStatus.getFileType() == osl::FileStatus::Link ) + { + if( nLinkLevel > 0 ) + aRet = resolveLink( aStatus.getLinkTargetURL(), o_rResolvedURL, o_rBaseName, o_rType, nLinkLevel-1 ); + else + aRet = osl::FileBase::E_MULTIHOP; + } + else + { + o_rResolvedURL = i_rURL; + o_rBaseName = aStatus.getFileName(); + o_rType = aStatus.getFileType(); + } + } + } + return aRet; +} + +void PPDParser::scanPPDDir( const String& rDir ) +{ + static struct suffix_t + { + const sal_Char* pSuffix; + const sal_Int32 nSuffixLen; + } const pSuffixes[] = + { { ".PS", 3 }, { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } }; + + const int nSuffixes = sizeof(pSuffixes)/sizeof(pSuffixes[0]); + + osl::Directory aDir( rDir ); + aDir.open(); + osl::DirectoryItem aItem; + + INetURLObject aPPDDir(rDir); + while( aDir.getNextItem( aItem ) == osl::FileBase::E_None ) + { + osl::FileStatus aStatus( FileStatusMask_FileName ); + if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None ) + { + rtl::OUStringBuffer aURLBuf( rDir.Len() + 64 ); + aURLBuf.append( rDir ); + aURLBuf.append( sal_Unicode( '/' ) ); + aURLBuf.append( aStatus.getFileName() ); + + rtl::OUString aFileURL, aFileName; + osl::FileStatus::Type eType = osl::FileStatus::Unknown; + + if( resolveLink( aURLBuf.makeStringAndClear(), aFileURL, aFileName, eType ) == osl::FileBase::E_None ) + { + if( eType == osl::FileStatus::Regular ) + { + INetURLObject aPPDFile = aPPDDir; + aPPDFile.Append( aFileName ); + + // match extension + for( int nSuffix = 0; nSuffix < nSuffixes; nSuffix++ ) + { + if( aFileName.getLength() > pSuffixes[nSuffix].nSuffixLen ) + { + if( aFileName.endsWithIgnoreAsciiCaseAsciiL( pSuffixes[nSuffix].pSuffix, pSuffixes[nSuffix].nSuffixLen ) ) + { + (*pAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - pSuffixes[nSuffix].nSuffixLen ) ] = aPPDFile.PathToFileName(); + break; + } + } + } + } + else if( eType == osl::FileStatus::Directory ) + { + scanPPDDir( aFileURL ); + } + } + } + } + aDir.close(); +} + +void PPDParser::initPPDFiles() +{ + if( pAllPPDFiles ) + return; + + pAllPPDFiles = new std::hash_map< OUString, OUString, OUStringHash >(); + + // check installation directories + std::list< OUString > aPathList; + psp::getPrinterPathList( aPathList, PRINTER_PPDDIR ); + for( std::list< OUString >::const_iterator ppd_it = aPathList.begin(); ppd_it != aPathList.end(); ++ppd_it ) + { + INetURLObject aPPDDir( *ppd_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + scanPPDDir( aPPDDir.GetMainURL( INetURLObject::NO_DECODE ) ); + } + if( pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ) + { + // last try: search in directory of executable (mainly for setup) + OUString aExe; + if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None ) + { + INetURLObject aDir( aExe ); + aDir.removeSegment(); +#ifdef DEBUG + fprintf( stderr, "scanning last chance dir: %s\n", OUStringToOString( aDir.GetMainURL( INetURLObject::NO_DECODE ), osl_getThreadTextEncoding() ).getStr() ); +#endif + scanPPDDir( aDir.GetMainURL( INetURLObject::NO_DECODE ) ); +#ifdef DEBUG + fprintf( stderr, "SGENPRT %s\n", pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ? "not found" : "found" ); +#endif + } + } +} + +void PPDParser::getKnownPPDDrivers( std::list< rtl::OUString >& o_rDrivers ) +{ + initPPDFiles(); + o_rDrivers.clear(); + + std::hash_map< OUString, OUString, OUStringHash >::const_iterator it; + for( it = pAllPPDFiles->begin(); it != pAllPPDFiles->end(); ++it ) + o_rDrivers.push_back( it->first ); +} + +String PPDParser::getPPDFile( const String& rFile ) +{ + INetURLObject aPPD( rFile, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + // someone might enter a full qualified name here + PPDDecompressStream aStream( aPPD.PathToFileName() ); + if( ! aStream.IsOpen() ) + { + std::hash_map< OUString, OUString, OUStringHash >::const_iterator it; + + bool bRetry = true; + do + { + initPPDFiles(); + // some PPD files contain dots beside the extension, so try name first + // and cut of points after that + rtl::OUString aBase( rFile ); + sal_Int32 nLastIndex = aBase.lastIndexOf( sal_Unicode( '/' ) ); + if( nLastIndex >= 0 ) + aBase = aBase.copy( nLastIndex+1 ); + do + { + it = pAllPPDFiles->find( aBase ); + nLastIndex = aBase.lastIndexOf( sal_Unicode( '.' ) ); + if( nLastIndex > 0 ) + aBase = aBase.copy( 0, nLastIndex ); + } while( it == pAllPPDFiles->end() && nLastIndex > 0 ); + + if( it == pAllPPDFiles->end() && bRetry ) + { + // a new file ? rehash + delete pAllPPDFiles; pAllPPDFiles = NULL; + bRetry = false; + // note this is optimized for office start where + // no new files occur and initPPDFiles is called only once + } + } while( ! pAllPPDFiles ); + + if( it != pAllPPDFiles->end() ) + aStream.Open( it->second ); + } + + String aRet; + if( aStream.IsOpen() ) + { + ByteString aLine; + aStream.ReadLine( aLine ); + if( aLine.Search( "*PPD-Adobe" ) == 0 ) + aRet = aStream.GetFileName(); + else + { + // our *Include hack does usually not begin + // with *PPD-Adobe, so try some lines for *Include + int nLines = 10; + while( aLine.Search( "*Include" ) != 0 && --nLines ) + aStream.ReadLine( aLine ); + if( nLines ) + aRet = aStream.GetFileName(); + } + } + + return aRet; +} + +String PPDParser::getPPDPrinterName( const String& rFile ) +{ + String aPath = getPPDFile( rFile ); + String aName; + + // read in the file + PPDDecompressStream aStream( aPath ); + if( aStream.IsOpen() ) + { + String aCurLine; + while( ! aStream.IsEof() && aStream.IsOpen() ) + { + ByteString aByteLine; + aStream.ReadLine( aByteLine ); + aCurLine = String( aByteLine, RTL_TEXTENCODING_MS_1252 ); + if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL ) + { + aCurLine.Erase( 0, 9 ); + aCurLine.EraseLeadingChars( ' ' ); + aCurLine.EraseTrailingChars( ' ' ); + aCurLine.EraseLeadingChars( '\t' ); + aCurLine.EraseTrailingChars( '\t' ); + aCurLine.EraseTrailingChars( '\r' ); + aCurLine.EraseTrailingChars( '\n' ); + aCurLine.EraseLeadingChars( '"' ); + aCurLine.EraseTrailingChars( '"' ); + aStream.Close(); + aStream.Open( getPPDFile( aCurLine ) ); + continue; + } + if( aCurLine.CompareToAscii( "*ModelName:", 11 ) == COMPARE_EQUAL ) + { + aName = aCurLine.GetToken( 1, '"' ); + break; + } + else if( aCurLine.CompareToAscii( "*NickName:", 10 ) == COMPARE_EQUAL ) + aName = aCurLine.GetToken( 1, '"' ); + } + } + return aName; +} + +const PPDParser* PPDParser::getParser( const String& rFile ) +{ + static ::osl::Mutex aMutex; + ::osl::Guard< ::osl::Mutex > aGuard( aMutex ); + + String aFile = rFile; + if( rFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL ) + aFile = getPPDFile( rFile ); + if( ! aFile.Len() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Could not get printer PPD file \"%s\" !\n", OUStringToOString( rFile, osl_getThreadTextEncoding() ).getStr() ); +#endif + return NULL; + } + + for( ::std::list< PPDParser* >::const_iterator it = aAllParsers.begin(); it != aAllParsers.end(); ++it ) + if( (*it)->m_aFile == aFile ) + return *it; + + PPDParser* pNewParser = NULL; + if( aFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL ) + pNewParser = new PPDParser( aFile ); + else + { + PrinterInfoManager& rMgr = PrinterInfoManager::get(); + if( rMgr.getType() == PrinterInfoManager::CUPS ) + { + pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile )); + } + } + if( pNewParser ) + { + // this may actually be the SGENPRT parser, + // so ensure uniquness here + aAllParsers.remove( pNewParser ); + // insert new parser to list + aAllParsers.push_front( pNewParser ); + } + return pNewParser; +} + +void PPDParser::freeAll() +{ + while( aAllParsers.begin() != aAllParsers.end() ) + { + delete aAllParsers.front(); + aAllParsers.pop_front(); + } + delete pAllPPDFiles; + pAllPPDFiles = NULL; +} + +PPDParser::PPDParser( const String& rFile ) : + m_aFile( rFile ), + m_bType42Capable( false ), + m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ), + m_pDefaultImageableArea( NULL ), + m_pImageableAreas( NULL ), + m_pDefaultPaperDimension( NULL ), + m_pPaperDimensions( NULL ), + m_pDefaultInputSlot( NULL ), + m_pInputSlots( NULL ), + m_pDefaultResolution( NULL ), + m_pResolutions( NULL ), + m_pDefaultDuplexType( NULL ), + m_pDuplexTypes( NULL ), + m_pFontList( NULL ) +{ + // read in the file + std::list< ByteString > aLines; + PPDDecompressStream aStream( m_aFile ); + bool bLanguageEncoding = false; + if( aStream.IsOpen() ) + { + ByteString aCurLine; + while( ! aStream.IsEof() ) + { + aStream.ReadLine( aCurLine ); + if( aCurLine.GetChar( 0 ) == '*' ) + { + if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL ) + { + aCurLine.Erase( 0, 9 ); + aCurLine.EraseLeadingChars( ' ' ); + aCurLine.EraseTrailingChars( ' ' ); + aCurLine.EraseLeadingChars( '\t' ); + aCurLine.EraseTrailingChars( '\t' ); + aCurLine.EraseTrailingChars( '\r' ); + aCurLine.EraseTrailingChars( '\n' ); + aCurLine.EraseLeadingChars( '"' ); + aCurLine.EraseTrailingChars( '"' ); + aStream.Close(); + aStream.Open( getPPDFile( String( aCurLine, m_aFileEncoding ) ) ); + continue; + } + else if( ! bLanguageEncoding && + aCurLine.CompareIgnoreCaseToAscii( "*languageencoding", 17 ) == COMPARE_EQUAL ) + { + bLanguageEncoding = true; // generally only the first one counts + ByteString aLower = aCurLine; + aLower.ToLowerAscii(); + if( aLower.Search( "isolatin1", 17 ) != STRING_NOTFOUND || + aLower.Search( "windowsansi", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_MS_1252; + else if( aLower.Search( "isolatin2", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2; + else if( aLower.Search( "isolatin5", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5; + else if( aLower.Search( "jis83-rksj", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS; + else if( aLower.Search( "macstandard", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN; + else if( aLower.Search( "utf-8", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_UTF8; + } + } + aLines.push_back( aCurLine ); + } + } + aStream.Close(); + + // now get the Values + parse( aLines ); +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "acquired %d Keys from PPD %s:\n", m_aKeys.size(), BSTRING( m_aFile ).GetBuffer() ); + for( PPDParser::hash_type::const_iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it ) + { + const PPDKey* pKey = it->second; + char* pSetupType = "<unknown>"; + switch( pKey->m_eSetupType ) + { + case PPDKey::ExitServer: pSetupType = "ExitServer";break; + case PPDKey::Prolog: pSetupType = "Prolog";break; + case PPDKey::DocumentSetup: pSetupType = "DocumentSetup";break; + case PPDKey::PageSetup: pSetupType = "PageSetup";break; + case PPDKey::JCLSetup: pSetupType = "JCLSetup";break; + case PPDKey::AnySetup: pSetupType = "AnySetup";break; + default: break; + }; + fprintf( stderr, "\t\"%s\" (\"%s\") (%d values) OrderDependency: %d %s\n", + BSTRING( pKey->getKey() ).GetBuffer(), + BSTRING( pKey->m_aUITranslation ).GetBuffer(), + pKey->countValues(), + pKey->m_nOrderDependency, + pSetupType ); + for( int j = 0; j < pKey->countValues(); j++ ) + { + fprintf( stderr, "\t\t" ); + const PPDValue* pValue = pKey->getValue( j ); + if( pValue == pKey->m_pDefaultValue ) + fprintf( stderr, "(Default:) " ); + char* pVType = "<unknown>"; + switch( pValue->m_eType ) + { + case eInvocation: pVType = "invocation";break; + case eQuoted: pVType = "quoted";break; + case eString: pVType = "string";break; + case eSymbol: pVType = "symbol";break; + case eNo: pVType = "no";break; + default: break; + }; + fprintf( stderr, "option: \"%s\" (\"%s\"), value: type %s \"%s\" (\"%s\")\n", + BSTRING( pValue->m_aOption ).GetBuffer(), + BSTRING( pValue->m_aOptionTranslation ).GetBuffer(), + pVType, + BSTRING( pValue->m_aValue ).GetBuffer(), + BSTRING( pValue->m_aValueTranslation ).GetBuffer() ); + } + } + fprintf( stderr, "constraints: (%d found)\n", m_aConstraints.size() ); + for( std::list< PPDConstraint >::const_iterator cit = m_aConstraints.begin(); cit != m_aConstraints.end(); ++cit ) + { + fprintf( stderr, "*\"%s\" \"%s\" *\"%s\" \"%s\"\n", + BSTRING( cit->m_pKey1->getKey() ).GetBuffer(), + cit->m_pOption1 ? BSTRING( cit->m_pOption1->m_aOption ).GetBuffer() : "<nil>", + BSTRING( cit->m_pKey2->getKey() ).GetBuffer(), + cit->m_pOption2 ? BSTRING( cit->m_pOption2->m_aOption ).GetBuffer() : "<nil>" + ); + } +#endif + + // fill in shortcuts + const PPDKey* pKey; + + m_pImageableAreas = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ImageableArea" ) ) ); + if( m_pImageableAreas ) + m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue(); + DBG_ASSERT( m_pImageableAreas, "Warning: no ImageableArea in PPD\n" ); + DBG_ASSERT( m_pDefaultImageableArea, "Warning: no DefaultImageableArea in PPD\n" ); + + m_pPaperDimensions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PaperDimension" ) ) ); + if( m_pPaperDimensions ) + m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue(); + DBG_ASSERT( m_pPaperDimensions, "Warning: no PaperDimension in PPD\n" ); + DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultPaperDimension in PPD\n" ); + + m_pResolutions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) ); + if( m_pResolutions ) + m_pDefaultResolution = m_pResolutions->getDefaultValue(); + DBG_ASSERT( m_pResolutions, "Warning: no Resolution in PPD\n" ); + DBG_ASSERT( m_pDefaultResolution, "Warning: no DefaultResolution in PPD\n" ); + + m_pInputSlots = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ); + if( m_pInputSlots ) + m_pDefaultInputSlot = m_pInputSlots->getDefaultValue(); + DBG_ASSERT( m_pPaperDimensions, "Warning: no InputSlot in PPD\n" ); + DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultInputSlot in PPD\n" ); + + m_pDuplexTypes = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) ); + if( m_pDuplexTypes ) + m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue(); + + m_pFontList = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Font" ) ) ); + DBG_ASSERT( m_pFontList, "Warning: no Font in PPD\n" ); + + // fill in direct values + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ModelName" ) ) )) ) + m_aPrinterName = pKey->getValue( 0 )->m_aValue; + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "NickName" ) ) )) ) + m_aNickName = pKey->getValue( 0 )->m_aValue; + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ColorDevice" ) ) )) ) + m_bColorDevice = pKey->getValue( 0 )->m_aValue.CompareIgnoreCaseToAscii( "true", 4 ) == COMPARE_EQUAL ? true : false; + + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "LanguageLevel" ) ) )) ) + m_nLanguageLevel = pKey->getValue( 0 )->m_aValue.ToInt32(); + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "TTRasterizer" ) ) )) ) + m_bType42Capable = pKey->getValue( 0 )->m_aValue.EqualsIgnoreCaseAscii( "Type42" ) ? true : false; +} + +PPDParser::~PPDParser() +{ + for( PPDParser::hash_type::iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it ) + delete it->second; +} + +void PPDParser::insertKey( const String& rKey, PPDKey* pKey ) +{ + m_aKeys[ rKey ] = pKey; + m_aOrderedKeys.push_back( pKey ); +} + +const PPDKey* PPDParser::getKey( int n ) const +{ + return ((unsigned int)n < m_aOrderedKeys.size() && n >= 0) ? m_aOrderedKeys[n] : NULL; +} + +const PPDKey* PPDParser::getKey( const String& rKey ) const +{ + PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey ); + return it != m_aKeys.end() ? it->second : NULL; +} + +bool PPDParser::hasKey( const PPDKey* pKey ) const +{ + return + pKey ? + ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() ? true : false ) : + false; +} + +static sal_uInt8 getNibble( sal_Char cChar ) +{ + sal_uInt8 nRet = 0; + if( cChar >= '0' && cChar <= '9' ) + nRet = sal_uInt8( cChar - '0' ); + else if( cChar >= 'A' && cChar <= 'F' ) + nRet = 10 + sal_uInt8( cChar - 'A' ); + else if( cChar >= 'a' && cChar <= 'f' ) + nRet = 10 + sal_uInt8( cChar - 'a' ); + return nRet; +} + +String PPDParser::handleTranslation( const ByteString& rString ) +{ + int nOrigLen = rString.Len(); + OStringBuffer aTrans( nOrigLen ); + const sal_Char* pStr = rString.GetBuffer(); + const sal_Char* pEnd = pStr + nOrigLen; + while( pStr < pEnd ) + { + if( *pStr == '<' ) + { + pStr++; + sal_Char cChar; + while( *pStr != '>' && pStr < pEnd-1 ) + { + cChar = getNibble( *pStr++ ) << 4; + cChar |= getNibble( *pStr++ ); + aTrans.append( cChar ); + } + pStr++; + } + else + aTrans.append( *pStr++ ); + } + return OStringToOUString( aTrans.makeStringAndClear(), m_aFileEncoding ); +} + +void PPDParser::parse( ::std::list< ByteString >& rLines ) +{ + PPDValue* pValue = NULL; + PPDKey* pKey = NULL; + + std::list< ByteString >::iterator line = rLines.begin(); + PPDParser::hash_type::const_iterator keyit; + while( line != rLines.end() ) + { + ByteString aCurrentLine( *line ); + ++line; + if( aCurrentLine.GetChar(0) != '*' ) + continue; + if( aCurrentLine.GetChar(1) == '%' ) + continue; + + ByteString aKey = GetCommandLineToken( 0, aCurrentLine.GetToken( 0, ':' ) ); + int nPos = aKey.Search( '/' ); + if( nPos != STRING_NOTFOUND ) + aKey.Erase( nPos ); + aKey.Erase( 0, 1 ); // remove the '*' + + if( aKey.Equals( "CloseUI" ) || aKey.Equals( "OpenGroup" ) || aKey.Equals( "CloseGroup" ) || aKey.Equals( "End" ) || aKey.Equals( "OpenSubGroup" ) || aKey.Equals( "CloseSubGroup" ) ) + continue; + + if( aKey.Equals( "OpenUI" ) ) + { + parseOpenUI( aCurrentLine ); + continue; + } + else if( aKey.Equals( "OrderDependency" ) ) + { + parseOrderDependency( aCurrentLine ); + continue; + } + else if( aKey.Equals( "UIConstraints" ) || aKey.Equals( "NonUIConstraints" ) ) + continue; // parsed in pass 2 + else if( aKey.Equals( "CustomPageSize" ) ) // currently not handled + continue; + + // default values are parsed in pass 2 + if( aKey.CompareTo( "Default", 7 ) == COMPARE_EQUAL ) + continue; + + bool bQuery = false; + if( aKey.GetChar( 0 ) == '?' ) + { + aKey.Erase( 0, 1 ); + bQuery = true; + } + + String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 ); + keyit = m_aKeys.find( aUniKey ); + if( keyit == m_aKeys.end() ) + { + pKey = new PPDKey( aUniKey ); + insertKey( aUniKey, pKey ); + } + else + pKey = keyit->second; + + String aOption; + nPos = aCurrentLine.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + { + aOption = String( aCurrentLine.Copy( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 ); + aOption = GetCommandLineToken( 1, aOption ); + int nTransPos = aOption.Search( '/' ); + if( nTransPos != STRING_NOTFOUND ) + aOption.Erase( nTransPos ); + } + pValue = pKey->insertValue( aOption ); + if( ! pValue ) + continue; + + if( nPos == STRING_NOTFOUND ) + { + // have a single main keyword + pValue->m_eType = eNo; + if( bQuery ) + pKey->eraseValue( aOption ); + continue; + } + + // found a colon, there may be an option + ByteString aLine = aCurrentLine.Copy( 1, nPos-1 ); + aLine = WhitespaceToSpace( aLine ); + int nTransPos = aLine.Search( '/' ); + if( nTransPos != STRING_NOTFOUND ) + pValue->m_aOptionTranslation = handleTranslation( aLine.Copy( nTransPos+1 ) ); + + // read in more lines if necessary for multiline values + aLine = aCurrentLine.Copy( nPos+1 ); + while( ! ( aLine.GetTokenCount( '"' ) & 1 ) && + line != rLines.end() ) + // while there is an even number of tokens; that m_eans + // an odd number of doubleqoutes + { + // copy the newlines also + aLine += '\n'; + aLine += *line; + ++line; + } + aLine = WhitespaceToSpace( aLine ); + + // check for invocation or quoted value + if( aLine.GetChar(0) == '"' ) + { + aLine.Erase( 0, 1 ); + nTransPos = aLine.Search( '"' ); + pValue->m_aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 ); + // after the second doublequote can follow a / and a translation + pValue->m_aValueTranslation = handleTranslation( aLine.Copy( nTransPos+2 ) ); + // check for quoted value + if( pValue->m_aOption.Len() && + aKey.CompareTo( "JCL", 3 ) != COMPARE_EQUAL ) + pValue->m_eType = eInvocation; + else + pValue->m_eType = eQuoted; + } + // check for symbol value + else if( aLine.GetChar(0) == '^' ) + { + aLine.Erase( 0, 1 ); + pValue->m_aValue = String( aLine, RTL_TEXTENCODING_MS_1252 ); + pValue->m_eType = eSymbol; + } + else + { + // must be a string value then + // strictly this is false because string values + // can contain any whitespace which is reduced + // to one space by now + // who cares ... + nTransPos = aLine.Search( '/' ); + if( nTransPos == STRING_NOTFOUND ) + nTransPos = aLine.Len(); + pValue->m_aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 ); + pValue->m_aValueTranslation = handleTranslation( aLine.Copy( nTransPos+1 ) ); + pValue->m_eType = eString; + } + + // eventually update query and remove from option list + if( bQuery && pKey->m_bQueryValue == FALSE ) + { + pKey->m_aQueryValue = *pValue; + pKey->m_bQueryValue = true; + pKey->eraseValue( pValue->m_aOption ); + } + } + + // second pass: fill in defaults + for( line = rLines.begin(); line != rLines.end(); ++line ) + { + ByteString aLine( *line ); + if( aLine.CompareTo( "*Default", 8 ) == COMPARE_EQUAL ) + { + String aKey( aLine.Copy( 8 ), RTL_TEXTENCODING_MS_1252 ); + USHORT nPos = aKey.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + { + aKey.Erase( nPos ); + String aOption( WhitespaceToSpace( aLine.Copy( nPos+9 ) ), RTL_TEXTENCODING_MS_1252 ); + keyit = m_aKeys.find( aKey ); + if( keyit != m_aKeys.end() ) + { + pKey = keyit->second; + const PPDValue* pDefValue = pKey->getValue( aOption ); + if( pKey->m_pDefaultValue == NULL ) + pKey->m_pDefaultValue = pDefValue; + } + else + { + // some PPDs contain defaults for keys that + // do not exist otherwise + // (example: DefaultResolution) + // so invent that key here and have a default value + pKey = new PPDKey( aKey ); + PPDValue* pNewValue = pKey->insertValue( aOption ); + pNewValue->m_eType = eInvocation; // or what ? + insertKey( aKey, pKey ); + } + } + } + else if( aLine.CompareTo( "*UIConstraints", 14 ) == COMPARE_EQUAL || + aLine.CompareTo( "*NonUIConstraints", 17 ) == COMPARE_EQUAL ) + parseConstraint( aLine ); + + } +} + +void PPDParser::parseOpenUI( const ByteString& rLine ) +{ + String aTranslation; + ByteString aKey = rLine; + + int nPos = aKey.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + aKey.Erase( nPos ); + nPos = aKey.Search( '/' ); + if( nPos != STRING_NOTFOUND ) + { + aTranslation = handleTranslation( aKey.Copy( nPos + 1 ) ); + aKey.Erase( nPos ); + } + aKey = GetCommandLineToken( 1, aKey ); + aKey.Erase( 0, 1 ); + + String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 ); + PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey ); + PPDKey* pKey; + if( keyit == m_aKeys.end() ) + { + pKey = new PPDKey( aUniKey ); + insertKey( aUniKey, pKey ); + } + else + pKey = keyit->second; + + pKey->m_bUIOption = true; + pKey->m_aUITranslation = aTranslation; + + ByteString aValue = WhitespaceToSpace( rLine.GetToken( 1, ':' ) ); + if( aValue.CompareIgnoreCaseToAscii( "boolean" ) == COMPARE_EQUAL ) + pKey->m_eUIType = PPDKey::Boolean; + else if( aValue.CompareIgnoreCaseToAscii( "pickmany" ) == COMPARE_EQUAL ) + pKey->m_eUIType = PPDKey::PickMany; + else + pKey->m_eUIType = PPDKey::PickOne; +} + +void PPDParser::parseOrderDependency( const ByteString& rLine ) +{ + ByteString aLine( rLine ); + int nPos = aLine.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + aLine.Erase( 0, nPos+1 ); + + int nOrder = GetCommandLineToken( 0, aLine ).ToInt32(); + ByteString aSetup = GetCommandLineToken( 1, aLine ); + String aKey( GetCommandLineToken( 2, aLine ), RTL_TEXTENCODING_MS_1252 ); + if( aKey.GetChar( 0 ) != '*' ) + return; // invalid order depency + aKey.Erase( 0, 1 ); + + PPDKey* pKey; + PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey ); + if( keyit == m_aKeys.end() ) + { + pKey = new PPDKey( aKey ); + insertKey( aKey, pKey ); + } + else + pKey = keyit->second; + + pKey->m_nOrderDependency = nOrder; + if( aSetup.Equals( "ExitServer" ) ) + pKey->m_eSetupType = PPDKey::ExitServer; + else if( aSetup.Equals( "Prolog" ) ) + pKey->m_eSetupType = PPDKey::Prolog; + else if( aSetup.Equals( "DocumentSetup" ) ) + pKey->m_eSetupType = PPDKey::DocumentSetup; + else if( aSetup.Equals( "PageSetup" ) ) + pKey->m_eSetupType = PPDKey::PageSetup; + else if( aSetup.Equals( "JCLSetup" ) ) + pKey->m_eSetupType = PPDKey::JCLSetup; + else + pKey->m_eSetupType = PPDKey::AnySetup; +} + +void PPDParser::parseConstraint( const ByteString& rLine ) +{ + bool bFailed = false; + + String aLine( rLine, RTL_TEXTENCODING_MS_1252 ); + aLine.Erase( 0, rLine.Search( ':' )+1 ); + PPDConstraint aConstraint; + int nTokens = GetCommandLineTokenCount( aLine ); + for( int i = 0; i < nTokens; i++ ) + { + String aToken = GetCommandLineToken( i, aLine ); + if( aToken.GetChar( 0 ) == '*' ) + { + aToken.Erase( 0, 1 ); + if( aConstraint.m_pKey1 ) + aConstraint.m_pKey2 = getKey( aToken ); + else + aConstraint.m_pKey1 = getKey( aToken ); + } + else + { + if( aConstraint.m_pKey2 ) + { + if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) ) + bFailed = true; + } + else if( aConstraint.m_pKey1 ) + { + if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) ) + bFailed = true; + } + else + // constraint for nonexistent keys; this happens + // e.g. in HP4PLUS3 (#75636#) + bFailed = true; + } + } + // there must be two keywords + if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed ) + { +#ifdef __DEBUG + fprintf( stderr, "Warning: constraint \"%s\" is invalid\n", rLine.GetStr() ); +#endif + } + else + m_aConstraints.push_back( aConstraint ); +} + +const String& PPDParser::getDefaultPaperDimension() const +{ + if( m_pDefaultPaperDimension ) + return m_pDefaultPaperDimension->m_aOption; + + return aEmptyString; +} + +bool PPDParser::getMargins( + const String& rPaperName, + int& rLeft, int& rRight, + int& rUpper, int& rLower ) const +{ + if( ! m_pImageableAreas || ! m_pPaperDimensions ) + return false; + + int nPDim=-1, nImArea=-1, i; + for( i = 0; i < m_pImageableAreas->countValues(); i++ ) + if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption ) + nImArea = i; + for( i = 0; i < m_pPaperDimensions->countValues(); i++ ) + if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption ) + nPDim = i; + if( nPDim == -1 || nImArea == -1 ) + return false; + + double ImLLx, ImLLy, ImURx, ImURy; + double PDWidth, PDHeight; + String aArea = m_pImageableAreas->getValue( nImArea )->m_aValue; + ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) ); + ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) ); + ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) ); + ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) ); +// sscanf( m_pImageableAreas->getValue( nImArea )->m_aValue.GetStr(), +// "%lg%lg%lg%lg", &ImLLx, &ImLLy, &ImURx, &ImURy ); + aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue; + PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); + PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); +// sscanf( m_pPaperDimensions->getValue( nPDim )->m_aValue.GetStr(), +// "%lg%lg", &PDWidth, &PDHeight ); + rLeft = (int)(ImLLx + 0.5); + rLower = (int)(ImLLy + 0.5); + rUpper = (int)(PDHeight - ImURy + 0.5); + rRight = (int)(PDWidth - ImURx + 0.5); + + return true; +} + +bool PPDParser::getPaperDimension( + const String& rPaperName, + int& rWidth, int& rHeight ) const +{ + if( ! m_pPaperDimensions ) + return false; + + int nPDim=-1; + for( int i = 0; i < m_pPaperDimensions->countValues(); i++ ) + if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption ) + nPDim = i; + if( nPDim == -1 ) + return false; + + double PDWidth, PDHeight; + String aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue; + PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); + PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); + rHeight = (int)(PDHeight + 0.5); + rWidth = (int)(PDWidth + 0.5); + + return true; +} + +const String& PPDParser::matchPaper( int nWidth, int nHeight ) const +{ + if( ! m_pPaperDimensions ) + return aEmptyString; + + int nPDim = -1; + double PDWidth, PDHeight; + double fSort = 2e36, fNewSort; + + for( int i = 0; i < m_pPaperDimensions->countValues(); i++ ) + { + String aArea = m_pPaperDimensions->getValue( i )->m_aValue; + PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); + PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); + PDWidth /= (double)nWidth; + PDHeight /= (double)nHeight; + if( PDWidth >= 0.9 && PDWidth <= 1.1 && + PDHeight >= 0.9 && PDHeight <= 1.1 ) + { + fNewSort = + (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight); + if( fNewSort == 0.0 ) // perfect match + return m_pPaperDimensions->getValue( i )->m_aOption; + + if( fNewSort < fSort ) + { + fSort = fNewSort; + nPDim = i; + } + } + } + + static bool bDontSwap = false; + if( nPDim == -1 && ! bDontSwap ) + { + // swap portrait/landscape and try again + bDontSwap = true; + const String& rRet = matchPaper( nHeight, nWidth ); + bDontSwap = false; + return rRet; + } + + return nPDim != -1 ? m_pPaperDimensions->getValue( nPDim )->m_aOption : aEmptyString; +} + +const String& PPDParser::getDefaultInputSlot() const +{ + if( m_pDefaultInputSlot ) + return m_pDefaultInputSlot->m_aValue; + return aEmptyString; +} + +const String& PPDParser::getSlot( int nSlot ) const +{ + if( ! m_pInputSlots ) + return aEmptyString; + + if( nSlot > 0 && nSlot < m_pInputSlots->countValues() ) + return m_pInputSlots->getValue( nSlot )->m_aOption; + else if( m_pInputSlots->countValues() > 0 ) + return m_pInputSlots->getValue( (ULONG)0 )->m_aOption; + + return aEmptyString; +} + +const String& PPDParser::getSlotCommand( int nSlot ) const +{ + if( ! m_pInputSlots ) + return aEmptyString; + + if( nSlot > 0 && nSlot < m_pInputSlots->countValues() ) + return m_pInputSlots->getValue( nSlot )->m_aValue; + else if( m_pInputSlots->countValues() > 0 ) + return m_pInputSlots->getValue( (ULONG)0 )->m_aValue; + + return aEmptyString; +} + +const String& PPDParser::getSlotCommand( const String& rSlot ) const +{ + if( ! m_pInputSlots ) + return aEmptyString; + + for( int i=0; i < m_pInputSlots->countValues(); i++ ) + { + const PPDValue* pValue = m_pInputSlots->getValue( i ); + if( pValue->m_aOption == rSlot ) + return pValue->m_aValue; + } + return aEmptyString; +} + +const String& PPDParser::getPaperDimension( int nPaperDimension ) const +{ + if( ! m_pPaperDimensions ) + return aEmptyString; + + if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() ) + return m_pPaperDimensions->getValue( nPaperDimension )->m_aOption; + else if( m_pPaperDimensions->countValues() > 0 ) + return m_pPaperDimensions->getValue( (ULONG)0 )->m_aOption; + + return aEmptyString; +} + +const String& PPDParser::getPaperDimensionCommand( int nPaperDimension ) const +{ + if( ! m_pPaperDimensions ) + return aEmptyString; + + if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() ) + return m_pPaperDimensions->getValue( nPaperDimension )->m_aValue; + else if( m_pPaperDimensions->countValues() > 0 ) + return m_pPaperDimensions->getValue( (ULONG)0 )->m_aValue; + + return aEmptyString; +} + +const String& PPDParser::getPaperDimensionCommand( const String& rPaperDimension ) const +{ + if( ! m_pPaperDimensions ) + return aEmptyString; + + for( int i=0; i < m_pPaperDimensions->countValues(); i++ ) + { + const PPDValue* pValue = m_pPaperDimensions->getValue( i ); + if( pValue->m_aOption == rPaperDimension ) + return pValue->m_aValue; + } + return aEmptyString; +} + +void PPDParser::getResolutionFromString( + const String& rString, + int& rXRes, int& rYRes ) const +{ + int nPos = 0, nDPIPos; + + rXRes = rYRes = 300; + + nDPIPos = rString.SearchAscii( "dpi" ); + if( nDPIPos != STRING_NOTFOUND ) + { + if( ( nPos = rString.Search( 'x' ) ) != STRING_NOTFOUND ) + { + rXRes = rString.Copy( 0, nPos ).ToInt32(); + rYRes = rString.GetToken( 1, 'x' ).Erase( nDPIPos - nPos - 1 ).ToInt32(); + } + else + rXRes = rYRes = rString.Copy( 0, nDPIPos ).ToInt32(); + } +} + +void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const +{ + if( m_pDefaultResolution ) + { + getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes ); + return; + } + + rXRes = 300; + rYRes = 300; +} + +int PPDParser::getResolutions() const +{ + if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && + m_pDefaultResolution ) + return 1; + return m_pResolutions ? m_pResolutions->countValues() : 0; +} + +void PPDParser::getResolution( int nNr, int& rXRes, int& rYRes ) const +{ + if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution && nNr == 0 ) + { + getDefaultResolution( rXRes, rYRes ); + return; + } + if( ! m_pResolutions ) + return; + + getResolutionFromString( m_pResolutions->getValue( nNr )->m_aOption, + rXRes, rYRes ); +} + +const String& PPDParser::getResolutionCommand( int nXRes, int nYRes ) const +{ + if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution ) + return m_pDefaultResolution->m_aValue; + + if( ! m_pResolutions ) + return aEmptyString; + + int nX, nY; + for( int i = 0; i < m_pResolutions->countValues(); i++ ) + { + getResolutionFromString( m_pResolutions->getValue( i )->m_aOption, + nX, nY ); + if( nX == nXRes && nY == nYRes ) + return m_pResolutions->getValue( i )->m_aValue; + } + return aEmptyString; +} + +const String& PPDParser::getDefaultDuplexType() const +{ + if( m_pDefaultDuplexType ) + return m_pDefaultDuplexType->m_aValue; + return aEmptyString; +} + +const String& PPDParser::getDuplex( int nDuplex ) const +{ + if( ! m_pDuplexTypes ) + return aEmptyString; + + if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() ) + return m_pDuplexTypes->getValue( nDuplex )->m_aOption; + else if( m_pDuplexTypes->countValues() > 0 ) + return m_pDuplexTypes->getValue( (ULONG)0 )->m_aOption; + + return aEmptyString; +} + +const String& PPDParser::getDuplexCommand( int nDuplex ) const +{ + if( ! m_pDuplexTypes ) + return aEmptyString; + + if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() ) + return m_pDuplexTypes->getValue( nDuplex )->m_aValue; + else if( m_pDuplexTypes->countValues() > 0 ) + return m_pDuplexTypes->getValue( (ULONG)0 )->m_aValue; + + return aEmptyString; +} + +const String& PPDParser::getDuplexCommand( const String& rDuplex ) const +{ + if( ! m_pDuplexTypes ) + return aEmptyString; + + for( int i=0; i < m_pDuplexTypes->countValues(); i++ ) + { + const PPDValue* pValue = m_pDuplexTypes->getValue( i ); + if( pValue->m_aOption == rDuplex ) + return pValue->m_aValue; + } + return aEmptyString; +} + +void PPDParser::getFontAttributes( + int nFont, + String& rEncoding, + String& rCharset ) const +{ + if( m_pFontList && nFont >= 0 && nFont < m_pFontList->countValues() ) + { + String aAttribs = + WhitespaceToSpace( m_pFontList->getValue( nFont )->m_aValue ); + rEncoding = GetCommandLineToken( 0, aAttribs ); + rCharset = GetCommandLineToken( 2, aAttribs ); + } +} + +void PPDParser::getFontAttributes( + const String& rFont, + String& rEncoding, + String& rCharset ) const +{ + if( m_pFontList ) + { + for( int i = 0; i < m_pFontList->countValues(); i++ ) + if( m_pFontList->getValue( i )->m_aOption == rFont ) + getFontAttributes( i, rEncoding, rCharset ); + } +} + +const String& PPDParser::getFont( int nFont ) const +{ + if( ! m_pFontList ) + return aEmptyString; + + if( nFont >=0 && nFont < m_pFontList->countValues() ) + return m_pFontList->getValue( nFont )->m_aOption; + return aEmptyString; +} + +/* + * PPDKey + */ + +PPDKey::PPDKey( const String& rKey ) : + m_aKey( rKey ), + m_pDefaultValue( NULL ), + m_bQueryValue( false ), + m_bUIOption( false ), + m_eUIType( PickOne ), + m_nOrderDependency( 100 ), + m_eSetupType( AnySetup ) +{ +} + +// ------------------------------------------------------------------- + +PPDKey::~PPDKey() +{ +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDKey::getValue( int n ) const +{ + return ((unsigned int)n < m_aOrderedValues.size() && n >= 0) ? m_aOrderedValues[n] : NULL; +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDKey::getValue( const String& rOption ) const +{ + PPDKey::hash_type::const_iterator it = m_aValues.find( rOption ); + return it != m_aValues.end() ? &it->second : NULL; +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDKey::getValueCaseInsensitive( const String& rOption ) const +{ + const PPDValue* pValue = getValue( rOption ); + if( ! pValue ) + { + for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ ) + if( m_aOrderedValues[n]->m_aOption.EqualsIgnoreCaseAscii( rOption ) ) + pValue = m_aOrderedValues[n]; + } + + return pValue; +} + +// ------------------------------------------------------------------- + +void PPDKey::eraseValue( const String& rOption ) +{ + PPDKey::hash_type::iterator it = m_aValues.find( rOption ); + if( it == m_aValues.end() ) + return; + + for( PPDKey::value_type::iterator vit = m_aOrderedValues.begin(); vit != m_aOrderedValues.end(); ++vit ) + { + if( *vit == &(it->second ) ) + { + m_aOrderedValues.erase( vit ); + break; + } + } + m_aValues.erase( it ); +} + +// ------------------------------------------------------------------- + +PPDValue* PPDKey::insertValue( const String& rOption ) +{ + if( m_aValues.find( rOption ) != m_aValues.end() ) + return NULL; + + PPDValue aValue; + aValue.m_aOption = rOption; + m_aValues[ rOption ] = aValue; + PPDValue* pValue = &m_aValues[rOption]; + m_aOrderedValues.push_back( pValue ); + return pValue; +} + +// ------------------------------------------------------------------- + +/* + * PPDContext + */ + +PPDContext::PPDContext( const PPDParser* pParser ) : + m_pParser( pParser ) +{ +} + +// ------------------------------------------------------------------- + +PPDContext& PPDContext::operator=( const PPDContext& rCopy ) +{ + m_pParser = rCopy.m_pParser; + m_aCurrentValues = rCopy.m_aCurrentValues; + return *this; +} + +// ------------------------------------------------------------------- + +PPDContext::~PPDContext() +{ +} + +// ------------------------------------------------------------------- + +const PPDKey* PPDContext::getModifiedKey( int n ) const +{ + hash_type::const_iterator it; + for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end() && n--; ++it ) + ; + return it != m_aCurrentValues.end() ? it->first : NULL; +} + +// ------------------------------------------------------------------- + +void PPDContext::setParser( const PPDParser* pParser ) +{ + if( pParser != m_pParser ) + { + m_aCurrentValues.clear(); + m_pParser = pParser; + } +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const +{ + if( ! m_pParser ) + return NULL; + + hash_type::const_iterator it; + it = m_aCurrentValues.find( pKey ); + if( it != m_aCurrentValues.end() ) + return it->second; + + if( ! m_pParser->hasKey( pKey ) ) + return NULL; + + const PPDValue* pValue = pKey->getDefaultValue(); + if( ! pValue ) + pValue = pKey->getValue( 0 ); + + return pValue; +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints ) +{ + if( ! m_pParser || ! pKey ) + return NULL; + + // pValue can be NULL - it means ignore this option + + if( ! m_pParser->hasKey( pKey ) ) + return NULL; + + // check constraints + if( pValue ) + { + if( bDontCareForConstraints ) + { + m_aCurrentValues[ pKey ] = pValue; + } + else if( checkConstraints( pKey, pValue, true ) ) + { + m_aCurrentValues[ pKey ] = pValue; + + // after setting this value, check all constraints ! + hash_type::iterator it = m_aCurrentValues.begin(); + while( it != m_aCurrentValues.end() ) + { + if( it->first != pKey && + ! checkConstraints( it->first, it->second, false ) ) + { +#ifdef __DEBUG + fprintf( stderr, "PPDContext::setValue: option %s (%s) is constrained after setting %s to %s\n", + it->first->getKey().GetStr(), + it->second->m_aOption.GetStr(), + pKey->getKey().GetStr(), + pValue->m_aOption.GetStr() ); +#endif + resetValue( it->first, true ); + it = m_aCurrentValues.begin(); + } + else + ++it; + } + } + } + else + m_aCurrentValues[ pKey ] = NULL; + + return pValue; +} + +// ------------------------------------------------------------------- + +bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue ) +{ + if( ! m_pParser || ! pKey || ! pValue ) + return false; + + // ensure that this key is already in the list if it exists at all + if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() ) + return checkConstraints( pKey, pValue, false ); + + // it is not in the list, insert it temporarily + bool bRet = false; + if( m_pParser->hasKey( pKey ) ) + { + const PPDValue* pDefValue = pKey->getDefaultValue(); + m_aCurrentValues[ pKey ] = pDefValue; + bRet = checkConstraints( pKey, pValue, false ); + m_aCurrentValues.erase( pKey ); + } + + return bRet; +} + +// ------------------------------------------------------------------- + +bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable ) +{ + if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) ) + return false; + + const PPDValue* pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) ); + if( ! pResetValue ) + pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) ); + if( ! pResetValue && bDefaultable ) + pResetValue = pKey->getDefaultValue(); + + bool bRet = pResetValue ? ( setValue( pKey, pResetValue ) == pResetValue ? true : false ) : false; + + return bRet; +} + +// ------------------------------------------------------------------- + +bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset ) +{ + if( ! pNewValue ) + return true; + + // sanity checks + if( ! m_pParser ) + return false; + + if( pKey->getValue( pNewValue->m_aOption ) != pNewValue ) + return false; + + // None / False and the default can always be set, but be careful ! + // setting them might influence constrained values + if( pNewValue->m_aOption.EqualsAscii( "None" ) || pNewValue->m_aOption.EqualsAscii( "False" ) || + pNewValue == pKey->getDefaultValue() ) + return true; + + const ::std::list< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() ); + for( ::std::list< PPDParser::PPDConstraint >::const_iterator it = rConstraints.begin(); it != rConstraints.end(); ++it ) + { + const PPDKey* pLeft = it->m_pKey1; + const PPDKey* pRight = it->m_pKey2; + if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) ) + continue; + + const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft; + const PPDValue* pOtherKeyOption = pKey == pLeft ? it->m_pOption2 : it->m_pOption1; + const PPDValue* pKeyOption = pKey == pLeft ? it->m_pOption1 : it->m_pOption2; + + // syntax *Key1 option1 *Key2 option2 + if( pKeyOption && pOtherKeyOption ) + { + if( pNewValue != pKeyOption ) + continue; + if( pOtherKeyOption == getValue( pOtherKey ) ) + { + return false; + } + } + // syntax *Key1 option *Key2 or *Key1 *Key2 option + else if( pOtherKeyOption || pKeyOption ) + { + if( pKeyOption ) + { + if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) ) + continue; // this should not happen, PPD broken + + if( pKeyOption == pNewValue && + ! pOtherKeyOption->m_aOption.EqualsAscii( "None" ) && + ! pOtherKeyOption->m_aOption.EqualsAscii( "False" ) ) + { + // check if the other value can be reset and + // do so if possible + if( bDoReset && resetValue( pOtherKey ) ) + continue; + + return false; + } + } + else if( pOtherKeyOption ) + { + if( getValue( pOtherKey ) == pOtherKeyOption && + ! pNewValue->m_aOption.EqualsAscii( "None" ) && + ! pNewValue->m_aOption.EqualsAscii( "False" ) ) + return false; + } + else + { + // this should not happen, PPD is broken + } + } + // syntax *Key1 *Key2 + else + { + const PPDValue* pOtherValue = getValue( pOtherKey ); + if( ! pOtherValue->m_aOption.EqualsAscii( "None" ) && + ! pOtherValue->m_aOption.EqualsAscii( "False" ) && + ! pNewValue->m_aOption.EqualsAscii( "None" ) && + ! pNewValue->m_aOption.EqualsAscii( "False" ) ) + return false; + } + } + return true; +} + +// ------------------------------------------------------------------- + +void PPDContext::getUnconstrainedValues( const PPDKey* pKey, ::std::list< const PPDValue* >& rValues ) +{ + rValues.clear(); + + if( ! m_pParser || ! pKey || ! m_pParser->hasKey( pKey ) ) + return; + + int nValues = pKey->countValues(); + for( int i = 0; i < nValues; i++ ) + { + const PPDValue* pValue = pKey->getValue( i ); + if( checkConstraints( pKey, pValue ) ) + rValues.push_back( pValue ); + } +} + + +// ------------------------------------------------------------------- + +void* PPDContext::getStreamableBuffer( ULONG& rBytes ) const +{ + rBytes = 0; + if( ! m_aCurrentValues.size() ) + return NULL; + hash_type::const_iterator it; + for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it ) + { + ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 ); + rBytes += aCopy.Len(); + rBytes += 1; // for ':' + if( it->second ) + { + aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 ); + rBytes += aCopy.Len(); + } + else + rBytes += 4; + rBytes += 1; // for '\0' + } + rBytes += 1; + void* pBuffer = new char[ rBytes ]; + memset( pBuffer, 0, rBytes ); + char* pRun = (char*)pBuffer; + for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it ) + { + ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 ); + int nBytes = aCopy.Len(); + memcpy( pRun, aCopy.GetBuffer(), nBytes ); + pRun += nBytes; + *pRun++ = ':'; + if( it->second ) + aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 ); + else + aCopy = "*nil"; + nBytes = aCopy.Len(); + memcpy( pRun, aCopy.GetBuffer(), nBytes ); + pRun += nBytes; + + *pRun++ = 0; + } + return pBuffer; +} + +// ------------------------------------------------------------------- + +void PPDContext::rebuildFromStreamBuffer( void* pBuffer, ULONG nBytes ) +{ + if( ! m_pParser ) + return; + + m_aCurrentValues.clear(); + + char* pRun = (char*)pBuffer; + while( nBytes && *pRun ) + { + ByteString aLine( pRun ); + int nPos = aLine.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + { + const PPDKey* pKey = m_pParser->getKey( String( aLine.Copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) ); + if( pKey ) + { + const PPDValue* pValue = NULL; + String aOption( aLine.Copy( nPos+1 ), RTL_TEXTENCODING_MS_1252 ); + if( ! aOption.EqualsAscii( "*nil" ) ) + pValue = pKey->getValue( aOption ); + m_aCurrentValues[ pKey ] = pValue; +#ifdef __DEBUG + fprintf( stderr, "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { %s, %s }\n", pKV->m_pKey->getKey().GetStr(), pKV->m_pCurrentValue ? pKV->m_pCurrentValue->m_aOption.GetStr() : "<nil>" ); +#endif + } + } + nBytes -= aLine.Len()+1; + pRun += aLine.Len()+1; + } +} + +// ------------------------------------------------------------------- + +int PPDContext::getRenderResolution() const +{ + // initialize to reasonable default, if parser is not set + int nDPI = 300; + if( m_pParser ) + { + int nDPIx = 300, nDPIy = 300; + const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) ); + if( pKey ) + { + const PPDValue* pValue = getValue( pKey ); + if( pValue ) + m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy ); + else + m_pParser->getDefaultResolution( nDPIx, nDPIy ); + } + else + m_pParser->getDefaultResolution( nDPIx, nDPIy ); + + nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy; + } + return nDPI; +} + +// ------------------------------------------------------------------- + +void PPDContext::getPageSize( String& rPaper, int& rWidth, int& rHeight ) const +{ + // initialize to reasonable default, if parser is not set + rPaper = String( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ); + rWidth = 595; + rHeight = 842; + if( m_pParser ) + { + const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + if( pKey ) + { + const PPDValue* pValue = getValue( pKey ); + if( pValue ) + { + rPaper = pValue->m_aOption; + m_pParser->getPaperDimension( rPaper, rWidth, rHeight ); + } + else + { + rPaper = m_pParser->getDefaultPaperDimension(); + m_pParser->getDefaultPaperDimension( rWidth, rHeight ); + } + } + } +} diff --git a/vcl/unx/source/printer/printerinfomanager.cxx b/vcl/unx/source/printer/printerinfomanager.cxx new file mode 100644 index 000000000000..cf5a4a886c41 --- /dev/null +++ b/vcl/unx/source/printer/printerinfomanager.cxx @@ -0,0 +1,1472 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: printerinfomanager.cxx,v $ + * $Revision: 1.50 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unistd.h> +#include <sys/wait.h> +#include <signal.h> + +#include "cupsmgr.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/strhelper.hxx" + +#include "tools/urlobj.hxx" +#include "tools/stream.hxx" +#include "tools/debug.hxx" +#include "tools/config.hxx" + +#include "rtl/strbuf.hxx" + +#include "osl/thread.hxx" +#include "osl/mutex.hxx" +#include "osl/process.h" + +// filename of configuration files +#define PRINT_FILENAME "psprint.conf" +// the group of the global defaults +#define GLOBAL_DEFAULTS_GROUP "__Global_Printer_Defaults__" + +#include <hash_set> + +using namespace psp; +using namespace rtl; +using namespace osl; + +namespace psp +{ + class SystemQueueInfo : public Thread + { + mutable Mutex m_aMutex; + bool m_bChanged; + std::list< PrinterInfoManager::SystemPrintQueue > + m_aQueues; + OUString m_aCommand; + + virtual void run(); + + public: + SystemQueueInfo(); + ~SystemQueueInfo(); + + bool hasChanged() const; + OUString getCommand() const; + + // sets changed status to false; therefore not const + void getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues ); + }; +} // namespace + +/* +* class PrinterInfoManager +*/ + +// ----------------------------------------------------------------- + +PrinterInfoManager& PrinterInfoManager::get() +{ + static PrinterInfoManager* pManager = NULL; + + if( ! pManager ) + { + pManager = CUPSManager::tryLoadCUPS(); + if( ! pManager ) + pManager = new PrinterInfoManager(); + + if( pManager ) + pManager->initialize(); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PrinterInfoManager::get create Manager of type %d\n", pManager->getType() ); + #endif + } + + return *pManager; +} + +// ----------------------------------------------------------------- + +PrinterInfoManager::PrinterInfoManager( Type eType ) : + m_pQueueInfo( NULL ), + m_eType( eType ), + m_bUseIncludeFeature( false ), + m_aSystemDefaultPaper( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ), + m_bDisableCUPS( false ) +{ + if( eType == Default ) + m_pQueueInfo = new SystemQueueInfo(); + initSystemDefaultPaper(); +} + +// ----------------------------------------------------------------- + +PrinterInfoManager::~PrinterInfoManager() +{ + delete m_pQueueInfo; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::isCUPSDisabled() const +{ + return m_bDisableCUPS; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::setCUPSDisabled( bool bDisable ) +{ + m_bDisableCUPS = bDisable; + writePrinterConfig(); + // actually we know the printers changed + // however this triggers reinitialization the right way + checkPrintersChanged( true ); +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::initSystemDefaultPaper() +{ + bool bSuccess = false; + + // try libpaper + + // #i78617# workaround missing paperconf command + FILE* pPipe = popen( "sh -c paperconf 2>/dev/null", "r" ); + if( pPipe ) + { + char pBuffer[ 1024 ]; + *pBuffer = 0; + fgets( pBuffer, sizeof(pBuffer)-1, pPipe ); + pclose( pPipe ); + + ByteString aPaper( pBuffer ); + aPaper = WhitespaceToSpace( aPaper ); + if( aPaper.Len() ) + { + m_aSystemDefaultPaper = OUString( OStringToOUString( aPaper, osl_getThreadTextEncoding() ) ); + bSuccess = true; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "paper from paperconf = %s\n", aPaper.GetBuffer() ); + #endif + } + if( bSuccess ) + return; + } + + // default value is Letter for US (en_US), Cannada (en_CA, fr_CA); else A4 + // en will be interpreted as en_US + + // note: at this point m_aSystemDefaultPaper is set to "A4" from the constructor + + // check for LC_PAPER + const char* pPaperLang = getenv( "LC_PAPER" ); + if( pPaperLang && *pPaperLang ) + { + OString aLang( pPaperLang ); + if( aLang.getLength() > 5 ) + aLang = aLang.copy( 0, 5 ); + if( aLang.getLength() == 5 ) + { + if( aLang.equalsIgnoreAsciiCase( "en_us" ) + || aLang.equalsIgnoreAsciiCase( "en_ca" ) + || aLang.equalsIgnoreAsciiCase( "fr_ca" ) + ) + m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) ); + } + else if( aLang.getLength() == 2 && aLang.equalsIgnoreAsciiCase( "en" ) ) + m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) ); + return; + } + + // use process locale to determine paper + rtl_Locale* pLoc = NULL; + osl_getProcessLocale( &pLoc ); + if( pLoc ) + { + if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Language->buffer, pLoc->Language->length, "en") ) + { + if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Country->buffer, pLoc->Country->length, "us") + || 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Country->buffer, pLoc->Country->length, "ca") + || pLoc->Country->length == 0 + ) + m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) ); + } + else if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Language->buffer, pLoc->Language->length, "fr") ) + { + if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Country->buffer, pLoc->Country->length, "ca") ) + m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) ); + } + } +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::checkPrintersChanged( bool bWait ) +{ + // check if files were created, deleted or modified since initialize() + ::std::list< WatchFile >::const_iterator it; + bool bChanged = false; + for( it = m_aWatchFiles.begin(); it != m_aWatchFiles.end() && ! bChanged; ++it ) + { + DirectoryItem aItem; + if( DirectoryItem::get( it->m_aFilePath, aItem ) ) + { + if( it->m_aModified.Seconds != 0 ) + bChanged = true; // file probably has vanished + } + else + { + FileStatus aStatus( FileStatusMask_ModifyTime ); + if( aItem.getFileStatus( aStatus ) ) + bChanged = true; // unlikely but not impossible + else + { + TimeValue aModified = aStatus.getModifyTime(); + if( aModified.Seconds != it->m_aModified.Seconds ) + bChanged = true; + } + } + } + + if( bWait && m_pQueueInfo ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "syncing printer discovery thread\n" ); + #endif + m_pQueueInfo->join(); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "done: syncing printer discovery thread\n" ); + #endif + } + + if( ! bChanged && m_pQueueInfo ) + bChanged = m_pQueueInfo->hasChanged(); + if( bChanged ) + { + initialize(); + } + + return bChanged; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::initialize() +{ + m_bUseIncludeFeature = false; + rtl_TextEncoding aEncoding = gsl_getSystemTextEncoding(); + m_aPrinters.clear(); + m_aWatchFiles.clear(); + OUString aDefaultPrinter; + + // first initialize the global defaults + // have to iterate over all possible files + // there should be only one global setup section in all + // available config files + m_aGlobalDefaults = PrinterInfo(); + + // need a parser for the PPDContext. generic printer should do. + m_aGlobalDefaults.m_pParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ); + m_aGlobalDefaults.m_aContext.setParser( m_aGlobalDefaults.m_pParser ); + m_aGlobalDefaults.m_bPerformFontSubstitution = true; + m_bDisableCUPS = false; + + if( ! m_aGlobalDefaults.m_pParser ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Error: no default PPD file SGENPRT available, shutting down psprint...\n" ); + #endif + return; + } + + std::list< OUString > aDirList; + psp::getPrinterPathList( aDirList, NULL ); + std::list< OUString >::const_iterator print_dir_it; + for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it ) + { + INetURLObject aFile( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) ); + Config aConfig( aFile.PathToFileName() ); + if( aConfig.HasGroup( GLOBAL_DEFAULTS_GROUP ) ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found global defaults in %s\n", OUStringToOString( aFile.PathToFileName(), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + aConfig.SetGroup( GLOBAL_DEFAULTS_GROUP ); + + ByteString aValue( aConfig.ReadKey( "Copies" ) ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nCopies = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "Orientation" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait; + + aValue = aConfig.ReadKey( "MarginAdjust" ); + if( aValue.Len() ) + { + m_aGlobalDefaults.m_nLeftMarginAdjust = aValue.GetToken( 0, ',' ).ToInt32(); + m_aGlobalDefaults.m_nRightMarginAdjust = aValue.GetToken( 1, ',' ).ToInt32(); + m_aGlobalDefaults.m_nTopMarginAdjust = aValue.GetToken( 2, ',' ).ToInt32(); + m_aGlobalDefaults.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32(); + } + + aValue = aConfig.ReadKey( "ColorDepth", "24" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nColorDepth = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "ColorDevice" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nColorDevice = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PSLevel" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nPSLevel = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PerformFontSubstitution" ); + if( aValue.Len() ) + { + if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) ) + m_aGlobalDefaults.m_bPerformFontSubstitution = true; + else + m_aGlobalDefaults.m_bPerformFontSubstitution = false; + } + + aValue = aConfig.ReadKey( "DisableCUPS" ); + if( aValue.Len() ) + { + if( aValue.Equals( "1" ) || aValue.EqualsIgnoreCaseAscii( "true" ) ) + m_bDisableCUPS = true; + else + m_bDisableCUPS = false; + } + + // get the PPDContext of global JobData + for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ ) + { + ByteString aKey( aConfig.GetKeyName( nKey ) ); + if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL ) + { + aValue = aConfig.ReadKey( aKey ); + const PPDKey* pKey = m_aGlobalDefaults.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) ); + if( pKey ) + { + m_aGlobalDefaults.m_aContext. + setValue( pKey, + aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ), + TRUE ); + } + } + else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL ) + { + aValue = aConfig.ReadKey( aKey ); + m_aGlobalDefaults.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "global settings: fontsubst = %s, %d substitutes\n", m_aGlobalDefaults.m_bPerformFontSubstitution ? "true" : "false", m_aGlobalDefaults.m_aFontSubstitutes.size() ); + #endif + } + } + setDefaultPaper( m_aGlobalDefaults.m_aContext ); + fillFontSubstitutions( m_aGlobalDefaults ); + + // now collect all available printers + for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it ) + { + INetURLObject aDir( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + INetURLObject aFile( aDir ); + aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) ); + + // check directory validity + OUString aUniPath; + FileBase::getFileURLFromSystemPath( aDir.PathToFileName(), aUniPath ); + Directory aDirectory( aUniPath ); + if( aDirectory.open() ) + continue; + aDirectory.close(); + + + FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aUniPath ); + FileStatus aStatus( FileStatusMask_ModifyTime ); + DirectoryItem aItem; + + // setup WatchFile list + WatchFile aWatchFile; + aWatchFile.m_aFilePath = aUniPath; + if( ! DirectoryItem::get( aUniPath, aItem ) && + ! aItem.getFileStatus( aStatus ) ) + { + aWatchFile.m_aModified = aStatus.getModifyTime(); + } + else + { + aWatchFile.m_aModified.Seconds = 0; + aWatchFile.m_aModified.Nanosec = 0; + } + m_aWatchFiles.push_back( aWatchFile ); + + Config aConfig( aFile.PathToFileName() ); + for( int nGroup = 0; nGroup < aConfig.GetGroupCount(); nGroup++ ) + { + aConfig.SetGroup( aConfig.GetGroupName( nGroup ) ); + ByteString aValue = aConfig.ReadKey( "Printer" ); + if( aValue.Len() ) + { + OUString aPrinterName; + + int nNamePos = aValue.Search( '/' ); + // check for valid value of "Printer" + if( nNamePos == STRING_NOTFOUND ) + continue; + + Printer aPrinter; + // initialize to global defaults + aPrinter.m_aInfo = m_aGlobalDefaults; + // global settings do not default the printer substitution + // list ! the substitution list in there is only used for + // newly created printers + aPrinter.m_aInfo.m_aFontSubstitutes.clear(); + aPrinter.m_aInfo.m_aFontSubstitutions.clear(); + + aPrinterName = String( aValue.Copy( nNamePos+1 ), RTL_TEXTENCODING_UTF8 ); + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + aPrinter.m_aInfo.m_aDriverName = String( aValue.Copy( 0, nNamePos ), RTL_TEXTENCODING_UTF8 ); + + // set parser, merge settings + // don't do this for CUPS printers as this is done + // by the CUPS system itself + if( aPrinter.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 ) + { + aPrinter.m_aInfo.m_pParser = PPDParser::getParser( aPrinter.m_aInfo.m_aDriverName ); + aPrinter.m_aInfo.m_aContext.setParser( aPrinter.m_aInfo.m_pParser ); + // note: setParser also purges the context + + // ignore this printer if its driver is not found + if( ! aPrinter.m_aInfo.m_pParser ) + continue; + + // merge the ppd context keys if the printer has the same keys and values + // this is a bit tricky, since it involves mixing two PPDs + // without constraints which might end up badly + // this feature should be use with caution + // it is mainly to select default paper sizes for new printers + for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ ) + { + const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified ); + const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey ); + const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL; + if( pDefKey && pPrinterKey ) + // at least the options exist in both PPDs + { + if( pDefValue ) + { + const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption ); + if( pPrinterValue ) + // the printer has a corresponding option for the key + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue ); + } + else + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL ); + } + } + + aValue = aConfig.ReadKey( "Command" ); + // no printer without a command + if( ! aValue.Len() ) + { + /* TODO: + * porters: please append your platform to the Solaris + * case if your platform has SystemV printing per default. + */ + #if defined SOLARIS || defined(IRIX) + aValue = "lp"; + #else + aValue = "lpr"; + #endif + } + aPrinter.m_aInfo.m_aCommand = String( aValue, RTL_TEXTENCODING_UTF8 ); + } + + aValue = aConfig.ReadKey( "QuickCommand" ); + aPrinter.m_aInfo.m_aQuickCommand = String( aValue, RTL_TEXTENCODING_UTF8 ); + + aValue = aConfig.ReadKey( "Features" ); + aPrinter.m_aInfo.m_aFeatures = String( aValue, RTL_TEXTENCODING_UTF8 ); + + // override the settings in m_aGlobalDefaults if keys exist + aValue = aConfig.ReadKey( "DefaultPrinter" ); + if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) ) + aDefaultPrinter = aPrinterName; + + aValue = aConfig.ReadKey( "Location" ); + aPrinter.m_aInfo.m_aLocation = String( aValue, RTL_TEXTENCODING_UTF8 ); + + aValue = aConfig.ReadKey( "Comment" ); + aPrinter.m_aInfo.m_aComment = String( aValue, RTL_TEXTENCODING_UTF8 ); + + aValue = aConfig.ReadKey( "Copies" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nCopies = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "Orientation" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait; + + aValue = aConfig.ReadKey( "MarginAdjust" ); + if( aValue.Len() ) + { + aPrinter.m_aInfo.m_nLeftMarginAdjust = aValue.GetToken( 0, ',' ).ToInt32(); + aPrinter.m_aInfo.m_nRightMarginAdjust = aValue.GetToken( 1, ',' ).ToInt32(); + aPrinter.m_aInfo.m_nTopMarginAdjust = aValue.GetToken( 2, ',' ).ToInt32(); + aPrinter.m_aInfo.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32(); + } + + aValue = aConfig.ReadKey( "ColorDepth" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nColorDepth = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "ColorDevice" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nColorDevice = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PSLevel" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nPSLevel = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PerformFontSubstitution" ); + if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) ) + aPrinter.m_aInfo.m_bPerformFontSubstitution = true; + else + aPrinter.m_aInfo.m_bPerformFontSubstitution = false; + + // now iterate over all keys to extract multi key information: + // 1. PPDContext information + // 2. Font substitution table + for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ ) + { + ByteString aKey( aConfig.GetKeyName( nKey ) ); + if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL && aPrinter.m_aInfo.m_pParser ) + { + aValue = aConfig.ReadKey( aKey ); + const PPDKey* pKey = aPrinter.m_aInfo.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) ); + if( pKey ) + { + aPrinter.m_aInfo.m_aContext. + setValue( pKey, + aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ), + TRUE ); + } + } + else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL ) + { + aValue = aConfig.ReadKey( aKey ); + aPrinter.m_aInfo.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + + setDefaultPaper( aPrinter.m_aInfo.m_aContext ); + fillFontSubstitutions( aPrinter.m_aInfo ); + + // finally insert printer + FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aPrinter.m_aFile ); + aPrinter.m_bModified = false; + aPrinter.m_aGroup = aConfig.GetGroupName( nGroup ); + std::hash_map< OUString, Printer, OUStringHash >::const_iterator find_it = + m_aPrinters.find( aPrinterName ); + if( find_it != m_aPrinters.end() ) + { + aPrinter.m_aAlternateFiles = find_it->second.m_aAlternateFiles; + aPrinter.m_aAlternateFiles.push_front( find_it->second.m_aFile ); + } + m_aPrinters[ aPrinterName ] = aPrinter; + } + } + } + + // set default printer + if( m_aPrinters.size() ) + { + if( m_aPrinters.find( aDefaultPrinter ) == m_aPrinters.end() ) + aDefaultPrinter = m_aPrinters.begin()->first; + } + else + aDefaultPrinter = OUString(); + m_aDefaultPrinter = aDefaultPrinter; + + if( m_eType != Default ) + return; + + // add a default printer for every available print queue + // merge paper and font substitution from default printer, + // all else from global defaults + PrinterInfo aMergeInfo( m_aGlobalDefaults ); + aMergeInfo.m_aDriverName = String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ); + aMergeInfo.m_aFeatures = String( RTL_CONSTASCII_USTRINGPARAM( "autoqueue" ) ); + + if( m_aDefaultPrinter.getLength() ) + { + PrinterInfo aDefaultInfo( getPrinterInfo( m_aDefaultPrinter ) ); + aMergeInfo.m_bPerformFontSubstitution = aDefaultInfo.m_bPerformFontSubstitution; + fillFontSubstitutions( aMergeInfo ); + + const PPDKey* pDefKey = aDefaultInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + const PPDKey* pMergeKey = aMergeInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + const PPDValue* pDefValue = aDefaultInfo.m_aContext.getValue( pDefKey ); + const PPDValue* pMergeValue = pMergeKey ? pMergeKey->getValue( pDefValue->m_aOption ) : NULL; + if( pMergeKey && pMergeValue ) + aMergeInfo.m_aContext.setValue( pMergeKey, pMergeValue ); + } + + getSystemPrintQueues(); + for( ::std::list< SystemPrintQueue >::iterator it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it ) + { + String aPrinterName( RTL_CONSTASCII_USTRINGPARAM( "<" ) ); + aPrinterName += String( it->m_aQueue ); + aPrinterName.Append( '>' ); + + if( m_aPrinters.find( aPrinterName ) != m_aPrinters.end() ) + // probably user made this one permanent in padmin + continue; + + String aCmd( m_aSystemPrintCommand ); + aCmd.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) ), it->m_aQueue ); + + Printer aPrinter; + + // initialize to merged defaults + aPrinter.m_aInfo = aMergeInfo; + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + aPrinter.m_aInfo.m_aCommand = aCmd; + aPrinter.m_aInfo.m_aComment = it->m_aComment; + aPrinter.m_aInfo.m_aLocation = it->m_aLocation; + aPrinter.m_bModified = false; + aPrinter.m_aGroup = ByteString( aPrinterName, aEncoding ); //provide group name in case user makes this one permanent in padmin + + m_aPrinters[ aPrinterName ] = aPrinter; + } +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::listPrinters( ::std::list< OUString >& rList ) const +{ + ::std::hash_map< OUString, Printer, OUStringHash >::const_iterator it; + rList.clear(); + for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it ) + rList.push_back( it->first ); +} + +// ----------------------------------------------------------------- + +const PrinterInfo& PrinterInfoManager::getPrinterInfo( const OUString& rPrinter ) const +{ + static PrinterInfo aEmptyInfo; + ::std::hash_map< OUString, Printer, OUStringHash >::const_iterator it = m_aPrinters.find( rPrinter ); + + DBG_ASSERT( it != m_aPrinters.end(), "Do not ask for info about nonexistant printers" ); + + return it != m_aPrinters.end() ? it->second.m_aInfo : aEmptyInfo; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ) +{ + ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinter ); + + DBG_ASSERT( it != m_aPrinters.end(), "Do not change nonexistant printers" ); + + if( it != m_aPrinters.end() ) + { + it->second.m_aInfo = rNewInfo; + // recalculate font substitutions + fillFontSubstitutions( it->second.m_aInfo ); + it->second.m_bModified = true; + writePrinterConfig(); + } +} + +// ----------------------------------------------------------------- + +// need to check writeability / creatability of config files +static bool checkWriteability( const OUString& rUniPath ) +{ + bool bRet = false; + OUString aSysPath; + FileBase::getSystemPathFromFileURL( rUniPath, aSysPath ); + SvFileStream aStream( aSysPath, STREAM_READ | STREAM_WRITE ); + if( aStream.IsOpen() && aStream.IsWritable() ) + bRet = true; + return bRet; +} + +bool PrinterInfoManager::writePrinterConfig() +{ + // find at least one writeable config + ::std::hash_map< OUString, Config*, OUStringHash > files; + ::std::hash_map< OUString, int, OUStringHash > rofiles; + ::std::hash_map< OUString, Config*, OUStringHash >::iterator file_it; + + for( ::std::list< WatchFile >::const_iterator wit = m_aWatchFiles.begin(); wit != m_aWatchFiles.end(); ++wit ) + { + if( checkWriteability( wit->m_aFilePath ) ) + { + files[ wit->m_aFilePath ] = new Config( wit->m_aFilePath ); + break; + } + } + + if( files.empty() ) + return false; + + Config* pGlobal = files.begin()->second; + pGlobal->SetGroup( GLOBAL_DEFAULTS_GROUP ); + pGlobal->WriteKey( "DisableCUPS", m_bDisableCUPS ? "true" : "false" ); + + ::std::hash_map< OUString, Printer, OUStringHash >::iterator it; + for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it ) + { + if( ! it->second.m_bModified ) + // printer was not changed, do nothing + continue; + + // don't save autoqueue printers + sal_Int32 nIndex = 0; + bool bAutoQueue = false; + while( nIndex != -1 && ! bAutoQueue ) + { + OUString aToken( it->second.m_aInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); + if( aToken.getLength() && aToken.compareToAscii( "autoqueue" ) == 0 ) + bAutoQueue = true; + } + if( bAutoQueue ) + continue; + + if( it->second.m_aFile.getLength() ) + { + // check if file is writable + if( files.find( it->second.m_aFile ) == files.end() ) + { + bool bInsertToNewFile = false; + // maybe it is simply not inserted yet + if( rofiles.find( it->second.m_aFile ) == rofiles.end() ) + { + if( checkWriteability( it->second.m_aFile ) ) + files[ it->second.m_aFile ] = new Config( it->second.m_aFile ); + else + bInsertToNewFile = true; + } + else + bInsertToNewFile = true; + // original file is read only, insert printer in a new writeable file + if( bInsertToNewFile ) + { + rofiles[ it->second.m_aFile ] = 1; + // update alternate file list + // the remove operation ensures uniqueness of each alternate + it->second.m_aAlternateFiles.remove( it->second.m_aFile ); + it->second.m_aAlternateFiles.remove( files.begin()->first ); + it->second.m_aAlternateFiles.push_front( it->second.m_aFile ); + // update file + it->second.m_aFile = files.begin()->first; + } + } + } + else // a new printer, write it to the first file available + it->second.m_aFile = files.begin()->first; + + if( ! it->second.m_aGroup.getLength() ) // probably a new printer + it->second.m_aGroup = OString( it->first.getStr(), it->first.getLength(), RTL_TEXTENCODING_UTF8 ); + + if( files.find( it->second.m_aFile ) != files.end() ) + { + Config* pConfig = files[ it->second.m_aFile ]; + pConfig->DeleteGroup( it->second.m_aGroup ); // else some old keys may remain + pConfig->SetGroup( it->second.m_aGroup ); + + ByteString aValue( String( it->second.m_aInfo.m_aDriverName ), RTL_TEXTENCODING_UTF8 ); + aValue += '/'; + aValue += ByteString( String( it->first ), RTL_TEXTENCODING_UTF8 ); + pConfig->WriteKey( "Printer", aValue ); + pConfig->WriteKey( "DefaultPrinter", it->first == m_aDefaultPrinter ? "1" : "0" ); + pConfig->WriteKey( "Location", ByteString( String( it->second.m_aInfo.m_aLocation ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Comment", ByteString( String( it->second.m_aInfo.m_aComment ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Command", ByteString( String( it->second.m_aInfo.m_aCommand ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "QuickCommand", ByteString( String( it->second.m_aInfo.m_aQuickCommand ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Features", ByteString( String( it->second.m_aInfo.m_aFeatures ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Copies", ByteString::CreateFromInt32( it->second.m_aInfo.m_nCopies ) ); + pConfig->WriteKey( "Orientation", it->second.m_aInfo.m_eOrientation == orientation::Landscape ? "Landscape" : "Portrait" ); + pConfig->WriteKey( "PSLevel", ByteString::CreateFromInt32( it->second.m_aInfo.m_nPSLevel ) ); + pConfig->WriteKey( "ColorDevice", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDevice ) ); + pConfig->WriteKey( "ColorDepth", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDepth ) ); + aValue = ByteString::CreateFromInt32( it->second.m_aInfo.m_nLeftMarginAdjust ); + aValue += ','; + aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nRightMarginAdjust ); + aValue += ','; + aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nTopMarginAdjust ); + aValue += ','; + aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nBottomMarginAdjust ); + pConfig->WriteKey( "MarginAdjust", aValue ); + + if( it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 ) + { + // write PPDContext (not for CUPS) + for( int i = 0; i < it->second.m_aInfo.m_aContext.countValuesModified(); i++ ) + { + const PPDKey* pKey = it->second.m_aInfo.m_aContext.getModifiedKey( i ); + ByteString aKey( "PPD_" ); + aKey += ByteString( pKey->getKey(), RTL_TEXTENCODING_ISO_8859_1 ); + + const PPDValue* pValue = it->second.m_aInfo.m_aContext.getValue( pKey ); + aValue = pValue ? ByteString( pValue->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ) : ByteString( "*nil" ); + pConfig->WriteKey( aKey, aValue ); + } + } + + // write font substitution table + pConfig->WriteKey( "PerformFontSubstitution", it->second.m_aInfo.m_bPerformFontSubstitution ? "true" : "false" ); + for( ::std::hash_map< OUString, OUString, OUStringHash >::const_iterator subst = it->second.m_aInfo.m_aFontSubstitutes.begin(); + subst != it->second.m_aInfo.m_aFontSubstitutes.end(); ++subst ) + { + ByteString aKey( "SubstFont_" ); + aKey.Append( OUStringToOString( subst->first, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + pConfig->WriteKey( aKey, OUStringToOString( subst->second, RTL_TEXTENCODING_ISO_8859_1 ) ); + } + } + } + + // get rid of Config objects. this also writes any changes + for( file_it = files.begin(); file_it != files.end(); ++file_it ) + delete file_it->second; + + return true; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::addPrinter( const OUString& rPrinterName, const OUString& rDriverName ) +{ + bool bSuccess = false; + + const PPDParser* pParser = NULL; + if( m_aPrinters.find( rPrinterName ) == m_aPrinters.end() && ( pParser = PPDParser::getParser( rDriverName ) ) ) + { + Printer aPrinter; + aPrinter.m_bModified = true; + aPrinter.m_aInfo = m_aGlobalDefaults; + aPrinter.m_aInfo.m_aDriverName = rDriverName; + aPrinter.m_aInfo.m_pParser = pParser; + aPrinter.m_aInfo.m_aContext.setParser( pParser ); + aPrinter.m_aInfo.m_aPrinterName = rPrinterName; + + fillFontSubstitutions( aPrinter.m_aInfo ); + // merge PPD values with global defaults + for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ ) + { + const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified ); + const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey ); + const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL; + if( pDefKey && pPrinterKey ) + // at least the options exist in both PPDs + { + if( pDefValue ) + { + const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption ); + if( pPrinterValue ) + // the printer has a corresponding option for the key + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue ); + } + else + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL ); + } + } + + m_aPrinters[ rPrinterName ] = aPrinter; + bSuccess = true; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "new printer %s, level = %d, colordevice = %d, depth = %d\n", + OUStringToOString( rPrinterName, osl_getThreadTextEncoding() ).getStr(), + m_aPrinters[rPrinterName].m_aInfo.m_nPSLevel, + m_aPrinters[rPrinterName].m_aInfo.m_nColorDevice, + m_aPrinters[rPrinterName].m_aInfo.m_nColorDepth ); + #endif + // comment: logically one should writePrinterConfig() here + // but immediately after addPrinter() a changePrinterInfo() + // will follow (see padmin code), which writes it again, + // so we can currently save some performance here + } + return bSuccess; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::removePrinter( const OUString& rPrinterName, bool bCheckOnly ) +{ + bool bSuccess = true; + + ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName ); + if( it != m_aPrinters.end() ) + { + if( it->second.m_aFile.getLength() ) + { + // this printer already exists in a config file + + + // check writeability of config file(s) + if( ! checkWriteability( it->second.m_aFile ) ) + bSuccess = false; + else + { + for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin(); + file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it ) + { + if( ! checkWriteability( *file_it ) ) + bSuccess = false; + } + } + if( bSuccess && ! bCheckOnly ) + { + + Config aConfig( it->second.m_aFile ); + aConfig.DeleteGroup( it->second.m_aGroup ); + aConfig.Flush(); + for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin(); + file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it ) + { + Config aAltConfig( *file_it ); + aAltConfig.DeleteGroup( it->second.m_aGroup ); + aAltConfig.Flush(); + } + } + } + if( bSuccess && ! bCheckOnly ) + { + m_aPrinters.erase( it ); + // need this here because someone may call + // checkPrintersChanged after the removal + // but then other added printers were not flushed + // to disk, so they are discarded + writePrinterConfig(); + } + } + return bSuccess; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::setDefaultPrinter( const OUString& rPrinterName ) +{ + bool bSuccess = false; + + ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName ); + if( it != m_aPrinters.end() ) + { + bSuccess = true; + it->second.m_bModified = true; + if( ( it = m_aPrinters.find( m_aDefaultPrinter ) ) != m_aPrinters.end() ) + it->second.m_bModified = true; + m_aDefaultPrinter = rPrinterName; + writePrinterConfig(); + } + return bSuccess; +} + +// ----------------------------------------------------------------- +bool PrinterInfoManager::addOrRemovePossible() const +{ + return true; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::fillFontSubstitutions( PrinterInfo& rInfo ) const +{ + PrintFontManager& rFontManager( PrintFontManager::get() ); + rInfo.m_aFontSubstitutions.clear(); + + if( ! rInfo.m_bPerformFontSubstitution || + ! rInfo.m_aFontSubstitutes.size() ) + return; + + ::std::list< FastPrintFontInfo > aFonts; + ::std::hash_map< OUString, ::std::list< FastPrintFontInfo >, OUStringHash > aPrinterFonts; + rFontManager.getFontListWithFastInfo( aFonts, rInfo.m_pParser ); + + // get builtin fonts + ::std::list< FastPrintFontInfo >::const_iterator it; + for( it = aFonts.begin(); it != aFonts.end(); ++it ) + if( it->m_eType == fonttype::Builtin ) + aPrinterFonts[ it->m_aFamilyName.toAsciiLowerCase() ].push_back( *it ); + + // map lower case, so build a local copy of the font substitutions + ::std::hash_map< OUString, OUString, OUStringHash > aSubstitutions; + ::std::hash_map< OUString, OUString, OUStringHash >::const_iterator subst; + for( subst = rInfo.m_aFontSubstitutes.begin(); subst != rInfo.m_aFontSubstitutes.end(); ++subst ) + { + OUString aFamily( subst->first.toAsciiLowerCase() ); + // first look if there is a builtin of this family + // in this case override the substitution table + if( aPrinterFonts.find( aFamily ) != aPrinterFonts.end() ) + aSubstitutions[ aFamily ] = aFamily; + else + aSubstitutions[ aFamily ] = subst->second.toAsciiLowerCase(); + } + + + // now find substitutions + for( it = aFonts.begin(); it != aFonts.end(); ++it ) + { + if( it->m_eType != fonttype::Builtin ) + { + OUString aFamily( it->m_aFamilyName.toAsciiLowerCase() ); + subst = aSubstitutions.find( aFamily ); + if( subst != aSubstitutions.end() ) + { + // search a substitution + const ::std::list< FastPrintFontInfo >& rBuiltins( aPrinterFonts[ aSubstitutions[ aFamily ] ] ); + ::std::list< FastPrintFontInfo >::const_iterator builtin; + int nLastMatch = -10000; + fontID nSubstitute = -1; + for( builtin = rBuiltins.begin(); builtin != rBuiltins.end(); ++builtin ) + { + int nMatch = 0; + int nDiff; + if( builtin->m_eItalic == it->m_eItalic ) + nMatch += 8000; + + nDiff = builtin->m_eWeight - it->m_eWeight; + nDiff = nDiff < 0 ? -nDiff : nDiff; + nMatch += 4000 - 1000*nDiff; + + nDiff = builtin->m_eWidth - it->m_eWidth; + nDiff = nDiff < 0 ? -nDiff : nDiff; + nMatch += 2000 - 500*nDiff; + + if( nMatch > nLastMatch ) + { + nLastMatch = nMatch; + nSubstitute = builtin->m_nID; + } + } + if( nSubstitute != -1 ) + { + rInfo.m_aFontSubstitutions[ it->m_nID ] = nSubstitute; + #if OSL_DEBUG_LEVEL > 2 + FastPrintFontInfo aInfo; + rFontManager.getFontFastInfo( nSubstitute, aInfo ); + fprintf( stderr, + "substitute %s %s %d %d\n" + " -> %s %s %d %d\n", + OUStringToOString( it->m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + it->m_eItalic == italic::Upright ? "r" : it->m_eItalic == italic::Oblique ? "o" : it->m_eItalic == italic::Italic ? "i" : "u", + it->m_eWeight, + it->m_eWidth, + + OUStringToOString( aInfo.m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + aInfo.m_eItalic == italic::Upright ? "r" : aInfo.m_eItalic == italic::Oblique ? "o" : aInfo.m_eItalic == italic::Italic ? "i" : "u", + aInfo.m_eWeight, + aInfo.m_eWidth + ); + #endif + } + } + } + } +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::getSystemPrintCommands( std::list< OUString >& rCommands ) +{ + if( m_pQueueInfo && m_pQueueInfo->hasChanged() ) + { + m_aSystemPrintCommand = m_pQueueInfo->getCommand(); + m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues ); + delete m_pQueueInfo, m_pQueueInfo = NULL; + } + + std::list< SystemPrintQueue >::const_iterator it; + rCommands.clear(); + String aPrinterConst( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) ); + for( it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it ) + { + String aCmd( m_aSystemPrintCommand ); + aCmd.SearchAndReplace( aPrinterConst, it->m_aQueue ); + rCommands.push_back( aCmd ); + } +} + +const std::list< PrinterInfoManager::SystemPrintQueue >& PrinterInfoManager::getSystemPrintQueues() +{ + if( m_pQueueInfo && m_pQueueInfo->hasChanged() ) + { + m_aSystemPrintCommand = m_pQueueInfo->getCommand(); + m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues ); + delete m_pQueueInfo, m_pQueueInfo = NULL; + } + + return m_aSystemPrintQueues; +} + +bool PrinterInfoManager::checkFeatureToken( const rtl::OUString& rPrinterName, const char* pToken ) const +{ + const PrinterInfo& rPrinterInfo( getPrinterInfo( rPrinterName ) ); + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + { + OUString aOuterToken = rPrinterInfo.m_aFeatures.getToken( 0, ',', nIndex ); + sal_Int32 nInnerIndex = 0; + OUString aInnerToken = aOuterToken.getToken( 0, '=', nInnerIndex ); + if( aInnerToken.equalsIgnoreAsciiCaseAscii( pToken ) ) + return true; + } + return false; +} + +FILE* PrinterInfoManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) +{ + const PrinterInfo& rPrinterInfo = getPrinterInfo (rPrintername); + const rtl::OUString& rCommand = (bQuickCommand && rPrinterInfo.m_aQuickCommand.getLength() ) ? + rPrinterInfo.m_aQuickCommand : rPrinterInfo.m_aCommand; + rtl::OString aShellCommand = OUStringToOString (rCommand, RTL_TEXTENCODING_ISO_8859_1); + aShellCommand += rtl::OString( " 2>/dev/null" ); + + return popen (aShellCommand.getStr(), "w"); +} + +int PrinterInfoManager::endSpool( const OUString& /*rPrintername*/, const OUString& /*rJobTitle*/, FILE* pFile, const JobData& /*rDocumentJobData*/ ) +{ + return (0 == pclose( pFile )); +} + +void PrinterInfoManager::setupJobContextData( JobData& rData ) +{ + std::hash_map< OUString, Printer, OUStringHash >::iterator it = + m_aPrinters.find( rData.m_aPrinterName ); + if( it != m_aPrinters.end() ) + { + rData.m_pParser = it->second.m_aInfo.m_pParser; + rData.m_aContext = it->second.m_aInfo.m_aContext; + } +} + +void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const +{ + if( ! rContext.getParser() ) + return; + + const PPDKey* pPageSizeKey = rContext.getParser()->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + if( ! pPageSizeKey ) + return; + + int nModified = rContext.countValuesModified(); + while( nModified-- && + rContext.getModifiedKey( nModified ) != pPageSizeKey ) + ; + + if( nModified >= 0 ) // paper was set already, do not modify + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "not setting default paper, already set %s\n", + OUStringToOString( rContext.getValue( pPageSizeKey )->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + return; + } + + // paper not set, fill in default value + const PPDValue* pPaperVal = NULL; + int nValues = pPageSizeKey->countValues(); + for( int i = 0; i < nValues && ! pPaperVal; i++ ) + { + const PPDValue* pVal = pPageSizeKey->getValue( i ); + if( pVal->m_aOption.EqualsIgnoreCaseAscii( m_aSystemDefaultPaper.getStr() ) ) + pPaperVal = pVal; + } + if( pPaperVal ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "setting default paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + rContext.setValue( pPageSizeKey, pPaperVal ); + #if OSL_DEBUG_LEVEL > 1 + pPaperVal = rContext.getValue( pPageSizeKey ); + fprintf( stderr, "-> got paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + } +} + +// ----------------------------------------------------------------- + +SystemQueueInfo::SystemQueueInfo() : + m_bChanged( false ) +{ + create(); +} + +SystemQueueInfo::~SystemQueueInfo() +{ + terminate(); +} + +bool SystemQueueInfo::hasChanged() const +{ + MutexGuard aGuard( m_aMutex ); + bool bChanged = m_bChanged; + return bChanged; +} + +void SystemQueueInfo::getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues ) +{ + MutexGuard aGuard( m_aMutex ); + rQueues = m_aQueues; + m_bChanged = false; +} + +OUString SystemQueueInfo::getCommand() const +{ + MutexGuard aGuard( m_aMutex ); + OUString aRet = m_aCommand; + return aRet; +} + +struct SystemCommandParameters; +typedef void(* tokenHandler)(const std::list< rtl::OString >&, + std::list< PrinterInfoManager::SystemPrintQueue >&, + const SystemCommandParameters*); + +struct SystemCommandParameters +{ + const char* pQueueCommand; + const char* pPrintCommand; + const char* pForeToken; + const char* pAftToken; + unsigned int nForeTokenCount; + tokenHandler pHandler; +}; + +#if ! (defined(LINUX) || defined(NETBSD) || defined(FREEBSD)) +static void lpgetSysQueueTokenHandler( + const std::list< rtl::OString >& i_rLines, + std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues, + const SystemCommandParameters* ) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + std::hash_set< OUString, OUStringHash > aUniqueSet; + std::hash_set< OUString, OUStringHash > aOnlySet; + aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_all" ) ) ); + aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); + + // the eventual "all" attribute of the "_all" queue tells us, which + // printers are to be used for this user at all + + // find _all: line + rtl::OString aAllLine( "_all:" ); + rtl::OString aAllAttr( "all=" ); + for( std::list< rtl::OString >::const_iterator it = i_rLines.begin(); + it != i_rLines.end(); ++it ) + { + if( it->indexOf( aAllLine, 0 ) == 0 ) + { + // now find the "all" attribute + ++it; + while( it != i_rLines.end() ) + { + rtl::OString aClean( WhitespaceToSpace( *it ) ); + if( aClean.indexOf( aAllAttr, 0 ) == 0 ) + { + // insert the comma separated entries into the set of printers to use + sal_Int32 nPos = aAllAttr.getLength(); + while( nPos != -1 ) + { + OString aTok( aClean.getToken( 0, ',', nPos ) ); + if( aTok.getLength() > 0 ) + aOnlySet.insert( rtl::OStringToOUString( aTok, aEncoding ) ); + } + break; + } + } + break; + } + } + + bool bInsertAttribute = false; + rtl::OString aDescrStr( "description=" ); + rtl::OString aLocStr( "location=" ); + for( std::list< rtl::OString >::const_iterator it = i_rLines.begin(); + it != i_rLines.end(); ++it ) + { + sal_Int32 nPos = 0; + // find the begin of a new printer section + nPos = it->indexOf( ':', 0 ); + if( nPos != -1 ) + { + OUString aSysQueue( rtl::OStringToOUString( it->copy( 0, nPos ), aEncoding ) ); + // do not insert duplicates (e.g. lpstat tends to produce such lines) + // in case there was a "_all" section, insert only those printer explicitly + // set in the "all" attribute + if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() && + ( aOnlySet.empty() || aOnlySet.find( aSysQueue ) != aOnlySet.end() ) + ) + { + o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() ); + o_rQueues.back().m_aQueue = aSysQueue; + o_rQueues.back().m_aLocation = aSysQueue; + aUniqueSet.insert( aSysQueue ); + bInsertAttribute = true; + } + else + bInsertAttribute = false; + continue; + } + if( bInsertAttribute && ! o_rQueues.empty() ) + { + // look for "description" attribute, insert as comment + nPos = it->indexOf( aDescrStr, 0 ); + if( nPos != -1 ) + { + ByteString aComment( WhitespaceToSpace( it->copy(nPos+12) ) ); + if( aComment.Len() > 0 ) + o_rQueues.back().m_aComment = String( aComment, aEncoding ); + continue; + } + // look for "location" attribute, inser as location + nPos = it->indexOf( aLocStr, 0 ); + if( nPos != -1 ) + { + ByteString aLoc( WhitespaceToSpace( it->copy(nPos+9) ) ); + if( aLoc.Len() > 0 ) + o_rQueues.back().m_aLocation = String( aLoc, aEncoding ); + continue; + } + } + } +} +#endif +static void standardSysQueueTokenHandler( + const std::list< rtl::OString >& i_rLines, + std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues, + const SystemCommandParameters* i_pParms) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + std::hash_set< OUString, OUStringHash > aUniqueSet; + rtl::OString aForeToken( i_pParms->pForeToken ); + rtl::OString aAftToken( i_pParms->pAftToken ); + /* Normal Unix print queue discovery, also used for Darwin 5 LPR printing + */ + for( std::list< rtl::OString >::const_iterator it = i_rLines.begin(); + it != i_rLines.end(); ++it ) + { + sal_Int32 nPos = 0; + + // search for a line describing a printer: + // find if there are enough tokens before the name + for( unsigned int i = 0; i < i_pParms->nForeTokenCount && nPos != -1; i++ ) + { + nPos = it->indexOf( aForeToken, nPos ); + if( nPos != -1 && it->getLength() >= nPos+aForeToken.getLength() ) + nPos += aForeToken.getLength(); + } + if( nPos != -1 ) + { + // find if there is the token after the queue + sal_Int32 nAftPos = it->indexOf( aAftToken, nPos ); + if( nAftPos != -1 ) + { + // get the queue name between fore and aft tokens + OUString aSysQueue( rtl::OStringToOUString( it->copy( nPos, nAftPos - nPos ), aEncoding ) ); + // do not insert duplicates (e.g. lpstat tends to produce such lines) + if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() ) + { + o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() ); + o_rQueues.back().m_aQueue = aSysQueue; + o_rQueues.back().m_aLocation = aSysQueue; + aUniqueSet.insert( aSysQueue ); + } + } + } + } +} + +static const struct SystemCommandParameters aParms[] = +{ + #if defined(LINUX) || defined(NETBSD) || defined(FREEBSD) + { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }, + { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }, + { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler } + #else + { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpget list", "lp -d \"(PRINTER)\"", "", ":", 0, lpgetSysQueueTokenHandler }, + { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler }, + { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }, + { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler } + #endif +}; + +void SystemQueueInfo::run() +{ + char pBuffer[1024]; + FILE *pPipe; + std::list< rtl::OString > aLines; + + /* Discover which command we can use to get a list of all printer queues */ + for( unsigned int i = 0; i < sizeof(aParms)/sizeof(aParms[0]); i++ ) + { + aLines.clear(); + rtl::OStringBuffer aCmdLine( 128 ); + aCmdLine.append( aParms[i].pQueueCommand ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "trying print queue command \"%s\" ... ", aParms[i].pQueueCommand ); + #endif + aCmdLine.append( " 2>/dev/null" ); + if( (pPipe = popen( aCmdLine.getStr(), "r" )) ) + { + while( fgets( pBuffer, 1024, pPipe ) ) + aLines.push_back( rtl::OString( pBuffer ) ); + if( ! pclose( pPipe ) ) + { + std::list< PrinterInfoManager::SystemPrintQueue > aSysPrintQueues; + aParms[i].pHandler( aLines, aSysPrintQueues, &(aParms[i]) ); + MutexGuard aGuard( m_aMutex ); + m_bChanged = true; + m_aQueues = aSysPrintQueues; + m_aCommand = rtl::OUString::createFromAscii( aParms[i].pPrintCommand ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "success\n" ); + #endif + break; + } + } + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "failed\n" ); + #endif + } +} + diff --git a/vcl/unx/source/printergfx/bitmap_gfx.cxx b/vcl/unx/source/printergfx/bitmap_gfx.cxx new file mode 100644 index 000000000000..b1ec82aa17e2 --- /dev/null +++ b/vcl/unx/source/printergfx/bitmap_gfx.cxx @@ -0,0 +1,735 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: bitmap_gfx.cxx,v $ + * $Revision: 1.13 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "psputil.hxx" + +#include "vcl/printergfx.hxx" +#include "vcl/strhelper.hxx" + +namespace psp { + +const sal_uInt32 nLineLength = 80; +const sal_uInt32 nBufferSize = 16384; + +/* + * + * Bitmap compression / Hex encoding / Ascii85 Encoding + * + */ + +PrinterBmp::~PrinterBmp () +{ /* dont need this, but C50 does */ } + +/* virtual base class */ + +class ByteEncoder +{ +private: + +public: + + virtual void EncodeByte (sal_uInt8 nByte) = 0; + virtual ~ByteEncoder () = 0; +}; + +ByteEncoder::~ByteEncoder () +{ /* dont need this, but the C50 does */ } + +/* HexEncoder */ + +class HexEncoder : public ByteEncoder +{ +private: + + osl::File* mpFile; + sal_uInt32 mnColumn; + sal_uInt32 mnOffset; + sal_Char mpFileBuffer[nBufferSize + 16]; + + HexEncoder (); /* dont use */ + +public: + + HexEncoder (osl::File* pFile); + virtual ~HexEncoder (); + void WriteAscii (sal_uInt8 nByte); + virtual void EncodeByte (sal_uInt8 nByte); + void FlushLine (); +}; + +HexEncoder::HexEncoder (osl::File* pFile) : + mpFile (pFile), + mnColumn (0), + mnOffset (0) +{} + +HexEncoder::~HexEncoder () +{ + FlushLine (); + if (mnColumn > 0) + WritePS (mpFile, "\n"); +} + +void +HexEncoder::WriteAscii (sal_uInt8 nByte) +{ + sal_uInt32 nOff = psp::getHexValueOf (nByte, mpFileBuffer + mnOffset); + mnColumn += nOff; + mnOffset += nOff; + + if (mnColumn >= nLineLength) + { + mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset); + mnColumn = 0; + } + if (mnOffset >= nBufferSize) + FlushLine (); +} + +void +HexEncoder::EncodeByte (sal_uInt8 nByte) +{ + WriteAscii (nByte); +} + +void +HexEncoder::FlushLine () +{ + if (mnOffset > 0) + { + WritePS (mpFile, mpFileBuffer, mnOffset); + mnOffset = 0; + } +} + +/* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to + indicate end of data EOD */ + +class Ascii85Encoder : public ByteEncoder +{ +private: + + osl::File* mpFile; + sal_uInt32 mnByte; + sal_uInt8 mpByteBuffer[4]; + + sal_uInt32 mnColumn; + sal_uInt32 mnOffset; + sal_Char mpFileBuffer[nBufferSize + 16]; + + Ascii85Encoder (); /* dont use */ + + inline void PutByte (sal_uInt8 nByte); + inline void PutEOD (); + void ConvertToAscii85 (); + void FlushLine (); + +public: + + Ascii85Encoder (osl::File* pFile); + virtual ~Ascii85Encoder (); + virtual void EncodeByte (sal_uInt8 nByte); + void WriteAscii (sal_uInt8 nByte); +}; + +Ascii85Encoder::Ascii85Encoder (osl::File* pFile) : + mpFile (pFile), + mnByte (0), + mnColumn (0), + mnOffset (0) +{} + +inline void +Ascii85Encoder::PutByte (sal_uInt8 nByte) +{ + mpByteBuffer [mnByte++] = nByte; +} + +inline void +Ascii85Encoder::PutEOD () +{ + WritePS (mpFile, "~>\n"); +} + +void +Ascii85Encoder::ConvertToAscii85 () +{ + if (mnByte < 4) + std::memset (mpByteBuffer + mnByte, 0, (4 - mnByte) * sizeof(sal_uInt8)); + + sal_uInt32 nByteValue = mpByteBuffer[0] * 256 * 256 * 256 + + mpByteBuffer[1] * 256 * 256 + + mpByteBuffer[2] * 256 + + mpByteBuffer[3]; + + if (nByteValue == 0 && mnByte == 4) + { + /* special case of 4 Bytes in row */ + mpFileBuffer [mnOffset] = 'z'; + + mnOffset += 1; + mnColumn += 1; + } + else + { + /* real ascii85 encoding */ + mpFileBuffer [mnOffset + 4] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 3] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 2] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 1] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 0] = (nByteValue % 85) + 33; + + mnColumn += (mnByte + 1); + mnOffset += (mnByte + 1); + + /* insert a newline if necessary */ + if (mnColumn > nLineLength) + { + sal_uInt32 nEolOff = mnColumn - nLineLength; + sal_uInt32 nBufOff = mnOffset - nEolOff; + + std::memmove (mpFileBuffer + nBufOff + 1, mpFileBuffer + nBufOff, nEolOff); + mpFileBuffer[ nBufOff ] = '\n'; + + mnOffset++; + mnColumn = nEolOff; + } + } + + mnByte = 0; +} + +void +Ascii85Encoder::WriteAscii (sal_uInt8 nByte) +{ + PutByte (nByte); + if (mnByte == 4) + ConvertToAscii85 (); + + if (mnColumn >= nLineLength) + { + mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset); + mnColumn = 0; + } + if (mnOffset >= nBufferSize) + FlushLine (); +} + +void +Ascii85Encoder::EncodeByte (sal_uInt8 nByte) +{ + WriteAscii (nByte); +} + +void +Ascii85Encoder::FlushLine () +{ + if (mnOffset > 0) + { + WritePS (mpFile, mpFileBuffer, mnOffset); + mnOffset = 0; + } +} + +Ascii85Encoder::~Ascii85Encoder () +{ + if (mnByte > 0) + ConvertToAscii85 (); + if (mnOffset > 0) + FlushLine (); + PutEOD (); +} + +/* LZW encoder */ + +class LZWEncoder : public Ascii85Encoder +{ +private: + + struct LZWCTreeNode + { + LZWCTreeNode* mpBrother; // next node with same parent + LZWCTreeNode* mpFirstChild; // first son + sal_uInt16 mnCode; // code for the string + sal_uInt16 mnValue; // pixelvalue + }; + + LZWCTreeNode* mpTable; // LZW compression data + LZWCTreeNode* mpPrefix; // the compression is as same as the TIFF compression + sal_uInt16 mnDataSize; + sal_uInt16 mnClearCode; + sal_uInt16 mnEOICode; + sal_uInt16 mnTableSize; + sal_uInt16 mnCodeSize; + sal_uInt32 mnOffset; + sal_uInt32 mdwShift; + + LZWEncoder (); + void WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen); + +public: + + LZWEncoder (osl::File* pOutputFile); + ~LZWEncoder (); + + virtual void EncodeByte (sal_uInt8 nByte); +}; + +LZWEncoder::LZWEncoder(osl::File* pOutputFile) : + Ascii85Encoder (pOutputFile) +{ + mnDataSize = 8; + + mnClearCode = 1 << mnDataSize; + mnEOICode = mnClearCode + 1; + mnTableSize = mnEOICode + 1; + mnCodeSize = mnDataSize + 1; + + mnOffset = 32; // free bits in dwShift + mdwShift = 0; + + mpTable = new LZWCTreeNode[ 4096 ]; + + for (sal_uInt32 i = 0; i < 4096; i++) + { + mpTable[i].mpBrother = NULL; + mpTable[i].mpFirstChild = NULL; + mpTable[i].mnCode = i; + mpTable[i].mnValue = (sal_uInt8)mpTable[i].mnCode; + } + + mpPrefix = NULL; + + WriteBits( mnClearCode, mnCodeSize ); +} + +LZWEncoder::~LZWEncoder() +{ + if (mpPrefix) + WriteBits (mpPrefix->mnCode, mnCodeSize); + + WriteBits (mnEOICode, mnCodeSize); + + delete[] mpTable; +} + +void +LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen) +{ + mdwShift |= (nCode << (mnOffset - nCodeLen)); + mnOffset -= nCodeLen; + while (mnOffset < 24) + { + WriteAscii ((sal_uInt8)(mdwShift >> 24)); + mdwShift <<= 8; + mnOffset += 8; + } + if (nCode == 257 && mnOffset != 32) + WriteAscii ((sal_uInt8)(mdwShift >> 24)); +} + +void +LZWEncoder::EncodeByte (sal_uInt8 nByte ) +{ + LZWCTreeNode* p; + sal_uInt16 i; + sal_uInt8 nV; + + if (!mpPrefix) + { + mpPrefix = mpTable + nByte; + } + else + { + nV = nByte; + for (p = mpPrefix->mpFirstChild; p != NULL; p = p->mpBrother) + { + if (p->mnValue == nV) + break; + } + + if (p != NULL) + { + mpPrefix = p; + } + else + { + WriteBits (mpPrefix->mnCode, mnCodeSize); + + if (mnTableSize == 409) + { + WriteBits (mnClearCode, mnCodeSize); + + for (i = 0; i < mnClearCode; i++) + mpTable[i].mpFirstChild = NULL; + + mnCodeSize = mnDataSize + 1; + mnTableSize = mnEOICode + 1; + } + else + { + if(mnTableSize == (sal_uInt16)((1 << mnCodeSize) - 1)) + mnCodeSize++; + + p = mpTable + (mnTableSize++); + p->mpBrother = mpPrefix->mpFirstChild; + mpPrefix->mpFirstChild = p; + p->mnValue = nV; + p->mpFirstChild = NULL; + } + + mpPrefix = mpTable + nV; + } + } +} + +/* + * + * bitmap handling routines + * + */ + +void +PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc, + const PrinterBmp& rBitmap) +{ + double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth(); + double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight(); + + PSGSave (); + PSTranslate (rDest.BottomLeft()); + PSScale (fScaleX, fScaleY); + + if (mnPSLevel >= 2) + { + if (rBitmap.GetDepth() == 1) + { + DrawPS2MonoImage (rBitmap, rSrc); + } + else + if (rBitmap.GetDepth() == 8 && mbColor) + { + // if the palette is larger than the image itself print it as a truecolor + // image to save diskspace. This is important for printing transparent + // bitmaps that are disassembled into small pieces + sal_Int32 nImageSz = rSrc.GetWidth() * rSrc.GetHeight(); + sal_Int32 nPaletteSz = rBitmap.GetPaletteEntryCount(); + if ((nImageSz < nPaletteSz) || (nImageSz < 24) ) + DrawPS2TrueColorImage (rBitmap, rSrc); + else + DrawPS2PaletteImage (rBitmap, rSrc); + } + else + if (rBitmap.GetDepth() == 24 && mbColor) + { + DrawPS2TrueColorImage (rBitmap, rSrc); + } + else + { + DrawPS2GrayImage (rBitmap, rSrc); + } + } + else + { + DrawPS1GrayImage (rBitmap, rSrc); + } + + PSGRestore (); +} + +/* XXX does not work XXX */ +void +PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc, + const PrinterBmp& /*rBitmap*/, const PrinterBmp& /*rTransBitmap*/) +{ + double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth(); + double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight(); + + PSGSave (); + PSTranslate (rDest.BottomLeft()); + PSScale (fScaleX, fScaleY); + PSGRestore (); +} + +/* XXX does not work XXX */ +void +PrinterGfx::DrawMask (const Rectangle& rDest, const Rectangle& rSrc, + const PrinterBmp &/*rBitmap*/, PrinterColor& /*rMaskColor*/) +{ + double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth(); + double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight(); + + PSGSave (); + PSTranslate (rDest.BottomLeft()); + PSScale (fScaleX, fScaleY); + PSGRestore (); +} + +/* + * + * Implementation: PS Level 1 + * + */ + +void +PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + sal_uInt32 nWidth = rArea.GetWidth(); + sal_uInt32 nHeight = rArea.GetHeight(); + + sal_Char pGrayImage [512]; + sal_Int32 nChar = 0; + + // image header + nChar += psp::getValueOf (nWidth, pGrayImage + nChar); + nChar += psp::appendStr (" ", pGrayImage + nChar); + nChar += psp::getValueOf (nHeight, pGrayImage + nChar); + nChar += psp::appendStr (" 8 ", pGrayImage + nChar); + nChar += psp::appendStr ("[ 1 0 0 1 0 ", pGrayImage + nChar); + nChar += psp::getValueOf (nHeight, pGrayImage + nChar); + nChar += psp::appendStr ("]", pGrayImage + nChar); + nChar += psp::appendStr (" {currentfile ", pGrayImage + nChar); + nChar += psp::getValueOf (nWidth, pGrayImage + nChar); + nChar += psp::appendStr (" string readhexstring pop}\n", pGrayImage + nChar); + nChar += psp::appendStr ("image\n", pGrayImage + nChar); + + WritePS (mpPageBody, pGrayImage); + + // image body + HexEncoder* pEncoder = new HexEncoder (mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn); + pEncoder->EncodeByte (nByte); + } + } + + delete pEncoder; + + WritePS (mpPageBody, "\n"); +} + +/* + * + * Implementation: PS Level 2 + * + */ + +void +PrinterGfx::writePS2ImageHeader (const Rectangle& rArea, psp::ImageType nType) +{ + sal_Int32 nChar = 0; + sal_Char pImage [512]; + + sal_Int32 nDictType = 0; + switch (nType) + { + case psp::TrueColorImage: nDictType = 0; break; + case psp::PaletteImage: nDictType = 1; break; + case psp::GrayScaleImage: nDictType = 2; break; + case psp::MonochromeImage: nDictType = 3; break; + default: break; + } + sal_Int32 nCompressType = mbCompressBmp ? 1 : 0; + + nChar += psp::getValueOf (rArea.GetWidth(), pImage + nChar); + nChar += psp::appendStr (" ", pImage + nChar); + nChar += psp::getValueOf (rArea.GetHeight(), pImage + nChar); + nChar += psp::appendStr (" ", pImage + nChar); + nChar += psp::getValueOf (nDictType, pImage + nChar); + nChar += psp::appendStr (" ", pImage + nChar); + nChar += psp::getValueOf (nCompressType, pImage + nChar); + nChar += psp::appendStr (" psp_imagedict image\n", pImage + nChar); + + WritePS (mpPageBody, pImage); +} + +void +PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType) +{ + switch (nType) + { + case psp::GrayScaleImage: + + WritePS (mpPageBody, "/DeviceGray setcolorspace\n"); + break; + + case psp::TrueColorImage: + + WritePS (mpPageBody, "/DeviceRGB setcolorspace\n"); + break; + + case psp::MonochromeImage: + case psp::PaletteImage: + { + + sal_Int32 nChar = 0; + sal_Char pImage [4096]; + + const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount(); + + nChar += psp::appendStr ("[/Indexed /DeviceRGB ", pImage + nChar); + nChar += psp::getValueOf (nSize - 1, pImage + nChar); + if (mbCompressBmp) + nChar += psp::appendStr ("\npsp_lzwstring\n", pImage + nChar); + else + nChar += psp::appendStr ("\npsp_ascii85string\n", pImage + nChar); + WritePS (mpPageBody, pImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + for (sal_uInt32 i = 0; i < nSize; i++) + { + PrinterColor aColor = rBitmap.GetPaletteColor(i); + + pEncoder->EncodeByte (aColor.GetRed()); + pEncoder->EncodeByte (aColor.GetGreen()); + pEncoder->EncodeByte (aColor.GetBlue()); + } + delete pEncoder; + + WritePS (mpPageBody, "pop ] setcolorspace\n"); + } + break; + default: break; + } +} + +void +PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::GrayScaleImage); + writePS2ImageHeader(rArea, psp::GrayScaleImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn); + pEncoder->EncodeByte (nByte); + } + } + + delete pEncoder; +} + +void +PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::MonochromeImage); + writePS2ImageHeader(rArea, psp::MonochromeImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + long nBitPos = 0; + sal_uChar nBit = 0; + sal_uChar nByte = 0; + + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + nBit = rBitmap.GetPixelIdx (nRow, nColumn); + nByte |= nBit << (7 - nBitPos); + + if (++nBitPos == 8) + { + pEncoder->EncodeByte (nByte); + nBitPos = 0; + nByte = 0; + } + } + // keep the row byte aligned + if (nBitPos != 0) + pEncoder->EncodeByte (nByte); + } + + delete pEncoder; +} + +void +PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::PaletteImage); + writePS2ImageHeader(rArea, psp::PaletteImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + sal_uChar nByte = rBitmap.GetPixelIdx (nRow, nColumn); + pEncoder->EncodeByte (nByte); + } + } + + delete pEncoder; +} + +void +PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::TrueColorImage); + writePS2ImageHeader(rArea, psp::TrueColorImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn); + pEncoder->EncodeByte (aColor.GetRed()); + pEncoder->EncodeByte (aColor.GetGreen()); + pEncoder->EncodeByte (aColor.GetBlue()); + } + } + + delete pEncoder; +} + +} /* namespace psp */ diff --git a/vcl/unx/source/printergfx/common_gfx.cxx b/vcl/unx/source/printergfx/common_gfx.cxx new file mode 100644 index 000000000000..632f0d70aa2f --- /dev/null +++ b/vcl/unx/source/printergfx/common_gfx.cxx @@ -0,0 +1,1310 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: common_gfx.cxx,v $ + * $Revision: 1.20.18.1 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "vcl/printergfx.hxx" +#include "vcl/printerjob.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "tools/debug.hxx" +#include "tools/color.hxx" +#include "tools/poly.hxx" + +using namespace psp ; + +static const sal_Int32 nMaxTextColumn = 80; + +GraphicsStatus::GraphicsStatus() : + mbArtItalic( false ), + mbArtBold( false ), + mnTextHeight( 0 ), + mnTextWidth( 0 ), + mfLineWidth( -1 ) +{ +} + +/* + * non graphics graphics routines + */ + +sal_Bool +PrinterGfx::Init (PrinterJob &rPrinterJob) +{ + mpPageHeader = rPrinterJob.GetCurrentPageHeader (); + mpPageBody = rPrinterJob.GetCurrentPageBody (); + mnDepth = rPrinterJob.GetDepth (); + mnPSLevel = rPrinterJob.GetPostscriptLevel (); + mbColor = rPrinterJob.IsColorPrinter (); + + mnDpi = rPrinterJob.GetResolution(); + rPrinterJob.GetScale (mfScaleX, mfScaleY); + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rPrinterJob.GetPrinterName() ) ); + if( mpFontSubstitutes ) + delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes); + if( rInfo.m_bPerformFontSubstitution ) + mpFontSubstitutes = new ::std::hash_map< fontID, fontID >( rInfo.m_aFontSubstitutions ); + else + mpFontSubstitutes = NULL; + mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False; + + return sal_True; +} + +sal_Bool +PrinterGfx::Init (const JobData& rData) +{ + mpPageHeader = NULL; + mpPageBody = NULL; + mnDepth = rData.m_nColorDepth; + mnPSLevel = rData.m_nPSLevel ? rData.m_nPSLevel : (rData.m_pParser ? rData.m_pParser->getLanguageLevel() : 2 ); + mbColor = rData.m_nColorDevice ? ( rData.m_nColorDevice == -1 ? sal_False : sal_True ) : (( rData.m_pParser ? (rData.m_pParser->isColorDevice() ? sal_True : sal_False ) : sal_True ) ); + int nRes = rData.m_aContext.getRenderResolution(); + mnDpi = nRes; + mfScaleX = (double)72.0 / (double)mnDpi; + mfScaleY = (double)72.0 / (double)mnDpi; + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rData.m_aPrinterName ) ); + if( mpFontSubstitutes ) + delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes); + if( rInfo.m_bPerformFontSubstitution ) + mpFontSubstitutes = new ::std::hash_map< fontID, fontID >( rInfo.m_aFontSubstitutions ); + else + mpFontSubstitutes = NULL; + mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False; + + return sal_True; +} + +void +PrinterGfx::GetResolution (sal_Int32 &rDpiX, sal_Int32 &rDpiY) const +{ + rDpiX = mnDpi; + rDpiY = mnDpi; +} + +sal_uInt16 +PrinterGfx::GetBitCount () +{ + return mnDepth; +} + +PrinterGfx::PrinterGfx() : + mpPageHeader (NULL), + mpPageBody (NULL), + mnFontID (0), + mnFallbackID (0), + mnTextAngle (0), + mbTextVertical (false), + mrFontMgr (PrintFontManager::get()), + mbCompressBmp (sal_True), + maFillColor (0xff,0,0), + maTextColor (0,0,0), + maLineColor (0, 0xff, 0), + mpFontSubstitutes( NULL ), + mbStrictSO52Compatibility( false ) +{ + maVirtualStatus.mfLineWidth = 1.0; + maVirtualStatus.mnTextHeight = 12; + maVirtualStatus.mnTextWidth = 0; + + maGraphicsStack.push_back( GraphicsStatus() ); +} + +PrinterGfx::~PrinterGfx() +{ + /* + * #95810# the original reasoning why mpFontSubstitutes is a pointer was + * that applications should release all PrinterGfx when printers change + * because they are really invalid; the corresponding printers may have + * changed their settings or even not exist anymore. + * + * Alas, this is not always done real time. So we keep a local copy of + * the font substitutes now in case of bad timing. + */ + delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes); +} + +void +PrinterGfx::Clear() +{ + mpPageHeader = NULL; + mpPageBody = NULL; + mnFontID = 0; + maVirtualStatus = GraphicsStatus(); + maVirtualStatus.mnTextHeight = 12; + maVirtualStatus.mnTextWidth = 0; + maVirtualStatus.mfLineWidth = 1.0; + mbTextVertical = false; + maLineColor = PrinterColor(); + maFillColor = PrinterColor(); + maTextColor = PrinterColor(); + mbCompressBmp = sal_True; + mnDpi = 300; + mnDepth = 24; + mnPSLevel = 2; + mbColor = sal_True; + mnTextAngle = 0; + + maClipRegion.clear(); + maGraphicsStack.clear(); + maGraphicsStack.push_back( GraphicsStatus() ); +} + +/* + * clip region handling + */ + +void +PrinterGfx::ResetClipRegion() +{ + maClipRegion.clear(); + PSGRestore (); + PSGSave (); // get "clean" clippath +} + +void +PrinterGfx::BeginSetClipRegion( sal_uInt32 ) +{ + maClipRegion.clear(); +} + +sal_Bool +PrinterGfx::UnionClipRegion (sal_Int32 nX,sal_Int32 nY,sal_Int32 nDX,sal_Int32 nDY) +{ + if( nDX && nDY ) + maClipRegion.push_back (Rectangle(Point(nX,nY ), Size(nDX,nDY))); + return sal_True; +} + +sal_Bool +PrinterGfx::JoinVerticalClipRectangles( std::list< Rectangle >::iterator& it, + Point& rOldPoint, sal_Int32& rColumn ) +{ + sal_Bool bSuccess = sal_False; + + std::list< Rectangle >::iterator tempit, nextit; + nextit = it; + ++nextit; + std::list< Point > leftside, rightside; + + Rectangle aLastRect( *it ); + leftside.push_back( Point( it->Left(), it->Top() ) ); + rightside.push_back( Point( it->Right()+1, it->Top() ) ); + while( nextit != maClipRegion.end() ) + { + tempit = nextit; + ++tempit; + if( nextit->Top() == aLastRect.Bottom()+1 ) + { + if( + ( nextit->Left() >= aLastRect.Left() && nextit->Left() <= aLastRect.Right() ) // left endpoint touches last rectangle + || + ( nextit->Right() >= aLastRect.Left() && nextit->Right() <= aLastRect.Right() ) // right endpoint touches last rectangle + || + ( nextit->Left() <= aLastRect.Left() && nextit->Right() >= aLastRect.Right() ) // whole line touches last rectangle + ) + { + if( aLastRect.GetHeight() > 1 || + abs( aLastRect.Left() - nextit->Left() ) > 2 || + abs( aLastRect.Right() - nextit->Right() ) > 2 + ) + { + leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) ); + rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) ); + } + aLastRect = *nextit; + leftside.push_back( aLastRect.TopLeft() ); + rightside.push_back( aLastRect.TopRight() ); + maClipRegion.erase( nextit ); + } + } + nextit = tempit; + } + if( leftside.size() > 1 ) + { + // push the last coordinates + leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) ); + rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) ); + + // cool, we can concatenate rectangles + int nDX = -65536, nDY = 65536; + int nNewDX = 0, nNewDY = 0; + + Point aLastPoint = leftside.front(); + PSBinMoveTo (aLastPoint, rOldPoint, rColumn); + leftside.pop_front(); + while( leftside.begin() != leftside.end() ) + { + Point aPoint (leftside.front()); + leftside.pop_front(); + // may have been the last one + if( leftside.begin() != leftside.end() ) + { + nNewDX = aPoint.X() - aLastPoint.X(); + nNewDY = aPoint.Y() - aLastPoint.Y(); + if( nNewDX == 0 && nDX == 0 ) + continue; + if( nDX != 0 && nNewDX != 0 && + (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX ) + continue; + } + PSBinLineTo (aPoint, rOldPoint, rColumn); + aLastPoint = aPoint; + } + + aLastPoint = rightside.back(); + nDX = -65536; + nDY = 65536; + PSBinLineTo (aLastPoint, rOldPoint, rColumn); + rightside.pop_back(); + while( rightside.begin() != rightside.end() ) + { + Point aPoint (rightside.back()); + rightside.pop_back(); + if( rightside.begin() != rightside.end() ) + { + nNewDX = aPoint.X() - aLastPoint.X(); + nNewDY = aPoint.Y() - aLastPoint.Y(); + if( nNewDX == 0 && nDX == 0 ) + continue; + if( nDX != 0 && nNewDX != 0 && + (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX ) + continue; + } + PSBinLineTo (aPoint, rOldPoint, rColumn); + } + + tempit = it; + ++tempit; + maClipRegion.erase( it ); + it = tempit; + bSuccess = sal_True; + } + return bSuccess; +} + +void +PrinterGfx::EndSetClipRegion() +{ + PSGRestore (); + PSGSave (); // get "clean" clippath + + PSBinStartPath (); + Point aOldPoint (0, 0); + sal_Int32 nColumn = 0; + + std::list< Rectangle >::iterator it = maClipRegion.begin(); + while( it != maClipRegion.end() ) + { + // try to concatenate adjacent rectangles + // first try in y direction, then in x direction + if( ! JoinVerticalClipRectangles( it, aOldPoint, nColumn ) ) + { + // failed, so it is a single rectangle + PSBinMoveTo (it->TopLeft(), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Left(), it->Bottom()+1 ), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Right()+1, it->Bottom()+1 ), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Right()+1, it->Top() ), aOldPoint, nColumn ); + ++it; + } + } + + PSBinEndPath (); + + WritePS (mpPageBody, "closepath clip newpath\n"); + maClipRegion.clear(); +} + +/* + * draw graphic primitives + */ + +void +PrinterGfx::DrawRect (const Rectangle& rRectangle ) +{ + char pRect [128]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (rRectangle.TopLeft().X(), pRect); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.TopLeft().Y(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.GetWidth(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.GetHeight(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, pRect, nChar); + WritePS (mpPageBody, "rectfill\n"); + } + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, pRect, nChar); + WritePS (mpPageBody, "rectstroke\n"); + } +} + +void +PrinterGfx::DrawLine (const Point& rFrom, const Point& rTo) +{ + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + PSMoveTo (rFrom); + PSLineTo (rTo); + WritePS (mpPageBody, "stroke\n" ); + } +} + +void +PrinterGfx::DrawPixel (const Point& rPoint, const PrinterColor& rPixelColor) +{ + if( rPixelColor.Is() ) + { + PSSetColor (rPixelColor); + PSSetColor (); + + PSMoveTo (rPoint); + PSLineTo (Point (rPoint.X ()+1, rPoint.Y ())); + PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()+1)); + PSLineTo (Point (rPoint.X (), rPoint.Y ()+1)); + WritePS (mpPageBody, "fill\n" ); + } +} + +void +PrinterGfx::DrawPolyLine (sal_uInt32 nPoints, const Point* pPath) +{ + if( maLineColor.Is() && nPoints && pPath ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + PSBinCurrentPath (nPoints, pPath); + + WritePS (mpPageBody, "stroke\n" ); + } +} + +void +PrinterGfx::DrawPolygon (sal_uInt32 nPoints, const Point* pPath) +{ + // premature end of operation + if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is())) + return; + + // setup closed path + Point aPoint( 0, 0 ); + sal_Int32 nColumn( 0 ); + + PSBinStartPath(); + PSBinMoveTo( pPath[0], aPoint, nColumn ); + for( unsigned int n = 1; n < nPoints; n++ ) + PSBinLineTo( pPath[n], aPoint, nColumn ); + if( pPath[0] != pPath[nPoints-1] ) + PSBinLineTo( pPath[0], aPoint, nColumn ); + PSBinEndPath(); + + // fill the polygon first, then draw the border, note that fill and + // stroke reset the currentpath + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + if (maLineColor.Is ()) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +void +PrinterGfx::DrawPolyPolygon (sal_uInt32 nPoly, const sal_uInt32* pSizes, const Point** pPaths ) +{ + // sanity check + if ( !nPoly || !pPaths || !(maFillColor.Is() || maLineColor.Is())) + return; + + + // setup closed path + for( unsigned int i = 0; i < nPoly; i++ ) + { + Point aPoint( 0, 0 ); + sal_Int32 nColumn( 0 ); + + PSBinStartPath(); + PSBinMoveTo( pPaths[i][0], aPoint, nColumn ); + for( unsigned int n = 1; n < pSizes[i]; n++ ) + PSBinLineTo( pPaths[i][n], aPoint, nColumn ); + if( pPaths[i][0] != pPaths[i][pSizes[i]-1] ) + PSBinLineTo( pPaths[i][0], aPoint, nColumn ); + PSBinEndPath(); + } + + // if eofill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + // first draw area + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + // now draw outlines + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +/* + * Bezier Polygon Drawing methods. + */ + +void +PrinterGfx::DrawPolyLineBezier (sal_uInt32 nPoints, const Point* pPath, const BYTE* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + + if ( maLineColor.Is() && nPoints && pPath ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + if (pFlgAry[0] != POLY_NORMAL) //There must be a starting point to moveto + { + return; + } + else + { + snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y()); + WritePS(mpPageBody, pString); + } + + // Handle the drawing of mixed lines mixed with curves + // - a normal point followed by a normal point is a line + // - a normal point followed by 2 control points and a normal point is a curve + for (unsigned int i=1; i<nPoints;) + { + if (pFlgAry[i+1] != POLY_CONTROL) //If the next point is a POLY_NORMAL, we're drawing a line + { + if (i+1 >= nPoints) return; //Make sure we don't pass the end of the array + snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y()); + i++; + } + else //Otherwise we're drawing a spline + { + if (i+3 >= nPoints) return; //Make sure we don't pass the end of the array + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPath[i+1].X(), pPath[i+1].Y(), + pPath[i+2].X(), pPath[i+2].Y(), + pPath[i+3].X(), pPath[i+3].Y()); + i+=3; + } + WritePS(mpPageBody, pString); + } + } + + // if eofill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + // first draw area + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + // now draw outlines + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +void +PrinterGfx::DrawPolygonBezier (sal_uInt32 nPoints, const Point* pPath, const BYTE* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + // premature end of operation + if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is())) + return; + + snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y()); + WritePS(mpPageBody, pString); //Move to the starting point for the PolyPoygon + for (unsigned int i=1; i < nPoints;) + { + if (pFlgAry[i] != POLY_CONTROL) + { + snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y()); + WritePS(mpPageBody, pString); + i++; + } + else + { + if (i+2 >= nPoints) + return; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) && + (pFlgAry[i+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPath[i].X(), pPath[i].Y(), + pPath[i+1].X(), pPath[i+1].Y(), + pPath[i+2].X(), pPath[i+2].Y()); + WritePS(mpPageBody, pString); + } + else + { + fprintf(stderr, "Strange output\n"); + } + i+=3; + } + } + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); +} + +void +PrinterGfx::DrawPolyPolygonBezier (sal_uInt32 nPoly, const sal_uInt32 * pPoints, const Point* const * pPtAry, const BYTE* const* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + if ( !nPoly || !pPtAry || !pPoints || !(maFillColor.Is() || maLineColor.Is())) + return; + + + for (unsigned int i=0; i<nPoly;i++) + { + sal_uInt32 nPoints = pPoints[i]; + // #112689# sanity check + if( nPoints == 0 || pPtAry[i] == NULL ) + continue; + + snprintf(pString, nBezString, "%li %li moveto\n", pPtAry[i][0].X(), pPtAry[i][0].Y()); //Move to the starting point + WritePS(mpPageBody, pString); + for (unsigned int j=1; j < nPoints;) + { + // if no flag array exists for this polygon, then it must be a regular + // polygon without beziers + if ( ! pFlgAry[i] || pFlgAry[i][j] != POLY_CONTROL) + { + snprintf(pString, nBezString, "%li %li lineto\n", pPtAry[i][j].X(), pPtAry[i][j].Y()); + WritePS(mpPageBody, pString); + j++; + } + else + { + if (j+2 >= nPoints) + break; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i][j] == POLY_CONTROL) && (pFlgAry[i][j+1] == POLY_CONTROL) && (pFlgAry[i][j+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPtAry[i][j].X(), pPtAry[i][j].Y(), + pPtAry[i][j+1].X(), pPtAry[i][j+1].Y(), + pPtAry[i][j+2].X(), pPtAry[i][j+2].Y()); + WritePS(mpPageBody, pString); + } + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "Strange output\n"); +#endif + } + j+=3; + } + } + } + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); +} + + +/* + * postscript generating routines + */ +void +PrinterGfx::PSGSave () +{ + WritePS (mpPageBody, "gsave\n" ); + GraphicsStatus aNewState; + if( maGraphicsStack.begin() != maGraphicsStack.end() ) + aNewState = maGraphicsStack.front(); + maGraphicsStack.push_front( aNewState ); +} + +void +PrinterGfx::PSGRestore () +{ + WritePS (mpPageBody, "grestore\n" ); + if( maGraphicsStack.begin() == maGraphicsStack.end() ) + WritePS (mpPageBody, "Error: too many grestores\n" ); + else + maGraphicsStack.pop_front(); +} + +void +PrinterGfx::PSSetLineWidth () +{ + if( currentState().mfLineWidth != maVirtualStatus.mfLineWidth ) + { + char pBuffer[128]; + sal_Int32 nChar = 0; + + currentState().mfLineWidth = maVirtualStatus.mfLineWidth; + nChar = psp::getValueOfDouble (pBuffer, maVirtualStatus.mfLineWidth, 5); + nChar += psp::appendStr (" setlinewidth\n", pBuffer + nChar); + WritePS (mpPageBody, pBuffer, nChar); + } +} + +void +PrinterGfx::PSSetColor () +{ + PrinterColor& rColor( maVirtualStatus.maColor ); + + if( currentState().maColor != rColor ) + { + currentState().maColor = rColor; + + char pBuffer[128]; + sal_Int32 nChar = 0; + + if( mbColor ) + { + nChar = psp::getValueOfDouble (pBuffer, + (double)rColor.GetRed() / 255.0, 5); + nChar += psp::appendStr (" ", pBuffer + nChar); + nChar += psp::getValueOfDouble (pBuffer + nChar, + (double)rColor.GetGreen() / 255.0, 5); + nChar += psp::appendStr (" ", pBuffer + nChar); + nChar += psp::getValueOfDouble (pBuffer + nChar, + (double)rColor.GetBlue() / 255.0, 5); + nChar += psp::appendStr (" setrgbcolor\n", pBuffer + nChar ); + } + else + { + Color aColor( rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() ); + sal_uInt8 nCol = aColor.GetLuminance(); + nChar = psp::getValueOfDouble( pBuffer, (double)nCol / 255.0, 5 ); + nChar += psp::appendStr( " setgray\n", pBuffer + nChar ); + } + + WritePS (mpPageBody, pBuffer, nChar); + } +} + +void +PrinterGfx::PSSetFont () +{ + GraphicsStatus& rCurrent( currentState() ); + if( maVirtualStatus.maFont != rCurrent.maFont || + maVirtualStatus.mnTextHeight != rCurrent.mnTextHeight || + maVirtualStatus.maEncoding != rCurrent.maEncoding || + maVirtualStatus.mnTextWidth != rCurrent.mnTextWidth || + maVirtualStatus.mbArtBold != rCurrent.mbArtBold || + maVirtualStatus.mbArtItalic != rCurrent.mbArtItalic + ) + { + rCurrent.maFont = maVirtualStatus.maFont; + rCurrent.maEncoding = maVirtualStatus.maEncoding; + rCurrent.mnTextWidth = maVirtualStatus.mnTextWidth; + rCurrent.mnTextHeight = maVirtualStatus.mnTextHeight; + rCurrent.mbArtItalic = maVirtualStatus.mbArtItalic; + rCurrent.mbArtBold = maVirtualStatus.mbArtBold; + + sal_Int32 nTextHeight = rCurrent.mnTextHeight; + sal_Int32 nTextWidth = rCurrent.mnTextWidth ? rCurrent.mnTextWidth + : rCurrent.mnTextHeight; + + sal_Char pSetFont [256]; + sal_Int32 nChar = 0; + + // postscript based fonts need reencoding + if ( ( rCurrent.maEncoding == RTL_TEXTENCODING_MS_1252) + || ( rCurrent.maEncoding == RTL_TEXTENCODING_ISO_8859_1) + || ( rCurrent.maEncoding >= RTL_TEXTENCODING_USER_START + && rCurrent.maEncoding <= RTL_TEXTENCODING_USER_END) + ) + { + rtl::OString aReencodedFont = + psp::GlyphSet::GetReencodedFontName (rCurrent.maEncoding, + rCurrent.maFont); + + nChar += psp::appendStr ("(", pSetFont + nChar); + nChar += psp::appendStr (aReencodedFont.getStr(), + pSetFont + nChar); + nChar += psp::appendStr (") cvn findfont ", + pSetFont + nChar); + } + else + // tt based fonts mustn't reencode, the encoding is implied by the fontname + // same for symbol type1 fonts, dont try to touch them + { + nChar += psp::appendStr ("(", pSetFont + nChar); + nChar += psp::appendStr (rCurrent.maFont.getStr(), + pSetFont + nChar); + nChar += psp::appendStr (") cvn findfont ", + pSetFont + nChar); + } + + if( ! rCurrent.mbArtItalic ) + { + nChar += psp::getValueOf (nTextWidth, pSetFont + nChar); + nChar += psp::appendStr (" ", pSetFont + nChar); + nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar); + nChar += psp::appendStr (" matrix scale makefont setfont\n", pSetFont + nChar); + } + else // skew 15 degrees to right + { + nChar += psp::appendStr ( " [", pSetFont + nChar); + nChar += psp::getValueOf (nTextWidth, pSetFont + nChar); + nChar += psp::appendStr (" 0 ", pSetFont + nChar); + nChar += psp::getValueOfDouble (pSetFont + nChar, 0.27*(double)nTextWidth, 3 ); + nChar += psp::appendStr ( " ", pSetFont + nChar); + nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar); + + nChar += psp::appendStr (" 0 0] makefont setfont\n", pSetFont + nChar); + } + + WritePS (mpPageBody, pSetFont); + } +} + +void +PrinterGfx::PSRotate (sal_Int32 nAngle) +{ + sal_Int32 nPostScriptAngle = -nAngle; + while( nPostScriptAngle < 0 ) + nPostScriptAngle += 3600; + + if (nPostScriptAngle == 0) + return; + + sal_Int32 nFullAngle = nPostScriptAngle / 10; + sal_Int32 nTenthAngle = nPostScriptAngle % 10; + + sal_Char pRotate [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (nFullAngle, pRotate); + nChar += psp::appendStr (".", pRotate + nChar); + nChar += psp::getValueOf (nTenthAngle, pRotate + nChar); + nChar += psp::appendStr (" rotate\n", pRotate + nChar); + + WritePS (mpPageBody, pRotate); +} + +void +PrinterGfx::PSPointOp (const Point& rPoint, const sal_Char* pOperator) +{ + sal_Char pPSCommand [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (rPoint.X(), pPSCommand); + nChar += psp::appendStr (" ", pPSCommand + nChar); + nChar += psp::getValueOf (rPoint.Y(), pPSCommand + nChar); + nChar += psp::appendStr (" ", pPSCommand + nChar); + nChar += psp::appendStr (pOperator, pPSCommand + nChar); + nChar += psp::appendStr ("\n", pPSCommand + nChar); + + DBG_ASSERT (nChar < 48, "Buffer overflow in PSPointOp"); + + WritePS (mpPageBody, pPSCommand); +} + +void +PrinterGfx::PSTranslate (const Point& rPoint) +{ + PSPointOp (rPoint, "translate"); +} + +void +PrinterGfx::PSMoveTo (const Point& rPoint) +{ + PSPointOp (rPoint, "moveto"); +} + +void +PrinterGfx::PSLineTo (const Point& rPoint) +{ + PSPointOp (rPoint, "lineto"); +} + +void +PrinterGfx::PSRMoveTo (sal_Int32 nDx, sal_Int32 nDy) +{ + Point aPoint(nDx, nDy); + PSPointOp (aPoint, "rmoveto"); +} + +/* get a compressed representation of the path information */ + +#define DEBUG_BINPATH 0 + +void +PrinterGfx::PSBinLineTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn) +{ +#if (DEBUG_BINPATH == 1) + PSLineTo (rCurrent); +#else + PSBinPath (rCurrent, rOld, lineto, nColumn); +#endif +} + +void +PrinterGfx::PSBinMoveTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn) +{ +#if (DEBUG_BINPATH == 1) + PSMoveTo (rCurrent); +#else + PSBinPath (rCurrent, rOld, moveto, nColumn); +#endif +} + +void +PrinterGfx::PSBinStartPath () +{ +#if (DEBUG_BINPATH == 1) + WritePS (mpPageBody, "% PSBinStartPath\n"); +#else + WritePS (mpPageBody, "readpath\n" ); +#endif +} + +void +PrinterGfx::PSBinEndPath () +{ +#if (DEBUG_BINPATH == 1) + WritePS (mpPageBody, "% PSBinEndPath\n"); +#else + WritePS (mpPageBody, "~\n"); +#endif +} + +void +PrinterGfx::PSBinCurrentPath (sal_uInt32 nPoints, const Point* pPath) +{ + // create the path + Point aPoint (0, 0); + sal_Int32 nColumn = 0; + + PSBinStartPath (); + PSBinMoveTo (*pPath, aPoint, nColumn); + for (unsigned int i = 1; i < nPoints; i++) + PSBinLineTo (pPath[i], aPoint, nColumn); + PSBinEndPath (); +} + +void +PrinterGfx::PSBinPath (const Point& rCurrent, Point& rOld, + pspath_t eType, sal_Int32& nColumn) +{ + sal_Char pPath[48]; + sal_Int32 nChar; + + // create the hex representation of the dx and dy path shift, store the field + // width as it is needed for the building the command + sal_Int32 nXPrec = getAlignedHexValueOf (rCurrent.X() - rOld.X(), pPath + 1); + sal_Int32 nYPrec = getAlignedHexValueOf (rCurrent.Y() - rOld.Y(), pPath + 1 + nXPrec); + pPath [ 1 + nXPrec + nYPrec ] = 0; + + // build the command, it is a char with bit represention 000cxxyy + // c represents the char, xx and yy repr. the field width of the dx and dy shift, + // dx and dy represent the number of bytes to read after the opcode + sal_Char cCmd = (eType == lineto ? (sal_Char)0x00 : (sal_Char)0x10); + switch (nYPrec) + { + case 2: break; + case 4: cCmd |= 0x01; break; + case 6: cCmd |= 0x02; break; + case 8: cCmd |= 0x03; break; + default: DBG_ERROR ("invalid x precision in binary path"); + } + switch (nXPrec) + { + case 2: break; + case 4: cCmd |= 0x04; break; + case 6: cCmd |= 0x08; break; + case 8: cCmd |= 0x0c; break; + default: DBG_ERROR ("invalid y precision in binary path"); + } + cCmd += 'A'; + pPath[0] = cCmd; + + // write the command to file, + // line breaking at column nMaxTextColumn (80) + nChar = 1 + nXPrec + nYPrec; + if ((nColumn + nChar) > nMaxTextColumn) + { + sal_Int32 nSegment = nMaxTextColumn - nColumn; + + WritePS (mpPageBody, pPath, nSegment); + WritePS (mpPageBody, "\n", 1); + WritePS (mpPageBody, pPath + nSegment, nChar - nSegment); + + nColumn = nChar - nSegment; + } + else + { + WritePS (mpPageBody, pPath, nChar); + + nColumn += nChar; + } + + rOld = rCurrent; +} + +void +PrinterGfx::PSScale (double fScaleX, double fScaleY) +{ + sal_Char pScale [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOfDouble (pScale, fScaleX, 5); + nChar += psp::appendStr (" ", pScale + nChar); + nChar += psp::getValueOfDouble (pScale + nChar, fScaleY, 5); + nChar += psp::appendStr (" scale\n", pScale + nChar); + + WritePS (mpPageBody, pScale); +} + +/* psshowtext helper routines: draw an hex string for show/xshow */ +void +PrinterGfx::PSHexString (const sal_uChar* pString, sal_Int16 nLen) +{ + sal_Char pHexString [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("<", pHexString); + for (int i = 0; i < nLen; i++) + { + if (nChar >= (nMaxTextColumn - 1)) + { + nChar += psp::appendStr ("\n", pHexString + nChar); + WritePS (mpPageBody, pHexString, nChar); + nChar = 0; + } + nChar += psp::getHexValueOf ((sal_Int32)pString[i], pHexString + nChar); + } + + nChar += psp::appendStr (">\n", pHexString + nChar); + WritePS (mpPageBody, pHexString, nChar); +} + +/* psshowtext helper routines: draw an array for xshow ps operator */ +void +PrinterGfx::PSDeltaArray (const sal_Int32 *pArray, sal_Int16 nEntries) +{ + sal_Char pPSArray [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("[", pPSArray + nChar); + nChar += psp::getValueOf (pArray[0], pPSArray + nChar); + + for (int i = 1; i < nEntries; i++) + { + if (nChar >= (nMaxTextColumn - 1)) + { + nChar += psp::appendStr ("\n", pPSArray + nChar); + WritePS (mpPageBody, pPSArray, nChar); + nChar = 0; + } + + nChar += psp::appendStr (" ", pPSArray + nChar); + nChar += psp::getValueOf (pArray[i] - pArray[i-1], pPSArray + nChar); + } + + nChar += psp::appendStr (" 0]\n", pPSArray + nChar); + WritePS (mpPageBody, pPSArray); +} + +/* the DrawText equivalent, pDeltaArray may be NULL. For Type1 fonts or single byte + * fonts in general nBytes and nGlyphs is the same. For printer resident Composite + * fonts it may be different (these fonts may be SJIS encoded for example) */ +void +PrinterGfx::PSShowText (const sal_uChar* pStr, sal_Int16 nGlyphs, sal_Int16 nBytes, + const sal_Int32* pDeltaArray) +{ + PSSetColor (maTextColor); + PSSetColor (); + PSSetFont (); + // rotate the user coordinate system + if (mnTextAngle != 0) + { + PSGSave (); + PSRotate (mnTextAngle); + } + + sal_Char pBuffer[256]; + if( maVirtualStatus.mbArtBold ) + { + sal_Int32 nLW = maVirtualStatus.mnTextWidth; + if( nLW == 0 ) + nLW = maVirtualStatus.mnTextHeight; + else + nLW = nLW < maVirtualStatus.mnTextHeight ? nLW : maVirtualStatus.mnTextHeight; + psp::getValueOfDouble( pBuffer, (double)nLW / 30.0 ); + } + // dispatch to the drawing method + if (pDeltaArray == NULL) + { + PSHexString (pStr, nBytes); + + if( maVirtualStatus.mbArtBold ) + { + WritePS( mpPageBody, pBuffer ); + WritePS( mpPageBody, " bshow\n" ); + } + else + WritePS (mpPageBody, "show\n"); + } + else + { + PSHexString (pStr, nBytes); + PSDeltaArray (pDeltaArray, nGlyphs - 1); + if( maVirtualStatus.mbArtBold ) + { + WritePS( mpPageBody, pBuffer ); + WritePS( mpPageBody, " bxshow\n" ); + } + else + WritePS (mpPageBody, "xshow\n"); + } + + // restore the user coordinate system + if (mnTextAngle != 0) + PSGRestore (); +} + +void +PrinterGfx::PSComment( const sal_Char* pComment ) +{ + const sal_Char* pLast = pComment; + while( pComment && *pComment ) + { + while( *pComment && *pComment != '\n' && *pComment != '\r' ) + pComment++; + if( pComment - pLast > 1 ) + { + WritePS( mpPageBody, "% ", 2 ); + WritePS( mpPageBody, pLast, pComment - pLast ); + WritePS( mpPageBody, "\n", 1 ); + } + if( *pComment ) + pLast = ++pComment; + } +} + +sal_Bool +PrinterGfx::DrawEPS( const Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize ) +{ + if( nSize == 0 ) + return sal_True; + if( ! mpPageBody ) + return sal_False; + + sal_Bool bSuccess = sal_False; + + // first search the BoundingBox of the EPS data + SvMemoryStream aStream( pPtr, nSize, STREAM_READ ); + aStream.Seek( STREAM_SEEK_TO_BEGIN ); + ByteString aLine; + + ByteString aDocTitle; + double fLeft = 0, fRight = 0, fTop = 0, fBottom = 0; + bool bEndComments = false; + while( ! aStream.IsEof() + && ( ( fLeft == 0 && fRight == 0 && fTop == 0 && fBottom == 0 ) || + ( aDocTitle.Len() == 0 && bEndComments == false ) ) + ) + { + aStream.ReadLine( aLine ); + if( aLine.Len() > 1 && aLine.GetChar( 0 ) == '%' ) + { + char cChar = aLine.GetChar(1); + if( cChar == '%' ) + { + if( aLine.CompareIgnoreCaseToAscii( "%%BoundingBox:", 14 ) == COMPARE_EQUAL ) + { + aLine = WhitespaceToSpace( aLine.GetToken( 1, ':' ) ); + if( aLine.Len() && aLine.Search( "atend" ) == STRING_NOTFOUND ) + { + fLeft = StringToDouble( GetCommandLineToken( 0, aLine ) ); + fBottom = StringToDouble( GetCommandLineToken( 1, aLine ) ); + fRight = StringToDouble( GetCommandLineToken( 2, aLine ) ); + fTop = StringToDouble( GetCommandLineToken( 3, aLine ) ); + } + } + else if( aLine.CompareIgnoreCaseToAscii( "%%Title:", 8 ) == COMPARE_EQUAL ) + aDocTitle = WhitespaceToSpace( aLine.Copy( 8 ) ); + else if( aLine.CompareIgnoreCaseToAscii( "%%EndComments", 13 ) == COMPARE_EQUAL ) + bEndComments = true; + } + else if( cChar == ' ' || cChar == '\t' || cChar == '\r' || cChar == '\n' ) + bEndComments = true; + } + else + bEndComments = true; + } + + static sal_uInt16 nEps = 0; + if( ! aDocTitle.Len() ) + aDocTitle = ByteString::CreateFromInt32( (sal_Int32)(nEps++) ); + + if( fLeft != fRight && fTop != fBottom ) + { + double fScaleX = (double)rBoundingBox.GetWidth()/(fRight-fLeft); + double fScaleY = -(double)rBoundingBox.GetHeight()/(fTop-fBottom); + Point aTranslatePoint( (int)(rBoundingBox.Left()-fLeft*fScaleX), + (int)(rBoundingBox.Bottom()+1-fBottom*fScaleY) ); + // prepare EPS + WritePS( mpPageBody, + "/b4_Inc_state save def\n" + "/dict_count countdictstack def\n" + "/op_count count 1 sub def\n" + "userdict begin\n" + "/showpage {} def\n" + "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n" + "10 setmiterlimit [] 0 setdash newpath\n" + "/languagelevel where\n" + "{pop languagelevel\n" + "1 ne\n" + " {false setstrokeadjust false setoverprint\n" + " } if\n" + "}if\n" ); + // set up clip path and scale + BeginSetClipRegion( 1 ); + UnionClipRegion( rBoundingBox.Left(), rBoundingBox.Top(), rBoundingBox.GetWidth(), rBoundingBox.GetHeight() ); + EndSetClipRegion(); + PSTranslate( aTranslatePoint ); + PSScale( fScaleX, fScaleY ); + + // DSC requires BeginDocument + WritePS( mpPageBody, "%%BeginDocument: " ); + WritePS( mpPageBody, aDocTitle ); + WritePS( mpPageBody, "\n" ); + + // write the EPS data + sal_uInt64 nOutLength; + mpPageBody->write( pPtr, nSize, nOutLength ); + bSuccess = nOutLength == nSize; + + // corresponding EndDocument + if( ((char*)pPtr)[ nSize-1 ] != '\n' ) + WritePS( mpPageBody, "\n" ); + WritePS( mpPageBody, "%%EndDocument\n" ); + + // clean up EPS + WritePS( mpPageBody, + "count op_count sub {pop} repeat\n" + "countdictstack dict_count sub {end} repeat\n" + "b4_Inc_state restore\n" ); + } + return bSuccess; +} diff --git a/vcl/unx/source/printergfx/glyphset.cxx b/vcl/unx/source/printergfx/glyphset.cxx new file mode 100644 index 000000000000..0d26a66cac2f --- /dev/null +++ b/vcl/unx/source/printergfx/glyphset.cxx @@ -0,0 +1,908 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: glyphset.cxx,v $ + * $Revision: 1.24 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "glyphset.hxx" +#include "psputil.hxx" + +#define NO_LIST +#include "sft.h" +#undef NO_LIST + +#include "vcl/printergfx.hxx" +#include "vcl/fontmanager.hxx" + +#include "osl/thread.h" + +#include "sal/alloca.h" + +#include "rtl/ustring.hxx" +#include "rtl/strbuf.hxx" + +#include <set> +#include <map> + +using namespace psp; +using namespace rtl; + +GlyphSet::GlyphSet () + : mnFontID (-1), + mbVertical (0), + mbUseFontEncoding (false) +{} + +GlyphSet::GlyphSet (sal_Int32 nFontID, sal_Bool bVertical) + : mnFontID (nFontID), + mbVertical (bVertical) +{ + PrintFontManager &rMgr = PrintFontManager::get(); + meBaseType = rMgr.getFontType (mnFontID); + maBaseName = OUStringToOString (rMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + mnBaseEncoding = rMgr.getFontEncoding(mnFontID); + mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID); +} + +GlyphSet::~GlyphSet () +{ + /* FIXME delete the glyphlist ??? */ +} + +sal_Int32 +GlyphSet::GetFontID () +{ + return mnFontID; +} + +fonttype::type +GlyphSet::GetFontType () +{ + return meBaseType; +} + +sal_Bool +GlyphSet::IsVertical () +{ + return mbVertical; +} + +sal_Bool +GlyphSet::SetFont (sal_Int32 nFontID, sal_Bool bVertical) +{ + if (mnFontID != -1) + return sal_False; + + mnFontID = nFontID; + mbVertical = bVertical; + + PrintFontManager &rMgr = PrintFontManager::get(); + meBaseType = rMgr.getFontType (mnFontID); + maBaseName = OUStringToOString (rMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + mnBaseEncoding = rMgr.getFontEncoding(mnFontID); + mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID); + + return sal_True; +} + +sal_Bool +GlyphSet::GetCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + return LookupCharID (nChar, nOutGlyphID, nOutGlyphSetID) + || AddCharID (nChar, nOutGlyphID, nOutGlyphSetID); +} + +sal_Bool +GlyphSet::GetGlyphID ( + sal_uInt32 nGlyph, + sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + return LookupGlyphID (nGlyph, nOutGlyphID, nOutGlyphSetID) + || AddGlyphID (nGlyph, nUnicode, nOutGlyphID, nOutGlyphSetID); +} + +sal_Bool +GlyphSet::LookupCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + char_list_t::iterator aGlyphSet; + sal_Int32 nGlyphSetID; + + // loop thru all the font subsets + for (aGlyphSet = maCharList.begin(), nGlyphSetID = 1; + aGlyphSet != maCharList.end(); + ++aGlyphSet, nGlyphSetID++) + { + // check every subset if it contains the queried unicode char + char_map_t::const_iterator aGlyph = (*aGlyphSet).find (nChar); + if (aGlyph != (*aGlyphSet).end()) + { + // success: found the unicode char, return the glyphid and the glyphsetid + *nOutGlyphSetID = nGlyphSetID; + *nOutGlyphID = (*aGlyph).second; + return sal_True; + } + } + + *nOutGlyphSetID = -1; + *nOutGlyphID = 0; + return sal_False; +} + +sal_Bool +GlyphSet::LookupGlyphID ( + sal_uInt32 nGlyph, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + glyph_list_t::iterator aGlyphSet; + sal_Int32 nGlyphSetID; + + // loop thru all the font subsets + for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1; + aGlyphSet != maGlyphList.end(); + ++aGlyphSet, nGlyphSetID++) + { + // check every subset if it contains the queried unicode char + glyph_map_t::const_iterator aGlyph = (*aGlyphSet).find (nGlyph); + if (aGlyph != (*aGlyphSet).end()) + { + // success: found the glyph id, return the mapped glyphid and the glyphsetid + *nOutGlyphSetID = nGlyphSetID; + *nOutGlyphID = (*aGlyph).second; + return sal_True; + } + } + + *nOutGlyphSetID = -1; + *nOutGlyphID = 0; + return sal_False; +} + +sal_uChar +GlyphSet::GetAnsiMapping (sal_Unicode nUnicodeChar) +{ + static rtl_UnicodeToTextConverter aConverter = + rtl_createUnicodeToTextConverter(RTL_TEXTENCODING_MS_1252); + static rtl_UnicodeToTextContext aContext = + rtl_createUnicodeToTextContext( aConverter ); + + sal_Char nAnsiChar; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR; + + sal_Size nSize = rtl_convertUnicodeToText( aConverter, aContext, + &nUnicodeChar, 1, &nAnsiChar, 1, + nCvtFlags, &nCvtInfo, &nCvtChars ); + + return nSize == 1 ? (sal_uChar)nAnsiChar : (sal_uChar)0; +} + +sal_uChar +GlyphSet::GetSymbolMapping (sal_Unicode nUnicodeChar) +{ + if (0x0000 < nUnicodeChar && nUnicodeChar < 0x0100) + return (sal_uChar)nUnicodeChar; + if (0xf000 < nUnicodeChar && nUnicodeChar < 0xf100) + return (sal_uChar)nUnicodeChar; + + return 0; +} + +void +GlyphSet::AddNotdef (char_map_t &rCharMap) +{ + if (rCharMap.size() == 0) + rCharMap[0] = 0; +} + +void +GlyphSet::AddNotdef (glyph_map_t &rGlyphMap) +{ + if (rGlyphMap.size() == 0) + rGlyphMap[0] = 0; +} +sal_Bool +GlyphSet::AddCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + sal_uChar nMappedChar; + + // XXX important: avoid to reencode type1 symbol fonts + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + nMappedChar = GetSymbolMapping (nChar); + else + nMappedChar = GetAnsiMapping (nChar); + + // create an empty glyphmap that is reserved for iso1252 encoded glyphs + // (or -- unencoded -- symbol glyphs) and a second map that takes any other + if (maCharList.empty()) + { + char_map_t aMap, aMapp; + + maCharList.push_back (aMap); + maCharList.push_back (aMapp); + } + // if the last map is full, create a new one + if ((!nMappedChar) && (maCharList.back().size() == 255)) + { + char_map_t aMap; + maCharList.push_back (aMap); + } + + // insert a new glyph in the font subset + if (nMappedChar) + { + // always put iso1252 chars into the first map, map them on itself + char_map_t& aGlyphSet = maCharList.front(); + AddNotdef (aGlyphSet); + + aGlyphSet [nChar] = nMappedChar; + *nOutGlyphSetID = 1; + *nOutGlyphID = nMappedChar; + } + else + { + // other chars are just appended to the list + char_map_t& aGlyphSet = maCharList.back(); + AddNotdef (aGlyphSet); + + int nSize = aGlyphSet.size(); + + aGlyphSet [nChar] = nSize; + *nOutGlyphSetID = maCharList.size(); + *nOutGlyphID = aGlyphSet [nChar]; + } + + return sal_True; +} + +sal_Bool +GlyphSet::AddGlyphID ( + sal_uInt32 nGlyph, + sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + sal_uChar nMappedChar; + + // XXX important: avoid to reencode type1 symbol fonts + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + nMappedChar = GetSymbolMapping (nUnicode); + else + nMappedChar = GetAnsiMapping (nUnicode); + + // create an empty glyphmap that is reserved for iso1252 encoded glyphs + // (or -- unencoded -- symbol glyphs) and a second map that takes any other + if (maGlyphList.empty()) + { + glyph_map_t aMap, aMapp; + + maGlyphList.push_back (aMap); + maGlyphList.push_back (aMapp); + } + // if the last map is full, create a new one + if ((!nMappedChar) && (maGlyphList.back().size() == 255)) + { + glyph_map_t aMap; + maGlyphList.push_back (aMap); + } + + // insert a new glyph in the font subset + if (nMappedChar) + { + // always put iso1252 chars into the first map, map them on itself + glyph_map_t& aGlyphSet = maGlyphList.front(); + AddNotdef (aGlyphSet); + + aGlyphSet [nGlyph] = nMappedChar; + *nOutGlyphSetID = 1; + *nOutGlyphID = nMappedChar; + } + else + { + // other chars are just appended to the list + glyph_map_t& aGlyphSet = maGlyphList.back(); + AddNotdef (aGlyphSet); + + int nSize = aGlyphSet.size(); + + aGlyphSet [nGlyph] = nSize; + *nOutGlyphSetID = maGlyphList.size(); + *nOutGlyphID = aGlyphSet [nGlyph]; + } + + return sal_True; +} + +OString +GlyphSet::GetCharSetName (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + { + OStringBuffer aSetName( maBaseName.getLength() + 32 ); + aSetName.append( maBaseName ); + aSetName.append( "FID" ); + aSetName.append( mnFontID ); + aSetName.append( mbVertical ? "VCSet" : "HCSet" ); + aSetName.append( nGlyphSetID ); + return aSetName.makeStringAndClear(); + } + else + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + { + return maBaseName; + } +} + +OString +GlyphSet::GetGlyphSetName (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + { + OStringBuffer aSetName( maBaseName.getLength() + 32 ); + aSetName.append( maBaseName ); + aSetName.append( "FID" ); + aSetName.append( mnFontID ); + aSetName.append( mbVertical ? "VGSet" : "HGSet" ); + aSetName.append( nGlyphSetID ); + return aSetName.makeStringAndClear(); + } + else + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + { + return maBaseName; + } +} + +sal_Int32 +GlyphSet::GetGlyphSetEncoding (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + return RTL_TEXTENCODING_DONTKNOW; + else + { + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + return RTL_TEXTENCODING_SYMBOL; + else + return nGlyphSetID == 1 ? RTL_TEXTENCODING_MS_1252 + : RTL_TEXTENCODING_USER_START + nGlyphSetID; + } +} + +OString +GlyphSet::GetGlyphSetEncodingName (rtl_TextEncoding nEnc, const OString &rFontName) +{ + if ( nEnc == RTL_TEXTENCODING_MS_1252 + || nEnc == RTL_TEXTENCODING_ISO_8859_1) + { + return OString("ISO1252Encoding"); + } + else + if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END) + { + return rFontName + + OString("Enc") + + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START)); + } + else + { + return OString(); + } +} + +OString +GlyphSet::GetGlyphSetEncodingName (sal_Int32 nGlyphSetID) +{ + return GetGlyphSetEncodingName (GetGlyphSetEncoding(nGlyphSetID), maBaseName); +} + +void +GlyphSet::PSDefineReencodedFont (osl::File* pOutFile, sal_Int32 nGlyphSetID) +{ + // only for ps fonts + if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1)) + return; + + sal_Char pEncodingVector [256]; + sal_Int32 nSize = 0; + + nSize += psp::appendStr ("(", pEncodingVector + nSize); + nSize += psp::appendStr (GetReencodedFontName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (") cvn (", pEncodingVector + nSize); + nSize += psp::appendStr (maBaseName.getStr(), + pEncodingVector + nSize); + nSize += psp::appendStr (") cvn ", pEncodingVector + nSize); + nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (" psp_definefont\n", + pEncodingVector + nSize); + + psp::WritePS (pOutFile, pEncodingVector); +} + +OString +GlyphSet::GetReencodedFontName (rtl_TextEncoding nEnc, const OString &rFontName) +{ + if ( nEnc == RTL_TEXTENCODING_MS_1252 + || nEnc == RTL_TEXTENCODING_ISO_8859_1) + { + return rFontName + + OString("-iso1252"); + } + else + if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END) + { + return rFontName + + OString("-enc") + + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START)); + } + else + { + return OString(); + } +} + +OString +GlyphSet::GetReencodedFontName (sal_Int32 nGlyphSetID) +{ + return GetReencodedFontName (GetGlyphSetEncoding(nGlyphSetID), maBaseName); +} + +void GlyphSet::DrawGlyphs( + PrinterGfx& rGfx, + const Point& rPoint, + const sal_uInt32* pGlyphIds, + const sal_Unicode* pUnicodes, + sal_Int16 nLen, + const sal_Int32* pDeltaArray ) +{ + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + std::set< sal_Int32 > aGlyphSet; + + // convert unicode to font glyph id and font subset + for (int nChar = 0; nChar < nLen; nChar++) + { + GetGlyphID (pGlyphIds[nChar], pUnicodes[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + aGlyphSet.insert (pGlyphSetID[nChar]); + } + + // loop over all glyph sets to detect substrings that can be xshown together + // without changing the postscript font + sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + + std::set< sal_Int32 >::iterator aSet; + for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet) + { + Point aPoint = rPoint; + sal_Int32 nOffset = 0; + sal_Int32 nGlyphs = 0; + sal_Int32 nChar; + + // get offset to first glyph + for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++) + { + nOffset = pDeltaArray [nChar]; + } + + // loop over all chars to extract those that share the current glyph set + for (nChar = 0; nChar < nLen; nChar++) + { + if (pGlyphSetID[nChar] == *aSet) + { + pGlyphSubset [nGlyphs] = pGlyphID [nChar]; + // the offset to the next glyph is determined by the glyph in + // front of the next glyph with the same glyphset id + // most often, this will be the current glyph + while ((nChar + 1) < nLen) + { + if (pGlyphSetID[nChar + 1] == *aSet) + break; + else + nChar += 1; + } + pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset; + + nGlyphs += 1; + } + } + + // show the text using the PrinterGfx text api + aPoint.Move (nOffset, 0); + + OString aGlyphSetName(GetGlyphSetName(*aSet)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet)); + rGfx.PSMoveTo (aPoint); + rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL); + } +} + +void +GlyphSet::DrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + // dispatch to the impl method + if (pDeltaArray == NULL) + ImplDrawText (rGfx, rPoint, pStr, nLen); + else + ImplDrawText (rGfx, rPoint, pStr, nLen, pDeltaArray); +} + +void +GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen) +{ + rGfx.PSMoveTo (rPoint); + + if( mbUseFontEncoding ) + { + OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) ); + OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) ); + rGfx.PSSetFont( aPSName, mnBaseEncoding ); + rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength() ); + return; + } + + int nChar; + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + + // convert unicode to glyph id and char set (font subset) + for (nChar = 0; nChar < nLen; nChar++) + GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + + // loop over the string to draw subsequent pieces of chars + // with the same postscript font + for (nChar = 0; nChar < nLen; /* atend */) + { + sal_Int32 nGlyphSetID = pGlyphSetID [nChar]; + sal_Int32 nGlyphs = 1; + for (int nNextChar = nChar + 1; nNextChar < nLen; nNextChar++) + { + if (pGlyphSetID[nNextChar] == nGlyphSetID) + nGlyphs++; + else + break; + } + + // show the text using the PrinterGfx text api + OString aGlyphSetName(GetCharSetName(nGlyphSetID)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(nGlyphSetID)); + rGfx.PSShowText (pGlyphID + nChar, nGlyphs, nGlyphs); + + nChar += nGlyphs; + } +} + +void +GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + if( mbUseFontEncoding ) + { + OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) ); + OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) ); + rGfx.PSMoveTo( rPoint ); + rGfx.PSSetFont( aPSName, mnBaseEncoding ); + rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength(), pDeltaArray ); + return; + } + + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + std::set< sal_Int32 > aGlyphSet; + + // convert unicode to font glyph id and font subset + for (int nChar = 0; nChar < nLen; nChar++) + { + GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + aGlyphSet.insert (pGlyphSetID[nChar]); + } + + // loop over all glyph sets to detect substrings that can be xshown together + // without changing the postscript font + sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + + std::set< sal_Int32 >::iterator aSet; + for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet) + { + Point aPoint = rPoint; + sal_Int32 nOffset = 0; + sal_Int32 nGlyphs = 0; + sal_Int32 nChar; + + // get offset to first glyph + for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++) + { + nOffset = pDeltaArray [nChar]; + } + + // loop over all chars to extract those that share the current glyph set + for (nChar = 0; nChar < nLen; nChar++) + { + if (pGlyphSetID[nChar] == *aSet) + { + pGlyphSubset [nGlyphs] = pGlyphID [nChar]; + // the offset to the next glyph is determined by the glyph in + // front of the next glyph with the same glyphset id + // most often, this will be the current glyph + while ((nChar + 1) < nLen) + { + if (pGlyphSetID[nChar + 1] == *aSet) + break; + else + nChar += 1; + } + pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset; + + nGlyphs += 1; + } + } + + // show the text using the PrinterGfx text api + aPoint.Move (nOffset, 0); + + OString aGlyphSetName(GetCharSetName(*aSet)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet)); + rGfx.PSMoveTo (aPoint); + rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL); + } +} + +sal_Bool +GlyphSet::PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx) +{ + // only for ps fonts + if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1)) + return sal_False; + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + return sal_False; + + PrintFontManager &rMgr = rGfx.GetFontMgr(); + + // loop thru all the font subsets + sal_Int32 nGlyphSetID = 0; + char_list_t::iterator aGlyphSet; + for (aGlyphSet = maCharList.begin(); aGlyphSet != maCharList.end(); aGlyphSet++) + { + ++nGlyphSetID; + + if (nGlyphSetID == 1) // latin1 page uses global reencoding table + { + PSDefineReencodedFont (pOutFile, nGlyphSetID); + continue; + } + if ((*aGlyphSet).size() == 0) // empty set, doesn't need reencoding + { + continue; + } + + // create reencoding table + + sal_Char pEncodingVector [256]; + sal_Int32 nSize = 0; + + nSize += psp::appendStr ("/", + pEncodingVector + nSize); + nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (" [ ", + pEncodingVector + nSize); + + // need a list of glyphs, sorted by glyphid + typedef std::map< sal_uInt8, sal_Unicode > ps_mapping_t; + typedef ps_mapping_t::value_type ps_value_t; + ps_mapping_t aSortedGlyphSet; + + char_map_t::const_iterator aUnsortedGlyph; + for (aUnsortedGlyph = (*aGlyphSet).begin(); + aUnsortedGlyph != (*aGlyphSet).end(); + ++aUnsortedGlyph) + { + aSortedGlyphSet.insert(ps_value_t((*aUnsortedGlyph).second, + (*aUnsortedGlyph).first)); + } + + ps_mapping_t::const_iterator aSortedGlyph; + // loop thru all the glyphs in the subset + for (aSortedGlyph = (aSortedGlyphSet).begin(); + aSortedGlyph != (aSortedGlyphSet).end(); + ++aSortedGlyph) + { + nSize += psp::appendStr ("/", + pEncodingVector + nSize); + + std::list< OString > aName( rMgr.getAdobeNameFromUnicode((*aSortedGlyph).second) ); + + if( aName.begin() != aName.end() ) + nSize += psp::appendStr ( aName.front(), pEncodingVector + nSize); + else + nSize += psp::appendStr (".notdef", pEncodingVector + nSize ); + nSize += psp::appendStr (" ", pEncodingVector + nSize); + // flush line + if (nSize >= 70) + { + nSize += psp::appendStr ("\n", pEncodingVector + nSize); + psp::WritePS (pOutFile, pEncodingVector); + nSize = 0; + } + } + + nSize += psp::appendStr ("] def\n", pEncodingVector + nSize); + psp::WritePS (pOutFile, pEncodingVector); + + PSDefineReencodedFont (pOutFile, nGlyphSetID); + } + + return sal_True; +} + +sal_Bool +GlyphSet::PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAsType42, std::list< OString >& rSuppliedFonts ) +{ + // only for truetype fonts + if (meBaseType != fonttype::TrueType) + return sal_False; + + TrueTypeFont *pTTFont; + OString aTTFileName (rGfx.GetFontMgr().getFontFileSysPath(mnFontID)); + int nFace = rGfx.GetFontMgr().getFontFaceNumber(mnFontID); + sal_Int32 nSuccess = OpenTTFontFile(aTTFileName.getStr(), nFace < 0 ? 0 : nFace, &pTTFont); + if (nSuccess != SF_OK) + return sal_False; + FILE* pTmpFile = tmpfile(); + if (pTmpFile == NULL) + return sal_False; + + // array of unicode source characters + sal_Unicode pUChars[256]; + + // encoding vector maps character encoding to the ordinal number + // of the glyph in the output file + sal_uChar pEncoding[256]; + sal_uInt16 pTTGlyphMapping[256]; + + // loop thru all the font subsets + sal_Int32 nCharSetID; + char_list_t::iterator aCharSet; + for (aCharSet = maCharList.begin(), nCharSetID = 1; + aCharSet != maCharList.end(); + ++aCharSet, nCharSetID++) + { + if ((*aCharSet).size() == 0) + continue; + + // loop thru all the chars in the subset + char_map_t::const_iterator aChar; + sal_Int32 n = 0; + for (aChar = (*aCharSet).begin(); aChar != (*aCharSet).end(); aChar++) + { + pUChars [n] = (*aChar).first; + pEncoding [n] = (*aChar).second; + n++; + } + // create a mapping from the unicode chars to the char encoding in + // source TrueType font + MapString (pTTFont, pUChars, (*aCharSet).size(), pTTGlyphMapping, mbVertical); + + // create the current subset + OString aCharSetName = GetCharSetName(nCharSetID); + fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aCharSetName.getStr() ); + if( bAsType42 ) + CreateT42FromTTGlyphs (pTTFont, pTmpFile, aCharSetName.getStr(), + pTTGlyphMapping, pEncoding, (*aCharSet).size() ); + else + CreateT3FromTTGlyphs (pTTFont, pTmpFile, aCharSetName.getStr(), + pTTGlyphMapping, pEncoding, (*aCharSet).size(), + 0 /* 0 = horizontal, 1 = vertical */ ); + fprintf( pTmpFile, "%%%%EndResource\n" ); + rSuppliedFonts.push_back( aCharSetName ); + } + + // loop thru all the font glyph subsets + sal_Int32 nGlyphSetID; + glyph_list_t::iterator aGlyphSet; + for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1; + aGlyphSet != maGlyphList.end(); + ++aGlyphSet, nGlyphSetID++) + { + if ((*aGlyphSet).size() == 0) + continue; + + // loop thru all the glyphs in the subset + glyph_map_t::const_iterator aGlyph; + sal_Int32 n = 0; + for (aGlyph = (*aGlyphSet).begin(); aGlyph != (*aGlyphSet).end(); aGlyph++) + { + pTTGlyphMapping [n] = (*aGlyph).first; + pEncoding [n] = (*aGlyph).second; + n++; + } + + // create the current subset + OString aGlyphSetName = GetGlyphSetName(nGlyphSetID); + fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aGlyphSetName.getStr() ); + if( bAsType42 ) + CreateT42FromTTGlyphs (pTTFont, pTmpFile, aGlyphSetName.getStr(), + pTTGlyphMapping, pEncoding, (*aGlyphSet).size() ); + else + CreateT3FromTTGlyphs (pTTFont, pTmpFile, aGlyphSetName.getStr(), + pTTGlyphMapping, pEncoding, (*aGlyphSet).size(), + 0 /* 0 = horizontal, 1 = vertical */ ); + fprintf( pTmpFile, "%%%%EndResource\n" ); + rSuppliedFonts.push_back( aGlyphSetName ); + } + + // copy the file into the page header + rewind(pTmpFile); + fflush(pTmpFile); + + sal_uChar pBuffer[0x2000]; + sal_uInt64 nIn; + sal_uInt64 nOut; + do + { + nIn = fread(pBuffer, 1, sizeof(pBuffer), pTmpFile); + rOutFile.write (pBuffer, nIn, nOut); + } + while ((nIn == nOut) && !feof(pTmpFile)); + + // cleanup + CloseTTFont (pTTFont); + fclose (pTmpFile); + + return sal_True; +} + + diff --git a/vcl/unx/source/printergfx/glyphset.hxx b/vcl/unx/source/printergfx/glyphset.hxx new file mode 100644 index 000000000000..f4cd15a56ae6 --- /dev/null +++ b/vcl/unx/source/printergfx/glyphset.hxx @@ -0,0 +1,138 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: glyphset.hxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _PSPRINT_GLYPHSET_HXX_ +#define _PSPRINT_GLYPHSET_HXX_ + +#include "vcl/fontmanager.hxx" + +#include "osl/file.hxx" + +#include "rtl/string.hxx" + +#include "tools/gen.hxx" + +#include <list> +#include <hash_map> + +namespace psp { + +class PrinterGfx; +class PrintFontManager; + +class GlyphSet +{ +private: + + sal_Int32 mnFontID; + sal_Bool mbVertical; + rtl::OString maBaseName; + fonttype::type meBaseType; + rtl_TextEncoding mnBaseEncoding; + bool mbUseFontEncoding; + + typedef std::hash_map< sal_Unicode, sal_uInt8 > char_map_t; + typedef std::list< char_map_t > char_list_t; + typedef std::hash_map< sal_uInt32, sal_uInt8 > glyph_map_t; + typedef std::list< glyph_map_t > glyph_list_t; + + char_list_t maCharList; + glyph_list_t maGlyphList; + + rtl::OString GetGlyphSetName (sal_Int32 nGlyphSetID); + rtl::OString GetCharSetName (sal_Int32 nGlyphSetID); + sal_Int32 GetGlyphSetEncoding (sal_Int32 nGlyphSetID); + rtl::OString GetGlyphSetEncodingName (sal_Int32 nGlyphSetID); + + rtl::OString GetReencodedFontName (sal_Int32 nGlyphSetID); + void PSDefineReencodedFont (osl::File* pOutFile, + sal_Int32 nGlyphSetID); + + sal_Bool GetCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool LookupCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool AddCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID); + sal_Bool GetGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool LookupGlyphID (sal_uInt32 nGlyph, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool AddGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID); + void AddNotdef (char_map_t &rCharMap); + void AddNotdef (glyph_map_t &rGlyphMap); + sal_uChar GetAnsiMapping (sal_Unicode nUnicodeChar); + sal_uChar GetSymbolMapping (sal_Unicode nUnicodeChar); + + void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen); + void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, + const sal_Int32* pDeltaArray); + +public: + + GlyphSet (); + GlyphSet (sal_Int32 nFontID, sal_Bool bVertical); + ~GlyphSet (); + + sal_Int32 GetFontID (); + fonttype::type GetFontType (); + static rtl::OString + GetReencodedFontName (rtl_TextEncoding nEnc, + const rtl::OString &rFontName); + static rtl::OString + GetGlyphSetEncodingName (rtl_TextEncoding nEnc, + const rtl::OString &rFontName); + sal_Bool IsVertical (); + + sal_Bool SetFont (sal_Int32 nFontID, sal_Bool bVertical); + + void DrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, + const sal_Int32* pDeltaArray = NULL); + void DrawGlyphs (PrinterGfx& rGfx, + const Point& rPoint, + const sal_uInt32* pGlyphIds, + const sal_Unicode* pUnicodes, + sal_Int16 nLen, + const sal_Int32* pDeltaArray ); + sal_Bool PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx); + sal_Bool PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAsType42, std::list< rtl::OString >& rSuppliedFonts ); +}; + + +} /* namespace psp */ + +#endif + diff --git a/vcl/unx/source/printergfx/makefile.mk b/vcl/unx/source/printergfx/makefile.mk new file mode 100644 index 000000000000..6de3e9bfe3bb --- /dev/null +++ b/vcl/unx/source/printergfx/makefile.mk @@ -0,0 +1,69 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.8 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=printergfx + +# --- Settings ----------------------------------------------------- + +ENABLE_EXCEPTIONS=true + +.INCLUDE : settings.mk + +.IF "$(ENABLE_CUPS)" != "" +CDEFS += -DENABLE_CUPS +.ENDIF + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"=="aqua" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"=="aqua" + +SLOFILES=\ + $(SLO)$/printerjob.obj \ + $(SLO)$/text_gfx.obj \ + $(SLO)$/psputil.obj \ + $(SLO)$/common_gfx.obj \ + $(SLO)$/glyphset.obj \ + $(SLO)$/bitmap_gfx.obj + +.ENDIF + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/vcl/unx/source/printergfx/printerjob.cxx b/vcl/unx/source/printergfx/printerjob.cxx new file mode 100644 index 000000000000..783dd5ff2b47 --- /dev/null +++ b/vcl/unx/source/printergfx/printerjob.cxx @@ -0,0 +1,1197 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: printerjob.cxx,v $ + * $Revision: 1.47 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <pwd.h> + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "vcl/printerjob.hxx" +#include "vcl/ppdparser.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/printergfx.hxx" + +#include "rtl/ustring.hxx" +#include "rtl/strbuf.hxx" +#include "rtl/ustrbuf.hxx" + +#include "osl/thread.h" +#include "sal/alloca.h" + +#include <algorithm> +#include <vector> + +using namespace psp; +using namespace rtl; + +// forward declaration + +#define nBLOCKSIZE 0x2000 + +namespace psp +{ + +sal_Bool +AppendPS (FILE* pDst, osl::File* pSrc, sal_uChar* pBuffer, + sal_uInt32 nBlockSize = nBLOCKSIZE) +{ + if ((pDst == NULL) || (pSrc == NULL)) + return sal_False; + + if (nBlockSize == 0) + nBlockSize = nBLOCKSIZE; + if (pBuffer == NULL) + pBuffer = (sal_uChar*)alloca (nBlockSize); + + pSrc->setPos (osl_Pos_Absolut, 0); + + sal_uInt64 nIn = 0; + sal_uInt64 nOut = 0; + do + { + pSrc->read (pBuffer, nBlockSize, nIn); + if (nIn > 0) + nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst); + } + while ((nIn > 0) && (nIn == nOut)); + + return sal_True; +} + +} // namespace psp + +/* + * private convenience routines for file handling + */ + +osl::File* +PrinterJob::CreateSpoolFile (const rtl::OUString& rName, const rtl::OUString& rExtension) +{ + osl::File::RC nError = osl::File::E_None; + osl::File* pFile = NULL; + + rtl::OUString aFile = rName + rExtension; + rtl::OUString aFileURL; + nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL ); + if (nError != osl::File::E_None) + return NULL; + aFileURL = maSpoolDirName + rtl::OUString::createFromAscii ("/") + aFileURL; + + pFile = new osl::File (aFileURL); + nError = pFile->open (OpenFlag_Read | OpenFlag_Write | OpenFlag_Create); + if (nError != osl::File::E_None) + { + delete pFile; + return NULL; + } + + pFile->setAttributes (aFileURL, + osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead); + return pFile; +} + +/* + * public methods of PrinterJob: for use in PrinterGfx + */ + +void +PrinterJob::GetScale (double &rXScale, double &rYScale) const +{ + rXScale = mfXScale; + rYScale = mfYScale; +} + +sal_uInt16 +PrinterJob::GetDepth () const +{ + sal_Int32 nLevel = GetPostscriptLevel(); + sal_Bool bColor = IsColorPrinter (); + + return nLevel > 1 && bColor ? 24 : 8; +} + +sal_uInt16 +PrinterJob::GetPostscriptLevel (const JobData *pJobData) const +{ + sal_uInt16 nPSLevel = 2; + + if( pJobData == NULL ) + pJobData = &m_aLastJobData; + + if( pJobData->m_nPSLevel ) + nPSLevel = pJobData->m_nPSLevel; + else + if( pJobData->m_pParser ) + nPSLevel = pJobData->m_pParser->getLanguageLevel(); + + return nPSLevel; +} + +sal_Bool +PrinterJob::IsColorPrinter () const +{ + sal_Bool bColor = sal_False; + + if( m_aLastJobData.m_nColorDevice ) + bColor = m_aLastJobData.m_nColorDevice == -1 ? sal_False : sal_True; + else if( m_aLastJobData.m_pParser ) + bColor = m_aLastJobData.m_pParser->isColorDevice() ? sal_True : sal_False; + + return bColor; +} + +osl::File* +PrinterJob::GetDocumentHeader () +{ + return mpJobHeader; +} + +osl::File* +PrinterJob::GetDocumentTrailer () +{ + return mpJobTrailer; +} + +osl::File* +PrinterJob::GetCurrentPageHeader () +{ + return maHeaderList.back(); +} + +osl::File* +PrinterJob::GetCurrentPageBody () +{ + return maPageList.back(); +} + +/* + * public methods of PrinterJob: the actual job / spool handling + */ + +PrinterJob::PrinterJob () : + mpJobHeader( NULL ), + mpJobTrailer( NULL ), + m_bQuickJob( false ) +{ +} + +namespace psp +{ + +/* check whether the given name points to a directory which is + usable for the user */ +sal_Bool +existsTmpDir (const char* pName) +{ + struct stat aFileStatus; + + if (pName == NULL) + return sal_False; + if (stat(pName, &aFileStatus) != 0) + return sal_False; + if (! S_ISDIR(aFileStatus.st_mode)) + return sal_False; + + return access(pName, W_OK | R_OK) == 0 ? sal_True : sal_False; +} + +/* return the username in the given buffer */ +sal_Bool +getUserName (char* pName, int nSize) +{ + struct passwd *pPWEntry; + struct passwd aPWEntry; + sal_Char pPWBuffer[256]; + + sal_Bool bSuccess = sal_False; + +#ifdef FREEBSD + pPWEntry = getpwuid( getuid()); +#else + if (getpwuid_r(getuid(), &aPWEntry, pPWBuffer, sizeof(pPWBuffer), &pPWEntry) != 0) + pPWEntry = NULL; +#endif + + if (pPWEntry != NULL && pPWEntry->pw_name != NULL) + { + sal_Int32 nLen = strlen(pPWEntry->pw_name); + if (nLen > 0 && nLen < nSize) + { + memcpy (pName, pPWEntry->pw_name, nLen); + pName[nLen] = '\0'; + + bSuccess = sal_True; + } + } + + // wipe the passwd off the stack + memset (pPWBuffer, 0, sizeof(pPWBuffer)); + + return bSuccess; +} + +/* remove all our temporary files, uses external program "rm", since + osl functionality is inadequate */ +void +removeSpoolDir (const rtl::OUString& rSpoolDir) +{ + rtl::OUString aSysPath; + if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) ) + { + // Conversion did not work, as this is quite a dangerous action, + // we should abort here .... + OSL_ENSURE( 0, "psprint: couldn't remove spool directory" ); + return; + } + rtl::OString aSysPathByte = + rtl::OUStringToOString (aSysPath, osl_getThreadTextEncoding()); + sal_Char pSystem [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("rm -rf ", pSystem); + nChar += psp::appendStr (aSysPathByte.getStr(), pSystem + nChar); + + system (pSystem); +} + +/* creates a spool directory with a "pidgin random" value based on + current system time */ +rtl::OUString +createSpoolDir () +{ + TimeValue aCur; + osl_getSystemTime( &aCur ); + sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000); + + rtl::OUString aTmpDir; + osl_getTempDirURL( &aTmpDir.pData ); + + do + { + rtl::OUStringBuffer aDir( aTmpDir.getLength() + 16 ); + aDir.append( aTmpDir ); + aDir.appendAscii( "/psp" ); + aDir.append(nRand); + rtl::OUString aResult = aDir.makeStringAndClear(); + if( osl::Directory::create( aResult ) == osl::FileBase::E_None ) + { + osl::File::setAttributes( aResult, + osl_File_Attribute_OwnWrite + | osl_File_Attribute_OwnRead + | osl_File_Attribute_OwnExe ); + return aResult; + } + nRand++; + } while( nRand ); + return rtl::OUString(); +} + +} // namespace psp + +PrinterJob::~PrinterJob () +{ + std::list< osl::File* >::iterator pPage; + for (pPage = maPageList.begin(); pPage != maPageList.end(); pPage++) + { + //(*pPage)->remove(); + delete *pPage; + } + for (pPage = maHeaderList.begin(); pPage != maHeaderList.end(); pPage++) + { + //(*pPage)->remove(); + delete *pPage; + } + // mpJobHeader->remove(); + delete mpJobHeader; + // mpJobTrailer->remove(); + delete mpJobTrailer; + + // XXX should really call osl::remove routines + removeSpoolDir (maSpoolDirName); + + // osl::Directory::remove (maSpoolDirName); +} + +namespace psp +{ + +// get locale invariant, 7bit clean current local time string +sal_Char* +getLocalTime(sal_Char* pBuffer) +{ + time_t nTime = time (NULL); + struct tm aTime; + struct tm *pLocalTime = localtime_r (&nTime, &aTime); + + return asctime_r(pLocalTime, pBuffer); +} + +} + +static bool isAscii( const rtl::OUString& rStr ) +{ + const sal_Unicode* pStr = rStr; + sal_Int32 nLen = rStr.getLength(); + for( sal_Int32 i = 0; i < nLen; i++ ) + if( pStr[i] > 127 ) + return false; + return true; +} + +sal_Bool +PrinterJob::StartJob ( + const rtl::OUString& rFileName, + int nMode, + const rtl::OUString& rJobName, + const rtl::OUString& rAppName, + const JobData& rSetupData, + PrinterGfx* pGraphics, + bool bIsQuickJob + ) +{ + m_bQuickJob = bIsQuickJob; + mnMaxWidthPt = mnMaxHeightPt = 0; + mnLandscapes = mnPortraits = 0; + m_pGraphics = pGraphics; + InitPaperSize (rSetupData); + + // create file container for document header and trailer + maFileName = rFileName; + mnFileMode = nMode; + maSpoolDirName = createSpoolDir (); + maJobTitle = rJobName; + + rtl::OUString aExt = rtl::OUString::createFromAscii (".ps"); + mpJobHeader = CreateSpoolFile (rtl::OUString::createFromAscii("psp_head"), aExt); + mpJobTrailer = CreateSpoolFile (rtl::OUString::createFromAscii("psp_tail"), aExt); + if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor + return sal_False; + + // write document header according to Document Structuring Conventions (DSC) + WritePS (mpJobHeader, + "%!PS-Adobe-3.0\n" + "%%BoundingBox: (atend)\n" ); + + rtl::OUString aFilterWS; + + // Creator (this application) + aFilterWS = WhitespaceToSpace( rAppName, FALSE ); + WritePS (mpJobHeader, "%%Creator: ("); + WritePS (mpJobHeader, aFilterWS); + WritePS (mpJobHeader, ")\n"); + + // For (user name) + sal_Char pUserName[64]; + if (getUserName(pUserName, sizeof(pUserName))) + { + WritePS (mpJobHeader, "%%For: ("); + WritePS (mpJobHeader, pUserName); + WritePS (mpJobHeader, ")\n"); + } + + // Creation Date (locale independent local time) + sal_Char pCreationDate [256]; + WritePS (mpJobHeader, "%%CreationDate: ("); + getLocalTime(pCreationDate); + for( unsigned int i = 0; i < sizeof(pCreationDate)/sizeof(pCreationDate[0]); i++ ) + { + if( pCreationDate[i] == '\n' ) + { + pCreationDate[i] = 0; + break; + } + } + WritePS (mpJobHeader, pCreationDate ); + WritePS (mpJobHeader, ")\n"); + + // Document Title + /* #i74335# + * The title should be clean ascii; rJobName however may + * contain any Unicode character. So implement the following + * algorithm: + * use rJobName, if it contains only ascii + * use the filename, if it contains only ascii + * else omit %%Title + */ + aFilterWS = WhitespaceToSpace( rJobName, FALSE ); + rtl::OUString aTitle( aFilterWS ); + if( ! isAscii( aTitle ) ) + { + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + aTitle = rFileName.getToken( 0, '/', nIndex ); + aTitle = WhitespaceToSpace( aTitle, FALSE ); + if( ! isAscii( aTitle ) ) + aTitle = rtl::OUString(); + } + + maJobTitle = aFilterWS; + if( aTitle.getLength() ) + { + WritePS (mpJobHeader, "%%Title: ("); + WritePS (mpJobHeader, aTitle); + WritePS (mpJobHeader, ")\n"); + } + + // Language Level + sal_Char pLevel[16]; + sal_Int32 nSz = getValueOf(GetPostscriptLevel(&rSetupData), pLevel); + pLevel[nSz++] = '\n'; + pLevel[nSz ] = '\0'; + WritePS (mpJobHeader, "%%LanguageLevel: "); + WritePS (mpJobHeader, pLevel); + + // Other + WritePS (mpJobHeader, "%%DocumentData: Clean7Bit\n"); + WritePS (mpJobHeader, "%%Pages: (atend)\n"); + WritePS (mpJobHeader, "%%Orientation: (atend)\n"); + WritePS (mpJobHeader, "%%PageOrder: Ascend\n"); + WritePS (mpJobHeader, "%%EndComments\n"); + + // write Prolog + writeProlog (mpJobHeader, rSetupData); + + // mark last job setup as not set + m_aLastJobData.m_pParser = NULL; + m_aLastJobData.m_aContext.setParser( NULL ); + + return sal_True; +} + +sal_Bool +PrinterJob::EndJob () +{ + // write document setup (done here because it + // includes the accumulated fonts + if( mpJobHeader ) + writeSetup( mpJobHeader, m_aDocumentJobData ); + m_pGraphics->OnEndJob(); + if( ! (mpJobHeader && mpJobTrailer) ) + return sal_False; + + // write document trailer according to Document Structuring Conventions (DSC) + rtl::OStringBuffer aTrailer(512); + aTrailer.append( "%%Trailer\n" ); + aTrailer.append( "%%BoundingBox: 0 0 " ); + aTrailer.append( (sal_Int32)mnMaxWidthPt ); + aTrailer.append( " " ); + aTrailer.append( (sal_Int32)mnMaxHeightPt ); + if( mnLandscapes > mnPortraits ) + aTrailer.append("\n%%Orientation: Landscape"); + else + aTrailer.append("\n%%Orientation: Portrait"); + aTrailer.append( "\n%%Pages: " ); + aTrailer.append( (sal_Int32)maPageList.size() ); + aTrailer.append( "\n%%EOF\n" ); + WritePS (mpJobTrailer, aTrailer.getStr()); + + /* + * spool the set of files to their final destination, this is U**X dependent + */ + + FILE* pDestFILE = NULL; + + /* create a destination either as file or as a pipe */ + sal_Bool bSpoolToFile = maFileName.getLength() > 0 ? sal_True : sal_False; + if (bSpoolToFile) + { + const rtl::OString aFileName = rtl::OUStringToOString (maFileName, + osl_getThreadTextEncoding()); + if( mnFileMode ) + { + int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode ); + if( nFile != -1 ) + { + pDestFILE = fdopen( nFile, "w" ); + if( pDestFILE == NULL ) + { + close( nFile ); + unlink( aFileName.getStr() ); + return sal_False; + } + } + else + chmod( aFileName.getStr(), mnFileMode ); + } + if (pDestFILE == NULL) + pDestFILE = fopen (aFileName.getStr(), "w"); + + if (pDestFILE == NULL) + return sal_False; + } + else + { + PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get (); + pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob ); + if (pDestFILE == NULL) + return sal_False; + } + + /* spool the document parts to the destination */ + + sal_uChar pBuffer[ nBLOCKSIZE ]; + + AppendPS (pDestFILE, mpJobHeader, pBuffer); + mpJobHeader->close(); + + sal_Bool bSuccess = sal_True; + std::list< osl::File* >::iterator pPageBody; + std::list< osl::File* >::iterator pPageHead; + for (pPageBody = maPageList.begin(), pPageHead = maHeaderList.begin(); + pPageBody != maPageList.end() && pPageHead != maHeaderList.end(); + pPageBody++, pPageHead++) + { + if( *pPageHead ) + { + osl::File::RC nError = (*pPageHead)->open(OpenFlag_Read); + if (nError == osl::File::E_None) + { + AppendPS (pDestFILE, *pPageHead, pBuffer); + (*pPageHead)->close(); + } + } + else + bSuccess = sal_False; + if( *pPageBody ) + { + osl::File::RC nError = (*pPageBody)->open(OpenFlag_Read); + if (nError == osl::File::E_None) + { + AppendPS (pDestFILE, *pPageBody, pBuffer); + (*pPageBody)->close(); + } + } + else + bSuccess = sal_False; + } + + AppendPS (pDestFILE, mpJobTrailer, pBuffer); + mpJobTrailer->close(); + + /* well done */ + + if (bSpoolToFile) + fclose (pDestFILE); + else + { + PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get(); + if (0 == rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName, + maJobTitle, pDestFILE, m_aDocumentJobData )) + { + bSuccess = sal_False; + } + } + + return bSuccess; +} + +sal_Bool +PrinterJob::AbortJob () +{ + m_pGraphics->OnEndJob(); + return sal_False; +} + +void +PrinterJob::InitPaperSize (const JobData& rJobSetup) +{ + int nRes = rJobSetup.m_aContext.getRenderResolution (); + + String aPaper; + int nWidth, nHeight; + rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight); + + int nLeft = 0, nRight = 0, nUpper = 0, nLower = 0; + const PPDParser* pParser = rJobSetup.m_aContext.getParser(); + if (pParser != NULL) + pParser->getMargins (aPaper, nLeft, nRight, nUpper, nLower); + + mnResolution = nRes; + + mnWidthPt = nWidth; + mnHeightPt = nHeight; + + if( mnWidthPt > mnMaxWidthPt ) + mnMaxWidthPt = mnWidthPt; + if( mnHeightPt > mnMaxHeightPt ) + mnMaxHeightPt = mnHeightPt; + + mnLMarginPt = nLeft; + mnRMarginPt = nRight; + mnTMarginPt = nUpper; + mnBMarginPt = nLower; + + mfXScale = (double)72.0 / (double)mnResolution; + mfYScale = -1.0 * (double)72.0 / (double)mnResolution; +} + + +sal_Bool +PrinterJob::StartPage (const JobData& rJobSetup) +{ + InitPaperSize (rJobSetup); + + rtl::OUString aPageNo = rtl::OUString::valueOf ((sal_Int32)maPageList.size()+1); // sequential page number must start with 1 + rtl::OUString aExt = aPageNo + rtl::OUString::createFromAscii (".ps"); + + osl::File* pPageHeader = CreateSpoolFile ( + rtl::OUString::createFromAscii("psp_pghead"), aExt); + osl::File* pPageBody = CreateSpoolFile ( + rtl::OUString::createFromAscii("psp_pgbody"), aExt); + + maHeaderList.push_back (pPageHeader); + maPageList.push_back (pPageBody); + + if( ! (pPageHeader && pPageBody) ) + return sal_False; + + /* #i7262# write setup only before first page + * don't do this in StartJob since the jobsetup there may be + * different. + */ + bool bSuccess = true; + if( 1 == maPageList.size() ) + m_aDocumentJobData = rJobSetup; + + // write page header according to Document Structuring Conventions (DSC) + WritePS (pPageHeader, "%%Page: "); + WritePS (pPageHeader, aPageNo); + WritePS (pPageHeader, " "); + WritePS (pPageHeader, aPageNo); + WritePS (pPageHeader, "\n"); + + if( rJobSetup.m_eOrientation == orientation::Landscape ) + { + WritePS (pPageHeader, "%%PageOrientation: Landscape\n"); + mnLandscapes++; + } + else + { + WritePS (pPageHeader, "%%PageOrientation: Portrait\n"); + mnPortraits++; + } + + sal_Char pBBox [256]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("%%PageBoundingBox: ", pBBox); + nChar += psp::getValueOf (mnLMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnBMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnWidthPt - mnRMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox + nChar); + nChar += psp::appendStr ("\n", pBBox + nChar); + + WritePS (pPageHeader, pBBox); + + if (bSuccess) + bSuccess = writePageSetup ( pPageHeader, rJobSetup ); + if(bSuccess) + m_aLastJobData = rJobSetup; + + + return bSuccess; +} + +sal_Bool +PrinterJob::EndPage () +{ + m_pGraphics->OnEndPage(); + + osl::File* pPageHeader = maHeaderList.back(); + osl::File* pPageBody = maPageList.back(); + + if( ! (pPageBody && pPageHeader) ) + return sal_False; + + // copy page to paper and write page trailer according to DSC + + sal_Char pTrailer[256]; + sal_Int32 nChar = 0; + nChar = psp::appendStr ("grestore grestore\n", pTrailer); + nChar += psp::appendStr ("showpage\n", pTrailer + nChar); + nChar += psp::appendStr ("%%PageTrailer\n\n", pTrailer + nChar); + WritePS (pPageBody, pTrailer); + + // this page is done for now, close it to avoid having too many open fd's + + pPageHeader->close(); + pPageBody->close(); + + return sal_True; +} + +sal_uInt32 +PrinterJob::GetErrorCode () +{ + /* TODO */ + return 0; +} + +struct less_ppd_key : public ::std::binary_function<double, double, bool> +{ + bool operator()(const PPDKey* left, const PPDKey* right) + { return left->getOrderDependency() < right->getOrderDependency(); } +}; + +static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature ) +{ + if( ! pKey || ! pValue ) + return true; + + OStringBuffer aFeature(256); + aFeature.append( "[{\n" ); + if( bUseIncluseFeature ) + aFeature.append( "%%IncludeFeature:" ); + else + aFeature.append( "%%BeginFeature:" ); + aFeature.append( " *" ); + aFeature.append( OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ) ); + aFeature.append( ' ' ); + aFeature.append( OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) ); + if( !bUseIncluseFeature ) + { + aFeature.append( '\n' ); + aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) ); + aFeature.append( "\n%%EndFeature" ); + } + aFeature.append( "\n} stopped cleartomark\n" ); + sal_uInt64 nWritten = 0; + return pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten ) + || nWritten != (sal_uInt64)aFeature.getLength() ? false : true; +} + +bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup ) +{ + bool bSuccess = true; + int i; + + // emit features ordered to OrderDependency + // ignore features that are set to default + + // sanity check + if( rJob.m_pParser == rJob.m_aContext.getParser() && + rJob.m_pParser && + ( m_aLastJobData.m_pParser == rJob.m_pParser || m_aLastJobData.m_pParser == NULL ) + ) + { + int nKeys = rJob.m_aContext.countValuesModified(); + ::std::vector< const PPDKey* > aKeys( nKeys ); + for( i = 0; i < nKeys; i++ ) + aKeys[i] = rJob.m_aContext.getModifiedKey( i ); + ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); + + for( i = 0; i < nKeys && bSuccess; i++ ) + { + const PPDKey* pKey = aKeys[i]; + bool bEmit = false; + if( bDocumentSetup ) + { + if( pKey->getSetupType() == PPDKey::DocumentSetup ) + bEmit = true; + } + else + { + if( pKey->getSetupType() == PPDKey::PageSetup || + pKey->getSetupType() == PPDKey::AnySetup ) + bEmit = true; + } + if( bEmit ) + { + const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); + if( pValue + && pValue->m_eType == eInvocation + && ( m_aLastJobData.m_pParser == NULL + || m_aLastJobData.m_aContext.getValue( pKey ) != pValue + || bDocumentSetup + ) + ) + { + // try to avoid PS level 2 feature commands if level is set to 1 + if( GetPostscriptLevel( &rJob ) == 1 ) + { + bool bHavePS2 = + ( pValue->m_aValue.SearchAscii( "<<" ) != STRING_NOTFOUND ) + || + ( pValue->m_aValue.SearchAscii( ">>" ) != STRING_NOTFOUND ); + if( bHavePS2 ) + continue; + } + bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() ); + } + } + } + } + else + bSuccess = false; + + return bSuccess; +} + +bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob ) +{ + bool bSuccess = true; + + WritePS (pFile, "%%BeginPageSetup\n%\n"); + + bSuccess = writeFeatureList( pFile, rJob, false ); + WritePS (pFile, "%%EndPageSetup\n"); + + sal_Char pTranslate [128]; + sal_Int32 nChar = 0; + + if( rJob.m_eOrientation == orientation::Portrait ) + { + nChar = psp::appendStr ("gsave\n[", pTranslate); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5); + nChar += psp::appendStr (" 0 0 ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfYScale, 5); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnRMarginPt, pTranslate + nChar); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnHeightPt-mnTMarginPt, + pTranslate + nChar); + nChar += psp::appendStr ("] concat\ngsave\n", + pTranslate + nChar); + } + else + { + nChar = psp::appendStr ("gsave\n", pTranslate); + nChar += psp::appendStr ("[ 0 ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, -mfYScale, 5); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5); + nChar += psp::appendStr (" 0 ", pTranslate + nChar ); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mnLMarginPt, 5 ); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnBMarginPt, pTranslate + nChar ); + nChar += psp::appendStr ("] concat\ngsave\n", + pTranslate + nChar); + } + + WritePS (pFile, pTranslate); + + return bSuccess; +} + +void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData ) +{ + const PPDKey* pKey = NULL; + + if( rJobData.m_pParser ) + pKey = rJobData.m_pParser->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "JobPatchFile" ) ) ); + if( ! pKey ) + return; + + // order the patch files + // according to PPD spec the JobPatchFile options must be int + // and should be emitted in order + std::list< sal_Int32 > patch_order; + int nValueCount = pKey->countValues(); + for( int i = 0; i < nValueCount; i++ ) + { + const PPDValue* pVal = pKey->getValue( i ); + patch_order.push_back( pVal->m_aOption.ToInt32() ); + if( patch_order.back() == 0 && ! pVal->m_aOption.EqualsAscii( "0" ) ) + { + WritePS( pFile, "% Warning: left out JobPatchFile option \"" ); + OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US ); + WritePS( pFile, aOption.getStr() ); + WritePS( pFile, + "\"\n% as it violates the PPD spec;\n" + "% JobPatchFile options need to be numbered for ordering.\n" ); + } + } + + patch_order.sort(); + patch_order.unique(); + + while( patch_order.begin() != patch_order.end() ) + { + // note: this discards patch files not adhering to the "int" scheme + // as there won't be a value for them + writeFeature( pFile, pKey, pKey->getValue( OUString::valueOf( patch_order.front() ) ), false ); + patch_order.pop_front(); + } +} + +bool PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData ) +{ + WritePS( pFile, "%%BeginProlog\n" ); + + // JobPatchFile feature needs to be emitted at begin of prolog + writeJobPatch( pFile, rJobData ); + + static const sal_Char pProlog[] = { + "%%BeginResource: procset PSPrint-Prolog 1.0 0\n" + "/ISO1252Encoding [\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n" + "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n" + "/zero /one /two /three /four /five /six /seven\n" + "/eight /nine /colon /semicolon /less /equal /greater /question\n" + "/at /A /B /C /D /E /F /G\n" + "/H /I /J /K /L /M /N /O\n" + "/P /Q /R /S /T /U /V /W\n" + "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n" + "/grave /a /b /c /d /e /f /g\n" + "/h /i /j /k /l /m /n /o\n" + "/p /q /r /s /t /u /v /w\n" + "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n" + "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n" + "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n" + "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n" + "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n" + "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n" + "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n" + "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n" + "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n" + "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n" + "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n" + "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n" + "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n" + "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n" + "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n" + "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n" + "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n" + "\n" + "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n" + "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n" + "currentdict end exch pop definefont pop } def\n" + "\n" + "/pathdict dup 8 dict def load begin\n" + "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n" + "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n" + "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n" + "eq 3 1 roll exch } def\n" + "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n" + "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n" + "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n" + "for 256 div exch pop exch { neg } if } def\n" + "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n" + "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n" + "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n" + "\n" + "systemdict /languagelevel known not {\n" + "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n" + "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n" + "roll show moveto 0 rmoveto } for pop pop } def\n" + "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n" + "rlineto closepath } def\n" + "/rectfill { rectangle fill } def\n" + "/rectstroke { rectangle stroke } def } if\n" + "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n" + "setlinewidth false charpath stroke setlinewidth } def\n" + "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n" + "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n" + "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n" + "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n" + "\n" + "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n" + "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n" + "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n" + "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n" + "/psp_imagedict {\n" + "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n" + "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n" + "def 7 dict dup\n" + "/ImageType 1 put dup\n" + "/Width 7 -1 roll put dup\n" + "/Height 5 index put dup\n" + "/BitsPerComponent 4 index psp_bitspercomponent put dup\n" + "/Decode 5 -1 roll psp_decodearray put dup\n" + "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n" + "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n" + "} def\n" + "%%EndResource\n" + "%%EndProlog\n" + }; + static const sal_Char pSO52CompatProlog[] = { + "%%BeginResource: procset PSPrint-Prolog 1.0 0\n" + "/ISO1252Encoding [\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n" + "/parenleft /parenright /asterisk /plus /comma /minus /period /slash\n" + "/zero /one /two /three /four /five /six /seven\n" + "/eight /nine /colon /semicolon /less /equal /greater /question\n" + "/at /A /B /C /D /E /F /G\n" + "/H /I /J /K /L /M /N /O\n" + "/P /Q /R /S /T /U /V /W\n" + "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n" + "/grave /a /b /c /d /e /f /g\n" + "/h /i /j /k /l /m /n /o\n" + "/p /q /r /s /t /u /v /w\n" + "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n" + "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n" + "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n" + "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n" + "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n" + "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n" + "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n" + "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n" + "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n" + "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n" + "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n" + "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n" + "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n" + "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n" + "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n" + "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n" + "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n" + "\n" + "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n" + "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n" + "currentdict end exch pop definefont pop } def\n" + "\n" + "/pathdict dup 8 dict def load begin\n" + "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n" + "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n" + "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n" + "eq 3 1 roll exch } def\n" + "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n" + "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n" + "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n" + "for 256 div exch pop exch { neg } if } def\n" + "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n" + "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n" + "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n" + "\n" + "systemdict /languagelevel known not {\n" + "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n" + "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n" + "roll show moveto 0 rmoveto } for pop pop } def\n" + "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n" + "rlineto closepath } def\n" + "/rectfill { rectangle fill } def\n" + "/rectstroke { rectangle stroke } def } if\n" + "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n" + "setlinewidth false charpath stroke setlinewidth } def\n" + "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n" + "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n" + "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n" + "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n" + "\n" + "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n" + "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n" + "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n" + "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n" + "/psp_imagedict {\n" + "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n" + "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n" + "def 7 dict dup\n" + "/ImageType 1 put dup\n" + "/Width 7 -1 roll put dup\n" + "/Height 5 index put dup\n" + "/BitsPerComponent 4 index psp_bitspercomponent put dup\n" + "/Decode 5 -1 roll psp_decodearray put dup\n" + "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n" + "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n" + "} def\n" + "%%EndResource\n" + "%%EndProlog\n" + }; + WritePS (pFile, m_pGraphics && m_pGraphics->getStrictSO52Compatibility() ? pSO52CompatProlog : pProlog); + + return true; +} + +bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob ) +{ + WritePS (pFile, "%%BeginSetup\n%\n"); + + // download fonts + std::list< rtl::OString > aFonts[2]; + m_pGraphics->writeResources( pFile, aFonts[0], aFonts[1] ); + + for( int i = 0; i < 2; i++ ) + { + if( !aFonts[i].empty() ) + { + std::list< rtl::OString >::const_iterator it = aFonts[i].begin(); + rtl::OStringBuffer aLine( 256 ); + if( i == 0 ) + aLine.append( "%%DocumentSuppliedResources: font " ); + else + aLine.append( "%%DocumentNeededResources: font " ); + aLine.append( *it ); + aLine.append( "\n" ); + WritePS ( pFile, aLine.getStr() ); + while( (++it) != aFonts[i].end() ) + { + aLine.setLength(0); + aLine.append( "%%+ font " ); + aLine.append( *it ); + aLine.append( "\n" ); + WritePS ( pFile, aLine.getStr() ); + } + } + } + + bool bSuccess = true; + // in case of external print dialog the number of copies is prepended + // to the job, let us not complicate things by emitting our own copy count + bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" ); + if( ! bExternalDialog && rJob.m_nCopies > 1 ) + { + // setup code + ByteString aLine( "/#copies " ); + aLine += ByteString::CreateFromInt32( rJob.m_nCopies ); + aLine += " def\n"; + sal_uInt64 nWritten = 0; + bSuccess = pFile->write( aLine.GetBuffer(), aLine.Len(), nWritten ) + || nWritten != aLine.Len() ? false : true; + + if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 ) + WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" ); + } + + bool bFeatureSuccess = writeFeatureList( pFile, rJob, true ); + + WritePS (pFile, "%%EndSetup\n"); + + return bSuccess && bFeatureSuccess; +} diff --git a/vcl/unx/source/printergfx/psheader.ps b/vcl/unx/source/printergfx/psheader.ps new file mode 100644 index 000000000000..7b947b3a470b --- /dev/null +++ b/vcl/unx/source/printergfx/psheader.ps @@ -0,0 +1,372 @@ +%************************************************************************* +% +% DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +% +% Copyright 2008 by Sun Microsystems, Inc. +% +% OpenOffice.org - a multi-platform office productivity suite +% +% $RCSfile: psheader.ps,v $ +% +% $Revision: 1.7 $ +% +% This file is part of OpenOffice.org. +% +% OpenOffice.org is free software: you can redistribute it and/or modify +% it under the terms of the GNU Lesser General Public License version 3 +% only, as published by the Free Software Foundation. +% +% OpenOffice.org is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU Lesser General Public License version 3 for more details +% (a copy is included in the LICENSE file that accompanied this code). +% +% You should have received a copy of the GNU Lesser General Public License +% version 3 along with OpenOffice.org. If not, see +% <http://www.openoffice.org/license.html> +% for a copy of the LGPLv3 License. +% +%************************************************************************* + +% +% +% readpath +% +% The intention of readpath is to save disk space since the vcl clip region routines +% produce a huge amount of lineto/moveto commands +% +% The principal idea is to maintain the current point on stack and to provide only deltas +% in the command. These deltas are added to the current point. The new point is used for +% the lineto and moveto command and saved on stack for the next command. +% +% pathdict implements binary/hex representation of lineto and moveto commands. +% The command consists of a 1byte opcode to switch between lineto and moveto and the size +% of the following delta-x and delta-y values. The opcode is read with /rcmd, the two +% coordinates are read with /rhex. The whole command is executed with /xcmd +% +% + +/pathdict dup 8 dict def load +begin + + % the command is of the bit format cxxyy + % with c=0 meaning lineto + % c=1 meaning moveto + % xx is a 2bit value for the number of bytes for x position + % yy is the same for y, values are off by one: 00 means 1; 11 means 4 ! + % the command has been added to 'A' to be always in the ascii character + % range. the command is followed by 2*xx + 2*yy hexchars. + % '~' denotes the special case of EOD + /rcmd { + { + currentfile 1 string readstring % s bool + pop % s + 0 get % s[0] + % --- check wether s[0] is CR, LF ... + dup 32 gt % s > ' ' ? then read on + { exit } + { pop } + ifelse + } + loop + + dup 126 eq { pop exit } if % -- Exit loop if cmd is '~' + 65 sub % cmd=s[0]-'A' + % -- Separate yy bits + dup 16#3 and 1 add % cmd yy + % -- Separate xx bits + exch % yy cmd + dup 16#C and -2 bitshift + 16#3 and 1 add exch % yy xx cmd + % -- Separate command bit + 16#10 and 16#10 eq % yy xx bool + 3 1 roll exch % bool xx yy + } def + + % length rhex -- reads a signed hex value of given length + % the left most bit of char 0 is considered as the sign (0 means '+', 1 means '-') + % the rest of the bits is considered to be the abs value. Please note that this + % does not match the C binary representation of integers + /rhex { + dup 1 sub exch % l-1 l + currentfile exch string readhexstring % l-1 substring[l] bool + pop + dup 0 get dup % l-1 s s[0] s[0] + % -- Extract the sign + 16#80 and 16#80 eq dup % l-1 s s[0] sign=- sign=- + % -- Mask out the sign bit and put value back + 3 1 roll % l-1 s sign=- s[0] sign=- + { 16#7f and } if % l-1 s sign=- +s[0] + 2 index 0 % l-1 s sign=- +s[0] s 0 + 3 -1 roll put % l-1 s sign=- s 0 +s[0] + % -- Read loop: add to prev sum, mul with 256 + 3 1 roll 0 % sign=- l-1 s Sum=0 + 0 1 5 -1 roll % sign=- s Sum=0 0 1 l-1 + { % sign=- s Sum idx + 2 index exch % sign=- s Sum s idx + get % sign=- s Sum s[idx] + add 256 mul % sign=- s Sum=(s[idx]+Sum)*256 + } + for + % -- mul was once too often, weave in the sign + 256 div % sign=- s Sum/256 + exch pop % sign=- Sum/256 + exch { neg } if % (sign=- ? -Sum : Sum) + } def + + % execute a single command, the former x and y position is already on stack + % only offsets are read from cmdstring + /xcmd { % x y + rcmd % x y bool wx wy + exch rhex % x y bool wy Dx + exch rhex % x y bool Dx Dy + exch 5 -1 roll % y bool Dy Dx x + add exch % y bool X Dy + 4 -1 roll add % bool X Y + 1 index 1 index % bool X Y X Y + 5 -1 roll % X Y X Y bool + { moveto } + { lineto } + ifelse % X Y + } def +end + +/readpath +{ + 0 0 % push initial-x initial-y + pathdict begin + { xcmd } loop + end + pop pop % pop final-x final-y +} def + +% +% +% if languagelevel is not in the systemdict then its level 1 interpreter: +% provide compatibility routines +% +% + +systemdict /languagelevel known not +{ + % string numarray xxshow - + % does only work for single byte fonts + /xshow { + exch dup % a s s + length 0 1 % a s l(s) 1 1 + 3 -1 roll 1 sub % a s 0 1 l(s)-1 + { % a s idx + dup % a s idx idx + % -- extract the delta offset + 3 index exch get % a s idx a[idx] + % -- extract the character + exch % a s a[idx] idx + 2 index exch get % a s a[idx] s[idx] + % -- create a tmp string for show + 1 string dup 0 % a s a[idx] s[idx] s1 s1 0 + 4 -1 roll % a s a[idx] s1 s1 0 s[idx] + put % a s a[idx] s1 + % -- store the current point + currentpoint 3 -1 roll % a s a[idx] x y s1 + % -- draw the character + show % a s a[idx] x y + % -- move to the offset + moveto 0 rmoveto % a s + } + for + pop pop % - + } def + + % x y width height rectfill + % x y width height rectshow + % in contrast to the languagelevel 2 operator + % they use and change the currentpath + /rectangle { + 4 -2 roll % width height x y + moveto % width height + 1 index 0 rlineto % width height % rmoveto(width, 0) + 0 exch rlineto % width % rmoveto(0, height) + neg 0 rlineto % - % rmoveto(-width, 0) + closepath + } def + + /rectfill { rectangle fill } def + /rectstroke { rectangle stroke } def +} +if + +% -- small test program +% 75 75 moveto /Times-Roman findfont 12 scalefont setfont +% <292a2b2c2d2e2f30313233343536373839> +% [5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 5] xshow <21>[0] xshow +% showpage + +% +% +% shortcuts for image header with compression +% +% + +/psp_lzwfilter { + currentfile /ASCII85Decode filter /LZWDecode filter +} def +/psp_ascii85filter { + currentfile /ASCII85Decode filter +} def +/psp_lzwstring { + psp_lzwfilter 1024 string readstring +} def +/psp_ascii85string { + psp_ascii85filter 1024 string readstring +} def +/psp_imagedict { + /psp_bitspercomponent { + 3 eq + { 1 } + { 8 } + ifelse + } def + /psp_decodearray { + [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get + } def + + 7 dict dup + /ImageType 1 put dup + /Width 7 -1 roll put dup + /Height 5 index put dup + /BitsPerComponent 4 index + psp_bitspercomponent put dup + /Decode 5 -1 roll + psp_decodearray put dup + /ImageMatrix [1 0 0 1 0 0] dup + 5 8 -1 roll put put dup + /DataSource 4 -1 roll + 1 eq + { psp_lzwfilter } + { psp_ascii85filter } + ifelse put +} def + + +% +% +% font encoding and reencoding +% +% + +/ISO1252Encoding [ + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle + /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash + /zero /one /two /three /four /five /six /seven + /eight /nine /colon /semicolon /less /equal /greater /question + /at /A /B /C /D /E /F /G + /H /I /J /K /L /M /N /O + /P /Q /R /S /T /U /V /W + /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore + /grave /a /b /c /d /e /f /g + /h /i /j /k /l /m /n /o + /p /q /r /s /t /u /v /w + /x /y /z /braceleft /bar /braceright /asciitilde /unused + /Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl + /circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused + /unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash + /tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis + /space /exclamdown /cent /sterling /currency /yen /brokenbar /section + /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron + /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered + /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown + /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla + /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis + /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply + /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls + /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla + /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis + /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide + /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis +] def + +% /fontname /encoding psp_findfont +/psp_findfont { + exch dup % encoding fontname fontname + findfont % encoding fontname + dup length dict + begin + { + 1 index /FID ne + { def } + { pop pop } + ifelse + } forall + /Encoding 3 -1 roll def + currentdict + end + /psp_reencodedfont exch definefont +} def + +% bshow shows a text in artificial bold +% this is achieved by first showing the text +% then stroking its outline over it with +% the linewidth set to the second parameter +% usage: (string) num bshow + +/bshow { + currentlinewidth % save current linewidth + 3 1 roll % move it to the last stack position + currentpoint % save the current point + 3 index % copy the string to show + show % show it + moveto % move to the original coordinates again + setlinewidth % set the linewidth + false charpath % create the outline path of the shown string + stroke % and stroke it + setlinewidth % reset the stored linewidth +} def + +% bxshow shows a text with a delta array in artificial bold +% that is it does what bshow does for show +% usage: (string) [deltaarray] num bxshow + +/bxshow { + currentlinewidth % save linewidth + 4 1 roll % move it to the last stack position + setlinewidth % set the new linewidth + exch % exchange string and delta array + dup + length % get length of string + 1 sub % prepare parameters for {} for + 0 1 + 3 -1 roll + { + 1 string % create a string object length 1 + 2 index % get the text + 2 index % get charpos (for index variable) + get % have char value at charpos + 1 index % prepare string for put + exch + 0 + exch + put % put into string of length 1 + dup % duplicate the it + currentpoint % save current position + 3 -1 roll % prepare show + show % show the character + moveto % move back to beginning + currentpoint % save current position + 3 -1 roll % prepare outline path of character + false charpath + stroke % stroke it + moveto % move back + % now move to next point + 2 index % get advance array + exch % get charpos + get % get advance element + 0 rmoveto % advance current position + } for + pop pop % remove string and delta array + setlinewidth % restore linewidth +} def diff --git a/vcl/unx/source/printergfx/psputil.cxx b/vcl/unx/source/printergfx/psputil.cxx new file mode 100644 index 000000000000..0b92f4ee423d --- /dev/null +++ b/vcl/unx/source/printergfx/psputil.cxx @@ -0,0 +1,271 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: psputil.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> + +#include "psputil.hxx" + +#include "tools/debug.hxx" + +namespace psp { + +/* + * string convenience routines + */ + +sal_Int32 +getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + const static sal_Char pHex [0x10] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + pBuffer[0] = pHex [(nValue & 0xF0) >> 4]; + pBuffer[1] = pHex [(nValue & 0x0F) ]; + + return 2; +} + +sal_Int32 +getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + // get sign + sal_Bool bNegative = nValue < 0; + nValue = bNegative ? -nValue : nValue; + + // get required buffer size, must be a multiple of two + sal_Int32 nPrecision; + if (nValue < 0x80) + nPrecision = 2; + else + if (nValue < 0x8000) + nPrecision = 4; + else + if (nValue < 0x800000) + nPrecision = 6; + else + nPrecision = 8; + + // convert the int into its hex representation, write it into the buffer + sal_Int32 nRet = nPrecision; + while (nPrecision) + { + nPrecision -= getHexValueOf (nValue % 256, pBuffer + nPrecision - 2 ); + nValue /= 256; + } + + // set sign bit + if (bNegative) + { + switch (pBuffer[0]) + { + case '0' : pBuffer[0] = '8'; break; + case '1' : pBuffer[0] = '9'; break; + case '2' : pBuffer[0] = 'A'; break; + case '3' : pBuffer[0] = 'B'; break; + case '4' : pBuffer[0] = 'C'; break; + case '5' : pBuffer[0] = 'D'; break; + case '6' : pBuffer[0] = 'E'; break; + case '7' : pBuffer[0] = 'F'; break; + default: DBG_ERROR("Already a signed value"); + } + } + + // report precision + return nRet; +} + + +sal_Int32 +getValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + sal_Int32 nChar = 0; + if (nValue < 0) + { + pBuffer [nChar++] = '-'; + nValue *= -1; + } + else + if (nValue == 0) + { + pBuffer [nChar++] = '0'; + return nChar; + } + + sal_Char pInvBuffer [32]; + sal_Int32 nInvChar = 0; + while (nValue > 0) + { + pInvBuffer [nInvChar++] = '0' + nValue % 10; + nValue /= 10; + } + while (nInvChar > 0) + { + pBuffer [nChar++] = pInvBuffer [--nInvChar]; + } + + return nChar; +} + +sal_Int32 +appendStr (const sal_Char* pSrc, sal_Char* pDst) +{ + sal_Int32 nBytes = strlen (pSrc); + strncpy (pDst, pSrc, nBytes + 1); + + return nBytes; +} + +sal_Int32 +appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes) +{ + strncpy (pDst, pSrc, nBytes); + pDst [nBytes] = '\0'; + return nBytes; +} + +/* + * copy strings to file + */ + +sal_Bool +WritePS (osl::File* pFile, const sal_Char* pString) +{ + sal_uInt64 nInLength = rtl_str_getLength (pString); + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (pString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength) +{ + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (pString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const rtl::OString &rString) +{ + sal_uInt64 nInLength = rString.getLength(); + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (rString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const rtl::OUString &rString) +{ + return WritePS (pFile, rtl::OUStringToOString(rString, RTL_TEXTENCODING_ASCII_US)); +} + +/* + * cache converter for use in postscript drawing routines + */ + +ConverterFactory::ConverterFactory() +{ +} + +ConverterFactory::~ConverterFactory () +{ + for( std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = m_aConverters.begin(); it != m_aConverters.end(); ++it ) + rtl_destroyUnicodeToTextConverter (it->second); +} + +rtl_UnicodeToTextConverter +ConverterFactory::Get (rtl_TextEncoding nEncoding) +{ + if (rtl_isOctetTextEncoding( nEncoding )) + { + std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = + m_aConverters.find( nEncoding ); + rtl_UnicodeToTextConverter aConverter; + if (it == m_aConverters.end()) + { + aConverter = rtl_createUnicodeToTextConverter (nEncoding); + m_aConverters[nEncoding] = aConverter; + } + else + aConverter = it->second; + return aConverter; + } + return NULL; +} + +// wrapper for rtl_convertUnicodeToText that handles the usual cases for +// textconversion in drawtext +sal_Size +ConverterFactory::Convert (const sal_Unicode *pText, int nTextLen, + sal_uChar *pBuffer, sal_Size nBufferSize, rtl_TextEncoding nEncoding) +{ + const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK + | RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK ; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + + rtl_UnicodeToTextConverter aConverter = Get (nEncoding); + rtl_UnicodeToTextContext aContext = rtl_createUnicodeToTextContext (aConverter); + + sal_Size nSize = rtl_convertUnicodeToText (aConverter, aContext, + pText, nTextLen, (sal_Char*)pBuffer, nBufferSize, + nCvtFlags, &nCvtInfo, &nCvtChars); + + rtl_destroyUnicodeToTextContext (aConverter, aContext); + + return nSize; +} + +ConverterFactory* +GetConverterFactory () +{ + static ConverterFactory* pCvt = NULL; + + if (pCvt == NULL) + pCvt = new ConverterFactory; + + return pCvt; +} + + +} /* namespace psp */ diff --git a/vcl/unx/source/printergfx/psputil.hxx b/vcl/unx/source/printergfx/psputil.hxx new file mode 100644 index 000000000000..b3227962e8a0 --- /dev/null +++ b/vcl/unx/source/printergfx/psputil.hxx @@ -0,0 +1,81 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: psputil.hxx,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _PSPRINT_PRINTERUTIL_HXX_ +#define _PSPRINT_PRINTERUTIL_HXX_ + +#include "osl/file.hxx" + +#include "rtl/ustring.hxx" +#include "rtl/string.hxx" +#include "rtl/tencinfo.h" +#include "rtl/textcvt.h" + +#include <map> + +namespace psp { + +/* + * string convenience routines + * sizeof(pBuffer) must be at least 2 Bytes, 0x00 <= nValue <= 0xFF, + * effective buffer of get*ValueOf() is NOT NULL-terminated + */ +sal_Int32 getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 getValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst); +sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes); + +sal_Bool WritePS (osl::File* pFile, const sal_Char* pString); +sal_Bool WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength); +sal_Bool WritePS (osl::File* pFile, const rtl::OString &rString); +sal_Bool WritePS (osl::File* pFile, const rtl::OUString &rString); + +class ConverterFactory +{ + +public: + ConverterFactory(); + ~ConverterFactory(); + rtl_UnicodeToTextConverter Get (rtl_TextEncoding nEncoding); + sal_Size Convert (const sal_Unicode *pText, int nTextLen, + sal_uChar *pBuffer, sal_Size nBufferSize, + rtl_TextEncoding nEncoding); +private: + + std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter > m_aConverters; +}; + +ConverterFactory* GetConverterFactory (); + +} /* namespace psp */ + +#endif /* _PSPRINT_PRINTERUTIL_HXX_ */ + diff --git a/vcl/unx/source/printergfx/text_gfx.cxx b/vcl/unx/source/printergfx/text_gfx.cxx new file mode 100644 index 000000000000..e9c173682f87 --- /dev/null +++ b/vcl/unx/source/printergfx/text_gfx.cxx @@ -0,0 +1,865 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: text_gfx.cxx,v $ + * $Revision: 1.31 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <math.h> + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "vcl/printergfx.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/helper.hxx" + +#include "osl/thread.h" + +#include "sal/alloca.h" + +using namespace psp ; + +namespace psp { +/* + container for a font and its helper fonts: + 1st font is the font substitute e.g. helvetica substitutes arial on the printer + 2nd is the font itself + 3rd is a fallback font, usually a font with unicode glyph repertoir (e.g. andale) + symbol fonts (adobe-fontspecific) may need special glyphmapping + (symbol page vc. latin page) +*/ +class Font3 +{ + private: + + #define Font3Size 3 + + fontID mpFont [Font3Size]; + bool mbSymbol; + + public: + + fontID GetFont (int nIdx) const + { return nIdx < Font3Size ? mpFont[nIdx] : -1 ; } + bool IsSymbolFont () const + { return mbSymbol; } + + Font3 (const PrinterGfx &rGfx); + ~Font3 () {} +}; + +Font3::Font3(const PrinterGfx &rGfx) +{ + mpFont[0] = rGfx.getFontSubstitute(); + mpFont[1] = rGfx.GetFontID(); + mpFont[2] = rGfx.getFallbackID(); + // mpFont[2] = rGfx.GetFontID(); + + PrintFontManager &rMgr = PrintFontManager::get(); + mbSymbol = mpFont[1] != -1 ? + rMgr.getFontEncoding(mpFont[1]) == RTL_TEXTENCODING_SYMBOL : false; +} + +} // namespace psp + +static int getVerticalDeltaAngle( sal_Unicode nChar ) +{ + int nAngle = 0; + if( ( nChar >= 0x1100 && nChar < 0x11fa ) || + ( nChar >= 0x3000 && nChar < 0xfb00 ) || + ( nChar >= 0xfe20 && nChar < 0xfe70 ) || + ( nChar >= 0xff00 && nChar < 0xff64 ) + ) + { + /* #i52932# remember: + nChar == 0x2010 || nChar == 0x2015 + nChar == 0x2016 || nChar == 0x2026 + + are nAngle = 0 also, but already handled in the first if + */ + if( ( nChar >= 0x3008 && nChar < 0x3019 && nChar != 0x3012 ) || + nChar == 0xff3b || nChar == 0xff3d || + (nChar >= 0xff6b && nChar < 0xff64 ) || + nChar == 0xffe3 + ) + nAngle = 0; + else if( nChar == 0x30fc ) + nAngle = -900; + else + nAngle = 900; + } + return nAngle; +} + +void +PrinterGfx::PSUploadPS1Font (sal_Int32 nFontID) +{ + std::list< sal_Int32 >::iterator aFont; + // already in the document header ? + for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont ) + if( nFontID == *aFont ) + return; + + // no occurrenc yet, mark for download + // add the fontid to the list + maPS1Font.push_back (nFontID); +} + +/* + * implement text handling printer routines, + */ + +sal_uInt16 +PrinterGfx::SetFont( + sal_Int32 nFontID, + sal_Int32 nHeight, + sal_Int32 nWidth, + sal_Int32 nAngle, + bool bVertical, + bool bArtItalic, + bool bArtBold + ) +{ + // font and encoding will be set by drawText again immediately + // before PSShowText + mnFontID = nFontID; + maVirtualStatus.maFont = rtl::OString(); + maVirtualStatus.maEncoding = RTL_TEXTENCODING_DONTKNOW; + maVirtualStatus.mnTextHeight = nHeight; + maVirtualStatus.mnTextWidth = nWidth; + maVirtualStatus.mbArtItalic = bArtItalic; + maVirtualStatus.mbArtBold = bArtBold; + mnTextAngle = nAngle; + mbTextVertical = bVertical; + + return 0; +} + +sal_uInt16 +PrinterGfx::SetFallbackFont ( sal_Int32 nFontID ) +{ + mnFallbackID = nFontID; + return 0; +} + +void PrinterGfx::drawGlyphs( + const Point& rPoint, + sal_uInt32* pGlyphIds, + sal_Unicode* pUnicodes, + sal_Int16 nLen, + sal_Int32* pDeltaArray + ) +{ + + // draw the string + // search for a glyph set matching the set font + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); aIter++) + if ( ((*aIter).GetFontID() == mnFontID) + && ((*aIter).IsVertical() == mbTextVertical)) + { + (*aIter).DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray); + break; + } + + // not found ? create a new one + if (aIter == maPS3Font.end()) + { + maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical)); + maPS3Font.back().DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray); + } +} + +void PrinterGfx::DrawGlyphs( + const Point& rPoint, + sal_uInt32* pGlyphIds, + sal_Unicode* pUnicodes, + sal_Int16 nLen, + sal_Int32* pDeltaArray + ) +{ + if( nLen <= 0 ) + return; + + if ( !mrFontMgr.isFontDownloadingAllowed( mnFontID ) ) + { + LicenseWarning(rPoint, pUnicodes, nLen, pDeltaArray); + return; + } + + if( mrFontMgr.getFontType( mnFontID ) != fonttype::TrueType ) + { + DrawText( rPoint, pUnicodes, nLen, pDeltaArray ); + return; + } + + // move and rotate the user coordinate system + // avoid the gsave/grestore for the simple cases since it allows + // reuse of the current font if it hasn't changed + sal_Int32 nCurrentTextAngle = mnTextAngle; + Point aPoint( rPoint ); + + if (nCurrentTextAngle != 0) + { + PSGSave (); + PSTranslate (rPoint); + PSRotate (nCurrentTextAngle); + mnTextAngle = 0; + aPoint = Point( 0, 0 ); + } + + if( mbTextVertical ) + { + // vertical glyphs can have an additional rotation ... sigh. + // so break up text in chunks of normal glyphs and print out + // specially rotated glyphs extra + sal_uInt32* pTempGlyphIds = (sal_uInt32*)alloca(sizeof(sal_Int32)*nLen); + sal_Int32* pTempDelta = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen); + sal_Unicode* pTempUnicodes = (sal_Unicode*)alloca(sizeof(sal_Unicode)*nLen); + sal_Int16 nTempLen = 0; + sal_Int32 nTempFirstDelta = 0; + Point aRotPoint; + sal_Int32 nTextHeight = maVirtualStatus.mnTextHeight; + sal_Int32 nTextWidth = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + sal_Int32 nAscend = mrFontMgr.getFontAscend( mnFontID ); + sal_Int32 nDescend = mrFontMgr.getFontDescend( mnFontID ); + + nDescend = nDescend * nTextHeight / 1000; + nAscend = nAscend * nTextHeight / 1000; + + for( sal_Int16 i = 0; i < nLen; i++ ) + { + sal_Int32 nRot = ((pGlyphIds[i] >> 24) & 3); + if( nRot == 0 ) + { + pTempUnicodes[nTempLen] = pUnicodes[i]; + pTempGlyphIds[nTempLen] = pGlyphIds[i]; + if( nTempLen > 0 ) + pTempDelta[nTempLen-1] = pDeltaArray[i-1]-nTempFirstDelta; + else + { + // the first element in pDeltaArray shows + // the offset of the second character + // so if the first glyph is normal + // then we do not need to move the delta indices + // else we have to move them down by one and + // recalculate aPoint and all deltas + if( i != 0 ) + nTempFirstDelta = pDeltaArray[ i-1 ]; + } + nTempLen++; + } + else + { + sal_Int32 nOffset = i > 0 ? pDeltaArray[i-1] : 0; + sal_Int32 nRotAngle = 0; + switch( nRot ) + { + case 3: + nRotAngle = 2700; + aRotPoint = Point( -nAscend*nTextWidth/nTextHeight, -nDescend*nTextWidth/nTextHeight - nOffset ); + break; + case 2: + nRotAngle = 1800; + aRotPoint = Point( -nOffset, (nAscend+nDescend) ); + break; + case 1: + nRotAngle = 900; + aRotPoint = Point( -nDescend*nTextWidth/nTextHeight, nOffset + nAscend*nTextWidth/nTextHeight ); + break; + } + sal_uInt32 nRotGlyphId = pGlyphIds[i]; + sal_Unicode nRotUnicode = pUnicodes[i]; + sal_Int32 nRotDelta = 0; + + // transform matrix to new individual direction + PSGSave (); + GraphicsStatus aSaveStatus = maVirtualStatus; + if( nRot != 2 ) // switch font aspect + { + maVirtualStatus.mnTextWidth = nTextHeight; + maVirtualStatus.mnTextHeight = nTextWidth; + } + if( aPoint.X() || aPoint.Y() ) + PSTranslate( aPoint ); + PSRotate (nRotAngle); + // draw the rotated glyph + drawGlyphs( aRotPoint, &nRotGlyphId, &nRotUnicode, 1, &nRotDelta ); + + // restore previous state + maVirtualStatus = aSaveStatus; + PSGRestore(); + } + } + + pGlyphIds = pTempGlyphIds; + pUnicodes = pTempUnicodes; + pDeltaArray = pTempDelta; + nLen = nTempLen; + + aPoint.X() += nTempFirstDelta; + } + + if( nLen > 0 ) + drawGlyphs( aPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray ); + + // restore the user coordinate system + if (nCurrentTextAngle != 0) + { + PSGRestore (); + mnTextAngle = nCurrentTextAngle; + } +} + +void +PrinterGfx::DrawText ( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + fontID nRestoreFont = mnFontID; + + // setup font[substitutes] and map the string into the symbol area in case of + // symbol font + Font3 aFont(*this); + sal_Unicode *pEffectiveStr; + if ( aFont.IsSymbolFont() ) + { + pEffectiveStr = (sal_Unicode*)alloca(nLen * sizeof(pStr[0])); + for (int i = 0; i < nLen; i++) + pEffectiveStr[i] = pStr[i] < 256 ? pStr[i] + 0xF000 : pStr[i]; + } + else + { + pEffectiveStr = const_cast<sal_Unicode*>(pStr); + } + + fontID *pFontMap = (fontID*) alloca(nLen * sizeof(fontID)); + sal_Int32 *pCharWidth = (sal_Int32*) alloca(nLen * sizeof(sal_Int32)); + + for( int n = 0; n < nLen; n++ ) + { + CharacterMetric aBBox; + pFontMap[n] = getCharMetric (aFont, pEffectiveStr[n], &aBBox); + pCharWidth[n] = getCharWidth (mbTextVertical, pEffectiveStr[n], &aBBox); + } + + // setup a new delta array, use virtual resolution of 1000 + sal_Int32* pNewDeltaArray = (sal_Int32*)alloca( sizeof( sal_Int32 )*nLen ); + if ( pDeltaArray != 0) + { + for (int i = 0; i < nLen - 1; i++) + pNewDeltaArray[i] = 1000 * pDeltaArray[i]; + pNewDeltaArray[nLen - 1] = 0; + } + else + { + pNewDeltaArray[0] = pCharWidth[0]; + for (int i = 1; i < nLen; i++) + pNewDeltaArray[i] = pNewDeltaArray[i-1] + pCharWidth[i]; + } + + // move and rotate the user coordinate system + // avoid the gsave/grestore for the simple cases since it allows + // reuse of the current font if it hasn't changed + sal_Int32 nCurrentTextAngle = mnTextAngle; + sal_Int32 nCurrentPointX; + sal_Int32 nCurrentPointY; + + if (nCurrentTextAngle != 0) + { + PSGSave (); + PSTranslate (rPoint); + PSRotate (nCurrentTextAngle); + mnTextAngle = 0; + + nCurrentPointX = 0; + nCurrentPointY = 0; + } + else + { + nCurrentPointX = rPoint.X(); + nCurrentPointY = rPoint.Y(); + } + + // draw the string + sal_Int32 nDelta = 0; + for (int nTo = 0; nTo < nLen; ) + { + int nFrom = nTo; + fontID nFont = pFontMap[ nFrom ]; + + while ((nTo < nLen) && (nFont == pFontMap[nTo])) + { + pNewDeltaArray[ nTo ] = (sal_Int32)(((0.5 + pNewDeltaArray[ nTo ]) / 1000.0) - nDelta); + nTo++ ; + } + + SetFont( nFont, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + mnTextAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold + ); + + if (mbTextVertical) + { + drawVerticalizedText( + Point(nCurrentPointX + nDelta, nCurrentPointY), + pEffectiveStr + nFrom, nTo - nFrom, + pNewDeltaArray + nFrom ); + } + else + { + drawText( + Point(nCurrentPointX + nDelta, nCurrentPointY), + pEffectiveStr + nFrom, nTo - nFrom, + pDeltaArray == NULL ? NULL : pNewDeltaArray + nFrom ); + } + nDelta += pNewDeltaArray[ nTo - 1 ]; + } + + // restore the user coordinate system + if (nCurrentTextAngle != 0) + { + PSGRestore (); + mnTextAngle = nCurrentTextAngle; + } + + // restore the original font settings + SetFont( nRestoreFont, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + mnTextAngle, mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold + ); +} + +void PrinterGfx::drawVerticalizedText( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + sal_Int32* pDelta = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) ); + + int nTextScale = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + int nNormalAngle = mnTextAngle; + int nDeltaAngle, nLastPos = 0; + + double fSin = sin( -2.0*M_PI*nNormalAngle/3600 ); + double fCos = cos( -2.0*M_PI*nNormalAngle/3600 ); + + PrintFontManager &rMgr = PrintFontManager::get(); + PrintFontInfo aInfo; + rMgr.getFontInfo( mnFontID, aInfo ); + + bool* pGsubFlags = (bool*)alloca( nLen * sizeof(bool) ); + rMgr.hasVerticalSubstitutions( mnFontID, pStr, nLen, pGsubFlags ); + + Point aPoint( rPoint ); + for( int i = 0; i < nLen; ) + { + while( ( nDeltaAngle = getVerticalDeltaAngle( pStr[i] ) ) == 0 && i < nLen ) + i++; + if( i <= nLen && i > nLastPos ) + { + for( int n = nLastPos; n < i; n++ ) + pDelta[n] = pDeltaArray[n] - (aPoint.X() - rPoint.X() ); + + SetFont( mnFontID, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + nNormalAngle, mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + drawText( aPoint, pStr + nLastPos, i - nLastPos, pDelta + nLastPos ); + + aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i-1] * fCos)); + aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i-1] * fSin)); + } + if( i < nLen ) + { + int nOldWidth = maVirtualStatus.mnTextWidth; + int nOldHeight = maVirtualStatus.mnTextHeight; + SetFont( mnFontID, + nTextScale, + maVirtualStatus.mnTextHeight, + nNormalAngle + nDeltaAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + + double nA = nTextScale * aInfo.m_nAscend / 1000.0; + double nD = nTextScale * aInfo.m_nDescend / 1000.0; + double fStretch = (double)maVirtualStatus.mnTextWidth / maVirtualStatus.mnTextHeight; + if( !pGsubFlags[i] ) + nD *= fStretch; + + Point aPos( aPoint ); + switch( nDeltaAngle ) + { + case +900: + aPos.X() += (sal_Int32)(+nA * fCos + nD * fSin); + aPos.Y() += (sal_Int32)(-nA * fSin + nD * fCos); + break; + case -900: + aPos.X() += (sal_Int32)(+nA * fSin + nD * fCos); + aPos.Y() += (sal_Int32)(-(nTextScale*fStretch - nD) * fCos); + break; + } + drawText( aPos, pStr+i, 1, NULL ); + if( i < nLen-1 && pDeltaArray ) + { + aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i] * fCos)); + aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i] * fSin)); + } + + // swap text width/height again + SetFont( mnFontID, + nOldHeight, + nOldWidth, + nNormalAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + } + i++; + nLastPos = i; + } + mnTextAngle = nNormalAngle; +} + +void +PrinterGfx::LicenseWarning(const Point& rPoint, const sal_Unicode* pStr, + sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + // treat it like a builtin font in case a user has that font also in the + // printer. This is not so unlikely as it may seem; no print embedding + // licensed fonts are often used (or so they say) in companies: + // they are installed on displays and printers, but get not embedded in + // they are installed on displays and printers, but get not embedded in + // print files or documents because they are not licensed for use outside + // the company. + rtl::OString aMessage( "The font " ); + aMessage += rtl::OUStringToOString( mrFontMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US ); + aMessage += " could not be downloaded\nbecause its license does not allow for that"; + PSComment( aMessage.getStr() ); + + rtl::OString aFontName = rtl::OUStringToOString( + mrFontMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + PSSetFont (aFontName, RTL_TEXTENCODING_ISO_8859_1); + + sal_Size nSize = 4 * nLen; + sal_uChar* pBuffer = (sal_uChar*)alloca (nSize* sizeof(sal_uChar)); + + ConverterFactory* pCvt = GetConverterFactory (); + nSize = pCvt->Convert (pStr, nLen, pBuffer, nSize, RTL_TEXTENCODING_ISO_8859_1); + + PSMoveTo (rPoint); + PSShowText (pBuffer, nLen, nSize, pDeltaArray); +} + +void +PrinterGfx::drawText( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + if (!(nLen > 0)) + return; + + fonttype::type eType = mrFontMgr.getFontType (mnFontID); + + if (eType == fonttype::Type1) + PSUploadPS1Font (mnFontID); + + if ( eType == fonttype::TrueType + && !mrFontMgr.isFontDownloadingAllowed(mnFontID)) + { + LicenseWarning(rPoint, pStr, nLen, pDeltaArray); + return; + } + + if( mrFontMgr.getUseOnlyFontEncoding( mnFontID ) ) + { + GlyphSet aGSet( mnFontID, mbTextVertical ); + aGSet.DrawText( *this, rPoint, pStr, nLen, pDeltaArray ); + return; + } + + // search for a glyph set matching the set font + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); aIter++) + if ( ((*aIter).GetFontID() == mnFontID) + && ((*aIter).IsVertical() == mbTextVertical)) + { + (*aIter).DrawText (*this, rPoint, pStr, nLen, pDeltaArray); + break; + } + + // not found ? create a new one + if (aIter == maPS3Font.end()) + { + maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical)); + maPS3Font.back().DrawText (*this, rPoint, pStr, nLen, pDeltaArray); + } +} + +int +PrinterGfx::getCharWidth (sal_Bool b_vert, sal_Unicode n_char, CharacterMetric *p_bbox) +{ + b_vert = b_vert && (getVerticalDeltaAngle(n_char) != 0); + int w = b_vert ? p_bbox->height : p_bbox->width; + w *= maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + return w; +} + +fontID +PrinterGfx::getCharMetric (const Font3 &rFont, sal_Unicode n_char, CharacterMetric *p_bbox) +{ + p_bbox->width = -1; + p_bbox->height = -1; + + for (fontID n = 0; n < 3; n++) + { + fontID n_font = rFont.GetFont(n); + if (n_font != -1) + { + if( mbStrictSO52Compatibility ) + { + fonttype::type eType = mrFontMgr.getFontType( n_font ); + if( (eType == fonttype::Builtin || eType == fonttype::Type1) ) + { + // note: any character exchanged here MUST also be changed + // in the compatibility ISO encoding vector in the prolog + // in printerjob.cxx + sal_Unicode aRepl = 0; + if( n_char == 0x2d ) + aRepl = 0x2212; + else if( n_char == 0x27 ) + aRepl = 0x2019; + /* + additional characters that may need backwards compatibility: + ISO5589 StdEnc Unicode suggested n_char -> aRepl + 0264 0302 0x00B4 0x00B4 (acute) -> 0x2019 (quiteright) + 0246 - 0x00A6 0x00A6 (brokenbar) -> 0x007C (bar) + 0225 0267 0x0095 0x0095 () -> 0x2022 (bullet) + 0140 0301 0x0060 0x0060 (grave) -> ? + */ + if( aRepl ) + { + mrFontMgr.getMetrics( n_font, aRepl, aRepl, p_bbox ); + if (p_bbox->width >= 0 && p_bbox->height >= 0) + return n_font; + } + } + } + mrFontMgr.getMetrics( n_font, n_char, n_char, p_bbox ); + } + if (p_bbox->width >= 0 && p_bbox->height >= 0) + return n_font; + } + if (n_char != '?') + return getCharMetric (rFont, '?', p_bbox); + + return rFont.GetFont(0) != -1 ? rFont.GetFont(0) : rFont.GetFont(1); +} + +fontID +PrinterGfx::getFontSubstitute () const +{ + if( mpFontSubstitutes ) + { + ::std::hash_map< fontID, fontID >::const_iterator it = + mpFontSubstitutes->find( mnFontID ); + if( it != mpFontSubstitutes->end() ) + return it->second; + } + + return -1; +} + +sal_Int32 +PrinterGfx::GetCharWidth (sal_Unicode nFrom, sal_Unicode nTo, long *pWidthArray) +{ + Font3 aFont(*this); + if (aFont.IsSymbolFont() && (nFrom < 256) && (nTo < 256)) + { + nFrom += 0xF000; + nTo += 0xF000; + } + + for( int n = 0; n < (nTo - nFrom + 1); n++ ) + { + CharacterMetric aBBox; + getCharMetric (aFont, n + nFrom, &aBBox); + pWidthArray[n] = getCharWidth (mbTextVertical, n + nFrom, &aBBox); + } + + // returned metrics have postscript precision + return 1000; +} + +const ::std::list< KernPair >& PrinterGfx::getKernPairs( bool bVertical ) const +{ + /* + * Note: this is only a 80% solution: if a font is only + * partially substituted in a string due to missing glyphs + * the results may not be perfect; the more so the more the + * substitution differs from the original metricwise. But + * vcl only asks for KernPairs for each font once and NOT + * in a string context this is the best we can do. + * In future the kerning should be done on a per string basis. + */ + fontID nFont = mnFontID; + if( mpFontSubstitutes ) + { + ::std::hash_map< fontID, fontID >::const_iterator it = + mpFontSubstitutes->find( mnFontID ); + if( it != mpFontSubstitutes->end() ) + nFont = it->second; + } + return mrFontMgr.getKernPairs( nFont, bVertical ); +} + +/* + * advanced glyph handling + */ + +sal_Bool +PrinterGfx::GetGlyphBoundRect (sal_Unicode /*c*/, Rectangle& /*rOutRect*/) +{ + return 0; +} + +sal_uInt32 +PrinterGfx::GetGlyphOutline (sal_Unicode /*c*/, + sal_uInt16 **/*ppPolySizes*/, Point **/*ppPoints*/, sal_uInt8 **/*ppFlags*/) +{ + return 0; +} + +/* + * spool the converted truetype fonts to the page header after the page body is + * complete + * for Type1 fonts spool additional reencoding vectors that are necessary to access the + * whole font + */ + +void +PrinterGfx::OnEndPage () +{ +} + +void +PrinterGfx::OnEndJob () +{ + maPS3Font.clear(); + maPS1Font.clear(); +} + +void +PrinterGfx::writeResources( osl::File* pFile, std::list< rtl::OString >& rSuppliedFonts, std::list< rtl::OString >& rNeededFonts ) +{ + // write all type 1 fonts + std::list< sal_Int32 >::iterator aFont; + // already in the document header ? + for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont) + { + const rtl::OString& rSysPath (mrFontMgr.getFontFileSysPath(*aFont) ); + rtl::OUString aUNCPath; + osl::File::getFileURLFromSystemPath (OStringToOUString (rSysPath, osl_getThreadTextEncoding()), aUNCPath); + osl::File aFontFile (aUNCPath); + + // provide the pfb or pfa font as a (pfa-)font resource + rtl::OString aPostScriptName = + rtl::OUStringToOString ( mrFontMgr.getPSName(*aFont), + RTL_TEXTENCODING_ASCII_US ); + + WritePS (pFile, "%%BeginResource: font "); + WritePS (pFile, aPostScriptName.getStr()); + WritePS (pFile, "\n"); + + osl::File::RC nError = aFontFile.open (OpenFlag_Read); + if (nError == osl::File::E_None) + { + convertPfbToPfa (aFontFile, *pFile); + aFontFile.close (); + + pFile->setPos(osl_Pos_Current, -1); + char lastchar = '\n'; + sal_uInt64 uBytes(1); + pFile->read((void *)(&lastchar), uBytes, uBytes); + if (lastchar != '\n') + WritePS (pFile, "\n"); + } + WritePS (pFile, "%%EndResource\n"); + rSuppliedFonts.push_back( aPostScriptName ); + } + + // write glyphsets and reencodings + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter) + { + if (aIter->GetFontType() == fonttype::TrueType) + { + aIter->PSUploadFont (*pFile, *this, mbUploadPS42Fonts ? true : false, rSuppliedFonts ); + } + else + // ( aIter->GetFontType() == fonttype::Type1 + // || aIter->GetFontType() == fonttype::Builtin ) + { + aIter->PSUploadEncoding (pFile, *this); + if( aIter->GetFontType() == fonttype::Builtin ) + rNeededFonts.push_back( + rtl::OUStringToOString( + mrFontMgr.getPSName( aIter->GetFontID() ), + RTL_TEXTENCODING_ASCII_US ) ); + } + } +} + +bool PrinterGfx::getStrictSO52Compatibility() const +{ + return mbStrictSO52Compatibility; +} + +void PrinterGfx::setStrictSO52Compatibility( bool bCompat) +{ + mbStrictSO52Compatibility = bCompat; +} diff --git a/vcl/unx/source/window/salframe.cxx b/vcl/unx/source/window/salframe.cxx index aca9f5dd27d0..8f2bccebc443 100644 --- a/vcl/unx/source/window/salframe.cxx +++ b/vcl/unx/source/window/salframe.cxx @@ -35,42 +35,43 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> -#include <sal/alloca.h> -#include <prex.h> +#include "prex.h" #include <X11/Xatom.h> #include <X11/keysym.h> -#include <FWS.hxx> +#include "FWS.hxx" #include <X11/extensions/shape.h> #ifndef SOLARIS #include <X11/extensions/dpms.h> #endif -#include <postx.h> - -#include <salunx.h> -#include <tools/debug.hxx> -#include <saldata.hxx> -#include <saldisp.hxx> -#include <vcl/salinst.hxx> -#include <salgdi.h> -#include <salframe.h> -#ifndef _SV_KEYCOES_HXX -#include <vcl/keycodes.hxx> -#endif -#include <soicon.hxx> -#include <dtint.hxx> -#include <sm.hxx> -#include <vcl/settings.hxx> -#include <wmadaptor.hxx> -#include <psprint/printerinfomanager.hxx> -#include <salprn.h> -#include <vcl/floatwin.hxx> -#include <vcl/sallayout.hxx> -#include <vcl/svapp.hxx> -#include <salbmp.h> -#include <i18n_ic.hxx> -#include <i18n_keysym.hxx> -#include <i18n_status.hxx> +#include "postx.h" + +#include "salunx.h" +#include "saldata.hxx" +#include "saldisp.hxx" +#include "salgdi.h" +#include "salframe.h" +#include "soicon.hxx" +#include "dtint.hxx" +#include "sm.hxx" +#include "wmadaptor.hxx" +#include "salprn.h" +#include "salbmp.h" +#include "i18n_ic.hxx" +#include "i18n_keysym.hxx" +#include "i18n_status.hxx" + +#include "vcl/salinst.hxx" +#include "vcl/floatwin.hxx" +#include "vcl/sallayout.hxx" +#include "vcl/svapp.hxx" +#include "vcl/keycodes.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/settings.hxx" + +#include "tools/debug.hxx" + +#include "sal/alloca.h" #include <algorithm> @@ -2793,11 +2794,7 @@ static USHORT sal_GetCode( int state ) if( state & ShiftMask ) nCode |= KEY_SHIFT; - if( (state & ControlMask ) -#ifdef MACOSX - || (state & Mod2Mask) // map Meta (aka Command key) to Ctrl -#endif - ) + if( state & ControlMask ) nCode |= KEY_MOD1; if( state & Mod1Mask ) nCode |= KEY_MOD2; @@ -3160,27 +3157,13 @@ long X11SalFrame::HandleKeyEvent( XKeyEvent *pEvent ) USHORT nModCode = 0; char aDummy; -#ifdef MACOSX - // map Meta (aka Command key) to Ctrl - if( pEvent->state & Mod2Mask ) - nModCode |= KEY_MOD1; - if( nKeySym == XK_Meta_L ) - nKeySym = XK_Control_L; - else if( nKeySym == XK_Meta_R ) - nKeySym = XK_Control_R; -#endif - if( pEvent->state & ShiftMask ) nModCode |= KEY_SHIFT; if( pEvent->state & ControlMask ) nModCode |= KEY_MOD1; -#ifdef MACOSX - if( pEvent->state & Mod2Mask ) - nModCode |= KEY_MOD3; -#else if( pEvent->state & Mod1Mask ) nModCode |= KEY_MOD2; -#endif + if( nKeySym == XK_Shift_L || nKeySym == XK_Shift_R || nKeySym == XK_Control_L || nKeySym == XK_Control_R || nKeySym == XK_Alt_L || nKeySym == XK_Alt_R @@ -3214,19 +3197,11 @@ long X11SalFrame::HandleKeyEvent( XKeyEvent *pEvent ) break; case XK_Alt_L: nExtModMask = MODKEY_LMOD2; -#ifdef MACOSX - nModMask = KEY_MOD2 | (pEvent->type==KeyRelease ? KEY_MOD3 : 0 ); -#else nModMask = KEY_MOD2; -#endif break; case XK_Alt_R: nExtModMask = MODKEY_RMOD2; -#ifdef MACOSX - nModMask = KEY_MOD2 | (pEvent->type==KeyRelease ? KEY_MOD3 : 0 ); -#else nModMask = KEY_MOD2; -#endif break; case XK_Shift_L: nExtModMask = MODKEY_LSHIFT; @@ -3629,37 +3604,6 @@ long X11SalFrame::HandleSizeEvent( XConfigureEvent *pEvent ) { if( maGeometry.nX != pEvent->x || maGeometry.nY != pEvent->y ) { -#ifdef MACOSX - // #i68019#: Apple X11 doesn't draw offscreen... - // Better would be to test if the X server we are running on is Apple X11, but ... - - Size aScreenSize = GetDisplay()->GetScreenSize( m_nScreen ); - unsigned int nScreenWidth = aScreenSize.Width(); - unsigned int nScreenHeight = aScreenSize.Height(); - - // Repaint the window if it was possible to draw outside of the screen (in theory) - // 1. the window was below the screen and the window was moved up - // 2. the window was above the screen and the window was moved down - // 3. the window was out of the screen on the right side and the window was moved left - // 4. the window's left part was out of the screen and the window was moved right - if ( ( maGeometry.nY+maGeometry.nHeight > nScreenHeight && - pEvent->y < maGeometry.nY ) || - ( maGeometry.nY < 0 && pEvent->y > maGeometry.nY ) || - ( maGeometry.nX+maGeometry.nWidth > nScreenWidth && - pEvent->x < maGeometry.nX ) || - ( maGeometry.nX < 0 && pEvent->x > maGeometry.nX) ) - { - XEvent aEvent; - aEvent.xexpose.type = Expose; - aEvent.xexpose.display = pDisplay_->GetDisplay(); - aEvent.xexpose.x = 0; - aEvent.xexpose.y = 0; - aEvent.xexpose.width = maGeometry.nWidth; - aEvent.xexpose.height = maGeometry.nHeight; - aEvent.xexpose.count = 0; - HandleExposeEvent(&aEvent); - } -#endif maGeometry.nX = pEvent->x; maGeometry.nY = pEvent->y; CallCallback( SALEVENT_MOVE, NULL ); |