diff options
Diffstat (limited to 'vcl/source/gdi/outdev3.cxx')
-rw-r--r-- | vcl/source/gdi/outdev3.cxx | 6326 |
1 files changed, 6326 insertions, 0 deletions
diff --git a/vcl/source/gdi/outdev3.cxx b/vcl/source/gdi/outdev3.cxx new file mode 100644 index 000000000000..4dbd5935f2ae --- /dev/null +++ b/vcl/source/gdi/outdev3.cxx @@ -0,0 +1,6326 @@ +/************************************************************************* + * + * $RCSfile: outdev3.cxx,v $ + * + * $Revision: 1.1.1.1 $ + * + * last change: $Author: hr $ $Date: 2000-09-18 17:05:38 $ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * - Sun Industry Standards Source License Version 1.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Sun Industry Standards Source License Version 1.1 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source License Version 1.1 (the "License"); You may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at http://www.openoffice.org/license.html. + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +#include <math.h> +#include <string.h> + +#define _SV_OUTDEV_CXX + +#ifndef REMOTE_APPSERVER +#ifndef _SV_SVSYS_HXX +#include <svsys.h> +#endif +#endif + +#ifndef REMOTE_APPSERVER +#ifndef _SV_SALGDI_HXX +#include <salgdi.hxx> +#endif +#else +#ifndef _SV_RMOUTDEV_HXX +#include <rmoutdev.hxx> +#endif +#endif + +#ifndef _RTL_TENCINFO_H +#include <rtl/tencinfo.h> +#endif +#ifndef _DEBUG_HXX +#include <tools/debug.hxx> +#endif + +#ifndef _SV_SVDATA_HXX +#include <svdata.hxx> +#endif +#ifndef _SV_METRIC_HXX +#include <metric.hxx> +#endif +#ifndef _SV_METAACT_HXX +#include <metaact.hxx> +#endif +#ifndef _SV_GDIMTF_HXX +#include <gdimtf.hxx> +#endif +#ifndef _SV_OUTDATA_HXX +#include <outdata.hxx> +#endif +#ifndef _SV_OUTFONT_HXX +#include <outfont.hxx> +#endif +#ifndef _SV_POLY_HXX +#include <poly.hxx> +#endif +#ifndef _SV_OUTDEV_H +#include <outdev.h> +#endif +#ifndef _SV_VIRDEV_HXX +#include <virdev.hxx> +#endif +#ifndef _SV_PRINT_HXX +#include <print.hxx> +#endif +#ifndef _SV_WINDOW_H +#include <window.h> +#endif +#ifndef _SV_WINDOW_HXX +#include <window.hxx> +#endif +#ifndef _SV_SVAPP_HXX +#include <svapp.hxx> +#endif +#ifndef _SV_BMPACC_HXX +#include <bmpacc.hxx> +#endif +#ifndef _SV_OUTDEV_HXX +#include <outdev.hxx> +#endif +#ifndef _SV_EDIT_HXX +#include <edit.hxx> +#endif + +#include <unohelp.hxx> + +#ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_ +#include <com/sun/star/text/XBreakIterator.hpp> +#endif + +#ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_ +#include <com/sun/star/text/WordType.hpp> +#endif + +#if defined UNX +#define GLYPH_FONT_HEIGHT 128 +#elif defined OS2 +#define GLYPH_FONT_HEIGHT 176 +#else +#define GLYPH_FONT_HEIGHT 256 +#endif + +#define WSstrcmp strcmp + +// ======================================================================= + +DBG_NAMEEX( OutputDevice ); +DBG_NAMEEX( Font ); + +// ======================================================================= + +#define OUTDEV_CHARCONVERT_REPLACE FALSE + +using namespace ::com::sun::star; +using namespace ::rtl; + + +// ======================================================================= + +#define MAX_DX_WORDS 120 +#define TEXT_DRAW_ELLIPSIS (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) + +// ======================================================================= + +#define UNDERLINE_LAST UNDERLINE_BOLDWAVE +#define STRIKEOUT_LAST STRIKEOUT_X + +// ======================================================================= + +void OutputDevice::ImplUpdateFontData( BOOL bNewFontLists ) +{ + if ( mpFontEntry ) + { + mpFontCache->Release( mpFontEntry ); + mpFontEntry = NULL; + } + if ( bNewFontLists ) + { + if ( mpGetDevFontList ) + { + delete mpGetDevFontList; + mpGetDevFontList = NULL; + } + if ( mpGetDevSizeList ) + { + delete mpGetDevSizeList; + mpGetDevSizeList = NULL; + } + } + + if ( GetOutDevType() == OUTDEV_PRINTER ) + { + mpFontCache->Clear(); + + if ( bNewFontLists ) + { +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( ImplGetGraphics() ) +#endif + { + mpFontList->Clear(); + mpGraphics->GetDevFontList( mpFontList ); + mpFontList->InitStdFonts(); + } + } + } + + mbInitFont = TRUE; + mbNewFont = TRUE; + + // Bei Fenstern auch alle Child-Fenster mit updaten + if ( GetOutDevType() == OUTDEV_WINDOW ) + { + Window* pChild = ((Window*)this)->mpFirstChild; + while ( pChild ) + { + pChild->ImplUpdateFontData( TRUE ); + pChild = pChild->mpNext; + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplUpdateAllFontData( BOOL bNewFontLists ) +{ + ImplSVData* pSVData = ImplGetSVData(); + + // Alle Fenster updaten + Window* pFrame = pSVData->maWinData.mpFirstFrame; + while ( pFrame ) + { + pFrame->ImplUpdateFontData( bNewFontLists ); + + Window* pSysWin = pFrame->mpFrameData->mpFirstOverlap; + while ( pSysWin ) + { + pSysWin->ImplUpdateFontData( bNewFontLists ); + pSysWin = pSysWin->mpNextOverlap; + } + + pFrame = pFrame->mpFrameData->mpNextFrame; + } + + // Alle VirDev's updaten + VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev; + while ( pVirDev ) + { + pVirDev->ImplUpdateFontData( bNewFontLists ); + pVirDev = pVirDev->mpNext; + } + + // Alle Printer updaten + Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter; + while ( pPrinter ) + { + pPrinter->ImplUpdateFontData( bNewFontLists ); + pPrinter = pPrinter->mpNext; + } + + // Globale Fontlisten leeren, damit diese geupdatet werden + pSVData->maGDIData.mpScreenFontCache->Clear(); + if ( bNewFontLists ) + { + pSVData->maGDIData.mpScreenFontList->Clear(); + pFrame = pSVData->maWinData.mpFirstFrame; + if ( pFrame ) + { +#ifndef REMOTE_APPSERVER + if ( pFrame->ImplGetGraphics() ) +#endif + { + pFrame->mpGraphics->GetDevFontList( pFrame->mpFrameData->mpFontList ); + pFrame->mpFrameData->mpFontList->InitStdFonts(); + } + } + } +} + +// ======================================================================= + +struct ImplFontSubstEntry +{ + XubString maName; + XubString maReplaceName; + XubString maMatchName; + XubString maMatchReplaceName; + USHORT mnFlags; + ImplFontSubstEntry* mpNext; +}; + +// ======================================================================= + +static void ImplStrEraseAllSymbols( XubString& rStr ) +{ + xub_StrLen i = 0; + xub_Unicode c = rStr.GetChar( i ); + while ( c ) + { + // Alle Zeichen kleiner 0 zwischen 9-A, Z-a und z-127 loeschen + if ( (c < 48) || ((c > 57) && (c < 65)) || ((c > 90) && (c < 97)) || + ((c > 122) && (c <= 127)) ) + rStr.Erase( i, 1 ); + else + i++; + c = rStr.GetChar( i ); + } +} + +// ----------------------------------------------------------------------- + +static xub_StrLen ImplStrMatch( const XubString& rStr1, const XubString& rStr2 ) +{ + xub_StrLen nMatch = 0; + const xub_Unicode* pStr1 = rStr1.GetBuffer(); + const xub_Unicode* pStr2 = rStr2.GetBuffer(); + while ( (*pStr1 == *pStr2) && *pStr1 ) + { + pStr1++; + pStr2++; + nMatch++; + } + return nMatch; +} + +// ----------------------------------------------------------------------- + +static int ImplStrFullMatch( const XubString& rStr1, const char* pStr2 ) +{ + const xub_Unicode* pStr1 = rStr1.GetBuffer(); + while ( (*pStr1 == (xub_Unicode)(unsigned char)*pStr2) && *pStr1 ) + { + pStr1++; + pStr2++; + } + return !(*pStr1); +} + +// ======================================================================= + +#if 0 + +#define FONT_ATTR_SYMBOL ((ULONG)0x00000001) +#define FONT_ATTR_FIXED ((ULONG)0x00000002) +#define FONT_ATTR_ITALIC ((ULONG)0x00000004) +#define FONT_ATTR_NORMAL ((ULONG)0x00000008) +#define FONT_ATTR_STANDARD ((ULONG)0x00000010) +#define FONT_ATTR_SPECIAL ((ULONG)0x00000020) +#define FONT_ATTR_TITLING ((ULONG)0x00000040) +#define FONT_ATTR_SERIF ((ULONG)0x00000080) +#define FONT_ATTR_NONSERIF ((ULONG)0x00000100) +#define FONT_ATTR_ROUNDED ((ULONG)0x00000200) +#define FONT_ATTR_OUTLINE ((ULONG)0x00000400) +#define FONT_ATTR_SHADOW ((ULONG)0x00000800) +#define FONT_ATTR_SCRIPT ((ULONG)0x00001000) +#define FONT_ATTR_HANDWRITING ((ULONG)0x00002000) +#define FONT_ATTR_DECORATION ((ULONG)0x00004000) +#define FONT_ATTR_CHARSCRIPT ((ULONG)0x00008000) +#define FONT_ATTR_CHANCERY ((ULONG)0x00010000) +#define FONT_ATTR_OLDSTYLE ((ULONG)0x00020000) +#define FONT_ATTR_FAVOR1 ((ULONG)0x01000000) +#define FONT_ATTR_FAVOR2 ((ULONG)0x02000000) +#define FONT_ATTR_FAVOR3 ((ULONG)0x04000000) +#define FONT_ATTR_FAVOR4 ((ULONG)0x08000000) +#define FONT_ATTR_FOUND ((ULONG)0x80000000) + +struct ImplFontAttrWidthSearchData +{ + const char* mpStr; + FontWidth meWidth; +}; + +static ImplFontAttrWidthSearchData const aImplWidthAttrSearchList[] = +{ +{ "narrow", WIDTH_CONDENSED }, +{ "semicondensed", WIDTH_SEMI_CONDENSED }, +{ "ultracondensed", WIDTH_ULTRA_CONDENSED }, +{ "semiexpanded", WIDTH_SEMI_EXPANDED }, +{ "ultraexpanded", WIDTH_ULTRA_EXPANDED }, +{ "expanded", WIDTH_EXPANDED }, +{ "wide", WIDTH_ULTRA_EXPANDED }, +{ "condensed", WIDTH_CONDENSED }, +{ "cond", WIDTH_CONDENSED }, +{ "cn", WIDTH_CONDENSED }, +{ NULL, WIDTH_DONTKNOW }, +}; + +struct ImplFontAttrWeightSearchData +{ + const char* mpStr; + FontWeight meWeight; +}; + +static ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] = +{ +{ "extrablack", WEIGHT_BLACK }, +{ "ultrablack", WEIGHT_BLACK }, +{ "black", WEIGHT_BLACK }, +{ "heavy", WEIGHT_BLACK }, +{ "ultrabold", WEIGHT_ULTRABOLD }, +{ "semibold", WEIGHT_SEMIBOLD }, +{ "bold", WEIGHT_BOLD }, +{ "ultralight", WEIGHT_ULTRALIGHT }, +{ "semilight", WEIGHT_SEMILIGHT }, +{ "light", WEIGHT_LIGHT }, +{ "demi", WEIGHT_SEMIBOLD }, +{ "medium", WEIGHT_MEDIUM }, +{ NULL, WEIGHT_DONTKNOW }, +}; + +struct ImplFontAttrTypeSearchData +{ + const char* mpStr; + ULONG mnType; +}; + +static ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] = +{ +{ "titling", FONT_ATTR_TITLING }, +{ "outline", FONT_ATTR_OUTLINE }, +{ "shadow", FONT_ATTR_SHADOW }, +{ NULL, 0 }, +}; + +// ======================================================================= + +struct ImplFontNameAttr +{ + const char* mpName; + FontFamily meFamily; + FontWeight meWeight; + FontWidth meWidth; + ULONG mnType; +}; + +static const ImplFontNameAttr aImplFullList[] = +{ +{ "Bookman", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_NORMAL | FONT_ATTR_STANDARD | FONT_ATTR_SERIF }, +{ NULL, FAMILY_DONTKNOW,WEIGHT_DONTKNOW,WIDTH_DONTKNOW, 0 } +}; + +static const ImplFontNameAttr aImplMatchList[] = +{ +{ "bookman", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_NORMAL | FONT_ATTR_STANDARD | FONT_ATTR_SERIF }, +{ "comicsansms", FAMILY_SCRIPT, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_NONSERIF | FONT_ATTR_SCRIPT | FONT_ATTR_CHARSCRIPT | FONT_ATTR_FAVOR3 }, +{ "kristenitc", FAMILY_SCRIPT, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_NONSERIF | FONT_ATTR_SCRIPT | FONT_ATTR_CHARSCRIPT | FONT_ATTR_FAVOR3 }, +{ "maiandragd", FAMILY_SCRIPT, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_NONSERIF | FONT_ATTR_SCRIPT | FONT_ATTR_CHARSCRIPT | FONT_ATTR_FAVOR3 }, +{ "arioso", FAMILY_SCRIPT, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_NONSERIF | FONT_ATTR_SCRIPT | FONT_ATTR_ITALIC | FONT_ATTR_DECORATION | FONT_ATTR_OLDSTYLE | FONT_ATTR_FAVOR3 }, +{ "tempussansitc", FAMILY_SCRIPT, WEIGHT_LIGHT, WIDTH_NORMAL, FONT_ATTR_NONSERIF | FONT_ATTR_SCRIPT | FONT_ATTR_CHARSCRIPT }, +{ "papyrus", FAMILY_SCRIPT, WEIGHT_LIGHT, WIDTH_NORMAL, FONT_ATTR_NONSERIF | FONT_ATTR_SCRIPT | FONT_ATTR_CHARSCRIPT }, +{ "lucidashadowtitling", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_TITLING | FONT_ATTR_OUTLINE | FONT_ATTR_SHADOW }, +{ "lucidaopenboldtitling",FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_TITLING | FONT_ATTR_OUTLINE }, +{ "lucidaopentitling", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_TITLING | FONT_ATTR_OUTLINE }, +{ "lucidaopen", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_OUTLINE }, +{ "lucidashadow", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_OUTLINE | FONT_ATTR_SHADOW }, +{ "chevara", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_EXPANDED, FONT_ATTR_SERIF | FONT_ATTR_TITLING | FONT_ATTR_OUTLINE }, +{ "colonnamt", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_SPECIAL | FONT_ATTR_OUTLINE }, +{ "imprintmtshadow", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_OUTLINE | FONT_ATTR_SHADOW }, +{ "castellar", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_TITLING | FONT_ATTR_OUTLINE }, +{ "algerian", FAMILY_ROMAN, WEIGHT_NORMAL, WIDTH_NORMAL, FONT_ATTR_SERIF | FONT_ATTR_SPECIAL | FONT_ATTR_TITLING | FONT_ATTR_OUTLINE | FONT_ATTR_SHADOW | FONT_ATTR_OLDSTYLE }, +{ NULL, FAMILY_DONTKNOW,WEIGHT_DONTKNOW,WIDTH_DONTKNOW, 0 } +}; + +#endif + +static const char* aImplSwissMatchList[] = +{ + "arial", + "avantgarde", + "cgomega", + "centurygothic", + "charcoal", + "chicago", + "frutiger", + "geneva", + "haettenschweiler", + "helmet", + "helv", + "lucida", + "impact", + "tahoma", + "univers", + "vagrounded", + "verdana", + NULL +}; + +static const char* aImplSwissSearchList[] = +{ + "sansserif", + "swiss", + NULL +}; + +static const char* aImplRomanMatchList[] = +{ + "algerian", + "antiqua", + "caliso", + "clarendon", + "colonna", + "garamond", + "newyork", + "palatino", + "timmons", + NULL +}; + +static const char* aImplRomanSearchList[] = +{ + "book", + "times", + "roman", + "bright", + NULL +}; + +static const char* aImplFixedMatchList[] = +{ + "lineprinter", + "monaco", + "typewriter", + NULL +}; + +static const char* aImplFixedSearchList[] = +{ + "console", + "courier", + "fixed", + "letter", + "monospace", + "terminal", + NULL +}; + +static const char* aImplScriptMatchList[] = +{ + "arioso", + "coronet", + "cursive", + "marigold", + "zapfchancery", + NULL +}; + +static const char* aImplScriptSearchList[] = +{ + "script", + "signet", + "handwriting", + "calligraphy", + NULL +}; + +static const char* aImplSymbolMatchList[] = +{ + "marlett", + "monotypesorts", + "msoutlook", + NULL +}; + +static const char* aImplSymbolSearchList[] = +{ + "symbol", + "bats", + "dings", + "math", + NULL +}; + +static const char* aImplTypeList[] = +{ + "black", + "bold", + "condensed", + "expanded", + "narrow", + "outline", + NULL +}; + +// ======================================================================= + +static const char* aImplSearchScriptList[] = +{ + "ce", + "we", + "cyr", + "tur", + "wt", + "greek", + "wl", + NULL +}; + +// ----------------------------------------------------------------------- + +struct ImplScriptSearchList +{ + const char* mpScript; + rtl_Script meScript; +}; + +static void ImplCutScriptAndSpaces( XubString& rName ) +{ + rName.EraseLeadingAndTrailingChars( ' ' ); + + USHORT nLen = rName.Len(); + if ( nLen < 3 ) + return; + + // Scriptname must be the last part of the fontname and + // looks like "fontname (scriptname)". So there can only be a + // script name at the and of the fontname, when the last char is + // ')'. + if ( rName.GetChar( nLen-1 ) == ')' ) + { + int nOpen = 1; + nLen -= 2; + while ( nLen ) + { + if ( rName.GetChar( nLen ) == '(' ) + { + nOpen--; + if ( !nOpen && nLen && (rName.GetChar( nLen-1 ) == ' ') ) + { + XubString aScript = rName.Copy( nLen+1, rName.Len()-1-nLen-1 ); + rName.Erase( nLen-1 ); + return; + } + } + if ( rName.GetChar( nLen ) == ')' ) + nOpen++; + nLen--; + } + } + + // For compatibility with older version we must search for a + // script name at the end of a fontname without brakets + USHORT nSpacePos = rName.SearchBackward( ' ' ); + if ( nSpacePos && (nSpacePos != STRING_NOTFOUND) ) + { + XubString aScript = rName.Copy( nSpacePos+1 ); + const char** pScript = aImplSearchScriptList; + while ( *pScript ) + { + if ( aScript.EqualsAscii( *pScript ) ) + { + rName.Erase( nSpacePos ); + break; + } + pScript++; + } + } +} + +// ======================================================================= + +#if 0 + +static const ImplFontNameAttr* ImplFindFontAttr( const XubString& rFontName ) +{ + const ImplFontNameAttr* pList; + + pList = aImplFullList; + while ( pList->mpName ) + { + if ( rFontName.EqualsAscii( pList->mpName ) ) + return pList; + pList++; + } + + pList = aImplMatchList; + while ( pList->mpName ) + { + if ( ImplStrFullMatch( rFontName, pList->mpName ) ) + return pList; + pList++; + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +static void ImplGetFontAttr( const XubString& rFontName, + FontFamily& rFamily, CharSet& rCharSet, + FontPitch& rPitch ) +{ + if ( (rFamily == FAMILY_DONTKNOW) || (rPitch == PITCH_DONTKNOW) || + (rCharSet == CHARSET_DONTKNOW) ) + { + } +} + +#endif + +// ======================================================================= + +BOOL ImplTestFontName( const XubString& rName, + const sal_Char** pMatchList, + const sal_Char** pSearchList ) +{ + const char** pAlias; + + pAlias = pMatchList; + while ( *pAlias ) + { + if ( ImplStrFullMatch( rName, *pAlias ) ) + return TRUE; + pAlias++; + } + + pAlias = pSearchList; + while ( *pAlias ) + { + if ( rName.SearchAscii( *pAlias ) != STRING_NOTFOUND ) + return TRUE; + pAlias++; + } + + return FALSE; +} + +// ======================================================================= + +static void ImplFontAttrFromName( const XubString& rFontName, + FontFamily& rFamily, CharSet& rCharSet, + FontPitch& rPitch ) +{ + if ( rFamily == FAMILY_DONTKNOW ) + { + if ( ImplTestFontName( rFontName, aImplRomanMatchList, aImplRomanSearchList ) ) + rFamily = FAMILY_ROMAN; + else if ( ImplTestFontName( rFontName, aImplSwissMatchList, aImplSwissSearchList ) ) + rFamily = FAMILY_SWISS; + else if ( ImplTestFontName( rFontName, aImplScriptMatchList, aImplScriptSearchList ) ) + rFamily = FAMILY_SCRIPT; + } + + if ( rPitch == PITCH_DONTKNOW ) + { + if ( ImplTestFontName( rFontName, aImplFixedMatchList, aImplFixedSearchList ) ) + rPitch = PITCH_FIXED; + } + + if ( rCharSet == RTL_TEXTENCODING_DONTKNOW ) + { + if ( ImplTestFontName( rFontName, aImplSymbolMatchList, aImplSymbolSearchList ) ) + rCharSet = RTL_TEXTENCODING_SYMBOL; + } +} + +// ======================================================================= + +static const char* aImplStdSwissList[] = +{ + "helvetica", + "arial", + "lucida sans", + "lucidasans", + "lucida", + "geneva", + "helmet", + NULL +}; + +static const char* aImplStdRomanList[] = +{ + "times", + "times new roman", + "timesnewroman", + "roman", + "lucida serif", + "lucidaserif", + "lucida bright", + "lucidabright", + "bookman", + "garamond", + "timmons", + NULL +}; + +static const char* aImplStdFixedList[] = +{ + "courier", + "courier new", + "lucida typewriter", + "lucidatypewriter", + "lucida sans typewriter", + "lucidasanstypewriter", + NULL +}; + +static const char* aImplStdScriptList[] = +{ + "zapf chancery", + "zapfchancery", + "lucida calligraphy", + "lucidacalligraphy", + "lucida handwriting", + "lucidahandwriting", + "arioso", + "script", + "marigold", + NULL +}; + +static const char* aImplStdSymbolList[] = +{ + "starbats", + "symbol", + "zapf dingbats", + "zapfdingbats", + "wingdings", + "lucida dingbats", + "lucidadingbats", + "lucida sans dingbats", + "lucidasansdingbats", + NULL +}; + +// ======================================================================= + +void ImplFreeOutDevFontData() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplFontSubstEntry* pEntry = pSVData->maGDIData.mpFirstFontSubst; + while ( pEntry ) + { + ImplFontSubstEntry* pNext = pEntry->mpNext; + delete pEntry; + pEntry = pNext; + } +} + +// ======================================================================= + +static ImplFontData* ImplFindScript( ImplDevFontListData* pData, + rtl_Script eScript ) +{ + // Testen, ob ein Font mit einem entsprechendem + // Script vorhanden ist + ImplFontData* pCurFontData = pData->mpFirst; + while ( pCurFontData ) + { + // Detect Unicode Font !!! + if ( pData->maMatchName.EqualsAscii( "arial unicode ms" ) ) + return pCurFontData; + if ( eScript == pCurFontData->meScript ) + return pCurFontData; + pCurFontData = pCurFontData->mpNext; + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +/* !!! UNICODE - Duerfte nicht mehr gebraucht werden !!! +static rtl_TextEncoding ImplGetFakeEncoding( rtl_TextEncoding eEncoding ) +{ + rtl_TextEncoding eSystemEncoding = GetSystemCharSet(); + // MS_1252 und 8859_1 sind kompatible + if ( ((eEncoding == RTL_TEXTENCODING_MS_1252) || + (eEncoding == RTL_TEXTENCODING_ISO_8859_1)) && + ((eSystemEncoding == RTL_TEXTENCODING_MS_1252) || + (eSystemEncoding == RTL_TEXTENCODING_ISO_8859_1)) ) + return eEncoding; + else + { + // Wir testen, ob beide Zeichensaetze dem gleichem Script + // entsprechen, um so der Applikation das gleiche Encoding + // vorzugaukeln. Dies ist beispielsweise bei Russisch wichtig + // da hier Fonts mit unterschiedlichem Encoding auftauchen + // koennen. + rtl_Script eSrcScript; + rtl_Script eSystemScript; + rtl_TextEncodingInfo aTEncInfo; + aTEncInfo.StructSize = sizeof( aTEncInfo ); + aTEncInfo.Script = SCRIPT_DONTKNOW; + if ( !rtl_getTextEncodingInfo( eEncoding, &aTEncInfo ) ) + return eEncoding; + else + eSrcScript = aTEncInfo.Script; + aTEncInfo.Script = SCRIPT_DONTKNOW; + if ( !rtl_getTextEncodingInfo( eSystemEncoding, &aTEncInfo ) ) + return eEncoding; + else + eSystemScript = aTEncInfo.Script; + if ( eSrcScript == eSystemScript ) + eEncoding = eSystemEncoding; + } + + return eEncoding; +} +*/ + +// ======================================================================= + +void OutputDevice::BeginFontSubstitution() +{ + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maGDIData.mbFontSubChanged = FALSE; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::EndFontSubstitution() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maGDIData.mbFontSubChanged ) + { + ImplUpdateAllFontData( FALSE ); + + Application* pApp = GetpApp(); + DataChangedEvent aDCEvt( DATACHANGED_FONTSUBSTITUTION ); + pApp->DataChanged( aDCEvt ); + pApp->NotifyAllWindows( aDCEvt ); + pSVData->maGDIData.mbFontSubChanged = FALSE; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::AddFontSubstitute( const XubString& rFontName, + const XubString& rReplaceFontName, + USHORT nFlags ) +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplFontSubstEntry* pEntry = new ImplFontSubstEntry; + + pEntry->maName = rFontName; + pEntry->maReplaceName = rReplaceFontName; + pEntry->maMatchName = rFontName; + pEntry->maMatchReplaceName = rReplaceFontName; + pEntry->mnFlags = nFlags; + pEntry->mpNext = pSVData->maGDIData.mpFirstFontSubst; + pEntry->maMatchName.ToLowerAscii(); + pEntry->maMatchReplaceName.ToLowerAscii(); + ImplCutScriptAndSpaces( pEntry->maMatchName ); + ImplCutScriptAndSpaces( pEntry->maMatchReplaceName ); + pSVData->maGDIData.mpFirstFontSubst = pEntry; + pSVData->maGDIData.mbFontSubChanged = TRUE; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::RemoveFontSubstitute( USHORT n ) +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplFontSubstEntry* pEntry = pSVData->maGDIData.mpFirstFontSubst; + ImplFontSubstEntry* pPrev = NULL; + USHORT nCount = 0; + while ( pEntry ) + { + if ( nCount == n ) + { + pSVData->maGDIData.mbFontSubChanged = TRUE; + if ( pPrev ) + pPrev->mpNext = pEntry->mpNext; + else + pSVData->maGDIData.mpFirstFontSubst = pEntry->mpNext; + delete pEntry; + break; + } + + nCount++; + pPrev = pEntry; + pEntry = pEntry->mpNext; + } +} + +// ----------------------------------------------------------------------- + +USHORT OutputDevice::GetFontSubstituteCount() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplFontSubstEntry* pEntry = pSVData->maGDIData.mpFirstFontSubst; + USHORT nCount = 0; + while ( pEntry ) + { + nCount++; + pEntry = pEntry->mpNext; + } + + return nCount; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::GetFontSubstitute( USHORT n, + XubString& rFontName, + XubString& rReplaceFontName, + USHORT& rFlags ) +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplFontSubstEntry* pEntry = pSVData->maGDIData.mpFirstFontSubst; + USHORT nCount = 0; + while ( pEntry ) + { + if ( nCount == n ) + { + rFontName = pEntry->maName; + rReplaceFontName = pEntry->maReplaceName; + rFlags = pEntry->mnFlags; + break; + } + + nCount++; + pEntry = pEntry->mpNext; + } +} + +// ----------------------------------------------------------------------- + +static BOOL ImplFontSubstitute( XubString& rFontName, + USHORT nFlags1, USHORT nFlags2 ) +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplFontSubstEntry* pEntry = pSVData->maGDIData.mpFirstFontSubst; + while ( pEntry ) + { + if ( ((pEntry->mnFlags & nFlags1) == nFlags2) && + (pEntry->maMatchName == rFontName) ) + { + rFontName = pEntry->maMatchReplaceName; + return TRUE; + } + + pEntry = pEntry->mpNext; + } + + return FALSE; +} + +// ======================================================================= + +ImplDevFontList::ImplDevFontList() : + List( CONTAINER_MAXBLOCKSIZE, 96, 32 ) +{ +#if 0 + mbIsInitMatchData = FALSE; +#endif +} + +// ----------------------------------------------------------------------- + +ImplDevFontList::~ImplDevFontList() +{ + // Alle Eintraege loeschen + ImplDevFontListData* pEntry = First(); + while ( pEntry ) + { + // Liste der Font loeschen + ImplFontData* pFontData = pEntry->mpFirst; + do + { + ImplFontData* pTempFontData = pFontData; + pFontData = pFontData->mpNext; + delete pTempFontData; + } + while ( pFontData ); + // Entry loeschen + delete pEntry; + + pEntry = Next(); + } +} + +// ----------------------------------------------------------------------- + +static StringCompare ImplCompareFontDataWithoutSize( const ImplFontData* pEntry1, + const ImplFontData* pEntry2 ) +{ + // Vergleichen nach CharSet, Groesse, Breite, Weight, Italic, StyleName + if ( pEntry1->meCharSet < pEntry2->meCharSet ) + return COMPARE_LESS; + else if ( pEntry1->meCharSet > pEntry2->meCharSet ) + return COMPARE_GREATER; + + if ( pEntry1->meWidthType < pEntry2->meWidthType ) + return COMPARE_LESS; + else if ( pEntry1->meWidthType > pEntry2->meWidthType ) + return COMPARE_GREATER; + + if ( pEntry1->meWeight < pEntry2->meWeight ) + return COMPARE_LESS; + else if ( pEntry1->meWeight > pEntry2->meWeight ) + return COMPARE_GREATER; + + if ( pEntry1->meItalic < pEntry2->meItalic ) + return COMPARE_LESS; + else if ( pEntry1->meItalic > pEntry2->meItalic ) + return COMPARE_GREATER; + + return pEntry1->maStyleName.CompareTo( pEntry2->maStyleName ); +} + +// ----------------------------------------------------------------------- + +static StringCompare ImplCompareFontData( const ImplFontData* pEntry1, + const ImplFontData* pEntry2 ) +{ + StringCompare eComp = ImplCompareFontDataWithoutSize( pEntry1, pEntry2 ); + if ( eComp != COMPARE_EQUAL ) + return eComp; + + if ( pEntry1->mnHeight < pEntry2->mnHeight ) + return COMPARE_LESS; + else if ( pEntry1->mnHeight > pEntry2->mnHeight ) + return COMPARE_GREATER; + + if ( pEntry1->mnWidth < pEntry2->mnWidth ) + return COMPARE_LESS; + else if ( pEntry1->mnWidth > pEntry2->mnWidth ) + return COMPARE_GREATER; + + return COMPARE_EQUAL; +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::Add( ImplFontData* pNewData ) +{ + XubString aSearchName = pNewData->maName; + aSearchName.ToLowerAscii(); + + // Query Script for FontTest + rtl_TextEncodingInfo aTEncInfo; + aTEncInfo.StructSize = sizeof( aTEncInfo ); + aTEncInfo.Script = SCRIPT_DONTKNOW; + rtl_getTextEncodingInfo( pNewData->meCharSet, &aTEncInfo ); + pNewData->meScript = aTEncInfo.Script; + + // Add Font + ULONG nIndex; + ImplDevFontListData* pFoundData = ImplFind( aSearchName, &nIndex ); + BOOL bDelete = FALSE; + + if ( !pFoundData ) + { + pFoundData = new ImplDevFontListData; + pFoundData->maName = pNewData->maName; + pFoundData->maMatchName = aSearchName; + pFoundData->maMatchName2 = aSearchName; + pFoundData->mpFirst = pNewData; +#if 0 + pFoundData->mbScalable = FALSE; + ImplStrEraseAllSymbols( pFoundData->maMatchName2 ); +/* + pFoundData->meMatchFamily = pNewData->meFamily; + pFoundData->meMatchPitch = pNewData->mePitch; + CharSet eCharSet = pNewData->meCharSet; + ImplFontAttrFromName( pFoundData->maMatchName2, pFoundData->meMatchFamily, + eCharSet, pFoundData->meMatchPitch ); + pFoundData->mbSymbol = eCharSet == RTL_TEXTENCODING_SYMBOL; +*/ +#else + pFoundData->meMatchFamily = pNewData->meFamily; + pFoundData->meMatchPitch = pNewData->mePitch; + pFoundData->mnMatch = 0; + CharSet eCharSet = pNewData->meCharSet; + ImplStrEraseAllSymbols( pFoundData->maMatchName2 ); + ImplFontAttrFromName( pFoundData->maMatchName2, pFoundData->meMatchFamily, + eCharSet, pFoundData->meMatchPitch ); + pFoundData->mbSymbol = eCharSet == RTL_TEXTENCODING_SYMBOL; +#endif + + pNewData->mpNext = NULL; + Insert( pFoundData, nIndex ); + } + else + { + // Name ersetzen (spart Speicherplatz) + pNewData->maName = pFoundData->maName; + + BOOL bInsert = TRUE; + ImplFontData* pPrev = NULL; + ImplFontData* pTemp = pFoundData->mpFirst; + do + { + StringCompare eComp = ImplCompareFontData( pNewData, pTemp ); + if ( eComp != COMPARE_GREATER ) + { + // Wenn Font gleich ist, nehmen wir einen Devicefont, + // oder ignorieren den Font + if ( eComp == COMPARE_EQUAL ) + { + // Wir nehmen den Font mit der besseren Quality, + // ansonsten ziehen wir den Device-Font vor + if ( (pNewData->mnQuality > pTemp->mnQuality) || + ((pNewData->mnQuality == pTemp->mnQuality) && + (pNewData->mbDevice && !pTemp->mbDevice)) ) + { + pNewData->mpNext = pTemp->mpNext; + if ( pPrev ) + pPrev->mpNext = pNewData; + else + pFoundData->mpFirst = pNewData; + delete pTemp; + } + else + { + bDelete = TRUE; + delete pNewData; + } + + bInsert = FALSE; + } + break; + } + + pPrev = pTemp; + pTemp = pTemp->mpNext; + } + while ( pTemp ); + + if ( bInsert ) + { + pNewData->mpNext = pTemp; + if ( pPrev ) + pPrev->mpNext = pNewData; + else + pFoundData->mpFirst = pNewData; + } + } + + // Match zusammenzaehlen + if ( !bDelete ) + { +#if 0 + if ( (pNewData->meType == TYPE_SCALABLE) && (pNewData->mnHeight == 0) ) + pFoundData->mbScalable = TRUE; +#else + if ( (pNewData->meType == TYPE_SCALABLE) && (pNewData->mnHeight == 0) ) + { + if ( pNewData->meWidthType == WIDTH_NORMAL ) + pFoundData->mnMatch += 30; + else + pFoundData->mnMatch += 3; + if ( pNewData->meItalic == ITALIC_NONE ) + pFoundData->mnMatch += 20; + else + pFoundData->mnMatch += 2; + if ( (pNewData->meWeight == WEIGHT_NORMAL) || (pNewData->meWeight == WEIGHT_MEDIUM) ) + pFoundData->mnMatch += 10; + else + pFoundData->mnMatch += 1; + } +#endif + } +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::ImplFind( const XubString& rFontName, ULONG* pIndex ) const +{ + ULONG nCount = Count(); + if ( !nCount ) + { + if ( pIndex ) + *pIndex = LIST_APPEND; + return NULL; + } + + // Fonts in der Liste suchen + ImplDevFontListData* pCompareData; + ImplDevFontListData* pFoundData = NULL; + ULONG nLow = 0; + ULONG nHigh = nCount-1; + ULONG nMid; + StringCompare eCompare; + + do + { + nMid = (nLow + nHigh) / 2; + pCompareData = Get( nMid ); + eCompare = rFontName.CompareTo( pCompareData->maMatchName ); + if ( eCompare == COMPARE_LESS ) + { + if ( !nMid ) + break; + nHigh = nMid-1; + } + else + { + if ( eCompare == COMPARE_GREATER ) + nLow = nMid + 1; + else + { + pFoundData = pCompareData; + break; + } + } + } + while ( nLow <= nHigh ); + + if ( pIndex ) + { + eCompare = rFontName.CompareTo( pCompareData->maMatchName ); + if ( eCompare == COMPARE_GREATER ) + *pIndex = (nMid+1); + else + *pIndex = nMid; + } + + return pFoundData; +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::FindFont( const XubString& rFontName ) const +{ + XubString aName = rFontName; + aName.ToLowerAscii(); + ImplCutScriptAndSpaces( aName ); + return ImplFind( aName ); +} + +// ----------------------------------------------------------------------- + +ImplDevFontListData* ImplDevFontList::FindStdFont( const sal_Char** pStdFontNames, + rtl_Script eScript ) const +{ + // We want a scalable font with a system script + ImplDevFontListData* pRasterFoundData = NULL; + ImplDevFontListData* pWrongScriptRasterData = NULL; + ImplDevFontListData* pWrongScriptData = NULL; + while ( *pStdFontNames ) + { + XubString aStdName( *pStdFontNames, RTL_TEXTENCODING_ASCII_US ); + ImplDevFontListData* pFoundData = ImplFind( aStdName ); + if ( pFoundData ) + { + if ( (eScript == SCRIPT_SYMBOL) || + ImplFindScript( pFoundData, eScript ) ) + { + if ( pFoundData->mpFirst->meType != TYPE_RASTER ) + return pFoundData; + else if ( !pRasterFoundData ) + pRasterFoundData = pFoundData; + } + else + { + if ( pFoundData->mpFirst->meType != TYPE_RASTER ) + { + if ( !pWrongScriptData ) + pWrongScriptData = pFoundData; + } + else + { + if ( !pWrongScriptRasterData ) + pWrongScriptRasterData = pFoundData; + } + } + } + pStdFontNames++; + } + + // Wenn keine passende Schrift, dann die Reihenfolge: + // - passender Zeichensatz + // - Skalierbar + // - eine passende Schrift die nicht skalierbar und den + // falschen Zeichensatz hat + // - keine + if ( pRasterFoundData ) + return pRasterFoundData; + else if ( pWrongScriptData ) + return pWrongScriptData; + else + return pWrongScriptRasterData; +} + +// ----------------------------------------------------------------------- + +void ImplDevFontList::Clear() +{ + // Alle Eintraege loeschen + ImplDevFontListData* pEntry = First(); + while ( pEntry ) + { + // Liste der Font loeschen + ImplFontData* pFontData = pEntry->mpFirst; + do + { + ImplFontData* pTempFontData = pFontData; + pFontData = pFontData->mpNext; + delete pTempFontData; + } + while ( pFontData ); + // Entry loeschen + delete pEntry; + + pEntry = Next(); + } + List::Clear(); + + // Standard-Fonts loeschen + for ( USHORT i = 0; i < IMPL_STDFONT_COUNT; i++ ) + mpStdFontAry[i] = NULL; +#if 0 + mbIsInitMatchData = FALSE; +#endif +} + +// ----------------------------------------------------------------------- + +#if 0 +void ImplDevFontList::InitMatchData() +{ + if ( mbIsInitMatchData ) + return; + mbIsInitMatchData = TRUE; +/* + // Fuer alle Eintraege die Matchdaten ermitteln + ImplDevFontListData* pEntry = First(); + while ( pEntry ) + { + ImplFontData* pFontData = pEntry->mpFirst; + + pEntry = Next(); + } +*/ +} +#endif + +// ----------------------------------------------------------------------- + +void ImplDevFontList::InitStdFonts() +{ + rtl_Script eSystemScript; + rtl_TextEncodingInfo aTextEncInfo; + aTextEncInfo.StructSize = sizeof( aTextEncInfo ); + aTextEncInfo.Script = SCRIPT_DONTKNOW; + rtl_getTextEncodingInfo( gsl_getSystemTextEncoding(), &aTextEncInfo ); + eSystemScript = aTextEncInfo.Script; + + mpStdFontAry[IMPL_STDFONT_SWISS] = FindStdFont( aImplStdSwissList, eSystemScript ); + mpStdFontAry[IMPL_STDFONT_ROMAN] = FindStdFont( aImplStdRomanList, eSystemScript ); + mpStdFontAry[IMPL_STDFONT_FIXED] = FindStdFont( aImplStdFixedList, eSystemScript ); + mpStdFontAry[IMPL_STDFONT_SCRIPT] = FindStdFont( aImplStdScriptList, eSystemScript ); + mpStdFontAry[IMPL_STDFONT_SYMBOL] = FindStdFont( aImplStdSymbolList, SCRIPT_SYMBOL ); +} + +// ======================================================================= + +void ImplGetDevSizeList::Add( long nNewHeight ) +{ + ULONG n = Count(); + if ( !n || (nNewHeight > Get( n-1 )) ) + Insert( (void*)nNewHeight, LIST_APPEND ); + else + { + for ( ULONG i=0 ; i < n; i++ ) + { + long nHeight = Get( i ); + + if ( nNewHeight <= nHeight ) + { + if ( nNewHeight != nHeight ) + Insert( (void*)nNewHeight, i ); + break; + } + } + } +} + +// ======================================================================= + +ImplFontEntry::~ImplFontEntry() +{ + if ( mpWidthAry ) + delete mpWidthAry; + + if ( mpKernPairs ) + delete mpKernPairs; + + if ( mpKernInfo ) + delete mpKernInfo; +} + +// ======================================================================= + +ImplFontCache::ImplFontCache( BOOL bPrinter ) +{ + mpFirstEntry = NULL; + mnRef0Count = 0; + mbPrinter = bPrinter; +} + +// ----------------------------------------------------------------------- + +ImplFontCache::~ImplFontCache() +{ + // Alle Eintraege loeschen + ImplFontEntry* pTemp; + ImplFontEntry* pEntry = mpFirstEntry; + while ( pEntry ) + { + pTemp = pEntry->mpNext; + delete pEntry; + pEntry = pTemp; + } +} + +// ----------------------------------------------------------------------- + +ImplFontEntry* ImplFontCache::Get( ImplDevFontList* pFontList, + const Font& rFont, const Size& rSize ) +{ + const XubString& rName = rFont.GetName(); + const XubString& rStyleName = rFont.GetStyleName(); + long nWidth = rSize.Width(); + long nHeight = rSize.Height(); + FontFamily eFamily = rFont.GetFamily(); + CharSet eCharSet = rFont.GetCharSet(); + FontWeight eWeight = rFont.GetWeight(); + FontItalic eItalic = rFont.GetItalic(); + FontPitch ePitch = rFont.GetPitch(); + short nOrientation = (short)(rFont.GetOrientation() % 3600); + BOOL bShadow = rFont.IsShadow(); + BOOL bOutline = rFont.IsOutline(); + + // Groesse anpassen + if ( nHeight < 0 ) + nHeight = -nHeight; + if ( nWidth < 0 ) + nWidth = -nWidth; + + // Eintrag suchen + ImplFontEntry* pPrevEntry = NULL; + ImplFontEntry* pEntry = mpFirstEntry; + while ( pEntry ) + { + ImplFontSelectData* pFontSelData = &(pEntry->maFontSelData); + if ( (nHeight == pFontSelData->mnHeight) && + (eWeight == pFontSelData->meWeight) && + (eItalic == pFontSelData->meItalic) && + (rName == pFontSelData->maName) && + (rStyleName == pFontSelData->maStyleName) && + (eCharSet == pFontSelData->meCharSet) && + (eFamily == pFontSelData->meFamily) && + (ePitch == pFontSelData->mePitch) && + (nWidth == pFontSelData->mnWidth) && + (nOrientation == pFontSelData->mnOrientation) ) + { + if ( !pEntry->mnRefCount ) + mnRef0Count--; + + pEntry->mnRefCount++; + + // Entry nach vorne bringen + if ( pPrevEntry ) + { + pPrevEntry->mpNext = pEntry->mpNext; + pEntry->mpNext = mpFirstEntry; + mpFirstEntry = pEntry; + } + + return pEntry; + } + + pPrevEntry = pEntry; + pEntry = pEntry->mpNext; + } + + // Wir suchen zuerst ueber den Namen den passenden Font + XubString aLowerFontName = rName; + XubString aFirstName; + XubString aTempName; + XubString aTempName2; + ImplDevFontListData* pFoundData; + ImplDevFontListData* pTempFoundData; + xub_StrLen nFirstNameIndex = 0; + xub_StrLen nIndex = 0; + USHORT nSubstFlags1 = FONT_SUBSTITUTE_ALWAYS; + USHORT nSubstFlags2 = FONT_SUBSTITUTE_ALWAYS; + rtl_Script eScript = SCRIPT_DONTKNOW; + rtl_Script eCharSetScript; + rtl_TextEncodingInfo aTextEncInfo; + aTextEncInfo.StructSize = sizeof( aTextEncInfo ); + aTextEncInfo.Script = SCRIPT_DONTKNOW; + rtl_getTextEncodingInfo( eCharSet, &aTextEncInfo ); + eCharSetScript = aTextEncInfo.Script; + if ( mbPrinter ) + nSubstFlags1 |= FONT_SUBSTITUTE_SCREENONLY; + aLowerFontName.ToLowerAscii(); + pTempFoundData = NULL; + do + { + aTempName = aLowerFontName.GetToken( 0, ';', nIndex ); + ImplCutScriptAndSpaces( aTempName ); + if ( !aFirstName.Len() ) + { + aFirstName = aTempName; + nFirstNameIndex = nIndex; + } + ImplFontSubstitute( aTempName, nSubstFlags1, nSubstFlags2 ); + pFoundData = pFontList->ImplFind( aTempName ); + if ( pFoundData ) + { + if ( eCharSetScript != SCRIPT_DONTKNOW ) + { + if ( !pTempFoundData ) + { + aTempName2 = aTempName; + pTempFoundData = pFoundData; + } + + // Testen, ob ein Font mit einem entsprechendem + // Script vorhanden ist + if ( ImplFindScript( pFoundData, eCharSetScript ) ) + { + aLowerFontName = aTempName; + break; + } + else + pFoundData = NULL; + } + else + { + aLowerFontName = aTempName; + break; + } + } + } + while ( nIndex != STRING_NOTFOUND ); + if ( !pFoundData && pTempFoundData ) + { + pFoundData = pTempFoundData; + aLowerFontName = aTempName2; + } + + // Danach versuchen wir es nocheinmal unter Beruecksichtigung + // der gloablen Fontersetzungstabelle, wobei wir jetzt auch + // die Fonts nehmen, die ersetzt werden sollen, wenn sie + // nicht vorhanden sind + if ( !pFoundData ) + { + nSubstFlags1 &= ~FONT_SUBSTITUTE_ALWAYS; + nSubstFlags2 &= ~FONT_SUBSTITUTE_ALWAYS; + nIndex = 0; + do + { + aTempName = aLowerFontName.GetToken( 0, ';', nIndex ); + ImplCutScriptAndSpaces( aTempName ); + if ( ImplFontSubstitute( aTempName, nSubstFlags1, nSubstFlags2 ) ) + { + pFoundData = pFontList->ImplFind( aTempName ); + if ( pFoundData ) + { + aLowerFontName = aTempName; + break; + } + } + } + while ( nIndex != STRING_NOTFOUND ); + } + + // Wenn kein Font mit dem entsprechenden Namen existiert, versuchen + // wir ueber den Namen und die Attribute einen passenden Font zu + // finden + ULONG nFontCount = pFontList->Count(); + if ( !pFoundData && nFontCount ) + { +#if 0 + pFontList->InitMatchData(); + + // 1. Token vom Fontnamen nehmen und Sonderzeichen entfernen + XubString aNoSymbolName = aFirstName; + ImplCutScriptAndSpaces( aNoSymbolName ); + ImplStrEraseAllSymbols( aNoSymbolName ); + + // Script evtl. aus CharSet gewinnen, wenn nicht ueber den Fontnamen + // ermittelt werden konnte + if ( eScript == SCRIPT_DONTKNOW ) + eScript = eCharSetScript; + + // wir versuchen zuerst einen Font zu finden, der ueber den Namen + // matched + ULONG nTestMatch; + ULONG nBestMatch = 0; + for ( ULONG i = 0; i < nFontCount; i++ ) + { + ImplDevFontListData* pData = pFontList->Get( i ); + + nTestMatch = 0; + + // Wir wollen schon Zeichen erkennen + if ( eScript != SCRIPT_DONTKNOW ) + { + if ( ImplFindScript( pData, eScript ) ) + nTestMatch += 1000000000; + } + + // skalierbare Schriften haben schon einen echten Vorteil + // gegenueber nicht skalierbaren Schriften + if ( pData->mbScalable ) + nTestMatch += 500000000; + +/* + // Wir gehen davon aus, wenn der Name groesstenteils matcht, + // das er schon zur richtigen Familie gehoert + + + // Beim matchen ignorieren wir alle Sonderzeichen + ULONG nTestMatch = ImplStrMatch( aNoSymbolName, pData->maMatchName2 ); + if ( nTestMatch >= nBestMatch ) + { + // Match nur erlaubt, wenn auch die Attribute uebereinstimmen + BOOL bTestFamily = pData->meMatchFamily != FAMILY_DONTKNOW; + BOOL bTestSymbol = pData->mbSymbol; + BOOL bTestFixed = pData->meMatchPitch == PITCH_FIXED; + if ( (bFixed == bTestFixed) && (bSymbol == bTestSymbol) && + (!bFamily || !bTestFamily || (eSearchFamily == pData->meMatchFamily)) ) + { + xub_StrLen nAttrMatch = 0; + // Die Anzahl der uebereinstimmenden Attribute zaehlen + const char** pTypeList = aImplTypeList; + while ( *pTypeList ) + { + if ( (aNoSymbolName.Search( *pTypeList ) != STRING_NOTFOUND) && + (pData->maMatchName2.Search( *pTypeList ) != STRING_NOTFOUND) ) + nAttrMatch++; + pTypeList++; + } + + // Wenn beide Matches gleich gut sind, + // entscheiden die uebereinstimmenden Attribute + if ( nBestMatch == nTestMatch ) + { + if ( (nAttrMatch > nBestAttrMatch) || + ((nAttrMatch == nBestAttrMatch) && + (pData->maMatchName2.Len() < nBestStrLen)) ) + { + pFoundData = pData; + nBestMatch = nTestMatch; + nBestAttrMatch = nAttrMatch; + nBestStrLen = pData->maMatchName2.Len(); + } + } + else + { + pFoundData = pData; + nBestMatch = nTestMatch; + nBestAttrMatch = nAttrMatch; + nBestStrLen = pData->maMatchName2.Len(); + } + } + } +*/ + + if ( nTestMatch > nBestMatch ) + { + pFoundData = pData; + nBestMatch = nTestMatch; + } + } + + if ( !pFoundData ) + { + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_ROMAN ); + // Wenn alles nichts hilft, nehmen wir den ersten + if ( !pFoundData ) + pFontList->Get( 0 ); + } +#else + // 1. Token vom Fontnamen nehmen und Sonderzeichen entfernen + XubString aNoSymbolName = aFirstName; + ImplStrEraseAllSymbols( aNoSymbolName ); + + // Suchattribute ermitteln + BOOL bFamily; + BOOL bSymbol; + BOOL bFixed; + FontFamily eSearchFamily = eFamily; + CharSet eSearchCharSet = eCharSet; + FontPitch eSearchPitch = ePitch; + ImplFontAttrFromName( aNoSymbolName, eSearchFamily, eSearchCharSet, eSearchPitch ); + bFamily = eSearchFamily != FAMILY_DONTKNOW; + bSymbol = eSearchCharSet == RTL_TEXTENCODING_SYMBOL; + bFixed = eSearchPitch == PITCH_FIXED; + // Solange in der Namesliste suchen, bis wir auswertbare + // Attribute gefunden haben + xub_StrLen nTempIndex = nFirstNameIndex; + while ( (nTempIndex != STRING_NOTFOUND) && !bFamily && !bFixed && !bSymbol ) + { + aTempName = aLowerFontName.GetToken( 0, ';', nTempIndex ); + ImplCutScriptAndSpaces( aTempName ); + if ( !aTempName.Len() ) + break; + ImplStrEraseAllSymbols( aTempName ); + ImplFontAttrFromName( aTempName, eSearchFamily, eSearchCharSet, eSearchPitch ); + bFamily = eSearchFamily != FAMILY_DONTKNOW; + bSymbol = eSearchCharSet == RTL_TEXTENCODING_SYMBOL; + bFixed = eSearchPitch == PITCH_FIXED; + } + aLowerFontName = aFirstName; + + // wir versuchen zuerst einen Font zu finden, der ueber den Namen + // matched + ULONG i; + xub_StrLen nBestMatch = 5; + xub_StrLen nBestAttrMatch = 0; + xub_StrLen nBestStrLen = 0xFFFF; + for ( i = 0; i < nFontCount; i++ ) + { + ImplDevFontListData* pData = pFontList->Get( i ); + // Beim matchen ignorieren wir alle Sonderzeichen + xub_StrLen nTestMatch = ImplStrMatch( aNoSymbolName, pData->maMatchName2 ); + if ( nTestMatch >= nBestMatch ) + { + // Match nur erlaubt, wenn auch die Attribute uebereinstimmen + BOOL bTestFamily = pData->meMatchFamily != FAMILY_DONTKNOW; + BOOL bTestSymbol = pData->mbSymbol; + BOOL bTestFixed = pData->meMatchPitch == PITCH_FIXED; + if ( (bFixed == bTestFixed) && (bSymbol == bTestSymbol) && + (!bFamily || !bTestFamily || (eSearchFamily == pData->meMatchFamily)) ) + { + xub_StrLen nAttrMatch = 0; + // Die Anzahl der uebereinstimmenden Attribute zaehlen + const char** pTypeList = aImplTypeList; + while ( *pTypeList ) + { + if ( (aNoSymbolName.SearchAscii( *pTypeList ) != STRING_NOTFOUND) && + (pData->maMatchName2.SearchAscii( *pTypeList ) != STRING_NOTFOUND) ) + nAttrMatch++; + pTypeList++; + } + + // Wenn beide Matches gleich gut sind, + // entscheiden die uebereinstimmenden Attribute + if ( nBestMatch == nTestMatch ) + { + if ( (nAttrMatch > nBestAttrMatch) || + ((nAttrMatch == nBestAttrMatch) && + (pData->maMatchName2.Len() < nBestStrLen)) ) + { + pFoundData = pData; + nBestMatch = nTestMatch; + nBestAttrMatch = nAttrMatch; + nBestStrLen = pData->maMatchName2.Len(); + } + } + else + { + pFoundData = pData; + nBestMatch = nTestMatch; + nBestAttrMatch = nAttrMatch; + nBestStrLen = pData->maMatchName2.Len(); + } + } + } + } + + // Wenn wir immer noch keinen passenden Font gefunden haben, versuchen + // wir es ueber die Attribute + if ( !pFoundData ) + { + if ( bFixed ) + { + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_FIXED ); + if ( !pFoundData ) + { + nBestMatch = 0; + for ( i = 0; i < nFontCount; i++ ) + { + ImplDevFontListData* pData = pFontList->Get( i ); + if ( (pData->meMatchPitch == PITCH_FIXED) && + !pData->mbSymbol && + (pData->meMatchFamily != FAMILY_DECORATIVE) ) + { + if ( pData->mnMatch > nBestMatch ) + { + pFoundData = pData; + nBestMatch = pData->mnMatch; + } + } + } + } + } + + if ( bFamily && !pFoundData ) + { + if ( eSearchFamily == FAMILY_SWISS ) + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_SWISS ); + else if ( eSearchFamily == FAMILY_ROMAN ) + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_ROMAN ); + else if ( eSearchFamily == FAMILY_SCRIPT ) + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_SCRIPT ); + if ( !pFoundData ) + { + nBestMatch = 0; + for ( i = 0; i < nFontCount; i++ ) + { + ImplDevFontListData* pData = pFontList->Get( i ); + if ( (pData->meMatchFamily == eSearchFamily) && + !pData->mbSymbol && + (pData->meMatchPitch != PITCH_FIXED) ) + { + if ( pData->mnMatch > nBestMatch ) + { + pFoundData = pData; + nBestMatch = pData->mnMatch; + } + } + } + } + } + + if ( bSymbol && !pFoundData ) + { + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_SYMBOL ); + if ( !pFoundData ) + { + nBestMatch = 0; + for ( i = 0; i < nFontCount; i++ ) + { + ImplDevFontListData* pData = pFontList->Get( i ); + if ( pData->mbSymbol ) + { + if ( pData->mnMatch > nBestMatch ) + { + pFoundData = pData; + nBestMatch = pData->mnMatch; + } + } + } + } + } + + if ( !pFoundData ) + { + pFoundData = pFontList->GetStandardFont( IMPL_STDFONT_ROMAN ); + // Wenn alles nichts hilft, nehmen wir den ersten + if ( !pFoundData ) + pFontList->Get( 0 ); + } + } +#endif + } + + // Script evtl. aus CharSet gewinnen, wenn nicht ueber den Fontnamen + // ermittelt werden konnte + if ( eScript == SCRIPT_DONTKNOW ) + eScript = eCharSetScript; + + // Jetzt suchen wir den Font ueber die Attribute + ImplFontData* pFontData = NULL; + if ( pFoundData ) + { + ULONG nBestMatch = 0; // Der groessere Wert ist der bessere + ULONG nBestHeightMatch = 0; // Der kleinere Wert ist der bessere + ULONG nBestWidthMatch = 0; // Der kleinere Wert ist der bessere + ULONG nMatch; + ULONG nHeightMatch; + ULONG nWidthMatch; + ImplFontData* pCurFontData; + +// !!!!! Wegen Office-Fehler !!!! +// XubString aCompareStyleName = rStyleName; +// aCompareStyleName.ToLowerAscii(); + + // Vorallem wegen OS2-System-Standard-Fonts vergleichen wir + // den FontNamen mit FontName + StyleName, damit + // WarpSans Bold auch einen fetten Font ergibt + const xub_Unicode* pCompareStyleName = NULL; + if ( (aLowerFontName.Len() > pFoundData->maMatchName.Len()) && + aLowerFontName.Equals( pFoundData->maMatchName, 0, pFoundData->maMatchName.Len() ) ) + pCompareStyleName = aLowerFontName.GetBuffer()+pFoundData->maMatchName.Len()+1; + + pCurFontData = pFoundData->mpFirst; + while ( pCurFontData ) + { + nMatch = 0; + nHeightMatch = 0; + nWidthMatch = 0; + +// if ( aCompareStyleName.Len() && +// aCompareStyleName.EqualsIgnoreCaseAscii( pCurFontData->maStyleName ) ) +// nMatch += 120000; + if ( pCompareStyleName && + pCurFontData->maStyleName.EqualsIgnoreCaseAscii( pCompareStyleName ) ) + nMatch += 120000; + + if ( eCharSet != RTL_TEXTENCODING_DONTKNOW ) + { + if ( eCharSet == pCurFontData->meCharSet ) + nMatch += 60000; + } + + if ( eScript == pCurFontData->meScript ) + nMatch += 30000; + + if ( (ePitch != PITCH_DONTKNOW) && (ePitch == pCurFontData->mePitch) ) + nMatch += 15000; + + if ( (eFamily != FAMILY_DONTKNOW) && (eFamily == pCurFontData->meFamily) ) + nMatch += 7500; + + // Normale Schriftbreiten bevorzugen, da wir noch keine Daten + // von den Applikationen bekommen + if ( pCurFontData->meWidthType == WIDTH_NORMAL ) + nMatch += 3750; + + if ( eWeight != WEIGHT_DONTKNOW ) + { + USHORT nReqWeight; + USHORT nGivenWeight; + USHORT nWeightDiff; + // schmale Fonts werden bei nicht Bold vor fetten + // Fonts bevorzugt + if ( eWeight > WEIGHT_MEDIUM ) + nReqWeight = ((USHORT)eWeight)+100; + else + nReqWeight = (USHORT)eWeight; + if ( pCurFontData->meWeight > WEIGHT_MEDIUM ) + nGivenWeight = ((USHORT)pCurFontData->meWeight)+100; + else + nGivenWeight = (USHORT)pCurFontData->meWeight; + if ( nReqWeight > nGivenWeight ) + nWeightDiff = nReqWeight-nGivenWeight; + else + nWeightDiff = nGivenWeight-nReqWeight; + + if ( nWeightDiff == 0 ) + nMatch += 1000; + else if ( nWeightDiff == 1 ) + nMatch += 700; + else if ( nWeightDiff < 50 ) + nMatch += 200; + } + if ( eItalic == ITALIC_NONE ) + { + if ( pCurFontData->meItalic == ITALIC_NONE ) + nMatch += 900; + } + else + { + if ( eItalic == pCurFontData->meItalic ) + nMatch += 900; + else if ( pCurFontData->meItalic != ITALIC_NONE ) + nMatch += 600; + } + + if ( pCurFontData->mbDevice ) + nMatch += 40; + if ( pCurFontData->meType == TYPE_SCALABLE ) + { + if ( nOrientation ) + nMatch += 80; + else + { + if ( nWidth ) + nMatch += 25; + else + nMatch += 5; + } + } + else + { + if ( nHeight == pCurFontData->mnHeight ) + { + nMatch += 20; + if ( nWidth == pCurFontData->mnWidth ) + nMatch += 10; + } + else + { + // Dann kommt die Size-Abweichung in die + // Bewertung rein. Hier bevorzugen wir + // nach Moeglichkeit den kleineren Font + if ( nHeight < pCurFontData->mnHeight ) + nHeightMatch += pCurFontData->mnHeight-nHeight; + else + nHeightMatch += (nHeight-pCurFontData->mnHeight-nHeight)+10000; + if ( nWidth && pCurFontData->mnWidth && (nWidth != pCurFontData->mnWidth) ) + { + if ( nWidth < pCurFontData->mnWidth ) + nWidthMatch += pCurFontData->mnWidth-nWidth; + else + nWidthMatch += nWidth-pCurFontData->mnWidth-nWidth; + } + } + } + + if ( nMatch > nBestMatch ) + { + pFontData = pCurFontData; + nBestMatch = nMatch; + nBestHeightMatch = nHeightMatch; + nBestWidthMatch = nWidthMatch; + } + else if ( nMatch == nBestMatch ) + { + // Wenn 2 gleichwertig sind, kommt die Size-Bewertung + // Hier gewinnt der jenige, der die niedrigere Abweichung + // in der Groesse hat (also den kleinsten Match) + if ( nHeightMatch < nBestHeightMatch ) + { + pFontData = pCurFontData; + nBestHeightMatch = nHeightMatch; + nBestWidthMatch = nWidthMatch; + } + else if ( nHeightMatch == nBestHeightMatch ) + { + if ( nWidthMatch < nBestWidthMatch ) + { + pFontData = pCurFontData; + nBestWidthMatch = nWidthMatch; + } + } + } + + pCurFontData = pCurFontData->mpNext; + } + } + + // Daten initialisieren und in die Liste aufnehmen + pEntry = new ImplFontEntry; + pEntry->mbInit = FALSE; + pEntry->mnRefCount = 1; + pEntry->mpNext = mpFirstEntry; + pEntry->mnWidthAryCount = 0; + pEntry->mnWidthArySize = 0; + pEntry->mpWidthAry = NULL; + pEntry->mpKernPairs = NULL; + pEntry->mpKernInfo = NULL; + pEntry->mnOwnOrientation = 0; + pEntry->mnOrientation = 0; + mpFirstEntry = pEntry; + + // Font-Selection-Daten setzen + ImplFontSelectData* pFontSelData = &(pEntry->maFontSelData); + pFontSelData->mpFontData = pFontData; + pFontSelData->mpSysSelData = NULL; + pFontSelData->maName = rName; + pFontSelData->maStyleName = rStyleName; + pFontSelData->mnWidth = nWidth; + pFontSelData->mnHeight = nHeight; + pFontSelData->meFamily = eFamily; + pFontSelData->meCharSet = eCharSet; + pFontSelData->meWidthType = WIDTH_DONTKNOW; + pFontSelData->meWeight = eWeight; + pFontSelData->meItalic = eItalic; + pFontSelData->mePitch = ePitch; + pFontSelData->mnOrientation = nOrientation; + + return pEntry; +} + +// ----------------------------------------------------------------------- + +void ImplFontCache::Release( ImplFontEntry* pEntry ) +{ + pEntry->mnRefCount--; + + if ( !pEntry->mnRefCount ) + { + if ( mnRef0Count >= MAXFONT_CACHE ) + { + // Letzten Entry mit RefCount 0 loeschen + ImplFontEntry* pPrevDelEntry = mpFirstEntry; + ImplFontEntry* pDelEntry = pPrevDelEntry->mpNext; + USHORT nCurRef0Count = !(pPrevDelEntry->mnRefCount); + while ( pDelEntry ) + { + if ( !pDelEntry->mnRefCount ) + nCurRef0Count++; + + if ( nCurRef0Count >= MAXFONT_CACHE ) + { + pPrevDelEntry->mpNext = pDelEntry->mpNext; + delete pDelEntry; + break; + } + + pPrevDelEntry = pDelEntry; + pDelEntry = pDelEntry->mpNext; + } + } + else + mnRef0Count++; + } +} + +// ----------------------------------------------------------------------- + +void ImplFontCache::Clear() +{ + // Alle Eintraege loeschen + ImplFontEntry* pTemp; + ImplFontEntry* pEntry = mpFirstEntry; + while ( pEntry ) + { + DBG_ASSERT( !pEntry->mnRefCount, "ImplFontCache::Clear() - Font in use" ); + pTemp = pEntry->mpNext; + delete pEntry; + pEntry = pTemp; + } + + mpFirstEntry = NULL; + mnRef0Count = 0; +} + +// ======================================================================= + +class ImplTextLineInfo +{ +private: + long mnWidth; + xub_StrLen mnIndex; + xub_StrLen mnLen; + +public: + ImplTextLineInfo( long nWidth, xub_StrLen nIndex, xub_StrLen nLen ) + { + mnWidth = nWidth; + mnIndex = nIndex; + mnLen = nLen; + } + + long GetWidth() const { return mnWidth; } + xub_StrLen GetIndex() const { return mnIndex; } + xub_StrLen GetLen() const { return mnLen; } +}; + +#define MULTITEXTLINEINFO_RESIZE 16 +typedef ImplTextLineInfo* PImplTextLineInfo; + +class ImplMultiTextLineInfo +{ +private: + PImplTextLineInfo* mpLines; + xub_StrLen mnLines; + xub_StrLen mnSize; + +public: + ImplMultiTextLineInfo(); + ~ImplMultiTextLineInfo(); + + void AddLine( ImplTextLineInfo* pLine ); + void Clear(); + + ImplTextLineInfo* GetLine( USHORT nLine ) const + { return mpLines[nLine]; } + xub_StrLen Count() const { return mnLines; } + +private: + ImplMultiTextLineInfo( const ImplMultiTextLineInfo& ); + ImplMultiTextLineInfo& operator=( const ImplMultiTextLineInfo& ); +}; + +ImplMultiTextLineInfo::ImplMultiTextLineInfo() +{ + mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE]; + mnLines = 0; + mnSize = MULTITEXTLINEINFO_RESIZE; +} + +ImplMultiTextLineInfo::~ImplMultiTextLineInfo() +{ + for ( xub_StrLen i = 0; i < mnLines; i++ ) + delete mpLines[i]; + delete mpLines; +} + +void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine ) +{ + if ( mnSize == mnLines ) + { + mnSize += MULTITEXTLINEINFO_RESIZE; + PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize]; + memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) ); + mpLines = pNewLines; + } + + mpLines[mnLines] = pLine; + mnLines++; +} + +void ImplMultiTextLineInfo::Clear() +{ + for ( xub_StrLen i = 0; i < mnLines; i++ ) + delete mpLines[i]; + mnLines = 0; +} + +// ======================================================================= + +void OutputDevice::ImplInitFont() +{ + DBG_TESTSOLARMUTEX(); + + if ( mbInitFont ) + { +#ifndef REMOTE_APPSERVER + mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData) ); + mbInitFont = FALSE; +#else + ImplFontEntry* pFontEntry = mpFontEntry; + RemoteFont aFont; + + if ( pFontEntry->maFontSelData.mpFontData ) + { + aFont.maName = pFontEntry->maFontSelData.mpFontData->maName; + aFont.maStyleName = pFontEntry->maFontSelData.mpFontData->maStyleName; + } + else + { + aFont.maName = pFontEntry->maFontSelData.maName; + aFont.maStyleName = pFontEntry->maFontSelData.maStyleName; + } + aFont.mnWidth = pFontEntry->maFontSelData.mnWidth; + aFont.mnHeight = pFontEntry->maFontSelData.mnHeight; + aFont.meFamily = pFontEntry->maFontSelData.meFamily; + aFont.meCharSet = pFontEntry->maFontSelData.meCharSet; + aFont.meWidthType = pFontEntry->maFontSelData.meWidthType; + aFont.meWeight = pFontEntry->maFontSelData.meWeight; + aFont.meItalic = pFontEntry->maFontSelData.meItalic; + aFont.mePitch = pFontEntry->maFontSelData.mePitch; + aFont.mnOrientation = pFontEntry->maFontSelData.mnOrientation; + mpGraphics->SetFont( aFont ); + pFontEntry->mnSetFontFlags = 0; + mbInitFont = FALSE; +#endif + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitTextColor() +{ + DBG_TESTSOLARMUTEX(); + + if ( mbInitTextColor ) + { +#ifndef REMOTE_APPSERVER + mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) ); +#else + mpGraphics->SetTextColor( GetTextColor() ); +#endif + mbInitTextColor = FALSE; + } +} + +// ----------------------------------------------------------------------- + +int OutputDevice::ImplNewFont() +{ + DBG_TESTSOLARMUTEX(); + + if ( !mbNewFont ) + return TRUE; + + mbNewFont = FALSE; + +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return FALSE; + } + SalGraphics* pGraphics = mpGraphics; +#else + // Da wegen Clipping hier NULL zurueckkommen kann, koennen wir nicht + // den Rueckgabewert nehmen + ImplGetServerGraphics(); + ImplServerGraphics* pGraphics = mpGraphics; +#endif + + // Groesse umrechnen + Size aSize = ImplLogicToDevicePixel( maFont.GetSize() ); + if ( !aSize.Height() ) + { + // Nur dann Defaultgroesse setzen, wenn Fonthoehe auch in logischen + // Koordinaaten 0 ist + if ( maFont.GetSize().Height() ) + aSize.Height() = 1; + else + aSize.Height() = (12*mnDPIY)/72; + } + + // Neuen Font besorgen + ImplFontEntry* pOldEntry = mpFontEntry; + mpFontEntry = mpFontCache->Get( mpFontList, maFont, aSize ); + ImplFontEntry* pFontEntry = mpFontEntry; + + // Feststellen, ob Font neu selektiert werden muss + if ( pFontEntry != pOldEntry ) + mbInitFont = TRUE; + + // these two may be filled in remote version + ImplKernPairData* pKernPairs = NULL; + long nKernPairs = 0; + // Wenn Font nicht initialisiert ist, dann sofort selektieren + if ( !pFontEntry->mbInit ) + { + ImplInitFont(); + + // und die Font-Daten besorgen + if ( pGraphics ) + { + pFontEntry->mbInit = TRUE; + + pFontEntry->maMetric.mnWidth = pFontEntry->maFontSelData.mnWidth; + pFontEntry->maMetric.meFamily = pFontEntry->maFontSelData.meFamily; + pFontEntry->maMetric.meCharSet = pFontEntry->maFontSelData.meCharSet; + pFontEntry->maMetric.meWeight = pFontEntry->maFontSelData.meWeight; + pFontEntry->maMetric.meItalic = pFontEntry->maFontSelData.meItalic; + pFontEntry->maMetric.mePitch = pFontEntry->maFontSelData.mePitch; + pFontEntry->maMetric.mnOrientation = pFontEntry->maFontSelData.mnOrientation; + if ( pFontEntry->maFontSelData.mpFontData ) + { + pFontEntry->maMetric.meType = pFontEntry->maFontSelData.mpFontData->meType; + pFontEntry->maMetric.maName = pFontEntry->maFontSelData.mpFontData->maName; + pFontEntry->maMetric.maStyleName= pFontEntry->maFontSelData.mpFontData->maStyleName; + pFontEntry->maMetric.mbDevice = pFontEntry->maFontSelData.mpFontData->mbDevice; + } + else + { + pFontEntry->maMetric.meType = TYPE_DONTKNOW; + pFontEntry->maMetric.maName = pFontEntry->maFontSelData.maName.GetToken( 0 ); + pFontEntry->maMetric.maStyleName= pFontEntry->maFontSelData.maStyleName; + pFontEntry->maMetric.mbDevice = FALSE; + } + pFontEntry->maMetric.mnSuperscriptSize = 0; + pFontEntry->maMetric.mnSuperscriptOffset = 0; + pFontEntry->maMetric.mnSubscriptSize = 0; + pFontEntry->maMetric.mnSubscriptOffset = 0; + pFontEntry->maMetric.mnUnderlineSize = 0; + pFontEntry->maMetric.mnUnderlineOffset = 0; + pFontEntry->maMetric.mnBUnderlineSize = 0; + pFontEntry->maMetric.mnBUnderlineOffset = 0; + pFontEntry->maMetric.mnDUnderlineSize = 0; + pFontEntry->maMetric.mnDUnderlineOffset1 = 0; + pFontEntry->maMetric.mnDUnderlineOffset2 = 0; + pFontEntry->maMetric.mnWUnderlineSize = 0; + pFontEntry->maMetric.mnWUnderlineOffset = 0; + pFontEntry->maMetric.mnStrikeoutSize = 0; + pFontEntry->maMetric.mnStrikeoutOffset = 0; + pFontEntry->maMetric.mnBStrikeoutSize = 0; + pFontEntry->maMetric.mnBStrikeoutOffset = 0; + pFontEntry->maMetric.mnDStrikeoutSize = 0; + pFontEntry->maMetric.mnDStrikeoutOffset1 = 0; + pFontEntry->maMetric.mnDStrikeoutOffset2 = 0; +#ifndef REMOTE_APPSERVER + pGraphics->GetFontMetric( &(pFontEntry->maMetric) ); + pFontEntry->mnWidthFactor = pGraphics->GetCharWidth( 0, CHARCACHE_STD-1, pFontEntry->maWidthAry ); +#else + long nFactor = 0; + + pGraphics->GetFontMetric( + pFontEntry->maMetric, + nFactor, 0, CHARCACHE_STD-1, pFontEntry->maWidthAry, + maFont.IsKerning(), &pKernPairs, nKernPairs + ); + pFontEntry->mnWidthFactor = nFactor; +#endif + if ( !pFontEntry->mnWidthFactor ) + { + memset( pFontEntry->maWidthAry, 0, sizeof(long)*(CHARCACHE_STD-1) ); + pFontEntry->mnWidthFactor = 1; + } + + pFontEntry->mbFixedFont = pFontEntry->maMetric.mePitch == PITCH_FIXED; + pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent+pFontEntry->maMetric.mnDescent; + pFontEntry->mbInitKernPairs = FALSE; + pFontEntry->mnKernPairs = nKernPairs; + + if ( pFontEntry->maFontSelData.mnOrientation && !pFontEntry->maMetric.mnOrientation && + (meOutDevType != OUTDEV_PRINTER) ) + { + pFontEntry->mnOwnOrientation = pFontEntry->maFontSelData.mnOrientation; + pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation; + } + else + pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation; + } + } + + // Wenn Kerning gewuenscht ist, die Kerning-Werte ermitteln + if ( maFont.IsKerning() ) + { + ImplInitKerningPairs( pKernPairs, nKernPairs ); + mbKerning = (pFontEntry->mnKernPairs) != 0; + } + else + mbKerning = FALSE; + + // Je nach TextAlign den TextOffset berechnen + TextAlign eAlign = maFont.GetAlign(); + if ( eAlign == ALIGN_BASELINE ) + { + mnTextOffX = 0; + mnTextOffY = 0; + } + else if ( eAlign == ALIGN_TOP ) + { + long nOrientation = pFontEntry->mnOrientation; + if ( nOrientation ) + { + double nRad = ((double)nOrientation * F_2PI) / 3600.0; + long nOff = pFontEntry->maMetric.mnAscent; + mnTextOffX = (long)(nOff * sin( nRad )); + mnTextOffY = (long)(nOff * cos( nRad )); + } + else + { + mnTextOffX = 0; + mnTextOffY = pFontEntry->maMetric.mnAscent; + } + } + else // eAlign == ALIGN_BOTTOM + { + long nOrientation = pFontEntry->mnOrientation; + if ( nOrientation ) + { + double nRad = ((double)nOrientation * F_2PI) / 3600.0; + long nOff = pFontEntry->maMetric.mnDescent; + mnTextOffX = (long)(nOff - (nOff * sin( nRad ))); + nOff = -nOff; + mnTextOffY = (long)(nOff * cos( nRad )); + } + else + { + mnTextOffX = 0; + mnTextOffY = -pFontEntry->maMetric.mnDescent; + } + } + + mbTextLines = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) || + ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW)); + mbTextSpecial = maFont.IsShadow() || maFont.IsOutline(); + + if ( pOldEntry ) + mpFontCache->Release( pOldEntry ); + + return TRUE; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplGetCharWidth( sal_Unicode c ) const +{ + USHORT nChar = (USHORT)c; + if ( nChar < CHARCACHE_STD ) + return mpFontEntry->maWidthAry[nChar]; + + ImplFontEntry* pFontEntry = mpFontEntry; + ImplWidthInfoData* pInfo; + ImplWidthInfoData* pWidthAry = pFontEntry->mpWidthAry; + USHORT nWidthCount = pFontEntry->mnWidthAryCount; + USHORT nInsIndex; + + if ( nWidthCount ) + { + USHORT nLow; + USHORT nHigh; + USHORT nMid; + USHORT nCompareChar; + + nLow = 0; + nHigh = nWidthCount-1; + do + { + nMid = (nLow+nHigh)/2; + pInfo = pWidthAry+nMid; + nCompareChar = pInfo->mnChar; + if ( nChar < nCompareChar ) + { + if ( !nMid ) + break; + nHigh = nMid-1; + } + else + { + if ( nChar > nCompareChar ) + nLow = nMid+1; + else + return pInfo->mnWidth; + } + } + while ( nLow <= nHigh ); + + if ( nChar > nCompareChar ) + nInsIndex = nMid+1; + else + nInsIndex = nMid; + } + else + { + pFontEntry->mnWidthArySize = WIDTHARY_INIT; + pFontEntry->mpWidthAry = new ImplWidthInfoData[pFontEntry->mnWidthArySize]; + pWidthAry = pFontEntry->mpWidthAry; + nInsIndex = 0; + } + + if ( nWidthCount == pFontEntry->mnWidthArySize ) + { + USHORT nOldSize = pFontEntry->mnWidthArySize; + pFontEntry->mnWidthArySize += WIDTHARY_RESIZE; + pFontEntry->mpWidthAry = new ImplWidthInfoData[pFontEntry->mnWidthArySize]; + memcpy( pFontEntry->mpWidthAry, pWidthAry, nOldSize*sizeof(ImplWidthInfoData) ); + delete pWidthAry; + pWidthAry = pFontEntry->mpWidthAry; + } + + // Um die Zeichenbreite zu ermitteln, brauchen wir einen Graphics und der + // Font muss natuerlich auch selektiert sein +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !((OutputDevice*)this)->ImplGetGraphics() ) + return 0; + } +#else + // Da wegen Clipping hier NULL zurueckkommen kann, koennen wir nicht + // den Rueckgabewert nehmen + ((OutputDevice*)this)->ImplGetServerGraphics(); +#endif + + if ( mbNewFont ) + ((OutputDevice*)this)->ImplNewFont(); + if ( mbInitFont ) + ((OutputDevice*)this)->ImplInitFont(); + + long nWidth; +#ifndef REMOTE_APPSERVER + long nWidthFactor = mpGraphics->GetCharWidth( nChar, nChar, &nWidth ); +#else + long nWidthFactor = pFontEntry->mnWidthFactor; + mpGraphics->GetCharWidth( nChar, nChar, &nWidth ); +#endif + if ( !nWidthFactor ) + return 0; + + DBG_ASSERT( (nWidthFactor == pFontEntry->mnWidthFactor), + "OutputDevice::ImplGetCharWidth() - other WidthFactor" ); + + // Breite in Liste einfuegen und zurueckgeben + pInfo = pWidthAry+nInsIndex; + memmove( pInfo+1, pInfo, (nWidthCount-nInsIndex)*sizeof(ImplWidthInfoData) ); + pFontEntry->mnWidthAryCount++; + pInfo->mnChar = nChar; + pInfo->mnWidth = nWidth; + return nWidth; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitKerningPairs( ImplKernPairData* pKernPairs, long nKernPairs ) +{ + if ( mbNewFont ) + { + if ( !ImplNewFont() ) + return; + } + + ImplFontEntry* pFontEntry = mpFontEntry; + if ( !pFontEntry->mbInitKernPairs ) + { + if ( mbInitFont ) + ImplInitFont(); + pFontEntry->mbInitKernPairs = TRUE; +#ifndef REMOTE_APPSERVER + pFontEntry->mnKernPairs = mpGraphics->GetKernPairs( 0, NULL ); + if ( pFontEntry->mnKernPairs ) + { + ImplKernInfoData* pKernInfo = new ImplKernInfoData; + pKernPairs = new ImplKernPairData[pFontEntry->mnKernPairs]; + memset( pKernPairs, 0, sizeof(ImplKernPairData)*pFontEntry->mnKernPairs ); + pFontEntry->mnKernPairs = mpGraphics->GetKernPairs( pFontEntry->mnKernPairs, pKernPairs ); + pFontEntry->mpKernInfo = pKernInfo; + pFontEntry->mpKernPairs = pKernPairs; + + // Wir akzeptieren erstmal nur max. 65535-Paare + USHORT nMaxKernPairs; + if ( pFontEntry->mnKernPairs > 0xFFFF ) + nMaxKernPairs = 0xFFFF; + else + nMaxKernPairs = (USHORT)pFontEntry->mnKernPairs; + memset( pKernInfo->maFirstAry, 0xFF, sizeof( pKernInfo->maFirstAry ) ); + memset( pKernInfo->maLastAry, 0, sizeof( pKernInfo->maLastAry ) ); + for ( USHORT i = 0; i < nMaxKernPairs; i++ ) + { + USHORT nFirst = pKernPairs[i].mnChar1; + if ( nFirst < 0xFF ) + { + if ( i < pKernInfo->maFirstAry[nFirst] ) + pKernInfo->maFirstAry[nFirst] = i; + pKernInfo->maLastAry[nFirst] = i; + } + } + } +#else + // Wir arbeiten erstmal nur mit USHORT + if( ! pKernPairs ) + nKernPairs = mpGraphics->GetKernPairs( &pKernPairs ); + if ( nKernPairs ) + { + ImplKernInfoData* pKernInfo = new ImplKernInfoData; + pFontEntry->mpKernInfo = pKernInfo; + pFontEntry->mpKernPairs = pKernPairs; + + // Wir akzeptieren erstmal nur max. 65535-Paare + USHORT nMaxKernPairs; + if( ! pFontEntry->mnKernPairs ) + pFontEntry->mnKernPairs = nKernPairs; + if ( pFontEntry->mnKernPairs > 0xFFFF ) + nMaxKernPairs = 0xFFFF; + else + nMaxKernPairs = (USHORT)pFontEntry->mnKernPairs; + memset( pKernInfo->maFirstAry, 0xFF, sizeof( pKernInfo->maFirstAry ) ); + memset( pKernInfo->maLastAry, 0, sizeof( pKernInfo->maLastAry ) ); + for ( USHORT i = 0; i < nMaxKernPairs; i++ ) + { + USHORT nFirst = pKernPairs[i].mnChar1; + if ( nFirst < 0xFF ) + { + if ( i < pKernInfo->maFirstAry[nFirst] ) + pKernInfo->maFirstAry[nFirst] = i; + pKernInfo->maLastAry[nFirst] = i; + } + } + } +#endif + } +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplCalcKerning( const sal_Unicode* pStr, xub_StrLen nLen, + long* pDXAry, xub_StrLen nAryLen ) const +{ + if ( !nLen ) + return 0; + + ImplFontEntry* pEntry = mpFontEntry; + ImplKernPairData* pKernPairs = pEntry->mpKernPairs; + ImplKernInfoData* pKernInfo = pEntry->mpKernInfo; + long nWidth = 0; + +#ifdef DBG_UTIL + { + ImplKernPairData aTestPair; +#ifdef __LITTLEENDIAN + ULONG nTestComp = ((ULONG)((USHORT)0xAABB) << 16) | (USHORT)0xCCDD; +#else + ULONG nTestComp = ((ULONG)((USHORT)0xCCDD) << 16) | (USHORT)0xAABB; +#endif + aTestPair.mnChar1 = 0xCCDD; + aTestPair.mnChar2 = 0xAABB; + DBG_ASSERT( nTestComp == *((ULONG*)&aTestPair), "Code doesn't work in this Version" ); + } +#endif + + for ( USHORT i = 0; i < nLen-1; i++ ) + { + USHORT nIndex = (USHORT)(unsigned char)pStr[i]; + USHORT nFirst = pKernInfo->maFirstAry[nIndex]; + USHORT nLast = pKernInfo->maLastAry[nIndex]; +#ifdef __LITTLEENDIAN + ULONG nComp = ((ULONG)((USHORT)(unsigned char)pStr[i+1]) << 16) | nIndex; +#else + ULONG nComp = ((ULONG)nIndex << 16) | ((USHORT)(unsigned char)pStr[i+1]); +#endif + for ( USHORT j = nFirst; j <= nLast; j++ ) + { + if ( nComp == *((ULONG*)&(pKernPairs[j])) ) + { + long nAmount = pKernPairs[j].mnKern; + nWidth += nAmount; + if ( pDXAry ) + { + for ( USHORT n = i; n < nAryLen; n++ ) + pDXAry[n] += nAmount; + } + } + } + } + + return nWidth; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplGetTextWidth( const xub_Unicode* pStr, xub_StrLen nLen, + const long* pDX ) +{ + ImplFontEntry* pFontEntry = mpFontEntry; + long nFactor = pFontEntry->mnWidthFactor; + long nWidth = 0; + + if ( nLen ) + { + if ( pDX ) + { + if ( nLen > 1 ) + nWidth += pDX[nLen-2]; + nWidth += ImplGetCharWidth( pStr[nLen-1] ) / nFactor; + } + else + { + // Bei Fixed-Fonts reicht eine Multiplikation + if ( pFontEntry->mbFixedFont ) + nWidth = ImplGetCharWidth( 'A' ) * nLen; + else + { + const sal_Unicode* pTempStr = pStr; + xub_StrLen nTempLen = nLen; + while ( nTempLen ) + { + nWidth += ImplGetCharWidth( *pTempStr ); + nTempLen--; + pTempStr++; + } + } + nWidth /= nFactor; + + // Kerning beruecksichtigen + if ( mbKerning ) + nWidth += ImplCalcKerning( pStr, nLen, NULL, 0 ); + } + } + + return nWidth; +} + +// ----------------------------------------------------------------------- + +static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY, + short nOrientation ) +{ + double nRealOrientation = nOrientation*F_PI1800; + double nCos = cos( nRealOrientation ); + double nSin = sin( nRealOrientation ); + + // Translation... + long nX = rX-nOriginX; + long nY = rY-nOriginY; + + // Rotation... + rX = ((long) ( nCos*nX + nSin*nY )) + nOriginX; + rY = ((long) - ( nSin*nX - nCos*nY )) + nOriginY; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY, + long nX, long nY, long nWidth, long nHeight ) +{ + if ( mpFontEntry->mnOrientation ) + { + if ( !(mpFontEntry->mnOrientation % 900) ) + { + long nX2 = nX+nWidth; + long nY2 = nY+nHeight; + ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation ); + ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation ); + nWidth = nX2-nX; + nHeight = nY2-nY; + } + else + { + // Da Polygone kleiner gezeichnet werden + nHeight++; + nWidth++; + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + Polygon aPoly( aRect ); + aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation ); +#ifndef REMOTE_APPSERVER + ImplDrawPolygon( aPoly ); +#else + mpGraphics->DrawPolygon( aPoly ); +#endif + return; + } + } + +#ifndef REMOTE_APPSERVER + mpGraphics->DrawRect( nX, nY, nWidth, nHeight ); +#else + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + mpGraphics->DrawRect( aRect ); +#endif +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextBackground( long nX, long nY, + const xub_Unicode* pStr, xub_StrLen nLen, + const long* pDXAry ) +{ +#ifndef REMOTE_APPSERVER + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = TRUE; + } + mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) ); + mbInitFillColor = TRUE; + + ImplDrawTextRect( nX, nY, nX, nY-mpFontEntry->maMetric.mnAscent, + ImplGetTextWidth( pStr, nLen, pDXAry ), + mpFontEntry->mnLineHeight ); +#else + Color aOldLineColor = GetLineColor(); + Color aOldFillColor = GetFillColor(); + SetLineColor(); + SetFillColor( GetTextFillColor() ); + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); + ImplDrawTextRect( nX, nY, nX, nY-mpFontEntry->maMetric.mnAscent, + ImplGetTextWidth( pStr, nLen, pDXAry ), + mpFontEntry->mnLineHeight ); + SetLineColor( aOldLineColor ); + SetFillColor( aOldFillColor ); +#endif +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::ImplGetTextBoundRect( long nX, long nY, + const xub_Unicode* pStr, xub_StrLen nLen, + const long* pDXAry ) +{ + if( !nLen ) + return Rectangle(); + + if ( mbNewFont ) + ImplNewFont(); + + if ( mbInitFont ) + ImplInitFont(); + + long nBaseX = nX, nBaseY = nY; + long nWidth = ImplGetTextWidth( pStr, nLen, pDXAry ), nHeight = mpFontEntry->mnLineHeight; + + nY -= mpFontEntry->maMetric.mnAscent; + + if ( mpFontEntry->mnOrientation ) + { + if ( !(mpFontEntry->mnOrientation % 900) ) + { + long nX2 = nX+nWidth; + long nY2 = nY+nHeight; + ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation ); + ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation ); + nWidth = nX2-nX; + nHeight = nY2-nY; + } + else + { + // Da Polygone kleiner gezeichnet werden + nHeight++; + nWidth++; + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + Polygon aPoly( aRect ); + aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation ); + return aPoly.GetBoundRect(); + } + } + + return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplInitTextLineSize() +{ + ImplFontEntry* pFontEntry = mpFontEntry; + long nLineHeight; + long nLineHeight2; + long nBLineHeight; + long nBLineHeight2; + long n2LineHeight; + long n2LineDY; + long n2LineDY2; + long nUnderlineOffset; + long nStrikeoutOffset; + + nLineHeight = ((mpFontEntry->maMetric.mnDescent*25)+50) / 100; + if ( !nLineHeight ) + nLineHeight = 1; + nLineHeight2 = nLineHeight / 2; + if ( !nLineHeight2 ) + nLineHeight2 = 1; + + nBLineHeight = ((mpFontEntry->maMetric.mnDescent*50)+50) / 100; + if ( nBLineHeight == nLineHeight ) + nBLineHeight++; + nBLineHeight2 = nBLineHeight/2; + if ( !nBLineHeight2 ) + nBLineHeight2 = 1; + + n2LineHeight = ((mpFontEntry->maMetric.mnDescent*16)+50) / 100; + if ( !n2LineHeight ) + n2LineHeight = 1; + n2LineDY = n2LineHeight; + if ( n2LineDY <= 0 ) + n2LineDY = 1; + n2LineDY2 = n2LineDY/2; + if ( !n2LineDY2 ) + n2LineDY2 = 1; + + nUnderlineOffset = mpFontEntry->maMetric.mnDescent/2 + 1; + nStrikeoutOffset = -((mpFontEntry->maMetric.mnAscent-mpFontEntry->maMetric.mnLeading)/3); + + if ( !pFontEntry->maMetric.mnUnderlineSize ) + { + pFontEntry->maMetric.mnUnderlineSize = nLineHeight; + pFontEntry->maMetric.mnUnderlineOffset = nUnderlineOffset - nLineHeight2; + } + if ( !pFontEntry->maMetric.mnBUnderlineSize ) + { + pFontEntry->maMetric.mnBUnderlineSize = nBLineHeight; + pFontEntry->maMetric.mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2; + } + if ( !pFontEntry->maMetric.mnDUnderlineSize ) + { + pFontEntry->maMetric.mnDUnderlineSize = n2LineHeight; + pFontEntry->maMetric.mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight; + pFontEntry->maMetric.mnDUnderlineOffset2 = pFontEntry->maMetric.mnDUnderlineOffset1 + n2LineDY + n2LineHeight; + } + if ( !pFontEntry->maMetric.mnWUnderlineSize ) + { + if ( mpFontEntry->maMetric.mnDescent < 6 ) + { + if ( (mpFontEntry->maMetric.mnDescent == 1) || + (mpFontEntry->maMetric.mnDescent == 2) ) + pFontEntry->maMetric.mnWUnderlineSize = mpFontEntry->maMetric.mnDescent; + else + pFontEntry->maMetric.mnWUnderlineSize = 3; + } + else + pFontEntry->maMetric.mnWUnderlineSize = ((mpFontEntry->maMetric.mnDescent*50)+50) / 100; + pFontEntry->maMetric.mnWUnderlineOffset = nUnderlineOffset; + } + if ( !pFontEntry->maMetric.mnStrikeoutSize ) + { + pFontEntry->maMetric.mnStrikeoutSize = nLineHeight; + pFontEntry->maMetric.mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2; + } + if ( !pFontEntry->maMetric.mnBStrikeoutSize ) + { + pFontEntry->maMetric.mnBStrikeoutSize = nBLineHeight; + pFontEntry->maMetric.mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2; + } + if ( !pFontEntry->maMetric.mnDStrikeoutSize ) + { + pFontEntry->maMetric.mnDStrikeoutSize = n2LineHeight; + pFontEntry->maMetric.mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight; + pFontEntry->maMetric.mnDStrikeoutOffset2 = pFontEntry->maMetric.mnDStrikeoutOffset1 + n2LineDY + n2LineHeight; + } +} + +// ----------------------------------------------------------------------- + +static void ImplDrawWavePixel( long nOriginX, long nOriginY, + long nCurX, long nCurY, + short nOrientation, +#ifndef REMOTE_APPSERVER + SalGraphics* pGraphics, +#else + ImplServerGraphics* pGraphics, +#endif + BOOL bDrawPixAsRect, + long nPixWidth, long nPixHeight ) +{ + if ( nOrientation ) + ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation ); + + if ( bDrawPixAsRect ) + { +#ifndef REMOTE_APPSERVER + pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight ); +#else + Point aPos( nCurX, nCurY ); + Size aSize( nPixWidth, nPixHeight ); + Rectangle aRect( aPos, aSize ); + pGraphics->DrawRect( aRect ); +#endif + } + else + { +#ifndef REMOTE_APPSERVER + pGraphics->DrawPixel( nCurX, nCurY ); +#else + Point aPos( nCurX, nCurY ); + pGraphics->DrawPixel( aPos ); +#endif + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY, + long nStartX, long nStartY, + long nWidth, long nHeight, + long nLineWidth, short nOrientation, + const Color& rColor ) +{ + if ( !nHeight ) + return; + + // Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben + if ( (nLineWidth == 1) && (nHeight == 1) ) + { +#ifndef REMOTE_APPSERVER + mpGraphics->SetLineColor( ImplColorToSal( rColor ) ); + mbInitLineColor = TRUE; +#else + Color aOldLineColor = GetLineColor(); + SetLineColor( rColor ); + if ( mbInitLineColor ) + ImplInitLineColor(); +#endif + + long nEndX = nStartX+nWidth; + long nEndY = nStartY; + if ( nOrientation ) + { + ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation ); + ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation ); + } +#ifndef REMOTE_APPSERVER + mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY ); +#else + mpGraphics->DrawLine( Point( nStartX, nStartY ), Point( nEndX, nEndY ) ); +#endif + +#ifdef REMOTE_APPSERVER + SetLineColor( aOldLineColor ); +#endif + } + else + { + long nCurX = nStartX; + long nCurY = nStartY; + long nDiffX = 2; + long nDiffY = nHeight-1; + long nCount = nWidth; + long nOffY = -1; + long nFreq; + long i; + long nPixWidth; + long nPixHeight; + BOOL bDrawPixAsRect; +#ifdef REMOTE_APPSERVER + Color aOldLineColor = GetLineColor(); + Color aOldFillColor = GetFillColor(); +#endif + // Auf Druckern die Pixel per DrawRect() ausgeben + if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) ) + { +#ifndef REMOTE_APPSERVER + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = TRUE; + } + mpGraphics->SetFillColor( ImplColorToSal( rColor ) ); + mbInitFillColor = TRUE; +#else + SetLineColor(); + SetFillColor( rColor ); + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); +#endif + bDrawPixAsRect = TRUE; + nPixWidth = nLineWidth; + nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; + } + else + { +#ifndef REMOTE_APPSERVER + mpGraphics->SetLineColor( ImplColorToSal( rColor ) ); + mbInitLineColor = TRUE; +#else + Color aOldLineColor = GetLineColor(); + SetLineColor( rColor ); + if ( mbInitLineColor ) + ImplInitLineColor(); +#endif + nPixWidth = 1; + nPixHeight = 1; + bDrawPixAsRect = FALSE; + } + + if ( !nDiffY ) + { + while ( nWidth ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nWidth--; + } + } + else + { + nCurY += nDiffY; + nFreq = nCount / (nDiffX+nDiffY); + while ( nFreq-- ) + { + for( i = nDiffY; i; --i ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nCurY += nOffY; + } + for( i = nDiffX; i; --i ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + } + nOffY = -nOffY; + } + nFreq = nCount % (nDiffX+nDiffY); + if ( nFreq ) + { + for( i = nDiffY; i && nFreq; --i, --nFreq ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nCurY += nOffY; + } + for( i = nDiffX; i && nFreq; --i, --nFreq ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + } + } + } + +#ifdef REMOTE_APPSERVER + SetLineColor( aOldLineColor ); + SetFillColor( aOldFillColor ); +#endif + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextLine( long nBaseX, + long nX, long nY, long nWidth, + FontStrikeout eStrikeout, + FontUnderline eUnderline ) +{ + ImplFontEntry* pFontEntry = mpFontEntry; + Color aTextLineColor = GetTextLineColor(); + long nBaseY = nY; + long nLineHeight; + long nLinePos; + long nLinePos2; + long nLeft; + BOOL bNormalLines = TRUE; + + if ( !IsTextLineColor() ) + aTextLineColor = GetTextColor(); + + if ( (eUnderline == UNDERLINE_SMALLWAVE) || + (eUnderline == UNDERLINE_WAVE) || + (eUnderline == UNDERLINE_DOUBLEWAVE) || + (eUnderline == UNDERLINE_BOLDWAVE) ) + { + if ( !pFontEntry->maMetric.mnWUnderlineSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnWUnderlineSize; + if ( (eUnderline == UNDERLINE_SMALLWAVE) && + (nLineHeight > 3) ) + nLineHeight = 3; + long nLineWidth = (mnDPIX/300); + if ( !nLineWidth ) + nLineWidth = 1; + if ( eUnderline == UNDERLINE_BOLDWAVE ) + nLineWidth *= 2; + nLinePos = nY + pFontEntry->maMetric.mnWUnderlineOffset - (nLineHeight / 2); + long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; + if ( eUnderline == UNDERLINE_DOUBLEWAVE ) + { + long nOrgLineHeight = nLineHeight; + nLineHeight /= 3; + if ( nLineHeight < 2 ) + { + if ( nOrgLineHeight > 1 ) + nLineHeight = 2; + else + nLineHeight = 1; + } + long nLineDY = nOrgLineHeight-(nLineHeight*2); + if ( nLineDY < nLineWidthHeight ) + nLineDY = nLineWidthHeight; + long nLineDY2 = nLineDY/2; + if ( !nLineDY2 ) + nLineDY2 = 1; + nLinePos -= nLineWidthHeight-nLineDY2; + ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontEntry->mnOrientation, aTextLineColor ); + nLinePos += nLineWidthHeight+nLineDY; + ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontEntry->mnOrientation, aTextLineColor ); + } + else + { + nLinePos -= nLineWidthHeight/2; + ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontEntry->mnOrientation, aTextLineColor ); + } + + if ( (eStrikeout == STRIKEOUT_NONE) || + (eStrikeout == STRIKEOUT_DONTKNOW) ) + bNormalLines = FALSE; + } + + if ( bNormalLines && + ((eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X)) ) + { + BOOL bOldMap = IsMapModeEnabled(); + EnableMapMode( FALSE ); + Color aOldColor = GetTextColor(); + SetTextColor( aTextLineColor ); + ImplInitTextColor(); + xub_Unicode c; + if ( eStrikeout == STRIKEOUT_SLASH ) + c = '/'; + else /* ( eStrikeout == STRIKEOUT_X ) */ + c = 'X'; + // Strikeout-String zusammenbauen + XubString aStrikeoutText( c ); + aStrikeoutText += aStrikeoutText.GetChar( 0 ); + long nStrikeoutWidth = GetTextWidth( aStrikeoutText ); + long nChars = nWidth/(nStrikeoutWidth/2); + aStrikeoutText.Fill( (USHORT)(nChars+1), c ); + // String solange kuerzen, bis er nicht allzuweit uebersteht + long nMaxWidth = nStrikeoutWidth/4; + if ( nMaxWidth < 2 ) + nMaxWidth = 2; + nMaxWidth += nWidth; + long nFullStrikeoutWidth = GetTextWidth( aStrikeoutText ); + while ( (nFullStrikeoutWidth > nMaxWidth) && aStrikeoutText.Len() ) + { + aStrikeoutText.Erase( aStrikeoutText.Len()-1 ); + nFullStrikeoutWidth = GetTextWidth( aStrikeoutText ); + } + if ( mpFontEntry->mnOrientation ) + ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation ); + ImplDrawTextDirect( nX, nY, + aStrikeoutText.GetBuffer(), aStrikeoutText.Len(), + NULL, FALSE ); + SetTextColor( aOldColor ); + ImplInitTextColor(); + EnableMapMode( bOldMap ); + + if ( (eUnderline == UNDERLINE_NONE) || + (eUnderline == UNDERLINE_DONTKNOW) || + (eUnderline == UNDERLINE_SMALLWAVE) || + (eUnderline == UNDERLINE_WAVE) || + (eUnderline == UNDERLINE_DOUBLEWAVE) || + (eUnderline == UNDERLINE_BOLDWAVE) ) + bNormalLines = FALSE; + } + + if ( bNormalLines ) + { +#ifndef REMOTE_APPSERVER + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = TRUE; + } + mpGraphics->SetFillColor( ImplColorToSal( aTextLineColor ) ); + mbInitFillColor = TRUE; +#else + Color aOldLineColor = GetLineColor(); + Color aOldFillColor = GetFillColor(); + SetLineColor(); + SetFillColor( aTextLineColor ); + if ( mbInitLineColor ) + ImplInitLineColor(); + if ( mbInitFillColor ) + ImplInitFillColor(); +#endif + + if ( eUnderline > UNDERLINE_LAST ) + eUnderline = UNDERLINE_SINGLE; + + if ( (eUnderline == UNDERLINE_SINGLE) || + (eUnderline == UNDERLINE_DOTTED) || + (eUnderline == UNDERLINE_DASH) || + (eUnderline == UNDERLINE_LONGDASH) || + (eUnderline == UNDERLINE_DASHDOT) || + (eUnderline == UNDERLINE_DASHDOTDOT) ) + { + if ( !pFontEntry->maMetric.mnUnderlineSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset; + } + else if ( (eUnderline == UNDERLINE_BOLD) || + (eUnderline == UNDERLINE_BOLDDOTTED) || + (eUnderline == UNDERLINE_BOLDDASH) || + (eUnderline == UNDERLINE_BOLDLONGDASH) || + (eUnderline == UNDERLINE_BOLDDASHDOT) || + (eUnderline == UNDERLINE_BOLDDASHDOTDOT) ) + { + if ( !pFontEntry->maMetric.mnBUnderlineSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnBUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset; + } + else if ( eUnderline == UNDERLINE_DOUBLE ) + { + if ( !pFontEntry->maMetric.mnDUnderlineSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnDUnderlineSize; + nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1; + nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2; + } + else + nLineHeight = 0; + + if ( nLineHeight ) + { + nLeft = nX; + + if ( (eUnderline == UNDERLINE_SINGLE) || + (eUnderline == UNDERLINE_BOLD) ) + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + else if ( eUnderline == UNDERLINE_DOUBLE ) + { + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); + } + else if ( (eUnderline == UNDERLINE_DOTTED) || + (eUnderline == UNDERLINE_BOLDDOTTED) ) + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nTempWidth = nDotWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempWidth > nEnd ) + nTempWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); + nLeft += nDotWidth*2; + } + } + else if ( (eUnderline == UNDERLINE_DASH) || + (eUnderline == UNDERLINE_LONGDASH) || + (eUnderline == UNDERLINE_BOLDDASH) || + (eUnderline == UNDERLINE_BOLDLONGDASH) ) + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nMinDashWidth; + long nMinSpaceWidth; + long nSpaceWidth; + long nDashWidth; + if ( (eUnderline == UNDERLINE_LONGDASH) || + (eUnderline == UNDERLINE_BOLDLONGDASH) ) + { + nMinDashWidth = nDotWidth*6; + nMinSpaceWidth = nDotWidth*2; + nDashWidth = 200; + nSpaceWidth = 100; + } + else + { + nMinDashWidth = nDotWidth*4; + nMinSpaceWidth = (nDotWidth*150)/100; + nDashWidth = 100; + nSpaceWidth = 50; + } + nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540; + nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540; + // DashWidth wird gegebenenfalls verbreitert, wenn + // die dicke der Linie im Verhaeltnis zur Laenge + // zu dick wird + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + if ( nSpaceWidth < nMinSpaceWidth ) + nSpaceWidth = nMinSpaceWidth; + long nTempWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempWidth > nEnd ) + nTempWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); + nLeft += nDashWidth+nSpaceWidth; + } + } + else if ( (eUnderline == UNDERLINE_DASHDOT) || + (eUnderline == UNDERLINE_BOLDDASHDOT) ) + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nDashWidth = ((100*mnDPIX)+1270)/2540; + long nMinDashWidth = nDotWidth*4; + // DashWidth wird gegebenenfalls verbreitert, wenn + // die dicke der Linie im Verhaeltnis zur Laenge + // zu dick wird + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + long nTempDotWidth = nDotWidth; + long nTempDashWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + if ( nLeft+nTempDashWidth > nEnd ) + nTempDashWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); + nLeft += nDashWidth+nDotWidth; + } + } + else if ( (eUnderline == UNDERLINE_DASHDOTDOT) || + (eUnderline == UNDERLINE_BOLDDASHDOTDOT) ) + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + long nDashWidth = ((100*mnDPIX)+1270)/2540; + long nMinDashWidth = nDotWidth*4; + // DashWidth wird gegebenenfalls verbreitert, wenn + // die dicke der Linie im Verhaeltnis zur Laenge + // zu dick wird + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + long nTempDotWidth = nDotWidth; + long nTempDashWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + if ( nLeft+nTempDashWidth > nEnd ) + nTempDashWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); + nLeft += nDashWidth+nDotWidth; + } + } + } + + if ( eStrikeout > STRIKEOUT_LAST ) + eStrikeout = STRIKEOUT_SINGLE; + + if ( eStrikeout == STRIKEOUT_SINGLE ) + { + if ( !pFontEntry->maMetric.mnStrikeoutSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnStrikeoutSize; + nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset; + } + else if ( eStrikeout == STRIKEOUT_BOLD ) + { + if ( !pFontEntry->maMetric.mnBStrikeoutSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize; + nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset; + } + else if ( eStrikeout == STRIKEOUT_DOUBLE ) + { + if ( !pFontEntry->maMetric.mnDStrikeoutSize ) + ImplInitTextLineSize(); + nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize; + nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1; + nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2; + } + else + nLineHeight = 0; + + if ( nLineHeight ) + { + nLeft = nX; + + if ( (eStrikeout == STRIKEOUT_SINGLE) || + (eStrikeout == STRIKEOUT_BOLD) ) + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + else if ( eStrikeout == STRIKEOUT_DOUBLE ) + { + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); + } + } + +#ifdef REMOTE_APPSERVER + SetLineColor( aOldLineColor ); + SetFillColor( aOldFillColor ); +#endif + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextLines( long nX, long nY, + const sal_Unicode* pStr, xub_StrLen nLen, + const long* pDXAry, + FontStrikeout eStrikeout, + FontUnderline eUnderline, + BOOL bWordLine ) +{ + if ( bWordLine ) + { + ::rtl::OUString aText( pStr, nLen ); + uno::Reference < text::XBreakIterator > xBI = vcl::unohelper::CreateBreakIterator(); + uno::Reference< linguistic::XHyphenator > xHyph; + text::LineBreakHyphenationOptions aHyphOptions( xHyph, 1 ); + text::LineBreakUserOptions aUserOptions; + + text::Boundary aBoundary = xBI->getWordBoundary( aText, 0, GetSettings().GetLocale(), text::WordType::ANYWORD_IGNOREWHITESPACES, TRUE ); + while ( ( aBoundary.startPos >= 0 ) && ( aBoundary.startPos < nLen ) ) + { + xub_StrLen nWordEnd = Max( (xub_StrLen)aBoundary.endPos, nLen ); + long nTempX = ImplGetTextWidth( pStr, aBoundary.startPos, pDXAry ); + long nWidth = ImplGetTextWidth( pStr+aBoundary.startPos, aBoundary.endPos-aBoundary.startPos, pDXAry ); + ImplDrawTextLine( nX, nX + nTempX, nY, nWidth, eStrikeout, eUnderline ); + aBoundary = xBI->nextWord( aText, aBoundary.endPos, GetSettings().GetLocale(), text::WordType::ANYWORD_IGNOREWHITESPACES ); + } + } + else + { + ImplDrawTextLine( nX, nX, nY, ImplGetTextWidth( pStr, nLen, pDXAry ), eStrikeout, eUnderline ); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, xub_Unicode c ) +{ + ImplDrawTextLines( nX, nY, &c, 1, NULL, STRIKEOUT_NONE, UNDERLINE_SINGLE, FALSE ); +} + +// ----------------------------------------------------------------------- + +BOOL OutputDevice::ImplDrawRotateText( long nX, long nY, + const xub_Unicode* pStr, xub_StrLen nLen, + const long* pDXAry ) +{ + if ( !mpOutDevData ) + ImplInitOutDevData(); + if ( !mpOutDevData->mpRotateDev ) + mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 ); + VirtualDevice* pVDev = mpOutDevData->mpRotateDev; + long nWidth = ImplGetTextWidth( pStr, nLen, pDXAry ); + long nHeight = mpFontEntry->mnLineHeight; + Size aSize( nWidth, nHeight ); + + if ( pVDev->SetOutputSizePixel( aSize ) ) + { + Font aFont( GetFont() ); + Bitmap aBmp; + long nOff; + + nX -= mnTextOffX; + nY -= mnTextOffY; + if ( GetTextAlign() == ALIGN_TOP ) + { + nOff = 0L; + nY += mpFontEntry->maMetric.mnAscent; + } + else if ( GetTextAlign() == ALIGN_BOTTOM ) + { + nOff = mpFontEntry->maMetric.mnAscent; + nY += -mpFontEntry->maMetric.mnDescent; + } + else + nOff = mpFontEntry->maMetric.mnAscent; + + aFont.SetShadow( FALSE ); + aFont.SetOutline( FALSE ); + aFont.SetOrientation( 0 ); + aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) ); + pVDev->SetFont( aFont ); + // Da Farben und Alignment noch im Font haengen, muessen diese jedesmal + // gesetzt werden + pVDev->SetTextAlign( ALIGN_TOP ); + pVDev->SetTextColor( Color( COL_BLACK ) ); + pVDev->SetTextFillColor(); + pVDev->ImplNewFont(); + pVDev->ImplInitFont(); + pVDev->ImplInitTextColor(); + pVDev->ImplDrawText( 0, 0, pStr, nLen, pDXAry ); + + aBmp = pVDev->GetBitmap( Point(), aSize ); + if ( !!aBmp && aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) ) + { + Point aTempPoint; + Polygon aPoly( Rectangle( aTempPoint, aSize ) ); + long nOldOffX = mnOutOffX; + long nOldOffY = mnOutOffY; + BOOL bOldMap = mbMap; + + aTempPoint.Y() = nOff; + aPoly.Rotate( aTempPoint, mpFontEntry->mnOwnOrientation ); + const Rectangle aBound( aPoly.GetBoundRect() ); + + mnOutOffX = 0L; + mnOutOffY = 0L; + mbMap = FALSE; + + DrawMask( Point( nX + aBound.Left(), + nY + aBound.Top() - mpFontEntry->maMetric.mnAscent ), + aBmp, GetTextColor() ); + + mnOutOffX = nOldOffX; + mnOutOffY = nOldOffY; + mbMap = bOldMap; + } + + return TRUE; + } + + return FALSE; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawTextDirect( long nX, long nY, + const xub_Unicode* pStr, xub_StrLen nLen, + const long* pDXAry, + BOOL bTextLines ) +{ + BOOL bDraw = FALSE; + ImplFontEntry* pFontEntry = mpFontEntry; + if ( pFontEntry->mnOwnOrientation ) + bDraw = ImplDrawRotateText( nX, nY, pStr, nLen, pDXAry ); + if ( !bDraw ) + { + if ( !pDXAry ) + { +#ifndef REMOTE_APPSERVER + if ( pFontEntry->mnSetFontFlags & SAL_SETFONT_USEDRAWTEXTARRAY ) + { + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + long nOffset = 0; + long aStackAry[128]; + long* pTempDXAry = (long*)ImplGetStackBuffer( sizeof(long)*(nLen-1), aStackAry, sizeof( aStackAry ) ); + for ( USHORT i = 0; i < nLen-1; i++ ) + { + nOffset += ImplGetCharWidth( pStr[i] ); + pTempDXAry[i] = nOffset / nFactor; + } + mpGraphics->DrawTextArray( nX, nY, pStr, nLen, pTempDXAry ); + ImplReleaseStackBuffer( pTempDXAry, aStackAry ); + } + else +#endif + mpGraphics->DrawText( nX, nY, pStr, nLen ); + } + else + { +#ifndef REMOTE_APPSERVER + if ( pFontEntry->mnSetFontFlags & SAL_SETFONT_USEDRAWTEXT ) + { + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + long nOffset = 0; + long nDiff; + long nTempX = nX; + const sal_Unicode* pTempStr = pStr; + xub_StrLen nCombineChars = 1; + for ( xub_StrLen i = 0; i < nLen-1; i++ ) + { + nOffset += ImplGetCharWidth( pStr[i] ); + nDiff = (nOffset/nFactor) - pDXAry[i]; + if ( (nDiff < -1) || (nDiff > 0) ) + { + mpGraphics->DrawText( nTempX, nY, pTempStr, nCombineChars ); + nTempX = nX+pDXAry[i]; + nOffset = pDXAry[i]*nFactor; + pTempStr += nCombineChars; + nCombineChars = 1; + } + else + nCombineChars++; + } + mpGraphics->DrawText( nTempX, nY, pTempStr, nCombineChars ); + } + else +#endif + mpGraphics->DrawTextArray( nX, nY, pStr, nLen, pDXAry ); + } + + if ( bTextLines ) + { + ImplDrawTextLines( nX, nY, pStr, nLen, pDXAry, + maFont.GetStrikeout(), + maFont.GetUnderline(), + maFont.IsWordLineMode() ); + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawSpecialText( long nX, long nY, + const xub_Unicode* pStr, xub_StrLen nLen, + const long* pDXAry ) +{ + Color aOldColor = GetTextColor(); + Color aOldTextLineColor = GetTextLineColor(); + + if ( maFont.IsShadow() ) + { + long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24); + if ( maFont.IsOutline() ) + nOff++; + SetTextLineColor(); + if ( GetTextColor().GetColor() == COL_BLACK ) + SetTextColor( Color( COL_LIGHTGRAY ) ); + else + SetTextColor( Color( COL_BLACK ) ); + ImplInitTextColor(); + ImplDrawTextDirect( nX+nOff, nY+nOff, pStr, nLen, pDXAry, mbTextLines ); + SetTextColor( aOldColor ); + SetTextLineColor( aOldTextLineColor ); + ImplInitTextColor(); + + if ( !maFont.IsOutline() ) + ImplDrawTextDirect( nX, nY, pStr, nLen, pDXAry, mbTextLines ); + } + + if ( maFont.IsOutline() ) + { + ImplDrawTextDirect( nX-1, nY+1, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX, nY+1, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX+1, nY+1, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX-1, nY, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX+1, nY, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX-1, nY-1, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX, nY-1, pStr, nLen, pDXAry, mbTextLines ); + ImplDrawTextDirect( nX+1, nY-1, pStr, nLen, pDXAry, mbTextLines ); + + SetTextColor( Color( COL_WHITE ) ); + SetTextLineColor( Color( COL_WHITE ) ); + ImplInitTextColor(); + ImplDrawTextDirect( nX, nY, pStr, nLen, pDXAry, mbTextLines ); + SetTextColor( aOldColor ); + SetTextLineColor( aOldTextLineColor ); + ImplInitTextColor(); + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplDrawText( long nX, long nY, + const xub_Unicode* pStr, xub_StrLen nLen, const long* pDXAry ) +{ + nX += mnTextOffX; + nY += mnTextOffY; + + if ( IsTextFillColor() ) + ImplDrawTextBackground( nX, nY, pStr, nLen, pDXAry ); + + if ( mbTextSpecial ) + ImplDrawSpecialText( nX, nY, pStr, nLen, pDXAry ); + else + ImplDrawTextDirect( nX, nY, pStr, nLen, pDXAry, mbTextLines ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::ImplFillDXAry( long* pDXAry, + const xub_Unicode* pStr, xub_StrLen nLen, long nWidth ) +{ + ImplFontEntry* pFontEntry = mpFontEntry; + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + + // Breiten-Array fuer errechnete Werte mit den Breiten der einzelnen + // Character fuellen + xub_StrLen i; + long nSum = 0; + for ( i = 0; i < nLen; i++ ) + { + // Characterbreiten ueber Array holen + nSum += ImplGetCharWidth( pStr[i] ); + pDXAry[i] = nSum / nFactor; + } + nSum /= nFactor; + + // Differenz zwischen Soll- und Ist-Laenge errechnen + // Zusaetzliche Pixel per Character errechnen + // Anzahl der zusaetzlich verbliebenen Pixel errechnen + long nDelta = (long)nWidth - nSum; + long nDeltaPerChar = 0; + long nDeltaRest = 0; + if ( nLen > 1 ) + { + nDeltaPerChar = nDelta / (long)(nLen-1); + nDeltaRest = nDelta % (long)(nLen-1); + } + long nDeltaRestAbs = Abs( nDeltaRest ); + + long nErrorSum = nDeltaRestAbs; + long nDeltaSum = 0; + for ( i = 0; i < nLen-1; i++, nErrorSum += nDeltaRestAbs ) + { + nDeltaSum += nDeltaPerChar; + if ( nErrorSum >= nLen-1 ) + { + nErrorSum -= nLen-1; + if ( nDeltaRest > 0 ) + nDeltaSum++; + else if ( nDeltaRest < 0 ) + nDeltaSum--; + } + pDXAry[i] += nDeltaSum; + } +} + +// ----------------------------------------------------------------------- + +long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, + long nWidth, const XubString& rStr, + USHORT nStyle ) const +{ + DBG_ASSERT( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" ); + + long nMaxLineWidth = 0; + rLineInfo.Clear(); + if ( rStr.Len() && ( nWidth > 0 ) ) + { + ::rtl::OUString aText( rStr ); + uno::Reference < text::XBreakIterator > xBI; + uno::Reference< linguistic::XHyphenator > xHyph; + text::LineBreakHyphenationOptions aHyphOptions( xHyph, 1 ); + text::LineBreakUserOptions aUserOptions; + + USHORT nPos = 0; + USHORT nLen = rStr.Len(); + while ( nPos < nLen ) + { + xub_StrLen nBreakPos = nPos; + while ( ( nBreakPos < nLen ) && ( rStr.GetChar( nBreakPos ) != _CR ) && ( rStr.GetChar( nBreakPos ) != _LF ) ) + nBreakPos++; + + long nLineWidth = GetTextWidth( rStr, nPos, nBreakPos-nPos ); + if ( ( nLineWidth > nWidth ) && ( nStyle & TEXT_DRAW_WORDBREAK ) ) + { + if ( !xBI.is() ) + xBI = vcl::unohelper::CreateBreakIterator(); + + xub_StrLen nSoftBreak = GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos ); + DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" ); + text::LineBreakResults aLBR = xBI->getLineBreak( aText, nSoftBreak, GetSettings().GetLocale(), nPos, aHyphOptions, aUserOptions ); + nBreakPos = aLBR.breakIndex; + if ( nBreakPos <= nPos ) + nBreakPos = nSoftBreak; + nLineWidth = GetTextWidth( rStr, nPos, nBreakPos-nPos ); + } + + if ( nLineWidth > nMaxLineWidth ) + nMaxLineWidth = nLineWidth; + + if ( ( rStr.GetChar( nBreakPos ) == _CR ) || ( rStr.GetChar( nBreakPos ) == _LF ) ) + { + nBreakPos++; + // CR/LF? + if ( ( nPos < nLen ) && ( rStr.GetChar( nBreakPos ) == _LF ) && ( rStr.GetChar( nBreakPos-1 ) == _CR ) ) + nBreakPos++; + } + + if ( nBreakPos == nPos ) + nBreakPos++; + + rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) ); + + nPos = nBreakPos; + } + } + + return nMaxLineWidth; +} + +// ======================================================================= + +void OutputDevice::SetFont( const Font& rNewFont ) +{ + DBG_TRACE( "OutputDevice::SetFont()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_CHKOBJ( &rNewFont, Font, NULL ); + + Font aFont( rNewFont ); + + if ( mnDrawMode & (DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | + DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | + DRAWMODE_GHOSTEDFILL) ) + { + Color aTextColor( aFont.GetColor() ); + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aTextColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aTextColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const UINT8 cLum = aTextColor.GetLuminance(); + aTextColor = Color( cLum, cLum, cLum ); + } + if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) + { + aTextColor = Color( (aTextColor.GetRed() >> 1 ) | 0x80, + (aTextColor.GetGreen() >> 1 ) | 0x80, + (aTextColor.GetBlue() >> 1 ) | 0x80 ); + } + aFont.SetColor( aTextColor ); + + BOOL bTransFill = aFont.IsTransparent(); + if ( !bTransFill ) + { + Color aTextFillColor( aFont.GetFillColor() ); + if ( mnDrawMode & DRAWMODE_BLACKFILL ) + aTextFillColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITEFILL ) + aTextFillColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYFILL ) + { + const UINT8 cLum = aTextFillColor.GetLuminance(); + aTextFillColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DRAWMODE_NOFILL ) + { + aTextFillColor = Color( COL_TRANSPARENT ); + bTransFill = TRUE; + } + if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) ) + { + aTextFillColor = Color( (aTextFillColor.GetRed() >> 1) | 0x80, + (aTextFillColor.GetGreen() >> 1) | 0x80, + (aTextFillColor.GetBlue() >> 1) | 0x80 ); + } + aFont.SetFillColor( aTextFillColor ); + } + } + + if ( mpMetaFile ) + { + const Color& rTextFillColor = aFont.GetFillColor(); + + mpMetaFile->AddAction( new MetaFontAction( aFont ) ); + mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlign() ) ); + mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) ); + mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) ); + } + + if ( !maFont.IsSameInstance( aFont ) ) + { + if ( maFont.GetColor() != aFont.GetColor() ) + mbInitTextColor = TRUE; + maFont = aFont; + mbNewFont = TRUE; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextColor( const Color& rColor ) +{ + DBG_TRACE( "OutputDevice::SetTextColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | + DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const UINT8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + + if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextColorAction( aColor ) ); + + if ( maFont.GetColor() != aColor ) + { + maFont.SetColor( aColor ); + mbInitTextColor = TRUE; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextFillColor() +{ + DBG_TRACE( "OutputDevice::SetTextFillColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), FALSE ) ); + + if ( maFont.GetColor() != Color( COL_TRANSPARENT ) ) + maFont.SetFillColor( Color( COL_TRANSPARENT ) ); + if ( !maFont.IsTransparent() ) + maFont.SetTransparent( TRUE ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextFillColor( const Color& rColor ) +{ + DBG_TRACE( "OutputDevice::SetTextFillColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + BOOL bTransFill = ImplIsColorTransparent( aColor ); + + if ( !bTransFill ) + { + if ( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | + DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | + DRAWMODE_GHOSTEDFILL ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKFILL ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITEFILL ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYFILL ) + { + const UINT8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DRAWMODE_NOFILL ) + { + aColor = Color( COL_TRANSPARENT ); + bTransFill = TRUE; + } + if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, TRUE ) ); + + if ( maFont.GetFillColor() != aColor ) + maFont.SetFillColor( aColor ); + if ( maFont.IsTransparent() != bTransFill ) + maFont.SetTransparent( bTransFill ); +} + +// ----------------------------------------------------------------------- + +Color OutputDevice::GetTextFillColor() const +{ + if ( maFont.IsTransparent() ) + return Color( COL_TRANSPARENT ); + else + return maFont.GetFillColor(); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextLineColor() +{ + DBG_TRACE( "OutputDevice::SetTextLineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), FALSE ) ); + + maTextLineColor = Color( COL_TRANSPARENT ); +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextLineColor( const Color& rColor ) +{ + DBG_TRACE( "OutputDevice::SetTextLineColor()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Color aColor( rColor ); + + if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | + DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT ) ) + { + if ( mnDrawMode & DRAWMODE_BLACKTEXT ) + aColor = Color( COL_BLACK ); + else if ( mnDrawMode & DRAWMODE_WHITETEXT ) + aColor = Color( COL_WHITE ); + else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) + { + const UINT8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + + if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) + { + aColor = Color( (aColor.GetRed() >> 1) | 0x80, + (aColor.GetGreen() >> 1) | 0x80, + (aColor.GetBlue() >> 1) | 0x80 ); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, TRUE ) ); + + maTextLineColor = aColor; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::SetTextAlign( TextAlign eAlign ) +{ + DBG_TRACE( "OutputDevice::SetTextAlign()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) ); + + if ( maFont.GetAlign() != eAlign ) + { + maFont.SetAlign( eAlign ); + mbNewFont = TRUE; + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawTextLine( const Point& rPos, long nWidth, + FontStrikeout eStrikeout, + FontUnderline eUnderline ) +{ + DBG_TRACE( "OutputDevice::DrawTextLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline ) ); + + if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) && + ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) ) + return; + + if ( !IsDeviceOutputNecessary() ) + return; + +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; +#else + if ( !ImplGetServerGraphics() ) + return; +#endif + + if ( mbNewFont ) + { + if ( !ImplNewFont() ) + return; + } + + Point aPos = ImplLogicToDevicePixel( rPos ); + nWidth = ImplLogicWidthToDevicePixel( nWidth ); + aPos.X() += mnTextOffX; + aPos.Y() += mnTextOffY; + ImplDrawTextLine( aPos.X(), aPos.X(), aPos.Y(), nWidth, eStrikeout, eUnderline ); +} + +// ------------------------------------------------------------------------ + +void OutputDevice::DrawWaveLine( const Point& rStartPos, const Point& rEndPos, + USHORT nStyle ) +{ + DBG_TRACE( "OutputDevice::DrawWaveLine()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + +#ifndef REMOTE_APPSERVER + if ( !IsDeviceOutputNecessary() ) + return; + + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; + + Point aStartPt = ImplLogicToDevicePixel( rStartPos ); + Point aEndPt = ImplLogicToDevicePixel( rEndPos ); + long nStartX = aStartPt.X(); + long nStartY = aStartPt.Y(); + long nEndX = aEndPt.X(); + long nEndY = aEndPt.Y(); + short nOrientation = 0; + + if ( (nStartY != nEndY) || (nStartX > nEndX) ) + { + long nDX = nEndX - nStartX; + double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) ); + nO /= F_PI1800; + nOrientation = (short)nO; + ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation ); + } + + long nWaveHeight; + if ( nStyle == WAVE_NORMAL ) + { + nWaveHeight = 3; + nStartY++; + nEndY++; + } + else if( nStyle == WAVE_SMALL ) + { + nWaveHeight = 2; + nStartY++; + nEndY++; + } + else // WAVE_FLAT + nWaveHeight = 1; + + ImplDrawWaveLine( nStartX, nStartY, nStartX, nStartY, + nEndX-nStartX, nWaveHeight, 1, + nOrientation, GetLineColor() ); +#else + ImplServerGraphics* pGraphics = ImplGetServerGraphics(); + if ( pGraphics ) + { + if ( mbInitLineColor ) + ImplInitLineColor(); + + Point aPos1 = ImplLogicToDevicePixel( rStartPos ); + Point aPos2 = ImplLogicToDevicePixel( rEndPos ); + pGraphics->DrawWaveLine( aPos1, aPos2, nStyle ); + } +#endif +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawText( const Point& rStartPt, const XubString& rStr, + xub_StrLen nIndex, xub_StrLen nLen ) +{ + DBG_TRACE( "OutputDevice::DrawText()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) ); + + if ( !IsDeviceOutputNecessary() ) + return; + + // String-Laenge fuer die Ermittlung der Groesse setzen + if ( (ULONG)nLen+nIndex > rStr.Len() ) + { + if ( nIndex < rStr.Len() ) + nLen = rStr.Len()-nIndex; + else + nLen = 0; + } + + // Ist die Ausgabe leer, dann mache nichts + if ( !nLen ) + return; + +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; +#else + if ( !ImplGetServerGraphics() ) + return; +#endif + + if ( mbNewFont ) + ImplNewFont(); + if ( mbInitFont ) + ImplInitFont(); + if ( mbInitTextColor ) + ImplInitTextColor(); + + Point aStartPt = ImplLogicToDevicePixel( rStartPt ); + + // Pointer auf den String-Buffer setzen und um den Index korrigieren + const sal_Unicode* pStr = rStr.GetBuffer(); + pStr += nIndex; + + if ( mbKerning ) + { + ImplFontEntry* pFontEntry = mpFontEntry; + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + USHORT i; + + // DX-Array berechnen + long nOffset = 0; + long aStackAry[128]; + long* pDXAry = (long*)ImplGetStackBuffer( sizeof(long)*(nLen-1), aStackAry, sizeof( aStackAry ) ); + for ( i = 0; i < nLen-1; i++ ) + { + nOffset += pCharWidthAry[(unsigned char)pStr[i]]; + pDXAry[i] = nOffset / nFactor; + } + ImplCalcKerning( pStr, nLen, pDXAry, nLen-1 ); + ImplDrawText( aStartPt.X(), aStartPt.Y(), pStr, nLen, pDXAry ); + ImplReleaseStackBuffer( pDXAry, aStackAry ); + } + else + ImplDrawText( aStartPt.X(), aStartPt.Y(), pStr, nLen, NULL ); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetTextWidth( const XubString& rStr, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_TRACE( "OutputDevice::GetTextWidth()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return 0; + } + + ImplFontEntry* pFontEntry = mpFontEntry; + long nWidth = 0; + + if ( nIndex < rStr.Len() ) + { + // String-Laenge fuer die Ermittlung der Groesse setzen + if ( (ULONG)nLen+nIndex > rStr.Len() ) + nLen = rStr.Len()-nIndex; + + if ( nLen ) + { + long* pCharWidthAry = pFontEntry->maWidthAry; + + // Bei Fixed-Fonts reicht eine Multiplikation + if ( pFontEntry->mbFixedFont ) + { + nWidth = pCharWidthAry['A'] * nLen; + nWidth /= pFontEntry->mnWidthFactor; + } + else + { + const sal_Unicode* pStr = rStr.GetBuffer(); + const sal_Unicode* pTempStr; + USHORT nTempLen; + pStr += nIndex; + pTempStr = pStr; + nTempLen = nLen; + while ( nTempLen ) + { + nWidth += ImplGetCharWidth( *pTempStr ); + nTempLen--; + pTempStr++; + } + nWidth /= pFontEntry->mnWidthFactor; + + // Kerning beruecksichtigen (tun wir nur bei Fonts ohne feste Breite) + if ( mbKerning ) + nWidth += ImplCalcKerning( pStr, nLen, NULL, 0 ); + } + } + } + + if ( mbMap ) + nWidth = ImplDevicePixelToLogicWidth( nWidth ); + + return nWidth; +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetTextHeight() const +{ + DBG_TRACE( "OutputDevice::GetTextHeight()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return 0; + } + + long nHeight = mpFontEntry->mnLineHeight; + + if ( mbMap ) + nHeight = ImplDevicePixelToLogicHeight( nHeight ); + + return nHeight; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawTextArray( const Point& rStartPt, const XubString& rStr, + const long* pDXAry, + xub_StrLen nIndex, xub_StrLen nLen ) +{ + DBG_TRACE( "OutputDevice::DrawTextArray()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) ); + + if ( !IsDeviceOutputNecessary() ) + return; + + // String-Laenge fuer die Ermittlung der Groesse setzen + if ( (ULONG)nLen+nIndex > rStr.Len() ) + { + if ( nIndex < rStr.Len() ) + nLen = rStr.Len()-nIndex; + else + nLen = 0; + } + + // Ist die Ausgabe leer, dann mache nichts + if ( !nLen ) + return; + + // Bei keinem Pos-Array, DrawText benutzen + if ( !pDXAry || (nLen < 2) ) + { + // hier Aufrufen, damit keine doppelte MetaFile Aufzeichnung + DrawText( rStartPt, rStr, nIndex, nLen ); + return; + } + +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; +#else + if ( !ImplGetServerGraphics() ) + return; +#endif + + if ( mbNewFont ) + { + if ( !ImplNewFont() ) + return; + } + if ( mbInitFont ) + ImplInitFont(); + if ( mbInitTextColor ) + ImplInitTextColor(); + + // Pointer auf den String-Buffer setzen und um den Index korrigieren + const sal_Unicode* pStr = rStr.GetBuffer(); + pStr += nIndex; + + Point aStartPt = ImplLogicToDevicePixel( rStartPt ); + if ( mbMap ) + { + long nLogStartX = rStartPt.X(); + long nPixStartX = aStartPt.X(); + long aStackAry[128]; + long* pPixDXAry = (long*)ImplGetStackBuffer( sizeof(long)*(nLen-1), aStackAry, sizeof( aStackAry ) ); + for ( xub_StrLen i = 0; i < (nLen-1); i++ ) + pPixDXAry[i] = ImplLogicXToDevicePixel( nLogStartX+pDXAry[i] )-nPixStartX; + ImplDrawText( aStartPt.X(), aStartPt.Y(), pStr, nLen, pPixDXAry ); + ImplReleaseStackBuffer( pPixDXAry, aStackAry ); + } + else + ImplDrawText( aStartPt.X(), aStartPt.Y(), pStr, nLen, pDXAry ); +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetTextArray( const UniString& rStr, long* pDXAry, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_TRACE( "OutputDevice::GetTextArray()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !pDXAry ) + return GetTextWidth( rStr, nIndex, nLen ); + + // String-Laenge fuer die Ermittlung der Groesse setzen + if ( (ULONG)nLen+nIndex > rStr.Len() ) + { + if ( nIndex < rStr.Len() ) + nLen = rStr.Len()-nIndex; + else + nLen = 0; + } + + if ( !nLen ) + return 0; + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return 0; + } + + ImplFontEntry* pFontEntry = mpFontEntry; + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + const sal_Unicode* pTempStr; + const sal_Unicode* pStr; + long nOffset = 0; + xub_StrLen i; + pStr = rStr.GetBuffer(); + pStr += nIndex; + pTempStr = pStr; + + // Breiten ermitteln + for ( i = 0; i < nLen; i++ ) + { + nOffset += ImplGetCharWidth( *pTempStr ); + pDXAry[i] = nOffset / nFactor; + pTempStr++; + } + + // Kerning beruecksichtigen + if ( mbKerning ) + ImplCalcKerning( pStr, nLen, pDXAry, nLen ); + + // Breite und Hoehe ermitteln + long nWidth = pDXAry[nLen-1]; + + // Wenn MapMode gesetzt, dann Werte umrechnen + if ( mbMap ) + { + for ( i = 0; i < nLen; i++ ) + pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] ); + + nWidth = ImplDevicePixelToLogicWidth( nWidth ); + } + + return nWidth; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawStretchText( const Point& rStartPt, ULONG nWidth, + const UniString& rStr, + xub_StrLen nIndex, xub_StrLen nLen ) +{ + DBG_TRACE( "OutputDevice::DrawStretchText()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) ); + + if ( !IsDeviceOutputNecessary() ) + return; + + // String-Laenge fuer die Ermittlung der Groesse setzen + if ( (ULONG)nLen+nIndex > rStr.Len() ) + { + if ( nIndex < rStr.Len() ) + nLen = rStr.Len()-nIndex; + else + nLen = 0; + } + + // Ist die Ausgabe leer, dann mache nichts + if ( !nLen ) + return; + +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; +#else + if ( !ImplGetServerGraphics() ) + return; +#endif + + if ( mbNewFont ) + { + if ( !ImplNewFont() ) + return; + } + if ( mbInitFont ) + ImplInitFont(); + if ( mbInitTextColor ) + ImplInitTextColor(); + + Point aStartPt = ImplLogicToDevicePixel( rStartPt ); + nWidth = ImplLogicWidthToDevicePixel( nWidth ); + + // Pointer auf den String-Buffer setzen und um den Index korrigieren + const sal_Unicode* pStr = rStr.GetBuffer(); + pStr += nIndex; + + // Breiten-Array fuer errechnete Werte allocieren und + // mit den Breiten der einzelnen Character fuellen lassen + long aStackAry[128]; + long* pDXAry = (long*)ImplGetStackBuffer( sizeof(long)*nLen, aStackAry, sizeof( aStackAry ) ); + ImplFillDXAry( pDXAry, pStr, nLen, (long)nWidth ); + ImplDrawText( aStartPt.X(), aStartPt.Y(), pStr, nLen, pDXAry ); + ImplReleaseStackBuffer( pDXAry, aStackAry ); +} + +// ----------------------------------------------------------------------- + +xub_StrLen OutputDevice::GetTextBreak( const XubString& rStr, long nTextWidth, + xub_StrLen nIndex, xub_StrLen nLen, + long nCharExtra ) const +{ + DBG_TRACE( "OutputDevice::GetTextBreak()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( nIndex > rStr.Len() ) + return 0; + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return 0; + } + + ImplFontEntry* pFontEntry = mpFontEntry; + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + const sal_Unicode* pStr; + long nCalcWidth = 0; + xub_StrLen nLastIndex; + + if ( mbMap ) + { + nTextWidth = ImplLogicWidthToDevicePixel( nTextWidth*10 ); + nTextWidth *= nFactor; + nTextWidth /= 10; + if ( nCharExtra ) + { + nCharExtra = ImplLogicWidthToDevicePixel( nCharExtra*10 ); + nCharExtra *= nFactor; + nCharExtra /= 10; + } + } + else + { + nCharExtra *= nFactor; + nTextWidth *= nFactor; + } + + // Letzte Index-Position ermitteln + if ( (ULONG)nIndex+nLen > rStr.Len() ) + nLastIndex = rStr.Len(); + else + nLastIndex = nIndex + nLen; + + pStr = rStr.GetBuffer(); + pStr += nIndex; + while ( nIndex < nLastIndex ) + { + nCalcWidth += ImplGetCharWidth( *pStr ); + + if ( nCalcWidth > nTextWidth ) + return nIndex; + + // Kerning beruecksichtigen + if ( mbKerning ) + nCalcWidth += ImplCalcKerning( pStr, 2, NULL, 0 )*nFactor; + nCalcWidth += nCharExtra; + + nIndex++; + pStr++; + } + + return STRING_LEN; +} + +// ----------------------------------------------------------------------- + +xub_StrLen OutputDevice::GetTextBreak( const XubString& rStr, long nTextWidth, + sal_Unicode nExtraChar, xub_StrLen& rExtraCharPos, + xub_StrLen nIndex, xub_StrLen nLen, + long nCharExtra ) const +{ + DBG_TRACE( "OutputDevice::GetTextBreak()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( nIndex > rStr.Len() ) + return 0; + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return 0; + } + + ImplFontEntry* pFontEntry = mpFontEntry; + long* pCharWidthAry = pFontEntry->maWidthAry; + long nFactor = pFontEntry->mnWidthFactor; + const sal_Unicode* pStr; + long nTextWidth2; + long nCalcWidth = 0; + xub_StrLen nIndex2 = STRING_LEN; + xub_StrLen nLastIndex; + + if ( mbMap ) + { + nTextWidth = ImplLogicWidthToDevicePixel( nTextWidth*10 ); + nTextWidth *= nFactor; + nTextWidth /= 10; + if ( nCharExtra ) + { + nCharExtra = ImplLogicWidthToDevicePixel( nCharExtra*10 ); + nCharExtra *= nFactor; + nCharExtra /= 10; + } + } + else + { + nCharExtra *= nFactor; + nTextWidth *= nFactor; + } + + // Letzte Index-Position ermitteln + if ( (ULONG)nIndex+nLen > rStr.Len() ) + nLastIndex = rStr.Len(); + else + nLastIndex = nIndex + nLen; + + nTextWidth2 = nTextWidth - ImplGetCharWidth( nExtraChar ) - nCharExtra; + + pStr = rStr.GetBuffer(); + pStr += nIndex; + while ( nIndex < nLastIndex ) + { + nCalcWidth += ImplGetCharWidth( *pStr ); + + if ( nCalcWidth > nTextWidth2 ) + { + if ( nIndex2 == STRING_LEN ) + nIndex2 = nIndex; + } + if ( nCalcWidth > nTextWidth ) + { + if ( nIndex2 == STRING_LEN ) + rExtraCharPos = nIndex; + else + rExtraCharPos = nIndex2; + return nIndex; + } + + // Kerning beruecksichtigen + if ( mbKerning ) + nCalcWidth += ImplCalcKerning( pStr, 2, NULL, 0 )*nFactor; + nCalcWidth += nCharExtra; + + nIndex++; + pStr++; + } + + rExtraCharPos = nIndex2; + return STRING_LEN; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::GetCharWidth( sal_Unicode nFirstChar, sal_Unicode nLastChar, + long* pWidthAry ) const +{ + DBG_TRACE( "OutputDevice::GetCharWidth()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + DBG_ASSERT( nFirstChar <= nLastChar, "OutputDevice::GetCharWidth(): nFirst > nLast" ); + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return; + } + + long nFactor = mpFontEntry->mnWidthFactor; + sal_Unicode nCharCount = nLastChar-nFirstChar+1; + + if ( mbMap ) + { + while ( nCharCount ) + { + *pWidthAry = ImplDevicePixelToLogicWidth( ImplGetCharWidth( nFirstChar ) ) / nFactor; + pWidthAry++; + nFirstChar++; + nCharCount--; + } + } + else + { + while ( nCharCount ) + { + *pWidthAry = ImplGetCharWidth( nFirstChar ) / nFactor; + pWidthAry++; + nFirstChar++; + nCharCount--; + } + } +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawText( const Rectangle& rRect, + const XubString& rStr, USHORT nStyle ) +{ + DBG_TRACE( "OutputDevice::DrawText( const Rectangle& )" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextRectAction( rRect, rStr, nStyle ) ); + + if ( !IsDeviceOutputNecessary() || !rStr.Len() || rRect.IsEmpty() ) + return; + + // Vorsichtshalber hier auch schon Aufrufen, da ImplDrawMnemonicLine() + // dies nicht macht +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; +#else + if ( !ImplGetServerGraphics() ) + return; +#endif + + Color aOldTextColor; + Color aOldTextFillColor; + BOOL bRestoreFillColor; + if ( nStyle & TEXT_DRAW_DISABLE ) + { + aOldTextColor = GetTextColor(); + if ( IsTextFillColor() ) + { + bRestoreFillColor = TRUE; + aOldTextFillColor = GetTextFillColor(); + } + else + bRestoreFillColor = FALSE; + SetTextColor( GetSettings().GetStyleSettings().GetLightColor() ); + Rectangle aRect = rRect; + aRect.Move( 1, 1 ); + DrawText( aRect, rStr, nStyle & ~TEXT_DRAW_DISABLE ); + SetTextColor( GetSettings().GetStyleSettings().GetShadowColor() ); + } + + long nWidth = rRect.GetWidth(); + long nHeight = rRect.GetHeight(); + + if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) ) + return; + + XubString aStr = rStr; + Point aPos = rRect.TopLeft(); + long nTextHeight = GetTextHeight(); + TextAlign eAlign = GetTextAlign(); + xub_StrLen nMnemonicPos = STRING_NOTFOUND; + + if ( nStyle & TEXT_DRAW_MNEMONIC ) + aStr = GetNonMnemonicString( aStr, nMnemonicPos ); + + // Mehrzeiligen Text behandeln wir anders + if ( nStyle & TEXT_DRAW_MULTILINE ) + { + XubString aLastLine; + ImplMultiTextLineInfo aMultiLineInfo; + ImplTextLineInfo* pLineInfo; + long nMaxTextWidth; + xub_StrLen i; + xub_StrLen nLines; + xub_StrLen nFormatLines; + + if ( nTextHeight ) + { + nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle ); + nLines = (xub_StrLen)(nHeight/nTextHeight); + nFormatLines = aMultiLineInfo.Count(); + if ( !nLines ) + nLines = 1; + if ( nFormatLines > nLines ) + { + if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) + { + // Letzte Zeile zusammenbauen und kuerzen + nFormatLines = nLines-1; + pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); + aLastLine = aStr.Copy( pLineInfo->GetIndex() ); + aLastLine.ConvertLineEnd( LINEEND_LF ); + // Alle LineFeed's durch Spaces ersetzen + xub_StrLen nLastLineLen = aLastLine.Len(); + for ( i = 0; i < nLastLineLen; i++ ) + { + if ( aLastLine.GetChar( i ) == _LF ) + aLastLine.SetChar( i, ' ' ); + } + aLastLine = GetEllipsisString( aLastLine, nWidth, nStyle ); + nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); + nStyle |= TEXT_DRAW_TOP; + } + } + else + { + if ( nMaxTextWidth <= nWidth ) + nStyle &= ~TEXT_DRAW_CLIP; + } + + // Muss in der Hoehe geclippt werden? + if ( nFormatLines*nTextHeight > nHeight ) + nStyle |= TEXT_DRAW_CLIP; + + // Clipping setzen + if ( nStyle & TEXT_DRAW_CLIP ) + { + Push( PUSH_CLIPREGION ); + IntersectClipRegion( rRect ); + } + + // Vertikales Alignment + if ( nStyle & TEXT_DRAW_BOTTOM ) + aPos.Y() += nHeight-(nFormatLines*nTextHeight); + else if ( nStyle & TEXT_DRAW_VCENTER ) + aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; + + // Font Alignment + if ( eAlign == ALIGN_BOTTOM ) + aPos.Y() += nTextHeight; + else if ( eAlign == ALIGN_BASELINE ) + aPos.Y() += GetFontMetric().GetAscent(); + + // Alle Zeilen ausgeben, bis auf die letzte + for ( i = 0; i < nFormatLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( nStyle & TEXT_DRAW_RIGHT ) + aPos.X() += nWidth-pLineInfo->GetWidth(); + else if ( nStyle & TEXT_DRAW_CENTER ) + aPos.X() += (nWidth-pLineInfo->GetWidth())/2; + xub_StrLen nIndex = pLineInfo->GetIndex(); + xub_StrLen nLineLen = pLineInfo->GetLen(); + DrawText( aPos, aStr, nIndex, nLineLen ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) ) + { + if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) ) + { + long nMnemonicX; + long nMnemonicY; + xub_Unicode cMnemonic; + Point aTempPos = LogicToPixel( aPos ); + cMnemonic = aStr.GetChar( nMnemonicPos ); + nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( GetTextWidth( aStr, nIndex, nMnemonicPos-nIndex ) ); + nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() ); + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, cMnemonic ); + } + } + aPos.Y() += nTextHeight; + aPos.X() = rRect.Left(); + } + + // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben, + // da die Zeile gekuerzt wurde + if ( aLastLine.Len() ) + DrawText( aPos, aLastLine ); + + // Clipping zuruecksetzen + if ( nStyle & TEXT_DRAW_CLIP ) + Pop(); + } + } + else + { + long nTextWidth = GetTextWidth( aStr ); + + // Evt. Text kuerzen + if ( nTextWidth > nWidth ) + { + if ( nStyle & TEXT_DRAW_ELLIPSIS ) + { + aStr = GetEllipsisString( aStr, nWidth, nStyle ); + nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); + nStyle |= TEXT_DRAW_LEFT; + nTextWidth = GetTextWidth( aStr ); + } + } + else + { + if ( nTextHeight <= nHeight ) + nStyle &= ~TEXT_DRAW_CLIP; + } + + // Vertikales Alignment + if ( nStyle & TEXT_DRAW_RIGHT ) + aPos.X() += nWidth-nTextWidth; + else if ( nStyle & TEXT_DRAW_CENTER ) + aPos.X() += (nWidth-nTextWidth)/2; + + // Font Alignment + if ( eAlign == ALIGN_BOTTOM ) + aPos.Y() += nTextHeight; + else if ( eAlign == ALIGN_BASELINE ) + aPos.Y() += GetFontMetric().GetAscent(); + + if ( nStyle & TEXT_DRAW_BOTTOM ) + aPos.Y() += nHeight-nTextHeight; + else if ( nStyle & TEXT_DRAW_VCENTER ) + aPos.Y() += (nHeight-nTextHeight)/2; + + long nMnemonicX; + long nMnemonicY; + xub_Unicode cMnemonic; + if ( nMnemonicPos != STRING_NOTFOUND ) + { + Point aTempPos = LogicToPixel( aPos ); + cMnemonic = aStr.GetChar( nMnemonicPos ); + nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( GetTextWidth( aStr, 0, nMnemonicPos ) ); + nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() ); + } + + if ( nStyle & TEXT_DRAW_CLIP ) + { + Push( PUSH_CLIPREGION ); + IntersectClipRegion( rRect ); + DrawText( aPos, aStr ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, cMnemonic ); + } + Pop(); + } + else + { + DrawText( aPos, aStr ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, cMnemonic ); + } + } + } + + if ( nStyle & TEXT_DRAW_DISABLE ) + { + SetTextColor( aOldTextColor ); + if ( bRestoreFillColor ) + SetTextFillColor( aOldTextFillColor ); + } +} + +// ----------------------------------------------------------------------- + +Rectangle OutputDevice::GetTextRect( const Rectangle& rRect, + const XubString& rStr, USHORT nStyle, + TextRectInfo* pInfo ) const +{ + DBG_TRACE( "OutputDevice::GetTextRect()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + Rectangle aRect = rRect; + XubString aStr = rStr; + xub_StrLen nLines; + long nWidth = rRect.GetWidth(); + long nMaxWidth; + long nTextHeight = GetTextHeight(); + + if ( nStyle & TEXT_DRAW_MNEMONIC ) + aStr = GetNonMnemonicString( aStr ); + + if ( nStyle & TEXT_DRAW_MULTILINE ) + { + ImplMultiTextLineInfo aMultiLineInfo; + ImplTextLineInfo* pLineInfo; + xub_StrLen nFormatLines; + xub_StrLen i; + + nMaxWidth = 0; + ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle ); + nFormatLines = aMultiLineInfo.Count(); + if ( !nTextHeight ) + nTextHeight = 1; + nLines = (USHORT)(aRect.GetHeight()/nTextHeight); + if ( pInfo ) + pInfo->mnLineCount = nFormatLines; + if ( !nLines ) + nLines = 1; + if ( nFormatLines <= nLines ) + nLines = nFormatLines; + else + { + if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) ) + nLines = nFormatLines; + else + { + if ( pInfo ) + pInfo->mbEllipsis = TRUE; + nMaxWidth = nWidth; + } + } + if ( pInfo ) + { + BOOL bMaxWidth = nMaxWidth == 0; + pInfo->mnMaxWidth = 0; + for ( i = 0; i < nLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) ) + nMaxWidth = pLineInfo->GetWidth(); + if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth ) + pInfo->mnMaxWidth = pLineInfo->GetWidth(); + } + } + else if ( !nMaxWidth ) + { + for ( i = 0; i < nLines; i++ ) + { + pLineInfo = aMultiLineInfo.GetLine( i ); + if ( pLineInfo->GetWidth() > nMaxWidth ) + nMaxWidth = pLineInfo->GetWidth(); + } + } + } + else + { + nLines = 1; + nMaxWidth = GetTextWidth( aStr ); + + if ( pInfo ) + { + pInfo->mnLineCount = 1; + pInfo->mnMaxWidth = nMaxWidth; + } + + if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) ) + { + if ( pInfo ) + pInfo->mbEllipsis = TRUE; + nMaxWidth = nWidth; + } + } + + if ( nStyle & TEXT_DRAW_RIGHT ) + aRect.Left() = aRect.Right()-nMaxWidth+1; + else if ( nStyle & TEXT_DRAW_CENTER ) + { + aRect.Left() += (nWidth-nMaxWidth)/2; + aRect.Right() = aRect.Left()+nMaxWidth-1; + } + else + aRect.Right() = aRect.Left()+nMaxWidth-1; + + if ( nStyle & TEXT_DRAW_BOTTOM ) + aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1; + else if ( nStyle & TEXT_DRAW_VCENTER ) + { + aRect.Top() += (aRect.GetHeight()-(nTextHeight*nLines))/2; + aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1; + } + else + aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1; + + return aRect; +} + +// ----------------------------------------------------------------------- + +static BOOL ImplIsCharIn( xub_Unicode c, const sal_Char* pStr ) +{ + while ( *pStr ) + { + if ( *pStr == c ) + return TRUE; + pStr++; + } + + return FALSE; +} + +// ----------------------------------------------------------------------- + +XubString OutputDevice::GetEllipsisString( const XubString& rStr, long nMaxWidth, + USHORT nStyle ) const +{ + DBG_TRACE( "OutputDevice::GetEllipsisString()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + XubString aStr = rStr; + xub_StrLen nIndex = GetTextBreak( aStr, nMaxWidth ); + + if ( nIndex != STRING_LEN ) + { + if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) + { + aStr.Erase( nIndex ); + if ( nIndex > 1 ) + { + aStr.AppendAscii( "..." ); + while ( aStr.Len() && (GetTextWidth( aStr ) > nMaxWidth) ) + { + if ( (nIndex > 1) || (nIndex == aStr.Len()) ) + nIndex--; + aStr.Erase( nIndex, 1 ); + } + } + + if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) ) + aStr += rStr.GetChar( 0 ); + } + else if ( nStyle & (TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) + { + static sal_Char const aPathSepChars[] = "\\/:"; + static sal_Char const aNewsSepChars[] = "."; + const sal_Char* pSepChars; + + if ( nStyle & TEXT_DRAW_PATHELLIPSIS ) + pSepChars = aPathSepChars; + else + pSepChars = aNewsSepChars; + + // Letztes Teilstueck ermitteln + xub_StrLen nLastContent = rStr.Len(); + while ( nLastContent ) + { + nLastContent--; + if ( ImplIsCharIn( rStr.GetChar( nLastContent ), pSepChars ) ) + break; + } + while ( nLastContent && + ImplIsCharIn( rStr.GetChar( nLastContent-1 ), pSepChars ) ) + nLastContent--; + + XubString aLastStr( rStr, nLastContent, rStr.Len() ); + XubString aTempLastStr( RTL_CONSTASCII_USTRINGPARAM( "..." ) ); + aTempLastStr += aLastStr; + if ( GetTextWidth( aTempLastStr ) > nMaxWidth ) + aStr = GetEllipsisString( rStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS ); + else + { + USHORT nFirstContent = 0; + while ( nFirstContent < nLastContent ) + { + nFirstContent++; + if ( ImplIsCharIn( rStr.GetChar( nFirstContent ), pSepChars ) ) + break; + } + while ( (nFirstContent < nLastContent) && + ImplIsCharIn( rStr.GetChar( nFirstContent ), pSepChars ) ) + nFirstContent++; + + if ( nFirstContent >= nLastContent ) + aStr = GetEllipsisString( rStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS ); + else + { + if ( nFirstContent > 4 ) + nFirstContent = 4; + XubString aFirstStr( rStr, 0, nFirstContent ); + aFirstStr.AppendAscii( "..." ); + XubString aTempStr = aFirstStr; + aTempStr += aLastStr; + if ( GetTextWidth( aTempStr ) > nMaxWidth ) + aStr = GetEllipsisString( rStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS ); + else + { + do + { + aStr = aTempStr; + while ( nFirstContent < nLastContent ) + { + nLastContent--; + if ( ImplIsCharIn( rStr.GetChar( nLastContent ), pSepChars ) ) + break; + } + while ( (nFirstContent < nLastContent) && + ImplIsCharIn( rStr.GetChar( nLastContent-1 ), pSepChars ) ) + nLastContent--; + + if ( nFirstContent < nLastContent ) + { + XubString aTempLastStr( rStr, nLastContent, rStr.Len() ); + aTempStr = aFirstStr; + aTempStr += aTempLastStr; + if ( GetTextWidth( aTempStr ) > nMaxWidth ) + break; + } + } + while ( nFirstContent < nLastContent ); + } + } + } + } + } + + return aStr; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::DrawCtrlText( const Point& rPos, const XubString& rStr, + xub_StrLen nIndex, xub_StrLen nLen, + USHORT nStyle ) +{ + DBG_TRACE( "OutputDevice::DrawCtrlText()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.Len()) ) + return; + + // Vorsichtshalber hier auch schon Aufrufen, da ImplDrawMnemonicLine() + // dies nicht macht +#ifndef REMOTE_APPSERVER + // we need a graphics + if ( !mpGraphics ) + { + if ( !ImplGetGraphics() ) + return; + } + + if ( mbInitClipRegion ) + ImplInitClipRegion(); + if ( mbOutputClipped ) + return; +#else + if ( !ImplGetServerGraphics() ) + return; +#endif + + XubString aStr = rStr; + xub_StrLen nMnemonicPos = STRING_NOTFOUND; + long nMnemonicX; + long nMnemonicY; + xub_Unicode cMnemonic; + if ( nStyle & TEXT_DRAW_MNEMONIC ) + { + aStr = GetNonMnemonicString( aStr, nMnemonicPos ); + if ( nMnemonicPos != STRING_NOTFOUND ) + { + if ( nMnemonicPos < nIndex ) + nIndex--; + else if ( (nLen < STRING_LEN) && + (nMnemonicPos >= nIndex) && (nMnemonicPos < (ULONG)(nIndex+nLen)) ) + nLen--; + Point aTempPos = LogicToPixel( rPos ); + cMnemonic = aStr.GetChar( nMnemonicPos ); + nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( GetTextWidth( aStr, 0, nMnemonicPos ) ); + nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() ); + } + } + + if ( nStyle & TEXT_DRAW_DISABLE ) + { + Color aOldTextColor; + Color aOldTextFillColor; + BOOL bRestoreFillColor; + aOldTextColor = GetTextColor(); + if ( IsTextFillColor() ) + { + bRestoreFillColor = TRUE; + aOldTextFillColor = GetTextFillColor(); + } + else + bRestoreFillColor = FALSE; + SetTextColor( GetSettings().GetStyleSettings().GetLightColor() ); + DrawText( Point( rPos.X()+1, rPos.Y()+1 ), aStr, nIndex, nLen ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX+1, nMnemonicY+1, cMnemonic ); + } + SetTextColor( GetSettings().GetStyleSettings().GetShadowColor() ); + DrawText( rPos, aStr, nIndex, nLen ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, cMnemonic ); + } + SetTextColor( aOldTextColor ); + if ( bRestoreFillColor ) + SetTextFillColor( aOldTextFillColor ); + } + else + { + DrawText( rPos, aStr, nIndex, nLen ); + if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) ) + { + if ( nMnemonicPos != STRING_NOTFOUND ) + ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, cMnemonic ); + } + } +} + +// ----------------------------------------------------------------------- + +long OutputDevice::GetCtrlTextWidth( const XubString& rStr, + xub_StrLen nIndex, xub_StrLen nLen, + USHORT nStyle ) const +{ + DBG_TRACE( "OutputDevice::GetCtrlTextSize()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + if ( nStyle & TEXT_DRAW_MNEMONIC ) + { + xub_StrLen nMnemonicPos; + XubString aStr = GetNonMnemonicString( rStr, nMnemonicPos ); + if ( nMnemonicPos != STRING_NOTFOUND ) + { + if ( nMnemonicPos < nIndex ) + nIndex--; + else if ( (nLen < STRING_LEN) && + (nMnemonicPos >= nIndex) && (nMnemonicPos < (ULONG)(nIndex+nLen)) ) + nLen--; + } + return GetTextWidth( aStr, nIndex, nLen ); + } + else + return GetTextWidth( rStr, nIndex, nLen ); +} + +// ----------------------------------------------------------------------- + +XubString OutputDevice::GetNonMnemonicString( const XubString& rStr, xub_StrLen& rMnemonicPos ) +{ + XubString aStr = rStr; + xub_StrLen nLen = aStr.Len(); + xub_StrLen i = 0; + + rMnemonicPos = STRING_NOTFOUND; + while ( i < nLen ) + { + if ( aStr.GetChar( i ) == '~' ) + { + if ( aStr.GetChar( i+1 ) != '~' ) + { + if ( rMnemonicPos == STRING_NOTFOUND ) + rMnemonicPos = i; + aStr.Erase( i, 1 ); + nLen--; + } + else + { + aStr.Erase( i, 1 ); + nLen--; + i++; + } + } + else + i++; + } + + return aStr; +} + +// ----------------------------------------------------------------------- + +USHORT OutputDevice::GetDevFontCount() const +{ + DBG_TRACE( "OutputDevice::GetDevFontCount()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + // Wenn wir schon eine Liste der Fonts haben, dann nicht iterieren + if ( mpGetDevFontList ) + return (USHORT)mpGetDevFontList->Count(); + + ((OutputDevice*)this)->mpGetDevFontList = new ImplGetDevFontList; + + // Fill Fontlist + ImplDevFontListData* pFontListData = mpFontList->First(); + while ( pFontListData ) + { + ImplFontData* pLastData = NULL; + ImplFontData* pData = pFontListData->mpFirst; + while ( pData ) + { + // Compare with the last font, because we wan't in the list + // only fonts, that have different attributes, but not + // different sizes + if ( !pLastData || + (ImplCompareFontDataWithoutSize( pLastData, pData ) != 0) ) + mpGetDevFontList->Add( pData ); + + pLastData = pData; + pData = pData->mpNext; + } + + pFontListData = mpFontList->Next(); + } + + return (USHORT)mpGetDevFontList->Count(); +} + +// ----------------------------------------------------------------------- + +FontInfo OutputDevice::GetDevFont( USHORT nDevFont ) const +{ + DBG_TRACE( "OutputDevice::GetDevFont()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + FontInfo aFontInfo; + USHORT nCount = GetDevFontCount(); + + // Wertebereich ueberpruefen + if ( nDevFont < nCount ) + { + ImplFontData* pData = mpGetDevFontList->Get( nDevFont ); + aFontInfo.SetName( pData->maName ); + aFontInfo.SetStyleName( pData->maStyleName ); +// !!! UNICODE !!! aFontInfo.SetCharSet( ImplGetFakeEncoding( pData->meCharSet ) ); + aFontInfo.SetCharSet( pData->meCharSet ); + aFontInfo.SetFamily( pData->meFamily ); + aFontInfo.SetPitch( pData->mePitch ); + aFontInfo.SetWeight( pData->meWeight ); + aFontInfo.SetItalic( pData->meItalic ); + aFontInfo.mpImplMetric->meType = pData->meType; + aFontInfo.mpImplMetric->mbDevice = pData->mbDevice; + } + + return aFontInfo; +} + +// ----------------------------------------------------------------------- + +USHORT OutputDevice::GetDevFontSizeCount( const Font& rFont ) const +{ + DBG_TRACE( "OutputDevice::GetDevFontSizeCount()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + XubString aFontName = rFont.GetName(); + + // Wenn die Liste schon existiert und der FontName sich nicht + // unterscheidet, dann brauchen wir Sie nicht neu erzeugen + if ( mpGetDevSizeList ) + { + if ( mpGetDevSizeList->GetFontName() == aFontName ) + return (USHORT)mpGetDevSizeList->Count(); + else + { + mpGetDevSizeList->Clear(); + mpGetDevSizeList->SetFontName( aFontName ); + } + } + else + ((OutputDevice*)this)->mpGetDevSizeList = new ImplGetDevSizeList( aFontName ); + + // Fonts aus unserer Fontliste in die GetDevFontSizeListe eintragen + ImplDevFontListData* pFontListData = mpFontList->FindFont( aFontName ); + if ( pFontListData ) + { + ImplFontData* pData = pFontListData->mpFirst; + do + { + mpGetDevSizeList->Add( pData->mnHeight ); + pData = pData->mpNext; + } + while ( pData ); + } + + return (USHORT)mpGetDevSizeList->Count(); +} + +// ----------------------------------------------------------------------- + +Size OutputDevice::GetDevFontSize( const Font& rFont, USHORT nSize ) const +{ + DBG_TRACE( "OutputDevice::GetDevFontSize()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + USHORT nCount = GetDevFontSizeCount( rFont ); + + // Wertebereich ueberpruefen + if ( nSize >= nCount ) + return Size(); + + // Wenn MapMode gesetzt ist, wird auf ,5-Points gerundet + Size aSize( 0, mpGetDevSizeList->Get( nSize ) ); + if ( mbMap ) + { + aSize.Height() *= 10; + MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) ); + aSize = PixelToLogic( aSize, aMap ); + aSize.Height() += 5; + aSize.Height() /= 10; + long nRound = aSize.Height() % 5; + if ( nRound >= 3 ) + aSize.Height() += (5-nRound); + else + aSize.Height() -= nRound; + aSize.Height() *= 10; + aSize = LogicToPixel( aSize, aMap ); + aSize = PixelToLogic( aSize ); + aSize.Height() += 5; + aSize.Height() /= 10; + } + return aSize; +} + +// ----------------------------------------------------------------------- + +BOOL OutputDevice::IsFontAvailable( const XubString& rFontName ) const +{ + DBG_TRACE( "OutputDevice::IsFontAvailable()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + return (mpFontList->FindFont( rFontName ) != 0); +} + +// ----------------------------------------------------------------------- + +FontMetric OutputDevice::GetFontMetric() const +{ + DBG_TRACE( "OutputDevice::GetFontMetric()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + FontMetric aMetric; + + if ( mbNewFont ) + { + if ( !((OutputDevice*)this)->ImplNewFont() ) + return aMetric; + } + + ImplFontEntry* pEntry = mpFontEntry; + ImplFontMetricData* pMetric = &(pEntry->maMetric); + + // Mappen und StarView Struktur fuellen + aMetric.Font::operator=( maFont ); + + // Fontdaten ermitteln und setzen + aMetric.SetName( pMetric->maName ); + aMetric.SetStyleName( pMetric->maStyleName ); + aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnLeading ) ) ); + aMetric.SetCharSet( pMetric->meCharSet ); + aMetric.SetFamily( pMetric->meFamily ); + aMetric.SetPitch( pMetric->mePitch ); + aMetric.SetWeight( pMetric->meWeight ); + aMetric.SetItalic( pMetric->meItalic ); + if ( pEntry->mnOwnOrientation ) + aMetric.SetOrientation( pEntry->mnOwnOrientation ); + else + aMetric.SetOrientation( pMetric->mnOrientation ); + if ( !mbKerning ) + aMetric.SetKerning( FALSE ); + + // restliche Metricen setzen + aMetric.mpImplMetric->meType = pMetric->meType; + aMetric.mpImplMetric->mbDevice = pMetric->mbDevice; + aMetric.mpImplMetric->mnAscent = ImplDevicePixelToLogicHeight( pMetric->mnAscent ); + aMetric.mpImplMetric->mnDescent = ImplDevicePixelToLogicHeight( pMetric->mnDescent ); + aMetric.mpImplMetric->mnLeading = ImplDevicePixelToLogicHeight( pMetric->mnLeading ); + aMetric.mpImplMetric->mnLineHeight = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent ); + aMetric.mpImplMetric->mnSlant = ImplDevicePixelToLogicHeight( pMetric->mnSlant ); + aMetric.mpImplMetric->mnFirstChar = pMetric->mnFirstChar; + aMetric.mpImplMetric->mnLastChar = pMetric->mnLastChar; + + return aMetric; +} + +// ----------------------------------------------------------------------- + +FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const +{ + // Uebergebenen Font selektieren, Metric abfragen und alten wieder + // selektieren + Font aOldFont = GetFont(); + ((OutputDevice*)this)->SetFont( rFont ); + FontMetric aMetric( GetFontMetric() ); + ((OutputDevice*)this)->SetFont( aOldFont ); + return aMetric; +} + +// ----------------------------------------------------------------------- + +ULONG OutputDevice::GetKerningPairCount() const +{ + DBG_TRACE( "OutputDevice::GetKerningPairCount()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + ((OutputDevice*)this)->ImplInitKerningPairs(); + return mpFontEntry->mnKernPairs; +} + +// ----------------------------------------------------------------------- + +void OutputDevice::GetKerningPairs( ULONG nPairs, KerningPair* pKernPairs ) const +{ + DBG_TRACE( "OutputDevice::GetKerningPairs()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + ((OutputDevice*)this)->ImplInitKerningPairs(); + if ( nPairs > mpFontEntry->mnKernPairs ) + nPairs = mpFontEntry->mnKernPairs; + if ( nPairs ) + memcpy( pKernPairs, mpFontEntry->mpKernPairs, nPairs*sizeof( KerningPair ) ); +} + +// ----------------------------------------------------------------------- + +BOOL OutputDevice::GetGlyphBoundRect( xub_Unicode cChar, Rectangle& rRect, BOOL bOptimize ) +{ + DBG_TRACE( "OutputDevice::GetGlyphBoundRect()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + BOOL bRet = FALSE; + +#ifndef REMOTE_APPSERVER + if ( mpGraphics || ImplGetGraphics() ) + { + Font* pOldFont; + long nLeft, nTop, nWidth, nHeight; + long nFontWidth, nFontHeight; + long nOrgWidth, nOrgHeight; + + if ( bOptimize ) + { + pOldFont = new Font( GetFont() ); + + Font aFont( *pOldFont ); + Size aFontSize( LogicToPixel( aFont.GetSize() ) ); + + if ( aFontSize.Width() && aFontSize.Height() ) + { + const double fFactor = (double) aFontSize.Width() / aFontSize.Height(); + + if ( fFactor < 1.0 ) + { + aFontSize.Width() = FRound( fFactor * 500. ); + aFontSize.Height() = 500; + } + else + { + aFontSize.Width() = 500; + aFontSize.Height() = FRound( 500. / fFactor ); + } + + aFont.SetSize( PixelToLogic( aFontSize ) ); + ((OutputDevice*)this)->SetFont( aFont ); + nFontWidth = aFont.GetSize().Width(); + nFontHeight = aFont.GetSize().Height(); + nOrgWidth = pOldFont->GetSize().Width(); + nOrgHeight = pOldFont->GetSize().Height(); + } + else + { + aFont.SetSize( PixelToLogic( Size( 0, 500 ) ) ); + ((OutputDevice*)this)->SetFont( aFont ); + nFontWidth = nFontHeight = aFont.GetSize().Height(); + nOrgWidth = nOrgHeight = pOldFont->GetSize().Height(); + } + } + + if ( mbNewFont ) + ImplNewFont(); + if ( mbInitFont ) + ImplInitFont(); + + if ( mpGraphics->GetGlyphBoundRect( cChar, &nLeft, &nTop, &nWidth, &nHeight ) ) + { + if ( bOptimize ) + { + nLeft = ImplDevicePixelToLogicWidth( nLeft ) * nOrgWidth / nFontWidth; + nTop = ImplDevicePixelToLogicHeight( nTop ) * nOrgHeight / nFontHeight; + nWidth = ImplDevicePixelToLogicWidth( nWidth ) * nOrgWidth / nFontWidth; + nHeight = ImplDevicePixelToLogicHeight( nHeight ) * nOrgHeight / nFontHeight; + } + else + { + nLeft = ImplDevicePixelToLogicWidth( nLeft ); + nTop = ImplDevicePixelToLogicHeight( nTop ); + nWidth = ImplDevicePixelToLogicWidth( nWidth ); + nHeight = ImplDevicePixelToLogicHeight( nHeight ); + } + + rRect = Rectangle( Point( nLeft, nTop ), Size( nWidth, nHeight ) ); + bRet = TRUE; + } + + if ( bOptimize ) + { + ((OutputDevice*)this)->SetFont( *pOldFont ); + delete pOldFont; + } + + if ( !bRet && (OUTDEV_PRINTER != meOutDevType) ) + { + if ( bOptimize ) + { + if ( mbNewFont ) + ImplNewFont(); + if ( mbInitFont ) + ImplInitFont(); + } + + VirtualDevice* pVDev = new VirtualDevice( 1 ); + long nWidth = ImplGetTextWidth( &cChar, 1, NULL ); + long nHeight = mpFontEntry->mnLineHeight; + Point aOffset( nWidth >> 1, 8 ); + Size aSize( nWidth + ( aOffset.X() << 1 ), nHeight + ( aOffset.Y() << 1 ) ); + + if ( pVDev->SetOutputSizePixel( aSize ) ) + { + Font aFont( GetFont() ); + Bitmap aBmp; + + aFont.SetShadow( FALSE ); + aFont.SetOutline( FALSE ); + aFont.SetOrientation( 0 ); + aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) ); + + pVDev->SetFont( aFont ); + pVDev->SetTextAlign( ALIGN_TOP ); + pVDev->SetTextColor( Color( COL_BLACK ) ); + pVDev->SetTextFillColor(); + pVDev->ImplNewFont(); + pVDev->ImplInitFont(); + pVDev->ImplInitTextColor(); + pVDev->ImplDrawText( aOffset.X(), aOffset.Y(), &cChar, 1, NULL ); + aBmp = pVDev->GetBitmap( Point(), aSize ); + delete pVDev; + + BitmapReadAccess* pAcc = aBmp.AcquireReadAccess(); + + if ( pAcc ) + { + const long nW = pAcc->Width(); + const long nW1 = nW - 1L; + const long nH = pAcc->Height(); + long nRight, nBottom; + const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); + BOOL bLineDone; + + nLeft = nW; + nTop = nH; + nRight = nBottom = -1L; + + for( long nY = 0L; nY < nH; nY++ ) + { + bLineDone = FALSE; + + for( long nX = 0L; ( nX < nW ) && !bLineDone; nX++ ) + { + if( pAcc->GetPixel( nY, nX ) == aBlack ) + { + // find y minimum + if( nY < nTop ) + nTop = nY; + + // find y maximum + if( nY > nBottom ) + nBottom = nY; + + // find x minimum + if( nX < nLeft ) + nLeft = nX; + + // find x maximum (last pixel in line) + for( long nX2 = nW1; nX2 >= nX; nX2-- ) + { + if( pAcc->GetPixel( nY, nX2 ) == aBlack ) + { + if( nX2 > nRight ) + nRight = nX2; + + bLineDone = TRUE; + break; + } + } + } + } + } + + if( nLeft < nW && nTop < nH && nRight > -1L && nBottom > -1L ) + { + nLeft -= aOffset.X(), nTop -= aOffset.Y(); + nRight -= aOffset.X(), nBottom -= aOffset.Y(); + + nWidth = ImplDevicePixelToLogicWidth( nRight - nLeft + 1L ); + nHeight = ImplDevicePixelToLogicHeight( nBottom - nTop + 1L ); + nLeft = ImplDevicePixelToLogicWidth( nLeft ); + nTop = ImplDevicePixelToLogicHeight( nTop ); + rRect = Rectangle( Point( nLeft, nTop ), Size( nWidth, nHeight ) ); + bRet = TRUE; + } + + aBmp.ReleaseAccess( pAcc ); + } + } + else + delete pVDev; + } + } +#else + if ( mbNewFont ) + ImplNewFont(); + if ( mbInitFont ) + ImplInitFont(); + + bRet = mpGraphics->GetGlyphBoundRect( cChar, rRect, bOptimize ); + + if ( bRet ) + { + rRect = Rectangle( Point( ImplDevicePixelToLogicWidth( rRect.Left() ), + ImplDevicePixelToLogicHeight( rRect.Top() ) ), + Size( ImplDevicePixelToLogicWidth( rRect.GetWidth() ), + ImplDevicePixelToLogicHeight( rRect.GetHeight() ) ) ); + } + +#endif + + if ( !bRet ) + rRect.SetEmpty(); + + return bRet; +} + +// ----------------------------------------------------------------------- + +BOOL OutputDevice::GetGlyphOutline( xub_Unicode cChar, PolyPolygon& rPolyPoly, BOOL bOptimize ) +{ + DBG_TRACE( "OutputDevice::GetGlyphOutline()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + + BOOL bRet = FALSE; + +#ifndef REMOTE_APPSERVER + if ( mpGraphics || ImplGetGraphics() ) + { + Font* pOldFont; + USHORT* pPolySizes = NULL; + SalPoint* pPoints = NULL; + BYTE* pFlags = NULL; + long nFontWidth, nFontHeight; + long nOrgWidth, nOrgHeight; + ULONG nPolyCount; + + if ( bOptimize ) + { + pOldFont = new Font( GetFont() ); + + Font aFont( *pOldFont ); + Size aFontSize( LogicToPixel( aFont.GetSize() ) ); + + if ( aFontSize.Width() && aFontSize.Height() ) + { + const double fFactor = (double) aFontSize.Width() / aFontSize.Height(); + + if ( fFactor < 1.0 ) + { + aFontSize.Width() = FRound( fFactor * 500. ); + aFontSize.Height() = 500; + } + else + { + aFontSize.Width() = 500; + aFontSize.Height() = FRound( 500. / fFactor ); + } + + aFont.SetSize( PixelToLogic( aFontSize ) ); + ((OutputDevice*)this)->SetFont( aFont ); + nFontWidth = aFont.GetSize().Width(); + nFontHeight = aFont.GetSize().Height(); + nOrgWidth = pOldFont->GetSize().Width(); + nOrgHeight = pOldFont->GetSize().Height(); + } + else + { + aFont.SetSize( PixelToLogic( Size( 0, 500 ) ) ); + ((OutputDevice*)this)->SetFont( aFont ); + nFontWidth = nFontHeight = aFont.GetSize().Height(); + nOrgWidth = nOrgHeight = pOldFont->GetSize().Height(); + } + } + + if ( mbNewFont ) + ImplNewFont(); + if ( mbInitFont ) + ImplInitFont(); + + nPolyCount = mpGraphics->GetGlyphOutline( cChar, &pPolySizes, &pPoints, &pFlags ); + if ( nPolyCount && pPolySizes && pPoints && pFlags ) + { + ULONG nTotalPos = 0UL; + + rPolyPoly.Clear(); + + for( ULONG i = 0UL; i < nPolyCount; i++ ) + { + const USHORT nSize = pPolySizes[ i ]; + + if( nSize ) + { + Polygon aPoly( nSize ); + Point* pPt = aPoly.ImplGetPointAry(); + BYTE* pFl = aPoly.ImplGetFlagAry(); + + memcpy( pFl, pFlags + nTotalPos, nSize ); + + for( USHORT n = 0; n < nSize; n++ ) + { + const SalPoint& rSalPt = pPoints[ nTotalPos++ ]; + Point& rPt = pPt[ n ]; + + if( bOptimize ) + { + rPt.X() = ImplDevicePixelToLogicWidth( rSalPt.mnX ) * + nOrgWidth / nFontWidth; + rPt.Y() = ImplDevicePixelToLogicHeight( rSalPt.mnY ) * + nOrgHeight / nFontHeight; + } + else + { + rPt.X() = ImplDevicePixelToLogicWidth( rSalPt.mnX ); + rPt.Y() = ImplDevicePixelToLogicHeight( rSalPt.mnY ); + } + } + + rPolyPoly.Insert( aPoly ); + } + } + + bRet = TRUE; + } + + delete[] pPolySizes; + delete[] pPoints; + delete[] pFlags; + + if ( bOptimize ) + { + ((OutputDevice*)this)->SetFont( *pOldFont ); + delete pOldFont; + } + + if ( !bRet && (OUTDEV_PRINTER != meOutDevType) ) + { + if ( bOptimize ) + { + if( mbNewFont ) + ImplNewFont(); + if( mbInitFont ) + ImplInitFont(); + } + + Font aFont( GetFont() ); + VirtualDevice* pVDev = new VirtualDevice( 1 ); + const Size aFontSize( pVDev->LogicToPixel( Size( 0, GLYPH_FONT_HEIGHT ), MAP_POINT ) ); + const long nOrgWidth = ImplGetTextWidth( &cChar, 1, NULL ); + const long nOrgHeight = mpFontEntry->mnLineHeight; + + aFont.SetShadow( FALSE ); + aFont.SetOutline( FALSE ); + aFont.SetOrientation( 0 ); + aFont.SetSize( aFontSize ); + pVDev->SetFont( aFont ); + pVDev->SetTextAlign( ALIGN_TOP ); + pVDev->SetTextColor( Color( COL_BLACK ) ); + pVDev->SetTextFillColor(); + pVDev->ImplNewFont(); + pVDev->ImplInitFont(); + pVDev->ImplInitTextColor(); + + const long nWidth = pVDev->ImplGetTextWidth( &cChar, 1, NULL ); + const long nHeight = pVDev->mpFontEntry->mnLineHeight; + const Point aOffset( nWidth >> 1, 8 ); + const Size aSize( nWidth + ( aOffset.X() << 1 ), nHeight + ( aOffset.Y() << 1 ) ); + const double fScaleX = ( nOrgWidth && nWidth ) ? ( (double) nOrgWidth / nWidth ) : 0.0; + const double fScaleY = ( nOrgHeight && nHeight ) ? ( (double) nOrgHeight / nHeight ) : 0.0; + + if ( pVDev->SetOutputSizePixel( aSize ) ) + { + Bitmap aBmp; + + pVDev->ImplDrawText( aOffset.X(), aOffset.Y(), &cChar, 1, NULL ); + aBmp = pVDev->GetBitmap( Point(), aSize ); + delete pVDev; + + if( aBmp.Vectorize( rPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES ) ) + { + const long nOffX = aOffset.X(), nOffY = aOffset.Y(); + + for( USHORT i = 0UL, nCount = rPolyPoly.Count(); i < nCount; i++ ) + { + Polygon& rPoly = rPolyPoly[ i ]; + + for( USHORT n = 0, nSize = rPoly.GetSize(); n < nSize; n++ ) + { + Point& rPt = rPoly[ n ]; + rPt.X() = FRound( ImplDevicePixelToLogicWidth( rPt.X() - nOffX ) * fScaleX ); + rPt.Y() = FRound( ImplDevicePixelToLogicHeight( rPt.Y() - nOffY ) * fScaleY ); + } + } + + bRet = TRUE; + } + } + else + delete pVDev; + } + } +#else + if ( mbNewFont ) + ImplNewFont(); + if ( mbInitFont ) + ImplInitFont(); + + bRet = mpGraphics->GetGlyphOutline( cChar, rPolyPoly, bOptimize ); + + if( bRet ) + { + for( USHORT i = 0UL, nCount = rPolyPoly.Count(); i < nCount; i++ ) + { + Polygon& rPoly = rPolyPoly[ i ]; + + for( USHORT n = 0, nSize = rPoly.GetSize(); n < nSize; n++ ) + { + Point& rPt = rPoly[ n ]; + rPt.X() = ImplDevicePixelToLogicWidth( rPt.X() ); + rPt.Y() = ImplDevicePixelToLogicHeight( rPt.Y() ); + } + } + } + +#endif + + if( !bRet ) + rPolyPoly = PolyPolygon(); + + return bRet; +} |