/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"




// INCLUDE ---------------------------------------------------------------

#include "scitems.hxx"
#include <editeng/eeitem.hxx>


#include <editeng/adjitem.hxx>
#include <svx/algitem.hxx>
#include <editeng/brshitem.hxx>
#include <svtools/colorcfg.hxx>
#include <editeng/colritem.hxx>
#include <editeng/editobj.hxx>
#include <editeng/editstat.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/forbiddencharacterstable.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/langitem.hxx>
#include <svx/rotmodit.hxx>
#include <editeng/scripttypeitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/unolingu.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <vcl/svapp.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>
#include <vcl/pdfextoutdevdata.hxx>

#ifndef _SVSTDARR_USHORTS
#define _SVSTDARR_USHORTS
#include <svl/svstdarr.hxx>
#endif

#include "output.hxx"
#include "document.hxx"
#include "cell.hxx"
#include "attrib.hxx"
#include "patattr.hxx"
#include "cellform.hxx"
#include "editutil.hxx"
#include "progress.hxx"
#include "scmod.hxx"
#include "fillinfo.hxx"

#include <math.h>

//!	Autofilter-Breite mit column.cxx zusammenfassen
#define DROPDOWN_BITMAP_SIZE        18

#define DRAWTEXT_MAX	32767

const sal_uInt16 SC_SHRINKAGAIN_MAX = 7;

// STATIC DATA -----------------------------------------------------------


// -----------------------------------------------------------------------

class ScDrawStringsVars
{
	ScOutputData*		pOutput;				// Verbindung

	const ScPatternAttr* pPattern;				// Attribute
	const SfxItemSet*	pCondSet;				// aus bedingter Formatierung

	Font				aFont;					// aus Attributen erzeugt
	FontMetric			aMetric;
	long				nAscentPixel;			// always pixels
	SvxCellOrientation	eAttrOrient;
	SvxCellHorJustify	eAttrHorJust;
	SvxCellVerJustify	eAttrVerJust;
	const SvxMarginItem* pMargin;
	sal_uInt16				nIndent;
	sal_Bool				bRotated;

	String				aString;				// Inhalte
	Size				aTextSize;
	long				nOriginalWidth;
    long                nMaxDigitWidth;
    long                nSignWidth;
    long                nDotWidth;
    long                nExpWidth;

	ScBaseCell*			pLastCell;
	sal_uLong				nValueFormat;
	sal_Bool				bLineBreak;
    sal_Bool                bRepeat;
    sal_Bool                bShrink;

	sal_Bool				bPixelToLogic;
	sal_Bool				bCellContrast;

	Color				aBackConfigColor;		// used for ScPatternAttr::GetFont calls
	Color				aTextConfigColor;

public:
				ScDrawStringsVars(ScOutputData* pData, sal_Bool bPTL);
				~ScDrawStringsVars();

				//	SetPattern = ex-SetVars
				//	SetPatternSimple: ohne Font

	void		SetPattern( const ScPatternAttr* pNew, const SfxItemSet* pSet, ScBaseCell* pCell, sal_uInt8 nScript );
	void		SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet );

	sal_Bool		SetText( ScBaseCell* pCell );	// sal_True -> pOldPattern vergessen
    void        SetTextToWidthOrHash( ScBaseCell* pCell, long nWidth );
	void		SetAutoText( const String& rAutoText );

	const ScPatternAttr*	GetPattern() const		{ return pPattern; }
	SvxCellOrientation		GetOrient() const		{ return eAttrOrient; }
	SvxCellHorJustify		GetHorJust() const		{ return eAttrHorJust; }
	SvxCellVerJustify		GetVerJust() const		{ return eAttrVerJust; }
	const SvxMarginItem*	GetMargin() const		{ return pMargin; }

	sal_uInt16	GetLeftTotal() const		{ return pMargin->GetLeftMargin() + nIndent; }

	const String&			GetString() const		{ return aString; }
	const Size&				GetTextSize() const		{ return aTextSize; }
	long					GetOriginalWidth() const { return nOriginalWidth; }

    sal_uLong   GetResultValueFormat( const ScBaseCell* pCell ) const;

	sal_uLong	GetValueFormat() const					{ return nValueFormat; }
	sal_Bool	GetLineBreak() const					{ return bLineBreak; }
	sal_Bool    IsRepeat() const                        { return bRepeat; }
    sal_Bool    IsShrink() const                        { return bShrink; }

	long	GetAscent() const	{ return nAscentPixel; }
	sal_Bool	IsRotated() const	{ return bRotated; }

    void    SetShrinkScale( long nScale, sal_uInt8 nScript );

	sal_Bool	HasCondHeight() const	{ return pCondSet && SFX_ITEM_SET ==
										pCondSet->GetItemState( ATTR_FONT_HEIGHT, sal_True ); }

    sal_Bool    HasEditCharacters() const;

private:
    void        SetHashText();
    long        GetMaxDigitWidth();     // in logic units
    long        GetSignWidth();
    long        GetDotWidth();
    long        GetExpWidth();
    void        TextChanged();
};

//==================================================================

ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, sal_Bool bPTL) :
	pOutput		( pData ),
	pPattern	( NULL ),
	pCondSet	( NULL ),
	eAttrOrient	( SVX_ORIENTATION_STANDARD ),
	eAttrHorJust( SVX_HOR_JUSTIFY_STANDARD ),
	eAttrVerJust( SVX_VER_JUSTIFY_BOTTOM ),
	pMargin		( NULL ),
	nIndent		( 0 ),
	bRotated	( sal_False ),
	nOriginalWidth( 0 ),
    nMaxDigitWidth( 0 ),
    nSignWidth( 0 ),
    nDotWidth( 0 ),
    nExpWidth( 0 ),
	pLastCell	( NULL ),
	nValueFormat( 0 ),
	bLineBreak	( sal_False ),
    bRepeat     ( sal_False ),
    bShrink     ( sal_False ),
	bPixelToLogic( bPTL )
{
	ScModule* pScMod = SC_MOD();
	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	bCellContrast = pOutput->bUseStyleColor &&
			Application::GetSettings().GetStyleSettings().GetHighContrastMode();

    const svtools::ColorConfig& rColorConfig = pScMod->GetColorConfig();
    aBackConfigColor.SetColor( rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor );
    aTextConfigColor.SetColor( rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor );
}

ScDrawStringsVars::~ScDrawStringsVars()
{
}

void ScDrawStringsVars::SetShrinkScale( long nScale, sal_uInt8 nScript )
{
    // text remains valid, size is updated

    OutputDevice* pDev = pOutput->pDev;
    OutputDevice* pRefDevice = pOutput->pRefDevice;
    OutputDevice* pFmtDevice = pOutput->pFmtDevice;

    // call GetFont with a modified fraction, use only the height

    Fraction aFraction( nScale, 100 );
    if ( !bPixelToLogic )
        aFraction *= pOutput->aZoomY;
    Font aTmpFont;
	pPattern->GetFont( aTmpFont, SC_AUTOCOL_RAW, pFmtDevice, &aFraction, pCondSet, nScript );
	long nNewHeight = aTmpFont.GetHeight();
	if ( nNewHeight > 0 )
        aFont.SetHeight( nNewHeight );

    // set font and dependent variables as in SetPattern

	pDev->SetFont( aFont );
	if ( pFmtDevice != pDev )
		pFmtDevice->SetFont( aFont );

	aMetric = pFmtDevice->GetFontMetric();
	if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetIntLeading() == 0 )
	{
		OutputDevice* pDefaultDev = Application::GetDefaultDevice();
		MapMode aOld = pDefaultDev->GetMapMode();
		pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
		aMetric = pDefaultDev->GetFontMetric( aFont );
		pDefaultDev->SetMapMode( aOld );
	}

	nAscentPixel = aMetric.GetAscent();
	if ( bPixelToLogic )
		nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();

    SetAutoText( aString );     // same text again, to get text size
}

void ScDrawStringsVars::SetPattern( const ScPatternAttr* pNew, const SfxItemSet* pSet,
									ScBaseCell* pCell, sal_uInt8 nScript )
{
    nMaxDigitWidth = 0;
    nSignWidth     = 0;
    nDotWidth      = 0;
    nExpWidth      = 0;

	pPattern = pNew;
	pCondSet = pSet;

	//	pPattern auswerten

	OutputDevice* pDev = pOutput->pDev;
	OutputDevice* pRefDevice = pOutput->pRefDevice;
	OutputDevice* pFmtDevice = pOutput->pFmtDevice;

	//	Font

	ScAutoFontColorMode eColorMode;
	if ( pOutput->bUseStyleColor )
	{
		if ( pOutput->bForceAutoColor )
			eColorMode = bCellContrast ? SC_AUTOCOL_IGNOREALL : SC_AUTOCOL_IGNOREFONT;
		else
			eColorMode = bCellContrast ? SC_AUTOCOL_IGNOREBACK : SC_AUTOCOL_DISPLAY;
	}
	else
		eColorMode = SC_AUTOCOL_PRINT;

	if ( bPixelToLogic )
		pPattern->GetFont( aFont, eColorMode, pFmtDevice, NULL, pCondSet, nScript,
							&aBackConfigColor, &aTextConfigColor );
	else
		pPattern->GetFont( aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript,
							&aBackConfigColor, &aTextConfigColor );
	aFont.SetAlign(ALIGN_BASELINE);

	//	Orientierung

    eAttrOrient = pPattern->GetCellOrientation( pCondSet );

	//	alignment

    eAttrHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet )).GetValue();

    eAttrVerJust = (SvxCellVerJustify)((const SvxVerJustifyItem&)pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet )).GetValue();
	if ( eAttrVerJust == SVX_VER_JUSTIFY_STANDARD )
		eAttrVerJust = SVX_VER_JUSTIFY_BOTTOM;

	//	line break

    bLineBreak = ((const SfxBoolItem&)pPattern->GetItem( ATTR_LINEBREAK, pCondSet )).GetValue();

    //  handle "repeat" alignment

    bRepeat = ( eAttrHorJust == SVX_HOR_JUSTIFY_REPEAT );
    if ( bRepeat )
    {
        // "repeat" disables rotation (before constructing the font)
        eAttrOrient = SVX_ORIENTATION_STANDARD;

        // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled)
        if ( bLineBreak )
            eAttrHorJust = SVX_HOR_JUSTIFY_STANDARD;
    }

	short nRot;
	switch (eAttrOrient)
	{
		case SVX_ORIENTATION_STANDARD:
			nRot = 0;
            bRotated = (((const SfxInt32Item&)pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet )).GetValue() != 0) &&
                       !bRepeat;
			break;
		case SVX_ORIENTATION_STACKED:
			nRot = 0;
			bRotated = sal_False;
			break;
		case SVX_ORIENTATION_TOPBOTTOM:
			nRot = 2700;
			bRotated = sal_False;
			break;
		case SVX_ORIENTATION_BOTTOMTOP:
			nRot = 900;
			bRotated = sal_False;
			break;
		default:
			DBG_ERROR("Falscher SvxCellOrientation Wert");
			nRot = 0;
			bRotated = sal_False;
			break;
	}
	aFont.SetOrientation( nRot );

	//	Syntax-Modus

	if (pOutput->bSyntaxMode)
		pOutput->SetSyntaxColor( &aFont, pCell );

	pDev->SetFont( aFont );
	if ( pFmtDevice != pDev )
		pFmtDevice->SetFont( aFont );

	aMetric = pFmtDevice->GetFontMetric();

	//
	//	Wenn auf dem Drucker das Leading 0 ist, gibt es Probleme
	//	-> Metric vom Bildschirm nehmen (wie EditEngine!)
	//

	if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetIntLeading() == 0 )
	{
		OutputDevice* pDefaultDev = Application::GetDefaultDevice();
		MapMode aOld = pDefaultDev->GetMapMode();
		pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
		aMetric = pDefaultDev->GetFontMetric( aFont );
		pDefaultDev->SetMapMode( aOld );
	}

	nAscentPixel = aMetric.GetAscent();
	if ( bPixelToLogic )
		nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();

    Color aULineColor( ((const SvxUnderlineItem&)pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet )).GetColor() );
	pDev->SetTextLineColor( aULineColor );

    Color aOLineColor( ((const SvxOverlineItem&)pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet )).GetColor() );
	pDev->SetOverlineColor( aOLineColor );

	//	Zahlenformat

//    sal_uLong nOld = nValueFormat;
	nValueFormat = pPattern->GetNumberFormat( pOutput->pDoc->GetFormatTable(), pCondSet );

/*	s.u.
	if (nValueFormat != nOld)
		pLastCell = NULL;			// immer neu formatieren
*/
	//	Raender

    pMargin = (const SvxMarginItem*)&pPattern->GetItem( ATTR_MARGIN, pCondSet );
	if ( eAttrHorJust == SVX_HOR_JUSTIFY_LEFT )
        nIndent = ((const SfxUInt16Item&)pPattern->GetItem( ATTR_INDENT, pCondSet )).GetValue();
	else
		nIndent = 0;

    //  "Shrink to fit"

    bShrink = static_cast<const SfxBoolItem&>(pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue();

	//	zumindest die Text-Groesse muss neu geholt werden
	//!	unterscheiden, und den Text nicht neu vom Numberformatter holen?

	pLastCell = NULL;
}

void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet )
{
    nMaxDigitWidth = 0;
    nSignWidth     = 0;
    nDotWidth      = 0;
    nExpWidth      = 0;
	//	wird gerufen, wenn sich die Font-Variablen nicht aendern (!StringDiffer)

	pPattern = pNew;
	pCondSet = pSet;		//! noetig ???

	//	Zahlenformat

	sal_uLong nOld = nValueFormat;
//	nValueFormat = pPattern->GetNumberFormat( pFormatter );
	const SfxPoolItem* pFormItem;
	if ( !pCondSet || pCondSet->GetItemState(ATTR_VALUE_FORMAT,sal_True,&pFormItem) != SFX_ITEM_SET )
		pFormItem = &pPattern->GetItem(ATTR_VALUE_FORMAT);
	const SfxPoolItem* pLangItem;
	if ( !pCondSet || pCondSet->GetItemState(ATTR_LANGUAGE_FORMAT,sal_True,&pLangItem) != SFX_ITEM_SET )
		pLangItem = &pPattern->GetItem(ATTR_LANGUAGE_FORMAT);
	nValueFormat = pOutput->pDoc->GetFormatTable()->GetFormatForLanguageIfBuiltIn(
					((SfxUInt32Item*)pFormItem)->GetValue(),
					((SvxLanguageItem*)pLangItem)->GetLanguage() );

	if (nValueFormat != nOld)
		pLastCell = NULL;			// immer neu formatieren

	//	Raender

    pMargin = (const SvxMarginItem*)&pPattern->GetItem( ATTR_MARGIN, pCondSet );

	if ( eAttrHorJust == SVX_HOR_JUSTIFY_LEFT )
        nIndent = ((const SfxUInt16Item&)pPattern->GetItem( ATTR_INDENT, pCondSet )).GetValue();
	else
		nIndent = 0;

    //  "Shrink to fit"

    bShrink = static_cast<const SfxBoolItem&>(pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue();
}

inline sal_Bool SameValue( ScBaseCell* pCell, ScBaseCell* pOldCell )	// pCell ist != 0
{
	return pOldCell && pOldCell->GetCellType() == CELLTYPE_VALUE &&
			pCell->GetCellType() == CELLTYPE_VALUE &&
			((ScValueCell*)pCell)->GetValue() == ((ScValueCell*)pOldCell)->GetValue();
}

sal_Bool ScDrawStringsVars::SetText( ScBaseCell* pCell )
{
	sal_Bool bChanged = sal_False;

	if (pCell)
	{
		if ( !SameValue( pCell, pLastCell ) )
		{
			pLastCell = pCell;			//	Zelle merken

			Color* pColor;
			sal_uLong nFormat = GetValueFormat();
			ScCellFormat::GetString( pCell,
									 nFormat, aString, &pColor,
									 *pOutput->pDoc->GetFormatTable(),
									 pOutput->bShowNullValues,
									 pOutput->bShowFormulas,
									 ftCheck );

			if (aString.Len() > DRAWTEXT_MAX)
				aString.Erase(DRAWTEXT_MAX);

			if ( pColor && !pOutput->bSyntaxMode && !( pOutput->bUseStyleColor && pOutput->bForceAutoColor ) )
			{
				OutputDevice* pDev = pOutput->pDev;
				aFont.SetColor(*pColor);
				pDev->SetFont( aFont );	// nur fuer Ausgabe
				bChanged = sal_True;
				pLastCell = NULL;		// naechstes Mal wieder hierherkommen
			}

            TextChanged();
		}
		//	sonst String/Groesse behalten
	}
	else
	{
		aString.Erase();
		pLastCell = NULL;
		aTextSize = Size(0,0);
		nOriginalWidth = 0;
	}

	return bChanged;
}

void ScDrawStringsVars::SetHashText()
{
    SetAutoText( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("###")) );
}

