xref: /aoo41x/main/vcl/aqua/source/gdi/ctlayout.cxx (revision 29f7de43)
12822fc04SHerbert Dürr /**************************************************************
22822fc04SHerbert Dürr  *
32822fc04SHerbert Dürr  * Licensed to the Apache Software Foundation (ASF) under one
42822fc04SHerbert Dürr  * or more contributor license agreements.  See the NOTICE file
52822fc04SHerbert Dürr  * distributed with this work for additional information
62822fc04SHerbert Dürr  * regarding copyright ownership.  The ASF licenses this file
72822fc04SHerbert Dürr  * to you under the Apache License, Version 2.0 (the
82822fc04SHerbert Dürr  * "License"); you may not use this file except in compliance
92822fc04SHerbert Dürr  * with the License.  You may obtain a copy of the License at
102822fc04SHerbert Dürr  *
112822fc04SHerbert Dürr  *   http://www.apache.org/licenses/LICENSE-2.0
122822fc04SHerbert Dürr  *
132822fc04SHerbert Dürr  * Unless required by applicable law or agreed to in writing,
142822fc04SHerbert Dürr  * software distributed under the License is distributed on an
152822fc04SHerbert Dürr  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
162822fc04SHerbert Dürr  * KIND, either express or implied.  See the License for the
172822fc04SHerbert Dürr  * specific language governing permissions and limitations
182822fc04SHerbert Dürr  * under the License.
192822fc04SHerbert Dürr  *
202822fc04SHerbert Dürr  *************************************************************/
212822fc04SHerbert Dürr 
222822fc04SHerbert Dürr #include "ctfonts.hxx"
232822fc04SHerbert Dürr 
242822fc04SHerbert Dürr // =======================================================================
252822fc04SHerbert Dürr 
262822fc04SHerbert Dürr class CTLayout
272822fc04SHerbert Dürr :	public SalLayout
282822fc04SHerbert Dürr {
292822fc04SHerbert Dürr public:
302822fc04SHerbert Dürr 	explicit        CTLayout( const CTTextStyle* );
312822fc04SHerbert Dürr 	virtual         ~CTLayout( void );
322822fc04SHerbert Dürr 
332822fc04SHerbert Dürr 	virtual bool	LayoutText( ImplLayoutArgs& );
342822fc04SHerbert Dürr 	virtual void	AdjustLayout( ImplLayoutArgs& );
352822fc04SHerbert Dürr 	virtual void	DrawText( SalGraphics& ) const;
362822fc04SHerbert Dürr 
372822fc04SHerbert Dürr 	virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
382822fc04SHerbert Dürr 						sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
392822fc04SHerbert Dürr 
402822fc04SHerbert Dürr 	virtual long    GetTextWidth() const;
412822fc04SHerbert Dürr 	virtual long    FillDXArray( sal_Int32* pDXArray ) const;
422822fc04SHerbert Dürr 	virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
432822fc04SHerbert Dürr 	virtual void    GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const;
442822fc04SHerbert Dürr 	virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
452822fc04SHerbert Dürr 	virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
462822fc04SHerbert Dürr 
472822fc04SHerbert Dürr 	const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
482822fc04SHerbert Dürr 
492822fc04SHerbert Dürr 	virtual void	InitFont( void) const;
502822fc04SHerbert Dürr 	virtual void	MoveGlyph( int nStart, long nNewXPos );
512822fc04SHerbert Dürr 	virtual void	DropGlyph( int nStart );
522822fc04SHerbert Dürr 	virtual void	Simplify( bool bIsBase );
532822fc04SHerbert Dürr 
542822fc04SHerbert Dürr private:
552822fc04SHerbert Dürr 	// CoreText specific objects
5699a05e04SHerbert Dürr 	CFMutableDictionaryRef mpStyleDict;
572822fc04SHerbert Dürr 	CFAttributedStringRef mpAttrString;
582822fc04SHerbert Dürr 	CTLineRef mpCTLine;
592822fc04SHerbert Dürr 
602822fc04SHerbert Dürr 	int mnCharCount;		// ==mnEndCharPos-mnMinCharPos
6161277561SHerbert Dürr 	int mnTrailingSpaceCount;
6261277561SHerbert Dürr 	double mfTrailingSpaceWidth;	// preserves the width of stripped-off trailing space
6399a05e04SHerbert Dürr 
642822fc04SHerbert Dürr 	// to prevent overflows
652822fc04SHerbert Dürr 	// font requests get size limited by downscaling huge fonts
662822fc04SHerbert Dürr 	// in these cases the font scale becomes something bigger than 1.0
672822fc04SHerbert Dürr 	float mfFontScale; // TODO: does CoreText have a font size limit?
682822fc04SHerbert Dürr 
6999a05e04SHerbert Dürr 	CGFloat mfFontRotation; // text direction angle (in radians)
70beee6bf3SHerbert Dürr 	CGFloat mfFontStretch;  // <1.0: font gets squeezed, >1.0: font gets stretched
7199a05e04SHerbert Dürr 
722822fc04SHerbert Dürr 	// cached details about the resulting layout
732822fc04SHerbert Dürr 	// mutable members since these details are all lazy initialized
742822fc04SHerbert Dürr 	mutable double	mfCachedWidth;			// cached value of resulting typographical width
752822fc04SHerbert Dürr 
762822fc04SHerbert Dürr 	// x-offset relative to layout origin
772822fc04SHerbert Dürr 	// currently only used in RTL-layouts
782822fc04SHerbert Dürr 	mutable long	mnBaseAdv;
792822fc04SHerbert Dürr };
802822fc04SHerbert Dürr 
812822fc04SHerbert Dürr // =======================================================================
822822fc04SHerbert Dürr 
CTLayout(const CTTextStyle * pTextStyle)832822fc04SHerbert Dürr CTLayout::CTLayout( const CTTextStyle* pTextStyle )
8499a05e04SHerbert Dürr :	mpStyleDict( pTextStyle->GetStyleDict() )
852822fc04SHerbert Dürr ,	mpAttrString( NULL )
862822fc04SHerbert Dürr ,	mpCTLine( NULL )
872822fc04SHerbert Dürr ,	mnCharCount( 0 )
8861277561SHerbert Dürr ,	mnTrailingSpaceCount( 0 )
8961277561SHerbert Dürr ,	mfTrailingSpaceWidth( 0.0 )
902822fc04SHerbert Dürr ,	mfFontScale( pTextStyle->mfFontScale )
9199a05e04SHerbert Dürr ,	mfFontRotation( pTextStyle->mfFontRotation )
9299a05e04SHerbert Dürr ,	mfFontStretch( pTextStyle->mfFontStretch )
932822fc04SHerbert Dürr ,	mfCachedWidth( -1 )
942822fc04SHerbert Dürr ,	mnBaseAdv( 0 )
952822fc04SHerbert Dürr {
9699a05e04SHerbert Dürr 	CFRetain( mpStyleDict );
972822fc04SHerbert Dürr }
982822fc04SHerbert Dürr 
992822fc04SHerbert Dürr // -----------------------------------------------------------------------
1002822fc04SHerbert Dürr 
~CTLayout()1012822fc04SHerbert Dürr CTLayout::~CTLayout()
1022822fc04SHerbert Dürr {
1032822fc04SHerbert Dürr 	if( mpCTLine )
1042822fc04SHerbert Dürr 		CFRelease( mpCTLine );
1052822fc04SHerbert Dürr 	if( mpAttrString )
1062822fc04SHerbert Dürr 		CFRelease( mpAttrString );
10799a05e04SHerbert Dürr 	CFRelease( mpStyleDict );
1082822fc04SHerbert Dürr }
1092822fc04SHerbert Dürr 
1102822fc04SHerbert Dürr // -----------------------------------------------------------------------
1112822fc04SHerbert Dürr 
LayoutText(ImplLayoutArgs & rArgs)1122822fc04SHerbert Dürr bool CTLayout::LayoutText( ImplLayoutArgs& rArgs )
1132822fc04SHerbert Dürr {
1145b651169SHerbert Dürr 	// release an eventual older layout
1152822fc04SHerbert Dürr 	if( mpAttrString )
1162822fc04SHerbert Dürr 		CFRelease( mpAttrString );
1172822fc04SHerbert Dürr 	mpAttrString = NULL;
1182822fc04SHerbert Dürr 	if( mpCTLine )
1192822fc04SHerbert Dürr 		CFRelease( mpCTLine );
1202822fc04SHerbert Dürr 	mpCTLine = NULL;
1212822fc04SHerbert Dürr 
1225b651169SHerbert Dürr 	// initialize the new layout
1232822fc04SHerbert Dürr 	SalLayout::AdjustLayout( rArgs );
1242822fc04SHerbert Dürr 	mnCharCount = mnEndCharPos - mnMinCharPos;
1252822fc04SHerbert Dürr 
1262822fc04SHerbert Dürr 	// short circuit if there is nothing to do
1272822fc04SHerbert Dürr 	if( mnCharCount <= 0 )
1282822fc04SHerbert Dürr 		return false;
1292822fc04SHerbert Dürr 
130250ab64bSHerbert Dürr 	// prepare the string to be layouted by CoreText
1312822fc04SHerbert Dürr 	CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos, mnCharCount, kCFAllocatorNull );
132250ab64bSHerbert Dürr 	// #i124375# force soft-hyphen visibility to meet the expectations of Writer+EditEngine
133250ab64bSHerbert Dürr 	if( CFStringFind( aCFText, (CFStringRef)@"\u00AD", 0).length > 0 )
134250ab64bSHerbert Dürr 	{
135250ab64bSHerbert Dürr 		NSString* pDashStr = [(NSString*)aCFText stringByReplacingOccurrencesOfString: @"\u00AD" withString: @"-"];
136250ab64bSHerbert Dürr 		aCFText = CFStringCreateCopy( NULL, (CFStringRef)pDashStr );
137250ab64bSHerbert Dürr 	}
138250ab64bSHerbert Dürr 
139250ab64bSHerbert Dürr 	// create the CoreText line layout using the requested text style
14099a05e04SHerbert Dürr 	mpAttrString = CFAttributedStringCreate( NULL, aCFText, mpStyleDict );
1412822fc04SHerbert Dürr 	mpCTLine = CTLineCreateWithAttributedString( mpAttrString );
1422822fc04SHerbert Dürr 	CFRelease( aCFText);
1432822fc04SHerbert Dürr 
1442822fc04SHerbert Dürr 	// get info about trailing whitespace to prepare for text justification in AdjustLayout()
14561277561SHerbert Dürr 	mnTrailingSpaceCount = 0;
14661277561SHerbert Dürr 	for( int i = mnEndCharPos; --i >= mnMinCharPos; ++mnTrailingSpaceCount )
14761277561SHerbert Dürr 		if( !IsSpacingGlyph( rArgs.mpStr[i] | GF_ISCHAR )
14861277561SHerbert Dürr 		&&  (rArgs.mpStr[i] != 0x00A0) )
1492822fc04SHerbert Dürr 			break;
1502822fc04SHerbert Dürr 	return true;
1512822fc04SHerbert Dürr }
1522822fc04SHerbert Dürr 
1532822fc04SHerbert Dürr // -----------------------------------------------------------------------
1542822fc04SHerbert Dürr 
AdjustLayout(ImplLayoutArgs & rArgs)1552822fc04SHerbert Dürr void CTLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1562822fc04SHerbert Dürr {
1572822fc04SHerbert Dürr 	if( !mpCTLine)
1582822fc04SHerbert Dürr 		return;
1592822fc04SHerbert Dürr 
1602822fc04SHerbert Dürr 	int nPixelWidth = rArgs.mnLayoutWidth;
161bca19674SHerbert Dürr 	if( rArgs.mpDXArray )
1622822fc04SHerbert Dürr 	{
1632822fc04SHerbert Dürr 		// for now we are only interested in the layout width
1642822fc04SHerbert Dürr 		// TODO: use all mpDXArray elements for layouting
165bca19674SHerbert Dürr 		nPixelWidth = rArgs.mpDXArray[ mnCharCount-1 ];
1662822fc04SHerbert Dürr 	}
16761277561SHerbert Dürr 	else if( !nPixelWidth ) // short-circuit if there is nothing to adjust
16861277561SHerbert Dürr 		return;
1692822fc04SHerbert Dürr 
1703e4cd16fSHerbert Dürr 	// short-circuit when justifying an all-whitespace string
17161277561SHerbert Dürr 	if( mnTrailingSpaceCount >= mnCharCount)
1723e4cd16fSHerbert Dürr 	{
173bca19674SHerbert Dürr 		mfCachedWidth = nPixelWidth / mfFontScale;
1743e4cd16fSHerbert Dürr 		return;
1753e4cd16fSHerbert Dürr 	}
1763e4cd16fSHerbert Dürr 
1772822fc04SHerbert Dürr 	// return early if there is nothing to do
1782822fc04SHerbert Dürr 	if( nPixelWidth <= 0 )
1792822fc04SHerbert Dürr 		return;
1802822fc04SHerbert Dürr 
1812822fc04SHerbert Dürr 	// HACK: justification requests which change the width by just one pixel are probably
1822822fc04SHerbert Dürr 	// #i86038# introduced by lossy conversions between integer based coordinate system
183bca19674SHerbert Dürr 	const int nOrigWidth = GetTextWidth();
1842822fc04SHerbert Dürr 	if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
1852822fc04SHerbert Dürr 		return;
1862822fc04SHerbert Dürr 
1872822fc04SHerbert Dürr 	// if the text to be justified has whitespace in it then
1882822fc04SHerbert Dürr 	// - Writer goes crazy with its HalfSpace magic
189bca19674SHerbert Dürr 	// - CoreText handles spaces specially (in particular at the text end)
19061277561SHerbert Dürr 	if( mnTrailingSpaceCount ) {
191bca19674SHerbert Dürr 		int nTrailingSpaceWidth = 0;
192bca19674SHerbert Dürr 		if( rArgs.mpDXArray) {
193bca19674SHerbert Dürr 			const int nFullPixWidth = nPixelWidth;
19461277561SHerbert Dürr 			nPixelWidth = rArgs.mpDXArray[ mnCharCount-1-mnTrailingSpaceCount ];
195bca19674SHerbert Dürr 			nTrailingSpaceWidth = nFullPixWidth - nPixelWidth;
19661277561SHerbert Dürr 			mfTrailingSpaceWidth = nTrailingSpaceWidth;
197bca19674SHerbert Dürr 		} else {
19861277561SHerbert Dürr 			if( mfTrailingSpaceWidth <= 0.0 )
19961277561SHerbert Dürr 				mfTrailingSpaceWidth = CTLineGetTrailingWhitespaceWidth( mpCTLine );
20061277561SHerbert Dürr 			nTrailingSpaceWidth = rint( mfTrailingSpaceWidth );
20161277561SHerbert Dürr 			nPixelWidth -= nTrailingSpaceWidth;
202bca19674SHerbert Dürr 		}
203bca19674SHerbert Dürr 		if( nPixelWidth <= 0 )
2042822fc04SHerbert Dürr 			return;
205bca19674SHerbert Dürr 
2062822fc04SHerbert Dürr 		// recreate the CoreText line layout without trailing spaces
2072822fc04SHerbert Dürr 		CFRelease( mpCTLine );
2082822fc04SHerbert Dürr 		CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos,
20961277561SHerbert Dürr 			mnCharCount - mnTrailingSpaceCount, kCFAllocatorNull );
21099a05e04SHerbert Dürr 		CFAttributedStringRef pAttrStr = CFAttributedStringCreate( NULL, aCFText, mpStyleDict );
2112822fc04SHerbert Dürr 		mpCTLine = CTLineCreateWithAttributedString( pAttrStr );
2122822fc04SHerbert Dürr 		CFRelease( aCFText);
2132822fc04SHerbert Dürr 		CFRelease( pAttrStr );
214bca19674SHerbert Dürr 
215bca19674SHerbert Dürr 		// in RTL-layouts trailing spaces are leftmost
216bca19674SHerbert Dürr 		// TODO: use BiDi-algorithm to thoroughly check this assumption
217bca19674SHerbert Dürr 		if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
218bca19674SHerbert Dürr 			mnBaseAdv = nTrailingSpaceWidth;
2192822fc04SHerbert Dürr 	}
2202822fc04SHerbert Dürr 
221bca19674SHerbert Dürr 	const double fAdjustedWidth = nPixelWidth / mfFontScale;
22261277561SHerbert Dürr 	CTLineRef pNewCTLine = CTLineCreateJustifiedLine( mpCTLine, 1.0, fAdjustedWidth );
2232822fc04SHerbert Dürr 	if( !pNewCTLine ) { // CTLineCreateJustifiedLine can and does fail
2242822fc04SHerbert Dürr 		// handle failure by keeping the unjustified layout
2252822fc04SHerbert Dürr 		// TODO: a better solution such as
2262822fc04SHerbert Dürr 		// - forcing glyph overlap
2272822fc04SHerbert Dürr 		// - changing the font size
2282822fc04SHerbert Dürr 		// - changing the CTM matrix
2292822fc04SHerbert Dürr 		return;
2302822fc04SHerbert Dürr 	}
2312822fc04SHerbert Dürr 	CFRelease( mpCTLine );
2322822fc04SHerbert Dürr 	mpCTLine = pNewCTLine;
23361277561SHerbert Dürr 	mfCachedWidth = fAdjustedWidth + mfTrailingSpaceWidth;
2342822fc04SHerbert Dürr }
2352822fc04SHerbert Dürr 
2362822fc04SHerbert Dürr // -----------------------------------------------------------------------
2372822fc04SHerbert Dürr 
DrawText(SalGraphics & rGraphics) const2382822fc04SHerbert Dürr void CTLayout::DrawText( SalGraphics& rGraphics ) const
2392822fc04SHerbert Dürr {
2402822fc04SHerbert Dürr 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
2415b651169SHerbert Dürr 
2422822fc04SHerbert Dürr 	// short circuit if there is nothing to do
2432822fc04SHerbert Dürr 	if( (mnCharCount <= 0)
2442822fc04SHerbert Dürr 	||  !rAquaGraphics.CheckContext() )
2452822fc04SHerbert Dürr 		return;
2465b651169SHerbert Dürr 
2472822fc04SHerbert Dürr 	// the view is vertically flipped => flipped glyphs
2482822fc04SHerbert Dürr 	// so apply a temporary transformation that it flips back
2492822fc04SHerbert Dürr 	// also compensate if the font was size limited
2502822fc04SHerbert Dürr 	CGContextSaveGState( rAquaGraphics.mrContext );
2512822fc04SHerbert Dürr 	CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
2522822fc04SHerbert Dürr 	CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
2532822fc04SHerbert Dürr 
2545b651169SHerbert Dürr 	// set the text transformation (e.g. position)
2552822fc04SHerbert Dürr 	const Point aVclPos = GetDrawPosition( Point(mnBaseAdv,0) );
2562822fc04SHerbert Dürr 	CGPoint aTextPos = { +aVclPos.X()/mfFontScale, -aVclPos.Y()/mfFontScale };
2572822fc04SHerbert Dürr 
25899a05e04SHerbert Dürr 	if( mfFontRotation != 0.0 )
2592822fc04SHerbert Dürr 	{
26099a05e04SHerbert Dürr 		CGContextRotateCTM( rAquaGraphics.mrContext, +mfFontRotation );
2612822fc04SHerbert Dürr 
26299a05e04SHerbert Dürr 		const CGAffineTransform aInvMatrix = CGAffineTransformMakeRotation( -mfFontRotation );
2632822fc04SHerbert Dürr 		aTextPos = CGPointApplyAffineTransform( aTextPos, aInvMatrix );
2642822fc04SHerbert Dürr 	}
2652822fc04SHerbert Dürr 
2662822fc04SHerbert Dürr 	CGContextSetTextPosition( rAquaGraphics.mrContext, aTextPos.x, aTextPos.y );
2675b651169SHerbert Dürr 
268*29f7de43SHerbert Dürr 	// request an update of the to-be-changed window area
2692822fc04SHerbert Dürr 	if( rAquaGraphics.IsWindowGraphics() )
2702822fc04SHerbert Dürr 	{
2712822fc04SHerbert Dürr 		const CGRect aInkRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext );
2722822fc04SHerbert Dürr 		const CGRect aRefreshRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aInkRect );
2732822fc04SHerbert Dürr 		rAquaGraphics.RefreshRect( aRefreshRect );
2742822fc04SHerbert Dürr 	}
2752822fc04SHerbert Dürr 
276*29f7de43SHerbert Dürr 	// set the text color as fill color (see kCTForegroundColorFromContextAttributeName)
277*29f7de43SHerbert Dürr 	CGContextSetFillColor( rAquaGraphics.mrContext, rAquaGraphics.maTextColor.AsArray() );
278*29f7de43SHerbert Dürr 
279*29f7de43SHerbert Dürr 	// draw the text
280*29f7de43SHerbert Dürr 	CTLineDraw( mpCTLine, rAquaGraphics.mrContext );
281*29f7de43SHerbert Dürr 
2822822fc04SHerbert Dürr 	// restore the original graphic context transformations
2832822fc04SHerbert Dürr 	CGContextRestoreGState( rAquaGraphics.mrContext );
2842822fc04SHerbert Dürr }
2852822fc04SHerbert Dürr 
2862822fc04SHerbert Dürr // -----------------------------------------------------------------------
2872822fc04SHerbert Dürr 
GetNextGlyphs(int nLen,sal_GlyphId * pOutGlyphIds,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvances,int * pCharIndexes) const2882822fc04SHerbert Dürr int CTLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
2892822fc04SHerbert Dürr 	sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
2902822fc04SHerbert Dürr {
2912822fc04SHerbert Dürr 	if( !mpCTLine )
2922822fc04SHerbert Dürr 		return 0;
2932822fc04SHerbert Dürr 
2942822fc04SHerbert Dürr 	if( nStart < 0 ) // first glyph requested?
2952822fc04SHerbert Dürr 		nStart = 0;
2962822fc04SHerbert Dürr 	nLen = 1; // TODO: handle nLen>1 below
2972822fc04SHerbert Dürr 
2982822fc04SHerbert Dürr 	// prepare to iterate over the glyph runs
2992822fc04SHerbert Dürr 	int nCount = 0;
3002822fc04SHerbert Dürr 	int nSubIndex = nStart;
3012822fc04SHerbert Dürr 
3022822fc04SHerbert Dürr 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
3032822fc04SHerbert Dürr 	typedef std::vector<CGGlyph> CGGlyphVector;
3042822fc04SHerbert Dürr 	typedef std::vector<CGPoint> CGPointVector;
3052822fc04SHerbert Dürr 	typedef std::vector<CGSize>  CGSizeVector;
3062822fc04SHerbert Dürr 	typedef std::vector<CFIndex> CFIndexVector;
3072822fc04SHerbert Dürr 	CGGlyphVector aCGGlyphVec;
3082822fc04SHerbert Dürr 	CGPointVector aCGPointVec;
3092822fc04SHerbert Dürr 	CGSizeVector  aCGSizeVec;
3102822fc04SHerbert Dürr 	CFIndexVector aCFIndexVec;
3112822fc04SHerbert Dürr 
3122822fc04SHerbert Dürr 	// TODO: iterate over cached layout
3132822fc04SHerbert Dürr 	CFArrayRef aGlyphRuns = rCT.LineGetGlyphRuns( mpCTLine );
3142822fc04SHerbert Dürr 	const int nRunCount = CFArrayGetCount( aGlyphRuns );
3152822fc04SHerbert Dürr 	for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) {
3162822fc04SHerbert Dürr 		CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex );
3172822fc04SHerbert Dürr 		const CFIndex nGlyphsInRun = rCT.RunGetGlyphCount( pGlyphRun );
3182822fc04SHerbert Dürr 		// skip to the first glyph run of interest
3192822fc04SHerbert Dürr 		if( nSubIndex >= nGlyphsInRun ) {
3202822fc04SHerbert Dürr 			nSubIndex -= nGlyphsInRun;
3212822fc04SHerbert Dürr 			continue;
3222822fc04SHerbert Dürr 		}
3232822fc04SHerbert Dürr 		const CFRange aFullRange = CFRangeMake( 0, nGlyphsInRun );
3242822fc04SHerbert Dürr 
3252822fc04SHerbert Dürr 		// get glyph run details
3262822fc04SHerbert Dürr 		const CGGlyph* pCGGlyphIdx = rCT.RunGetGlyphsPtr( pGlyphRun );
3272822fc04SHerbert Dürr 		if( !pCGGlyphIdx ) {
3282822fc04SHerbert Dürr 			aCGGlyphVec.reserve( nGlyphsInRun );
3292822fc04SHerbert Dürr 			CTRunGetGlyphs( pGlyphRun, aFullRange, &aCGGlyphVec[0] );
3302822fc04SHerbert Dürr 			pCGGlyphIdx = &aCGGlyphVec[0];
3312822fc04SHerbert Dürr 		}
3322822fc04SHerbert Dürr 		const CGPoint* pCGGlyphPos = rCT.RunGetPositionsPtr( pGlyphRun );
3332822fc04SHerbert Dürr 		if( !pCGGlyphPos ) {
3342822fc04SHerbert Dürr 			aCGPointVec.reserve( nGlyphsInRun );
3352822fc04SHerbert Dürr 			CTRunGetPositions( pGlyphRun, aFullRange, &aCGPointVec[0] );
3362822fc04SHerbert Dürr 			pCGGlyphPos = &aCGPointVec[0];
3372822fc04SHerbert Dürr 		}
3382822fc04SHerbert Dürr 
3392822fc04SHerbert Dürr 		const CGSize* pCGGlyphAdvs = NULL;
3402822fc04SHerbert Dürr 		if( pGlyphAdvances) {
3412822fc04SHerbert Dürr 			pCGGlyphAdvs = rCT.RunGetAdvancesPtr( pGlyphRun );
3422822fc04SHerbert Dürr 			if( !pCGGlyphAdvs) {
3432822fc04SHerbert Dürr 				aCGSizeVec.reserve( nGlyphsInRun );
3442822fc04SHerbert Dürr 				CTRunGetAdvances( pGlyphRun, aFullRange, &aCGSizeVec[0] );
3452822fc04SHerbert Dürr 				pCGGlyphAdvs = &aCGSizeVec[0];
3462822fc04SHerbert Dürr 			}
3472822fc04SHerbert Dürr 		}
3482822fc04SHerbert Dürr 
3492822fc04SHerbert Dürr 		const CFIndex* pCGGlyphStrIdx = NULL;
3502822fc04SHerbert Dürr 		if( pCharIndexes) {
3512822fc04SHerbert Dürr 			pCGGlyphStrIdx = rCT.RunGetStringIndicesPtr( pGlyphRun );
3522822fc04SHerbert Dürr 			if( !pCGGlyphStrIdx) {
3532822fc04SHerbert Dürr 				aCFIndexVec.reserve( nGlyphsInRun );
3542822fc04SHerbert Dürr 				CTRunGetStringIndices( pGlyphRun, aFullRange, &aCFIndexVec[0] );
3552822fc04SHerbert Dürr 				pCGGlyphStrIdx = &aCFIndexVec[0];
3562822fc04SHerbert Dürr 			}
3572822fc04SHerbert Dürr 		}
3582822fc04SHerbert Dürr 
3592822fc04SHerbert Dürr 		// get the details for each interesting glyph
3602822fc04SHerbert Dürr 		// TODO: handle nLen>1
3612822fc04SHerbert Dürr 		for(; (--nLen >= 0) && (nSubIndex < nGlyphsInRun); ++nSubIndex, ++nStart )
3622822fc04SHerbert Dürr 		{
3632822fc04SHerbert Dürr 			// convert glyph details for VCL
3642822fc04SHerbert Dürr 			*(pOutGlyphIds++) = pCGGlyphIdx[ nSubIndex ];
3652822fc04SHerbert Dürr 			if( pGlyphAdvances )
36699a05e04SHerbert Dürr 				*(pGlyphAdvances++) = mfFontStretch * pCGGlyphAdvs[ nSubIndex ].width;
3672822fc04SHerbert Dürr 			if( pCharIndexes )
3682822fc04SHerbert Dürr 				*(pCharIndexes++) = pCGGlyphStrIdx[ nSubIndex] + mnMinCharPos;
3692822fc04SHerbert Dürr 			if( !nCount++ ) {
3702822fc04SHerbert Dürr 				const CGPoint& rCurPos = pCGGlyphPos[ nSubIndex ];
37199a05e04SHerbert Dürr 				rPos = GetDrawPosition( Point( mfFontScale * mfFontStretch * rCurPos.x, mfFontScale * rCurPos.y) );
3722822fc04SHerbert Dürr 			}
3732822fc04SHerbert Dürr 		}
3742822fc04SHerbert Dürr 		nSubIndex = 0; // prepare for the next glyph run
3752822fc04SHerbert Dürr 		break; // TODO: handle nLen>1
3762822fc04SHerbert Dürr 	}
3772822fc04SHerbert Dürr 
3782822fc04SHerbert Dürr 	return nCount;
3792822fc04SHerbert Dürr }
3802822fc04SHerbert Dürr 
3812822fc04SHerbert Dürr // -----------------------------------------------------------------------
3822822fc04SHerbert Dürr 
GetTextWidth() const3832822fc04SHerbert Dürr long CTLayout::GetTextWidth() const
3842822fc04SHerbert Dürr {
3852822fc04SHerbert Dürr 	if( (mnCharCount <= 0) || !mpCTLine )
3862822fc04SHerbert Dürr 		return 0;
3872822fc04SHerbert Dürr 
3882822fc04SHerbert Dürr 	if( mfCachedWidth < 0.0 )
3892822fc04SHerbert Dürr 		mfCachedWidth = CTLineGetTypographicBounds( mpCTLine, NULL, NULL, NULL );
3902822fc04SHerbert Dürr 
3912822fc04SHerbert Dürr 	const long nScaledWidth = lrint( mfFontScale * mfCachedWidth );
3922822fc04SHerbert Dürr 	return nScaledWidth;
3932822fc04SHerbert Dürr }
3942822fc04SHerbert Dürr 
3952822fc04SHerbert Dürr // -----------------------------------------------------------------------
3962822fc04SHerbert Dürr 
FillDXArray(sal_Int32 * pDXArray) const3972822fc04SHerbert Dürr long CTLayout::FillDXArray( sal_Int32* pDXArray ) const
3982822fc04SHerbert Dürr {
3992822fc04SHerbert Dürr 	// short circuit requests which don't need full details
4002822fc04SHerbert Dürr 	if( !pDXArray )
4012822fc04SHerbert Dürr 		return GetTextWidth();
4022822fc04SHerbert Dürr 
4032822fc04SHerbert Dürr 	long nPixWidth = GetTextWidth();
4042822fc04SHerbert Dürr 	if( pDXArray ) {
405d237788cSHerbert Dürr 		// prepare the sub-pixel accurate logical-width array
406d237788cSHerbert Dürr 		::std::vector<float> aWidthVector( mnCharCount );
40761277561SHerbert Dürr 		if( mnTrailingSpaceCount && (mfTrailingSpaceWidth > 0.0) ) {
40861277561SHerbert Dürr 			const double fOneWidth = mfTrailingSpaceWidth / mnTrailingSpaceCount;
40961277561SHerbert Dürr 			for( int i = 1; i <= mnTrailingSpaceCount; ++i)
41061277561SHerbert Dürr 				aWidthVector[ mnCharCount - i ] = fOneWidth;
41161277561SHerbert Dürr 		}
41261277561SHerbert Dürr 		// measure advances in each glyph run
4132822fc04SHerbert Dürr 		CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine );
4142822fc04SHerbert Dürr 		const int nRunCount = CFArrayGetCount( aGlyphRuns );
4152822fc04SHerbert Dürr 		typedef std::vector<CGSize> CGSizeVector;
4162822fc04SHerbert Dürr 		CGSizeVector aSizeVec;
4172822fc04SHerbert Dürr 		typedef std::vector<CFIndex> CFIndexVector;
4182822fc04SHerbert Dürr 		CFIndexVector aIndexVec;
4192822fc04SHerbert Dürr 		for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) {
4202822fc04SHerbert Dürr 			CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex );
4212822fc04SHerbert Dürr 			const CFIndex nGlyphCount = CTRunGetGlyphCount( pGlyphRun );
4222822fc04SHerbert Dürr 			const CFRange aFullRange = CFRangeMake( 0, nGlyphCount );
423bca19674SHerbert Dürr 			aSizeVec.resize( nGlyphCount );
424bca19674SHerbert Dürr 			aIndexVec.resize( nGlyphCount );
4252822fc04SHerbert Dürr 			CTRunGetAdvances( pGlyphRun, aFullRange, &aSizeVec[0] );
4262822fc04SHerbert Dürr 			CTRunGetStringIndices( pGlyphRun, aFullRange, &aIndexVec[0] );
4272822fc04SHerbert Dürr 			for( int i = 0; i != nGlyphCount; ++i ) {
4282822fc04SHerbert Dürr 				const int nRelIdx = aIndexVec[i];
429d237788cSHerbert Dürr 				aWidthVector[nRelIdx] += aSizeVec[i].width;
430d237788cSHerbert Dürr  			}
431d237788cSHerbert Dürr  		}
432d237788cSHerbert Dürr 
433d237788cSHerbert Dürr 		// convert the sub-pixel accurate array into classic pDXArray integers
434d237788cSHerbert Dürr 		float fWidthSum = 0.0;
435d237788cSHerbert Dürr 		sal_Int32 nOldDX = 0;
436d237788cSHerbert Dürr 		for( int i = 0; i < mnCharCount; ++i) {
437d237788cSHerbert Dürr 			const sal_Int32 nNewDX = rint( fWidthSum += aWidthVector[i]);
438d237788cSHerbert Dürr 			pDXArray[i] = nNewDX - nOldDX;
439d237788cSHerbert Dürr 			nOldDX = nNewDX;
4402822fc04SHerbert Dürr 		}
4412822fc04SHerbert Dürr 	}
4422822fc04SHerbert Dürr 
4432822fc04SHerbert Dürr 	return nPixWidth;
4442822fc04SHerbert Dürr }
4452822fc04SHerbert Dürr 
4462822fc04SHerbert Dürr // -----------------------------------------------------------------------
4472822fc04SHerbert Dürr 
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const4482822fc04SHerbert Dürr int CTLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
4492822fc04SHerbert Dürr {
4502822fc04SHerbert Dürr 	if( !mpCTLine )
4512822fc04SHerbert Dürr 		return STRING_LEN;
4522822fc04SHerbert Dürr 
4532822fc04SHerbert Dürr 	CTTypesetterRef aCTTypeSetter = CTTypesetterCreateWithAttributedString( mpAttrString );
4542822fc04SHerbert Dürr 
455b5accd65SHerbert Dürr 	CFIndex nBestGuess = (nCharExtra >= 0) ? 0 : mnCharCount;
456b5accd65SHerbert Dürr 	for( int i = 1; i <= mnCharCount; i *= 2 )
457b5accd65SHerbert Dürr 	{
458b5accd65SHerbert Dürr 		// guess the target width considering char-extra expansion/condensation
459b5accd65SHerbert Dürr 		const long nTargetWidth = nMaxWidth - nBestGuess * nCharExtra;
460b5accd65SHerbert Dürr 		const double fCTMaxWidth = nTargetWidth / (nFactor * mfFontScale);
461b5accd65SHerbert Dürr 		// calculate the breaking index for the guessed target width
462b5accd65SHerbert Dürr 		const CFIndex nNewIndex = CTTypesetterSuggestClusterBreak( aCTTypeSetter, 0, fCTMaxWidth );
463b5accd65SHerbert Dürr 		if( nNewIndex >= mnCharCount ) {
464b5accd65SHerbert Dürr 			CFRelease( aCTTypeSetter );
465b5accd65SHerbert Dürr 			return STRING_LEN;
466b5accd65SHerbert Dürr 		}
467b5accd65SHerbert Dürr 		// check if the original extra-width guess was good
468b5accd65SHerbert Dürr 		if( !nCharExtra )
469b5accd65SHerbert Dürr 			nBestGuess = nNewIndex;
470b5accd65SHerbert Dürr 		if( nBestGuess == nNewIndex )
471b5accd65SHerbert Dürr 			break;
472b5accd65SHerbert Dürr 		// prepare another round for a different number of characters
473b5accd65SHerbert Dürr 		CFIndex nNewGuess = (nNewIndex + nBestGuess + 1) / 2;
474b5accd65SHerbert Dürr 		if( nNewGuess == nBestGuess )
475b5accd65SHerbert Dürr 			nNewGuess += (nNewIndex > nBestGuess) ? +1 : -1;
476b5accd65SHerbert Dürr 		nBestGuess = nNewGuess;
477b5accd65SHerbert Dürr 	}
478b5accd65SHerbert Dürr 
479b5accd65SHerbert Dürr 	// suggest the best fitting cluster break as breaking position
480b5accd65SHerbert Dürr 	CFRelease( aCTTypeSetter );
481b5accd65SHerbert Dürr 	const int nIndex = nBestGuess + mnMinCharPos;
482b5accd65SHerbert Dürr 	return nIndex;
4832822fc04SHerbert Dürr }
4842822fc04SHerbert Dürr 
4852822fc04SHerbert Dürr // -----------------------------------------------------------------------
4862822fc04SHerbert Dürr 
GetCaretPositions(int nMaxIndex,sal_Int32 * pCaretXArray) const4872822fc04SHerbert Dürr void CTLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
4882822fc04SHerbert Dürr {
4892822fc04SHerbert Dürr 	DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
4902822fc04SHerbert Dürr 		"CTLayout::GetCaretPositions() : invalid number of caret pairs requested");
4912822fc04SHerbert Dürr 
4922822fc04SHerbert Dürr 	// initialize the caret positions
4932822fc04SHerbert Dürr 	for( int i = 0; i < nMaxIndex; ++i )
4942822fc04SHerbert Dürr 		pCaretXArray[ i ] = -1;
4952822fc04SHerbert Dürr 
4962822fc04SHerbert Dürr 	const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
4972822fc04SHerbert Dürr 	for( int n = 0; n <= mnCharCount; ++n )
4982822fc04SHerbert Dürr 	{
4992822fc04SHerbert Dürr 		// measure the characters cursor position
5002822fc04SHerbert Dürr 		CGFloat fPos2 = -1;
5012822fc04SHerbert Dürr 		const CGFloat fPos1 = rCT.LineGetOffsetForStringIndex( mpCTLine, n, &fPos2 );
5022822fc04SHerbert Dürr 		(void)fPos2; // TODO: split cursor at line direction change
5032822fc04SHerbert Dürr 		// update previous trailing position
5042822fc04SHerbert Dürr 		if( n > 0 )
5052822fc04SHerbert Dürr 			pCaretXArray[ 2*n-1 ] = lrint( fPos1 * mfFontScale );
5062822fc04SHerbert Dürr 		// update current leading position
5072822fc04SHerbert Dürr 		if( 2*n >= nMaxIndex )
5082822fc04SHerbert Dürr 			break;
5092822fc04SHerbert Dürr 		pCaretXArray[ 2*n+0 ] = lrint( fPos1 * mfFontScale );
5102822fc04SHerbert Dürr 	}
5112822fc04SHerbert Dürr }
5122822fc04SHerbert Dürr 
5132822fc04SHerbert Dürr // -----------------------------------------------------------------------
5142822fc04SHerbert Dürr 
GetBoundRect(SalGraphics & rGraphics,Rectangle & rVCLRect) const5152822fc04SHerbert Dürr bool CTLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rVCLRect ) const
5162822fc04SHerbert Dürr {
5172822fc04SHerbert Dürr 	AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
5182822fc04SHerbert Dürr 	CGRect aMacRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext );
5192822fc04SHerbert Dürr 	CGPoint aMacPos = CGContextGetTextPosition( rAquaGraphics.mrContext );
5202822fc04SHerbert Dürr 	aMacRect.origin.x -= aMacPos.x;
5212822fc04SHerbert Dürr 	aMacRect.origin.y -= aMacPos.y;
5222822fc04SHerbert Dürr 
5232822fc04SHerbert Dürr 	const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
5242822fc04SHerbert Dürr 
5252822fc04SHerbert Dürr 	// CoreText top-bottom are vertically flipped from a VCL aspect
5262822fc04SHerbert Dürr 	rVCLRect.Left()   = aPos.X() + mfFontScale * aMacRect.origin.x;
5272822fc04SHerbert Dürr 	rVCLRect.Right()  = aPos.X() + mfFontScale * (aMacRect.origin.x + aMacRect.size.width);
5282822fc04SHerbert Dürr 	rVCLRect.Bottom() = aPos.Y() - mfFontScale * aMacRect.origin.y;
5292822fc04SHerbert Dürr 	rVCLRect.Top()    = aPos.Y() - mfFontScale * (aMacRect.origin.y + aMacRect.size.height);
5302822fc04SHerbert Dürr 	return true;
5312822fc04SHerbert Dürr }
5322822fc04SHerbert Dürr 
5332822fc04SHerbert Dürr // =======================================================================
5342822fc04SHerbert Dürr 
5352822fc04SHerbert Dürr // glyph fallback is supported directly by Aqua
5362822fc04SHerbert Dürr // so methods used only by MultiSalLayout can be dummy implementated
GetGlyphOutlines(SalGraphics &,PolyPolyVector &) const5372822fc04SHerbert Dürr bool CTLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
InitFont() const5382822fc04SHerbert Dürr void CTLayout::InitFont() const {}
MoveGlyph(int,long)5392822fc04SHerbert Dürr void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
DropGlyph(int)5402822fc04SHerbert Dürr void CTLayout::DropGlyph( int /*nStart*/ ) {}
Simplify(bool)5412822fc04SHerbert Dürr void CTLayout::Simplify( bool /*bIsBase*/ ) {}
5422822fc04SHerbert Dürr 
5432822fc04SHerbert Dürr // get the ImplFontData for a glyph fallback font
5442822fc04SHerbert Dürr // for a glyphid that was returned by CTLayout::GetNextGlyphs()
GetFallbackFontData(sal_GlyphId) const5452822fc04SHerbert Dürr const ImplFontData* CTLayout::GetFallbackFontData( sal_GlyphId /*aGlyphId*/ ) const
5462822fc04SHerbert Dürr {
5472822fc04SHerbert Dürr #if 0
5482822fc04SHerbert Dürr 	// check if any fallback fonts were needed
5492822fc04SHerbert Dürr 	if( !mpFallbackInfo )
5502822fc04SHerbert Dürr 		return NULL;
5512822fc04SHerbert Dürr 	// check if the current glyph needs a fallback font
5522822fc04SHerbert Dürr 	int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
5532822fc04SHerbert Dürr 	if( !nFallbackLevel )
5542822fc04SHerbert Dürr 		return NULL;
5552822fc04SHerbert Dürr 	pFallbackFont = mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
5562822fc04SHerbert Dürr #else
5572822fc04SHerbert Dürr 	// let CoreText's font cascading handle glyph fallback
5582822fc04SHerbert Dürr 	const ImplFontData* pFallbackFont = NULL;
5592822fc04SHerbert Dürr #endif
5602822fc04SHerbert Dürr 	return pFallbackFont;
5612822fc04SHerbert Dürr }
5622822fc04SHerbert Dürr 
5632822fc04SHerbert Dürr // =======================================================================
5642822fc04SHerbert Dürr 
GetTextLayout(void) const5652822fc04SHerbert Dürr SalLayout* CTTextStyle::GetTextLayout( void ) const
5662822fc04SHerbert Dürr {
5672822fc04SHerbert Dürr 	return new CTLayout( this);
5682822fc04SHerbert Dürr }
5692822fc04SHerbert Dürr 
5702822fc04SHerbert Dürr // =======================================================================
5712822fc04SHerbert Dürr 
572