xref: /aoo41x/main/vcl/aqua/source/gdi/atslayout.cxx (revision e26449d3)
19f62ea84SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
39f62ea84SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
49f62ea84SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
59f62ea84SAndrew Rist  * distributed with this work for additional information
69f62ea84SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
79f62ea84SAndrew Rist  * to you under the Apache License, Version 2.0 (the
89f62ea84SAndrew Rist  * "License"); you may not use this file except in compliance
99f62ea84SAndrew Rist  * with the License.  You may obtain a copy of the License at
109f62ea84SAndrew Rist  *
119f62ea84SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
129f62ea84SAndrew Rist  *
139f62ea84SAndrew Rist  * Unless required by applicable law or agreed to in writing,
149f62ea84SAndrew Rist  * software distributed under the License is distributed on an
159f62ea84SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
169f62ea84SAndrew Rist  * KIND, either express or implied.  See the License for the
179f62ea84SAndrew Rist  * specific language governing permissions and limitations
189f62ea84SAndrew Rist  * under the License.
199f62ea84SAndrew Rist  *
209f62ea84SAndrew Rist  *************************************************************/
219f62ea84SAndrew Rist 
229f62ea84SAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir #include "tools/debug.hxx"
25cdf0e10cSrcweir 
26cdf0e10cSrcweir #include "aqua/saldata.hxx"
27cdf0e10cSrcweir #include "aqua/salgdi.h"
2851747b8eSHerbert Dürr #include "atsfonts.hxx"
29cdf0e10cSrcweir 
30cdf0e10cSrcweir #include "sallayout.hxx"
31cdf0e10cSrcweir #include "salgdi.hxx"
32cdf0e10cSrcweir 
33cdf0e10cSrcweir #include <math.h>
34cdf0e10cSrcweir 
35cdf0e10cSrcweir // =======================================================================
36cdf0e10cSrcweir 
37cdf0e10cSrcweir class ATSLayout : public SalLayout
38cdf0e10cSrcweir {
39cdf0e10cSrcweir public:
40*e26449d3SHerbert Dürr 	explicit        ATSLayout( ATSUStyle&, float fFontScale );
41cdf0e10cSrcweir 	virtual         ~ATSLayout();
42cdf0e10cSrcweir 
43cdf0e10cSrcweir 	virtual bool	LayoutText( ImplLayoutArgs& );
44cdf0e10cSrcweir 	virtual void	AdjustLayout( ImplLayoutArgs& );
45cdf0e10cSrcweir 	virtual void	DrawText( SalGraphics& ) const;
46cdf0e10cSrcweir 
47*e26449d3SHerbert Dürr 	virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
48cdf0e10cSrcweir 						sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
49cdf0e10cSrcweir 
50cdf0e10cSrcweir 	virtual long    GetTextWidth() const;
51cdf0e10cSrcweir 	virtual long    FillDXArray( long* pDXArray ) const;
52cdf0e10cSrcweir 	virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
53cdf0e10cSrcweir 	virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
54cdf0e10cSrcweir 	virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
55cdf0e10cSrcweir 	virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
56cdf0e10cSrcweir 
57cdf0e10cSrcweir 	const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
58cdf0e10cSrcweir 
59cdf0e10cSrcweir 	virtual void    InitFont();
60cdf0e10cSrcweir 	virtual void    MoveGlyph( int nStart, long nNewXPos );
61cdf0e10cSrcweir 	virtual void    DropGlyph( int nStart );
62cdf0e10cSrcweir 	virtual void    Simplify( bool bIsBase );
63cdf0e10cSrcweir 
64cdf0e10cSrcweir private:
65cdf0e10cSrcweir 	ATSUStyle&			mrATSUStyle;
66cdf0e10cSrcweir 	ATSUTextLayout		maATSULayout;
67cdf0e10cSrcweir 	int					mnCharCount;		// ==mnEndCharPos-mnMinCharPos
68cdf0e10cSrcweir 	// to prevent ATS overflowing the Fixed16.16 values
69cdf0e10cSrcweir 	// ATS font requests get size limited by downscaling huge fonts
70cdf0e10cSrcweir 	// in these cases the font scale becomes something bigger than 1.0
71cdf0e10cSrcweir 	float				mfFontScale;
72cdf0e10cSrcweir 
73cdf0e10cSrcweir private:
74cdf0e10cSrcweir 	bool	InitGIA( ImplLayoutArgs* pArgs = NULL ) const;
75cdf0e10cSrcweir 	bool	GetIdealX() const;
76cdf0e10cSrcweir 	bool	GetDeltaY() const;
77cdf0e10cSrcweir 	void	InvalidateMeasurements();
78cdf0e10cSrcweir 
79cdf0e10cSrcweir 	int	Fixed2Vcl( Fixed ) const;       // convert ATSU-Fixed units to VCL units
80cdf0e10cSrcweir 	int	AtsuPix2Vcl( int ) const;       // convert ATSU-Pixel units to VCL units
81cdf0e10cSrcweir 	Fixed	Vcl2Fixed( int ) const;     // convert VCL units to ATSU-Fixed units
82cdf0e10cSrcweir 
83*e26449d3SHerbert Dürr 	// cached details about the resulting layout
84*e26449d3SHerbert Dürr 	// mutable members since these details are all lazy initialized
85*e26449d3SHerbert Dürr 	mutable int			mnGlyphCount;			// glyph count
86*e26449d3SHerbert Dürr 	mutable Fixed		mnCachedWidth;			// cached value of resulting typographical width
87*e26449d3SHerbert Dürr 	int					mnTrailingSpaceWidth;   // in Pixels
88cdf0e10cSrcweir 
89cdf0e10cSrcweir 	mutable ATSGlyphRef*	mpGlyphIds;			// ATSU glyph ids
90*e26449d3SHerbert Dürr 	mutable Fixed*			mpCharWidths;       // map relative charpos to charwidth
91*e26449d3SHerbert Dürr 	mutable int*			mpChars2Glyphs;     // map relative charpos to absolute glyphpos
92*e26449d3SHerbert Dürr 	mutable int*			mpGlyphs2Chars;     // map absolute glyphpos to absolute charpos
93*e26449d3SHerbert Dürr 	mutable bool*			mpGlyphRTLFlags;    // BiDi status for glyphs: true if RTL
94*e26449d3SHerbert Dürr 	mutable Fixed*			mpGlyphAdvances;	// contains glyph widths for the justified layout
95*e26449d3SHerbert Dürr 	mutable Fixed*			mpGlyphOrigAdvs;	// contains glyph widths for the unjustified layout
96*e26449d3SHerbert Dürr 	mutable Fixed*			mpDeltaY;			// vertical offset from the baseline
97cdf0e10cSrcweir 
98cdf0e10cSrcweir 	struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; };
99cdf0e10cSrcweir 	typedef std::vector<SubPortion> SubPortionVector;
100cdf0e10cSrcweir 	mutable SubPortionVector	maSubPortions;		// Writer&ATSUI layouts can differ quite a bit...
101cdf0e10cSrcweir 
102*e26449d3SHerbert Dürr 	// storing details about fonts used in glyph-fallback for this layout
103*e26449d3SHerbert Dürr 	mutable class FallbackInfo*	mpFallbackInfo;
104cdf0e10cSrcweir 
105*e26449d3SHerbert Dürr 	// x-offset relative to layout origin
106*e26449d3SHerbert Dürr 	// currently only used in RTL-layouts
107*e26449d3SHerbert Dürr 	mutable Fixed			mnBaseAdv;
108cdf0e10cSrcweir };
109cdf0e10cSrcweir 
110cdf0e10cSrcweir class FallbackInfo
111cdf0e10cSrcweir {
112cdf0e10cSrcweir public:
FallbackInfo()113*e26449d3SHerbert Dürr 	FallbackInfo() : mnMaxLevel(0) {}
114*e26449d3SHerbert Dürr 	int AddFallback( ATSUFontID );
115*e26449d3SHerbert Dürr 	const ImplFontData* GetFallbackFontData( int nLevel ) const;
116cdf0e10cSrcweir 
117cdf0e10cSrcweir private:
118*e26449d3SHerbert Dürr 	const ImplMacFontData* maFontData[ MAX_FALLBACK ];
119*e26449d3SHerbert Dürr 	ATSUFontID             maATSUFontId[ MAX_FALLBACK ];
120*e26449d3SHerbert Dürr 	int                    mnMaxLevel;
121cdf0e10cSrcweir };
122cdf0e10cSrcweir 
123cdf0e10cSrcweir // =======================================================================
124cdf0e10cSrcweir 
ATSLayout(ATSUStyle & rATSUStyle,float fFontScale)125cdf0e10cSrcweir ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
126*e26449d3SHerbert Dürr :	mrATSUStyle( rATSUStyle ),
127cdf0e10cSrcweir 	maATSULayout( NULL ),
128cdf0e10cSrcweir 	mnCharCount( 0 ),
129cdf0e10cSrcweir 	mfFontScale( fFontScale ),
130cdf0e10cSrcweir 	mnGlyphCount( -1 ),
131cdf0e10cSrcweir 	mnCachedWidth( 0 ),
132cdf0e10cSrcweir 	mnTrailingSpaceWidth( 0 ),
133cdf0e10cSrcweir 	mpGlyphIds( NULL ),
134cdf0e10cSrcweir 	mpCharWidths( NULL ),
135cdf0e10cSrcweir 	mpChars2Glyphs( NULL ),
136cdf0e10cSrcweir 	mpGlyphs2Chars( NULL ),
137cdf0e10cSrcweir 	mpGlyphRTLFlags( NULL ),
138cdf0e10cSrcweir 	mpGlyphAdvances( NULL ),
139cdf0e10cSrcweir 	mpGlyphOrigAdvs( NULL ),
140cdf0e10cSrcweir 	mpDeltaY( NULL ),
141cdf0e10cSrcweir 	mpFallbackInfo( NULL ),
142cdf0e10cSrcweir 	mnBaseAdv( 0 )
143cdf0e10cSrcweir {}
144cdf0e10cSrcweir 
145cdf0e10cSrcweir // -----------------------------------------------------------------------
146cdf0e10cSrcweir 
~ATSLayout()147cdf0e10cSrcweir ATSLayout::~ATSLayout()
148cdf0e10cSrcweir {
149*e26449d3SHerbert Dürr 	if( mpDeltaY )
150*e26449d3SHerbert Dürr 		ATSUDirectReleaseLayoutDataArrayPtr( NULL,
151*e26449d3SHerbert Dürr 			kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
152cdf0e10cSrcweir 
153*e26449d3SHerbert Dürr 	if( maATSULayout )
154*e26449d3SHerbert Dürr 		ATSUDisposeTextLayout( maATSULayout );
155cdf0e10cSrcweir 
156*e26449d3SHerbert Dürr 	delete[] mpGlyphRTLFlags;
157*e26449d3SHerbert Dürr 	delete[] mpGlyphs2Chars;
158*e26449d3SHerbert Dürr 	delete[] mpChars2Glyphs;
159*e26449d3SHerbert Dürr 	if( mpCharWidths != mpGlyphAdvances )
160*e26449d3SHerbert Dürr 		delete[] mpCharWidths;
161*e26449d3SHerbert Dürr 	delete[] mpGlyphIds;
162*e26449d3SHerbert Dürr 	delete[] mpGlyphOrigAdvs;
163*e26449d3SHerbert Dürr 	delete[] mpGlyphAdvances;
164*e26449d3SHerbert Dürr 
165*e26449d3SHerbert Dürr 	delete mpFallbackInfo;
166cdf0e10cSrcweir }
167cdf0e10cSrcweir 
168cdf0e10cSrcweir // -----------------------------------------------------------------------
169cdf0e10cSrcweir 
Fixed2Vcl(Fixed nFixed) const170cdf0e10cSrcweir inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const
171cdf0e10cSrcweir {
172cdf0e10cSrcweir 	float fFloat = mfFontScale * FixedToFloat( nFixed );
173cdf0e10cSrcweir 	return static_cast<int>(fFloat + 0.5);
174cdf0e10cSrcweir }
175cdf0e10cSrcweir 
176cdf0e10cSrcweir // -----------------------------------------------------------------------
177cdf0e10cSrcweir 
AtsuPix2Vcl(int nAtsuPixel) const178cdf0e10cSrcweir inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const
179cdf0e10cSrcweir {
180cdf0e10cSrcweir 	float fVclPixel = mfFontScale * nAtsuPixel;
181cdf0e10cSrcweir 	fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5;	// prepare rounding to int
182cdf0e10cSrcweir 	int nVclPixel = static_cast<int>( fVclPixel);
183cdf0e10cSrcweir 	return nVclPixel;
184cdf0e10cSrcweir }
185cdf0e10cSrcweir 
186cdf0e10cSrcweir // -----------------------------------------------------------------------
187cdf0e10cSrcweir 
Vcl2Fixed(int nPixel) const188cdf0e10cSrcweir inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const
189cdf0e10cSrcweir {
190cdf0e10cSrcweir 	return FloatToFixed( nPixel / mfFontScale );
191cdf0e10cSrcweir }
192cdf0e10cSrcweir 
193cdf0e10cSrcweir // -----------------------------------------------------------------------
194cdf0e10cSrcweir /**
195cdf0e10cSrcweir  * ATSLayout::LayoutText : Manage text layouting
196cdf0e10cSrcweir  *
197cdf0e10cSrcweir  * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout
198cdf0e10cSrcweir  *
199cdf0e10cSrcweir  * Typographic layout of text by using the style maATSUStyle
200cdf0e10cSrcweir  *
201cdf0e10cSrcweir  * @return : true if everything is ok
202cdf0e10cSrcweir **/
LayoutText(ImplLayoutArgs & rArgs)203cdf0e10cSrcweir bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs )
204cdf0e10cSrcweir {
205cdf0e10cSrcweir 	if( maATSULayout )
206cdf0e10cSrcweir 		ATSUDisposeTextLayout( maATSULayout );
207cdf0e10cSrcweir 
208*e26449d3SHerbert Dürr 	maATSULayout = NULL;
209cdf0e10cSrcweir 
210cdf0e10cSrcweir 	// Layout text
211cdf0e10cSrcweir 	// set up our locals, verify parameters...
212*e26449d3SHerbert Dürr 	DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!");
213*e26449d3SHerbert Dürr 	DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!");
214cdf0e10cSrcweir 
215cdf0e10cSrcweir 	SalLayout::AdjustLayout( rArgs );
216cdf0e10cSrcweir 	mnCharCount = mnEndCharPos - mnMinCharPos;
217cdf0e10cSrcweir 
218cdf0e10cSrcweir 	// Workaround a bug in ATSUI with empty string
219cdf0e10cSrcweir 	if( mnCharCount<=0 )
220cdf0e10cSrcweir 		return false;
221cdf0e10cSrcweir 
222cdf0e10cSrcweir 	// create the ATSUI layout
223cdf0e10cSrcweir 	UniCharCount nRunLengths[1] = { mnCharCount };
224cdf0e10cSrcweir 	const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths);
225cdf0e10cSrcweir 	OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr,
226*e26449d3SHerbert Dürr 		rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength,
227*e26449d3SHerbert Dürr 		nRunCount, &nRunLengths[0], &mrATSUStyle, &maATSULayout);
228cdf0e10cSrcweir 
229cdf0e10cSrcweir 	DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n");
230cdf0e10cSrcweir 	if( eStatus != noErr )
231cdf0e10cSrcweir 		return false;
232cdf0e10cSrcweir 
233cdf0e10cSrcweir 	// prepare setting of layout controls
234cdf0e10cSrcweir 	static const int nMaxTagCount = 1;
235cdf0e10cSrcweir 	ATSUAttributeTag aTagAttrs[ nMaxTagCount ];
236cdf0e10cSrcweir 	ByteCount aTagSizes[ nMaxTagCount ];
237cdf0e10cSrcweir 	ATSUAttributeValuePtr aTagValues[ nMaxTagCount ];
238cdf0e10cSrcweir 
239cdf0e10cSrcweir 	// prepare control of "glyph fallback"
240cdf0e10cSrcweir 	const SalData* pSalData = GetSalData();
241cdf0e10cSrcweir 	ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks;
242cdf0e10cSrcweir 	aTagAttrs[0]  = kATSULineFontFallbacksTag;
243cdf0e10cSrcweir 	aTagSizes[0]  = sizeof( ATSUFontFallbacks );
244cdf0e10cSrcweir 	aTagValues[0] = &aFontFallbacks;
245cdf0e10cSrcweir 
246cdf0e10cSrcweir 	// set paragraph layout controls
247cdf0e10cSrcweir 	ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
248cdf0e10cSrcweir 
249cdf0e10cSrcweir 	// enable "glyph fallback"
250cdf0e10cSrcweir 	ATSUSetTransientFontMatching( maATSULayout, true );
251cdf0e10cSrcweir 
252cdf0e10cSrcweir 	// control run-specific layout controls
253cdf0e10cSrcweir 	if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 )
254cdf0e10cSrcweir 	{
255cdf0e10cSrcweir 		// control BiDi defaults
256cdf0e10cSrcweir 		BOOL nLineDirTag = kATSULeftToRightBaseDirection;
257cdf0e10cSrcweir 		if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 )
258cdf0e10cSrcweir 			nLineDirTag = kATSURightToLeftBaseDirection;
259cdf0e10cSrcweir 		aTagAttrs[0] = kATSULineDirectionTag;
260cdf0e10cSrcweir 		aTagSizes[0] = sizeof( nLineDirTag );
261cdf0e10cSrcweir 		aTagValues[0] = &nLineDirTag;
262cdf0e10cSrcweir 		// set run-specific layout controls
263*e26449d3SHerbert Dürr #if 0 // why don't line-controls work as reliable as layout-controls???
264cdf0e10cSrcweir 		ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues );
265cdf0e10cSrcweir #else
266cdf0e10cSrcweir 		ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
267cdf0e10cSrcweir #endif
268cdf0e10cSrcweir 	}
269cdf0e10cSrcweir 
270cdf0e10cSrcweir 	return true;
271cdf0e10cSrcweir }
272cdf0e10cSrcweir 
273cdf0e10cSrcweir // -----------------------------------------------------------------------
274cdf0e10cSrcweir /**
275cdf0e10cSrcweir  * ATSLayout::AdjustLayout : Adjust layout style
276cdf0e10cSrcweir  *
277cdf0e10cSrcweir  * @param rArgs: contains attributes relevant to do a text specific layout
278cdf0e10cSrcweir  *
279cdf0e10cSrcweir  * Adjust text layout by moving glyphs to match the requested logical widths
280cdf0e10cSrcweir  *
281cdf0e10cSrcweir  * @return : none
282cdf0e10cSrcweir **/
AdjustLayout(ImplLayoutArgs & rArgs)283cdf0e10cSrcweir void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs )
284cdf0e10cSrcweir {
285cdf0e10cSrcweir 	int nOrigWidth = GetTextWidth();
286cdf0e10cSrcweir 	int nPixelWidth = rArgs.mnLayoutWidth;
287cdf0e10cSrcweir 	if( !nPixelWidth && rArgs.mpDXArray ) {
288cdf0e10cSrcweir 		// for now we are only interested in the layout width
289cdf0e10cSrcweir 		// TODO: use all mpDXArray elements for layouting
290cdf0e10cSrcweir 		nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ];
291cdf0e10cSrcweir 
292cdf0e10cSrcweir 		// workaround for ATSUI not using trailing spaces for justification
293cdf0e10cSrcweir 		int i = mnCharCount;
294cdf0e10cSrcweir 		while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {}
295cdf0e10cSrcweir 		if( i < 0 ) // nothing to do if the text is all spaces
296cdf0e10cSrcweir 			return;
297cdf0e10cSrcweir 		// #i91685# trailing letters are left aligned (right aligned for RTL)
298cdf0e10cSrcweir 		mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ];
299cdf0e10cSrcweir 		if( i > 0 )
300cdf0e10cSrcweir 			mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ];
301*e26449d3SHerbert Dürr 		InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead?
302cdf0e10cSrcweir 		mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] );
303cdf0e10cSrcweir 		// ignore trailing space for calculating the available width
304cdf0e10cSrcweir 		nOrigWidth -= mnTrailingSpaceWidth;
305cdf0e10cSrcweir 		nPixelWidth -= mnTrailingSpaceWidth;
306cdf0e10cSrcweir 		// in RTL-layouts trailing spaces are leftmost
307cdf0e10cSrcweir 		// TODO: use BiDi-algorithm to thoroughly check this assumption
308cdf0e10cSrcweir 		if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
309cdf0e10cSrcweir 			mnBaseAdv = mnTrailingSpaceWidth;
310cdf0e10cSrcweir 	}
311cdf0e10cSrcweir 	// return early if there is nothing to do
312cdf0e10cSrcweir 	if( !nPixelWidth )
313cdf0e10cSrcweir 		return;
314cdf0e10cSrcweir 
315cdf0e10cSrcweir 	// HACK: justification requests which change the width by just one pixel were probably
316cdf0e10cSrcweir 	// #i86038# introduced by lossy conversions between integer based coordinate system
317cdf0e10cSrcweir 	// => ignoring such requests has many more benefits than eventual drawbacks
318cdf0e10cSrcweir 	if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
319cdf0e10cSrcweir 		return;
320cdf0e10cSrcweir 
321cdf0e10cSrcweir 	// changing the layout will make all previous measurements invalid
322cdf0e10cSrcweir 	InvalidateMeasurements();
323cdf0e10cSrcweir 
324cdf0e10cSrcweir 	ATSUAttributeTag nTags[3];
325cdf0e10cSrcweir 	ATSUAttributeValuePtr nVals[3];
326cdf0e10cSrcweir 	ByteCount nBytes[3];
327cdf0e10cSrcweir 
328cdf0e10cSrcweir 	Fixed nFixedWidth = Vcl2Fixed( nPixelWidth );
329cdf0e10cSrcweir 	mnCachedWidth = nFixedWidth;
330cdf0e10cSrcweir 	Fract nFractFactor = kATSUFullJustification;
331cdf0e10cSrcweir 	ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter;
332cdf0e10cSrcweir 
333cdf0e10cSrcweir 	nTags[0] = kATSULineWidthTag;
334cdf0e10cSrcweir 	nVals[0] = &nFixedWidth;
335cdf0e10cSrcweir 	nBytes[0] = sizeof(Fixed);
336cdf0e10cSrcweir 	nTags[1] = kATSULineLayoutOptionsTag;
337cdf0e10cSrcweir 	nVals[1] = &nLineLayoutOptions;
338cdf0e10cSrcweir 	nBytes[1] = sizeof(ATSLineLayoutOptions);
339cdf0e10cSrcweir 	nTags[2] = kATSULineJustificationFactorTag;
340cdf0e10cSrcweir 	nVals[2] = &nFractFactor;
341cdf0e10cSrcweir 	nBytes[2] = sizeof(Fract);
342cdf0e10cSrcweir 
343cdf0e10cSrcweir 	OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals );
344cdf0e10cSrcweir 	if( eStatus != noErr )
345cdf0e10cSrcweir 		return;
346cdf0e10cSrcweir 
347cdf0e10cSrcweir 	// update the measurements of the justified layout to match the justification request
348cdf0e10cSrcweir 	if( rArgs.mpDXArray )
349cdf0e10cSrcweir 		InitGIA( &rArgs );
350cdf0e10cSrcweir }
351cdf0e10cSrcweir 
352cdf0e10cSrcweir // -----------------------------------------------------------------------
353cdf0e10cSrcweir /**
354cdf0e10cSrcweir  * ATSLayout::DrawText : Draw text to screen
355cdf0e10cSrcweir  *
356cdf0e10cSrcweir  * @param rGraphics: device to draw to
357cdf0e10cSrcweir  *
358cdf0e10cSrcweir  * Draw the layouted text to the CGContext
359cdf0e10cSrcweir  *
360cdf0e10cSrcweir  * @return : none
361cdf0e10cSrcweir **/
DrawText(SalGraphics & rGraphics) const362cdf0e10cSrcweir void ATSLayout::DrawText( SalGraphics& rGraphics ) const
363cdf0e10cSrcweir {
364cdf0e10cSrcweir 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
365cdf0e10cSrcweir 
366cdf0e10cSrcweir 	// short circuit if there is nothing to do
367cdf0e10cSrcweir 	if( (mnCharCount <= 0)
368cdf0e10cSrcweir 	||  !rAquaGraphics.CheckContext() )
369cdf0e10cSrcweir 		return;
370cdf0e10cSrcweir 
371cdf0e10cSrcweir 	// the view is vertically flipped => flipped glyphs
372*e26449d3SHerbert Dürr 	// so apply a temporary transformation that it flips back
373cdf0e10cSrcweir 	// also compensate if the font was size limited
374*e26449d3SHerbert Dürr 	CGContextSaveGState( rAquaGraphics.mrContext );
375*e26449d3SHerbert Dürr 	CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
376*e26449d3SHerbert Dürr 	CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
377cdf0e10cSrcweir 
378cdf0e10cSrcweir 	// prepare ATSUI drawing attributes
379cdf0e10cSrcweir 	static const ItemCount nMaxControls = 8;
380cdf0e10cSrcweir 	ATSUAttributeTag theTags[ nMaxControls ];
381cdf0e10cSrcweir 	ByteCount theSizes[ nMaxControls];
382cdf0e10cSrcweir 	ATSUAttributeValuePtr theValues[ nMaxControls ];
383cdf0e10cSrcweir 	ItemCount numcontrols = 0;
384cdf0e10cSrcweir 
385cdf0e10cSrcweir 	// Tell ATSUI to use CoreGraphics
386*e26449d3SHerbert Dürr 	theTags[numcontrols] = kATSUCGContextTag;
387*e26449d3SHerbert Dürr 	theSizes[numcontrols] = sizeof( CGContextRef );
388*e26449d3SHerbert Dürr 	theValues[numcontrols++] = &rAquaGraphics.mrContext;
389cdf0e10cSrcweir 
390cdf0e10cSrcweir 	// Rotate if necessary
391cdf0e10cSrcweir 	if( rAquaGraphics.mnATSUIRotation != 0 )
392cdf0e10cSrcweir 	{
393cdf0e10cSrcweir 		Fixed theAngle = rAquaGraphics.mnATSUIRotation;
394cdf0e10cSrcweir 		theTags[numcontrols] = kATSULineRotationTag;
395cdf0e10cSrcweir 		theSizes[numcontrols] = sizeof( Fixed );
396cdf0e10cSrcweir 		theValues[numcontrols++] = &theAngle;
397cdf0e10cSrcweir 	}
398cdf0e10cSrcweir 
399cdf0e10cSrcweir 	DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" );
400cdf0e10cSrcweir 	OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues);
401cdf0e10cSrcweir 	DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" );
402cdf0e10cSrcweir 
403cdf0e10cSrcweir 	// Draw the text
404cdf0e10cSrcweir 	const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) );
405cdf0e10cSrcweir 	const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
406cdf0e10cSrcweir 	const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring
407cdf0e10cSrcweir 	if( maSubPortions.empty() )
408cdf0e10cSrcweir 		ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY );
409cdf0e10cSrcweir 	else
410cdf0e10cSrcweir 	{
411cdf0e10cSrcweir 		// draw the sub-portions and apply individual adjustments
412cdf0e10cSrcweir 		SubPortionVector::const_iterator it = maSubPortions.begin();
413cdf0e10cSrcweir 		for(; it != maSubPortions.end(); ++it )
414cdf0e10cSrcweir 		{
415cdf0e10cSrcweir 			const SubPortion& rSubPortion = *it;
416cdf0e10cSrcweir 			// calculate sub-portion offset for rotated text
417cdf0e10cSrcweir 			Fixed nXOfsFixed = 0, nYOfsFixed = 0;
418cdf0e10cSrcweir 			if( rAquaGraphics.mnATSUIRotation != 0 )
419cdf0e10cSrcweir 			{
420cdf0e10cSrcweir 				const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000);
421cdf0e10cSrcweir 				nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians ));
422cdf0e10cSrcweir 				nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians ));
423cdf0e10cSrcweir 			}
424cdf0e10cSrcweir 
425cdf0e10cSrcweir 			// draw sub-portions
426cdf0e10cSrcweir 			ATSUDrawText( maATSULayout,
427cdf0e10cSrcweir 				rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos,
428cdf0e10cSrcweir 				nFixedX + nXOfsFixed, nFixedY + nYOfsFixed );
429cdf0e10cSrcweir 		}
430cdf0e10cSrcweir 	}
431cdf0e10cSrcweir 
432cdf0e10cSrcweir 	// request an update of the changed window area
433cdf0e10cSrcweir 	if( rAquaGraphics.IsWindowGraphics() )
434cdf0e10cSrcweir 	{
435cdf0e10cSrcweir 		Rect drawRect; // rectangle of the changed area
436cdf0e10cSrcweir 		theErr = ATSUMeasureTextImage( maATSULayout,
437cdf0e10cSrcweir 			mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect );
438cdf0e10cSrcweir 		if( theErr == noErr )
439*e26449d3SHerbert Dürr 		{
440*e26449d3SHerbert Dürr 			// FIXME: transformation from baseline to top left
441*e26449d3SHerbert Dürr 			// with the simple approach below we invalidate too much
442*e26449d3SHerbert Dürr 			short d = drawRect.bottom - drawRect.top;
443*e26449d3SHerbert Dürr 			drawRect.top -= d;
444*e26449d3SHerbert Dürr 			drawRect.bottom += d;
445*e26449d3SHerbert Dürr 			CGRect aRect = CGRectMake( drawRect.left, drawRect.top,
446*e26449d3SHerbert Dürr 					drawRect.right - drawRect.left,
447*e26449d3SHerbert Dürr 					drawRect.bottom - drawRect.top );
448*e26449d3SHerbert Dürr 			aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect );
449cdf0e10cSrcweir 			rAquaGraphics.RefreshRect( aRect );
450*e26449d3SHerbert Dürr 		}
451cdf0e10cSrcweir 	}
452cdf0e10cSrcweir 
453*e26449d3SHerbert Dürr 	// restore the original graphic context transformations
454cdf0e10cSrcweir 	CGContextRestoreGState( rAquaGraphics.mrContext );
455cdf0e10cSrcweir }
456cdf0e10cSrcweir 
457cdf0e10cSrcweir // -----------------------------------------------------------------------
458cdf0e10cSrcweir /**
459cdf0e10cSrcweir  * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout
460cdf0e10cSrcweir  *
461cdf0e10cSrcweir  * @param nLen: max number of char
462cdf0e10cSrcweir  * @param pGlyphs: returned array of glyph ids
463cdf0e10cSrcweir  * @param rPos: returned x starting position
464cdf0e10cSrcweir  * @param nStart: index of the first requested glyph
465cdf0e10cSrcweir  * @param pGlyphAdvances: returned array of glyphs advances
466cdf0e10cSrcweir  * @param pCharIndexes: returned array of char indexes
467cdf0e10cSrcweir  *
468cdf0e10cSrcweir  * Returns infos about the next glyphs in the text layout
469cdf0e10cSrcweir  *
470cdf0e10cSrcweir  * @return : number of glyph details that were provided
471cdf0e10cSrcweir **/
GetNextGlyphs(int nLen,sal_GlyphId * pOutGlyphIds,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvances,int * pCharIndexes) const472248a599fSHerbert Dürr int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
473*e26449d3SHerbert Dürr 	sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
474cdf0e10cSrcweir {
475cdf0e10cSrcweir 	if( nStart < 0 )                // first glyph requested?
476cdf0e10cSrcweir 		nStart = 0;
477cdf0e10cSrcweir 
478cdf0e10cSrcweir 	// get glyph measurements
479cdf0e10cSrcweir 	InitGIA();
480cdf0e10cSrcweir 	// some measurements are only needed for multi-glyph results
481cdf0e10cSrcweir 	if( nLen > 1 )
482cdf0e10cSrcweir 	{
483cdf0e10cSrcweir 		GetIdealX();
484cdf0e10cSrcweir 		GetDeltaY();
485cdf0e10cSrcweir 	}
486cdf0e10cSrcweir 
487cdf0e10cSrcweir 	if( nStart >= mnGlyphCount )    // no glyph left?
488cdf0e10cSrcweir 		return 0;
489cdf0e10cSrcweir 
490cdf0e10cSrcweir 	// calculate glyph position relative to layout base
491*e26449d3SHerbert Dürr 	// TODO: avoid for nStart!=0 case by reusing rPos
492*e26449d3SHerbert Dürr 	Fixed nXOffset = mnBaseAdv;
493*e26449d3SHerbert Dürr 	for( int i = 0; i < nStart; ++i )
494*e26449d3SHerbert Dürr 		nXOffset += mpGlyphAdvances[ i ];
495*e26449d3SHerbert Dürr 	// if sub-portion offsets are involved there is an additional x-offset
496*e26449d3SHerbert Dürr 	if( !maSubPortions.empty() )
497*e26449d3SHerbert Dürr 	{
498*e26449d3SHerbert Dürr 		// prepare to find the sub-portion
499*e26449d3SHerbert Dürr 		int nCharPos = nStart + mnMinCharPos;
500*e26449d3SHerbert Dürr 		if( mpGlyphs2Chars )
501*e26449d3SHerbert Dürr 			nCharPos = mpGlyphs2Chars[nStart];
502*e26449d3SHerbert Dürr 
503*e26449d3SHerbert Dürr 		// find the matching subportion
504*e26449d3SHerbert Dürr 		// TODO: is a non-linear search worth it?
505*e26449d3SHerbert Dürr 		SubPortionVector::const_iterator it = maSubPortions.begin();
506*e26449d3SHerbert Dürr 		for(; it != maSubPortions.end(); ++it) {
507*e26449d3SHerbert Dürr 			const SubPortion& r = *it;
508*e26449d3SHerbert Dürr 			if( nCharPos < r.mnMinCharPos )
509*e26449d3SHerbert Dürr 				continue;
510*e26449d3SHerbert Dürr 			if( nCharPos >= r.mnEndCharPos )
511*e26449d3SHerbert Dürr 				continue;
512*e26449d3SHerbert Dürr 			// apply the sub-portion xoffset
513*e26449d3SHerbert Dürr 			nXOffset += r.mnXOffset;
514*e26449d3SHerbert Dürr 			break;
515*e26449d3SHerbert Dürr 		}
516*e26449d3SHerbert Dürr 	}
517cdf0e10cSrcweir 
518cdf0e10cSrcweir 	Fixed nYOffset = 0;
519cdf0e10cSrcweir 	if( mpDeltaY )
520cdf0e10cSrcweir 		nYOffset = mpDeltaY[ nStart ];
521cdf0e10cSrcweir 
522*e26449d3SHerbert Dürr 	// calculate absolute position in pixel units
523*e26449d3SHerbert Dürr 	const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) );
524*e26449d3SHerbert Dürr 	rPos = GetDrawPosition( aRelativePos );
525cdf0e10cSrcweir 
526cdf0e10cSrcweir 	// update return values
527cdf0e10cSrcweir 	int nCount = 0;
528*e26449d3SHerbert Dürr 	while( nCount < nLen )
529*e26449d3SHerbert Dürr 	{
530*e26449d3SHerbert Dürr 		++nCount;
531*e26449d3SHerbert Dürr 		sal_GlyphId aGlyphId = mpGlyphIds[nStart];
532*e26449d3SHerbert Dürr 
533*e26449d3SHerbert Dürr 		// check if glyph fallback is needed for this glyph
534*e26449d3SHerbert Dürr 		// TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead?
535*e26449d3SHerbert Dürr 		const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos;
536*e26449d3SHerbert Dürr 		ATSUFontID nFallbackFontID = kATSUInvalidFontID;
537*e26449d3SHerbert Dürr 		UniCharArrayOffset nChangedOffset = 0;
538*e26449d3SHerbert Dürr 		UniCharCount nChangedLength = 0;
539*e26449d3SHerbert Dürr 		OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd,
540*e26449d3SHerbert Dürr 			&nFallbackFontID, &nChangedOffset, &nChangedLength );
541*e26449d3SHerbert Dürr 		if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) )
542*e26449d3SHerbert Dürr 		{
543*e26449d3SHerbert Dürr 			// fallback is needed
544*e26449d3SHerbert Dürr 			if( !mpFallbackInfo )
545*e26449d3SHerbert Dürr 				mpFallbackInfo = new FallbackInfo;
546*e26449d3SHerbert Dürr 			// register fallback font
547*e26449d3SHerbert Dürr 			const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID );
548*e26449d3SHerbert Dürr 			// update sal_GlyphId with fallback level
549*e26449d3SHerbert Dürr 			aGlyphId |= (nLevel << GF_FONTSHIFT);
550*e26449d3SHerbert Dürr 		}
551*e26449d3SHerbert Dürr 
552*e26449d3SHerbert Dürr 		// update resulting glyphid array
553*e26449d3SHerbert Dürr 		*(pOutGlyphIds++) = aGlyphId;
554*e26449d3SHerbert Dürr 
555*e26449d3SHerbert Dürr 		// update returned glyph advance array
556*e26449d3SHerbert Dürr 		if( pGlyphAdvances )
557*e26449d3SHerbert Dürr 			*(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] );
558*e26449d3SHerbert Dürr 
559*e26449d3SHerbert Dürr 		// update returned index-into-string array
560*e26449d3SHerbert Dürr 		if( pCharIndexes )
561*e26449d3SHerbert Dürr 		{
562*e26449d3SHerbert Dürr 			int nCharPos;
563*e26449d3SHerbert Dürr 			if( mpGlyphs2Chars )
564*e26449d3SHerbert Dürr 				nCharPos = mpGlyphs2Chars[nStart];
565*e26449d3SHerbert Dürr 			else
566*e26449d3SHerbert Dürr 				nCharPos = nStart + mnMinCharPos;
567*e26449d3SHerbert Dürr 			*(pCharIndexes++) = nCharPos;
568*e26449d3SHerbert Dürr 		}
569*e26449d3SHerbert Dürr 
570*e26449d3SHerbert Dürr 		// stop at last glyph
571*e26449d3SHerbert Dürr 		if( ++nStart >= mnGlyphCount )
572*e26449d3SHerbert Dürr 			break;
573*e26449d3SHerbert Dürr 
574*e26449d3SHerbert Dürr 		// stop when next the x-position is unexpected
575*e26449d3SHerbert Dürr 		if( !maSubPortions.empty() )
576*e26449d3SHerbert Dürr 			break;	 // TODO: finish the complete sub-portion
577*e26449d3SHerbert Dürr 		if( !pGlyphAdvances && mpGlyphOrigAdvs )
578*e26449d3SHerbert Dürr 			if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
579*e26449d3SHerbert Dürr 				break;
580*e26449d3SHerbert Dürr 
581*e26449d3SHerbert Dürr 		// stop when the next y-position is unexpected
582*e26449d3SHerbert Dürr 		if( mpDeltaY )
583*e26449d3SHerbert Dürr 			if( mpDeltaY[nStart-1] != mpDeltaY[nStart] )
584*e26449d3SHerbert Dürr 				break;
585*e26449d3SHerbert Dürr 	}
586*e26449d3SHerbert Dürr 
587*e26449d3SHerbert Dürr 	return nCount;
588cdf0e10cSrcweir }
589cdf0e10cSrcweir 
590cdf0e10cSrcweir // -----------------------------------------------------------------------
591cdf0e10cSrcweir /**
592cdf0e10cSrcweir  * ATSLayout::GetTextWidth : Get typographic width of layouted text
593cdf0e10cSrcweir  *
594cdf0e10cSrcweir  * Get typographic bounds of the text
595cdf0e10cSrcweir  *
596cdf0e10cSrcweir  * @return : text width
597cdf0e10cSrcweir **/
GetTextWidth() const598cdf0e10cSrcweir long ATSLayout::GetTextWidth() const
599cdf0e10cSrcweir {
600cdf0e10cSrcweir 	if( mnCharCount <= 0 )
601cdf0e10cSrcweir 		return 0;
602cdf0e10cSrcweir 
603cdf0e10cSrcweir 	DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n");
604cdf0e10cSrcweir 	if( !maATSULayout )
605cdf0e10cSrcweir 		return 0;
606cdf0e10cSrcweir 
607cdf0e10cSrcweir 	if( !mnCachedWidth )
608cdf0e10cSrcweir 	{
609cdf0e10cSrcweir 		// prepare precise measurements on pixel based or reference-device
610cdf0e10cSrcweir 		const UInt16 eTypeOfBounds = kATSUseFractionalOrigins;
611cdf0e10cSrcweir 
612cdf0e10cSrcweir 		// determine number of needed measurement trapezoids
613cdf0e10cSrcweir 		ItemCount nMaxBounds = 0;
614cdf0e10cSrcweir 		OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
615cdf0e10cSrcweir 			eTypeOfBounds, 0, NULL, &nMaxBounds );
616cdf0e10cSrcweir 		if( (err != noErr)
617cdf0e10cSrcweir 		||  (nMaxBounds <= 0) )
618cdf0e10cSrcweir 			return 0;
619cdf0e10cSrcweir 
620cdf0e10cSrcweir 		// get the trapezoids
621cdf0e10cSrcweir 		typedef std::vector<ATSTrapezoid> TrapezoidVector;
622cdf0e10cSrcweir 		TrapezoidVector aTrapezoidVector( nMaxBounds );
623cdf0e10cSrcweir 		ItemCount nBoundsCount = 0;
624cdf0e10cSrcweir 		err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
625cdf0e10cSrcweir 			eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount );
626cdf0e10cSrcweir 		if( err != noErr )
627cdf0e10cSrcweir 			return 0;
628cdf0e10cSrcweir 
629cdf0e10cSrcweir 		DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n");
630cdf0e10cSrcweir 
631cdf0e10cSrcweir 		// find the bound extremas
632cdf0e10cSrcweir 		Fixed nLeftBound = 0;
633cdf0e10cSrcweir 		Fixed nRightBound = 0;
634cdf0e10cSrcweir 		for( ItemCount i = 0; i < nBoundsCount; ++i )
635cdf0e10cSrcweir 		{
636cdf0e10cSrcweir 			const ATSTrapezoid& rTrap = aTrapezoidVector[i];
637cdf0e10cSrcweir 			if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) )
638cdf0e10cSrcweir 				nLeftBound = rTrap.lowerLeft.x;
639cdf0e10cSrcweir 			if( (i == 0) || (nRightBound > rTrap.lowerRight.x) )
640cdf0e10cSrcweir 				nRightBound = rTrap.lowerRight.x;
641cdf0e10cSrcweir 		}
642cdf0e10cSrcweir 
643cdf0e10cSrcweir 		// measure the bound extremas
644cdf0e10cSrcweir 		mnCachedWidth = nRightBound - nLeftBound;
645cdf0e10cSrcweir 		// adjust for eliminated trailing space widths
646cdf0e10cSrcweir 	}
647cdf0e10cSrcweir 
648*e26449d3SHerbert Dürr 	int nScaledWidth = Fixed2Vcl( mnCachedWidth );
649cdf0e10cSrcweir 	nScaledWidth += mnTrailingSpaceWidth;
650*e26449d3SHerbert Dürr 	return nScaledWidth;
651cdf0e10cSrcweir }
652cdf0e10cSrcweir 
653cdf0e10cSrcweir // -----------------------------------------------------------------------
654cdf0e10cSrcweir /**
655cdf0e10cSrcweir  * ATSLayout::FillDXArray : Get Char widths
656cdf0e10cSrcweir  *
657cdf0e10cSrcweir  * @param pDXArray: array to be filled with x-advances
658cdf0e10cSrcweir  *
659cdf0e10cSrcweir  * Fill the pDXArray with horizontal deltas : CharWidths
660cdf0e10cSrcweir  *
661cdf0e10cSrcweir  * @return : typographical width of the complete text layout
662cdf0e10cSrcweir **/
FillDXArray(long * pDXArray) const663cdf0e10cSrcweir long ATSLayout::FillDXArray( long* pDXArray ) const
664cdf0e10cSrcweir {
665*e26449d3SHerbert Dürr 	// short circuit requests which don't need full details
666*e26449d3SHerbert Dürr 	if( !pDXArray )
667*e26449d3SHerbert Dürr 		return GetTextWidth();
668cdf0e10cSrcweir 
669cdf0e10cSrcweir 	// check assumptions
670cdf0e10cSrcweir 	DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" );
671cdf0e10cSrcweir 
672cdf0e10cSrcweir 	// initialize details about the resulting layout
673*e26449d3SHerbert Dürr 	InitGIA();
674*e26449d3SHerbert Dürr 
675*e26449d3SHerbert Dürr 	// distribute the widths among the string elements
676*e26449d3SHerbert Dürr 	int nPixWidth = 0;
677*e26449d3SHerbert Dürr 	mnCachedWidth = 0;
678*e26449d3SHerbert Dürr 	for( int i = 0; i < mnCharCount; ++i )
679*e26449d3SHerbert Dürr 	{
680*e26449d3SHerbert Dürr 		// convert and adjust for accumulated rounding errors
681*e26449d3SHerbert Dürr 		mnCachedWidth += mpCharWidths[i];
682*e26449d3SHerbert Dürr 		const int nOldPixWidth = nPixWidth;
683*e26449d3SHerbert Dürr 		nPixWidth = Fixed2Vcl( mnCachedWidth );
684*e26449d3SHerbert Dürr 		pDXArray[i] = nPixWidth - nOldPixWidth;
685*e26449d3SHerbert Dürr 	}
686*e26449d3SHerbert Dürr 
687*e26449d3SHerbert Dürr 	return nPixWidth;
688cdf0e10cSrcweir }
689cdf0e10cSrcweir 
690cdf0e10cSrcweir // -----------------------------------------------------------------------
691cdf0e10cSrcweir /**
692cdf0e10cSrcweir  * ATSLayout::GetTextBreak : Find line break depending on width
693cdf0e10cSrcweir  *
694cdf0e10cSrcweir  * @param nMaxWidth : maximal logical text width in subpixel units
695cdf0e10cSrcweir  * @param nCharExtra: expanded/condensed spacing in subpixel units
696cdf0e10cSrcweir  * @param nFactor:    number of subpixel units per pixel
697cdf0e10cSrcweir  *
698cdf0e10cSrcweir  * Measure the layouted text to find the typographical line break
699cdf0e10cSrcweir  * the result is needed by the language specific line breaking
700cdf0e10cSrcweir  *
701cdf0e10cSrcweir  * @return : string index corresponding to the suggested line break
702cdf0e10cSrcweir **/
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const703cdf0e10cSrcweir int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
704cdf0e10cSrcweir {
705cdf0e10cSrcweir 	if( !maATSULayout )
706cdf0e10cSrcweir 		return STRING_LEN;
707cdf0e10cSrcweir 
708cdf0e10cSrcweir 	// the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine()
709cdf0e10cSrcweir 	if( nCharExtra != 0 )
710cdf0e10cSrcweir 	{
711cdf0e10cSrcweir 		// prepare the measurement by layouting and measuring the un-expanded/un-condensed text
712cdf0e10cSrcweir 		if( !InitGIA() )
713cdf0e10cSrcweir 			return STRING_LEN;
714cdf0e10cSrcweir 
715cdf0e10cSrcweir 		// TODO: use a better way than by testing each the char position
716cdf0e10cSrcweir 		ATSUTextMeasurement nATSUSumWidth = 0;
717cdf0e10cSrcweir 		const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor );
718cdf0e10cSrcweir 		const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor;
719cdf0e10cSrcweir 		for( int i = 0; i < mnCharCount; ++i )
720cdf0e10cSrcweir 		{
721cdf0e10cSrcweir 			nATSUSumWidth += mpCharWidths[i];
722cdf0e10cSrcweir 			if( nATSUSumWidth >= nATSUMaxWidth )
723cdf0e10cSrcweir 				return (mnMinCharPos + i);
724cdf0e10cSrcweir 			nATSUSumWidth += nATSUExtraWidth;
725cdf0e10cSrcweir 			if( nATSUSumWidth >= nATSUMaxWidth )
726cdf0e10cSrcweir 				if( i+1 < mnCharCount )
727cdf0e10cSrcweir 					return (mnMinCharPos + i);
728cdf0e10cSrcweir 		}
729cdf0e10cSrcweir 
730cdf0e10cSrcweir 		return STRING_LEN;
731cdf0e10cSrcweir 	}
732cdf0e10cSrcweir 
733cdf0e10cSrcweir 	// get a quick overview on what could fit
734*e26449d3SHerbert Dürr 	const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
735*e26449d3SHerbert Dürr 	if( nPixelWidth <= 0 )
736*e26449d3SHerbert Dürr 		return mnMinCharPos;
737cdf0e10cSrcweir 
738cdf0e10cSrcweir 	// check assumptions
739cdf0e10cSrcweir 	DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" );
740cdf0e10cSrcweir 
741cdf0e10cSrcweir 	// initial measurement of text break position
742cdf0e10cSrcweir 	UniCharArrayOffset nBreakPos = mnMinCharPos;
743cdf0e10cSrcweir 	const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth );
744cdf0e10cSrcweir 	if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
745cdf0e10cSrcweir 		return mnMinCharPos;      //           or do ATSUMaxWidth=0x10000;
746cdf0e10cSrcweir 	OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos,
747cdf0e10cSrcweir 		nATSUMaxWidth, false, &nBreakPos );
748cdf0e10cSrcweir 	if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) )
749cdf0e10cSrcweir 		return STRING_LEN;
750cdf0e10cSrcweir 
751cdf0e10cSrcweir 	// the result from ATSUBreakLine() doesn't match the semantics expected by its
752cdf0e10cSrcweir 	// application layer callers from SW+SVX+I18N. Adjust the results to the expectations:
753cdf0e10cSrcweir 
754cdf0e10cSrcweir 	// ATSU reports that everything fits even when trailing spaces would break the line
755cdf0e10cSrcweir 	// #i89789# OOo's application layers expect STRING_LEN if everything fits
756cdf0e10cSrcweir 	if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) )
757cdf0e10cSrcweir 		return STRING_LEN;
758cdf0e10cSrcweir 
759cdf0e10cSrcweir 	// GetTextBreak()'s callers expect it to return the "stupid visual line break".
760cdf0e10cSrcweir 	// Returning anything else result.s in subtle problems in the application layers.
761cdf0e10cSrcweir 	static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method
762cdf0e10cSrcweir 	if( !bInWord )
763cdf0e10cSrcweir 		return nBreakPos;
764cdf0e10cSrcweir 
765cdf0e10cSrcweir 	// emulate stupid visual line breaking by line breaking for the remaining width
766cdf0e10cSrcweir 	ATSUTextMeasurement nLeft, nRight, nDummy;
767cdf0e10cSrcweir 	eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos,
768cdf0e10cSrcweir 		&nLeft, &nRight, &nDummy, &nDummy );
769cdf0e10cSrcweir 	if( eStatus != noErr )
770cdf0e10cSrcweir 		return nBreakPos;
771cdf0e10cSrcweir 	const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft);
772cdf0e10cSrcweir 	if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
773cdf0e10cSrcweir 		return nBreakPos;
774cdf0e10cSrcweir 	UniCharArrayOffset nBreakPosInWord = nBreakPos;
775cdf0e10cSrcweir 	eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord );
776cdf0e10cSrcweir 	return nBreakPosInWord;
777cdf0e10cSrcweir }
778cdf0e10cSrcweir 
779cdf0e10cSrcweir // -----------------------------------------------------------------------
780cdf0e10cSrcweir /**
781cdf0e10cSrcweir  * ATSLayout::GetCaretPositions : Find positions of carets
782cdf0e10cSrcweir  *
783cdf0e10cSrcweir  * @param nMaxIndex position to which we want to find the carets
784cdf0e10cSrcweir  *
785cdf0e10cSrcweir  * Fill the array of positions of carets (for cursors and selections)
786cdf0e10cSrcweir  *
787cdf0e10cSrcweir  * @return : none
788cdf0e10cSrcweir **/
GetCaretPositions(int nMaxIndex,long * pCaretXArray) const789cdf0e10cSrcweir void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
790cdf0e10cSrcweir {
791cdf0e10cSrcweir 	DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
792cdf0e10cSrcweir 		"ATSLayout::GetCaretPositions() : invalid number of caret pairs requested");
793cdf0e10cSrcweir 
794cdf0e10cSrcweir 	// initialize the caret positions
795*e26449d3SHerbert Dürr 	for( int i = 0; i < nMaxIndex; ++i )
796*e26449d3SHerbert Dürr 		pCaretXArray[ i ] = -1;
797cdf0e10cSrcweir 
798cdf0e10cSrcweir 	for( int n = 0; n <= mnCharCount; ++n )
799cdf0e10cSrcweir 	{
800cdf0e10cSrcweir 		// measure the characters cursor position
801cdf0e10cSrcweir 		typedef unsigned char Boolean;
802cdf0e10cSrcweir 		const Boolean bIsLeading = true;
803cdf0e10cSrcweir 		ATSUCaret aCaret0, aCaret1;
804cdf0e10cSrcweir 		Boolean bIsSplit;
805cdf0e10cSrcweir 		OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout,
806cdf0e10cSrcweir 			mnMinCharPos + n, bIsLeading, kATSUByCharacter,
807cdf0e10cSrcweir 			&aCaret0, &aCaret1, &bIsSplit );
808cdf0e10cSrcweir 		if( eStatus != noErr )
809cdf0e10cSrcweir 			continue;
810cdf0e10cSrcweir 		const Fixed nFixedPos = mnBaseAdv + aCaret0.fX;
811cdf0e10cSrcweir 		// convert the measurement to pixel units
812cdf0e10cSrcweir 		const int nPixelPos = Fixed2Vcl( nFixedPos );
813cdf0e10cSrcweir 		// update previous trailing position
814cdf0e10cSrcweir 		if( n > 0 )
815cdf0e10cSrcweir 			pCaretXArray[2*n-1] = nPixelPos;
816cdf0e10cSrcweir 		// update current leading position
817cdf0e10cSrcweir 		if( 2*n >= nMaxIndex )
818cdf0e10cSrcweir 			break;
819cdf0e10cSrcweir 		pCaretXArray[2*n+0] = nPixelPos;
820cdf0e10cSrcweir 	}
821cdf0e10cSrcweir }
822cdf0e10cSrcweir 
823cdf0e10cSrcweir // -----------------------------------------------------------------------
824cdf0e10cSrcweir /**
825cdf0e10cSrcweir  * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text
826cdf0e10cSrcweir  *
827cdf0e10cSrcweir  * @param rVCLRect: rectangle of text image (layout) measures
828cdf0e10cSrcweir  *
829cdf0e10cSrcweir  * Get ink bounds of the text
830cdf0e10cSrcweir  *
831cdf0e10cSrcweir  * @return : measurement valid
832cdf0e10cSrcweir **/
GetBoundRect(SalGraphics &,Rectangle & rVCLRect) const833cdf0e10cSrcweir bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const
834cdf0e10cSrcweir {
835cdf0e10cSrcweir 	const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
836cdf0e10cSrcweir 	const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
837cdf0e10cSrcweir 	const Fixed nFixedY = Vcl2Fixed( +aPos.Y() );
838cdf0e10cSrcweir 
839cdf0e10cSrcweir 	Rect aMacRect;
840cdf0e10cSrcweir 	OSStatus eStatus = ATSUMeasureTextImage( maATSULayout,
841cdf0e10cSrcweir 		mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect );
842cdf0e10cSrcweir 	if( eStatus != noErr )
843cdf0e10cSrcweir 	    return false;
844cdf0e10cSrcweir 
845cdf0e10cSrcweir 	// ATSU top-bottom are vertically flipped from a VCL aspect
846cdf0e10cSrcweir 	rVCLRect.Left()   = AtsuPix2Vcl( aMacRect.left );
847cdf0e10cSrcweir 	rVCLRect.Top()    = AtsuPix2Vcl( aMacRect.top );
848cdf0e10cSrcweir 	rVCLRect.Right()  = AtsuPix2Vcl( aMacRect.right );
849cdf0e10cSrcweir 	rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom );
850cdf0e10cSrcweir 	return true;
851cdf0e10cSrcweir }
852cdf0e10cSrcweir 
853cdf0e10cSrcweir // -----------------------------------------------------------------------
854cdf0e10cSrcweir /**
855cdf0e10cSrcweir  * ATSLayout::InitGIA() : get many informations about layouted text
856cdf0e10cSrcweir  *
857cdf0e10cSrcweir  * Fills arrays of information about the gylph layout previously done
858cdf0e10cSrcweir  *	in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline),
859cdf0e10cSrcweir  *  mapping between glyph index and character index, chars widths
860cdf0e10cSrcweir  *
861cdf0e10cSrcweir  * @return : true if everything could be computed, otherwise false
862cdf0e10cSrcweir **/
InitGIA(ImplLayoutArgs * pArgs) const863cdf0e10cSrcweir bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const
864cdf0e10cSrcweir {
865cdf0e10cSrcweir 	// no need to run InitGIA more than once on the same ATSLayout object
866cdf0e10cSrcweir 	if( mnGlyphCount >= 0 )
867cdf0e10cSrcweir 		return true;
868cdf0e10cSrcweir 	mnGlyphCount = 0;
869cdf0e10cSrcweir 
870cdf0e10cSrcweir 	// Workaround a bug in ATSUI with empty string
871cdf0e10cSrcweir 	if( mnCharCount <=  0 )
872cdf0e10cSrcweir 		return false;
873cdf0e10cSrcweir 
874cdf0e10cSrcweir 	// initialize character details
875cdf0e10cSrcweir 	mpCharWidths	= new Fixed[ mnCharCount ];
876cdf0e10cSrcweir 	mpChars2Glyphs	= new int[ mnCharCount ];
877cdf0e10cSrcweir 	for( int n = 0; n < mnCharCount; ++n )
878cdf0e10cSrcweir 	{
879cdf0e10cSrcweir 		mpCharWidths[ n ] = 0;
880cdf0e10cSrcweir 		mpChars2Glyphs[ n ] = -1;
881cdf0e10cSrcweir 	}
882cdf0e10cSrcweir 
883cdf0e10cSrcweir 	// get details about the glyph layout
884cdf0e10cSrcweir 	ItemCount iLayoutDataCount;
885cdf0e10cSrcweir 	const ATSLayoutRecord* pALR;
886cdf0e10cSrcweir 	OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
887cdf0e10cSrcweir 		maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
888cdf0e10cSrcweir 		(void**)&pALR, &iLayoutDataCount );
889cdf0e10cSrcweir 	DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n");
890cdf0e10cSrcweir 	if( (eStatus != noErr)
891cdf0e10cSrcweir 	|| (iLayoutDataCount <= 1) )
892cdf0e10cSrcweir 		return false;
893cdf0e10cSrcweir 
894cdf0e10cSrcweir 	// initialize glyph details
895cdf0e10cSrcweir 	mpGlyphIds      = new ATSGlyphRef[ iLayoutDataCount ];
896cdf0e10cSrcweir 	mpGlyphAdvances	= new Fixed[ iLayoutDataCount ];
897cdf0e10cSrcweir 	mpGlyphs2Chars	= new int[ iLayoutDataCount ];
898cdf0e10cSrcweir 
899cdf0e10cSrcweir 	// measure details of the glyph layout
900cdf0e10cSrcweir 	Fixed nLeftPos = 0;
901cdf0e10cSrcweir 	for( ItemCount i = 0; i < iLayoutDataCount; ++i )
902cdf0e10cSrcweir 	{
903cdf0e10cSrcweir 		const ATSLayoutRecord& rALR = pALR[i];
904cdf0e10cSrcweir 
905cdf0e10cSrcweir 		// distribute the widths as fairly as possible among the chars
906cdf0e10cSrcweir 		const int nRelativeIdx = (rALR.originalOffset / 2);
907cdf0e10cSrcweir 		if( i+1 < iLayoutDataCount )
908cdf0e10cSrcweir 			mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos;
909cdf0e10cSrcweir 
910cdf0e10cSrcweir 		// new glyph is available => finish measurement of old glyph
911cdf0e10cSrcweir 		if( mnGlyphCount > 0 )
912cdf0e10cSrcweir 			mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos;
913cdf0e10cSrcweir 
914cdf0e10cSrcweir 		// ignore marker or deleted glyphs
915cdf0e10cSrcweir 		enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF};
916cdf0e10cSrcweir 		if( rALR.glyphID >= MARKED_OUTGLYPH )
917cdf0e10cSrcweir 			continue;
918cdf0e10cSrcweir 
919cdf0e10cSrcweir 		DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph),
920cdf0e10cSrcweir 			"ATSLayout::InitGIA(): terminator glyph not marked as deleted!" );
921cdf0e10cSrcweir 
922cdf0e10cSrcweir 		// store details of the visible glyphs
923cdf0e10cSrcweir 		nLeftPos = rALR.realPos;
924cdf0e10cSrcweir 		mpGlyphIds[ mnGlyphCount ] = rALR.glyphID;
925cdf0e10cSrcweir 
926cdf0e10cSrcweir 		// map visible glyphs to their counterparts in the UTF16-character array
927cdf0e10cSrcweir 		mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos;
928cdf0e10cSrcweir 		mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount;
929cdf0e10cSrcweir 
930cdf0e10cSrcweir 		++mnGlyphCount;
931cdf0e10cSrcweir 	}
932cdf0e10cSrcweir 
933cdf0e10cSrcweir 	// measure complete width
934cdf0e10cSrcweir 	mnCachedWidth = mnBaseAdv;
935cdf0e10cSrcweir 	mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos;
936cdf0e10cSrcweir 
937cdf0e10cSrcweir #if (OSL_DEBUG_LEVEL > 1)
938cdf0e10cSrcweir 	Fixed nWidthSum = mnBaseAdv;
939cdf0e10cSrcweir 	for( int n = 0; n < mnCharCount; ++n )
940cdf0e10cSrcweir 		nWidthSum += mpCharWidths[ n ];
941cdf0e10cSrcweir 	DBG_ASSERT( (nWidthSum==mnCachedWidth),
942cdf0e10cSrcweir 		"ATSLayout::InitGIA(): measured widths do not match!\n" );
943cdf0e10cSrcweir #endif
944cdf0e10cSrcweir 
945cdf0e10cSrcweir 	// #i91183# we need to split up the portion into sub-portions
946cdf0e10cSrcweir 	// if the ATSU-layout differs too much from the requested layout
947cdf0e10cSrcweir 	if( pArgs && pArgs->mpDXArray )
948cdf0e10cSrcweir 	{
949cdf0e10cSrcweir 		// TODO: non-strong-LTR case cases should be handled too
950cdf0e10cSrcweir 		if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG)
951cdf0e10cSrcweir 		&& !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) )
952cdf0e10cSrcweir 		{
953cdf0e10cSrcweir 			Fixed nSumCharWidths = 0;
954cdf0e10cSrcweir 			SubPortion aSubPortion = { mnMinCharPos, 0, 0 };
955cdf0e10cSrcweir 			for( int i = 0; i < mnCharCount; ++i )
956cdf0e10cSrcweir 			{
957cdf0e10cSrcweir 				// calculate related logical position
958cdf0e10cSrcweir 				nSumCharWidths += mpCharWidths[i];
959cdf0e10cSrcweir 
960cdf0e10cSrcweir 				// start new sub-portion if needed
961cdf0e10cSrcweir 				const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]);
962cdf0e10cSrcweir 				const Fixed nNextXOffset = nNextXPos - nSumCharWidths;
963cdf0e10cSrcweir 				const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset;
964cdf0e10cSrcweir 				if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) {
965cdf0e10cSrcweir 					// get to the end of the current sub-portion
966cdf0e10cSrcweir 					// prevent splitting up at diacritics etc.
967cdf0e10cSrcweir 					int j = i;
968cdf0e10cSrcweir 					while( (++j < mnCharCount) && !mpCharWidths[j] );
969cdf0e10cSrcweir 					aSubPortion.mnEndCharPos = mnMinCharPos + j;
970cdf0e10cSrcweir 					// emit current sub-portion
971cdf0e10cSrcweir 					maSubPortions.push_back( aSubPortion );
972cdf0e10cSrcweir 					// prepare next sub-portion
973cdf0e10cSrcweir 					aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos;
974cdf0e10cSrcweir 					aSubPortion.mnXOffset = nNextXOffset;
975cdf0e10cSrcweir 				}
976cdf0e10cSrcweir 			}
977cdf0e10cSrcweir 
978cdf0e10cSrcweir 			// emit the remaining sub-portion
979cdf0e10cSrcweir 			if( !maSubPortions.empty() )
980cdf0e10cSrcweir 			{
981cdf0e10cSrcweir 				aSubPortion.mnEndCharPos = mnEndCharPos;
982cdf0e10cSrcweir 				if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos )
983cdf0e10cSrcweir 					maSubPortions.push_back( aSubPortion );
984cdf0e10cSrcweir 			}
985cdf0e10cSrcweir 		}
986cdf0e10cSrcweir 
987cdf0e10cSrcweir 		// override layouted charwidths with requested charwidths
988cdf0e10cSrcweir 		for( int n = 0; n < mnCharCount; ++n )
989cdf0e10cSrcweir 			mpCharWidths[ n ] = pArgs->mpDXArray[ n ];
990cdf0e10cSrcweir 	}
991cdf0e10cSrcweir 
992cdf0e10cSrcweir 	// release the ATSU layout records
993cdf0e10cSrcweir 	ATSUDirectReleaseLayoutDataArrayPtr(NULL,
994cdf0e10cSrcweir 		kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR );
995cdf0e10cSrcweir 
996cdf0e10cSrcweir 	return true;
997cdf0e10cSrcweir }
998cdf0e10cSrcweir 
999cdf0e10cSrcweir // -----------------------------------------------------------------------
1000cdf0e10cSrcweir 
GetIdealX() const1001cdf0e10cSrcweir bool ATSLayout::GetIdealX() const
1002cdf0e10cSrcweir {
1003cdf0e10cSrcweir 	// compute the ideal advance widths only once
1004cdf0e10cSrcweir 	if( mpGlyphOrigAdvs != NULL )
1005cdf0e10cSrcweir 		return true;
1006cdf0e10cSrcweir 
1007cdf0e10cSrcweir 	DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" );
1008cdf0e10cSrcweir 	DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" );
1009cdf0e10cSrcweir 
1010cdf0e10cSrcweir 	// TODO: cache ideal metrics per glyph?
1011cdf0e10cSrcweir 	std::vector<ATSGlyphIdealMetrics> aIdealMetrics;
1012cdf0e10cSrcweir 	aIdealMetrics.resize( mnGlyphCount );
1013cdf0e10cSrcweir 	OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle,
1014cdf0e10cSrcweir 		mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] );
1015cdf0e10cSrcweir 	DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!");
1016cdf0e10cSrcweir 	if( theErr != noErr )
1017cdf0e10cSrcweir 		return false;
1018cdf0e10cSrcweir 
1019cdf0e10cSrcweir 	mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ];
1020cdf0e10cSrcweir 	for( int i = 0;i < mnGlyphCount;++i )
1021cdf0e10cSrcweir 		mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x );
1022cdf0e10cSrcweir 
1023cdf0e10cSrcweir 	return true;
1024cdf0e10cSrcweir }
1025cdf0e10cSrcweir 
1026cdf0e10cSrcweir // -----------------------------------------------------------------------
1027cdf0e10cSrcweir 
GetDeltaY() const1028cdf0e10cSrcweir bool ATSLayout::GetDeltaY() const
1029cdf0e10cSrcweir {
1030cdf0e10cSrcweir 	// don't bother to get the same delta-y-array more than once
1031cdf0e10cSrcweir 	if( mpDeltaY != NULL )
1032cdf0e10cSrcweir 		return true;
1033cdf0e10cSrcweir 
1034cdf0e10cSrcweir #if 1
1035*e26449d3SHerbert Dürr 	if( !maATSULayout )
1036cdf0e10cSrcweir 		return false;
1037cdf0e10cSrcweir 
1038cdf0e10cSrcweir 	// get and keep the y-deltas in the mpDeltaY member variable
1039cdf0e10cSrcweir 	// => release it in the destructor
1040cdf0e10cSrcweir 	ItemCount nDeltaCount = 0;
1041cdf0e10cSrcweir 	OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
1042cdf0e10cSrcweir 		maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray,
1043cdf0e10cSrcweir 		(void**)&mpDeltaY, &nDeltaCount );
1044cdf0e10cSrcweir 
1045cdf0e10cSrcweir 	DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n");
1046cdf0e10cSrcweir 	if( theErr != noErr )
1047cdf0e10cSrcweir 		return false;
1048cdf0e10cSrcweir 
1049cdf0e10cSrcweir 	if( mpDeltaY == NULL )
1050cdf0e10cSrcweir 		return true;
1051cdf0e10cSrcweir 
1052cdf0e10cSrcweir 	if( nDeltaCount != (ItemCount)mnGlyphCount )
1053cdf0e10cSrcweir 	{
1054cdf0e10cSrcweir 		DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" );
1055cdf0e10cSrcweir 		ATSUDirectReleaseLayoutDataArrayPtr( NULL,
1056cdf0e10cSrcweir 			kATSUDirectDataBaselineDeltaFixedArray,	(void**)&mpDeltaY );
1057cdf0e10cSrcweir 		mpDeltaY = NULL;
1058cdf0e10cSrcweir 		return false;
1059cdf0e10cSrcweir 	}
1060cdf0e10cSrcweir #endif
1061cdf0e10cSrcweir 
1062cdf0e10cSrcweir 	return true;
1063cdf0e10cSrcweir }
1064cdf0e10cSrcweir 
1065cdf0e10cSrcweir // -----------------------------------------------------------------------
1066cdf0e10cSrcweir 
1067cdf0e10cSrcweir #define DELETEAZ( X ) { delete[] X; X = NULL; }
1068cdf0e10cSrcweir 
InvalidateMeasurements()1069cdf0e10cSrcweir void ATSLayout::InvalidateMeasurements()
1070cdf0e10cSrcweir {
1071cdf0e10cSrcweir 	mnGlyphCount = -1;
1072cdf0e10cSrcweir 	DELETEAZ( mpGlyphIds );
1073cdf0e10cSrcweir 	DELETEAZ( mpCharWidths );
1074cdf0e10cSrcweir 	DELETEAZ( mpChars2Glyphs );
1075cdf0e10cSrcweir 	DELETEAZ( mpGlyphs2Chars );
1076cdf0e10cSrcweir 	DELETEAZ( mpGlyphRTLFlags );
1077cdf0e10cSrcweir 	DELETEAZ( mpGlyphAdvances );
1078cdf0e10cSrcweir 	DELETEAZ( mpGlyphOrigAdvs );
1079cdf0e10cSrcweir 	DELETEAZ( mpDeltaY );
1080cdf0e10cSrcweir }
1081cdf0e10cSrcweir 
1082cdf0e10cSrcweir // =======================================================================
1083cdf0e10cSrcweir 
1084cdf0e10cSrcweir #if 0
1085cdf0e10cSrcweir // helper class to convert ATSUI outlines to VCL PolyPolygons
1086cdf0e10cSrcweir class PolyArgs
1087cdf0e10cSrcweir {
1088cdf0e10cSrcweir public:
1089cdf0e10cSrcweir 				PolyArgs();
1090cdf0e10cSrcweir 				~PolyArgs();
1091cdf0e10cSrcweir 
1092cdf0e10cSrcweir 	void		Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset );
1093cdf0e10cSrcweir 	void		AddPoint( const Float32Point&, PolyFlags );
1094cdf0e10cSrcweir 	void		ClosePolygon();
1095cdf0e10cSrcweir 
1096cdf0e10cSrcweir private:
1097cdf0e10cSrcweir 	PolyPolygon* mpPolyPoly;
1098cdf0e10cSrcweir 	long		mnXOffset, mnYOffset;
1099cdf0e10cSrcweir 
1100cdf0e10cSrcweir 	Point*		mpPointAry;
1101cdf0e10cSrcweir 	BYTE*		mpFlagAry;
1102cdf0e10cSrcweir 	USHORT		mnMaxPoints;
1103cdf0e10cSrcweir 
1104cdf0e10cSrcweir 	USHORT		mnPointCount;
1105cdf0e10cSrcweir 	USHORT		mnPolyCount;
1106cdf0e10cSrcweir 	bool		mbHasOffline;
1107cdf0e10cSrcweir };
1108cdf0e10cSrcweir 
1109cdf0e10cSrcweir // -----------------------------------------------------------------------
1110cdf0e10cSrcweir 
1111cdf0e10cSrcweir PolyArgs::PolyArgs()
1112cdf0e10cSrcweir :	mpPolyPoly(NULL),
1113cdf0e10cSrcweir 	mnPointCount(0),
1114cdf0e10cSrcweir 	mnPolyCount(0),
1115cdf0e10cSrcweir 	mbHasOffline(false)
1116cdf0e10cSrcweir {
1117cdf0e10cSrcweir 	mnMaxPoints = 256;
1118cdf0e10cSrcweir 	mpPointAry	= new Point[ mnMaxPoints ];
1119cdf0e10cSrcweir 	mpFlagAry	= new BYTE [ mnMaxPoints ];
1120cdf0e10cSrcweir }
1121cdf0e10cSrcweir 
1122cdf0e10cSrcweir // -----------------------------------------------------------------------
1123cdf0e10cSrcweir 
1124cdf0e10cSrcweir PolyArgs::~PolyArgs()
1125cdf0e10cSrcweir {
1126cdf0e10cSrcweir 	delete[] mpFlagAry;
1127cdf0e10cSrcweir 	delete[] mpPointAry;
1128cdf0e10cSrcweir }
1129cdf0e10cSrcweir 
1130cdf0e10cSrcweir // -----------------------------------------------------------------------
1131cdf0e10cSrcweir 
1132cdf0e10cSrcweir void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset )
1133cdf0e10cSrcweir {
1134cdf0e10cSrcweir 	mnXOffset = nXOffset;
1135cdf0e10cSrcweir 	mnYOffset = nYOffset;
1136cdf0e10cSrcweir 	mpPolyPoly = pPolyPoly;
1137cdf0e10cSrcweir 
1138cdf0e10cSrcweir 	mpPolyPoly->Clear();
1139cdf0e10cSrcweir 	mnPointCount = 0;
1140cdf0e10cSrcweir 	mnPolyCount  = 0;
1141cdf0e10cSrcweir }
1142cdf0e10cSrcweir 
1143cdf0e10cSrcweir // -----------------------------------------------------------------------
1144cdf0e10cSrcweir 
1145cdf0e10cSrcweir void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags )
1146cdf0e10cSrcweir {
1147cdf0e10cSrcweir 	if( mnPointCount >= mnMaxPoints )
1148cdf0e10cSrcweir 	{
1149cdf0e10cSrcweir 		// resize if needed (TODO: use STL?)
1150cdf0e10cSrcweir 		mnMaxPoints *= 4;
1151cdf0e10cSrcweir 		Point* mpNewPoints = new Point[ mnMaxPoints ];
1152cdf0e10cSrcweir 		BYTE* mpNewFlags = new BYTE[ mnMaxPoints ];
1153cdf0e10cSrcweir 		for( int i = 0; i < mnPointCount; ++i )
1154cdf0e10cSrcweir 		{
1155cdf0e10cSrcweir 			mpNewPoints[ i ] = mpPointAry[ i ];
1156cdf0e10cSrcweir 			mpNewFlags[ i ] = mpFlagAry[ i ];
1157cdf0e10cSrcweir 		}
1158cdf0e10cSrcweir 		delete[] mpFlagAry;
1159cdf0e10cSrcweir 		delete[] mpPointAry;
1160cdf0e10cSrcweir 		mpPointAry = mpNewPoints;
1161cdf0e10cSrcweir 		mpFlagAry = mpNewFlags;
1162cdf0e10cSrcweir 	}
1163cdf0e10cSrcweir 
1164cdf0e10cSrcweir 	// convert to pixels and add startpoint offset
1165cdf0e10cSrcweir 	int nXPos = Float32ToInt( rPoint.x );
1166cdf0e10cSrcweir 	int nYPos = Float32ToInt( rPoint.y );
1167cdf0e10cSrcweir 	mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset );
1168cdf0e10cSrcweir 	// set point flags
1169cdf0e10cSrcweir 	mpFlagAry[ mnPointCount++ ]= eFlags;
1170cdf0e10cSrcweir 	mbHasOffline |= (eFlags != POLY_NORMAL);
1171cdf0e10cSrcweir }
1172cdf0e10cSrcweir 
1173cdf0e10cSrcweir // -----------------------------------------------------------------------
1174cdf0e10cSrcweir 
1175cdf0e10cSrcweir void PolyArgs::ClosePolygon()
1176cdf0e10cSrcweir {
1177cdf0e10cSrcweir 	if( !mnPolyCount++ )
1178cdf0e10cSrcweir 		 return;
1179cdf0e10cSrcweir 
1180cdf0e10cSrcweir 	// append finished polygon
1181cdf0e10cSrcweir 	Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) );
1182cdf0e10cSrcweir 	mpPolyPoly->Insert( aPoly );
1183cdf0e10cSrcweir 
1184cdf0e10cSrcweir 	// prepare for new polygon
1185cdf0e10cSrcweir 	mnPointCount = 0;
1186cdf0e10cSrcweir 	mbHasOffline = false;
1187cdf0e10cSrcweir }
1188cdf0e10cSrcweir #endif
1189cdf0e10cSrcweir // =======================================================================
1190cdf0e10cSrcweir 
1191cdf0e10cSrcweir // glyph fallback is supported directly by Aqua
1192cdf0e10cSrcweir // so methods used only by MultiSalLayout can be dummy implementated
GetGlyphOutlines(SalGraphics &,PolyPolyVector &) const1193cdf0e10cSrcweir bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
InitFont()1194cdf0e10cSrcweir void ATSLayout::InitFont() {}
MoveGlyph(int,long)1195cdf0e10cSrcweir void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
DropGlyph(int)1196cdf0e10cSrcweir void ATSLayout::DropGlyph( int /*nStart*/ ) {}
Simplify(bool)1197cdf0e10cSrcweir void ATSLayout::Simplify( bool /*bIsBase*/ ) {}
1198cdf0e10cSrcweir 
1199cdf0e10cSrcweir // get the ImplFontData for a glyph fallback font
1200cdf0e10cSrcweir // for a glyphid that was returned by ATSLayout::GetNextGlyphs()
GetFallbackFontData(sal_GlyphId aGlyphId) const1201248a599fSHerbert Dürr const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const
1202cdf0e10cSrcweir {
1203*e26449d3SHerbert Dürr 	// check if any fallback fonts were needed
1204*e26449d3SHerbert Dürr 	if( !mpFallbackInfo )
1205*e26449d3SHerbert Dürr 		return NULL;
1206*e26449d3SHerbert Dürr 	// check if the current glyph needs a fallback font
1207*e26449d3SHerbert Dürr 	int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
1208*e26449d3SHerbert Dürr 	if( !nFallbackLevel )
1209*e26449d3SHerbert Dürr 		return NULL;
1210cdf0e10cSrcweir 	return mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
1211cdf0e10cSrcweir }
1212cdf0e10cSrcweir 
1213cdf0e10cSrcweir // =======================================================================
1214cdf0e10cSrcweir 
AddFallback(ATSUFontID nFontId)1215cdf0e10cSrcweir int FallbackInfo::AddFallback( ATSUFontID nFontId )
1216cdf0e10cSrcweir {
1217*e26449d3SHerbert Dürr 	// check if the fallback font is already known
1218*e26449d3SHerbert Dürr 	for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel )
1219*e26449d3SHerbert Dürr 		if( maATSUFontId[ nLevel ] == nFontId )
1220*e26449d3SHerbert Dürr 			return (nLevel + 1);
1221cdf0e10cSrcweir 
1222cdf0e10cSrcweir 	// append new fallback font if possible
1223*e26449d3SHerbert Dürr 	if( mnMaxLevel >= MAX_FALLBACK-1 )
1224*e26449d3SHerbert Dürr 		return 0;
1225*e26449d3SHerbert Dürr 	// keep ATSU font id of fallback font
1226*e26449d3SHerbert Dürr 	maATSUFontId[ mnMaxLevel ] = nFontId;
1227*e26449d3SHerbert Dürr 	// find and cache the corresponding ImplFontData pointer
1228*e26449d3SHerbert Dürr 	const SystemFontList* pSFL = GetSalData()->mpFontList;
1229*e26449d3SHerbert Dürr 	const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId );
1230*e26449d3SHerbert Dürr 	maFontData[ mnMaxLevel ] = pFontData;
1231*e26449d3SHerbert Dürr 	// increase fallback level by one
1232*e26449d3SHerbert Dürr 	return (++mnMaxLevel);
1233cdf0e10cSrcweir }
1234cdf0e10cSrcweir 
1235cdf0e10cSrcweir // -----------------------------------------------------------------------
1236cdf0e10cSrcweir 
GetFallbackFontData(int nFallbackLevel) const1237cdf0e10cSrcweir const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const
1238cdf0e10cSrcweir {
1239*e26449d3SHerbert Dürr 	const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ];
1240*e26449d3SHerbert Dürr 	return pFallbackFont;
1241cdf0e10cSrcweir }
1242cdf0e10cSrcweir 
1243cdf0e10cSrcweir // =======================================================================
1244cdf0e10cSrcweir 
GetTextLayout(ImplLayoutArgs &,int)1245cdf0e10cSrcweir SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ )
1246cdf0e10cSrcweir {
1247*e26449d3SHerbert Dürr 	ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale );
1248*e26449d3SHerbert Dürr 	return pATSLayout;
1249cdf0e10cSrcweir }
1250cdf0e10cSrcweir 
1251cdf0e10cSrcweir // =======================================================================
1252cdf0e10cSrcweir 
1253