void ScDrawStringsVars::SetTextToWidthOrHash( ScBaseCell* pCell, long nWidth )
{
    // #i113045# do the single-character width calculations in logic units
    if (bPixelToLogic)
        nWidth = pOutput->pRefDevice->PixelToLogic(Size(nWidth,0)).Width();

    if (!pCell)
        return;

    CellType eType = pCell->GetCellType();
    if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA)
        // must be a value or formula cell.
        return;

    if (eType == CELLTYPE_FORMULA)
    {
        ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell);
        if (pFCell->GetErrCode() != 0 || pOutput->bShowFormulas)
        {
            SetHashText();      // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#)
            return;
        }
        // If it's formula, the result must be a value.
        if (!pFCell->IsValue())
            return;
    }

    sal_uLong nFormat = GetResultValueFormat(pCell);
    if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
    {
        // Not 'General' number format.  Set hash text and bail out.
        SetHashText();
        return;
    }

    double fVal = (eType == CELLTYPE_VALUE) ? 
        static_cast<ScValueCell*>(pCell)->GetValue() : static_cast<ScFormulaCell*>(pCell)->GetValue();

    const SvNumberformat* pNumFormat = pOutput->pDoc->GetFormatTable()->GetEntry(nFormat);
    if (!pNumFormat)
        return;

    long nMaxDigit = GetMaxDigitWidth();
    sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);

    if (!pNumFormat->GetOutputString(fVal, nNumDigits, aString))
        // Failed to get output string.  Bail out.
        return;

    sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0;
    xub_StrLen nLen = aString.Len();
    sal_Unicode cDecSep = ScGlobal::GetpLocaleData()->getLocaleItem().decimalSeparator.getStr()[0];
    for (xub_StrLen i = 0; i < nLen; ++i)
    {
        sal_Unicode c = aString.GetChar(i);
        if (c == sal_Unicode('-'))
            ++nSignCount;
        else if (c == cDecSep)
            ++nDecimalCount;
        else if (c == sal_Unicode('E'))
            ++nExpCount;
    }

    // #i112250# A small value might be formatted as "0" when only counting the digits,
    // but fit into the column when considering the smaller width of the decimal separator.
    if (aString.EqualsAscii("0") && fVal != 0.0)
        nDecimalCount = 1;

    if (nDecimalCount)
        nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount;
    if (nSignCount)
        nWidth += (nMaxDigit - GetSignWidth()) * nSignCount;
    if (nExpCount)
        nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;

    if (nDecimalCount || nSignCount || nExpCount)
    {
        // Re-calculate.
        nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);    
        if (!pNumFormat->GetOutputString(fVal, nNumDigits, aString))
            // Failed to get output string.  Bail out.
            return;
    }

    long nActualTextWidth = pOutput->pFmtDevice->GetTextWidth(aString);
    if (nActualTextWidth > nWidth)
    {
        // Even after the decimal adjustment the text doesn't fit.  Give up.
        SetHashText();
        return;
    }

    TextChanged();
    pLastCell = NULL;   // #i113022# equal cell and format in another column may give different string
}

void ScDrawStringsVars::SetAutoText( const String& rAutoText )
{
	aString = rAutoText;

	OutputDevice* pRefDevice = pOutput->pRefDevice;
	OutputDevice* pFmtDevice = pOutput->pFmtDevice;
	aTextSize.Width() = pFmtDevice->GetTextWidth( aString );
	aTextSize.Height() = pFmtDevice->GetTextHeight();

	if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
	{
		double fMul = pOutput->GetStretch();
		aTextSize.Width() = (long)(aTextSize.Width() / fMul + 0.5);
	}

	aTextSize.Height() = aMetric.GetAscent() + aMetric.GetDescent();
	if ( GetOrient() != SVX_ORIENTATION_STANDARD )
	{
		long nTemp = aTextSize.Height();
		aTextSize.Height() = aTextSize.Width();
		aTextSize.Width() = nTemp;
	}

	nOriginalWidth = aTextSize.Width();
	if ( bPixelToLogic )
		aTextSize = pRefDevice->LogicToPixel( aTextSize );

	pLastCell = NULL;		// derselbe Text kann in der naechsten Zelle wieder passen
}

long ScDrawStringsVars::GetMaxDigitWidth()
{
    if (nMaxDigitWidth > 0)
        return nMaxDigitWidth;

    sal_Char cZero = '0';
    for (sal_Char i = 0; i < 10; ++i)
    {
        sal_Char cDigit = cZero + i;    
        long n = pOutput->pFmtDevice->GetTextWidth(String(cDigit));
        nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
    }
    return nMaxDigitWidth;
}

long ScDrawStringsVars::GetSignWidth()
{
    if (nSignWidth > 0)
        return nSignWidth;

    nSignWidth = pOutput->pFmtDevice->GetTextWidth(String('-'));
    return nSignWidth;
}

long ScDrawStringsVars::GetDotWidth()
{
    if (nDotWidth > 0)
        return nDotWidth;

    const ::rtl::OUString& sep = ScGlobal::GetpLocaleData()->getLocaleItem().decimalSeparator;
    nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep);
    return nDotWidth;
}

long ScDrawStringsVars::GetExpWidth()
{
    if (nExpWidth > 0)
        return nExpWidth;

    nExpWidth = pOutput->pFmtDevice->GetTextWidth(String('E'));
    return nExpWidth;
}

void ScDrawStringsVars::TextChanged()
{
    OutputDevice* pRefDevice = pOutput->pRefDevice;
    OutputDevice* pFmtDevice = pOutput->pFmtDevice;
    aTextSize.Width() = pFmtDevice->GetTextWidth( aString );
    aTextSize.Height() = pFmtDevice->GetTextHeight();

    if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
    {
        double fMul = pOutput->GetStretch();
        aTextSize.Width() = (long)(aTextSize.Width() / fMul + 0.5);
    }

    aTextSize.Height() = aMetric.GetAscent() + aMetric.GetDescent();
    if ( GetOrient() != SVX_ORIENTATION_STANDARD )
    {
        long nTemp = aTextSize.Height();
        aTextSize.Height() = aTextSize.Width();
        aTextSize.Width() = nTemp;
    }

    nOriginalWidth = aTextSize.Width();
    if ( bPixelToLogic )
        aTextSize = pRefDevice->LogicToPixel( aTextSize );
}

sal_Bool ScDrawStringsVars::HasEditCharacters() const
{
    static const sal_Unicode pChars[] =
    {
        CHAR_NBSP, CHAR_SHY, CHAR_ZWSP, CHAR_LRM, CHAR_RLM, CHAR_NBHY, CHAR_ZWNBSP, 0
    };
    return aString.SearchChar( pChars ) != STRING_NOTFOUND;
}

sal_uLong ScDrawStringsVars::GetResultValueFormat( const ScBaseCell* pCell ) const
{
    // Get the effective number format, including formula result types.
    // This assumes that a formula cell has already been calculated.

    if ( (nValueFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && pCell && pCell->GetCellType() == CELLTYPE_FORMULA )
        return static_cast<const ScFormulaCell*>(pCell)->GetStandardFormat(*pOutput->pDoc->GetFormatTable(), nValueFormat);
    else
        return nValueFormat;
}

//==================================================================

double ScOutputData::GetStretch()
{
	if ( pRefDevice->IsMapMode() )
	{
		//	#95920# If a non-trivial MapMode is set, its scale is now already
		//	taken into account in the OutputDevice's font handling
		//	(OutputDevice::ImplNewFont, see #95414#).
		//	The old handling below is only needed for pixel output.
		return 1.0;
	}

	// calculation in double is faster than Fraction multiplication
	// and doesn't overflow

	if ( pRefDevice == pFmtDevice )
	{
		MapMode aOld = pRefDevice->GetMapMode();
		return ((double)aOld.GetScaleY()) / ((double)aOld.GetScaleX()) * ((double)aZoomY) / ((double)aZoomX);
	}
	else
	{
		// when formatting for printer, device map mode has already been taken care of
		return ((double)aZoomY) / ((double)aZoomX);
	}
}

//==================================================================

//
//	output strings
//

void lcl_DoHyperlinkResult( OutputDevice* pDev, const Rectangle& rRect, ScBaseCell* pCell )
{
    vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() );

    String aCellText;
    String aURL;
    if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA )
    {
        ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell);
        if ( pFCell->IsHyperLinkCell() )
            pFCell->GetURLResult( aURL, aCellText );
    }

    if ( aURL.Len() && pPDFData )
    {
        vcl::PDFExtOutDevBookmarkEntry aBookmark;
        aBookmark.nLinkId = pPDFData->CreateLink( rRect );
        aBookmark.aBookmark = aURL;
        std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
        rBookmarks.push_back( aBookmark );
    }
}

void ScOutputData::SetSyntaxColor( Font* pFont, ScBaseCell* pCell )
{
	if (pCell)
	{
		switch (pCell->GetCellType())
		{
			case CELLTYPE_VALUE:
				pFont->SetColor( *pValueColor );
				break;
			case CELLTYPE_STRING:
				pFont->SetColor( *pTextColor );
				break;
			case CELLTYPE_FORMULA:
				pFont->SetColor( *pFormulaColor );
				break;
            default:
            {
                // added to avoid warnings
            }
		}
	}
}

void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor )
{
	ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 );
	SfxItemSet aSet( rEngine.GetEmptyItemSet() );
	aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) );
	rEngine.QuickSetAttribs( aSet, aSel );
	// function is called with update mode set to FALSE
}

void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, ScBaseCell* pCell )
{
	if (pCell)
	{
		Color aColor;
		switch (pCell->GetCellType())
		{
			case CELLTYPE_VALUE:
				aColor = *pValueColor;
				break;
			case CELLTYPE_STRING:
				aColor = *pTextColor;
				break;
			case CELLTYPE_FORMULA:
				aColor = *pFormulaColor;
				break;
            default:
            {
                // added to avoid warnings
            }
		}
		lcl_SetEditColor( rEngine, aColor );
	}
}

sal_Bool ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY,
									SCCOL& rOverX, SCROW& rOverY,
									sal_Bool bVisRowChanged )
{
	sal_Bool bDoMerge = sal_False;
	sal_Bool bIsLeft = ( nX == nVisX1 );
	sal_Bool bIsTop  = ( nY == nVisY1 ) || bVisRowChanged;

	CellInfo* pInfo = &pRowInfo[nArrY].pCellInfo[nX+1];
	if ( pInfo->bHOverlapped && pInfo->bVOverlapped )
		bDoMerge = bIsLeft && bIsTop;
	else if ( pInfo->bHOverlapped )
		bDoMerge = bIsLeft;
	else if ( pInfo->bVOverlapped )
		bDoMerge = bIsTop;

									// weiter solange versteckt
/*	if (!bDoMerge)
		return sal_False;
*/

	rOverX = nX;
	rOverY = nY;
	sal_Bool bHOver = pInfo->bHOverlapped;
	sal_Bool bVOver = pInfo->bVOverlapped;
	sal_Bool bHidden;

	while (bHOver)				// nY konstant
	{
		--rOverX;
        bHidden = pDoc->ColHidden(rOverX, nTab);
		if ( !bDoMerge && !bHidden )
			return sal_False;

		if (rOverX >= nX1 && !bHidden)
		{
//			rVirtPosX -= pRowInfo[0].pCellInfo[rOverX+1].nWidth;
			bHOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bHOverlapped;
			bVOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bVOverlapped;
		}
		else
		{
//			if (!bClipVirt)
//				rVirtPosX -= (long) (pDoc->GetColWidth( rOverX, nTab ) * nPPTX);
			sal_uInt16 nOverlap = ((ScMergeFlagAttr*)pDoc->GetAttr(
								rOverX, rOverY, nTab, ATTR_MERGE_FLAG ))->GetValue();
			bHOver = ((nOverlap & SC_MF_HOR) != 0);
			bVOver = ((nOverlap & SC_MF_VER) != 0);
		}
	}

	while (bVOver)
	{
		--rOverY;
        bHidden = pDoc->RowHidden(rOverY, nTab);
		if ( !bDoMerge && !bHidden )
			return sal_False;

		if (nArrY>0)
			--nArrY;						// lokale Kopie !

		if (rOverX >= nX1 && rOverY >= nY1 &&
            !pDoc->ColHidden(rOverX, nTab) &&
            !pDoc->RowHidden(rOverY, nTab) &&
			pRowInfo[nArrY].nRowNo == rOverY)
		{
//			rVirtPosY -= pRowInfo[nArrY].nHeight;
			bHOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bHOverlapped;
			bVOver = pRowInfo[nArrY].pCellInfo[rOverX+1].bVOverlapped;
		}
		else
		{
//			if (!bClipVirt)
//				rVirtPosY -= (long) (pDoc->GetRowHeight( rOverY, nTab ) * nPPTY);
			sal_uInt16 nOverlap = ((ScMergeFlagAttr*)pDoc->GetAttr(
								rOverX, rOverY, nTab, ATTR_MERGE_FLAG ))->GetValue();
			bHOver = ((nOverlap & SC_MF_HOR) != 0);
			bVOver = ((nOverlap & SC_MF_VER) != 0);
		}
	}

	return sal_True;
}

inline sal_Bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr*& rpNewPattern )
{
	DBG_ASSERT( rpNewPattern, "pNewPattern" );

	if ( rpNewPattern == rpOldPattern )
		return sal_False;
	else if ( !rpOldPattern )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT ) != &rpOldPattern->GetItem( ATTR_FONT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_FONT_POSTURE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ) != &rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_UNDERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_OVERLINE ) != &rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_WORDLINE ) != &rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ) != &rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_CONTOUR ) != &rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_SHADOWED ) != &rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_COLOR ) != &rpOldPattern->GetItem( ATTR_FONT_COLOR ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_HOR_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_VER_JUSTIFY ) != &rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) )
		return sal_True;
    else if ( &rpNewPattern->GetItem( ATTR_STACKED ) != &rpOldPattern->GetItem( ATTR_STACKED ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_LINEBREAK ) != &rpOldPattern->GetItem( ATTR_LINEBREAK ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_MARGIN ) != &rpOldPattern->GetItem( ATTR_MARGIN ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_ROTATE_VALUE ) != &rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FORBIDDEN_RULES ) != &rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ) != &rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_FONT_RELIEF ) != &rpOldPattern->GetItem( ATTR_FONT_RELIEF ) )
		return sal_True;
	else if ( &rpNewPattern->GetItem( ATTR_BACKGROUND ) != &rpOldPattern->GetItem( ATTR_BACKGROUND ) )
		return sal_True;	// needed with automatic text color
	else
	{
		rpOldPattern = rpNewPattern;
		return sal_False;
	}
}

inline void lcl_CreateInterpretProgress( sal_Bool& bProgress, ScDocument* pDoc,
		ScFormulaCell* pFCell )
{
	if ( !bProgress && pFCell->GetDirty() )
	{
		ScProgress::CreateInterpretProgress( pDoc, sal_True );
		bProgress = sal_True;
	}
}

