xref: /AOO41X/main/vcl/aqua/source/gdi/atslayout.cxx (revision e26449d3b24846fb7600c5866a6e7ae082e782f2)
151747b8eSHerbert Dürr /**************************************************************
251747b8eSHerbert Dürr  *
351747b8eSHerbert Dürr  * Licensed to the Apache Software Foundation (ASF) under one
451747b8eSHerbert Dürr  * or more contributor license agreements.  See the NOTICE file
551747b8eSHerbert Dürr  * distributed with this work for additional information
651747b8eSHerbert Dürr  * regarding copyright ownership.  The ASF licenses this file
751747b8eSHerbert Dürr  * to you under the Apache License, Version 2.0 (the
851747b8eSHerbert Dürr  * "License"); you may not use this file except in compliance
951747b8eSHerbert Dürr  * with the License.  You may obtain a copy of the License at
1051747b8eSHerbert Dürr  *
1151747b8eSHerbert Dürr  *   http://www.apache.org/licenses/LICENSE-2.0
1251747b8eSHerbert Dürr  *
1351747b8eSHerbert Dürr  * Unless required by applicable law or agreed to in writing,
1451747b8eSHerbert Dürr  * software distributed under the License is distributed on an
1551747b8eSHerbert Dürr  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1651747b8eSHerbert Dürr  * KIND, either express or implied.  See the License for the
1751747b8eSHerbert Dürr  * specific language governing permissions and limitations
1851747b8eSHerbert Dürr  * under the License.
1951747b8eSHerbert Dürr  *
2051747b8eSHerbert Dürr  *************************************************************/
2151747b8eSHerbert Dürr 
2251747b8eSHerbert Dürr 
2351747b8eSHerbert Dürr 
2451747b8eSHerbert Dürr #include "tools/debug.hxx"
2551747b8eSHerbert Dürr 
2651747b8eSHerbert Dürr #include "aqua/saldata.hxx"
2751747b8eSHerbert Dürr #include "aqua/salgdi.h"
2851747b8eSHerbert Dürr #include "atsfonts.hxx"
2951747b8eSHerbert Dürr 
3051747b8eSHerbert Dürr #include "sallayout.hxx"
3151747b8eSHerbert Dürr #include "salgdi.hxx"
3251747b8eSHerbert Dürr 
3351747b8eSHerbert Dürr #include <math.h>
3451747b8eSHerbert Dürr 
3551747b8eSHerbert Dürr // =======================================================================
3651747b8eSHerbert Dürr 
3751747b8eSHerbert Dürr class ATSLayout : public SalLayout
3851747b8eSHerbert Dürr {
3951747b8eSHerbert Dürr public:
40*e26449d3SHerbert Dürr 	explicit        ATSLayout( ATSUStyle&, float fFontScale );
4151747b8eSHerbert Dürr 	virtual         ~ATSLayout();
4251747b8eSHerbert Dürr 
4351747b8eSHerbert Dürr 	virtual bool	LayoutText( ImplLayoutArgs& );
4451747b8eSHerbert Dürr 	virtual void	AdjustLayout( ImplLayoutArgs& );
4551747b8eSHerbert Dürr 	virtual void	DrawText( SalGraphics& ) const;
4651747b8eSHerbert Dürr 
47*e26449d3SHerbert Dürr 	virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
4851747b8eSHerbert Dürr 						sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
4951747b8eSHerbert Dürr 
5051747b8eSHerbert Dürr 	virtual long    GetTextWidth() const;
5151747b8eSHerbert Dürr 	virtual long    FillDXArray( long* pDXArray ) const;
5251747b8eSHerbert Dürr 	virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
5351747b8eSHerbert Dürr 	virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
5451747b8eSHerbert Dürr 	virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
5551747b8eSHerbert Dürr 	virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
5651747b8eSHerbert Dürr 
5751747b8eSHerbert Dürr 	const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
5851747b8eSHerbert Dürr 
5951747b8eSHerbert Dürr 	virtual void    InitFont();
6051747b8eSHerbert Dürr 	virtual void    MoveGlyph( int nStart, long nNewXPos );
6151747b8eSHerbert Dürr 	virtual void    DropGlyph( int nStart );
6251747b8eSHerbert Dürr 	virtual void    Simplify( bool bIsBase );
6351747b8eSHerbert Dürr 
6451747b8eSHerbert Dürr private:
6551747b8eSHerbert Dürr 	ATSUStyle&			mrATSUStyle;
6651747b8eSHerbert Dürr 	ATSUTextLayout		maATSULayout;
6751747b8eSHerbert Dürr 	int					mnCharCount;		// ==mnEndCharPos-mnMinCharPos
6851747b8eSHerbert Dürr 	// to prevent ATS overflowing the Fixed16.16 values
6951747b8eSHerbert Dürr 	// ATS font requests get size limited by downscaling huge fonts
7051747b8eSHerbert Dürr 	// in these cases the font scale becomes something bigger than 1.0
7151747b8eSHerbert Dürr 	float				mfFontScale;
7251747b8eSHerbert Dürr 
7351747b8eSHerbert Dürr private:
7451747b8eSHerbert Dürr 	bool	InitGIA( ImplLayoutArgs* pArgs = NULL ) const;
7551747b8eSHerbert Dürr 	bool	GetIdealX() const;
7651747b8eSHerbert Dürr 	bool	GetDeltaY() const;
7751747b8eSHerbert Dürr 	void	InvalidateMeasurements();
7851747b8eSHerbert Dürr 
7951747b8eSHerbert Dürr 	int	Fixed2Vcl( Fixed ) const;       // convert ATSU-Fixed units to VCL units
8051747b8eSHerbert Dürr 	int	AtsuPix2Vcl( int ) const;       // convert ATSU-Pixel units to VCL units
8151747b8eSHerbert Dürr 	Fixed	Vcl2Fixed( int ) const;     // convert VCL units to ATSU-Fixed units
8251747b8eSHerbert Dürr 
8351747b8eSHerbert Dürr 	// cached details about the resulting layout
8451747b8eSHerbert Dürr 	// mutable members since these details are all lazy initialized
8551747b8eSHerbert Dürr 	mutable int			mnGlyphCount;			// glyph count
8651747b8eSHerbert Dürr 	mutable Fixed		mnCachedWidth;			// cached value of resulting typographical width
8751747b8eSHerbert Dürr 	int					mnTrailingSpaceWidth;   // in Pixels
8851747b8eSHerbert Dürr 
8951747b8eSHerbert Dürr 	mutable ATSGlyphRef*	mpGlyphIds;			// ATSU glyph ids
9051747b8eSHerbert Dürr 	mutable Fixed*			mpCharWidths;       // map relative charpos to charwidth
9151747b8eSHerbert Dürr 	mutable int*			mpChars2Glyphs;     // map relative charpos to absolute glyphpos
9251747b8eSHerbert Dürr 	mutable int*			mpGlyphs2Chars;     // map absolute glyphpos to absolute charpos
9351747b8eSHerbert Dürr 	mutable bool*			mpGlyphRTLFlags;    // BiDi status for glyphs: true if RTL
9451747b8eSHerbert Dürr 	mutable Fixed*			mpGlyphAdvances;	// contains glyph widths for the justified layout
9551747b8eSHerbert Dürr 	mutable Fixed*			mpGlyphOrigAdvs;	// contains glyph widths for the unjustified layout
9651747b8eSHerbert Dürr 	mutable Fixed*			mpDeltaY;			// vertical offset from the baseline
9751747b8eSHerbert Dürr 
9851747b8eSHerbert Dürr 	struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; };
9951747b8eSHerbert Dürr 	typedef std::vector<SubPortion> SubPortionVector;
10051747b8eSHerbert Dürr 	mutable SubPortionVector	maSubPortions;		// Writer&ATSUI layouts can differ quite a bit...
10151747b8eSHerbert Dürr 
10251747b8eSHerbert Dürr 	// storing details about fonts used in glyph-fallback for this layout
10351747b8eSHerbert Dürr 	mutable class FallbackInfo*	mpFallbackInfo;
10451747b8eSHerbert Dürr 
10551747b8eSHerbert Dürr 	// x-offset relative to layout origin
10651747b8eSHerbert Dürr 	// currently only used in RTL-layouts
10751747b8eSHerbert Dürr 	mutable Fixed			mnBaseAdv;
10851747b8eSHerbert Dürr };
10951747b8eSHerbert Dürr 
11051747b8eSHerbert Dürr class FallbackInfo
11151747b8eSHerbert Dürr {
11251747b8eSHerbert Dürr public:
FallbackInfo()11351747b8eSHerbert Dürr 	FallbackInfo() : mnMaxLevel(0) {}
11451747b8eSHerbert Dürr 	int AddFallback( ATSUFontID );
11551747b8eSHerbert Dürr 	const ImplFontData* GetFallbackFontData( int nLevel ) const;
11651747b8eSHerbert Dürr 
11751747b8eSHerbert Dürr private:
11851747b8eSHerbert Dürr 	const ImplMacFontData* maFontData[ MAX_FALLBACK ];
11951747b8eSHerbert Dürr 	ATSUFontID             maATSUFontId[ MAX_FALLBACK ];
12051747b8eSHerbert Dürr 	int                    mnMaxLevel;
12151747b8eSHerbert Dürr };
12251747b8eSHerbert Dürr 
12351747b8eSHerbert Dürr // =======================================================================
12451747b8eSHerbert Dürr 
ATSLayout(ATSUStyle & rATSUStyle,float fFontScale)12551747b8eSHerbert Dürr ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
12651747b8eSHerbert Dürr :	mrATSUStyle( rATSUStyle ),
12751747b8eSHerbert Dürr 	maATSULayout( NULL ),
12851747b8eSHerbert Dürr 	mnCharCount( 0 ),
12951747b8eSHerbert Dürr 	mfFontScale( fFontScale ),
13051747b8eSHerbert Dürr 	mnGlyphCount( -1 ),
13151747b8eSHerbert Dürr 	mnCachedWidth( 0 ),
13251747b8eSHerbert Dürr 	mnTrailingSpaceWidth( 0 ),
13351747b8eSHerbert Dürr 	mpGlyphIds( NULL ),
13451747b8eSHerbert Dürr 	mpCharWidths( NULL ),
13551747b8eSHerbert Dürr 	mpChars2Glyphs( NULL ),
13651747b8eSHerbert Dürr 	mpGlyphs2Chars( NULL ),
13751747b8eSHerbert Dürr 	mpGlyphRTLFlags( NULL ),
13851747b8eSHerbert Dürr 	mpGlyphAdvances( NULL ),
13951747b8eSHerbert Dürr 	mpGlyphOrigAdvs( NULL ),
14051747b8eSHerbert Dürr 	mpDeltaY( NULL ),
14151747b8eSHerbert Dürr 	mpFallbackInfo( NULL ),
14251747b8eSHerbert Dürr 	mnBaseAdv( 0 )
14351747b8eSHerbert Dürr {}
14451747b8eSHerbert Dürr 
14551747b8eSHerbert Dürr // -----------------------------------------------------------------------
14651747b8eSHerbert Dürr 
~ATSLayout()14751747b8eSHerbert Dürr ATSLayout::~ATSLayout()
14851747b8eSHerbert Dürr {
14951747b8eSHerbert Dürr 	if( mpDeltaY )
15051747b8eSHerbert Dürr 		ATSUDirectReleaseLayoutDataArrayPtr( NULL,
15151747b8eSHerbert Dürr 			kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
15251747b8eSHerbert Dürr 
15351747b8eSHerbert Dürr 	if( maATSULayout )
15451747b8eSHerbert Dürr 		ATSUDisposeTextLayout( maATSULayout );
15551747b8eSHerbert Dürr 
15651747b8eSHerbert Dürr 	delete[] mpGlyphRTLFlags;
15751747b8eSHerbert Dürr 	delete[] mpGlyphs2Chars;
15851747b8eSHerbert Dürr 	delete[] mpChars2Glyphs;
15951747b8eSHerbert Dürr 	if( mpCharWidths != mpGlyphAdvances )
16051747b8eSHerbert Dürr 		delete[] mpCharWidths;
16151747b8eSHerbert Dürr 	delete[] mpGlyphIds;
16251747b8eSHerbert Dürr 	delete[] mpGlyphOrigAdvs;
16351747b8eSHerbert Dürr 	delete[] mpGlyphAdvances;
16451747b8eSHerbert Dürr 
16551747b8eSHerbert Dürr 	delete mpFallbackInfo;
16651747b8eSHerbert Dürr }
16751747b8eSHerbert Dürr 
16851747b8eSHerbert Dürr // -----------------------------------------------------------------------
16951747b8eSHerbert Dürr 
Fixed2Vcl(Fixed nFixed) const17051747b8eSHerbert Dürr inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const
17151747b8eSHerbert Dürr {
17251747b8eSHerbert Dürr 	float fFloat = mfFontScale * FixedToFloat( nFixed );
17351747b8eSHerbert Dürr 	return static_cast<int>(fFloat + 0.5);
17451747b8eSHerbert Dürr }
17551747b8eSHerbert Dürr 
17651747b8eSHerbert Dürr // -----------------------------------------------------------------------
17751747b8eSHerbert Dürr 
AtsuPix2Vcl(int nAtsuPixel) const17851747b8eSHerbert Dürr inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const
17951747b8eSHerbert Dürr {
18051747b8eSHerbert Dürr 	float fVclPixel = mfFontScale * nAtsuPixel;
18151747b8eSHerbert Dürr 	fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5;	// prepare rounding to int
18251747b8eSHerbert Dürr 	int nVclPixel = static_cast<int>( fVclPixel);
18351747b8eSHerbert Dürr 	return nVclPixel;
18451747b8eSHerbert Dürr }
18551747b8eSHerbert Dürr 
18651747b8eSHerbert Dürr // -----------------------------------------------------------------------
18751747b8eSHerbert Dürr 
Vcl2Fixed(int nPixel) const18851747b8eSHerbert Dürr inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const
18951747b8eSHerbert Dürr {
19051747b8eSHerbert Dürr 	return FloatToFixed( nPixel / mfFontScale );
19151747b8eSHerbert Dürr }
19251747b8eSHerbert Dürr 
19351747b8eSHerbert Dürr // -----------------------------------------------------------------------
19451747b8eSHerbert Dürr /**
19551747b8eSHerbert Dürr  * ATSLayout::LayoutText : Manage text layouting
19651747b8eSHerbert Dürr  *
19751747b8eSHerbert Dürr  * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout
19851747b8eSHerbert Dürr  *
19951747b8eSHerbert Dürr  * Typographic layout of text by using the style maATSUStyle
20051747b8eSHerbert Dürr  *
20151747b8eSHerbert Dürr  * @return : true if everything is ok
20251747b8eSHerbert Dürr **/
LayoutText(ImplLayoutArgs & rArgs)20351747b8eSHerbert Dürr bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs )
20451747b8eSHerbert Dürr {
20551747b8eSHerbert Dürr 	if( maATSULayout )
20651747b8eSHerbert Dürr 		ATSUDisposeTextLayout( maATSULayout );
20751747b8eSHerbert Dürr 
20851747b8eSHerbert Dürr 	maATSULayout = NULL;
20951747b8eSHerbert Dürr 
21051747b8eSHerbert Dürr 	// Layout text
21151747b8eSHerbert Dürr 	// set up our locals, verify parameters...
21251747b8eSHerbert Dürr 	DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!");
21351747b8eSHerbert Dürr 	DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!");
21451747b8eSHerbert Dürr 
21551747b8eSHerbert Dürr 	SalLayout::AdjustLayout( rArgs );
21651747b8eSHerbert Dürr 	mnCharCount = mnEndCharPos - mnMinCharPos;
21751747b8eSHerbert Dürr 
21851747b8eSHerbert Dürr 	// Workaround a bug in ATSUI with empty string
21951747b8eSHerbert Dürr 	if( mnCharCount<=0 )
22051747b8eSHerbert Dürr 		return false;
22151747b8eSHerbert Dürr 
22251747b8eSHerbert Dürr 	// create the ATSUI layout
22351747b8eSHerbert Dürr 	UniCharCount nRunLengths[1] = { mnCharCount };
22451747b8eSHerbert Dürr 	const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths);
22551747b8eSHerbert Dürr 	OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr,
22651747b8eSHerbert Dürr 		rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength,
227*e26449d3SHerbert Dürr 		nRunCount, &nRunLengths[0], &mrATSUStyle, &maATSULayout);
22851747b8eSHerbert Dürr 
22951747b8eSHerbert Dürr 	DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n");
23051747b8eSHerbert Dürr 	if( eStatus != noErr )
23151747b8eSHerbert Dürr 		return false;
23251747b8eSHerbert Dürr 
23351747b8eSHerbert Dürr 	// prepare setting of layout controls
23451747b8eSHerbert Dürr 	static const int nMaxTagCount = 1;
23551747b8eSHerbert Dürr 	ATSUAttributeTag aTagAttrs[ nMaxTagCount ];
23651747b8eSHerbert Dürr 	ByteCount aTagSizes[ nMaxTagCount ];
23751747b8eSHerbert Dürr 	ATSUAttributeValuePtr aTagValues[ nMaxTagCount ];
23851747b8eSHerbert Dürr 
23951747b8eSHerbert Dürr 	// prepare control of "glyph fallback"
24051747b8eSHerbert Dürr 	const SalData* pSalData = GetSalData();
24151747b8eSHerbert Dürr 	ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks;
24251747b8eSHerbert Dürr 	aTagAttrs[0]  = kATSULineFontFallbacksTag;
24351747b8eSHerbert Dürr 	aTagSizes[0]  = sizeof( ATSUFontFallbacks );
24451747b8eSHerbert Dürr 	aTagValues[0] = &aFontFallbacks;
24551747b8eSHerbert Dürr 
24651747b8eSHerbert Dürr 	// set paragraph layout controls
24751747b8eSHerbert Dürr 	ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
24851747b8eSHerbert Dürr 
24951747b8eSHerbert Dürr 	// enable "glyph fallback"
25051747b8eSHerbert Dürr 	ATSUSetTransientFontMatching( maATSULayout, true );
25151747b8eSHerbert Dürr 
25251747b8eSHerbert Dürr 	// control run-specific layout controls
25351747b8eSHerbert Dürr 	if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 )
25451747b8eSHerbert Dürr 	{
25551747b8eSHerbert Dürr 		// control BiDi defaults
25651747b8eSHerbert Dürr 		BOOL nLineDirTag = kATSULeftToRightBaseDirection;
25751747b8eSHerbert Dürr 		if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 )
25851747b8eSHerbert Dürr 			nLineDirTag = kATSURightToLeftBaseDirection;
25951747b8eSHerbert Dürr 		aTagAttrs[0] = kATSULineDirectionTag;
26051747b8eSHerbert Dürr 		aTagSizes[0] = sizeof( nLineDirTag );
26151747b8eSHerbert Dürr 		aTagValues[0] = &nLineDirTag;
26251747b8eSHerbert Dürr 		// set run-specific layout controls
263*e26449d3SHerbert Dürr #if 0 // why don't line-controls work as reliable as layout-controls???
26451747b8eSHerbert Dürr 		ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues );
26551747b8eSHerbert Dürr #else
26651747b8eSHerbert Dürr 		ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
26751747b8eSHerbert Dürr #endif
26851747b8eSHerbert Dürr 	}
26951747b8eSHerbert Dürr 
27051747b8eSHerbert Dürr 	return true;
27151747b8eSHerbert Dürr }
27251747b8eSHerbert Dürr 
27351747b8eSHerbert Dürr // -----------------------------------------------------------------------
27451747b8eSHerbert Dürr /**
27551747b8eSHerbert Dürr  * ATSLayout::AdjustLayout : Adjust layout style
27651747b8eSHerbert Dürr  *
27751747b8eSHerbert Dürr  * @param rArgs: contains attributes relevant to do a text specific layout
27851747b8eSHerbert Dürr  *
27951747b8eSHerbert Dürr  * Adjust text layout by moving glyphs to match the requested logical widths
28051747b8eSHerbert Dürr  *
28151747b8eSHerbert Dürr  * @return : none
28251747b8eSHerbert Dürr **/
AdjustLayout(ImplLayoutArgs & rArgs)28351747b8eSHerbert Dürr void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs )
28451747b8eSHerbert Dürr {
28551747b8eSHerbert Dürr 	int nOrigWidth = GetTextWidth();
28651747b8eSHerbert Dürr 	int nPixelWidth = rArgs.mnLayoutWidth;
28751747b8eSHerbert Dürr 	if( !nPixelWidth && rArgs.mpDXArray ) {
28851747b8eSHerbert Dürr 		// for now we are only interested in the layout width
28951747b8eSHerbert Dürr 		// TODO: use all mpDXArray elements for layouting
29051747b8eSHerbert Dürr 		nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ];
29151747b8eSHerbert Dürr 
29251747b8eSHerbert Dürr 		// workaround for ATSUI not using trailing spaces for justification
29351747b8eSHerbert Dürr 		int i = mnCharCount;
29451747b8eSHerbert Dürr 		while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {}
29551747b8eSHerbert Dürr 		if( i < 0 ) // nothing to do if the text is all spaces
29651747b8eSHerbert Dürr 			return;
29751747b8eSHerbert Dürr 		// #i91685# trailing letters are left aligned (right aligned for RTL)
29851747b8eSHerbert Dürr 		mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ];
29951747b8eSHerbert Dürr 		if( i > 0 )
30051747b8eSHerbert Dürr 			mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ];
30151747b8eSHerbert Dürr 		InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead?
30251747b8eSHerbert Dürr 		mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] );
30351747b8eSHerbert Dürr 		// ignore trailing space for calculating the available width
30451747b8eSHerbert Dürr 		nOrigWidth -= mnTrailingSpaceWidth;
30551747b8eSHerbert Dürr 		nPixelWidth -= mnTrailingSpaceWidth;
30651747b8eSHerbert Dürr 		// in RTL-layouts trailing spaces are leftmost
30751747b8eSHerbert Dürr 		// TODO: use BiDi-algorithm to thoroughly check this assumption
30851747b8eSHerbert Dürr 		if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
30951747b8eSHerbert Dürr 			mnBaseAdv = mnTrailingSpaceWidth;
31051747b8eSHerbert Dürr 	}
31151747b8eSHerbert Dürr 	// return early if there is nothing to do
31251747b8eSHerbert Dürr 	if( !nPixelWidth )
31351747b8eSHerbert Dürr 		return;
31451747b8eSHerbert Dürr 
31551747b8eSHerbert Dürr 	// HACK: justification requests which change the width by just one pixel were probably
31651747b8eSHerbert Dürr 	// #i86038# introduced by lossy conversions between integer based coordinate system
31751747b8eSHerbert Dürr 	// => ignoring such requests has many more benefits than eventual drawbacks
31851747b8eSHerbert Dürr 	if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
31951747b8eSHerbert Dürr 		return;
32051747b8eSHerbert Dürr 
32151747b8eSHerbert Dürr 	// changing the layout will make all previous measurements invalid
32251747b8eSHerbert Dürr 	InvalidateMeasurements();
32351747b8eSHerbert Dürr 
32451747b8eSHerbert Dürr 	ATSUAttributeTag nTags[3];
32551747b8eSHerbert Dürr 	ATSUAttributeValuePtr nVals[3];
32651747b8eSHerbert Dürr 	ByteCount nBytes[3];
32751747b8eSHerbert Dürr 
32851747b8eSHerbert Dürr 	Fixed nFixedWidth = Vcl2Fixed( nPixelWidth );
32951747b8eSHerbert Dürr 	mnCachedWidth = nFixedWidth;
33051747b8eSHerbert Dürr 	Fract nFractFactor = kATSUFullJustification;
33151747b8eSHerbert Dürr 	ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter;
33251747b8eSHerbert Dürr 
33351747b8eSHerbert Dürr 	nTags[0] = kATSULineWidthTag;
33451747b8eSHerbert Dürr 	nVals[0] = &nFixedWidth;
33551747b8eSHerbert Dürr 	nBytes[0] = sizeof(Fixed);
33651747b8eSHerbert Dürr 	nTags[1] = kATSULineLayoutOptionsTag;
33751747b8eSHerbert Dürr 	nVals[1] = &nLineLayoutOptions;
33851747b8eSHerbert Dürr 	nBytes[1] = sizeof(ATSLineLayoutOptions);
33951747b8eSHerbert Dürr 	nTags[2] = kATSULineJustificationFactorTag;
34051747b8eSHerbert Dürr 	nVals[2] = &nFractFactor;
34151747b8eSHerbert Dürr 	nBytes[2] = sizeof(Fract);
34251747b8eSHerbert Dürr 
34351747b8eSHerbert Dürr 	OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals );
34451747b8eSHerbert Dürr 	if( eStatus != noErr )
34551747b8eSHerbert Dürr 		return;
34651747b8eSHerbert Dürr 
34751747b8eSHerbert Dürr 	// update the measurements of the justified layout to match the justification request
34851747b8eSHerbert Dürr 	if( rArgs.mpDXArray )
34951747b8eSHerbert Dürr 		InitGIA( &rArgs );
35051747b8eSHerbert Dürr }
35151747b8eSHerbert Dürr 
35251747b8eSHerbert Dürr // -----------------------------------------------------------------------
35351747b8eSHerbert Dürr /**
35451747b8eSHerbert Dürr  * ATSLayout::DrawText : Draw text to screen
35551747b8eSHerbert Dürr  *
35651747b8eSHerbert Dürr  * @param rGraphics: device to draw to
35751747b8eSHerbert Dürr  *
35851747b8eSHerbert Dürr  * Draw the layouted text to the CGContext
35951747b8eSHerbert Dürr  *
36051747b8eSHerbert Dürr  * @return : none
36151747b8eSHerbert Dürr **/
DrawText(SalGraphics & rGraphics) const36251747b8eSHerbert Dürr void ATSLayout::DrawText( SalGraphics& rGraphics ) const
36351747b8eSHerbert Dürr {
36451747b8eSHerbert Dürr 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
36551747b8eSHerbert Dürr 
36651747b8eSHerbert Dürr 	// short circuit if there is nothing to do
36751747b8eSHerbert Dürr 	if( (mnCharCount <= 0)
36851747b8eSHerbert Dürr 	||  !rAquaGraphics.CheckContext() )
36951747b8eSHerbert Dürr 		return;
37051747b8eSHerbert Dürr 
37151747b8eSHerbert Dürr 	// the view is vertically flipped => flipped glyphs
37251747b8eSHerbert Dürr 	// so apply a temporary transformation that it flips back
37351747b8eSHerbert Dürr 	// also compensate if the font was size limited
37451747b8eSHerbert Dürr 	CGContextSaveGState( rAquaGraphics.mrContext );
37551747b8eSHerbert Dürr 	CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
37651747b8eSHerbert Dürr 	CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
37751747b8eSHerbert Dürr 
37851747b8eSHerbert Dürr 	// prepare ATSUI drawing attributes
37951747b8eSHerbert Dürr 	static const ItemCount nMaxControls = 8;
38051747b8eSHerbert Dürr 	ATSUAttributeTag theTags[ nMaxControls ];
38151747b8eSHerbert Dürr 	ByteCount theSizes[ nMaxControls];
38251747b8eSHerbert Dürr 	ATSUAttributeValuePtr theValues[ nMaxControls ];
38351747b8eSHerbert Dürr 	ItemCount numcontrols = 0;
38451747b8eSHerbert Dürr 
38551747b8eSHerbert Dürr 	// Tell ATSUI to use CoreGraphics
38651747b8eSHerbert Dürr 	theTags[numcontrols] = kATSUCGContextTag;
38751747b8eSHerbert Dürr 	theSizes[numcontrols] = sizeof( CGContextRef );
38851747b8eSHerbert Dürr 	theValues[numcontrols++] = &rAquaGraphics.mrContext;
38951747b8eSHerbert Dürr 
39051747b8eSHerbert Dürr 	// Rotate if necessary
39151747b8eSHerbert Dürr 	if( rAquaGraphics.mnATSUIRotation != 0 )
39251747b8eSHerbert Dürr 	{
39351747b8eSHerbert Dürr 		Fixed theAngle = rAquaGraphics.mnATSUIRotation;
39451747b8eSHerbert Dürr 		theTags[numcontrols] = kATSULineRotationTag;
39551747b8eSHerbert Dürr 		theSizes[numcontrols] = sizeof( Fixed );
39651747b8eSHerbert Dürr 		theValues[numcontrols++] = &theAngle;
39751747b8eSHerbert Dürr 	}
39851747b8eSHerbert Dürr 
39951747b8eSHerbert Dürr 	DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" );
40051747b8eSHerbert Dürr 	OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues);
40151747b8eSHerbert Dürr 	DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" );
40251747b8eSHerbert Dürr 
40351747b8eSHerbert Dürr 	// Draw the text
40451747b8eSHerbert Dürr 	const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) );
40551747b8eSHerbert Dürr 	const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
40651747b8eSHerbert Dürr 	const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring
40751747b8eSHerbert Dürr 	if( maSubPortions.empty() )
40851747b8eSHerbert Dürr 		ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY );
40951747b8eSHerbert Dürr 	else
41051747b8eSHerbert Dürr 	{
41151747b8eSHerbert Dürr 		// draw the sub-portions and apply individual adjustments
41251747b8eSHerbert Dürr 		SubPortionVector::const_iterator it = maSubPortions.begin();
41351747b8eSHerbert Dürr 		for(; it != maSubPortions.end(); ++it )
41451747b8eSHerbert Dürr 		{
41551747b8eSHerbert Dürr 			const SubPortion& rSubPortion = *it;
41651747b8eSHerbert Dürr 			// calculate sub-portion offset for rotated text
41751747b8eSHerbert Dürr 			Fixed nXOfsFixed = 0, nYOfsFixed = 0;
41851747b8eSHerbert Dürr 			if( rAquaGraphics.mnATSUIRotation != 0 )
41951747b8eSHerbert Dürr 			{
42051747b8eSHerbert Dürr 				const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000);
42151747b8eSHerbert Dürr 				nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians ));
42251747b8eSHerbert Dürr 				nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians ));
42351747b8eSHerbert Dürr 			}
42451747b8eSHerbert Dürr 
42551747b8eSHerbert Dürr 			// draw sub-portions
42651747b8eSHerbert Dürr 			ATSUDrawText( maATSULayout,
42751747b8eSHerbert Dürr 				rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos,
42851747b8eSHerbert Dürr 				nFixedX + nXOfsFixed, nFixedY + nYOfsFixed );
42951747b8eSHerbert Dürr 		}
43051747b8eSHerbert Dürr 	}
43151747b8eSHerbert Dürr 
43251747b8eSHerbert Dürr 	// request an update of the changed window area
43351747b8eSHerbert Dürr 	if( rAquaGraphics.IsWindowGraphics() )
43451747b8eSHerbert Dürr 	{
43551747b8eSHerbert Dürr 		Rect drawRect; // rectangle of the changed area
43651747b8eSHerbert Dürr 		theErr = ATSUMeasureTextImage( maATSULayout,
43751747b8eSHerbert Dürr 			mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect );
43851747b8eSHerbert Dürr 		if( theErr == noErr )
43951747b8eSHerbert Dürr 		{
44051747b8eSHerbert Dürr 			// FIXME: transformation from baseline to top left
44151747b8eSHerbert Dürr 			// with the simple approach below we invalidate too much
44251747b8eSHerbert Dürr 			short d = drawRect.bottom - drawRect.top;
44351747b8eSHerbert Dürr 			drawRect.top -= d;
44451747b8eSHerbert Dürr 			drawRect.bottom += d;
44551747b8eSHerbert Dürr 			CGRect aRect = CGRectMake( drawRect.left, drawRect.top,
44651747b8eSHerbert Dürr 					drawRect.right - drawRect.left,
44751747b8eSHerbert Dürr 					drawRect.bottom - drawRect.top );
44851747b8eSHerbert Dürr 			aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect );
44951747b8eSHerbert Dürr 			rAquaGraphics.RefreshRect( aRect );
45051747b8eSHerbert Dürr 		}
45151747b8eSHerbert Dürr 	}
45251747b8eSHerbert Dürr 
45351747b8eSHerbert Dürr 	// restore the original graphic context transformations
45451747b8eSHerbert Dürr 	CGContextRestoreGState( rAquaGraphics.mrContext );
45551747b8eSHerbert Dürr }
45651747b8eSHerbert Dürr 
45751747b8eSHerbert Dürr // -----------------------------------------------------------------------
45851747b8eSHerbert Dürr /**
45951747b8eSHerbert Dürr  * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout
46051747b8eSHerbert Dürr  *
46151747b8eSHerbert Dürr  * @param nLen: max number of char
46251747b8eSHerbert Dürr  * @param pGlyphs: returned array of glyph ids
46351747b8eSHerbert Dürr  * @param rPos: returned x starting position
46451747b8eSHerbert Dürr  * @param nStart: index of the first requested glyph
46551747b8eSHerbert Dürr  * @param pGlyphAdvances: returned array of glyphs advances
46651747b8eSHerbert Dürr  * @param pCharIndexes: returned array of char indexes
46751747b8eSHerbert Dürr  *
46851747b8eSHerbert Dürr  * Returns infos about the next glyphs in the text layout
46951747b8eSHerbert Dürr  *
47051747b8eSHerbert Dürr  * @return : number of glyph details that were provided
47151747b8eSHerbert Dürr **/
GetNextGlyphs(int nLen,sal_GlyphId * pOutGlyphIds,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvances,int * pCharIndexes) const47251747b8eSHerbert Dürr int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
47351747b8eSHerbert Dürr 	sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
47451747b8eSHerbert Dürr {
47551747b8eSHerbert Dürr 	if( nStart < 0 )                // first glyph requested?
47651747b8eSHerbert Dürr 		nStart = 0;
47751747b8eSHerbert Dürr 
47851747b8eSHerbert Dürr 	// get glyph measurements
47951747b8eSHerbert Dürr 	InitGIA();
48051747b8eSHerbert Dürr 	// some measurements are only needed for multi-glyph results
48151747b8eSHerbert Dürr 	if( nLen > 1 )
48251747b8eSHerbert Dürr 	{
48351747b8eSHerbert Dürr 		GetIdealX();
48451747b8eSHerbert Dürr 		GetDeltaY();
48551747b8eSHerbert Dürr 	}
48651747b8eSHerbert Dürr 
48751747b8eSHerbert Dürr 	if( nStart >= mnGlyphCount )    // no glyph left?
48851747b8eSHerbert Dürr 		return 0;
48951747b8eSHerbert Dürr 
49051747b8eSHerbert Dürr 	// calculate glyph position relative to layout base
49151747b8eSHerbert Dürr 	// TODO: avoid for nStart!=0 case by reusing rPos
49251747b8eSHerbert Dürr 	Fixed nXOffset = mnBaseAdv;
49351747b8eSHerbert Dürr 	for( int i = 0; i < nStart; ++i )
49451747b8eSHerbert Dürr 		nXOffset += mpGlyphAdvances[ i ];
49551747b8eSHerbert Dürr 	// if sub-portion offsets are involved there is an additional x-offset
49651747b8eSHerbert Dürr 	if( !maSubPortions.empty() )
49751747b8eSHerbert Dürr 	{
49851747b8eSHerbert Dürr 		// prepare to find the sub-portion
49951747b8eSHerbert Dürr 		int nCharPos = nStart + mnMinCharPos;
50051747b8eSHerbert Dürr 		if( mpGlyphs2Chars )
50151747b8eSHerbert Dürr 			nCharPos = mpGlyphs2Chars[nStart];
50251747b8eSHerbert Dürr 
50351747b8eSHerbert Dürr 		// find the matching subportion
50451747b8eSHerbert Dürr 		// TODO: is a non-linear search worth it?
50551747b8eSHerbert Dürr 		SubPortionVector::const_iterator it = maSubPortions.begin();
50651747b8eSHerbert Dürr 		for(; it != maSubPortions.end(); ++it) {
50751747b8eSHerbert Dürr 			const SubPortion& r = *it;
50851747b8eSHerbert Dürr 			if( nCharPos < r.mnMinCharPos )
50951747b8eSHerbert Dürr 				continue;
51051747b8eSHerbert Dürr 			if( nCharPos >= r.mnEndCharPos )
51151747b8eSHerbert Dürr 				continue;
51251747b8eSHerbert Dürr 			// apply the sub-portion xoffset
51351747b8eSHerbert Dürr 			nXOffset += r.mnXOffset;
51451747b8eSHerbert Dürr 			break;
51551747b8eSHerbert Dürr 		}
51651747b8eSHerbert Dürr 	}
51751747b8eSHerbert Dürr 
51851747b8eSHerbert Dürr 	Fixed nYOffset = 0;
51951747b8eSHerbert Dürr 	if( mpDeltaY )
52051747b8eSHerbert Dürr 		nYOffset = mpDeltaY[ nStart ];
52151747b8eSHerbert Dürr 
52251747b8eSHerbert Dürr 	// calculate absolute position in pixel units
52351747b8eSHerbert Dürr 	const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) );
52451747b8eSHerbert Dürr 	rPos = GetDrawPosition( aRelativePos );
52551747b8eSHerbert Dürr 
52651747b8eSHerbert Dürr 	// update return values
52751747b8eSHerbert Dürr 	int nCount = 0;
52851747b8eSHerbert Dürr 	while( nCount < nLen )
52951747b8eSHerbert Dürr 	{
53051747b8eSHerbert Dürr 		++nCount;
53151747b8eSHerbert Dürr 		sal_GlyphId aGlyphId = mpGlyphIds[nStart];
53251747b8eSHerbert Dürr 
53351747b8eSHerbert Dürr 		// check if glyph fallback is needed for this glyph
53451747b8eSHerbert Dürr 		// TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead?
53551747b8eSHerbert Dürr 		const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos;
53651747b8eSHerbert Dürr 		ATSUFontID nFallbackFontID = kATSUInvalidFontID;
53751747b8eSHerbert Dürr 		UniCharArrayOffset nChangedOffset = 0;
53851747b8eSHerbert Dürr 		UniCharCount nChangedLength = 0;
53951747b8eSHerbert Dürr 		OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd,
54051747b8eSHerbert Dürr 			&nFallbackFontID, &nChangedOffset, &nChangedLength );
54151747b8eSHerbert Dürr 		if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) )
54251747b8eSHerbert Dürr 		{
54351747b8eSHerbert Dürr 			// fallback is needed
54451747b8eSHerbert Dürr 			if( !mpFallbackInfo )
54551747b8eSHerbert Dürr 				mpFallbackInfo = new FallbackInfo;
54651747b8eSHerbert Dürr 			// register fallback font
54751747b8eSHerbert Dürr 			const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID );
54851747b8eSHerbert Dürr 			// update sal_GlyphId with fallback level
54951747b8eSHerbert Dürr 			aGlyphId |= (nLevel << GF_FONTSHIFT);
55051747b8eSHerbert Dürr 		}
55151747b8eSHerbert Dürr 
55251747b8eSHerbert Dürr 		// update resulting glyphid array
55351747b8eSHerbert Dürr 		*(pOutGlyphIds++) = aGlyphId;
55451747b8eSHerbert Dürr 
55551747b8eSHerbert Dürr 		// update returned glyph advance array
55651747b8eSHerbert Dürr 		if( pGlyphAdvances )
55751747b8eSHerbert Dürr 			*(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] );
55851747b8eSHerbert Dürr 
55951747b8eSHerbert Dürr 		// update returned index-into-string array
56051747b8eSHerbert Dürr 		if( pCharIndexes )
56151747b8eSHerbert Dürr 		{
56251747b8eSHerbert Dürr 			int nCharPos;
56351747b8eSHerbert Dürr 			if( mpGlyphs2Chars )
56451747b8eSHerbert Dürr 				nCharPos = mpGlyphs2Chars[nStart];
56551747b8eSHerbert Dürr 			else
56651747b8eSHerbert Dürr 				nCharPos = nStart + mnMinCharPos;
56751747b8eSHerbert Dürr 			*(pCharIndexes++) = nCharPos;
56851747b8eSHerbert Dürr 		}
56951747b8eSHerbert Dürr 
57051747b8eSHerbert Dürr 		// stop at last glyph
57151747b8eSHerbert Dürr 		if( ++nStart >= mnGlyphCount )
57251747b8eSHerbert Dürr 			break;
57351747b8eSHerbert Dürr 
57451747b8eSHerbert Dürr 		// stop when next the x-position is unexpected
57551747b8eSHerbert Dürr 		if( !maSubPortions.empty() )
57651747b8eSHerbert Dürr 			break;	 // TODO: finish the complete sub-portion
57751747b8eSHerbert Dürr 		if( !pGlyphAdvances && mpGlyphOrigAdvs )
57851747b8eSHerbert Dürr 			if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
57951747b8eSHerbert Dürr 				break;
58051747b8eSHerbert Dürr 
58151747b8eSHerbert Dürr 		// stop when the next y-position is unexpected
58251747b8eSHerbert Dürr 		if( mpDeltaY )
58351747b8eSHerbert Dürr 			if( mpDeltaY[nStart-1] != mpDeltaY[nStart] )
58451747b8eSHerbert Dürr 				break;
58551747b8eSHerbert Dürr 	}
58651747b8eSHerbert Dürr 
58751747b8eSHerbert Dürr 	return nCount;
58851747b8eSHerbert Dürr }
58951747b8eSHerbert Dürr 
59051747b8eSHerbert Dürr // -----------------------------------------------------------------------
59151747b8eSHerbert Dürr /**
59251747b8eSHerbert Dürr  * ATSLayout::GetTextWidth : Get typographic width of layouted text
59351747b8eSHerbert Dürr  *
59451747b8eSHerbert Dürr  * Get typographic bounds of the text
59551747b8eSHerbert Dürr  *
59651747b8eSHerbert Dürr  * @return : text width
59751747b8eSHerbert Dürr **/
GetTextWidth() const59851747b8eSHerbert Dürr long ATSLayout::GetTextWidth() const
59951747b8eSHerbert Dürr {
60051747b8eSHerbert Dürr 	if( mnCharCount <= 0 )
60151747b8eSHerbert Dürr 		return 0;
60251747b8eSHerbert Dürr 
60351747b8eSHerbert Dürr 	DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n");
60451747b8eSHerbert Dürr 	if( !maATSULayout )
60551747b8eSHerbert Dürr 		return 0;
60651747b8eSHerbert Dürr 
60751747b8eSHerbert Dürr 	if( !mnCachedWidth )
60851747b8eSHerbert Dürr 	{
60951747b8eSHerbert Dürr 		// prepare precise measurements on pixel based or reference-device
61051747b8eSHerbert Dürr 		const UInt16 eTypeOfBounds = kATSUseFractionalOrigins;
61151747b8eSHerbert Dürr 
61251747b8eSHerbert Dürr 		// determine number of needed measurement trapezoids
61351747b8eSHerbert Dürr 		ItemCount nMaxBounds = 0;
61451747b8eSHerbert Dürr 		OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
61551747b8eSHerbert Dürr 			eTypeOfBounds, 0, NULL, &nMaxBounds );
61651747b8eSHerbert Dürr 		if( (err != noErr)
61751747b8eSHerbert Dürr 		||  (nMaxBounds <= 0) )
61851747b8eSHerbert Dürr 			return 0;
61951747b8eSHerbert Dürr 
62051747b8eSHerbert Dürr 		// get the trapezoids
62151747b8eSHerbert Dürr 		typedef std::vector<ATSTrapezoid> TrapezoidVector;
62251747b8eSHerbert Dürr 		TrapezoidVector aTrapezoidVector( nMaxBounds );
62351747b8eSHerbert Dürr 		ItemCount nBoundsCount = 0;
62451747b8eSHerbert Dürr 		err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
62551747b8eSHerbert Dürr 			eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount );
62651747b8eSHerbert Dürr 		if( err != noErr )
62751747b8eSHerbert Dürr 			return 0;
62851747b8eSHerbert Dürr 
62951747b8eSHerbert Dürr 		DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n");
63051747b8eSHerbert Dürr 
63151747b8eSHerbert Dürr 		// find the bound extremas
63251747b8eSHerbert Dürr 		Fixed nLeftBound = 0;
63351747b8eSHerbert Dürr 		Fixed nRightBound = 0;
63451747b8eSHerbert Dürr 		for( ItemCount i = 0; i < nBoundsCount; ++i )
63551747b8eSHerbert Dürr 		{
63651747b8eSHerbert Dürr 			const ATSTrapezoid& rTrap = aTrapezoidVector[i];
63751747b8eSHerbert Dürr 			if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) )
63851747b8eSHerbert Dürr 				nLeftBound = rTrap.lowerLeft.x;
63951747b8eSHerbert Dürr 			if( (i == 0) || (nRightBound > rTrap.lowerRight.x) )
64051747b8eSHerbert Dürr 				nRightBound = rTrap.lowerRight.x;
64151747b8eSHerbert Dürr 		}
64251747b8eSHerbert Dürr 
64351747b8eSHerbert Dürr 		// measure the bound extremas
64451747b8eSHerbert Dürr 		mnCachedWidth = nRightBound - nLeftBound;
64551747b8eSHerbert Dürr 		// adjust for eliminated trailing space widths
64651747b8eSHerbert Dürr 	}
64751747b8eSHerbert Dürr 
64851747b8eSHerbert Dürr 	int nScaledWidth = Fixed2Vcl( mnCachedWidth );
64951747b8eSHerbert Dürr 	nScaledWidth += mnTrailingSpaceWidth;
65051747b8eSHerbert Dürr 	return nScaledWidth;
65151747b8eSHerbert Dürr }
65251747b8eSHerbert Dürr 
65351747b8eSHerbert Dürr // -----------------------------------------------------------------------
65451747b8eSHerbert Dürr /**
65551747b8eSHerbert Dürr  * ATSLayout::FillDXArray : Get Char widths
65651747b8eSHerbert Dürr  *
65751747b8eSHerbert Dürr  * @param pDXArray: array to be filled with x-advances
65851747b8eSHerbert Dürr  *
65951747b8eSHerbert Dürr  * Fill the pDXArray with horizontal deltas : CharWidths
66051747b8eSHerbert Dürr  *
66151747b8eSHerbert Dürr  * @return : typographical width of the complete text layout
66251747b8eSHerbert Dürr **/
FillDXArray(long * pDXArray) const66351747b8eSHerbert Dürr long ATSLayout::FillDXArray( long* pDXArray ) const
66451747b8eSHerbert Dürr {
66551747b8eSHerbert Dürr 	// short circuit requests which don't need full details
66651747b8eSHerbert Dürr 	if( !pDXArray )
66751747b8eSHerbert Dürr 		return GetTextWidth();
66851747b8eSHerbert Dürr 
66951747b8eSHerbert Dürr 	// check assumptions
67051747b8eSHerbert Dürr 	DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" );
67151747b8eSHerbert Dürr 
67251747b8eSHerbert Dürr 	// initialize details about the resulting layout
67351747b8eSHerbert Dürr 	InitGIA();
67451747b8eSHerbert Dürr 
67551747b8eSHerbert Dürr 	// distribute the widths among the string elements
67651747b8eSHerbert Dürr 	int nPixWidth = 0;
67751747b8eSHerbert Dürr 	mnCachedWidth = 0;
67851747b8eSHerbert Dürr 	for( int i = 0; i < mnCharCount; ++i )
67951747b8eSHerbert Dürr 	{
68051747b8eSHerbert Dürr 		// convert and adjust for accumulated rounding errors
68151747b8eSHerbert Dürr 		mnCachedWidth += mpCharWidths[i];
68251747b8eSHerbert Dürr 		const int nOldPixWidth = nPixWidth;
68351747b8eSHerbert Dürr 		nPixWidth = Fixed2Vcl( mnCachedWidth );
68451747b8eSHerbert Dürr 		pDXArray[i] = nPixWidth - nOldPixWidth;
68551747b8eSHerbert Dürr 	}
68651747b8eSHerbert Dürr 
68751747b8eSHerbert Dürr 	return nPixWidth;
68851747b8eSHerbert Dürr }
68951747b8eSHerbert Dürr 
69051747b8eSHerbert Dürr // -----------------------------------------------------------------------
69151747b8eSHerbert Dürr /**
69251747b8eSHerbert Dürr  * ATSLayout::GetTextBreak : Find line break depending on width
69351747b8eSHerbert Dürr  *
69451747b8eSHerbert Dürr  * @param nMaxWidth : maximal logical text width in subpixel units
69551747b8eSHerbert Dürr  * @param nCharExtra: expanded/condensed spacing in subpixel units
69651747b8eSHerbert Dürr  * @param nFactor:    number of subpixel units per pixel
69751747b8eSHerbert Dürr  *
69851747b8eSHerbert Dürr  * Measure the layouted text to find the typographical line break
69951747b8eSHerbert Dürr  * the result is needed by the language specific line breaking
70051747b8eSHerbert Dürr  *
70151747b8eSHerbert Dürr  * @return : string index corresponding to the suggested line break
70251747b8eSHerbert Dürr **/
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const70351747b8eSHerbert Dürr int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
70451747b8eSHerbert Dürr {
70551747b8eSHerbert Dürr 	if( !maATSULayout )
70651747b8eSHerbert Dürr 		return STRING_LEN;
70751747b8eSHerbert Dürr 
70851747b8eSHerbert Dürr 	// the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine()
70951747b8eSHerbert Dürr 	if( nCharExtra != 0 )
71051747b8eSHerbert Dürr 	{
71151747b8eSHerbert Dürr 		// prepare the measurement by layouting and measuring the un-expanded/un-condensed text
71251747b8eSHerbert Dürr 		if( !InitGIA() )
71351747b8eSHerbert Dürr 			return STRING_LEN;
71451747b8eSHerbert Dürr 
71551747b8eSHerbert Dürr 		// TODO: use a better way than by testing each the char position
71651747b8eSHerbert Dürr 		ATSUTextMeasurement nATSUSumWidth = 0;
71751747b8eSHerbert Dürr 		const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor );
71851747b8eSHerbert Dürr 		const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor;
71951747b8eSHerbert Dürr 		for( int i = 0; i < mnCharCount; ++i )
72051747b8eSHerbert Dürr 		{
72151747b8eSHerbert Dürr 			nATSUSumWidth += mpCharWidths[i];
72251747b8eSHerbert Dürr 			if( nATSUSumWidth >= nATSUMaxWidth )
72351747b8eSHerbert Dürr 				return (mnMinCharPos + i);
72451747b8eSHerbert Dürr 			nATSUSumWidth += nATSUExtraWidth;
72551747b8eSHerbert Dürr 			if( nATSUSumWidth >= nATSUMaxWidth )
72651747b8eSHerbert Dürr 				if( i+1 < mnCharCount )
72751747b8eSHerbert Dürr 					return (mnMinCharPos + i);
72851747b8eSHerbert Dürr 		}
72951747b8eSHerbert Dürr 
73051747b8eSHerbert Dürr 		return STRING_LEN;
73151747b8eSHerbert Dürr 	}
73251747b8eSHerbert Dürr 
73351747b8eSHerbert Dürr 	// get a quick overview on what could fit
73451747b8eSHerbert Dürr 	const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
73551747b8eSHerbert Dürr 	if( nPixelWidth <= 0 )
73651747b8eSHerbert Dürr 		return mnMinCharPos;
73751747b8eSHerbert Dürr 
73851747b8eSHerbert Dürr 	// check assumptions
73951747b8eSHerbert Dürr 	DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" );
74051747b8eSHerbert Dürr 
74151747b8eSHerbert Dürr 	// initial measurement of text break position
74251747b8eSHerbert Dürr 	UniCharArrayOffset nBreakPos = mnMinCharPos;
74351747b8eSHerbert Dürr 	const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth );
74451747b8eSHerbert Dürr 	if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
74551747b8eSHerbert Dürr 		return mnMinCharPos;      //           or do ATSUMaxWidth=0x10000;
74651747b8eSHerbert Dürr 	OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos,
74751747b8eSHerbert Dürr 		nATSUMaxWidth, false, &nBreakPos );
74851747b8eSHerbert Dürr 	if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) )
74951747b8eSHerbert Dürr 		return STRING_LEN;
75051747b8eSHerbert Dürr 
75151747b8eSHerbert Dürr 	// the result from ATSUBreakLine() doesn't match the semantics expected by its
75251747b8eSHerbert Dürr 	// application layer callers from SW+SVX+I18N. Adjust the results to the expectations:
75351747b8eSHerbert Dürr 
75451747b8eSHerbert Dürr 	// ATSU reports that everything fits even when trailing spaces would break the line
75551747b8eSHerbert Dürr 	// #i89789# OOo's application layers expect STRING_LEN if everything fits
75651747b8eSHerbert Dürr 	if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) )
75751747b8eSHerbert Dürr 		return STRING_LEN;
75851747b8eSHerbert Dürr 
75951747b8eSHerbert Dürr 	// GetTextBreak()'s callers expect it to return the "stupid visual line break".
76051747b8eSHerbert Dürr 	// Returning anything else result.s in subtle problems in the application layers.
76151747b8eSHerbert Dürr 	static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method
76251747b8eSHerbert Dürr 	if( !bInWord )
76351747b8eSHerbert Dürr 		return nBreakPos;
76451747b8eSHerbert Dürr 
76551747b8eSHerbert Dürr 	// emulate stupid visual line breaking by line breaking for the remaining width
76651747b8eSHerbert Dürr 	ATSUTextMeasurement nLeft, nRight, nDummy;
76751747b8eSHerbert Dürr 	eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos,
76851747b8eSHerbert Dürr 		&nLeft, &nRight, &nDummy, &nDummy );
76951747b8eSHerbert Dürr 	if( eStatus != noErr )
77051747b8eSHerbert Dürr 		return nBreakPos;
77151747b8eSHerbert Dürr 	const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft);
77251747b8eSHerbert Dürr 	if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
77351747b8eSHerbert Dürr 		return nBreakPos;
77451747b8eSHerbert Dürr 	UniCharArrayOffset nBreakPosInWord = nBreakPos;
77551747b8eSHerbert Dürr 	eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord );
77651747b8eSHerbert Dürr 	return nBreakPosInWord;
77751747b8eSHerbert Dürr }
77851747b8eSHerbert Dürr 
77951747b8eSHerbert Dürr // -----------------------------------------------------------------------
78051747b8eSHerbert Dürr /**
78151747b8eSHerbert Dürr  * ATSLayout::GetCaretPositions : Find positions of carets
78251747b8eSHerbert Dürr  *
78351747b8eSHerbert Dürr  * @param nMaxIndex position to which we want to find the carets
78451747b8eSHerbert Dürr  *
78551747b8eSHerbert Dürr  * Fill the array of positions of carets (for cursors and selections)
78651747b8eSHerbert Dürr  *
78751747b8eSHerbert Dürr  * @return : none
78851747b8eSHerbert Dürr **/
GetCaretPositions(int nMaxIndex,long * pCaretXArray) const78951747b8eSHerbert Dürr void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
79051747b8eSHerbert Dürr {
79151747b8eSHerbert Dürr 	DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
79251747b8eSHerbert Dürr 		"ATSLayout::GetCaretPositions() : invalid number of caret pairs requested");
79351747b8eSHerbert Dürr 
79451747b8eSHerbert Dürr 	// initialize the caret positions
79551747b8eSHerbert Dürr 	for( int i = 0; i < nMaxIndex; ++i )
79651747b8eSHerbert Dürr 		pCaretXArray[ i ] = -1;
79751747b8eSHerbert Dürr 
79851747b8eSHerbert Dürr 	for( int n = 0; n <= mnCharCount; ++n )
79951747b8eSHerbert Dürr 	{
80051747b8eSHerbert Dürr 		// measure the characters cursor position
80151747b8eSHerbert Dürr 		typedef unsigned char Boolean;
80251747b8eSHerbert Dürr 		const Boolean bIsLeading = true;
80351747b8eSHerbert Dürr 		ATSUCaret aCaret0, aCaret1;
80451747b8eSHerbert Dürr 		Boolean bIsSplit;
80551747b8eSHerbert Dürr 		OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout,
80651747b8eSHerbert Dürr 			mnMinCharPos + n, bIsLeading, kATSUByCharacter,
80751747b8eSHerbert Dürr 			&aCaret0, &aCaret1, &bIsSplit );
80851747b8eSHerbert Dürr 		if( eStatus != noErr )
80951747b8eSHerbert Dürr 			continue;
81051747b8eSHerbert Dürr 		const Fixed nFixedPos = mnBaseAdv + aCaret0.fX;
81151747b8eSHerbert Dürr 		// convert the measurement to pixel units
81251747b8eSHerbert Dürr 		const int nPixelPos = Fixed2Vcl( nFixedPos );
81351747b8eSHerbert Dürr 		// update previous trailing position
81451747b8eSHerbert Dürr 		if( n > 0 )
81551747b8eSHerbert Dürr 			pCaretXArray[2*n-1] = nPixelPos;
81651747b8eSHerbert Dürr 		// update current leading position
81751747b8eSHerbert Dürr 		if( 2*n >= nMaxIndex )
81851747b8eSHerbert Dürr 			break;
81951747b8eSHerbert Dürr 		pCaretXArray[2*n+0] = nPixelPos;
82051747b8eSHerbert Dürr 	}
82151747b8eSHerbert Dürr }
82251747b8eSHerbert Dürr 
82351747b8eSHerbert Dürr // -----------------------------------------------------------------------
82451747b8eSHerbert Dürr /**
82551747b8eSHerbert Dürr  * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text
82651747b8eSHerbert Dürr  *
82751747b8eSHerbert Dürr  * @param rVCLRect: rectangle of text image (layout) measures
82851747b8eSHerbert Dürr  *
82951747b8eSHerbert Dürr  * Get ink bounds of the text
83051747b8eSHerbert Dürr  *
83151747b8eSHerbert Dürr  * @return : measurement valid
83251747b8eSHerbert Dürr **/
GetBoundRect(SalGraphics &,Rectangle & rVCLRect) const83351747b8eSHerbert Dürr bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const
83451747b8eSHerbert Dürr {
83551747b8eSHerbert Dürr 	const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
83651747b8eSHerbert Dürr 	const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
83751747b8eSHerbert Dürr 	const Fixed nFixedY = Vcl2Fixed( +aPos.Y() );
83851747b8eSHerbert Dürr 
83951747b8eSHerbert Dürr 	Rect aMacRect;
84051747b8eSHerbert Dürr 	OSStatus eStatus = ATSUMeasureTextImage( maATSULayout,
84151747b8eSHerbert Dürr 		mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect );
84251747b8eSHerbert Dürr 	if( eStatus != noErr )
84351747b8eSHerbert Dürr 	    return false;
84451747b8eSHerbert Dürr 
84551747b8eSHerbert Dürr 	// ATSU top-bottom are vertically flipped from a VCL aspect
84651747b8eSHerbert Dürr 	rVCLRect.Left()   = AtsuPix2Vcl( aMacRect.left );
84751747b8eSHerbert Dürr 	rVCLRect.Top()    = AtsuPix2Vcl( aMacRect.top );
84851747b8eSHerbert Dürr 	rVCLRect.Right()  = AtsuPix2Vcl( aMacRect.right );
84951747b8eSHerbert Dürr 	rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom );
85051747b8eSHerbert Dürr 	return true;
85151747b8eSHerbert Dürr }
85251747b8eSHerbert Dürr 
85351747b8eSHerbert Dürr // -----------------------------------------------------------------------
85451747b8eSHerbert Dürr /**
85551747b8eSHerbert Dürr  * ATSLayout::InitGIA() : get many informations about layouted text
85651747b8eSHerbert Dürr  *
85751747b8eSHerbert Dürr  * Fills arrays of information about the gylph layout previously done
85851747b8eSHerbert Dürr  *	in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline),
85951747b8eSHerbert Dürr  *  mapping between glyph index and character index, chars widths
86051747b8eSHerbert Dürr  *
86151747b8eSHerbert Dürr  * @return : true if everything could be computed, otherwise false
86251747b8eSHerbert Dürr **/
InitGIA(ImplLayoutArgs * pArgs) const86351747b8eSHerbert Dürr bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const
86451747b8eSHerbert Dürr {
86551747b8eSHerbert Dürr 	// no need to run InitGIA more than once on the same ATSLayout object
86651747b8eSHerbert Dürr 	if( mnGlyphCount >= 0 )
86751747b8eSHerbert Dürr 		return true;
86851747b8eSHerbert Dürr 	mnGlyphCount = 0;
86951747b8eSHerbert Dürr 
87051747b8eSHerbert Dürr 	// Workaround a bug in ATSUI with empty string
87151747b8eSHerbert Dürr 	if( mnCharCount <=  0 )
87251747b8eSHerbert Dürr 		return false;
87351747b8eSHerbert Dürr 
87451747b8eSHerbert Dürr 	// initialize character details
87551747b8eSHerbert Dürr 	mpCharWidths	= new Fixed[ mnCharCount ];
87651747b8eSHerbert Dürr 	mpChars2Glyphs	= new int[ mnCharCount ];
87751747b8eSHerbert Dürr 	for( int n = 0; n < mnCharCount; ++n )
87851747b8eSHerbert Dürr 	{
87951747b8eSHerbert Dürr 		mpCharWidths[ n ] = 0;
88051747b8eSHerbert Dürr 		mpChars2Glyphs[ n ] = -1;
88151747b8eSHerbert Dürr 	}
88251747b8eSHerbert Dürr 
88351747b8eSHerbert Dürr 	// get details about the glyph layout
88451747b8eSHerbert Dürr 	ItemCount iLayoutDataCount;
88551747b8eSHerbert Dürr 	const ATSLayoutRecord* pALR;
88651747b8eSHerbert Dürr 	OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
88751747b8eSHerbert Dürr 		maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
88851747b8eSHerbert Dürr 		(void**)&pALR, &iLayoutDataCount );
88951747b8eSHerbert Dürr 	DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n");
89051747b8eSHerbert Dürr 	if( (eStatus != noErr)
89151747b8eSHerbert Dürr 	|| (iLayoutDataCount <= 1) )
89251747b8eSHerbert Dürr 		return false;
89351747b8eSHerbert Dürr 
89451747b8eSHerbert Dürr 	// initialize glyph details
89551747b8eSHerbert Dürr 	mpGlyphIds      = new ATSGlyphRef[ iLayoutDataCount ];
89651747b8eSHerbert Dürr 	mpGlyphAdvances	= new Fixed[ iLayoutDataCount ];
89751747b8eSHerbert Dürr 	mpGlyphs2Chars	= new int[ iLayoutDataCount ];
89851747b8eSHerbert Dürr 
89951747b8eSHerbert Dürr 	// measure details of the glyph layout
90051747b8eSHerbert Dürr 	Fixed nLeftPos = 0;
90151747b8eSHerbert Dürr 	for( ItemCount i = 0; i < iLayoutDataCount; ++i )
90251747b8eSHerbert Dürr 	{
90351747b8eSHerbert Dürr 		const ATSLayoutRecord& rALR = pALR[i];
90451747b8eSHerbert Dürr 
90551747b8eSHerbert Dürr 		// distribute the widths as fairly as possible among the chars
90651747b8eSHerbert Dürr 		const int nRelativeIdx = (rALR.originalOffset / 2);
90751747b8eSHerbert Dürr 		if( i+1 < iLayoutDataCount )
90851747b8eSHerbert Dürr 			mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos;
90951747b8eSHerbert Dürr 
91051747b8eSHerbert Dürr 		// new glyph is available => finish measurement of old glyph
91151747b8eSHerbert Dürr 		if( mnGlyphCount > 0 )
91251747b8eSHerbert Dürr 			mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos;
91351747b8eSHerbert Dürr 
91451747b8eSHerbert Dürr 		// ignore marker or deleted glyphs
91551747b8eSHerbert Dürr 		enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF};
91651747b8eSHerbert Dürr 		if( rALR.glyphID >= MARKED_OUTGLYPH )
91751747b8eSHerbert Dürr 			continue;
91851747b8eSHerbert Dürr 
91951747b8eSHerbert Dürr 		DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph),
92051747b8eSHerbert Dürr 			"ATSLayout::InitGIA(): terminator glyph not marked as deleted!" );
92151747b8eSHerbert Dürr 
92251747b8eSHerbert Dürr 		// store details of the visible glyphs
92351747b8eSHerbert Dürr 		nLeftPos = rALR.realPos;
92451747b8eSHerbert Dürr 		mpGlyphIds[ mnGlyphCount ] = rALR.glyphID;
92551747b8eSHerbert Dürr 
92651747b8eSHerbert Dürr 		// map visible glyphs to their counterparts in the UTF16-character array
92751747b8eSHerbert Dürr 		mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos;
92851747b8eSHerbert Dürr 		mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount;
92951747b8eSHerbert Dürr 
93051747b8eSHerbert Dürr 		++mnGlyphCount;
93151747b8eSHerbert Dürr 	}
93251747b8eSHerbert Dürr 
93351747b8eSHerbert Dürr 	// measure complete width
93451747b8eSHerbert Dürr 	mnCachedWidth = mnBaseAdv;
93551747b8eSHerbert Dürr 	mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos;
93651747b8eSHerbert Dürr 
93751747b8eSHerbert Dürr #if (OSL_DEBUG_LEVEL > 1)
93851747b8eSHerbert Dürr 	Fixed nWidthSum = mnBaseAdv;
93951747b8eSHerbert Dürr 	for( int n = 0; n < mnCharCount; ++n )
94051747b8eSHerbert Dürr 		nWidthSum += mpCharWidths[ n ];
94151747b8eSHerbert Dürr 	DBG_ASSERT( (nWidthSum==mnCachedWidth),
94251747b8eSHerbert Dürr 		"ATSLayout::InitGIA(): measured widths do not match!\n" );
94351747b8eSHerbert Dürr #endif
94451747b8eSHerbert Dürr 
94551747b8eSHerbert Dürr 	// #i91183# we need to split up the portion into sub-portions
94651747b8eSHerbert Dürr 	// if the ATSU-layout differs too much from the requested layout
94751747b8eSHerbert Dürr 	if( pArgs && pArgs->mpDXArray )
94851747b8eSHerbert Dürr 	{
94951747b8eSHerbert Dürr 		// TODO: non-strong-LTR case cases should be handled too
95051747b8eSHerbert Dürr 		if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG)
95151747b8eSHerbert Dürr 		&& !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) )
95251747b8eSHerbert Dürr 		{
95351747b8eSHerbert Dürr 			Fixed nSumCharWidths = 0;
95451747b8eSHerbert Dürr 			SubPortion aSubPortion = { mnMinCharPos, 0, 0 };
95551747b8eSHerbert Dürr 			for( int i = 0; i < mnCharCount; ++i )
95651747b8eSHerbert Dürr 			{
95751747b8eSHerbert Dürr 				// calculate related logical position
95851747b8eSHerbert Dürr 				nSumCharWidths += mpCharWidths[i];
95951747b8eSHerbert Dürr 
96051747b8eSHerbert Dürr 				// start new sub-portion if needed
96151747b8eSHerbert Dürr 				const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]);
96251747b8eSHerbert Dürr 				const Fixed nNextXOffset = nNextXPos - nSumCharWidths;
96351747b8eSHerbert Dürr 				const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset;
96451747b8eSHerbert Dürr 				if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) {
96551747b8eSHerbert Dürr 					// get to the end of the current sub-portion
96651747b8eSHerbert Dürr 					// prevent splitting up at diacritics etc.
96751747b8eSHerbert Dürr 					int j = i;
96851747b8eSHerbert Dürr 					while( (++j < mnCharCount) && !mpCharWidths[j] );
96951747b8eSHerbert Dürr 					aSubPortion.mnEndCharPos = mnMinCharPos + j;
97051747b8eSHerbert Dürr 					// emit current sub-portion
97151747b8eSHerbert Dürr 					maSubPortions.push_back( aSubPortion );
97251747b8eSHerbert Dürr 					// prepare next sub-portion
97351747b8eSHerbert Dürr 					aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos;
97451747b8eSHerbert Dürr 					aSubPortion.mnXOffset = nNextXOffset;
97551747b8eSHerbert Dürr 				}
97651747b8eSHerbert Dürr 			}
97751747b8eSHerbert Dürr 
97851747b8eSHerbert Dürr 			// emit the remaining sub-portion
97951747b8eSHerbert Dürr 			if( !maSubPortions.empty() )
98051747b8eSHerbert Dürr 			{
98151747b8eSHerbert Dürr 				aSubPortion.mnEndCharPos = mnEndCharPos;
98251747b8eSHerbert Dürr 				if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos )
98351747b8eSHerbert Dürr 					maSubPortions.push_back( aSubPortion );
98451747b8eSHerbert Dürr 			}
98551747b8eSHerbert Dürr 		}
98651747b8eSHerbert Dürr 
98751747b8eSHerbert Dürr 		// override layouted charwidths with requested charwidths
98851747b8eSHerbert Dürr 		for( int n = 0; n < mnCharCount; ++n )
98951747b8eSHerbert Dürr 			mpCharWidths[ n ] = pArgs->mpDXArray[ n ];
99051747b8eSHerbert Dürr 	}
99151747b8eSHerbert Dürr 
99251747b8eSHerbert Dürr 	// release the ATSU layout records
99351747b8eSHerbert Dürr 	ATSUDirectReleaseLayoutDataArrayPtr(NULL,
99451747b8eSHerbert Dürr 		kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR );
99551747b8eSHerbert Dürr 
99651747b8eSHerbert Dürr 	return true;
99751747b8eSHerbert Dürr }
99851747b8eSHerbert Dürr 
99951747b8eSHerbert Dürr // -----------------------------------------------------------------------
100051747b8eSHerbert Dürr 
GetIdealX() const100151747b8eSHerbert Dürr bool ATSLayout::GetIdealX() const
100251747b8eSHerbert Dürr {
100351747b8eSHerbert Dürr 	// compute the ideal advance widths only once
100451747b8eSHerbert Dürr 	if( mpGlyphOrigAdvs != NULL )
100551747b8eSHerbert Dürr 		return true;
100651747b8eSHerbert Dürr 
100751747b8eSHerbert Dürr 	DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" );
100851747b8eSHerbert Dürr 	DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" );
100951747b8eSHerbert Dürr 
101051747b8eSHerbert Dürr 	// TODO: cache ideal metrics per glyph?
101151747b8eSHerbert Dürr 	std::vector<ATSGlyphIdealMetrics> aIdealMetrics;
101251747b8eSHerbert Dürr 	aIdealMetrics.resize( mnGlyphCount );
101351747b8eSHerbert Dürr 	OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle,
101451747b8eSHerbert Dürr 		mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] );
101551747b8eSHerbert Dürr 	DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!");
101651747b8eSHerbert Dürr 	if( theErr != noErr )
101751747b8eSHerbert Dürr 		return false;
101851747b8eSHerbert Dürr 
101951747b8eSHerbert Dürr 	mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ];
102051747b8eSHerbert Dürr 	for( int i = 0;i < mnGlyphCount;++i )
102151747b8eSHerbert Dürr 		mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x );
102251747b8eSHerbert Dürr 
102351747b8eSHerbert Dürr 	return true;
102451747b8eSHerbert Dürr }
102551747b8eSHerbert Dürr 
102651747b8eSHerbert Dürr // -----------------------------------------------------------------------
102751747b8eSHerbert Dürr 
GetDeltaY() const102851747b8eSHerbert Dürr bool ATSLayout::GetDeltaY() const
102951747b8eSHerbert Dürr {
103051747b8eSHerbert Dürr 	// don't bother to get the same delta-y-array more than once
103151747b8eSHerbert Dürr 	if( mpDeltaY != NULL )
103251747b8eSHerbert Dürr 		return true;
103351747b8eSHerbert Dürr 
103451747b8eSHerbert Dürr #if 1
103551747b8eSHerbert Dürr 	if( !maATSULayout )
103651747b8eSHerbert Dürr 		return false;
103751747b8eSHerbert Dürr 
103851747b8eSHerbert Dürr 	// get and keep the y-deltas in the mpDeltaY member variable
103951747b8eSHerbert Dürr 	// => release it in the destructor
104051747b8eSHerbert Dürr 	ItemCount nDeltaCount = 0;
104151747b8eSHerbert Dürr 	OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
104251747b8eSHerbert Dürr 		maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray,
104351747b8eSHerbert Dürr 		(void**)&mpDeltaY, &nDeltaCount );
104451747b8eSHerbert Dürr 
104551747b8eSHerbert Dürr 	DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n");
104651747b8eSHerbert Dürr 	if( theErr != noErr )
104751747b8eSHerbert Dürr 		return false;
104851747b8eSHerbert Dürr 
104951747b8eSHerbert Dürr 	if( mpDeltaY == NULL )
105051747b8eSHerbert Dürr 		return true;
105151747b8eSHerbert Dürr 
105251747b8eSHerbert Dürr 	if( nDeltaCount != (ItemCount)mnGlyphCount )
105351747b8eSHerbert Dürr 	{
105451747b8eSHerbert Dürr 		DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" );
105551747b8eSHerbert Dürr 		ATSUDirectReleaseLayoutDataArrayPtr( NULL,
105651747b8eSHerbert Dürr 			kATSUDirectDataBaselineDeltaFixedArray,	(void**)&mpDeltaY );
105751747b8eSHerbert Dürr 		mpDeltaY = NULL;
105851747b8eSHerbert Dürr 		return false;
105951747b8eSHerbert Dürr 	}
106051747b8eSHerbert Dürr #endif
106151747b8eSHerbert Dürr 
106251747b8eSHerbert Dürr 	return true;
106351747b8eSHerbert Dürr }
106451747b8eSHerbert Dürr 
106551747b8eSHerbert Dürr // -----------------------------------------------------------------------
106651747b8eSHerbert Dürr 
106751747b8eSHerbert Dürr #define DELETEAZ( X ) { delete[] X; X = NULL; }
106851747b8eSHerbert Dürr 
InvalidateMeasurements()106951747b8eSHerbert Dürr void ATSLayout::InvalidateMeasurements()
107051747b8eSHerbert Dürr {
107151747b8eSHerbert Dürr 	mnGlyphCount = -1;
107251747b8eSHerbert Dürr 	DELETEAZ( mpGlyphIds );
107351747b8eSHerbert Dürr 	DELETEAZ( mpCharWidths );
107451747b8eSHerbert Dürr 	DELETEAZ( mpChars2Glyphs );
107551747b8eSHerbert Dürr 	DELETEAZ( mpGlyphs2Chars );
107651747b8eSHerbert Dürr 	DELETEAZ( mpGlyphRTLFlags );
107751747b8eSHerbert Dürr 	DELETEAZ( mpGlyphAdvances );
107851747b8eSHerbert Dürr 	DELETEAZ( mpGlyphOrigAdvs );
107951747b8eSHerbert Dürr 	DELETEAZ( mpDeltaY );
108051747b8eSHerbert Dürr }
108151747b8eSHerbert Dürr 
108251747b8eSHerbert Dürr // =======================================================================
108351747b8eSHerbert Dürr 
108451747b8eSHerbert Dürr #if 0
108551747b8eSHerbert Dürr // helper class to convert ATSUI outlines to VCL PolyPolygons
108651747b8eSHerbert Dürr class PolyArgs
108751747b8eSHerbert Dürr {
108851747b8eSHerbert Dürr public:
108951747b8eSHerbert Dürr 				PolyArgs();
109051747b8eSHerbert Dürr 				~PolyArgs();
109151747b8eSHerbert Dürr 
109251747b8eSHerbert Dürr 	void		Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset );
109351747b8eSHerbert Dürr 	void		AddPoint( const Float32Point&, PolyFlags );
109451747b8eSHerbert Dürr 	void		ClosePolygon();
109551747b8eSHerbert Dürr 
109651747b8eSHerbert Dürr private:
109751747b8eSHerbert Dürr 	PolyPolygon* mpPolyPoly;
109851747b8eSHerbert Dürr 	long		mnXOffset, mnYOffset;
109951747b8eSHerbert Dürr 
110051747b8eSHerbert Dürr 	Point*		mpPointAry;
110151747b8eSHerbert Dürr 	BYTE*		mpFlagAry;
110251747b8eSHerbert Dürr 	USHORT		mnMaxPoints;
110351747b8eSHerbert Dürr 
110451747b8eSHerbert Dürr 	USHORT		mnPointCount;
110551747b8eSHerbert Dürr 	USHORT		mnPolyCount;
110651747b8eSHerbert Dürr 	bool		mbHasOffline;
110751747b8eSHerbert Dürr };
110851747b8eSHerbert Dürr 
110951747b8eSHerbert Dürr // -----------------------------------------------------------------------
111051747b8eSHerbert Dürr 
111151747b8eSHerbert Dürr PolyArgs::PolyArgs()
111251747b8eSHerbert Dürr :	mpPolyPoly(NULL),
111351747b8eSHerbert Dürr 	mnPointCount(0),
111451747b8eSHerbert Dürr 	mnPolyCount(0),
111551747b8eSHerbert Dürr 	mbHasOffline(false)
111651747b8eSHerbert Dürr {
111751747b8eSHerbert Dürr 	mnMaxPoints = 256;
111851747b8eSHerbert Dürr 	mpPointAry	= new Point[ mnMaxPoints ];
111951747b8eSHerbert Dürr 	mpFlagAry	= new BYTE [ mnMaxPoints ];
112051747b8eSHerbert Dürr }
112151747b8eSHerbert Dürr 
112251747b8eSHerbert Dürr // -----------------------------------------------------------------------
112351747b8eSHerbert Dürr 
112451747b8eSHerbert Dürr PolyArgs::~PolyArgs()
112551747b8eSHerbert Dürr {
112651747b8eSHerbert Dürr 	delete[] mpFlagAry;
112751747b8eSHerbert Dürr 	delete[] mpPointAry;
112851747b8eSHerbert Dürr }
112951747b8eSHerbert Dürr 
113051747b8eSHerbert Dürr // -----------------------------------------------------------------------
113151747b8eSHerbert Dürr 
113251747b8eSHerbert Dürr void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset )
113351747b8eSHerbert Dürr {
113451747b8eSHerbert Dürr 	mnXOffset = nXOffset;
113551747b8eSHerbert Dürr 	mnYOffset = nYOffset;
113651747b8eSHerbert Dürr 	mpPolyPoly = pPolyPoly;
113751747b8eSHerbert Dürr 
113851747b8eSHerbert Dürr 	mpPolyPoly->Clear();
113951747b8eSHerbert Dürr 	mnPointCount = 0;
114051747b8eSHerbert Dürr 	mnPolyCount  = 0;
114151747b8eSHerbert Dürr }
114251747b8eSHerbert Dürr 
114351747b8eSHerbert Dürr // -----------------------------------------------------------------------
114451747b8eSHerbert Dürr 
114551747b8eSHerbert Dürr void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags )
114651747b8eSHerbert Dürr {
114751747b8eSHerbert Dürr 	if( mnPointCount >= mnMaxPoints )
114851747b8eSHerbert Dürr 	{
114951747b8eSHerbert Dürr 		// resize if needed (TODO: use STL?)
115051747b8eSHerbert Dürr 		mnMaxPoints *= 4;
115151747b8eSHerbert Dürr 		Point* mpNewPoints = new Point[ mnMaxPoints ];
115251747b8eSHerbert Dürr 		BYTE* mpNewFlags = new BYTE[ mnMaxPoints ];
115351747b8eSHerbert Dürr 		for( int i = 0; i < mnPointCount; ++i )
115451747b8eSHerbert Dürr 		{
115551747b8eSHerbert Dürr 			mpNewPoints[ i ] = mpPointAry[ i ];
115651747b8eSHerbert Dürr 			mpNewFlags[ i ] = mpFlagAry[ i ];
115751747b8eSHerbert Dürr 		}
115851747b8eSHerbert Dürr 		delete[] mpFlagAry;
115951747b8eSHerbert Dürr 		delete[] mpPointAry;
116051747b8eSHerbert Dürr 		mpPointAry = mpNewPoints;
116151747b8eSHerbert Dürr 		mpFlagAry = mpNewFlags;
116251747b8eSHerbert Dürr 	}
116351747b8eSHerbert Dürr 
116451747b8eSHerbert Dürr 	// convert to pixels and add startpoint offset
116551747b8eSHerbert Dürr 	int nXPos = Float32ToInt( rPoint.x );
116651747b8eSHerbert Dürr 	int nYPos = Float32ToInt( rPoint.y );
116751747b8eSHerbert Dürr 	mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset );
116851747b8eSHerbert Dürr 	// set point flags
116951747b8eSHerbert Dürr 	mpFlagAry[ mnPointCount++ ]= eFlags;
117051747b8eSHerbert Dürr 	mbHasOffline |= (eFlags != POLY_NORMAL);
117151747b8eSHerbert Dürr }
117251747b8eSHerbert Dürr 
117351747b8eSHerbert Dürr // -----------------------------------------------------------------------
117451747b8eSHerbert Dürr 
117551747b8eSHerbert Dürr void PolyArgs::ClosePolygon()
117651747b8eSHerbert Dürr {
117751747b8eSHerbert Dürr 	if( !mnPolyCount++ )
117851747b8eSHerbert Dürr 		 return;
117951747b8eSHerbert Dürr 
118051747b8eSHerbert Dürr 	// append finished polygon
118151747b8eSHerbert Dürr 	Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) );
118251747b8eSHerbert Dürr 	mpPolyPoly->Insert( aPoly );
118351747b8eSHerbert Dürr 
118451747b8eSHerbert Dürr 	// prepare for new polygon
118551747b8eSHerbert Dürr 	mnPointCount = 0;
118651747b8eSHerbert Dürr 	mbHasOffline = false;
118751747b8eSHerbert Dürr }
118851747b8eSHerbert Dürr #endif
118951747b8eSHerbert Dürr // =======================================================================
119051747b8eSHerbert Dürr 
119151747b8eSHerbert Dürr // glyph fallback is supported directly by Aqua
119251747b8eSHerbert Dürr // so methods used only by MultiSalLayout can be dummy implementated
GetGlyphOutlines(SalGraphics &,PolyPolyVector &) const119351747b8eSHerbert Dürr bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
InitFont()119451747b8eSHerbert Dürr void ATSLayout::InitFont() {}
MoveGlyph(int,long)119551747b8eSHerbert Dürr void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
DropGlyph(int)119651747b8eSHerbert Dürr void ATSLayout::DropGlyph( int /*nStart*/ ) {}
Simplify(bool)119751747b8eSHerbert Dürr void ATSLayout::Simplify( bool /*bIsBase*/ ) {}
119851747b8eSHerbert Dürr 
119951747b8eSHerbert Dürr // get the ImplFontData for a glyph fallback font
120051747b8eSHerbert Dürr // for a glyphid that was returned by ATSLayout::GetNextGlyphs()
GetFallbackFontData(sal_GlyphId aGlyphId) const120151747b8eSHerbert Dürr const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const
120251747b8eSHerbert Dürr {
120351747b8eSHerbert Dürr 	// check if any fallback fonts were needed
120451747b8eSHerbert Dürr 	if( !mpFallbackInfo )
120551747b8eSHerbert Dürr 		return NULL;
120651747b8eSHerbert Dürr 	// check if the current glyph needs a fallback font
120751747b8eSHerbert Dürr 	int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
120851747b8eSHerbert Dürr 	if( !nFallbackLevel )
120951747b8eSHerbert Dürr 		return NULL;
121051747b8eSHerbert Dürr 	return mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
121151747b8eSHerbert Dürr }
121251747b8eSHerbert Dürr 
121351747b8eSHerbert Dürr // =======================================================================
121451747b8eSHerbert Dürr 
AddFallback(ATSUFontID nFontId)121551747b8eSHerbert Dürr int FallbackInfo::AddFallback( ATSUFontID nFontId )
121651747b8eSHerbert Dürr {
121751747b8eSHerbert Dürr 	// check if the fallback font is already known
121851747b8eSHerbert Dürr 	for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel )
121951747b8eSHerbert Dürr 		if( maATSUFontId[ nLevel ] == nFontId )
122051747b8eSHerbert Dürr 			return (nLevel + 1);
122151747b8eSHerbert Dürr 
122251747b8eSHerbert Dürr 	// append new fallback font if possible
122351747b8eSHerbert Dürr 	if( mnMaxLevel >= MAX_FALLBACK-1 )
122451747b8eSHerbert Dürr 		return 0;
122551747b8eSHerbert Dürr 	// keep ATSU font id of fallback font
122651747b8eSHerbert Dürr 	maATSUFontId[ mnMaxLevel ] = nFontId;
122751747b8eSHerbert Dürr 	// find and cache the corresponding ImplFontData pointer
122851747b8eSHerbert Dürr 	const SystemFontList* pSFL = GetSalData()->mpFontList;
122951747b8eSHerbert Dürr 	const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId );
123051747b8eSHerbert Dürr 	maFontData[ mnMaxLevel ] = pFontData;
123151747b8eSHerbert Dürr 	// increase fallback level by one
123251747b8eSHerbert Dürr 	return (++mnMaxLevel);
123351747b8eSHerbert Dürr }
123451747b8eSHerbert Dürr 
123551747b8eSHerbert Dürr // -----------------------------------------------------------------------
123651747b8eSHerbert Dürr 
GetFallbackFontData(int nFallbackLevel) const123751747b8eSHerbert Dürr const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const
123851747b8eSHerbert Dürr {
123951747b8eSHerbert Dürr 	const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ];
124051747b8eSHerbert Dürr 	return pFallbackFont;
124151747b8eSHerbert Dürr }
124251747b8eSHerbert Dürr 
124351747b8eSHerbert Dürr // =======================================================================
124451747b8eSHerbert Dürr 
GetTextLayout(ImplLayoutArgs &,int)124551747b8eSHerbert Dürr SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ )
124651747b8eSHerbert Dürr {
124751747b8eSHerbert Dürr 	ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale );
124851747b8eSHerbert Dürr 	return pATSLayout;
124951747b8eSHerbert Dürr }
125051747b8eSHerbert Dürr 
125151747b8eSHerbert Dürr // =======================================================================
125251747b8eSHerbert Dürr 
1253