xref: /aoo41x/main/vcl/aqua/source/gdi/ctlayout.cxx (revision 2822fc04)
1*2822fc04SHerbert Dürr /**************************************************************
2*2822fc04SHerbert Dürr  *
3*2822fc04SHerbert Dürr  * Licensed to the Apache Software Foundation (ASF) under one
4*2822fc04SHerbert Dürr  * or more contributor license agreements.  See the NOTICE file
5*2822fc04SHerbert Dürr  * distributed with this work for additional information
6*2822fc04SHerbert Dürr  * regarding copyright ownership.  The ASF licenses this file
7*2822fc04SHerbert Dürr  * to you under the Apache License, Version 2.0 (the
8*2822fc04SHerbert Dürr  * "License"); you may not use this file except in compliance
9*2822fc04SHerbert Dürr  * with the License.  You may obtain a copy of the License at
10*2822fc04SHerbert Dürr  *
11*2822fc04SHerbert Dürr  *   http://www.apache.org/licenses/LICENSE-2.0
12*2822fc04SHerbert Dürr  *
13*2822fc04SHerbert Dürr  * Unless required by applicable law or agreed to in writing,
14*2822fc04SHerbert Dürr  * software distributed under the License is distributed on an
15*2822fc04SHerbert Dürr  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*2822fc04SHerbert Dürr  * KIND, either express or implied.  See the License for the
17*2822fc04SHerbert Dürr  * specific language governing permissions and limitations
18*2822fc04SHerbert Dürr  * under the License.
19*2822fc04SHerbert Dürr  *
20*2822fc04SHerbert Dürr  *************************************************************/
21*2822fc04SHerbert Dürr 
22*2822fc04SHerbert Dürr //#include "salgdi.hxx"
23*2822fc04SHerbert Dürr #include "tools/debug.hxx"
24*2822fc04SHerbert Dürr 
25*2822fc04SHerbert Dürr #include "ctfonts.hxx"
26*2822fc04SHerbert Dürr 
27*2822fc04SHerbert Dürr // =======================================================================
28*2822fc04SHerbert Dürr 
29*2822fc04SHerbert Dürr class CTLayout
30*2822fc04SHerbert Dürr :	public SalLayout
31*2822fc04SHerbert Dürr {
32*2822fc04SHerbert Dürr public:
33*2822fc04SHerbert Dürr 	explicit        CTLayout( const CTTextStyle* );
34*2822fc04SHerbert Dürr 	virtual         ~CTLayout( void );
35*2822fc04SHerbert Dürr 
36*2822fc04SHerbert Dürr 	virtual bool	LayoutText( ImplLayoutArgs& );
37*2822fc04SHerbert Dürr 	virtual void	AdjustLayout( ImplLayoutArgs& );
38*2822fc04SHerbert Dürr 	virtual void	DrawText( SalGraphics& ) const;
39*2822fc04SHerbert Dürr 
40*2822fc04SHerbert Dürr 	virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
41*2822fc04SHerbert Dürr 						sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
42*2822fc04SHerbert Dürr 
43*2822fc04SHerbert Dürr 	virtual long    GetTextWidth() const;
44*2822fc04SHerbert Dürr 	virtual long    FillDXArray( sal_Int32* pDXArray ) const;
45*2822fc04SHerbert Dürr 	virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
46*2822fc04SHerbert Dürr 	virtual void    GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const;
47*2822fc04SHerbert Dürr 	virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
48*2822fc04SHerbert Dürr 	virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
49*2822fc04SHerbert Dürr 
50*2822fc04SHerbert Dürr 	const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
51*2822fc04SHerbert Dürr 
52*2822fc04SHerbert Dürr 	virtual void	InitFont( void) const;
53*2822fc04SHerbert Dürr 	virtual void	MoveGlyph( int nStart, long nNewXPos );
54*2822fc04SHerbert Dürr 	virtual void	DropGlyph( int nStart );
55*2822fc04SHerbert Dürr 	virtual void	Simplify( bool bIsBase );
56*2822fc04SHerbert Dürr 
57*2822fc04SHerbert Dürr private:
58*2822fc04SHerbert Dürr 	const CTTextStyle* const	mpTextStyle;
59*2822fc04SHerbert Dürr 
60*2822fc04SHerbert Dürr 	// CoreText specific objects
61*2822fc04SHerbert Dürr 	CFAttributedStringRef mpAttrString;
62*2822fc04SHerbert Dürr 	CTLineRef mpCTLine;
63*2822fc04SHerbert Dürr 
64*2822fc04SHerbert Dürr 	int mnCharCount;		// ==mnEndCharPos-mnMinCharPos
65*2822fc04SHerbert Dürr 	int mnTrailingSpaces;
66*2822fc04SHerbert Dürr 
67*2822fc04SHerbert Dürr 	// to prevent overflows
68*2822fc04SHerbert Dürr 	// font requests get size limited by downscaling huge fonts
69*2822fc04SHerbert Dürr 	// in these cases the font scale becomes something bigger than 1.0
70*2822fc04SHerbert Dürr 	float mfFontScale; // TODO: does CoreText have a font size limit?
71*2822fc04SHerbert Dürr 
72*2822fc04SHerbert Dürr 	// cached details about the resulting layout
73*2822fc04SHerbert Dürr 	// mutable members since these details are all lazy initialized
74*2822fc04SHerbert Dürr 	mutable double	mfCachedWidth;			// cached value of resulting typographical width
75*2822fc04SHerbert Dürr 	mutable double	mfTrailingSpaceWidth;   // in Pixels
76*2822fc04SHerbert Dürr 
77*2822fc04SHerbert Dürr 	// x-offset relative to layout origin
78*2822fc04SHerbert Dürr 	// currently only used in RTL-layouts
79*2822fc04SHerbert Dürr 	mutable long	mnBaseAdv;
80*2822fc04SHerbert Dürr };
81*2822fc04SHerbert Dürr 
82*2822fc04SHerbert Dürr // =======================================================================
83*2822fc04SHerbert Dürr 
84*2822fc04SHerbert Dürr CTLayout::CTLayout( const CTTextStyle* pTextStyle )
85*2822fc04SHerbert Dürr :	mpTextStyle( pTextStyle )
86*2822fc04SHerbert Dürr ,	mpAttrString( NULL )
87*2822fc04SHerbert Dürr ,	mpCTLine( NULL )
88*2822fc04SHerbert Dürr ,	mnCharCount( 0 )
89*2822fc04SHerbert Dürr ,	mnTrailingSpaces( 0 )
90*2822fc04SHerbert Dürr ,	mfFontScale( pTextStyle->mfFontScale )
91*2822fc04SHerbert Dürr ,	mfCachedWidth( -1 )
92*2822fc04SHerbert Dürr ,	mfTrailingSpaceWidth( 0 )
93*2822fc04SHerbert Dürr ,	mnBaseAdv( 0 )
94*2822fc04SHerbert Dürr {
95*2822fc04SHerbert Dürr 	CFRetain( mpTextStyle->GetStyleDict() );
96*2822fc04SHerbert Dürr }
97*2822fc04SHerbert Dürr 
98*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
99*2822fc04SHerbert Dürr 
100*2822fc04SHerbert Dürr CTLayout::~CTLayout()
101*2822fc04SHerbert Dürr {
102*2822fc04SHerbert Dürr 	if( mpCTLine )
103*2822fc04SHerbert Dürr 		CFRelease( mpCTLine );
104*2822fc04SHerbert Dürr 	if( mpAttrString )
105*2822fc04SHerbert Dürr 		CFRelease( mpAttrString );
106*2822fc04SHerbert Dürr 	CFRelease( mpTextStyle->GetStyleDict() );
107*2822fc04SHerbert Dürr }
108*2822fc04SHerbert Dürr 
109*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
110*2822fc04SHerbert Dürr 
111*2822fc04SHerbert Dürr bool CTLayout::LayoutText( ImplLayoutArgs& rArgs )
112*2822fc04SHerbert Dürr {
113*2822fc04SHerbert Dürr 	if( mpAttrString )
114*2822fc04SHerbert Dürr 		CFRelease( mpAttrString );
115*2822fc04SHerbert Dürr 	mpAttrString = NULL;
116*2822fc04SHerbert Dürr 	if( mpCTLine )
117*2822fc04SHerbert Dürr 		CFRelease( mpCTLine );
118*2822fc04SHerbert Dürr 	mpCTLine = NULL;
119*2822fc04SHerbert Dürr 
120*2822fc04SHerbert Dürr 	SalLayout::AdjustLayout( rArgs );
121*2822fc04SHerbert Dürr 	mnCharCount = mnEndCharPos - mnMinCharPos;
122*2822fc04SHerbert Dürr 
123*2822fc04SHerbert Dürr 	// short circuit if there is nothing to do
124*2822fc04SHerbert Dürr 	if( mnCharCount <= 0 )
125*2822fc04SHerbert Dürr 		return false;
126*2822fc04SHerbert Dürr 
127*2822fc04SHerbert Dürr 	// create the CoreText line layout
128*2822fc04SHerbert Dürr 	CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos, mnCharCount, kCFAllocatorNull );
129*2822fc04SHerbert Dürr 	mpAttrString = CFAttributedStringCreate( NULL, aCFText, mpTextStyle->GetStyleDict() );
130*2822fc04SHerbert Dürr 	mpCTLine = CTLineCreateWithAttributedString( mpAttrString );
131*2822fc04SHerbert Dürr 	CFRelease( aCFText);
132*2822fc04SHerbert Dürr 
133*2822fc04SHerbert Dürr 	// get info about trailing whitespace to prepare for text justification in AdjustLayout()
134*2822fc04SHerbert Dürr 	mnTrailingSpaces = 0;
135*2822fc04SHerbert Dürr 	for( int i = mnEndCharPos; --i >= mnMinCharPos; ++mnTrailingSpaces )
136*2822fc04SHerbert Dürr 		if( !IsSpacingGlyph( rArgs.mpStr[i] | GF_ISCHAR ))
137*2822fc04SHerbert Dürr 			break;
138*2822fc04SHerbert Dürr 	return true;
139*2822fc04SHerbert Dürr }
140*2822fc04SHerbert Dürr 
141*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
142*2822fc04SHerbert Dürr 
143*2822fc04SHerbert Dürr void CTLayout::AdjustLayout( ImplLayoutArgs& rArgs )
144*2822fc04SHerbert Dürr {
145*2822fc04SHerbert Dürr 	if( !mpCTLine)
146*2822fc04SHerbert Dürr 		return;
147*2822fc04SHerbert Dürr 
148*2822fc04SHerbert Dürr 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
149*2822fc04SHerbert Dürr 	// CoreText fills trailing space during justification so we have to
150*2822fc04SHerbert Dürr 	// take that into account when requesting CT to justify something
151*2822fc04SHerbert Dürr 	mfTrailingSpaceWidth = rCT.LineGetTrailingWhitespaceWidth( mpCTLine );
152*2822fc04SHerbert Dürr 	const int nTrailingSpaceWidth = rint( mfFontScale * mfTrailingSpaceWidth );
153*2822fc04SHerbert Dürr 
154*2822fc04SHerbert Dürr 	int nOrigWidth = GetTextWidth();
155*2822fc04SHerbert Dürr 	int nPixelWidth = rArgs.mnLayoutWidth;
156*2822fc04SHerbert Dürr 	if( nPixelWidth )
157*2822fc04SHerbert Dürr 	{
158*2822fc04SHerbert Dürr 		nPixelWidth -= nTrailingSpaceWidth;
159*2822fc04SHerbert Dürr 		if( nPixelWidth <= 0)
160*2822fc04SHerbert Dürr 			return;
161*2822fc04SHerbert Dürr 	}
162*2822fc04SHerbert Dürr 	else if( rArgs.mpDXArray )
163*2822fc04SHerbert Dürr 	{
164*2822fc04SHerbert Dürr 		// for now we are only interested in the layout width
165*2822fc04SHerbert Dürr 		// TODO: use all mpDXArray elements for layouting
166*2822fc04SHerbert Dürr 		nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 - mnTrailingSpaces ];
167*2822fc04SHerbert Dürr 	}
168*2822fc04SHerbert Dürr 
169*2822fc04SHerbert Dürr 	// in RTL-layouts trailing spaces are leftmost
170*2822fc04SHerbert Dürr 	// TODO: use BiDi-algorithm to thoroughly check this assumption
171*2822fc04SHerbert Dürr 	if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
172*2822fc04SHerbert Dürr 		mnBaseAdv = nTrailingSpaceWidth;
173*2822fc04SHerbert Dürr 
174*2822fc04SHerbert Dürr 	// return early if there is nothing to do
175*2822fc04SHerbert Dürr 	if( nPixelWidth <= 0 )
176*2822fc04SHerbert Dürr 		return;
177*2822fc04SHerbert Dürr 
178*2822fc04SHerbert Dürr 	// HACK: justification requests which change the width by just one pixel are probably
179*2822fc04SHerbert Dürr 	// #i86038# introduced by lossy conversions between integer based coordinate system
180*2822fc04SHerbert Dürr 	if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
181*2822fc04SHerbert Dürr 		return;
182*2822fc04SHerbert Dürr 
183*2822fc04SHerbert Dürr 	// if the text to be justified has whitespace in it then
184*2822fc04SHerbert Dürr 	// - Writer goes crazy with its HalfSpace magic
185*2822fc04SHerbert Dürr 	// - LayoutEngine handles spaces specially (in particular at the text start or end)
186*2822fc04SHerbert Dürr 	if( mnTrailingSpaces ) {
187*2822fc04SHerbert Dürr 		// adjust for Writer's SwFntObj::DrawText() Halfspace magic at the text end
188*2822fc04SHerbert Dürr 		std::vector<sal_Int32> aOrigDXAry;
189*2822fc04SHerbert Dürr 		aOrigDXAry.resize( mnCharCount);
190*2822fc04SHerbert Dürr 		FillDXArray( &aOrigDXAry[0] );
191*2822fc04SHerbert Dürr 		int nLastCharSpace = rArgs.mpDXArray[ mnCharCount-1-mnTrailingSpaces ]
192*2822fc04SHerbert Dürr 			- aOrigDXAry[ mnCharCount-1-mnTrailingSpaces ];
193*2822fc04SHerbert Dürr 		nPixelWidth -= nLastCharSpace;
194*2822fc04SHerbert Dürr 		if( nPixelWidth < 0 )
195*2822fc04SHerbert Dürr 			return;
196*2822fc04SHerbert Dürr 		// recreate the CoreText line layout without trailing spaces
197*2822fc04SHerbert Dürr 		CFRelease( mpCTLine );
198*2822fc04SHerbert Dürr 		CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos,
199*2822fc04SHerbert Dürr 			mnCharCount - mnTrailingSpaces, kCFAllocatorNull );
200*2822fc04SHerbert Dürr 		CFAttributedStringRef pAttrStr = CFAttributedStringCreate( NULL, aCFText, mpTextStyle->GetStyleDict() );
201*2822fc04SHerbert Dürr 		mpCTLine = CTLineCreateWithAttributedString( pAttrStr );
202*2822fc04SHerbert Dürr 		CFRelease( aCFText);
203*2822fc04SHerbert Dürr 		CFRelease( pAttrStr );
204*2822fc04SHerbert Dürr 	}
205*2822fc04SHerbert Dürr 
206*2822fc04SHerbert Dürr 	CTLineRef pNewCTLine = rCT.LineCreateJustifiedLine( mpCTLine, 1.0, nPixelWidth / mfFontScale );
207*2822fc04SHerbert Dürr 	if( !pNewCTLine ) { // CTLineCreateJustifiedLine can and does fail
208*2822fc04SHerbert Dürr 		// handle failure by keeping the unjustified layout
209*2822fc04SHerbert Dürr 		// TODO: a better solution such as
210*2822fc04SHerbert Dürr 		// - forcing glyph overlap
211*2822fc04SHerbert Dürr 		// - changing the font size
212*2822fc04SHerbert Dürr 		// - changing the CTM matrix
213*2822fc04SHerbert Dürr 		return;
214*2822fc04SHerbert Dürr 	}
215*2822fc04SHerbert Dürr 	CFRelease( mpCTLine );
216*2822fc04SHerbert Dürr 	mpCTLine = pNewCTLine;
217*2822fc04SHerbert Dürr 	mfCachedWidth = -1; // TODO: can we set it directly to target width we requested? For now we re-measure
218*2822fc04SHerbert Dürr 	mfTrailingSpaceWidth = 0;
219*2822fc04SHerbert Dürr }
220*2822fc04SHerbert Dürr 
221*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
222*2822fc04SHerbert Dürr 
223*2822fc04SHerbert Dürr void CTLayout::DrawText( SalGraphics& rGraphics ) const
224*2822fc04SHerbert Dürr {
225*2822fc04SHerbert Dürr 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
226*2822fc04SHerbert Dürr 
227*2822fc04SHerbert Dürr 	// short circuit if there is nothing to do
228*2822fc04SHerbert Dürr 	if( (mnCharCount <= 0)
229*2822fc04SHerbert Dürr 	||  !rAquaGraphics.CheckContext() )
230*2822fc04SHerbert Dürr 		return;
231*2822fc04SHerbert Dürr 
232*2822fc04SHerbert Dürr 	// the view is vertically flipped => flipped glyphs
233*2822fc04SHerbert Dürr 	// so apply a temporary transformation that it flips back
234*2822fc04SHerbert Dürr 	// also compensate if the font was size limited
235*2822fc04SHerbert Dürr 	CGContextSaveGState( rAquaGraphics.mrContext );
236*2822fc04SHerbert Dürr 	CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
237*2822fc04SHerbert Dürr 	CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
238*2822fc04SHerbert Dürr 
239*2822fc04SHerbert Dürr 	// Draw the text
240*2822fc04SHerbert Dürr 	const Point aVclPos = GetDrawPosition( Point(mnBaseAdv,0) );
241*2822fc04SHerbert Dürr 	CGPoint aTextPos = { +aVclPos.X()/mfFontScale, -aVclPos.Y()/mfFontScale };
242*2822fc04SHerbert Dürr 
243*2822fc04SHerbert Dürr 	if( mpTextStyle->mfFontRotation != 0.0 )
244*2822fc04SHerbert Dürr 	{
245*2822fc04SHerbert Dürr 		const CGFloat fRadians = mpTextStyle->mfFontRotation;
246*2822fc04SHerbert Dürr 		CGContextRotateCTM( rAquaGraphics.mrContext, +fRadians );
247*2822fc04SHerbert Dürr 
248*2822fc04SHerbert Dürr 		const CGAffineTransform aInvMatrix = CGAffineTransformMakeRotation( -fRadians );
249*2822fc04SHerbert Dürr 		aTextPos = CGPointApplyAffineTransform( aTextPos, aInvMatrix );
250*2822fc04SHerbert Dürr 	}
251*2822fc04SHerbert Dürr 
252*2822fc04SHerbert Dürr 	CGContextSetTextPosition( rAquaGraphics.mrContext, aTextPos.x, aTextPos.y );
253*2822fc04SHerbert Dürr 	CTLineDraw( mpCTLine, rAquaGraphics.mrContext );
254*2822fc04SHerbert Dürr 
255*2822fc04SHerbert Dürr 	// request an update of the changed window area
256*2822fc04SHerbert Dürr 	if( rAquaGraphics.IsWindowGraphics() )
257*2822fc04SHerbert Dürr 	{
258*2822fc04SHerbert Dürr 		const CGRect aInkRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext );
259*2822fc04SHerbert Dürr 		const CGRect aRefreshRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aInkRect );
260*2822fc04SHerbert Dürr 		rAquaGraphics.RefreshRect( aRefreshRect );
261*2822fc04SHerbert Dürr 	}
262*2822fc04SHerbert Dürr 
263*2822fc04SHerbert Dürr 	// restore the original graphic context transformations
264*2822fc04SHerbert Dürr 	CGContextRestoreGState( rAquaGraphics.mrContext );
265*2822fc04SHerbert Dürr }
266*2822fc04SHerbert Dürr 
267*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
268*2822fc04SHerbert Dürr 
269*2822fc04SHerbert Dürr int CTLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
270*2822fc04SHerbert Dürr 	sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
271*2822fc04SHerbert Dürr {
272*2822fc04SHerbert Dürr 	if( !mpCTLine )
273*2822fc04SHerbert Dürr 		return 0;
274*2822fc04SHerbert Dürr 
275*2822fc04SHerbert Dürr 	if( nStart < 0 ) // first glyph requested?
276*2822fc04SHerbert Dürr 		nStart = 0;
277*2822fc04SHerbert Dürr 	nLen = 1; // TODO: handle nLen>1 below
278*2822fc04SHerbert Dürr 
279*2822fc04SHerbert Dürr 	// prepare to iterate over the glyph runs
280*2822fc04SHerbert Dürr 	int nCount = 0;
281*2822fc04SHerbert Dürr 	int nSubIndex = nStart;
282*2822fc04SHerbert Dürr 
283*2822fc04SHerbert Dürr 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
284*2822fc04SHerbert Dürr 	typedef std::vector<CGGlyph> CGGlyphVector;
285*2822fc04SHerbert Dürr 	typedef std::vector<CGPoint> CGPointVector;
286*2822fc04SHerbert Dürr 	typedef std::vector<CGSize>  CGSizeVector;
287*2822fc04SHerbert Dürr 	typedef std::vector<CFIndex> CFIndexVector;
288*2822fc04SHerbert Dürr 	CGGlyphVector aCGGlyphVec;
289*2822fc04SHerbert Dürr 	CGPointVector aCGPointVec;
290*2822fc04SHerbert Dürr 	CGSizeVector  aCGSizeVec;
291*2822fc04SHerbert Dürr 	CFIndexVector aCFIndexVec;
292*2822fc04SHerbert Dürr 
293*2822fc04SHerbert Dürr 	// TODO: iterate over cached layout
294*2822fc04SHerbert Dürr 	CFArrayRef aGlyphRuns = rCT.LineGetGlyphRuns( mpCTLine );
295*2822fc04SHerbert Dürr 	const int nRunCount = CFArrayGetCount( aGlyphRuns );
296*2822fc04SHerbert Dürr 	for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) {
297*2822fc04SHerbert Dürr 		CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex );
298*2822fc04SHerbert Dürr 		const CFIndex nGlyphsInRun = rCT.RunGetGlyphCount( pGlyphRun );
299*2822fc04SHerbert Dürr 		// skip to the first glyph run of interest
300*2822fc04SHerbert Dürr 		if( nSubIndex >= nGlyphsInRun ) {
301*2822fc04SHerbert Dürr 			nSubIndex -= nGlyphsInRun;
302*2822fc04SHerbert Dürr 			continue;
303*2822fc04SHerbert Dürr 		}
304*2822fc04SHerbert Dürr 		const CFRange aFullRange = CFRangeMake( 0, nGlyphsInRun );
305*2822fc04SHerbert Dürr 
306*2822fc04SHerbert Dürr 		// get glyph run details
307*2822fc04SHerbert Dürr 		const CGGlyph* pCGGlyphIdx = rCT.RunGetGlyphsPtr( pGlyphRun );
308*2822fc04SHerbert Dürr 		if( !pCGGlyphIdx ) {
309*2822fc04SHerbert Dürr 			aCGGlyphVec.reserve( nGlyphsInRun );
310*2822fc04SHerbert Dürr 			CTRunGetGlyphs( pGlyphRun, aFullRange, &aCGGlyphVec[0] );
311*2822fc04SHerbert Dürr 			pCGGlyphIdx = &aCGGlyphVec[0];
312*2822fc04SHerbert Dürr 		}
313*2822fc04SHerbert Dürr 		const CGPoint* pCGGlyphPos = rCT.RunGetPositionsPtr( pGlyphRun );
314*2822fc04SHerbert Dürr 		if( !pCGGlyphPos ) {
315*2822fc04SHerbert Dürr 			aCGPointVec.reserve( nGlyphsInRun );
316*2822fc04SHerbert Dürr 			CTRunGetPositions( pGlyphRun, aFullRange, &aCGPointVec[0] );
317*2822fc04SHerbert Dürr 			pCGGlyphPos = &aCGPointVec[0];
318*2822fc04SHerbert Dürr 		}
319*2822fc04SHerbert Dürr 
320*2822fc04SHerbert Dürr 		const CGSize* pCGGlyphAdvs = NULL;
321*2822fc04SHerbert Dürr 		if( pGlyphAdvances) {
322*2822fc04SHerbert Dürr 			pCGGlyphAdvs = rCT.RunGetAdvancesPtr( pGlyphRun );
323*2822fc04SHerbert Dürr 			if( !pCGGlyphAdvs) {
324*2822fc04SHerbert Dürr 				aCGSizeVec.reserve( nGlyphsInRun );
325*2822fc04SHerbert Dürr 				CTRunGetAdvances( pGlyphRun, aFullRange, &aCGSizeVec[0] );
326*2822fc04SHerbert Dürr 				pCGGlyphAdvs = &aCGSizeVec[0];
327*2822fc04SHerbert Dürr 			}
328*2822fc04SHerbert Dürr 		}
329*2822fc04SHerbert Dürr 
330*2822fc04SHerbert Dürr 		const CFIndex* pCGGlyphStrIdx = NULL;
331*2822fc04SHerbert Dürr 		if( pCharIndexes) {
332*2822fc04SHerbert Dürr 			pCGGlyphStrIdx = rCT.RunGetStringIndicesPtr( pGlyphRun );
333*2822fc04SHerbert Dürr 			if( !pCGGlyphStrIdx) {
334*2822fc04SHerbert Dürr 				aCFIndexVec.reserve( nGlyphsInRun );
335*2822fc04SHerbert Dürr 				CTRunGetStringIndices( pGlyphRun, aFullRange, &aCFIndexVec[0] );
336*2822fc04SHerbert Dürr 				pCGGlyphStrIdx = &aCFIndexVec[0];
337*2822fc04SHerbert Dürr 			}
338*2822fc04SHerbert Dürr 		}
339*2822fc04SHerbert Dürr 
340*2822fc04SHerbert Dürr 		// get the details for each interesting glyph
341*2822fc04SHerbert Dürr 		// TODO: handle nLen>1
342*2822fc04SHerbert Dürr 		for(; (--nLen >= 0) && (nSubIndex < nGlyphsInRun); ++nSubIndex, ++nStart )
343*2822fc04SHerbert Dürr 		{
344*2822fc04SHerbert Dürr 			// convert glyph details for VCL
345*2822fc04SHerbert Dürr 			*(pOutGlyphIds++) = pCGGlyphIdx[ nSubIndex ];
346*2822fc04SHerbert Dürr 			if( pGlyphAdvances )
347*2822fc04SHerbert Dürr 				*(pGlyphAdvances++) = pCGGlyphAdvs[ nSubIndex ].width;
348*2822fc04SHerbert Dürr 			if( pCharIndexes )
349*2822fc04SHerbert Dürr 				*(pCharIndexes++) = pCGGlyphStrIdx[ nSubIndex] + mnMinCharPos;
350*2822fc04SHerbert Dürr 			if( !nCount++ ) {
351*2822fc04SHerbert Dürr 				const CGPoint& rCurPos = pCGGlyphPos[ nSubIndex ];
352*2822fc04SHerbert Dürr 				rPos = GetDrawPosition( Point( mfFontScale * rCurPos.x, mfFontScale * rCurPos.y) );
353*2822fc04SHerbert Dürr 			}
354*2822fc04SHerbert Dürr 		}
355*2822fc04SHerbert Dürr 		nSubIndex = 0; // prepare for the next glyph run
356*2822fc04SHerbert Dürr 		break; // TODO: handle nLen>1
357*2822fc04SHerbert Dürr 	}
358*2822fc04SHerbert Dürr 
359*2822fc04SHerbert Dürr 	return nCount;
360*2822fc04SHerbert Dürr }
361*2822fc04SHerbert Dürr 
362*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
363*2822fc04SHerbert Dürr 
364*2822fc04SHerbert Dürr long CTLayout::GetTextWidth() const
365*2822fc04SHerbert Dürr {
366*2822fc04SHerbert Dürr 	if( (mnCharCount <= 0) || !mpCTLine )
367*2822fc04SHerbert Dürr 		return 0;
368*2822fc04SHerbert Dürr 
369*2822fc04SHerbert Dürr 	if( mfCachedWidth < 0.0 )
370*2822fc04SHerbert Dürr 		mfCachedWidth = CTLineGetTypographicBounds( mpCTLine, NULL, NULL, NULL );
371*2822fc04SHerbert Dürr 
372*2822fc04SHerbert Dürr 	const long nScaledWidth = lrint( mfFontScale * mfCachedWidth );
373*2822fc04SHerbert Dürr 	return nScaledWidth;
374*2822fc04SHerbert Dürr }
375*2822fc04SHerbert Dürr 
376*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
377*2822fc04SHerbert Dürr 
378*2822fc04SHerbert Dürr long CTLayout::FillDXArray( sal_Int32* pDXArray ) const
379*2822fc04SHerbert Dürr {
380*2822fc04SHerbert Dürr 	// short circuit requests which don't need full details
381*2822fc04SHerbert Dürr 	if( !pDXArray )
382*2822fc04SHerbert Dürr 		return GetTextWidth();
383*2822fc04SHerbert Dürr 
384*2822fc04SHerbert Dürr 	long nPixWidth = GetTextWidth();
385*2822fc04SHerbert Dürr 	if( pDXArray ) {
386*2822fc04SHerbert Dürr 		// initialize the result array
387*2822fc04SHerbert Dürr 		for( int i = 0; i < mnCharCount; ++i)
388*2822fc04SHerbert Dürr 			pDXArray[i] = 0;
389*2822fc04SHerbert Dürr 		// handle each glyph run
390*2822fc04SHerbert Dürr 		CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine );
391*2822fc04SHerbert Dürr 		const int nRunCount = CFArrayGetCount( aGlyphRuns );
392*2822fc04SHerbert Dürr 		typedef std::vector<CGSize> CGSizeVector;
393*2822fc04SHerbert Dürr 		CGSizeVector aSizeVec;
394*2822fc04SHerbert Dürr 		typedef std::vector<CFIndex> CFIndexVector;
395*2822fc04SHerbert Dürr 		CFIndexVector aIndexVec;
396*2822fc04SHerbert Dürr 		for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) {
397*2822fc04SHerbert Dürr 			CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex );
398*2822fc04SHerbert Dürr 			const CFIndex nGlyphCount = CTRunGetGlyphCount( pGlyphRun );
399*2822fc04SHerbert Dürr 			const CFRange aFullRange = CFRangeMake( 0, nGlyphCount );
400*2822fc04SHerbert Dürr 			aSizeVec.reserve( nGlyphCount );
401*2822fc04SHerbert Dürr 			aIndexVec.reserve( nGlyphCount );
402*2822fc04SHerbert Dürr 			CTRunGetAdvances( pGlyphRun, aFullRange, &aSizeVec[0] );
403*2822fc04SHerbert Dürr 			CTRunGetStringIndices( pGlyphRun, aFullRange, &aIndexVec[0] );
404*2822fc04SHerbert Dürr 			for( int i = 0; i != nGlyphCount; ++i ) {
405*2822fc04SHerbert Dürr 				const int nRelIdx = aIndexVec[i];
406*2822fc04SHerbert Dürr 				pDXArray[ nRelIdx ] += aSizeVec[i].width;
407*2822fc04SHerbert Dürr 			}
408*2822fc04SHerbert Dürr 		}
409*2822fc04SHerbert Dürr 	}
410*2822fc04SHerbert Dürr 
411*2822fc04SHerbert Dürr 	return nPixWidth;
412*2822fc04SHerbert Dürr }
413*2822fc04SHerbert Dürr 
414*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
415*2822fc04SHerbert Dürr 
416*2822fc04SHerbert Dürr int CTLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
417*2822fc04SHerbert Dürr {
418*2822fc04SHerbert Dürr 	if( !mpCTLine )
419*2822fc04SHerbert Dürr 		return STRING_LEN;
420*2822fc04SHerbert Dürr 
421*2822fc04SHerbert Dürr 	CTTypesetterRef aCTTypeSetter = CTTypesetterCreateWithAttributedString( mpAttrString );
422*2822fc04SHerbert Dürr 	const double fCTMaxWidth = (double)nMaxWidth / (nFactor * mfFontScale);
423*2822fc04SHerbert Dürr 	CFIndex nIndex = CTTypesetterSuggestClusterBreak( aCTTypeSetter, 0, fCTMaxWidth );
424*2822fc04SHerbert Dürr 	if( nIndex >= mnCharCount )
425*2822fc04SHerbert Dürr 		return STRING_LEN;
426*2822fc04SHerbert Dürr 
427*2822fc04SHerbert Dürr 	nIndex += mnMinCharPos;
428*2822fc04SHerbert Dürr 	return (int)nIndex;
429*2822fc04SHerbert Dürr }
430*2822fc04SHerbert Dürr 
431*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
432*2822fc04SHerbert Dürr 
433*2822fc04SHerbert Dürr void CTLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
434*2822fc04SHerbert Dürr {
435*2822fc04SHerbert Dürr 	DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
436*2822fc04SHerbert Dürr 		"CTLayout::GetCaretPositions() : invalid number of caret pairs requested");
437*2822fc04SHerbert Dürr 
438*2822fc04SHerbert Dürr 	// initialize the caret positions
439*2822fc04SHerbert Dürr 	for( int i = 0; i < nMaxIndex; ++i )
440*2822fc04SHerbert Dürr 		pCaretXArray[ i ] = -1;
441*2822fc04SHerbert Dürr 
442*2822fc04SHerbert Dürr 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
443*2822fc04SHerbert Dürr 	for( int n = 0; n <= mnCharCount; ++n )
444*2822fc04SHerbert Dürr 	{
445*2822fc04SHerbert Dürr 		// measure the characters cursor position
446*2822fc04SHerbert Dürr 		CGFloat fPos2 = -1;
447*2822fc04SHerbert Dürr 		const CGFloat fPos1 = rCT.LineGetOffsetForStringIndex( mpCTLine, n, &fPos2 );
448*2822fc04SHerbert Dürr 		(void)fPos2; // TODO: split cursor at line direction change
449*2822fc04SHerbert Dürr 		// update previous trailing position
450*2822fc04SHerbert Dürr 		if( n > 0 )
451*2822fc04SHerbert Dürr 			pCaretXArray[ 2*n-1 ] = lrint( fPos1 * mfFontScale );
452*2822fc04SHerbert Dürr 		// update current leading position
453*2822fc04SHerbert Dürr 		if( 2*n >= nMaxIndex )
454*2822fc04SHerbert Dürr 			break;
455*2822fc04SHerbert Dürr 		pCaretXArray[ 2*n+0 ] = lrint( fPos1 * mfFontScale );
456*2822fc04SHerbert Dürr 	}
457*2822fc04SHerbert Dürr }
458*2822fc04SHerbert Dürr 
459*2822fc04SHerbert Dürr // -----------------------------------------------------------------------
460*2822fc04SHerbert Dürr 
461*2822fc04SHerbert Dürr bool CTLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rVCLRect ) const
462*2822fc04SHerbert Dürr {
463*2822fc04SHerbert Dürr 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
464*2822fc04SHerbert Dürr 	CGRect aMacRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext );
465*2822fc04SHerbert Dürr 	CGPoint aMacPos = CGContextGetTextPosition( rAquaGraphics.mrContext );
466*2822fc04SHerbert Dürr 	aMacRect.origin.x -= aMacPos.x;
467*2822fc04SHerbert Dürr 	aMacRect.origin.y -= aMacPos.y;
468*2822fc04SHerbert Dürr 
469*2822fc04SHerbert Dürr 	const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
470*2822fc04SHerbert Dürr 
471*2822fc04SHerbert Dürr 	// CoreText top-bottom are vertically flipped from a VCL aspect
472*2822fc04SHerbert Dürr 	rVCLRect.Left()   = aPos.X() + mfFontScale * aMacRect.origin.x;
473*2822fc04SHerbert Dürr 	rVCLRect.Right()  = aPos.X() + mfFontScale * (aMacRect.origin.x + aMacRect.size.width);
474*2822fc04SHerbert Dürr 	rVCLRect.Bottom() = aPos.Y() - mfFontScale * aMacRect.origin.y;
475*2822fc04SHerbert Dürr 	rVCLRect.Top()    = aPos.Y() - mfFontScale * (aMacRect.origin.y + aMacRect.size.height);
476*2822fc04SHerbert Dürr 	return true;
477*2822fc04SHerbert Dürr }
478*2822fc04SHerbert Dürr 
479*2822fc04SHerbert Dürr // =======================================================================
480*2822fc04SHerbert Dürr 
481*2822fc04SHerbert Dürr // glyph fallback is supported directly by Aqua
482*2822fc04SHerbert Dürr // so methods used only by MultiSalLayout can be dummy implementated
483*2822fc04SHerbert Dürr bool CTLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
484*2822fc04SHerbert Dürr void CTLayout::InitFont() const {}
485*2822fc04SHerbert Dürr void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
486*2822fc04SHerbert Dürr void CTLayout::DropGlyph( int /*nStart*/ ) {}
487*2822fc04SHerbert Dürr void CTLayout::Simplify( bool /*bIsBase*/ ) {}
488*2822fc04SHerbert Dürr 
489*2822fc04SHerbert Dürr // get the ImplFontData for a glyph fallback font
490*2822fc04SHerbert Dürr // for a glyphid that was returned by CTLayout::GetNextGlyphs()
491*2822fc04SHerbert Dürr const ImplFontData* CTLayout::GetFallbackFontData( sal_GlyphId /*aGlyphId*/ ) const
492*2822fc04SHerbert Dürr {
493*2822fc04SHerbert Dürr #if 0
494*2822fc04SHerbert Dürr 	// check if any fallback fonts were needed
495*2822fc04SHerbert Dürr 	if( !mpFallbackInfo )
496*2822fc04SHerbert Dürr 		return NULL;
497*2822fc04SHerbert Dürr 	// check if the current glyph needs a fallback font
498*2822fc04SHerbert Dürr 	int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
499*2822fc04SHerbert Dürr 	if( !nFallbackLevel )
500*2822fc04SHerbert Dürr 		return NULL;
501*2822fc04SHerbert Dürr 	pFallbackFont = mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
502*2822fc04SHerbert Dürr #else
503*2822fc04SHerbert Dürr 	// let CoreText's font cascading handle glyph fallback
504*2822fc04SHerbert Dürr 	const ImplFontData* pFallbackFont = NULL;
505*2822fc04SHerbert Dürr #endif
506*2822fc04SHerbert Dürr 	return pFallbackFont;
507*2822fc04SHerbert Dürr }
508*2822fc04SHerbert Dürr 
509*2822fc04SHerbert Dürr // =======================================================================
510*2822fc04SHerbert Dürr 
511*2822fc04SHerbert Dürr SalLayout* CTTextStyle::GetTextLayout( void ) const
512*2822fc04SHerbert Dürr {
513*2822fc04SHerbert Dürr 	return new CTLayout( this);
514*2822fc04SHerbert Dürr }
515*2822fc04SHerbert Dürr 
516*2822fc04SHerbert Dürr // =======================================================================
517*2822fc04SHerbert Dürr 
518