inline sal_uInt8 GetScriptType( ScDocument* pDoc, ScBaseCell* pCell,
							const ScPatternAttr* pPattern,
							const SfxItemSet* pCondSet )
{
	return pDoc->GetCellScriptType( pCell, pPattern->GetNumberFormat( pDoc->GetFormatTable(), pCondSet ) );
}

inline sal_Bool IsAmbiguousScript( sal_uInt8 nScript )
{
	return ( nScript != SCRIPTTYPE_LATIN &&
			 nScript != SCRIPTTYPE_ASIAN &&
			 nScript != SCRIPTTYPE_COMPLEX );
}

sal_Bool ScOutputData::IsEmptyCellText( RowInfo* pThisRowInfo, SCCOL nX, SCROW nY )
{
	// pThisRowInfo may be NULL

	sal_Bool bEmpty;
	if ( pThisRowInfo && nX <= nX2 )
		bEmpty = pThisRowInfo->pCellInfo[nX+1].bEmptyCellText;
	else
		bEmpty = ( pDoc->GetCell( ScAddress( nX, nY, nTab ) ) == NULL );

	if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
	{
		//	for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated
		//	into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)

		sal_Bool bIsPrint = ( eType == OUTTYPE_PRINTER );

		if ( bIsPrint || bTabProtected )
		{
			const ScProtectionAttr* pAttr = (const ScProtectionAttr*)
					pDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION );
			if ( bIsPrint && pAttr->GetHidePrint() )
				bEmpty = sal_True;
			else if ( bTabProtected )
			{
				if ( pAttr->GetHideCell() )
					bEmpty = sal_True;
				else if ( bShowFormulas && pAttr->GetHideFormula() )
				{
					ScBaseCell* pCell = pDoc->GetCell( ScAddress( nX, nY, nTab ) );
					if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA )
						bEmpty = sal_True;
				}
			}
		}
	}
	return bEmpty;
}

void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScBaseCell*& rpCell )
{
	pDoc->GetCell( nCol, nRow, nTabP, rpCell );
	if ( rpCell && IsEmptyCellText( NULL, nCol, nRow ) )
		rpCell = NULL;
}

sal_Bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY )
{
	//	apply the same logic here as in DrawStrings/DrawEdit:
	//	Stop at non-empty or merged or overlapped cell,
	//	where a note is empty as well as a cell that's hidden by protection settings

	const ScBaseCell* pCell = pDoc->GetCell( ScAddress( nX, nY, nTab ) );
	if ( pCell && pCell->GetCellType() != CELLTYPE_NOTE && !IsEmptyCellText( NULL, nX, nY ) )
	{
		return sal_False;
	}

	const ScPatternAttr* pPattern = pDoc->GetPattern( nX, nY, nTab );
	if ( ((const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE)).IsMerged() ||
		 ((const ScMergeFlagAttr&)pPattern->GetItem(ATTR_MERGE_FLAG)).IsOverlapped() )
	{
		return sal_False;
	}

	return sal_True;
}

// nX, nArrY:		loop variables from DrawStrings / DrawEdit
// nPosX, nPosY:	corresponding positions for nX, nArrY
// nCellX, nCellY:	position of the cell that contains the text
// nNeeded:			Text width, including margin
// rPattern:		cell format at nCellX, nCellY
// nHorJustify:		horizontal alignment (visual) to determine which cells to use for long strings
// bCellIsValue:	if set, don't extend into empty cells
// bBreak:			if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set)
// bOverwrite:		if set, also extend into non-empty cells (for rotated text)
// rParam           output: various area parameters.

void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, long nPosX, long nPosY,
                                  SCCOL nCellX, SCROW nCellY, long nNeeded,
                                  const ScPatternAttr& rPattern,
                                  sal_uInt16 nHorJustify, bool bCellIsValue,
                                  bool bBreak, bool bOverwrite,
                                  OutputAreaParam& rParam )
{
    //  rThisRowInfo may be for a different row than nCellY, is still used for clip marks
	RowInfo& rThisRowInfo = pRowInfo[nArrY];

	long nLayoutSign = bLayoutRTL ? -1 : 1;

    long nCellPosX = nPosX;         // find nCellX position, starting at nX/nPosX
	SCCOL nCompCol = nX;
	while ( nCellX > nCompCol )
	{
        //! extra member function for width?
		long nColWidth = ( nCompCol <= nX2 ) ?
				pRowInfo[0].pCellInfo[nCompCol+1].nWidth :
				(long) ( pDoc->GetColWidth( nCompCol, nTab ) * nPPTX );
		nCellPosX += nColWidth * nLayoutSign;
		++nCompCol;
	}
	while ( nCellX < nCompCol )
	{
		--nCompCol;
		long nColWidth = ( nCompCol <= nX2 ) ?
				pRowInfo[0].pCellInfo[nCompCol+1].nWidth :
				(long) ( pDoc->GetColWidth( nCompCol, nTab ) * nPPTX );
		nCellPosX -= nColWidth * nLayoutSign;
	}

    long nCellPosY = nPosY;         // find nCellY position, starting at nArrY/nPosY
	SCSIZE nCompArr = nArrY;
	SCROW nCompRow = pRowInfo[nCompArr].nRowNo;
	while ( nCellY > nCompRow )
	{
		if ( nCompArr + 1 < nArrCount )
		{
			nCellPosY += pRowInfo[nCompArr].nHeight;
			++nCompArr;
			nCompRow = pRowInfo[nCompArr].nRowNo;
		}
		else
		{
			sal_uInt16 nDocHeight = pDoc->GetRowHeight( nCompRow, nTab );
			if ( nDocHeight )
				nCellPosY += (long) ( nDocHeight * nPPTY );
			++nCompRow;
		}
	}
    nCellPosY -= (long) pDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, nPPTY );

	const ScMergeAttr* pMerge = (const ScMergeAttr*)&rPattern.GetItem( ATTR_MERGE );
	sal_Bool bMerged = pMerge->IsMerged();
	long nMergeCols = pMerge->GetColMerge();
	if ( nMergeCols == 0 )
		nMergeCols = 1;
	long nMergeRows = pMerge->GetRowMerge();
	if ( nMergeRows == 0 )
		nMergeRows = 1;

	long i;
	long nMergeSizeX = 0;
	for ( i=0; i<nMergeCols; i++ )
	{
		long nColWidth = ( nCellX+i <= nX2 ) ?
				pRowInfo[0].pCellInfo[nCellX+i+1].nWidth :
                (long) ( pDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * nPPTX );
		nMergeSizeX += nColWidth;
	}
	long nMergeSizeY = 0;
	short nDirect = 0;
	if ( rThisRowInfo.nRowNo == nCellY )
	{
		// take first row's height from row info
		nMergeSizeY += rThisRowInfo.nHeight;
        nDirect = 1;        // skip in loop
	}
    // following rows always from document
    nMergeSizeY += (long) pDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, nPPTY);

    --nMergeSizeX;      // leave out the grid horizontally, also for alignment (align between grid lines)

    rParam.mnColWidth = nMergeSizeX; // store the actual column width.

	//
	// construct the rectangles using logical left/right values (justify is called at the end)
	//

    //  rAlignRect is the single cell or merged area, used for alignment.

    rParam.maAlignRect.Left() = nCellPosX;
    rParam.maAlignRect.Right() = nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign;
    rParam.maAlignRect.Top() = nCellPosY;
    rParam.maAlignRect.Bottom() = nCellPosY + nMergeSizeY - 1;

    //  rClipRect is all cells that are used for output.
    //  For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used.

    rParam.maClipRect = rParam.maAlignRect;
	if ( nNeeded > nMergeSizeX )
	{
		SvxCellHorJustify eHorJust = (SvxCellHorJustify)nHorJustify;

		long nMissing = nNeeded - nMergeSizeX;
		long nLeftMissing = 0;
		long nRightMissing = 0;
		switch ( eHorJust )
		{
			case SVX_HOR_JUSTIFY_LEFT:
				nRightMissing = nMissing;
				break;
			case SVX_HOR_JUSTIFY_RIGHT:
				nLeftMissing = nMissing;
				break;
			case SVX_HOR_JUSTIFY_CENTER:
				nLeftMissing = nMissing / 2;
				nRightMissing = nMissing - nLeftMissing;
				break;
            default:
            {
                // added to avoid warnings
            }
		}

		// nLeftMissing, nRightMissing are logical, eHorJust values are visual
		if ( bLayoutRTL )
			::std::swap( nLeftMissing, nRightMissing );

		SCCOL nRightX = nCellX;
		SCCOL nLeftX = nCellX;
		if ( !bMerged && !bCellIsValue && !bBreak )
		{
            //  look for empty cells into which the text can be extended

			while ( nRightMissing > 0 && nRightX < MAXCOL && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) )
			{
				++nRightX;
				long nAdd = (long) ( pDoc->GetColWidth( nRightX, nTab ) * nPPTX );
				nRightMissing -= nAdd;
                rParam.maClipRect.Right() += nAdd * nLayoutSign;

				if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 )
					rThisRowInfo.pCellInfo[nRightX].bHideGrid = sal_True;
			}

			while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) )
			{
				if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 )
					rThisRowInfo.pCellInfo[nLeftX].bHideGrid = sal_True;

				--nLeftX;
				long nAdd = (long) ( pDoc->GetColWidth( nLeftX, nTab ) * nPPTX );
				nLeftMissing -= nAdd;
                rParam.maClipRect.Left() -= nAdd * nLayoutSign;
			}
		}

        //  Set flag and reserve space for clipping mark triangle,
        //  even if rThisRowInfo isn't for nCellY (merged cells).
		if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue )
		{
			rThisRowInfo.pCellInfo[nRightX+1].nClipMark |= SC_CLIPMARK_RIGHT;
			bAnyClipped = sal_True;
			long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX );
            rParam.maClipRect.Right() -= nMarkPixel * nLayoutSign;
		}
		if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue )
		{
			rThisRowInfo.pCellInfo[nLeftX+1].nClipMark |= SC_CLIPMARK_LEFT;
			bAnyClipped = sal_True;
			long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX );
            rParam.maClipRect.Left() += nMarkPixel * nLayoutSign;
		}

        rParam.mbLeftClip = ( nLeftMissing > 0 );
        rParam.mbRightClip = ( nRightMissing > 0 );
	}
	else
	{
        rParam.mbLeftClip = rParam.mbRightClip = sal_False;

        // leave space for AutoFilter on screen
		// (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize)

        if ( eType==OUTTYPE_WINDOW &&
             ( static_cast<const ScMergeFlagAttr&>(rPattern.GetItem(ATTR_MERGE_FLAG)).GetValue() & SC_MF_AUTO ) &&
             ( !bBreak || pRefDevice == pFmtDevice ) )
        {
            // filter drop-down width is now independent from row height
            const long nFilter = DROPDOWN_BITMAP_SIZE;
            sal_Bool bFit = ( nNeeded + nFilter <= nMergeSizeX );
            if ( bFit || bCellIsValue )
            {
                // content fits even in the remaining area without the filter button
                // -> align within that remaining area

                rParam.maAlignRect.Right() -= nFilter * nLayoutSign;
                rParam.maClipRect.Right() -= nFilter * nLayoutSign;

                // if a number doesn't fit, don't hide part of the number behind the button
                // -> set clip flags, so "###" replacement is used (but also within the smaller area)

                if ( !bFit )
                    rParam.mbLeftClip = rParam.mbRightClip = sal_True;
            }
        }
	}

    //  justify both rectangles for alignment calculation, use with DrawText etc.

    rParam.maAlignRect.Justify();
    rParam.maClipRect.Justify();

#if 0
	//! Test !!!
	pDev->Push();
	pDev->SetLineColor();
	pDev->SetFillColor( COL_LIGHTGREEN );
    pDev->DrawRect( pDev->PixelToLogic(rParam.maClipRect) );
    pDev->DrawRect( rParam.maClipRect );    // print preview
	pDev->Pop();
	//! Test !!!
#endif
}

void ScOutputData::DrawStrings( sal_Bool bPixelToLogic )
{
	DBG_ASSERT( pDev == pRefDevice ||
				pDev->GetMapMode().GetMapUnit() == pRefDevice->GetMapMode().GetMapUnit(),
				"DrawStrings: unterschiedliche MapUnits ?!?!" );

    vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() );

	sal_Bool bWasIdleDisabled = pDoc->IsIdleDisabled();
	pDoc->DisableIdle( sal_True );
	Size aMinSize = pRefDevice->PixelToLogic(Size(0,100));		// erst darueber wird ausgegeben
//    sal_uInt32 nMinHeight = aMinSize.Height() / 200;                // 1/2 Pixel

	ScDrawStringsVars aVars( this, bPixelToLogic );

	sal_Bool bProgress = sal_False;

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
		nInitPosX += nMirrorW - 1;				// pixels
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	SCCOL nLastContentCol = MAXCOL;
	if ( nX2 < MAXCOL )
        nLastContentCol = sal::static_int_cast<SCCOL>(
            nLastContentCol - pDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, MAXCOL, nY2, nTab, DIR_RIGHT ) );
	SCCOL nLoopStartX = nX1;
	if ( nX1 > 0 )
		--nLoopStartX;			// start before nX1 for rest of long text to the left

	// variables for GetOutputArea
    OutputAreaParam aAreaParam;
	sal_Bool bCellIsValue = sal_False;
	long nNeededWidth = 0;
	SvxCellHorJustify eOutHorJust = SVX_HOR_JUSTIFY_STANDARD;
	const ScPatternAttr* pPattern = NULL;
	const SfxItemSet* pCondSet = NULL;
    const ScPatternAttr* pOldPattern = NULL;
    const SfxItemSet* pOldCondSet = NULL;
    sal_uInt8 nOldScript = 0;

	long nPosY = nScrY;
	for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		if ( pThisRowInfo->bChanged )
		{
			SCROW nY = pThisRowInfo->nRowNo;
//            long nCellHeight = (long) pThisRowInfo->nHeight;
			long nPosX = nInitPosX;
			if ( nLoopStartX < nX1 )
				nPosX -= pRowInfo[0].pCellInfo[nLoopStartX+1].nWidth * nLayoutSign;
			for (SCCOL nX=nLoopStartX; nX<=nX2; nX++)
			{
				sal_Bool bMergeEmpty = sal_False;
				CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];
				sal_Bool bEmpty = nX < nX1 || pInfo->bEmptyCellText;

				SCCOL nCellX = nX;					// position where the cell really starts
				SCROW nCellY = nY;
				sal_Bool bDoCell = sal_False;
				sal_Bool bNeedEdit = sal_False;

				//
				//	Part of a merged cell?
				//

                sal_Bool bOverlapped = ( pInfo->bHOverlapped || pInfo->bVOverlapped );
                if ( bOverlapped )
				{
					bEmpty = sal_True;

					SCCOL nOverX;					// start of the merged cells
					SCROW nOverY;
					sal_Bool bVisChanged = !pRowInfo[nArrY-1].bChanged;
					if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged ))
					{
						nCellX = nOverX;
						nCellY = nOverY;
						bDoCell = sal_True;
					}
					else
						bMergeEmpty = sal_True;
				}

				//
				//	Rest of a long text further to the left?
				//

				if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped )
				{
					SCCOL nTempX=nX1;
					while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
						--nTempX;

					if ( nTempX < nX1 &&
						 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
						 !pDoc->HasAttrib( nTempX,nY,nTab, nX1,nY,nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ) )
					{
						nCellX = nTempX;
						bDoCell = sal_True;
					}
				}

				//
				//	Rest of a long text further to the right?
				//

				if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped )
				{
					//	don't have to look further than nLastContentCol

					SCCOL nTempX=nX;
					while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
						++nTempX;

					if ( nTempX > nX &&
						 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
						 !pDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ) )
					{
						nCellX = nTempX;
						bDoCell = sal_True;
					}
				}

				//
				//	normal visible cell
				//

				if (!bEmpty)
					bDoCell = sal_True;

				//
				//	don't output the cell that's being edited
				//

				if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
					bDoCell = sal_False;

				//
				//	output the cell text
				//

				ScBaseCell* pCell = NULL;
				if (bDoCell)
				{
					if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
						pCell = pThisRowInfo->pCellInfo[nCellX+1].pCell;
					else
						GetVisibleCell( nCellX, nCellY, nTab, pCell );		// get from document
					if ( !pCell )
						bDoCell = sal_False;
					else if ( pCell->GetCellType() == CELLTYPE_EDIT )
						bNeedEdit = sal_True;
				}
				if (bDoCell && !bNeedEdit)
				{
					if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
					{
						CellInfo& rCellInfo = pThisRowInfo->pCellInfo[nCellX+1];
						pPattern = rCellInfo.pPatternAttr;
						pCondSet = rCellInfo.pConditionSet;

                        if ( !pPattern )
                        {
                            // #i68085# pattern from cell info for hidden columns is null,
                            // test for null is quicker than using column flags
                            pPattern = pDoc->GetPattern( nCellX, nCellY, nTab );
                            pCondSet = pDoc->GetCondResult( nCellX, nCellY, nTab );
                        }
					}
					else		// get from document
					{
						pPattern = pDoc->GetPattern( nCellX, nCellY, nTab );
						pCondSet = pDoc->GetCondResult( nCellX, nCellY, nTab );
					}

					sal_uInt8 nScript = GetScriptType( pDoc, pCell, pPattern, pCondSet );
					if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType();
					if ( pPattern != pOldPattern || pCondSet != pOldCondSet ||
						 nScript != nOldScript || bSyntaxMode )
					{
						if ( StringDiffer(pOldPattern,pPattern) ||
							 pCondSet != pOldCondSet || nScript != nOldScript || bSyntaxMode )
							aVars.SetPattern( pPattern, pCondSet, pCell, nScript );
						else
							aVars.SetPatternSimple( pPattern, pCondSet );
						pOldPattern = pPattern;
						pOldCondSet = pCondSet;
						nOldScript = nScript;
					}

					//	use edit engine for rotated, stacked or mixed-script text
					if ( aVars.GetOrient() == SVX_ORIENTATION_STACKED ||
						 aVars.IsRotated() || IsAmbiguousScript(nScript) )
						bNeedEdit = sal_True;
				}
				if (bDoCell && !bNeedEdit)
				{
					sal_Bool bFormulaCell = (pCell->GetCellType() == CELLTYPE_FORMULA );
					if ( bFormulaCell )
						lcl_CreateInterpretProgress( bProgress, pDoc, (ScFormulaCell*)pCell );
					if ( aVars.SetText(pCell) )
						pOldPattern = NULL;
                    bNeedEdit = aVars.HasEditCharacters() ||
					                (bFormulaCell && ((ScFormulaCell*)pCell)->IsMultilineResult());                
                }
                long nTotalMargin = 0;
                if (bDoCell && !bNeedEdit)
                {
					CellType eCellType = pCell->GetCellType();
					bCellIsValue = ( eCellType == CELLTYPE_VALUE );
					if ( eCellType == CELLTYPE_FORMULA )
					{
						ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
						bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
					}

					eOutHorJust = ( aVars.GetHorJust() != SVX_HOR_JUSTIFY_STANDARD ) ?
								  aVars.GetHorJust() :
								  ( bCellIsValue ? SVX_HOR_JUSTIFY_RIGHT : SVX_HOR_JUSTIFY_LEFT );

					if ( eOutHorJust == SVX_HOR_JUSTIFY_BLOCK || eOutHorJust == SVX_HOR_JUSTIFY_REPEAT )
						eOutHorJust = SVX_HOR_JUSTIFY_LEFT;		// repeat is not yet implemented

					sal_Bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SVX_HOR_JUSTIFY_BLOCK );

                    // #i111387# #o11817313# disable automatic line breaks only for "General" number format
                    if ( bBreak && bCellIsValue && ( aVars.GetResultValueFormat(pCell) % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 )
                        bBreak = sal_False;

					sal_Bool bRepeat = aVars.IsRepeat() && !bBreak;
					sal_Bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat;

                    nTotalMargin = 
                        static_cast<long>(aVars.GetLeftTotal() * nPPTX) + 
                        static_cast<long>(aVars.GetMargin()->GetRightMargin() * nPPTX);

                    nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin;

					// GetOutputArea gives justfied rectangles
					GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth,
                                   *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
                                   bCellIsValue || bRepeat || bShrink, bBreak, sal_False,
                                   aAreaParam );

                    if ( bShrink )
                    {
                        if ( aVars.GetOrient() != SVX_ORIENTATION_STANDARD )
                        {
                            // Only horizontal scaling is handled here.
                            // DrawEdit is used to vertically scale 90 deg rotated text.
                            bNeedEdit = sal_True;
                        }
                        else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip )     // horizontal
                        {
                            long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
                            long nScaleSize = aVars.GetTextSize().Width();         // without margin

                            if ( nScaleSize > 0 )       // 0 if the text is empty (formulas, number formats)
                            {
                                long nScale = ( nAvailable * 100 ) / nScaleSize;

                                aVars.SetShrinkScale( nScale, nOldScript );
                                long nNewSize = aVars.GetTextSize().Width();

                                sal_uInt16 nShrinkAgain = 0;
                                while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
                                {
                                    // If the text is still too large, reduce the scale again by 10%, until it fits,
                                    // at most 7 times (it's less than 50% of the calculated scale then).

                                    nScale = ( nScale * 9 ) / 10;
                                    aVars.SetShrinkScale( nScale, nOldScript );
                                    nNewSize = aVars.GetTextSize().Width();
                                    ++nShrinkAgain;
                                }
                                // If even at half the size the font still isn't rendered smaller,
                                // fall back to normal clipping (showing ### for numbers).
                                if ( nNewSize <= nAvailable )
                                    aAreaParam.mbLeftClip = aAreaParam.mbRightClip = sal_False;

                                pOldPattern = NULL;
                            }
                        }
                    }

                    if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip )
                    {
                        long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
                        long nRepeatSize = aVars.GetTextSize().Width();         // without margin
                        // When formatting for the printer, the text sizes don't always add up.
                        // Round down (too few repetitions) rather than exceeding the cell size then:
                        if ( pFmtDevice != pRefDevice )
                            ++nRepeatSize;
                        if ( nRepeatSize > 0 )
                        {
                            long nRepeatCount = nAvailable / nRepeatSize;
                            if ( nRepeatCount > 1 )
                            {
                                String aCellStr = aVars.GetString();
                                String aRepeated = aCellStr;
                                for ( long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
                                    aRepeated.Append( aCellStr );
                                aVars.SetAutoText( aRepeated );
                            }
                        }
                    }

					//	use edit engine if automatic line breaks are needed
					if ( bBreak )
					{
						if ( aVars.GetOrient() == SVX_ORIENTATION_STANDARD )
                            bNeedEdit = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip );
						else
						{
							long nHeight = aVars.GetTextSize().Height() +
											(long)(aVars.GetMargin()->GetTopMargin()*nPPTY) +
											(long)(aVars.GetMargin()->GetBottomMargin()*nPPTY);
                            bNeedEdit = ( nHeight > aAreaParam.maClipRect.GetHeight() );
						}
					}
				}
				if (bNeedEdit)
				{
					//	mark the cell in CellInfo to be drawn in DrawEdit:
					//	Cells to the left are marked directly, cells to the
					//	right are handled by the flag for nX2
					SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2;
					RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0];
					pMarkRowInfo->pCellInfo[nMarkX+1].bEditEngine = sal_True;
					bDoCell = sal_False;	// don't draw here
				}
				if ( bDoCell )
				{
                    if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
					{
                        // Adjust the decimals to fit the available column width.
                        aVars.SetTextToWidthOrHash(pCell, aAreaParam.mnColWidth - nTotalMargin);
						nNeededWidth = aVars.GetTextSize().Width() +
									(long) ( aVars.GetLeftTotal() * nPPTX ) +
									(long) ( aVars.GetMargin()->GetRightMargin() * nPPTX );
                        if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() )
                            aAreaParam.mbLeftClip = aAreaParam.mbRightClip = sal_False;

						//	If the "###" replacement doesn't fit into the cells, no clip marks
						//	are shown, as the "###" already denotes too little space.
						//	The rectangles from the first GetOutputArea call remain valid.
					}

                    long nJustPosX = aAreaParam.maAlignRect.Left();		// "justified" - effect of alignment will be added
                    long nJustPosY = aAreaParam.maAlignRect.Top();
                    long nAvailWidth = aAreaParam.maAlignRect.GetWidth();
                    long nOutHeight = aAreaParam.maAlignRect.GetHeight();

                    sal_Bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
                    if ( aAreaParam.maClipRect.Left() < nScrX )
					{
                        aAreaParam.maClipRect.Left() = nScrX;
                        aAreaParam.mbLeftClip = sal_True;
					}
                    if ( aAreaParam.maClipRect.Right() > nScrX + nScrW )
					{
                        aAreaParam.maClipRect.Right() = nScrX + nScrW;			//! minus one?
                        aAreaParam.mbRightClip = sal_True;
					}

                    sal_Bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
					sal_Bool bVClip = sal_False;

                    if ( aAreaParam.maClipRect.Top() < nScrY )
                    {
                        aAreaParam.maClipRect.Top() = nScrY;
                        bVClip = sal_True;
                    }
                    if ( aAreaParam.maClipRect.Bottom() > nScrY + nScrH )
                    {
                        aAreaParam.maClipRect.Bottom() = nScrY + nScrH;         //! minus one?
                        bVClip = sal_True;
                    }

					//
					//		horizontalen Platz testen
					//

					sal_Bool bRightAdjusted = sal_False;		// to correct text width calculation later
					sal_Bool bNeedEditEngine = sal_False;
					if ( !bNeedEditEngine && !bOutside )
					{
						switch (eOutHorJust)
						{
							case SVX_HOR_JUSTIFY_LEFT:
								nJustPosX += (long) ( aVars.GetLeftTotal() * nPPTX );
								break;
							case SVX_HOR_JUSTIFY_RIGHT:
								nJustPosX += nAvailWidth - aVars.GetTextSize().Width() -
											(long) ( aVars.GetMargin()->GetRightMargin() * nPPTX );
								bRightAdjusted = sal_True;
								break;
							case SVX_HOR_JUSTIFY_CENTER:
								nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() +
											(long) ( aVars.GetLeftTotal() * nPPTX ) -
											(long) ( aVars.GetMargin()->GetRightMargin() * nPPTX ) ) / 2;
								break;
                            default:
                            {
                                // added to avoid warnings
                            }
						}

						long nTestClipHeight = aVars.GetTextSize().Height();
						switch (aVars.GetVerJust())
						{
							case SVX_VER_JUSTIFY_TOP:
								{
									long nTop = (long)( aVars.GetMargin()->GetTopMargin() * nPPTY );
									nJustPosY += nTop;
									nTestClipHeight += nTop;
								}
								break;
							case SVX_VER_JUSTIFY_BOTTOM:
								{
									long nBot = (long)( aVars.GetMargin()->GetBottomMargin() * nPPTY );
									nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot;
									nTestClipHeight += nBot;
								}
								break;
							case SVX_VER_JUSTIFY_CENTER:
								{
									long nTop = (long)( aVars.GetMargin()->GetTopMargin() * nPPTY );
									long nBot = (long)( aVars.GetMargin()->GetBottomMargin() * nPPTY );
									nJustPosY += ( nOutHeight + nTop -
													aVars.GetTextSize().Height() - nBot ) / 2;
									nTestClipHeight += Abs( nTop - nBot );
								}
								break;
                            default:
                            {
                                // added to avoid warnings
                            }
						}

						if ( nTestClipHeight > nOutHeight )
						{
							//	kein vertikales Clipping beim Drucken von Zellen mit
							//	optimaler Hoehe, ausser bei Groesse in bedingter Formatierung
							if ( eType != OUTTYPE_PRINTER ||
									( pDoc->GetRowFlags( nCellY, nTab ) & CR_MANUALSIZE ) ||
									( aVars.HasCondHeight() ) )
								bVClip = sal_True;
						}

						if ( bHClip || bVClip )
						{
							//	nur die betroffene Dimension clippen,
							//	damit bei nicht-proportionalem Resize nicht alle
							//	rechtsbuendigen Zahlen abgeschnitten werden:

							if (!bHClip)
							{
                                aAreaParam.maClipRect.Left() = nScrX;
                                aAreaParam.maClipRect.Right() = nScrX+nScrW;
							}
							if (!bVClip)
							{
                                aAreaParam.maClipRect.Top() = nScrY;
                                aAreaParam.maClipRect.Bottom() = nScrY+nScrH;
							}

							//	aClipRect is not used after SetClipRegion/IntersectClipRegion,
							//	so it can be modified here
							if (bPixelToLogic)
                                aAreaParam.maClipRect = pRefDevice->PixelToLogic( aAreaParam.maClipRect );

							if (bMetaFile)
							{
								pDev->Push();
                                pDev->IntersectClipRegion( aAreaParam.maClipRect );
							}
							else
                                pDev->SetClipRegion( Region( aAreaParam.maClipRect ) );
						}

                        Point aURLStart( nJustPosX, nJustPosY );    // copy before modifying for orientation

						switch (aVars.GetOrient())
						{
							case SVX_ORIENTATION_STANDARD:
								nJustPosY += aVars.GetAscent();
								break;
							case SVX_ORIENTATION_TOPBOTTOM:
								nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent();
								break;
							case SVX_ORIENTATION_BOTTOMTOP:
								nJustPosY += aVars.GetTextSize().Height();
								nJustPosX += aVars.GetAscent();
								break;
                            default:
                            {
                                // added to avoid warnings
                            }
						}

						// When clipping, the visible part is now completely defined by the alignment,
						// there's no more special handling to show the right part of RTL text.

						Point aDrawTextPos( nJustPosX, nJustPosY );
						if ( bPixelToLogic )
						{
							//	undo text width adjustment in pixels
							if (bRightAdjusted)
								aDrawTextPos.X() += aVars.GetTextSize().Width();

							aDrawTextPos = pRefDevice->PixelToLogic( aDrawTextPos );

							//	redo text width adjustment in logic units
							if (bRightAdjusted)
								aDrawTextPos.X() -= aVars.GetOriginalWidth();
						}

						//	in Metafiles immer DrawTextArray, damit die Positionen mit
						//	aufgezeichnet werden (fuer nicht-proportionales Resize):

						String aString = aVars.GetString();
						if (bMetaFile || pFmtDevice != pDev || aZoomX != aZoomY)
						{
							sal_Int32* pDX = new sal_Int32[aString.Len()];
							pFmtDevice->GetTextArray( aString, pDX );

							if ( !pRefDevice->GetConnectMetaFile() ||
									pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
							{
								double fMul = GetStretch();
								xub_StrLen nLen = aString.Len();
								for (xub_StrLen i=0; i<nLen; i++)
									pDX[i] = (long)(pDX[i] / fMul + 0.5);
							}

							pDev->DrawTextArray( aDrawTextPos, aString, pDX );
							delete[] pDX;
						}
						else
							pDev->DrawText( aDrawTextPos, aString );

						if ( bHClip || bVClip )
						{
							if (bMetaFile)
								pDev->Pop();
							else
								pDev->SetClipRegion();
						}

                        // PDF: whole-cell hyperlink from formula?
                        sal_Bool bHasURL = pPDFData && pCell && pCell->GetCellType() == CELLTYPE_FORMULA &&
                                        static_cast<ScFormulaCell*>(pCell)->IsHyperLinkCell();
                        if ( bHasURL )
                        {
                            Rectangle aURLRect( aURLStart, aVars.GetTextSize() );
                            lcl_DoHyperlinkResult( pDev, aURLRect, pCell );
                        }
					}
				}
				nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
			}
		}
		nPosY += pRowInfo[nArrY].nHeight;
	}
	if ( bProgress )
		ScProgress::DeleteInterpretProgress();
	pDoc->DisableIdle( bWasIdleDisabled );
}

//	-------------------------------------------------------------------------------

