1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/types.h>
21 #include <boost/ptr_container/ptr_vector.hpp>
22 #include "tools/debug.hxx"
24 #include "quartz/utils.h"
26 #include "ctfonts.hxx"
27 #include "CTRunData.hxx"
30 class CTLayout
: public SalLayout
33 explicit CTLayout( const CoreTextStyle
* );
36 virtual bool LayoutText( ImplLayoutArgs
& ) SAL_OVERRIDE
;
37 virtual void AdjustLayout( ImplLayoutArgs
& ) SAL_OVERRIDE
;
38 virtual void DrawText( SalGraphics
& ) const SAL_OVERRIDE
;
39 virtual bool DrawTextSpecial( SalGraphics
& rGraphics
, sal_uInt32 flags
) const SAL_OVERRIDE
;
41 virtual int GetNextGlyphs( int nLen
, sal_GlyphId
* pOutGlyphIds
, Point
& rPos
, int&,
42 DeviceCoordinate
* pGlyphAdvances
, int* pCharIndexes
,
43 const PhysicalFontFace
** pFallbackFonts
) const SAL_OVERRIDE
;
45 virtual DeviceCoordinate
GetTextWidth() const SAL_OVERRIDE
;
46 virtual DeviceCoordinate
FillDXArray( DeviceCoordinate
* pDXArray
) const SAL_OVERRIDE
;
47 virtual sal_Int32
GetTextBreak(DeviceCoordinate nMaxWidth
, DeviceCoordinate nCharExtra
, int nFactor
) const SAL_OVERRIDE
;
48 virtual void GetCaretPositions( int nArraySize
, long* pCaretXArray
) const SAL_OVERRIDE
;
49 virtual bool GetBoundRect( SalGraphics
&, Rectangle
& ) const SAL_OVERRIDE
;
51 virtual void InitFont() const SAL_OVERRIDE
;
52 virtual void MoveGlyph( int nStart
, long nNewXPos
) SAL_OVERRIDE
;
53 virtual void DropGlyph( int nStart
) SAL_OVERRIDE
;
54 virtual void Simplify( bool bIsBase
) SAL_OVERRIDE
;
57 void drawCTLine(AquaSalGraphics
& rAquaGraphics
, CTLineRef ctline
, const CoreTextStyle
* const pStyle
) const;
58 CGPoint
GetTextDrawPosition() const;
59 bool CacheGlyphLayout() const;
61 const CoreTextStyle
* const mpTextStyle
;
63 // CoreText specific objects
64 CFAttributedStringRef mpAttrString
;
67 int mnCharCount
; // ==mnEndCharPos-mnMinCharPos
68 int mnTrailingSpaceCount
;
69 double mfTrailingSpaceWidth
;
71 // cached details about the resulting layout
72 // mutable members since these details are all lazy initialized
73 mutable double mfCachedWidth
; // cached value of resulting typographical width
75 // x-offset relative to layout origin
76 // currently only used in RTL-layouts
77 mutable double mfBaseAdv
;
79 mutable bool bLayouted
; // true if the glyph layout information are cached and current;
80 mutable boost::ptr_vector
<CTRunData
> m_vRunData
;
84 CTLayout::CTLayout( const CoreTextStyle
* pTextStyle
)
85 : mpTextStyle( pTextStyle
)
86 , mpAttrString( NULL
)
89 , mnTrailingSpaceCount( 0 )
90 , mfTrailingSpaceWidth( 0.0 )
101 SAL_INFO( "vcl.ct", "CFRelease(" << mpCTLine
<< ")" );
102 CFRelease( mpCTLine
);
105 CFRelease( mpAttrString
);
108 bool CTLayout::LayoutText( ImplLayoutArgs
& rArgs
)
110 m_vRunData
.release();
113 // release an eventual older layout
115 CFRelease( mpAttrString
);
119 SAL_INFO( "vcl.ct", "CFRelease(" << mpCTLine
<< ")" );
120 CFRelease( mpCTLine
);
124 // initialize the new layout
125 SalLayout::AdjustLayout( rArgs
);
126 mnCharCount
= mnEndCharPos
- mnMinCharPos
;
128 // short circuit if there is nothing to do
129 if( mnCharCount
<= 0 )
132 // create the CoreText line layout
133 CFStringRef aCFText
= CFStringCreateWithCharactersNoCopy( NULL
,
134 rArgs
.mpStr
+ mnMinCharPos
,
137 // CFAttributedStringCreate copies the attribues parameter
138 mpAttrString
= CFAttributedStringCreate( NULL
, aCFText
, mpTextStyle
->GetStyleDict() );
139 mpCTLine
= CTLineCreateWithAttributedString( mpAttrString
);
140 SAL_INFO( "vcl.ct", "CTLineCreateWithAttributedString(\"" << GetOUString(aCFText
) << "\") =p " << mpCTLine
);
143 mnTrailingSpaceCount
= 0;
144 // reverse search for first 'non-space'...
145 for( int i
= mnEndCharPos
- 1; i
>= mnMinCharPos
; i
--)
147 sal_Unicode nChar
= rArgs
.mpStr
[i
];
148 if ((nChar
<= 0x0020) || // blank
149 (nChar
== 0x00A0) || // non breaking space
150 (nChar
>= 0x2000 && nChar
<= 0x200F) || // whitespace
151 (nChar
== 0x3000)) // ideographic space
153 mnTrailingSpaceCount
+= 1;
163 void CTLayout::AdjustLayout( ImplLayoutArgs
& rArgs
)
171 int nPixelWidth
= rArgs
.mpDXArray
? rArgs
.mpDXArray
[ mnCharCount
- 1 ] : rArgs
.mnLayoutWidth
;
172 if( nPixelWidth
<= 0)
175 // HACK: justification requests which change the width by just one pixel are probably
176 // #i86038# introduced by lossy conversions between integer based coordinate system
177 int fuzz
= (nPixelWidth
- GetTextWidth()) / 2;
183 // if the text to be justified has whitespace in it then
184 // - Writer goes crazy with its HalfSpace magic
185 // - CoreText handles spaces specially (in particular at the text end)
186 if( mnTrailingSpaceCount
)
190 int nFullPixelWidth
= nPixelWidth
;
191 nPixelWidth
= mnTrailingSpaceCount
== mnCharCount
192 ? 0 : rArgs
.mpDXArray
[ mnCharCount
- mnTrailingSpaceCount
- 1];
193 mfTrailingSpaceWidth
= nFullPixelWidth
- nPixelWidth
;
197 if(mfTrailingSpaceWidth
<= 0.0)
199 mfTrailingSpaceWidth
= CTLineGetTrailingWhitespaceWidth( mpCTLine
);
200 nPixelWidth
-= rint(mfTrailingSpaceWidth
);
207 // recreate the CoreText line layout without trailing spaces
208 SAL_INFO( "vcl.ct", "CFRelease(" << mpCTLine
<< ")" );
209 CFRelease( mpCTLine
);
210 CFStringRef aCFText
= CFStringCreateWithCharactersNoCopy( NULL
,
211 rArgs
.mpStr
+ mnMinCharPos
,
212 mnCharCount
- mnTrailingSpaceCount
,
214 CFAttributedStringRef pAttrStr
= CFAttributedStringCreate( NULL
,
216 mpTextStyle
->GetStyleDict() );
217 mpCTLine
= CTLineCreateWithAttributedString( pAttrStr
);
218 SAL_INFO( "vcl.ct", "CTLineCreateWithAttributedString(\"" << GetOUString(aCFText
) << "\") = " << mpCTLine
);
219 CFRelease( pAttrStr
);
220 CFRelease( aCFText
);
223 // in RTL-layouts trailing spaces are leftmost
224 // TODO: use BiDi-algorithm to thoroughly check this assumption
225 if( rArgs
.mnFlags
& SalLayoutFlags::BiDiRtl
)
227 mfBaseAdv
= mfTrailingSpaceWidth
;
231 CTLineRef pNewCTLine
= CTLineCreateJustifiedLine( mpCTLine
, 1.0, nPixelWidth
);
232 SAL_INFO( "vcl.ct", "CTLineCreateJustifiedLine(" << mpCTLine
<< ",1.0," << nPixelWidth
<< ") = " << pNewCTLine
);
236 // CTLineCreateJustifiedLine can and does fail
237 // handle failure by keeping the unjustified layout
238 // TODO: a better solution such as
239 // - forcing glyph overlap
240 // - changing the font size
241 // - changing the CTM matrix
244 SAL_INFO( "vcl.ct", "CFRelease(" << mpCTLine
<< ")" );
245 CFRelease( mpCTLine
);
246 mpCTLine
= pNewCTLine
;
247 mfCachedWidth
= nPixelWidth
+ mfTrailingSpaceWidth
;
250 // When drawing right aligned text, rounding errors in the position returned by
251 // GetDrawPosition() cause the right margin of the text to change whenever text
252 // width changes causing "jumping letters" effect. So here we calculate the
253 // drawing position relative to the right margin on our own to avoid the
254 // rounding errors. That is basically a hack, and it should go away if one day
255 // we managed to get rid of those rounding errors.
257 // We continue using GetDrawPosition() for non-right aligned text, to minimize
258 // any unforeseen side effects.
259 CGPoint
CTLayout::GetTextDrawPosition() const
261 CGFloat fPosX
, fPosY
;
263 if (mnLayoutFlags
& SalLayoutFlags::RightAlign
)
265 // text is always drawn at its leftmost point
266 const Point aPos
= DrawBase();
267 fPosX
= aPos
.X() + mfBaseAdv
- GetTextWidth();
272 const Point aPos
= GetDrawPosition(Point(mfBaseAdv
, 0));
277 CGPoint aTextPos
= { +fPosX
, -fPosY
};
281 /* use to deal with special font decoration like 'outline' drawing
282 * return true if it was able to handle the drawing
283 * false if not, in which case the caller
284 * is supposed to fallback to 'generic' method
286 bool CTLayout::DrawTextSpecial( SalGraphics
& rGraphics
, sal_uInt32 flags
) const
288 AquaSalGraphics
& rAquaGraphics
= static_cast<AquaSalGraphics
&>(rGraphics
);
290 // short circuit if there is nothing to do
291 if( (mnCharCount
<= 0) || !rAquaGraphics
.CheckContext() )
293 #if 0 /* temporarely revert to the 'old way' */
294 if (flags
& DRAWTEXT_F_OUTLINE
)
296 CFMutableDictionaryRef styledict
= CFDictionaryCreateMutableCopy(
297 CFAllocatorGetDefault(),
298 CFDictionaryGetCount(mpTextStyle
->GetStyleDict()),
299 mpTextStyle
->GetStyleDict());
302 CFNumberRef rStroke
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &nStroke
);
303 CFDictionarySetValue(styledict
, kCTStrokeWidthAttributeName
, rStroke
);
305 CFAttributedStringRef pAttrStr
= CFAttributedStringCreate(
307 CFAttributedStringGetString(mpAttrString
),
309 CTLineRef pCTLine
= CTLineCreateWithAttributedString( pAttrStr
);
310 SAL_INFO( "vcl.ct", "CTLineCreateWithAttributedString(" << GetOUString(CFAttributedStringGetString(mpAttrSring
)) << ") = " << pCTLine
);
311 CFRelease( pAttrStr
);
313 /* draw the text in 'outline' */
314 drawCTLine(rAquaGraphics
, pCTLine
, mpTextStyle
);
315 SAL_INFO( "vcl.ct", "CFRelease(" << pCTLine
<< ")" );
322 return SalLayout::DrawTextSpecial(rGraphics
, flags
);
326 void CTLayout::drawCTLine(AquaSalGraphics
& rAquaGraphics
, CTLineRef ctline
, const CoreTextStyle
* const pStyle
) const
328 // the view is vertically flipped => flipped glyphs
329 // so apply a temporary transformation that it flips back
330 // also compensate if the font was size limited
331 CGContextRef context
= rAquaGraphics
.mrContext
;
332 SAL_INFO( "vcl.ct", "CGContextSaveGState(" << context
<< ")" );
333 CGContextSaveGState( context
);
334 SAL_INFO( "vcl.ct", "CGContextScaleCTM(" << context
<< ",1.0,-1.0)" );
335 CGContextScaleCTM( context
, 1.0, -1.0 );
336 CGContextSetShouldAntialias( context
, !rAquaGraphics
.mbNonAntialiasedText
);
338 // set the text transformation (e.g. position)
339 CGPoint aTextPos
= GetTextDrawPosition();
341 if( pStyle
->mfFontRotation
!= 0.0 )
343 const CGFloat fRadians
= pStyle
->mfFontRotation
;
344 SAL_INFO( "vcl.ct", "CGContextRotateCTM(" << context
<< "," << +fRadians
<< ")" );
345 CGContextRotateCTM( context
, +fRadians
);
347 const CGAffineTransform aInvMatrix
= CGAffineTransformMakeRotation( -fRadians
);
348 aTextPos
= CGPointApplyAffineTransform( aTextPos
, aInvMatrix
);
351 SAL_INFO( "vcl.ct", "CGContextSetTextPosition(" << context
<< "," << aTextPos
<< ")" );
352 CGContextSetTextPosition( context
, aTextPos
.x
, aTextPos
.y
);
355 // request an update of the to-be-changed window area
356 if( rAquaGraphics
.IsWindowGraphics() )
358 const CGRect aInkRect
= CTLineGetImageBounds( mpCTLine
, context
);
359 const CGRect aRefreshRect
= CGContextConvertRectToDeviceSpace( context
, aInkRect
);
360 rAquaGraphics
.RefreshRect( aRefreshRect
);
364 // set the text color as fill color (see kCTForegroundColorFromContextAttributeName)
365 CGContextSetFillColor( context
, rAquaGraphics
.maTextColor
.AsArray() );
367 SAL_INFO( "vcl.ct", "CTLineDraw(" << ctline
<< "," << context
<< ")" );
369 CTLineDraw( ctline
, context
);
371 if(mnLayoutFlags
& SalLayoutFlags::DrawBullet
)
373 CFArrayRef runArray
= CTLineGetGlyphRuns(ctline
);
374 CFIndex runCount
= CFArrayGetCount(runArray
);
376 for (CFIndex runIndex
= 0; runIndex
< runCount
; runIndex
++)
378 CTRunRef run
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex(runArray
, runIndex
));
379 CFIndex runGlyphCount
= CTRunGetGlyphCount(run
);
382 CFIndex runGlyphIndex
= 0;
383 CFIndex stringIndice
= 0;
385 for (; runGlyphIndex
< runGlyphCount
; runGlyphIndex
++)
387 CFRange glyphRange
= CFRangeMake(runGlyphIndex
, 1);
388 CTRunGetStringIndices( run
, glyphRange
, &stringIndice
);
389 UniChar curChar
= CFStringGetCharacterAtIndex (CFAttributedStringGetString(mpAttrString
), stringIndice
);
395 CTFontRef runFont
= static_cast<CTFontRef
>(CFDictionaryGetValue(CTRunGetAttributes(run
),
396 kCTFontAttributeName
));
397 CGFloat baseSize
= CTFontGetSize(runFont
);
398 CTRunGetTypographicBounds ( run
, glyphRange
,
399 &ascent
, &descent
, &leading
);
400 CTRunGetPositions(run
, glyphRange
, &position
);
401 CTRunGetAdvances(run
, glyphRange
, &advance
);
403 if(mnLayoutFlags
& SalLayoutFlags::Vertical
)
405 bulletRect
= CGRectMake(position
.x
- advance
.width
/ 4,
406 -position
.y
, baseSize
/ 5, baseSize
/ 5 );
410 bulletRect
= CGRectMake(position
.x
+ advance
.width
/ 4,
411 position
.y
+ ascent
/ 3 - baseSize
/ 5, baseSize
/ 5, baseSize
/ 5 );
414 CGContextSaveGState(context
);
415 CGContextTranslateCTM(context
, aTextPos
.x
, aTextPos
.y
);
416 RGBAColor
bulletColor(MAKE_SALCOLOR(0x26, 0x8b, 0xd2 )); // NON_PRINTING_CHARACTER_COLOR
417 CGContextSetFillColor( context
, bulletColor
.AsArray() );
418 CGContextSetStrokeColor(context
, bulletColor
.AsArray());
419 if(mnLayoutFlags
& SalLayoutFlags::Vertical
)
421 CGContextRotateCTM( context
, -F_PI
/2 );
424 CGContextBeginPath(context
);
425 CGContextAddEllipseInRect(context
, bulletRect
);
426 CGContextDrawPath(context
, kCGPathFillStroke
); // Or kCGPathFill
427 CGContextRestoreGState(context
);
432 // restore the original graphic context transformations
433 SAL_INFO( "vcl.ct", "CGContextRestoreGState(" << context
<< ")" );
434 CGContextRestoreGState( context
);
437 void CTLayout::DrawText( SalGraphics
& rGraphics
) const
439 AquaSalGraphics
& rAquaGraphics
= static_cast<AquaSalGraphics
&>(rGraphics
);
441 // short circuit if there is nothing to do
442 if( (mnCharCount
<= 0) || !rAquaGraphics
.CheckContext() )
445 drawCTLine(rAquaGraphics
, mpCTLine
, mpTextStyle
);
448 bool CTLayout::CacheGlyphLayout() const // eew!
450 m_vRunData
.release();
455 CFArrayRef aRuns
= CTLineGetGlyphRuns( mpCTLine
);
456 const int nRun
= CFArrayGetCount( aRuns
);
459 for( int i
= 0; i
< nRun
; ++i
)
461 CTRunRef pRun
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex( aRuns
, i
));
462 CTRunData
* pRunData
= new CTRunData(pRun
, nPos
);
463 m_vRunData
.push_back(pRunData
);
464 nPos
+= pRunData
->m_nGlyphs
;
470 int CTLayout::GetNextGlyphs( int nLen
, sal_GlyphId
* pOutGlyphIds
, Point
& rPos
, int& nStart
,
471 DeviceCoordinate
* pGlyphAdvances
, int* pCharIndexes
,
472 const PhysicalFontFace
** pFallbackFonts
) const
483 if( nStart
< 0 ) // first glyph requested?
487 const PhysicalFontFace
* pFallbackFont
= NULL
;
488 CTFontRef pFont
= NULL
;
489 CTFontDescriptorRef pFontDesc
= NULL
;
490 ImplDevFontAttributes rDevFontAttr
;
492 boost::ptr_vector
<CTRunData
>::const_iterator iter
= m_vRunData
.begin();
494 while(iter
!= m_vRunData
.end() && iter
->m_EndPos
<= nStart
)
498 if(iter
== m_vRunData
.end())
506 pFont
= static_cast<CTFontRef
>(CFDictionaryGetValue( mpTextStyle
->GetStyleDict(), kCTFontAttributeName
));
507 pFontDesc
= CTFontCopyFontDescriptor( iter
->m_pFont
);
508 rDevFontAttr
= DevFontFromCTFontDescriptor( pFontDesc
, NULL
);
513 while( i
< nStart
+ nLen
)
515 // convert glyph details for VCL
516 int j
= i
- iter
->m_StartPos
;
517 *(pOutGlyphIds
++) = iter
->m_pGlyphs
[ j
];
520 *(pGlyphAdvances
++) = lrint(iter
->m_pAdvances
[ j
].width
);
524 *(pCharIndexes
++) = iter
->m_pStringIndices
[ j
] + mnMinCharPos
;
528 if ( !CFEqual( iter
->m_pFont
, pFont
) )
530 pFallbackFont
= new CoreTextFontData( rDevFontAttr
, reinterpret_cast<sal_IntPtr
>(pFontDesc
) );
531 *(pFallbackFonts
++) = pFallbackFont
;
535 *(pFallbackFonts
++) = NULL
;
540 const CGPoint
& rFirstPos
= iter
->m_pPositions
[ j
];
541 rPos
= GetDrawPosition( Point( rFirstPos
.x
, rFirstPos
.y
) );
545 if(i
== iter
->m_EndPos
)
547 // note: we assume that we do not have empty runs in the middle of things
549 if( iter
== m_vRunData
.end())
555 pFont
= static_cast<CTFontRef
>(CFDictionaryGetValue( mpTextStyle
->GetStyleDict(), kCTFontAttributeName
));
556 pFontDesc
= CTFontCopyFontDescriptor( iter
->m_pFont
);
557 rDevFontAttr
= DevFontFromCTFontDescriptor( pFontDesc
, NULL
);
567 DeviceCoordinate
CTLayout::GetTextWidth() const
569 if( (mnCharCount
<= 0) || !mpCTLine
)
572 if( mfCachedWidth
< 0.0 )
574 mfCachedWidth
= CTLineGetTypographicBounds( mpCTLine
, NULL
, NULL
, NULL
);
577 return mfCachedWidth
;
580 DeviceCoordinate
CTLayout::FillDXArray( DeviceCoordinate
* pDXArray
) const
582 DeviceCoordinate nPixelWidth
= GetTextWidth();
584 // short circuit requests which don't need full details
590 for(int i
= 0; i
< mnCharCount
; i
++)
595 // prepare the sub-pixel accurate logical-width array
596 ::std::vector
<float> aWidthVector( mnCharCount
);
597 if( mnTrailingSpaceCount
&& (mfTrailingSpaceWidth
> 0.0) )
599 const double fOneWidth
= mfTrailingSpaceWidth
/ mnTrailingSpaceCount
;
600 for(int i
= mnCharCount
- mnTrailingSpaceCount
; i
< mnCharCount
; i
++)
602 aWidthVector
[i
] = fOneWidth
;
606 // handle each glyph run
607 CFArrayRef aGlyphRuns
= CTLineGetGlyphRuns( mpCTLine
);
608 const int nRunCount
= CFArrayGetCount( aGlyphRuns
);
609 typedef std::vector
<CGSize
> CGSizeVector
;
610 CGSizeVector aSizeVector
;
611 typedef std::vector
<CFIndex
> CFIndexVector
;
612 CFIndexVector aIndexVector
;
614 for( int nRunIndex
= 0; nRunIndex
< nRunCount
; ++nRunIndex
)
616 CTRunRef pGlyphRun
= static_cast<CTRunRef
>(CFArrayGetValueAtIndex( aGlyphRuns
, nRunIndex
));
617 const CFIndex nGlyphCount
= CTRunGetGlyphCount( pGlyphRun
);
618 const CFRange aFullRange
= CFRangeMake( 0, nGlyphCount
);
620 aSizeVector
.resize( nGlyphCount
);
621 aIndexVector
.resize( nGlyphCount
);
622 CTRunGetAdvances( pGlyphRun
, aFullRange
, &aSizeVector
[0] );
623 CTRunGetStringIndices( pGlyphRun
, aFullRange
, &aIndexVector
[0] );
625 for( int i
= 0; i
!= nGlyphCount
; ++i
)
627 const int nRelIndex
= aIndexVector
[i
];
628 SAL_INFO( "vcl.ct", "aWidthVector[ g:" << i
<< "-> c:" << nRelIndex
<< " ] = " << aWidthVector
[nRelIndex
] << " + " << aSizeVector
[i
].width
<< " = " << aWidthVector
[nRelIndex
] + aSizeVector
[i
].width
);
629 aWidthVector
[nRelIndex
] += aSizeVector
[i
].width
;
633 // convert the sub-pixel accurate array into classic pDXArray integers
634 float fWidthSum
= 0.0;
635 sal_Int32 nOldDX
= 0;
636 for( int i
= 0; i
< mnCharCount
; ++i
)
638 const sal_Int32 nNewDX
= rint( fWidthSum
+= aWidthVector
[i
]);
639 pDXArray
[i
] = nNewDX
- nOldDX
;
645 sal_Int32
CTLayout::GetTextBreak( DeviceCoordinate nMaxWidth
, DeviceCoordinate nCharExtra
, int nFactor
) const
649 SAL_INFO("vcl.ct", "GetTextBreak mpCTLine == NULL");
652 CTTypesetterRef aCTTypeSetter
= CTTypesetterCreateWithAttributedString( mpAttrString
);
653 CFIndex nBestGuess
= (nCharExtra
>= 0) ? 0 : mnCharCount
;
654 for( int i
= 1; i
<= mnCharCount
; i
*= 2 )
656 // guess the target width considering char-extra expansion/condensation
657 const double nTargetWidth
= nMaxWidth
- nBestGuess
* nCharExtra
;
658 const double fCTMaxWidth
= nTargetWidth
/ nFactor
;
659 // calculate the breaking index for the guessed target width
660 const CFIndex nNewIndex
= CTTypesetterSuggestClusterBreak( aCTTypeSetter
, 0, fCTMaxWidth
);
661 if( nNewIndex
>= mnCharCount
)
663 CFRelease( aCTTypeSetter
);
666 // check if the original extra-width guess was good
668 nBestGuess
= nNewIndex
;
669 if( nBestGuess
== nNewIndex
)
671 // prepare another round for a different number of characters
672 CFIndex nNewGuess
= (nNewIndex
+ nBestGuess
+ 1) / 2;
673 if( nNewGuess
== nBestGuess
)
674 nNewGuess
+= (nNewIndex
> nBestGuess
) ? +1 : -1;
675 nBestGuess
= nNewGuess
;
678 // suggest the best fitting cluster break as breaking position
679 CFRelease( aCTTypeSetter
);
680 const int nIndex
= nBestGuess
+ mnMinCharPos
;
681 SAL_INFO("vcl.ct", "GetTextBreak nIndex:" << nIndex
);
685 void CTLayout::GetCaretPositions( int nMaxIndex
, long* pCaretXArray
) const
687 DBG_ASSERT( ((nMaxIndex
>0)&&!(nMaxIndex
&1)),
688 "CTLayout::GetCaretPositions() : invalid number of caret pairs requested");
690 // initialize the caret positions
691 for( int i
= 0; i
< nMaxIndex
; ++i
)
693 pCaretXArray
[ i
] = -1;
695 for( int n
= 0; n
<= mnCharCount
; ++n
)
697 // measure the characters cursor position
699 const CGFloat fPos1
= CTLineGetOffsetForStringIndex( mpCTLine
, n
, &fPos2
);
700 (void)fPos2
; // TODO: split cursor at line direction change
702 // update previous trailing position
704 pCaretXArray
[ 2*n
-1 ] = lrint( fPos1
);
706 // update current leading position
707 if( 2*n
>= nMaxIndex
)
709 pCaretXArray
[ 2*n
+0 ] = lrint( fPos1
);
713 bool CTLayout::GetBoundRect( SalGraphics
& rGraphics
, Rectangle
& rVCLRect
) const
715 // Closely mimic DrawText(), except that instead of calling
716 // CTLineDraw() to draw the line, we call CTLineGetImageBounds()
717 // to get its bounds. But all the coordinate system manipulation
718 // before that is the same => should be factored out?
720 AquaSalGraphics
& rAquaGraphics
= static_cast<AquaSalGraphics
&>(rGraphics
);
722 if( !rAquaGraphics
.CheckContext() )
725 CGContextSaveGState( rAquaGraphics
.mrContext
);
726 CGContextScaleCTM( rAquaGraphics
.mrContext
, 1.0, -1.0 );
727 CGContextSetShouldAntialias( rAquaGraphics
.mrContext
, !rAquaGraphics
.mbNonAntialiasedText
);
729 const CGPoint aVclPos
= GetTextDrawPosition();
730 CGPoint aTextPos
= GetTextDrawPosition();
732 if( mpTextStyle
->mfFontRotation
!= 0.0 )
734 const CGFloat fRadians
= mpTextStyle
->mfFontRotation
;
735 CGContextRotateCTM( rAquaGraphics
.mrContext
, +fRadians
);
737 const CGAffineTransform aInvMatrix
= CGAffineTransformMakeRotation( -fRadians
);
738 aTextPos
= CGPointApplyAffineTransform( aTextPos
, aInvMatrix
);
741 CGContextSetTextPosition( rAquaGraphics
.mrContext
, aTextPos
.x
, aTextPos
.y
);
742 CGRect aMacRect
= CTLineGetImageBounds( mpCTLine
, rAquaGraphics
.mrContext
);
744 if( mpTextStyle
->mfFontRotation
!= 0.0 )
746 const CGFloat fRadians
= mpTextStyle
->mfFontRotation
;
747 const CGAffineTransform aMatrix
= CGAffineTransformMakeRotation( +fRadians
);
748 aMacRect
= CGRectApplyAffineTransform( aMacRect
, aMatrix
);
751 CGContextRestoreGState( rAquaGraphics
.mrContext
);
753 rVCLRect
.Left() = aVclPos
.x
+ lrint(aMacRect
.origin
.x
);
754 rVCLRect
.Right() = aVclPos
.x
+ lrint(aMacRect
.origin
.x
+ aMacRect
.size
.width
);
755 rVCLRect
.Bottom() = aVclPos
.x
- lrint(aMacRect
.origin
.y
);
756 rVCLRect
.Top() = aVclPos
.x
- lrint(aMacRect
.origin
.y
+ aMacRect
.size
.height
);
761 // glyph fallback is supported directly by Aqua
762 // so methods used only by MultiSalLayout can be dummy implementated
763 void CTLayout::InitFont() const {}
764 void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
765 void CTLayout::DropGlyph( int /*nStart*/ ) {}
766 void CTLayout::Simplify( bool /*bIsBase*/ ) {}
768 SalLayout
* CoreTextStyle::GetTextLayout() const
770 return new CTLayout( this);
773 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */