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 5627be7afaSHerbert Dürr CFMutableDictionaryRef mpStyleDict; 572822fc04SHerbert Dürr CFAttributedStringRef mpAttrString; 582822fc04SHerbert Dürr CTLineRef mpCTLine; 592822fc04SHerbert Dürr 602822fc04SHerbert Dürr int mnCharCount; // ==mnEndCharPos-mnMinCharPos 61a752d5ddSHerbert Dürr int mnTrailingSpaceCount; 62a752d5ddSHerbert Dürr double mfTrailingSpaceWidth; // preserves the width of stripped-off trailing space 6327be7afaSHerbert 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 6927be7afaSHerbert Dürr CGFloat mfFontRotation; // text direction angle (in radians) 702611f30cSHerbert Dürr CGFloat mfFontStretch; // <1.0: font gets squeezed, >1.0: font gets stretched 7127be7afaSHerbert 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 832822fc04SHerbert Dürr CTLayout::CTLayout( const CTTextStyle* pTextStyle ) 8427be7afaSHerbert Dürr : mpStyleDict( pTextStyle->GetStyleDict() ) 852822fc04SHerbert Dürr , mpAttrString( NULL ) 862822fc04SHerbert Dürr , mpCTLine( NULL ) 872822fc04SHerbert Dürr , mnCharCount( 0 ) 88a752d5ddSHerbert Dürr , mnTrailingSpaceCount( 0 ) 89a752d5ddSHerbert Dürr , mfTrailingSpaceWidth( 0.0 ) 902822fc04SHerbert Dürr , mfFontScale( pTextStyle->mfFontScale ) 9127be7afaSHerbert Dürr , mfFontRotation( pTextStyle->mfFontRotation ) 9227be7afaSHerbert Dürr , mfFontStretch( pTextStyle->mfFontStretch ) 932822fc04SHerbert Dürr , mfCachedWidth( -1 ) 942822fc04SHerbert Dürr , mnBaseAdv( 0 ) 952822fc04SHerbert Dürr { 9627be7afaSHerbert Dürr CFRetain( mpStyleDict ); 972822fc04SHerbert Dürr } 982822fc04SHerbert Dürr 992822fc04SHerbert Dürr // ----------------------------------------------------------------------- 1002822fc04SHerbert Dürr 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 ); 10727be7afaSHerbert Dürr CFRelease( mpStyleDict ); 1082822fc04SHerbert Dürr } 1092822fc04SHerbert Dürr 1102822fc04SHerbert Dürr // ----------------------------------------------------------------------- 1112822fc04SHerbert Dürr 1122822fc04SHerbert Dürr bool CTLayout::LayoutText( ImplLayoutArgs& rArgs ) 1132822fc04SHerbert Dürr { 114e89c3263SHerbert 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 122e89c3263SHerbert 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 130a135d000SHerbert Dürr // prepare the string to be layouted by CoreText 1312822fc04SHerbert Dürr CFStringRef aCFText = CFStringCreateWithCharactersNoCopy( NULL, rArgs.mpStr + mnMinCharPos, mnCharCount, kCFAllocatorNull ); 132a135d000SHerbert Dürr // #i124375# force soft-hyphen visibility to meet the expectations of Writer+EditEngine 133a135d000SHerbert Dürr if( CFStringFind( aCFText, (CFStringRef)@"\u00AD", 0).length > 0 ) 134a135d000SHerbert Dürr { 135a135d000SHerbert Dürr NSString* pDashStr = [(NSString*)aCFText stringByReplacingOccurrencesOfString: @"\u00AD" withString: @"-"]; 136a135d000SHerbert Dürr aCFText = CFStringCreateCopy( NULL, (CFStringRef)pDashStr ); 137a135d000SHerbert Dürr } 138a135d000SHerbert Dürr 139a135d000SHerbert Dürr // create the CoreText line layout using the requested text style 14027be7afaSHerbert 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() 145a752d5ddSHerbert Dürr mnTrailingSpaceCount = 0; 146a752d5ddSHerbert Dürr for( int i = mnEndCharPos; --i >= mnMinCharPos; ++mnTrailingSpaceCount ) 147a752d5ddSHerbert Dürr if( !IsSpacingGlyph( rArgs.mpStr[i] | GF_ISCHAR ) 148a752d5ddSHerbert 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 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 } 167a752d5ddSHerbert Dürr else if( !nPixelWidth ) // short-circuit if there is nothing to adjust 168a752d5ddSHerbert Dürr return; 1692822fc04SHerbert Dürr 1703e4cd16fSHerbert Dürr // short-circuit when justifying an all-whitespace string 171a752d5ddSHerbert 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) 190a752d5ddSHerbert Dürr if( mnTrailingSpaceCount ) { 191bca19674SHerbert Dürr int nTrailingSpaceWidth = 0; 192bca19674SHerbert Dürr if( rArgs.mpDXArray) { 193bca19674SHerbert Dürr const int nFullPixWidth = nPixelWidth; 194a752d5ddSHerbert Dürr nPixelWidth = rArgs.mpDXArray[ mnCharCount-1-mnTrailingSpaceCount ]; 195bca19674SHerbert Dürr nTrailingSpaceWidth = nFullPixWidth - nPixelWidth; 196a752d5ddSHerbert Dürr mfTrailingSpaceWidth = nTrailingSpaceWidth; 197bca19674SHerbert Dürr } else { 198a752d5ddSHerbert Dürr if( mfTrailingSpaceWidth <= 0.0 ) 199a752d5ddSHerbert Dürr mfTrailingSpaceWidth = CTLineGetTrailingWhitespaceWidth( mpCTLine ); 200a752d5ddSHerbert Dürr nTrailingSpaceWidth = rint( mfTrailingSpaceWidth ); 201a752d5ddSHerbert 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, 209a752d5ddSHerbert Dürr mnCharCount - mnTrailingSpaceCount, kCFAllocatorNull ); 21027be7afaSHerbert 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; 222a752d5ddSHerbert 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; 233a752d5ddSHerbert Dürr mfCachedWidth = fAdjustedWidth + mfTrailingSpaceWidth; 2342822fc04SHerbert Dürr } 2352822fc04SHerbert Dürr 2362822fc04SHerbert Dürr // ----------------------------------------------------------------------- 2372822fc04SHerbert Dürr 2382822fc04SHerbert Dürr void CTLayout::DrawText( SalGraphics& rGraphics ) const 2392822fc04SHerbert Dürr { 2402822fc04SHerbert Dürr AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics); 241e89c3263SHerbert 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; 246e89c3263SHerbert 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 254e89c3263SHerbert 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 25827be7afaSHerbert Dürr if( mfFontRotation != 0.0 ) 2592822fc04SHerbert Dürr { 26027be7afaSHerbert Dürr CGContextRotateCTM( rAquaGraphics.mrContext, +mfFontRotation ); 2612822fc04SHerbert Dürr 26227be7afaSHerbert 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 ); 267e89c3263SHerbert Dürr 268e89c3263SHerbert Dürr // set the text color as fill color (see kCTForegroundColorFromContextAttributeName) 269e89c3263SHerbert Dürr CGContextSetFillColor( rAquaGraphics.mrContext, rAquaGraphics.maTextColor.AsArray() ); 270e89c3263SHerbert Dürr 271e89c3263SHerbert Dürr // draw the text 2722822fc04SHerbert Dürr CTLineDraw( mpCTLine, rAquaGraphics.mrContext ); 2732822fc04SHerbert Dürr 2742822fc04SHerbert Dürr // request an update of the changed window area 2752822fc04SHerbert Dürr if( rAquaGraphics.IsWindowGraphics() ) 2762822fc04SHerbert Dürr { 2772822fc04SHerbert Dürr const CGRect aInkRect = CTLineGetImageBounds( mpCTLine, rAquaGraphics.mrContext ); 2782822fc04SHerbert Dürr const CGRect aRefreshRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aInkRect ); 2792822fc04SHerbert Dürr rAquaGraphics.RefreshRect( aRefreshRect ); 2802822fc04SHerbert Dürr } 2812822fc04SHerbert 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 2882822fc04SHerbert 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 ) 36627be7afaSHerbert 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 ]; 37127be7afaSHerbert 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 3832822fc04SHerbert 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 3972822fc04SHerbert 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 ); 407a752d5ddSHerbert Dürr if( mnTrailingSpaceCount && (mfTrailingSpaceWidth > 0.0) ) { 408a752d5ddSHerbert Dürr const double fOneWidth = mfTrailingSpaceWidth / mnTrailingSpaceCount; 409a752d5ddSHerbert Dürr for( int i = 1; i <= mnTrailingSpaceCount; ++i) 410a752d5ddSHerbert Dürr aWidthVector[ mnCharCount - i ] = fOneWidth; 411a752d5ddSHerbert Dürr } 412a752d5ddSHerbert 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 4482822fc04SHerbert 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 455*00e12258SHerbert Dürr CFIndex nBestGuess = (nCharExtra >= 0) ? 0 : mnCharCount; 456*00e12258SHerbert Dürr for( int i = 1; i <= mnCharCount; i *= 2 ) 457*00e12258SHerbert Dürr { 458*00e12258SHerbert Dürr // guess the target width considering char-extra expansion/condensation 459*00e12258SHerbert Dürr const long nTargetWidth = nMaxWidth - nBestGuess * nCharExtra; 460*00e12258SHerbert Dürr const double fCTMaxWidth = nTargetWidth / (nFactor * mfFontScale); 461*00e12258SHerbert Dürr // calculate the breaking index for the guessed target width 462*00e12258SHerbert Dürr const CFIndex nNewIndex = CTTypesetterSuggestClusterBreak( aCTTypeSetter, 0, fCTMaxWidth ); 463*00e12258SHerbert Dürr if( nNewIndex >= mnCharCount ) { 464*00e12258SHerbert Dürr CFRelease( aCTTypeSetter ); 465*00e12258SHerbert Dürr return STRING_LEN; 466*00e12258SHerbert Dürr } 467*00e12258SHerbert Dürr // check if the original extra-width guess was good 468*00e12258SHerbert Dürr if( !nCharExtra ) 469*00e12258SHerbert Dürr nBestGuess = nNewIndex; 470*00e12258SHerbert Dürr if( nBestGuess == nNewIndex ) 471*00e12258SHerbert Dürr break; 472*00e12258SHerbert Dürr // prepare another round for a different number of characters 473*00e12258SHerbert Dürr CFIndex nNewGuess = (nNewIndex + nBestGuess + 1) / 2; 474*00e12258SHerbert Dürr if( nNewGuess == nBestGuess ) 475*00e12258SHerbert Dürr nNewGuess += (nNewIndex > nBestGuess) ? +1 : -1; 476*00e12258SHerbert Dürr nBestGuess = nNewGuess; 477*00e12258SHerbert Dürr } 478*00e12258SHerbert Dürr 479*00e12258SHerbert Dürr // suggest the best fitting cluster break as breaking position 480*00e12258SHerbert Dürr CFRelease( aCTTypeSetter ); 481*00e12258SHerbert Dürr const int nIndex = nBestGuess + mnMinCharPos; 482*00e12258SHerbert Dürr return nIndex; 4832822fc04SHerbert Dürr } 4842822fc04SHerbert Dürr 4852822fc04SHerbert Dürr // ----------------------------------------------------------------------- 4862822fc04SHerbert Dürr 4872822fc04SHerbert 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 5152822fc04SHerbert 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 5372822fc04SHerbert Dürr bool CTLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; } 5382822fc04SHerbert Dürr void CTLayout::InitFont() const {} 5392822fc04SHerbert Dürr void CTLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {} 5402822fc04SHerbert Dürr void CTLayout::DropGlyph( int /*nStart*/ ) {} 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() 5452822fc04SHerbert 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 5652822fc04SHerbert 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