ScFieldEditEngine* ScOutputData::CreateOutputEditEngine()
{
    ScFieldEditEngine* pEngine = new ScFieldEditEngine( pDoc->GetEnginePool() );
    pEngine->SetUpdateMode( sal_False );
    // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice
    pEngine->SetRefDevice( pFmtDevice );
    sal_uInt32 nCtrl = pEngine->GetControlWord();
    if ( bShowSpellErrors )
        nCtrl |= EE_CNTRL_ONLINESPELLING;
    if ( eType == OUTTYPE_PRINTER )
        nCtrl &= ~EE_CNTRL_MARKFIELDS;
    if ( eType == OUTTYPE_WINDOW && pRefDevice == pFmtDevice )
        nCtrl &= ~EE_CNTRL_FORMAT100;       // use the actual MapMode
    pEngine->SetControlWord( nCtrl );
    pDoc->ApplyAsianEditSettings( *pEngine );
    pEngine->EnableAutoColor( bUseStyleColor );
    pEngine->SetDefaultHorizontalTextDirection( (EEHorizontalTextDirection)pDoc->GetEditTextDirection( nTab ) );
    return pEngine;
}

void lcl_ClearEdit( EditEngine& rEngine )		// Text und Attribute
{
	rEngine.SetUpdateMode( sal_False );

	rEngine.SetText(EMPTY_STRING);
	//	keine Para-Attribute uebrigbehalten...
	const SfxItemSet& rPara = rEngine.GetParaAttribs(0);
	if (rPara.Count())
		rEngine.SetParaAttribs( 0,
					SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) );
}

sal_Bool lcl_SafeIsValue( ScBaseCell* pCell )
{
	if (!pCell)
		return sal_False;

	sal_Bool bRet = sal_False;
	switch ( pCell->GetCellType() )
	{
		case CELLTYPE_VALUE:
			bRet = sal_True;
			break;
		case CELLTYPE_FORMULA:
			{
				ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
				if ( pFCell->IsRunning() || pFCell->IsValue() )
					bRet = sal_True;
			}
			break;
        default:
        {
            // added to avoid warnings
        }
	}
	return bRet;
}

void lcl_ScaleFonts( EditEngine& rEngine, long nPercent )
{
    sal_Bool bUpdateMode = rEngine.GetUpdateMode();
    if ( bUpdateMode )
        rEngine.SetUpdateMode( sal_False );

    sal_uInt16 nParCount = rEngine.GetParagraphCount();
    for (sal_uInt16 nPar=0; nPar<nParCount; nPar++)
    {
        SvUShorts aPortions;
        rEngine.GetPortions( nPar, aPortions );

        sal_uInt16 nPCount = aPortions.Count();
        sal_uInt16 nStart = 0;
        for ( sal_uInt16 nPos=0; nPos<nPCount; nPos++ )
        {
            sal_uInt16 nEnd = aPortions.GetObject( nPos );
            ESelection aSel( nPar, nStart, nPar, nEnd );
            SfxItemSet aAttribs = rEngine.GetAttribs( aSel );

            long nWestern = static_cast<const SvxFontHeightItem&>(aAttribs.Get(EE_CHAR_FONTHEIGHT)).GetHeight();
            long nCJK = static_cast<const SvxFontHeightItem&>(aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK)).GetHeight();
            long nCTL = static_cast<const SvxFontHeightItem&>(aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL)).GetHeight();

            nWestern = ( nWestern * nPercent ) / 100;
            nCJK     = ( nCJK     * nPercent ) / 100;
            nCTL     = ( nCTL     * nPercent ) / 100;

            aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) );
            aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) );
            aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) );

            rEngine.QuickSetAttribs( aAttribs, aSel );      //! remove paragraph attributes from aAttribs?

            nStart = nEnd;
        }
    }

    if ( bUpdateMode )
        rEngine.SetUpdateMode( sal_True );
}

long lcl_GetEditSize( EditEngine& rEngine, sal_Bool bWidth, sal_Bool bSwap, long nAttrRotate )
{
    if ( bSwap )
        bWidth = !bWidth;

    if ( nAttrRotate )
    {
        long nRealWidth  = (long) rEngine.CalcTextWidth();
        long nRealHeight = rEngine.GetTextHeight();

        // assuming standard mode, otherwise width isn't used

        double nRealOrient = nAttrRotate * F_PI18000;	// 1/100th degrees
        double nAbsCos = fabs( cos( nRealOrient ) );
        double nAbsSin = fabs( sin( nRealOrient ) );
        if ( bWidth )
            return (long) ( nRealWidth * nAbsCos + nRealHeight * nAbsSin );
        else
            return (long) ( nRealHeight * nAbsCos + nRealWidth * nAbsSin );
    }
    else if ( bWidth )
        return (long) rEngine.CalcTextWidth();
    else
        return rEngine.GetTextHeight();
}


void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const Rectangle& rAlignRect,
            long nLeftM, long nTopM, long nRightM, long nBottomM,
            sal_Bool bWidth, sal_uInt16 nOrient, long nAttrRotate, sal_Bool bPixelToLogic,
            long& rEngineWidth, long& rEngineHeight, long& rNeededPixel, bool& rLeftClip, bool& rRightClip )
{
    if ( !bWidth )
    {
        // vertical

        long nScaleSize = bPixelToLogic ?
            pRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;

        // Don't scale if it fits already.
        // Allowing to extend into the margin, to avoid scaling at optimal height.
        if ( nScaleSize <= rAlignRect.GetHeight() )
            return;

        sal_Bool bSwap = ( nOrient == SVX_ORIENTATION_TOPBOTTOM || nOrient == SVX_ORIENTATION_BOTTOMTOP );
        long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM;
        long nScale = ( nAvailable * 100 ) / nScaleSize;

        lcl_ScaleFonts( rEngine, nScale );
        rEngineHeight = lcl_GetEditSize( rEngine, sal_False, bSwap, nAttrRotate );
        long nNewSize = bPixelToLogic ?
            pRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;

        sal_uInt16 nShrinkAgain = 0;
        while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
        {
            // further reduce, like in DrawStrings
            lcl_ScaleFonts( rEngine, 90 );     // reduce by 10%
            rEngineHeight = lcl_GetEditSize( rEngine, sal_False, bSwap, nAttrRotate );
            nNewSize = bPixelToLogic ?
                pRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
            ++nShrinkAgain;
        }

        // sizes for further processing (alignment etc):
        rEngineWidth = lcl_GetEditSize( rEngine, sal_True, bSwap, nAttrRotate );
        long nPixelWidth = bPixelToLogic ?
            pRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
        rNeededPixel = nPixelWidth + nLeftM + nRightM;
    }
    else if ( rLeftClip || rRightClip )
    {
        // horizontal

        long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM;
        long nScaleSize = rNeededPixel - nLeftM - nRightM;      // without margin

        if ( nScaleSize <= nAvailable )
            return;

        long nScale = ( nAvailable * 100 ) / nScaleSize;

        lcl_ScaleFonts( rEngine, nScale );
        rEngineWidth = lcl_GetEditSize( rEngine, sal_True, sal_False, nAttrRotate );
        long nNewSize = bPixelToLogic ?
            pRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;

        sal_uInt16 nShrinkAgain = 0;
        while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
        {
            // further reduce, like in DrawStrings
            lcl_ScaleFonts( rEngine, 90 );     // reduce by 10%
            rEngineWidth = lcl_GetEditSize( rEngine, sal_True, sal_False, nAttrRotate );
            nNewSize = bPixelToLogic ?
                pRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
            ++nShrinkAgain;
        }
        if ( nNewSize <= nAvailable )
            rLeftClip = rRightClip = sal_False;

        // sizes for further processing (alignment etc):
        rNeededPixel = nNewSize + nLeftM + nRightM;
        rEngineHeight = lcl_GetEditSize( rEngine, sal_False, sal_False, nAttrRotate );
    }
}

void ScOutputData::DrawEdit(sal_Bool bPixelToLogic)
{
    vcl::PDFExtOutDevData* pPDFData = PTR_CAST( vcl::PDFExtOutDevData, pDev->GetExtOutDevData() );

	Size aMinSize = pRefDevice->PixelToLogic(Size(0,100));		// erst darueber wird ausgegeben
//    sal_uInt32 nMinHeight = aMinSize.Height() / 200;                // 1/2 Pixel

	ScModule* pScMod = SC_MOD();
    sal_Int32 nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	sal_Bool bCellContrast = bUseStyleColor &&
			Application::GetSettings().GetStyleSettings().GetHighContrastMode();

	ScFieldEditEngine* pEngine = NULL;
	sal_Bool bHyphenatorSet = sal_False;
	const ScPatternAttr* pOldPattern = NULL;
	const SfxItemSet*	 pOldCondSet = NULL;
	ScBaseCell* pCell = NULL;

	Size aRefOne = pRefDevice->PixelToLogic(Size(1,1));

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
	{
#if 0
		Size aOnePixel = pDev->PixelToLogic(Size(1,1));
		long nOneX = aOnePixel.Width();
		nInitPosX += nMirrorW - nOneX;
#endif
		nInitPosX += nMirrorW - 1;
	}
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	//!	store nLastContentCol as member!
	SCCOL nLastContentCol = MAXCOL;
	if ( nX2 < MAXCOL )
        nLastContentCol = sal::static_int_cast<SCCOL>(
            nLastContentCol - pDoc->GetEmptyLinesInBlock( nX2+1, nY1, nTab, MAXCOL, nY2, nTab, DIR_RIGHT ) );

	long nRowPosY = nScrY;
	for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++)			// 0 fuer Reste von zusammengefassten
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
//        long nCellHeight = (long) pThisRowInfo->nHeight;
		if (nArrY==1) nRowPosY = nScrY;							// vorher wird einzeln berechnet

		if ( pThisRowInfo->bChanged || nArrY==0 )
		{
			long nPosX = 0;
			for (SCCOL nX=0; nX<=nX2; nX++)					// wegen Ueberhaengen
			{
				if (nX==nX1) nPosX = nInitPosX;					// positions before nX1 are calculated individually

				CellInfo*	pInfo = &pThisRowInfo->pCellInfo[nX+1];
				if (pInfo->bEditEngine)
				{
					SCROW nY = pThisRowInfo->nRowNo;

					SCCOL nCellX = nX;					// position where the cell really starts
					SCROW nCellY = nY;
					sal_Bool bDoCell = sal_False;

					long nPosY = nRowPosY;
					if ( nArrY == 0 )
					{
						nPosY = nScrY;
						nY = pRowInfo[1].nRowNo;
						SCCOL nOverX;					// start of the merged cells
						SCROW nOverY;
						if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, sal_True ))
						{
							nCellX = nOverX;
							nCellY = nOverY;
							bDoCell = sal_True;
						}
					}
					else if ( nX == nX2 && !pThisRowInfo->pCellInfo[nX+1].pCell )
					{
						//	Rest of a long text further to the right?

						SCCOL nTempX=nX;
						while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
							++nTempX;

						if ( nTempX > nX &&
							 !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
							 !pDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ) )
						{
							nCellX = nTempX;
							bDoCell = sal_True;
						}
					}
					else
					{
						bDoCell = sal_True;
					}

					if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
						bDoCell = sal_False;

                    const ScPatternAttr* pPattern = NULL;
                    const SfxItemSet* pCondSet = NULL;
					if (bDoCell)
					{
                        if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 &&
                             !pDoc->ColHidden(nCellX, nTab) )
						{
							CellInfo& rCellInfo = pThisRowInfo->pCellInfo[nCellX+1];
							pPattern = rCellInfo.pPatternAttr;
							pCondSet = rCellInfo.pConditionSet;
							pCell = rCellInfo.pCell;
						}
						else		// get from document
						{
							pPattern = pDoc->GetPattern( nCellX, nCellY, nTab );
							pCondSet = pDoc->GetCondResult( nCellX, nCellY, nTab );
							GetVisibleCell( nCellX, nCellY, nTab, pCell );
						}
						if ( !pCell )
							bDoCell = sal_False;
					}
					if (bDoCell)
					{
						sal_Bool bHidden = sal_False;

						//
						//	Create EditEngine
						//

						if (!pEngine)
                            pEngine = CreateOutputEditEngine();
						else
							lcl_ClearEdit( *pEngine );		// also calls SetUpdateMode(sal_False)

						sal_Bool bCellIsValue = lcl_SafeIsValue(pCell);

						SvxCellHorJustify eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&)
											pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet)).GetValue();
						sal_Bool bBreak = ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) ||
										((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK, pCondSet)).GetValue();
                        sal_Bool bRepeat = ( eHorJust == SVX_HOR_JUSTIFY_REPEAT && !bBreak );
                        sal_Bool bShrink = !bBreak && !bRepeat && static_cast<const SfxBoolItem&>
                                        (pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue();
                        SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
						long nAttrRotate = ((const SfxInt32Item&)pPattern->
											GetItem(ATTR_ROTATE_VALUE, pCondSet)).GetValue();
                        if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT )
                        {
                            // ignore orientation/rotation if "repeat" is active
                            eOrient = SVX_ORIENTATION_STANDARD;
                            nAttrRotate = 0;

                            // #i31843# "repeat" with "line breaks" is treated as default alignment
                            // (but rotation is still disabled)
                            if ( bBreak )
                                eHorJust = SVX_HOR_JUSTIFY_STANDARD;
                        }
						if ( eOrient==SVX_ORIENTATION_STANDARD && nAttrRotate )
						{
							//!	Flag setzen, um die Zelle in DrawRotated wiederzufinden ?
							//!	(oder Flag schon bei DrawBackground, dann hier keine Abfrage)
							bHidden = sal_True;		// gedreht wird getrennt ausgegeben
						}

						sal_Bool bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED &&
								((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() );
						if ( bAsianVertical )
						{
							// in asian mode, use EditEngine::SetVertical instead of EE_CNTRL_ONECHARPERLINE
							eOrient = SVX_ORIENTATION_STANDARD;
							// default alignment for asian vertical mode is top-right
							if ( eHorJust == SVX_HOR_JUSTIFY_STANDARD )
								eHorJust = SVX_HOR_JUSTIFY_RIGHT;
						}

						SvxCellHorJustify eOutHorJust =
							( eHorJust != SVX_HOR_JUSTIFY_STANDARD ) ? eHorJust :
							( bCellIsValue ? SVX_HOR_JUSTIFY_RIGHT : SVX_HOR_JUSTIFY_LEFT );

						if ( eOutHorJust == SVX_HOR_JUSTIFY_BLOCK || eOutHorJust == SVX_HOR_JUSTIFY_REPEAT )
							eOutHorJust = SVX_HOR_JUSTIFY_LEFT;		// repeat is not yet implemented


//!						if ( !bHidden && eType == OUTTYPE_PRINTER &&
//!							pDev->GetOutDevType() == OUTDEV_WINDOW &&
//!							((const SvxFontHeightItem&)pPattern->
//!							GetItem(ATTR_FONT_HEIGHT)).GetHeight() <= nMinHeight )
//!						{
//!							Point aPos( nStartX, nStartY );
//!							pDev->DrawPixel( aPos,
//!											((const SvxColorItem&)pPattern->
//!											GetItem( ATTR_FONT_COLOR )).GetValue() );
//!							bHidden = sal_True;
//!						}

						if (!bHidden)
						{
							//!	mirror margin values for RTL?
							//!	move margin down to after final GetOutputArea call

							const SvxMarginItem* pMargin = (const SvxMarginItem*)
													&pPattern->GetItem(ATTR_MARGIN, pCondSet);
							sal_uInt16 nIndent = 0;
							if ( eHorJust == SVX_HOR_JUSTIFY_LEFT )
								nIndent = ((const SfxUInt16Item&)pPattern->
													GetItem(ATTR_INDENT, pCondSet)).GetValue();

							long nLeftM = (long) ( (pMargin->GetLeftMargin() + nIndent) * nPPTX );
							long nTopM  = (long) ( pMargin->GetTopMargin() * nPPTY );
							long nRightM = (long) ( pMargin->GetRightMargin() * nPPTX );
							long nBottomM = (long) ( pMargin->GetBottomMargin() * nPPTY );

							SCCOL nXForPos = nX;
							if ( nXForPos < nX1 )
							{
								nXForPos = nX1;
								nPosX = nInitPosX;
							}
							SCSIZE nArrYForPos = nArrY;
							if ( nArrYForPos < 1 )
							{
								nArrYForPos = 1;
								nPosY = nScrY;
							}

                            OutputAreaParam aAreaParam;

							//
							//	Initial page size - large for normal text, cell size for automatic line breaks
							//

							Size aPaperSize = Size( 1000000, 1000000 );
							if ( bBreak || eOrient == SVX_ORIENTATION_STACKED || bAsianVertical )
							{
								//!	also stacked, AsianVertical

								//	call GetOutputArea with nNeeded=0, to get only the cell width

								//!	handle nArrY == 0
								GetOutputArea( nXForPos, nArrYForPos, nPosX, nPosY, nCellX, nCellY, 0,
                                               *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
                                               bCellIsValue, true, false, aAreaParam );

								//! special ScEditUtil handling if formatting for printer

								if ( eOrient == SVX_ORIENTATION_TOPBOTTOM || eOrient == SVX_ORIENTATION_BOTTOMTOP )
                                    aPaperSize.Width() = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
								else
                                    aPaperSize.Width() = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;

								if (bAsianVertical && bBreak)
								{
									//	add some extra height (default margin value) for safety
									//	as long as GetEditArea isn't used below
									long nExtraHeight = (long)( 20 * nPPTY );
                                    aPaperSize.Height() = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM + nExtraHeight;
								}
							}
							if (bPixelToLogic)
                            {
                                Size aLogicSize = pRefDevice->PixelToLogic(aPaperSize);
                                if ( bBreak && !bAsianVertical && pRefDevice != pFmtDevice )
                                {
                                    // #i85342# screen display and formatting for printer,
                                    // use same GetEditArea call as in ScViewData::SetEditEngine

                                    Fraction aFract(1,1);
                                    Rectangle aUtilRect = ScEditUtil( pDoc, nCellX, nCellY, nTab, Point(0,0), pFmtDevice,
                                        HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( pPattern, sal_False );
                                    aLogicSize.Width() = aUtilRect.GetWidth();
                                }
                                pEngine->SetPaperSize(aLogicSize);
                            }
							else
								pEngine->SetPaperSize(aPaperSize);

							//
							//	Fill the EditEngine (cell attributes and text)
							//

							SvxCellVerJustify eVerJust = (SvxCellVerJustify)((const SvxVerJustifyItem&)
												pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet)).GetValue();

							// default alignment for asian vertical mode is top-right
							if ( bAsianVertical && eVerJust == SVX_VER_JUSTIFY_STANDARD )
								eVerJust = SVX_VER_JUSTIFY_TOP;

							// syntax highlighting mode is ignored here
							// StringDiffer doesn't look at hyphenate, language items
							if ( pPattern != pOldPattern || pCondSet != pOldCondSet )
							{
								SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() );
								pPattern->FillEditItemSet( pSet, pCondSet );

								pEngine->SetDefaults( pSet );
								pOldPattern = pPattern;
								pOldCondSet = pCondSet;

								sal_uLong nControl = pEngine->GetControlWord();
								if (eOrient==SVX_ORIENTATION_STACKED)
									nControl |= EE_CNTRL_ONECHARPERLINE;
								else
									nControl &= ~EE_CNTRL_ONECHARPERLINE;
								pEngine->SetControlWord( nControl );

								if ( !bHyphenatorSet && ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() )
								{
									//	set hyphenator the first time it is needed
                                    com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
									pEngine->SetHyphenator( xXHyphenator );
									bHyphenatorSet = sal_True;
								}

								Color aBackCol = ((const SvxBrushItem&)
									pPattern->GetItem( ATTR_BACKGROUND, pCondSet )).GetColor();
								if ( bUseStyleColor && ( aBackCol.GetTransparency() > 0 || bCellContrast ) )
									aBackCol.SetColor( nConfBackColor );
								pEngine->SetBackgroundColor( aBackCol );
							}

							//	horizontal alignment now may depend on cell content
							//	(for values with number formats with mixed script types)
							//	-> always set adjustment

							SvxAdjust eSvxAdjust = SVX_ADJUST_LEFT;
							if (eOrient==SVX_ORIENTATION_STACKED)
								eSvxAdjust = SVX_ADJUST_CENTER;
							else if (bBreak)
							{
								if (eOrient==SVX_ORIENTATION_STANDARD && !bAsianVertical)
									switch (eHorJust)
									{
										case SVX_HOR_JUSTIFY_STANDARD:
											eSvxAdjust = bCellIsValue ? SVX_ADJUST_RIGHT : SVX_ADJUST_LEFT;
											break;
										case SVX_HOR_JUSTIFY_LEFT:
										case SVX_HOR_JUSTIFY_REPEAT:			// nicht implementiert
											eSvxAdjust = SVX_ADJUST_LEFT;
											break;
										case SVX_HOR_JUSTIFY_RIGHT:
											eSvxAdjust = SVX_ADJUST_RIGHT;
											break;
										case SVX_HOR_JUSTIFY_CENTER:
											eSvxAdjust = SVX_ADJUST_CENTER;
											break;
										case SVX_HOR_JUSTIFY_BLOCK:
											eSvxAdjust = SVX_ADJUST_BLOCK;
											break;
									}
								else
									switch (eVerJust)
									{
										case SVX_VER_JUSTIFY_TOP:
											eSvxAdjust = (eOrient==SVX_ORIENTATION_TOPBOTTOM || bAsianVertical) ?
														SVX_ADJUST_LEFT : SVX_ADJUST_RIGHT;
											break;
										case SVX_VER_JUSTIFY_CENTER:
											eSvxAdjust = SVX_ADJUST_CENTER;
											break;
										case SVX_VER_JUSTIFY_BOTTOM:
										case SVX_HOR_JUSTIFY_STANDARD:
											eSvxAdjust = (eOrient==SVX_ORIENTATION_TOPBOTTOM || bAsianVertical) ?
														SVX_ADJUST_RIGHT : SVX_ADJUST_LEFT;
											break;
									}
							}
							pEngine->SetDefaultItem( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );

							//	Read content from cell

							sal_Bool bWrapFields = sal_False;
							if (pCell)
							{
								if (pCell->GetCellType() == CELLTYPE_EDIT)
								{
									const EditTextObject* pData;
									((ScEditCell*)pCell)->GetData(pData);

									if (pData)
									{
										pEngine->SetText(*pData);

										if ( bBreak && !bAsianVertical && pData->HasField() )
										{
											//	Fields aren't wrapped, so clipping is enabled to prevent
											//	a field from being drawn beyond the cell size

											bWrapFields = sal_True;
										}
									}
									else 
									{
										DBG_ERROR("pData == 0");
									}
								}
								else
								{
									sal_uLong nFormat = pPattern->GetNumberFormat(
																pDoc->GetFormatTable(), pCondSet );
									String aString;
									Color* pColor;
									ScCellFormat::GetString( pCell,
															 nFormat,aString, &pColor,
															 *pDoc->GetFormatTable(),
															 bShowNullValues,
															 bShowFormulas,
															 ftCheck );

									pEngine->SetText(aString);
									if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
										lcl_SetEditColor( *pEngine, *pColor );
								}

								if ( bSyntaxMode )
									SetEditSyntaxColor( *pEngine, pCell );
								else if ( bUseStyleColor && bForceAutoColor )
									lcl_SetEditColor( *pEngine, COL_AUTO );		//! or have a flag at EditEngine
							}
							else
							{
								DBG_ERROR("pCell == NULL");
							}

							pEngine->SetVertical( bAsianVertical );
							pEngine->SetUpdateMode( sal_True );		// after SetText, before CalcTextWidth/GetTextHeight

							//
							//	Get final output area using the calculated width
							//

							long nEngineWidth;
							if ( bBreak && eOrient != SVX_ORIENTATION_STACKED && !bAsianVertical )
								nEngineWidth = 0;
							else
								nEngineWidth = (long) pEngine->CalcTextWidth();
							long nEngineHeight = pEngine->GetTextHeight();

							if (eOrient != SVX_ORIENTATION_STANDARD &&
								eOrient != SVX_ORIENTATION_STACKED)
							{
								long nTemp = nEngineWidth;
								nEngineWidth = nEngineHeight;
								nEngineHeight = nTemp;
							}

							if (eOrient == SVX_ORIENTATION_STACKED)
								nEngineWidth = nEngineWidth * 11 / 10;

							long nNeededPixel = nEngineWidth;
							if (bPixelToLogic)
								nNeededPixel = pRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
							nNeededPixel += nLeftM + nRightM;

							if ( ( !bBreak && eOrient != SVX_ORIENTATION_STACKED ) || bAsianVertical || bShrink )
							{
								// for break, the first GetOutputArea call is sufficient
								GetOutputArea( nXForPos, nArrYForPos, nPosX, nPosY, nCellX, nCellY, nNeededPixel,
                                               *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
                                               bCellIsValue || bRepeat || bShrink, false, false, aAreaParam );

                                if ( bShrink )
                                {
                                    sal_Bool bWidth = ( eOrient == SVX_ORIENTATION_STANDARD && !bAsianVertical );
                                    ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect,
                                        nLeftM, nTopM, nRightM, nBottomM, bWidth,
                                        sal::static_int_cast<sal_uInt16>(eOrient), 0, bPixelToLogic,
                                        nEngineWidth, nEngineHeight, nNeededPixel, 
                                        aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
                                }

                                if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && pEngine->GetParagraphCount() == 1 )
                                {
                                    // First check if twice the space for the formatted text is available
                                    // (otherwise just keep it unchanged).

                                    long nFormatted = nNeededPixel - nLeftM - nRightM;      // without margin
                                    long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
                                    if ( nAvailable >= 2 * nFormatted )
                                    {
                                        // "repeat" is handled with unformatted text (for performance reasons)
                                        String aCellStr = pEngine->GetText();
                                        pEngine->SetText( aCellStr );

                                        long nRepeatSize = (long) pEngine->CalcTextWidth();
                                        if (bPixelToLogic)
                                            nRepeatSize = pRefDevice->LogicToPixel(Size(nRepeatSize,0)).Width();
                                        if ( pFmtDevice != pRefDevice )
                                            ++nRepeatSize;
                                        if ( nRepeatSize > 0 )
                                        {
                                            long nRepeatCount = nAvailable / nRepeatSize;
                                            if ( nRepeatCount > 1 )
                                            {
                                                String aRepeated = aCellStr;
                                                for ( long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
                                                    aRepeated.Append( aCellStr );
                                                pEngine->SetText( aRepeated );

                                                nEngineHeight = pEngine->GetTextHeight();
                                                nEngineWidth = (long) pEngine->CalcTextWidth();
                                                if (bPixelToLogic)
                                                	nNeededPixel = pRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width();
                                                else
                                                    nNeededPixel = nEngineWidth;
                                                nNeededPixel += nLeftM + nRightM;
                                            }
                                        }
                                    }
                                }

                                if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
								{
									pEngine->SetText( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("###")) );
									nEngineWidth = (long) pEngine->CalcTextWidth();
									if (bPixelToLogic)
										nNeededPixel = pRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width();
									else
										nNeededPixel = nEngineWidth;
									nNeededPixel += nLeftM + nRightM;

									//	No clip marks if "###" doesn't fit (same as in DrawStrings)
								}

								if ( eOutHorJust != SVX_HOR_JUSTIFY_LEFT && eOrient == SVX_ORIENTATION_STANDARD )
								{
									aPaperSize.Width() = nNeededPixel + 1;
									if (bPixelToLogic)
										pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize));
									else
										pEngine->SetPaperSize(aPaperSize);
								}
							}

                            long nStartX = aAreaParam.maAlignRect.Left();
                            long nStartY = aAreaParam.maAlignRect.Top();
                            long nCellWidth = aAreaParam.maAlignRect.GetWidth();
							long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
                            long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;

							if ( bBreak || eOrient != SVX_ORIENTATION_STANDARD || bAsianVertical )
							{
								//	text with automatic breaks is aligned only within the
								//	edit engine's paper size, the output of the whole area
								//	is always left-aligned

								nStartX += nLeftM;
							}
							else
							{
								if ( eOutHorJust == SVX_HOR_JUSTIFY_RIGHT )
									nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
								else if ( eOutHorJust == SVX_HOR_JUSTIFY_CENTER )
									nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
								else
									nStartX += nLeftM;
							}

                            sal_Bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
                            if ( aAreaParam.maClipRect.Left() < nScrX )
							{
                                aAreaParam.maClipRect.Left() = nScrX;
                                aAreaParam.mbLeftClip = true;
							}
                            if ( aAreaParam.maClipRect.Right() > nScrX + nScrW )
							{
                                aAreaParam.maClipRect.Right() = nScrX + nScrW;			//! minus one?
                                aAreaParam.mbRightClip = true;
							}

							if ( !bHidden && !bOutside )
							{
                                bool bClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
								sal_Bool bSimClip = sal_False;

								if ( bWrapFields )
								{
									//	Fields in a cell with automatic breaks: clip to cell width
									bClip = sal_True;
								}

                                if ( aAreaParam.maClipRect.Top() < nScrY )
                                {
                                    aAreaParam.maClipRect.Top() = nScrY;
                                    bClip = sal_True;
                                }
                                if ( aAreaParam.maClipRect.Bottom() > nScrY + nScrH )
                                {
                                    aAreaParam.maClipRect.Bottom() = nScrY + nScrH;     //! minus one?
                                    bClip = sal_True;
                                }

								Size aCellSize;			// output area, excluding margins, in logical units
								if (bPixelToLogic)
									aCellSize = pRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
								else
									aCellSize = Size( nOutWidth, nOutHeight );

								if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
								{
									const ScMergeAttr* pMerge =
											(ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE);
									sal_Bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;

									//	Don't clip for text height when printing rows with optimal height,
									//	except when font size is from conditional formatting.
									//!	Allow clipping when vertically merged?
									if ( eType != OUTTYPE_PRINTER ||
										( pDoc->GetRowFlags( nCellY, nTab ) & CR_MANUALSIZE ) ||
										( pCondSet && SFX_ITEM_SET ==
											pCondSet->GetItemState(ATTR_FONT_HEIGHT, sal_True) ) )
										bClip = sal_True;
									else
										bSimClip = sal_True;

									//	Show clip marks if height is at least 5pt too small and
									//	there are several lines of text.
									//	Not for asian vertical text, because that would interfere
									//	with the default right position of the text.
									//	Only with automatic line breaks, to avoid having to find
									//	the cells with the horizontal end of the text again.
									if ( nEngineHeight - aCellSize.Height() > 100 &&
										 ( bBreak || eOrient == SVX_ORIENTATION_STACKED ) &&
										 !bAsianVertical && bMarkClipped &&
										 ( pEngine->GetParagraphCount() > 1 || pEngine->GetLineCount(0) > 1 ) )
									{
										CellInfo* pClipMarkCell = NULL;
										if ( bMerged )
										{
											//	anywhere in the merged area...
											SCCOL nClipX = ( nX < nX1 ) ? nX1 : nX;
											pClipMarkCell = &pRowInfo[(nArrY != 0) ? nArrY : 1].pCellInfo[nClipX+1];
										}
										else
											pClipMarkCell = &pThisRowInfo->pCellInfo[nX+1];

										pClipMarkCell->nClipMark |= SC_CLIPMARK_RIGHT;		//! also allow left?
										bAnyClipped = sal_True;

										long nMarkPixel = (long)( SC_CLIPMARK_SIZE * nPPTX );
                                        if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
                                            aAreaParam.maClipRect.Right() -= nMarkPixel;
									}
								}

#if 0
								long nClipStartY = nStartY;
								if (nArrY==0 || bVisChanged)
								{
									if ( nClipStartY < nRowPosY )
									{
										long nDif = nRowPosY - nClipStartY;
										bClip = sal_True;
										nClipStartY = nRowPosY;
										aClipSize.Height() -= nDif;
									}
								}
#endif

								Rectangle aLogicClip;
								if (bClip || bSimClip)
								{
									// Clip marks are already handled in GetOutputArea

									if (bPixelToLogic)
                                        aLogicClip = pRefDevice->PixelToLogic( aAreaParam.maClipRect );
									else
                                        aLogicClip = aAreaParam.maClipRect;

									if (bClip)	// bei bSimClip nur aClipRect initialisieren
									{
										if (bMetaFile)
										{
											pDev->Push();
											pDev->IntersectClipRegion( aLogicClip );
										}
										else
											pDev->SetClipRegion( Region( aLogicClip ) );
									}
								}

								Point aLogicStart;
								if (bPixelToLogic)
									aLogicStart = pRefDevice->PixelToLogic( Point(nStartX,nStartY) );
								else
									aLogicStart = Point(nStartX, nStartY);
								if ( eOrient!=SVX_ORIENTATION_STANDARD || bAsianVertical || !bBreak )
								{
									long nAvailWidth = aCellSize.Width();
                                    // space for AutoFilter is already handled in GetOutputArea

									//	horizontal alignment

									if (eOrient==SVX_ORIENTATION_STANDARD && !bAsianVertical)
									{
										if (eHorJust==SVX_HOR_JUSTIFY_RIGHT ||
											eHorJust==SVX_HOR_JUSTIFY_CENTER ||
											(eHorJust==SVX_HOR_JUSTIFY_STANDARD && bCellIsValue) )
										{
											pEngine->SetUpdateMode( sal_False );

											SvxAdjust eEditAdjust =
												(eHorJust==SVX_HOR_JUSTIFY_CENTER) ?
													SVX_ADJUST_CENTER : SVX_ADJUST_RIGHT;
											pEngine->SetDefaultItem(
												SvxAdjustItem( eEditAdjust, EE_PARA_JUST ) );

											// #55142# reset adjustment for the next cell
											pOldPattern = NULL;

											pEngine->SetUpdateMode( sal_True );
										}
									}
									else
									{
										if (eHorJust==SVX_HOR_JUSTIFY_RIGHT)
											aLogicStart.X() += nAvailWidth - nEngineWidth;
										else if (eHorJust==SVX_HOR_JUSTIFY_CENTER)
											aLogicStart.X() += (nAvailWidth - nEngineWidth) / 2;
									}
								}

								if ( bAsianVertical )
								{
									// paper size is subtracted below
									aLogicStart.X() += nEngineWidth;
								}

								if ( ( bAsianVertical || eOrient == SVX_ORIENTATION_TOPBOTTOM ||
										eOrient == SVX_ORIENTATION_BOTTOMTOP ) && bBreak )
								{
									// vertical adjustment is within the EditEngine
									if (bPixelToLogic)
										aLogicStart.Y() += pRefDevice->PixelToLogic(Size(0,nTopM)).Height();
									else
										aLogicStart.Y() += nTopM;
								}

								if ( ( eOrient==SVX_ORIENTATION_STANDARD && !bAsianVertical ) ||
									 eOrient==SVX_ORIENTATION_STACKED || !bBreak )
								{
									if (eVerJust==SVX_VER_JUSTIFY_BOTTOM ||
										eVerJust==SVX_VER_JUSTIFY_STANDARD)
									{
										//!	if pRefDevice != pFmtDevice, keep heights in logic units,
										//! only converting margin?

										if (bPixelToLogic)
											aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0, nTopM +
															pRefDevice->LogicToPixel(aCellSize).Height() -
															pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
															)).Height();
										else
											aLogicStart.Y() += nTopM + aCellSize.Height() - nEngineHeight;
									}
									else if (eVerJust==SVX_VER_JUSTIFY_CENTER)
									{
										if (bPixelToLogic)
											aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0, nTopM + (
															pRefDevice->LogicToPixel(aCellSize).Height() -
															pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
															/ 2)).Height();
										else
											aLogicStart.Y() += nTopM + (aCellSize.Height() - nEngineHeight) / 2;
									}
									else		// top
									{
										if (bPixelToLogic)
											aLogicStart.Y() += pRefDevice->PixelToLogic(Size(0,nTopM)).Height();
										else
											aLogicStart.Y() += nTopM;
									}
								}

                                Point aURLStart = aLogicStart;      // copy before modifying for orientation

								short nOriVal = 0;
								if (eOrient==SVX_ORIENTATION_TOPBOTTOM)
								{
									// nOriVal = -900;
									nOriVal = 2700;
									aLogicStart.X() += nEngineWidth;
								}
								else if (eOrient==SVX_ORIENTATION_BOTTOMTOP)
								{
									nOriVal = 900;
									aLogicStart.Y() += bBreak ? pEngine->GetPaperSize().Width() :
																nEngineHeight;
								}
								else if (eOrient==SVX_ORIENTATION_STACKED)
								{
									Size aPaperLogic = pEngine->GetPaperSize();
									aPaperLogic.Width() = nEngineWidth;
									pEngine->SetPaperSize(aPaperLogic);
								}

								if ( pEngine->IsRightToLeft( 0 ) )
								{
									//	For right-to-left, EditEngine always calculates its lines
									//	beginning from the right edge, but EditLine::nStartPosX is
									//	of sal_uInt16 type, so the PaperSize must be limited to USHRT_MAX.
									Size aLogicPaper = pEngine->GetPaperSize();
									if ( aLogicPaper.Width() > USHRT_MAX )
									{
										aLogicPaper.Width() = USHRT_MAX;
										pEngine->SetPaperSize(aLogicPaper);
									}
								}

								// bMoveClipped handling has been replaced by complete alignment
								// handling (also extending to the left).

								if ( bSimClip && !nOriVal && !bAsianVertical )
								{
									//	kein hartes Clipping, aber nur die betroffenen
									//	Zeilen ausgeben

									Point aDocStart = aLogicClip.TopLeft();
									aDocStart -= aLogicStart;
									pEngine->Draw( pDev, aLogicClip, aDocStart, sal_False );
								}
								else
								{
									if (bAsianVertical)
									{
										//	with SetVertical, the start position is top left of
										//	the whole output area, not the text itself
										aLogicStart.X() -= pEngine->GetPaperSize().Width();
									}
									pEngine->Draw( pDev, aLogicStart, nOriVal );
								}

								if (bClip)
								{
									if (bMetaFile)
										pDev->Pop();
									else
										pDev->SetClipRegion();
								}

                                // PDF: whole-cell hyperlink from formula?
                                sal_Bool bHasURL = pPDFData && pCell && pCell->GetCellType() == CELLTYPE_FORMULA &&
                                                static_cast<ScFormulaCell*>(pCell)->IsHyperLinkCell();
                                if ( bHasURL )
                                {
                                    long nURLWidth = (long) pEngine->CalcTextWidth();
                                    long nURLHeight = pEngine->GetTextHeight();
                                    if ( bBreak )
                                    {
                                        Size aPaper = pEngine->GetPaperSize();
                                        if ( bAsianVertical )
                                            nURLHeight = aPaper.Height();
                                        else
                                            nURLWidth = aPaper.Width();
                                    }
                                    if ( eOrient == SVX_ORIENTATION_TOPBOTTOM || eOrient == SVX_ORIENTATION_BOTTOMTOP )
                                        std::swap( nURLWidth, nURLHeight );
                                    else if ( bAsianVertical )
                                        aURLStart.X() -= nURLWidth;

                                    Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) );
                                    lcl_DoHyperlinkResult( pDev, aURLRect, pCell );
                                }
							}
						}
					}
				}
				nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
			}
		}
		nRowPosY += pRowInfo[nArrY].nHeight;
	}

	delete pEngine;

	if (bAnyRotated)
		DrawRotated(bPixelToLogic);		//! von aussen rufen ?
}

//	-------------------------------------------------------------------------------

void ScOutputData::DrawRotated(sal_Bool bPixelToLogic)
{
	//!	nRotMax speichern
	SCCOL nRotMax = nX2;
	for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
		if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
			nRotMax = pRowInfo[nRotY].nRotMaxCol;


	ScModule* pScMod = SC_MOD();
    sal_Int32 nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
	//	#105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True)
	sal_Bool bCellContrast = bUseStyleColor &&
			Application::GetSettings().GetStyleSettings().GetHighContrastMode();

	ScFieldEditEngine* pEngine = NULL;
	sal_Bool bHyphenatorSet = sal_False;
	const ScPatternAttr* pPattern;
	const SfxItemSet*	 pCondSet;
	const ScPatternAttr* pOldPattern = NULL;
	const SfxItemSet*	 pOldCondSet = NULL;
	ScBaseCell* pCell = NULL;

	long nInitPosX = nScrX;
	if ( bLayoutRTL )
	{
#if 0
		Size aOnePixel = pDev->PixelToLogic(Size(1,1));
		long nOneX = aOnePixel.Width();
		nInitPosX += nMirrorW - nOneX;
#endif
		nInitPosX += nMirrorW - 1;
	}
	long nLayoutSign = bLayoutRTL ? -1 : 1;

	long nRowPosY = nScrY;
	for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++)			// 0 fuer Reste von zusammengefassten
	{
		RowInfo* pThisRowInfo = &pRowInfo[nArrY];
		long nCellHeight = (long) pThisRowInfo->nHeight;
		if (nArrY==1) nRowPosY = nScrY;							// vorher wird einzeln berechnet

		if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE )
		{
			long nPosX = 0;
			for (SCCOL nX=0; nX<=nRotMax; nX++)
			{
				if (nX==nX1) nPosX = nInitPosX;					// positions before nX1 are calculated individually

				CellInfo* pInfo = &pThisRowInfo->pCellInfo[nX+1];
				if ( pInfo->nRotateDir != SC_ROTDIR_NONE )
				{
					SCROW nY = pThisRowInfo->nRowNo;

					sal_Bool bHidden = sal_False;
					if (bEditMode)
						if ( nX == nEditCol && nY == nEditRow )
							bHidden = sal_True;

					if (!bHidden)
					{
						if (!pEngine)
                            pEngine = CreateOutputEditEngine();
						else
							lcl_ClearEdit( *pEngine );		// also calls SetUpdateMode(sal_False)

						long nPosY = nRowPosY;
						sal_Bool bVisChanged = sal_False;

						//!	Rest von zusammengefasster Zelle weiter oben funktioniert nicht!

						sal_Bool bFromDoc = sal_False;
						pPattern = pInfo->pPatternAttr;
						pCondSet = pInfo->pConditionSet;
						if (!pPattern)
						{
							pPattern = pDoc->GetPattern( nX, nY, nTab );
							bFromDoc = sal_True;
						}
						pCell = pInfo->pCell;
						if (bFromDoc)
							pCondSet = pDoc->GetCondResult( nX, nY, nTab );

						if (!pCell && nX>nX2)
							GetVisibleCell( nX, nY, nTab, pCell );

						if ( !pCell || IsEmptyCellText( pThisRowInfo, nX, nY ) )
							bHidden = sal_True;		// nRotateDir is also set without a cell

						long nCellWidth = (long) pRowInfo[0].pCellInfo[nX+1].nWidth;

						SvxCellHorJustify eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&)
											pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet)).GetValue();
						sal_Bool bBreak = ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) ||
									((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK, pCondSet)).GetValue();
                        sal_Bool bRepeat = ( eHorJust == SVX_HOR_JUSTIFY_REPEAT && !bBreak );
                        sal_Bool bShrink = !bBreak && !bRepeat && static_cast<const SfxBoolItem&>
                                        (pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet )).GetValue();
                        SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );

						const ScMergeAttr* pMerge =
								(ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE);
						sal_Bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;

						long nStartX = nPosX;
						long nStartY = nPosY;
						if (nX<nX1)
						{
							if ((bBreak || eOrient!=SVX_ORIENTATION_STANDARD) && !bMerged)
								bHidden = sal_True;
							else
							{
								nStartX = nInitPosX;
								SCCOL nCol = nX1;
								while (nCol > nX)
								{
									--nCol;
									nStartX -= nLayoutSign * (long) pRowInfo[0].pCellInfo[nCol+1].nWidth;
								}
							}
						}
						long nCellStartX = nStartX;

						//	Ersatzdarstellung fuer zu kleinen Text weggelassen

						if (!bHidden)
						{
							long nOutWidth = nCellWidth - 1;
							long nOutHeight;
							if (pInfo)
								nOutHeight = nCellHeight;
							else
								nOutHeight = (long) ( pDoc->GetRowHeight(nY,nTab) * nPPTY );

							if ( bMerged )								// Zusammengefasst
							{
								SCCOL nCountX = pMerge->GetColMerge();
								for (SCCOL i=1; i<nCountX; i++)
									nOutWidth += (long) ( pDoc->GetColWidth(nX+i,nTab) * nPPTX );
								SCROW nCountY = pMerge->GetRowMerge();
                                nOutHeight += (long) pDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, nTab, nPPTY);
							}

							SvxCellVerJustify eVerJust = (SvxCellVerJustify)((const SvxVerJustifyItem&)
												pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet)).GetValue();

							// Syntax-Modus wird hier ignoriert...

							// StringDiffer doesn't look at hyphenate, language items
							if ( pPattern != pOldPattern || pCondSet != pOldCondSet )
							{
								SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() );
								pPattern->FillEditItemSet( pSet, pCondSet );

																	// Ausrichtung fuer EditEngine
								SvxAdjust eSvxAdjust = SVX_ADJUST_LEFT;
								if (eOrient==SVX_ORIENTATION_STACKED)
									eSvxAdjust = SVX_ADJUST_CENTER;
								// Adjustment fuer bBreak ist hier weggelassen
								pSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );

								pEngine->SetDefaults( pSet );
								pOldPattern = pPattern;
								pOldCondSet = pCondSet;

								sal_uLong nControl = pEngine->GetControlWord();
								if (eOrient==SVX_ORIENTATION_STACKED)
									nControl |= EE_CNTRL_ONECHARPERLINE;
								else
									nControl &= ~EE_CNTRL_ONECHARPERLINE;
								pEngine->SetControlWord( nControl );

								if ( !bHyphenatorSet && ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() )
								{
									//	set hyphenator the first time it is needed
                                    com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
									pEngine->SetHyphenator( xXHyphenator );
									bHyphenatorSet = sal_True;
								}

								Color aBackCol = ((const SvxBrushItem&)
									pPattern->GetItem( ATTR_BACKGROUND, pCondSet )).GetColor();
								if ( bUseStyleColor && ( aBackCol.GetTransparency() > 0 || bCellContrast ) )
									aBackCol.SetColor( nConfBackColor );
								pEngine->SetBackgroundColor( aBackCol );
							}

							//	Raender

							//!		Position und Papersize auf EditUtil umstellen !!!

							const SvxMarginItem* pMargin = (const SvxMarginItem*)
													&pPattern->GetItem(ATTR_MARGIN, pCondSet);
							sal_uInt16 nIndent = 0;
							if ( eHorJust == SVX_HOR_JUSTIFY_LEFT )
								nIndent = ((const SfxUInt16Item&)pPattern->
													GetItem(ATTR_INDENT, pCondSet)).GetValue();

							long nTotalHeight = nOutHeight;	// ohne Rand abzuziehen
							if ( bPixelToLogic )
								nTotalHeight = pRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height();

							long nLeftM = (long) ( (pMargin->GetLeftMargin() + nIndent) * nPPTX );
							long nTopM  = (long) ( pMargin->GetTopMargin() * nPPTY );
                            long nRightM  = (long) ( pMargin->GetRightMargin() * nPPTX );
                            long nBottomM = (long) ( pMargin->GetBottomMargin() * nPPTY );
							nStartX += nLeftM;
							nStartY += nTopM;
							nOutWidth -= nLeftM + nRightM;
							nOutHeight -= nTopM + nBottomM;

							//	Rotation schon hier, um bei Umbruch auch PaperSize anzupassen
							long nAttrRotate = 0;
							double nSin = 0.0;
							double nCos = 1.0;
							SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
							if ( eOrient == SVX_ORIENTATION_STANDARD )
							{
								nAttrRotate = ((const SfxInt32Item&)pPattern->
													GetItem(ATTR_ROTATE_VALUE, pCondSet)).GetValue();
								if ( nAttrRotate )
								{
									eRotMode = (SvxRotateMode)((const SvxRotateModeItem&)
												pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet)).GetValue();

									if ( nAttrRotate == 18000 )
										eRotMode = SVX_ROTATE_MODE_STANDARD;	// keinen Ueberlauf

									if ( bLayoutRTL )
										nAttrRotate = -nAttrRotate;

									double nRealOrient = nAttrRotate * F_PI18000;	// 1/100 Grad
									nCos = cos( nRealOrient );
									nSin = sin( nRealOrient );
								}
							}

							Size aPaperSize = Size( 1000000, 1000000 );
							if (eOrient==SVX_ORIENTATION_STACKED)
								aPaperSize.Width() = nOutWidth;				// zum Zentrieren
							else if (bBreak)
							{
								if (nAttrRotate)
								{
									//!	richtige PaperSize fuer Umbruch haengt von der Zeilenzahl
									//!	ab, solange die Zeilen nicht einzeln versetzt ausgegeben
									//!	werden koennen -> darum unbegrenzt, also kein Umbruch.
									//!	Mit versetzten Zeilen waere das folgende richtig:
									aPaperSize.Width() = (long)(nOutHeight / fabs(nSin));
								}
								else if (eOrient == SVX_ORIENTATION_STANDARD)
									aPaperSize.Width() = nOutWidth;
								else
									aPaperSize.Width() = nOutHeight - 1;
							}
							if (bPixelToLogic)
								pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize));
							else
								pEngine->SetPaperSize(aPaperSize);	// Scale ist immer 1

							//	Daten aus Zelle lesen

							if (pCell)
							{
								if (pCell->GetCellType() == CELLTYPE_EDIT)
								{
									const EditTextObject* pData;
									((ScEditCell*)pCell)->GetData(pData);

									if (pData)
										pEngine->SetText(*pData);
									else
									{
										DBG_ERROR("pData == 0");
									}
								}
								else
								{
									sal_uLong nFormat = pPattern->GetNumberFormat(
																pDoc->GetFormatTable(), pCondSet );
									String aString;
									Color* pColor;
									ScCellFormat::GetString( pCell,
															 nFormat,aString, &pColor,
															 *pDoc->GetFormatTable(),
															 bShowNullValues,
															 bShowFormulas,
															 ftCheck );

									pEngine->SetText(aString);
									if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
										lcl_SetEditColor( *pEngine, *pColor );
								}

								if ( bSyntaxMode )
									SetEditSyntaxColor( *pEngine, pCell );
								else if ( bUseStyleColor && bForceAutoColor )
									lcl_SetEditColor( *pEngine, COL_AUTO );		//! or have a flag at EditEngine
							}
							else
							{
								DBG_ERROR("pCell == NULL");
							}

							pEngine->SetUpdateMode( sal_True );		// after SetText, before CalcTextWidth/GetTextHeight

							long nEngineWidth  = (long) pEngine->CalcTextWidth();
							long nEngineHeight = pEngine->GetTextHeight();

							if (nAttrRotate && bBreak)
							{
								double nAbsCos = fabs( nCos );
								double nAbsSin = fabs( nSin );

								// #47740# adjust witdh of papersize for height of text
								int nSteps = 5;
								while (nSteps > 0)
								{
									// everything is in pixels
									long nEnginePixel = pRefDevice->LogicToPixel(
															Size(0,nEngineHeight)).Height();
									long nEffHeight = nOutHeight - (long)(nEnginePixel * nAbsCos) + 2;
									long nNewWidth = (long)(nEffHeight / nAbsSin) + 2;
									sal_Bool bFits = ( nNewWidth >= aPaperSize.Width() );
									if ( bFits )
										nSteps = 0;
									else
									{
										if ( nNewWidth < 4 )
										{
											// can't fit -> fall back to using half height
											nEffHeight = nOutHeight / 2;
											nNewWidth = (long)(nEffHeight / nAbsSin) + 2;
											nSteps = 0;
										}
										else
											--nSteps;

										// set paper width and get new text height
										aPaperSize.Width() = nNewWidth;
										if (bPixelToLogic)
											pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize));
										else
											pEngine->SetPaperSize(aPaperSize);	// Scale ist immer 1
										//pEngine->QuickFormatDoc( sal_True );
										nEngineWidth  = (long) pEngine->CalcTextWidth();
										nEngineHeight = pEngine->GetTextHeight();
									}
								}
							}

							long nRealWidth  = nEngineWidth;
							long nRealHeight = nEngineHeight;

							//	wenn gedreht, Groesse anpassen
							if (nAttrRotate)
							{
								double nAbsCos = fabs( nCos );
								double nAbsSin = fabs( nSin );

								if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
									nEngineWidth = (long) ( nRealWidth * nAbsCos +
															nRealHeight * nAbsSin );
								else
									nEngineWidth = (long) ( nRealHeight / nAbsSin );
								//!	begrenzen !!!

								nEngineHeight = (long) ( nRealHeight * nAbsCos +
														 nRealWidth * nAbsSin );
							}

							if (!nAttrRotate)			//	hier nur gedrehter Text
								bHidden = sal_True;			//! vorher abfragen !!!

							//!	weglassen, was nicht hereinragt

							if (!bHidden)
							{
								sal_Bool bClip = sal_False;
								Size aClipSize = Size( nScrX+nScrW-nStartX, nScrY+nScrH-nStartY );

								//	weiterschreiben

								Size aCellSize;
								if (bPixelToLogic)
									aCellSize = pRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
								else
									aCellSize = Size( nOutWidth, nOutHeight );	// Scale ist 1

								long nGridWidth = nEngineWidth;
								sal_Bool bNegative = sal_False;
								if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
								{
									nGridWidth = aCellSize.Width() +
											Abs((long) ( aCellSize.Height() * nCos / nSin ));
									bNegative = ( pInfo->nRotateDir == SC_ROTDIR_LEFT );
									if ( bLayoutRTL )
										bNegative = !bNegative;
								}

								// use GetOutputArea to hide the grid
								// (clip region is done manually below)
                                OutputAreaParam aAreaParam;

								SCCOL nCellX = nX;
								SCROW nCellY = nY;
								SvxCellHorJustify eOutHorJust = eHorJust;
								if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
									eOutHorJust = bNegative ? SVX_HOR_JUSTIFY_RIGHT : SVX_HOR_JUSTIFY_LEFT;
								long nNeededWidth = nGridWidth;		// in pixel for GetOutputArea
								if ( bPixelToLogic )
									nNeededWidth =  pRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width();

								GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth,
                                                *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
                                                sal_False, sal_False, sal_True, aAreaParam );

                                if ( bShrink )
                                {
                                    long nPixelWidth = bPixelToLogic ?
                                        pRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth;
                                    long nNeededPixel = nPixelWidth + nLeftM + nRightM;

                                    aAreaParam.mbLeftClip = aAreaParam.mbRightClip = sal_True;

                                    // always do height
                                    ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
                                        sal_False, sal::static_int_cast<sal_uInt16>(eOrient), nAttrRotate, bPixelToLogic,
                                        nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );

                                    if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
                                    {
                                        // do width only if rotating within the cell (standard mode)
                                        ShrinkEditEngine( *pEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
                                            sal_True, sal::static_int_cast<sal_uInt16>(eOrient), nAttrRotate, bPixelToLogic,
                                            nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
                                    }

                                    // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine
                                    // (but width is only valid for standard mode)
                                    nRealWidth  = (long) pEngine->CalcTextWidth();
                                    nRealHeight = pEngine->GetTextHeight();

                                    if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
                                        nEngineWidth = (long) ( nRealHeight / fabs( nSin ) );
                                }

								// sal_Bool bVClip = ( nEngineHeight > aCellSize.Height() );

								long nClipStartX = nStartX;
								if (nX<nX1)
								{
									//! Clipping unnoetig, wenn links am Fenster

									bClip = sal_True;					// nur Rest ausgeben!
									if (nStartX<nScrX)
									{
										long nDif = nScrX - nStartX;
										nClipStartX = nScrX;
										aClipSize.Width() -= nDif;
									}
								}

								long nClipStartY = nStartY;
								if (nArrY==0 || bVisChanged)
								{
									if ( nClipStartY < nRowPosY )
									{
										long nDif = nRowPosY - nClipStartY;
										bClip = sal_True;
										nClipStartY = nRowPosY;
										aClipSize.Height() -= nDif;
									}
								}

								bClip = sal_True;		// always clip at the window/page border

								//Rectangle aClipRect;
								if (bClip)
								{
									if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ )
									{
										//	gedrehten, ausgerichteten Text nur an den
										//	Seitengrenzen clippen
										nClipStartX = nScrX;
										aClipSize.Width() = nScrW;
									}

									if (bPixelToLogic)
                                        aAreaParam.maClipRect = pRefDevice->PixelToLogic( Rectangle(
														Point(nClipStartX,nClipStartY), aClipSize ) );
									else
                                        aAreaParam.maClipRect = Rectangle(Point(nClipStartX, nClipStartY),
																aClipSize );	// Scale = 1

									if (bMetaFile)
									{
										pDev->Push();
                                        pDev->IntersectClipRegion( aAreaParam.maClipRect );
									}
									else
                                        pDev->SetClipRegion( Region( aAreaParam.maClipRect ) );
								}

								Point aLogicStart;
								if (bPixelToLogic)
									aLogicStart = pRefDevice->PixelToLogic( Point(nStartX,nStartY) );
								else
									aLogicStart = Point(nStartX, nStartY);
								if ( eOrient!=SVX_ORIENTATION_STANDARD || !bBreak )
								{
									long nAvailWidth = aCellSize.Width();
									if (eType==OUTTYPE_WINDOW &&
											eOrient!=SVX_ORIENTATION_STACKED &&
											pInfo && pInfo->bAutoFilter)
									{
                                        // filter drop-down width is now independent from row height
                                        if (bPixelToLogic)
                                            nAvailWidth -= pRefDevice->PixelToLogic(Size(0,DROPDOWN_BITMAP_SIZE)).Height();
                                        else
                                            nAvailWidth -= DROPDOWN_BITMAP_SIZE;
										long nComp = nEngineWidth;
										if (nAvailWidth<nComp) nAvailWidth=nComp;
									}

									//	horizontale Ausrichtung

									if (eOrient==SVX_ORIENTATION_STANDARD && !nAttrRotate)
									{
										if (eHorJust==SVX_HOR_JUSTIFY_RIGHT ||
											eHorJust==SVX_HOR_JUSTIFY_CENTER)
										{
											pEngine->SetUpdateMode( sal_False );

											SvxAdjust eSvxAdjust =
												(eHorJust==SVX_HOR_JUSTIFY_RIGHT) ?
													SVX_ADJUST_RIGHT : SVX_ADJUST_CENTER;
											pEngine->SetDefaultItem(
												SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );

											aPaperSize.Width() = nOutWidth;
											if (bPixelToLogic)
												pEngine->SetPaperSize(pRefDevice->PixelToLogic(aPaperSize));
											else
												pEngine->SetPaperSize(aPaperSize);

											pEngine->SetUpdateMode( sal_True );
										}
									}
									else
									{
										//	bei gedrehtem Text ist Standard zentriert
										if (eHorJust==SVX_HOR_JUSTIFY_RIGHT)
											aLogicStart.X() += nAvailWidth - nEngineWidth;
										else if (eHorJust==SVX_HOR_JUSTIFY_CENTER ||
												 eHorJust==SVX_HOR_JUSTIFY_STANDARD)
											aLogicStart.X() += (nAvailWidth - nEngineWidth) / 2;
									}
								}

								if ( bLayoutRTL )
								{
									if (bPixelToLogic)
										aLogicStart.X() -= pRefDevice->PixelToLogic(
														Size( nCellWidth, 0 ) ).Width();
									else
										aLogicStart.X() -= nCellWidth;
								}

								if ( eOrient==SVX_ORIENTATION_STANDARD ||
									 eOrient==SVX_ORIENTATION_STACKED || !bBreak )
								{
									if (eVerJust==SVX_VER_JUSTIFY_BOTTOM ||
										eVerJust==SVX_VER_JUSTIFY_STANDARD)
									{
										if (bPixelToLogic)
											aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0,
															pRefDevice->LogicToPixel(aCellSize).Height() -
															pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
															)).Height();
										else
											aLogicStart.Y() += aCellSize.Height() - nEngineHeight;
									}

									else if (eVerJust==SVX_VER_JUSTIFY_CENTER)
									{
										if (bPixelToLogic)
											aLogicStart.Y() += pRefDevice->PixelToLogic( Size(0,(
															pRefDevice->LogicToPixel(aCellSize).Height() -
															pRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height())
															/ 2)).Height();
										else
											aLogicStart.Y() += (aCellSize.Height() - nEngineHeight) / 2;
									}
								}

								// TOPBOTTON and BOTTOMTOP are handled in DrawStrings/DrawEdit
								DBG_ASSERT( eOrient == SVX_ORIENTATION_STANDARD && nAttrRotate,
											"DrawRotated: no rotation" );

								long nOriVal = 0;
								if ( nAttrRotate )
								{
									// Attribut ist 1/100, Font 1/10 Grad
									nOriVal = nAttrRotate / 10;

									double nAddX = 0.0;
									double nAddY = 0.0;
									if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD )
									{
										//!	begrenzen !!!
										double nH = nRealHeight * nCos;
										nAddX += nH * ( nCos / fabs(nSin) );
									}
									if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD )
										nAddX -= nRealWidth * nCos;
									if ( nSin < 0.0 )
										nAddX -= nRealHeight * nSin;
									if ( nSin > 0.0 )
										nAddY += nRealWidth * nSin;
									if ( nCos < 0.0 )
										nAddY -= nRealHeight * nCos;

									if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
									{
										//!	begrenzen !!!
										double nSkew = nTotalHeight * nCos / fabs(nSin);
										if ( eRotMode == SVX_ROTATE_MODE_CENTER )
											nAddX -= nSkew * 0.5;
										if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) ||
											 ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) )
											nAddX -= nSkew;

										long nUp = 0;
										if ( eVerJust == SVX_VER_JUSTIFY_CENTER )
											nUp = ( aCellSize.Height() - nEngineHeight ) / 2;
										else if ( eVerJust == SVX_VER_JUSTIFY_TOP )
										{
											if ( nSin > 0.0 )
												nUp = aCellSize.Height() - nEngineHeight;
										}
										else	// BOTTOM / STANDARD
										{
											if ( nSin < 0.0 )
												nUp = aCellSize.Height() - nEngineHeight;
										}
										if ( nUp )
											nAddX += ( nUp * nCos / fabs(nSin) );
									}

									aLogicStart.X() += (long) nAddX;
									aLogicStart.Y() += (long) nAddY;
								}

								//	bSimClip is not used here (because nOriVal is set)

								if ( pEngine->IsRightToLeft( 0 ) )
								{
									//	For right-to-left, EditEngine always calculates its lines
									//	beginning from the right edge, but EditLine::nStartPosX is
									//	of sal_uInt16 type, so the PaperSize must be limited to USHRT_MAX.
									Size aLogicPaper = pEngine->GetPaperSize();
									if ( aLogicPaper.Width() > USHRT_MAX )
									{
										aLogicPaper.Width() = USHRT_MAX;
										pEngine->SetPaperSize(aLogicPaper);
									}
								}

								pEngine->Draw( pDev, aLogicStart, (short)nOriVal );

								if (bClip)
								{
									if (bMetaFile)
										pDev->Pop();
									else
										pDev->SetClipRegion();
								}
							}
						}
					}
				}
				nPosX += pRowInfo[0].pCellInfo[nX+1].nWidth * nLayoutSign;
			}
		}
		nRowPosY += pRowInfo[nArrY].nHeight;
	}

	delete pEngine;
}



