151747b8eSHerbert Dürr /**************************************************************
251747b8eSHerbert Dürr *
351747b8eSHerbert Dürr * Licensed to the Apache Software Foundation (ASF) under one
451747b8eSHerbert Dürr * or more contributor license agreements. See the NOTICE file
551747b8eSHerbert Dürr * distributed with this work for additional information
651747b8eSHerbert Dürr * regarding copyright ownership. The ASF licenses this file
751747b8eSHerbert Dürr * to you under the Apache License, Version 2.0 (the
851747b8eSHerbert Dürr * "License"); you may not use this file except in compliance
951747b8eSHerbert Dürr * with the License. You may obtain a copy of the License at
1051747b8eSHerbert Dürr *
1151747b8eSHerbert Dürr * http://www.apache.org/licenses/LICENSE-2.0
1251747b8eSHerbert Dürr *
1351747b8eSHerbert Dürr * Unless required by applicable law or agreed to in writing,
1451747b8eSHerbert Dürr * software distributed under the License is distributed on an
1551747b8eSHerbert Dürr * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1651747b8eSHerbert Dürr * KIND, either express or implied. See the License for the
1751747b8eSHerbert Dürr * specific language governing permissions and limitations
1851747b8eSHerbert Dürr * under the License.
1951747b8eSHerbert Dürr *
2051747b8eSHerbert Dürr *************************************************************/
2151747b8eSHerbert Dürr
2251747b8eSHerbert Dürr
2351747b8eSHerbert Dürr
2451747b8eSHerbert Dürr #include "tools/debug.hxx"
2551747b8eSHerbert Dürr
2651747b8eSHerbert Dürr #include "aqua/saldata.hxx"
2751747b8eSHerbert Dürr #include "aqua/salgdi.h"
2851747b8eSHerbert Dürr #include "atsfonts.hxx"
2951747b8eSHerbert Dürr
3051747b8eSHerbert Dürr #include "sallayout.hxx"
3151747b8eSHerbert Dürr #include "salgdi.hxx"
3251747b8eSHerbert Dürr
3351747b8eSHerbert Dürr #include <math.h>
3451747b8eSHerbert Dürr
3551747b8eSHerbert Dürr // =======================================================================
3651747b8eSHerbert Dürr
3751747b8eSHerbert Dürr class ATSLayout : public SalLayout
3851747b8eSHerbert Dürr {
3951747b8eSHerbert Dürr public:
40*e26449d3SHerbert Dürr explicit ATSLayout( ATSUStyle&, float fFontScale );
4151747b8eSHerbert Dürr virtual ~ATSLayout();
4251747b8eSHerbert Dürr
4351747b8eSHerbert Dürr virtual bool LayoutText( ImplLayoutArgs& );
4451747b8eSHerbert Dürr virtual void AdjustLayout( ImplLayoutArgs& );
4551747b8eSHerbert Dürr virtual void DrawText( SalGraphics& ) const;
4651747b8eSHerbert Dürr
47*e26449d3SHerbert Dürr virtual int GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
4851747b8eSHerbert Dürr sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
4951747b8eSHerbert Dürr
5051747b8eSHerbert Dürr virtual long GetTextWidth() const;
5151747b8eSHerbert Dürr virtual long FillDXArray( long* pDXArray ) const;
5251747b8eSHerbert Dürr virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
5351747b8eSHerbert Dürr virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
5451747b8eSHerbert Dürr virtual bool GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
5551747b8eSHerbert Dürr virtual bool GetBoundRect( SalGraphics&, Rectangle& ) const;
5651747b8eSHerbert Dürr
5751747b8eSHerbert Dürr const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
5851747b8eSHerbert Dürr
5951747b8eSHerbert Dürr virtual void InitFont();
6051747b8eSHerbert Dürr virtual void MoveGlyph( int nStart, long nNewXPos );
6151747b8eSHerbert Dürr virtual void DropGlyph( int nStart );
6251747b8eSHerbert Dürr virtual void Simplify( bool bIsBase );
6351747b8eSHerbert Dürr
6451747b8eSHerbert Dürr private:
6551747b8eSHerbert Dürr ATSUStyle& mrATSUStyle;
6651747b8eSHerbert Dürr ATSUTextLayout maATSULayout;
6751747b8eSHerbert Dürr int mnCharCount; // ==mnEndCharPos-mnMinCharPos
6851747b8eSHerbert Dürr // to prevent ATS overflowing the Fixed16.16 values
6951747b8eSHerbert Dürr // ATS font requests get size limited by downscaling huge fonts
7051747b8eSHerbert Dürr // in these cases the font scale becomes something bigger than 1.0
7151747b8eSHerbert Dürr float mfFontScale;
7251747b8eSHerbert Dürr
7351747b8eSHerbert Dürr private:
7451747b8eSHerbert Dürr bool InitGIA( ImplLayoutArgs* pArgs = NULL ) const;
7551747b8eSHerbert Dürr bool GetIdealX() const;
7651747b8eSHerbert Dürr bool GetDeltaY() const;
7751747b8eSHerbert Dürr void InvalidateMeasurements();
7851747b8eSHerbert Dürr
7951747b8eSHerbert Dürr int Fixed2Vcl( Fixed ) const; // convert ATSU-Fixed units to VCL units
8051747b8eSHerbert Dürr int AtsuPix2Vcl( int ) const; // convert ATSU-Pixel units to VCL units
8151747b8eSHerbert Dürr Fixed Vcl2Fixed( int ) const; // convert VCL units to ATSU-Fixed units
8251747b8eSHerbert Dürr
8351747b8eSHerbert Dürr // cached details about the resulting layout
8451747b8eSHerbert Dürr // mutable members since these details are all lazy initialized
8551747b8eSHerbert Dürr mutable int mnGlyphCount; // glyph count
8651747b8eSHerbert Dürr mutable Fixed mnCachedWidth; // cached value of resulting typographical width
8751747b8eSHerbert Dürr int mnTrailingSpaceWidth; // in Pixels
8851747b8eSHerbert Dürr
8951747b8eSHerbert Dürr mutable ATSGlyphRef* mpGlyphIds; // ATSU glyph ids
9051747b8eSHerbert Dürr mutable Fixed* mpCharWidths; // map relative charpos to charwidth
9151747b8eSHerbert Dürr mutable int* mpChars2Glyphs; // map relative charpos to absolute glyphpos
9251747b8eSHerbert Dürr mutable int* mpGlyphs2Chars; // map absolute glyphpos to absolute charpos
9351747b8eSHerbert Dürr mutable bool* mpGlyphRTLFlags; // BiDi status for glyphs: true if RTL
9451747b8eSHerbert Dürr mutable Fixed* mpGlyphAdvances; // contains glyph widths for the justified layout
9551747b8eSHerbert Dürr mutable Fixed* mpGlyphOrigAdvs; // contains glyph widths for the unjustified layout
9651747b8eSHerbert Dürr mutable Fixed* mpDeltaY; // vertical offset from the baseline
9751747b8eSHerbert Dürr
9851747b8eSHerbert Dürr struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; };
9951747b8eSHerbert Dürr typedef std::vector<SubPortion> SubPortionVector;
10051747b8eSHerbert Dürr mutable SubPortionVector maSubPortions; // Writer&ATSUI layouts can differ quite a bit...
10151747b8eSHerbert Dürr
10251747b8eSHerbert Dürr // storing details about fonts used in glyph-fallback for this layout
10351747b8eSHerbert Dürr mutable class FallbackInfo* mpFallbackInfo;
10451747b8eSHerbert Dürr
10551747b8eSHerbert Dürr // x-offset relative to layout origin
10651747b8eSHerbert Dürr // currently only used in RTL-layouts
10751747b8eSHerbert Dürr mutable Fixed mnBaseAdv;
10851747b8eSHerbert Dürr };
10951747b8eSHerbert Dürr
11051747b8eSHerbert Dürr class FallbackInfo
11151747b8eSHerbert Dürr {
11251747b8eSHerbert Dürr public:
FallbackInfo()11351747b8eSHerbert Dürr FallbackInfo() : mnMaxLevel(0) {}
11451747b8eSHerbert Dürr int AddFallback( ATSUFontID );
11551747b8eSHerbert Dürr const ImplFontData* GetFallbackFontData( int nLevel ) const;
11651747b8eSHerbert Dürr
11751747b8eSHerbert Dürr private:
11851747b8eSHerbert Dürr const ImplMacFontData* maFontData[ MAX_FALLBACK ];
11951747b8eSHerbert Dürr ATSUFontID maATSUFontId[ MAX_FALLBACK ];
12051747b8eSHerbert Dürr int mnMaxLevel;
12151747b8eSHerbert Dürr };
12251747b8eSHerbert Dürr
12351747b8eSHerbert Dürr // =======================================================================
12451747b8eSHerbert Dürr
ATSLayout(ATSUStyle & rATSUStyle,float fFontScale)12551747b8eSHerbert Dürr ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
12651747b8eSHerbert Dürr : mrATSUStyle( rATSUStyle ),
12751747b8eSHerbert Dürr maATSULayout( NULL ),
12851747b8eSHerbert Dürr mnCharCount( 0 ),
12951747b8eSHerbert Dürr mfFontScale( fFontScale ),
13051747b8eSHerbert Dürr mnGlyphCount( -1 ),
13151747b8eSHerbert Dürr mnCachedWidth( 0 ),
13251747b8eSHerbert Dürr mnTrailingSpaceWidth( 0 ),
13351747b8eSHerbert Dürr mpGlyphIds( NULL ),
13451747b8eSHerbert Dürr mpCharWidths( NULL ),
13551747b8eSHerbert Dürr mpChars2Glyphs( NULL ),
13651747b8eSHerbert Dürr mpGlyphs2Chars( NULL ),
13751747b8eSHerbert Dürr mpGlyphRTLFlags( NULL ),
13851747b8eSHerbert Dürr mpGlyphAdvances( NULL ),
13951747b8eSHerbert Dürr mpGlyphOrigAdvs( NULL ),
14051747b8eSHerbert Dürr mpDeltaY( NULL ),
14151747b8eSHerbert Dürr mpFallbackInfo( NULL ),
14251747b8eSHerbert Dürr mnBaseAdv( 0 )
14351747b8eSHerbert Dürr {}
14451747b8eSHerbert Dürr
14551747b8eSHerbert Dürr // -----------------------------------------------------------------------
14651747b8eSHerbert Dürr
~ATSLayout()14751747b8eSHerbert Dürr ATSLayout::~ATSLayout()
14851747b8eSHerbert Dürr {
14951747b8eSHerbert Dürr if( mpDeltaY )
15051747b8eSHerbert Dürr ATSUDirectReleaseLayoutDataArrayPtr( NULL,
15151747b8eSHerbert Dürr kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
15251747b8eSHerbert Dürr
15351747b8eSHerbert Dürr if( maATSULayout )
15451747b8eSHerbert Dürr ATSUDisposeTextLayout( maATSULayout );
15551747b8eSHerbert Dürr
15651747b8eSHerbert Dürr delete[] mpGlyphRTLFlags;
15751747b8eSHerbert Dürr delete[] mpGlyphs2Chars;
15851747b8eSHerbert Dürr delete[] mpChars2Glyphs;
15951747b8eSHerbert Dürr if( mpCharWidths != mpGlyphAdvances )
16051747b8eSHerbert Dürr delete[] mpCharWidths;
16151747b8eSHerbert Dürr delete[] mpGlyphIds;
16251747b8eSHerbert Dürr delete[] mpGlyphOrigAdvs;
16351747b8eSHerbert Dürr delete[] mpGlyphAdvances;
16451747b8eSHerbert Dürr
16551747b8eSHerbert Dürr delete mpFallbackInfo;
16651747b8eSHerbert Dürr }
16751747b8eSHerbert Dürr
16851747b8eSHerbert Dürr // -----------------------------------------------------------------------
16951747b8eSHerbert Dürr
Fixed2Vcl(Fixed nFixed) const17051747b8eSHerbert Dürr inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const
17151747b8eSHerbert Dürr {
17251747b8eSHerbert Dürr float fFloat = mfFontScale * FixedToFloat( nFixed );
17351747b8eSHerbert Dürr return static_cast<int>(fFloat + 0.5);
17451747b8eSHerbert Dürr }
17551747b8eSHerbert Dürr
17651747b8eSHerbert Dürr // -----------------------------------------------------------------------
17751747b8eSHerbert Dürr
AtsuPix2Vcl(int nAtsuPixel) const17851747b8eSHerbert Dürr inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const
17951747b8eSHerbert Dürr {
18051747b8eSHerbert Dürr float fVclPixel = mfFontScale * nAtsuPixel;
18151747b8eSHerbert Dürr fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5; // prepare rounding to int
18251747b8eSHerbert Dürr int nVclPixel = static_cast<int>( fVclPixel);
18351747b8eSHerbert Dürr return nVclPixel;
18451747b8eSHerbert Dürr }
18551747b8eSHerbert Dürr
18651747b8eSHerbert Dürr // -----------------------------------------------------------------------
18751747b8eSHerbert Dürr
Vcl2Fixed(int nPixel) const18851747b8eSHerbert Dürr inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const
18951747b8eSHerbert Dürr {
19051747b8eSHerbert Dürr return FloatToFixed( nPixel / mfFontScale );
19151747b8eSHerbert Dürr }
19251747b8eSHerbert Dürr
19351747b8eSHerbert Dürr // -----------------------------------------------------------------------
19451747b8eSHerbert Dürr /**
19551747b8eSHerbert Dürr * ATSLayout::LayoutText : Manage text layouting
19651747b8eSHerbert Dürr *
19751747b8eSHerbert Dürr * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout
19851747b8eSHerbert Dürr *
19951747b8eSHerbert Dürr * Typographic layout of text by using the style maATSUStyle
20051747b8eSHerbert Dürr *
20151747b8eSHerbert Dürr * @return : true if everything is ok
20251747b8eSHerbert Dürr **/
LayoutText(ImplLayoutArgs & rArgs)20351747b8eSHerbert Dürr bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs )
20451747b8eSHerbert Dürr {
20551747b8eSHerbert Dürr if( maATSULayout )
20651747b8eSHerbert Dürr ATSUDisposeTextLayout( maATSULayout );
20751747b8eSHerbert Dürr
20851747b8eSHerbert Dürr maATSULayout = NULL;
20951747b8eSHerbert Dürr
21051747b8eSHerbert Dürr // Layout text
21151747b8eSHerbert Dürr // set up our locals, verify parameters...
21251747b8eSHerbert Dürr DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!");
21351747b8eSHerbert Dürr DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!");
21451747b8eSHerbert Dürr
21551747b8eSHerbert Dürr SalLayout::AdjustLayout( rArgs );
21651747b8eSHerbert Dürr mnCharCount = mnEndCharPos - mnMinCharPos;
21751747b8eSHerbert Dürr
21851747b8eSHerbert Dürr // Workaround a bug in ATSUI with empty string
21951747b8eSHerbert Dürr if( mnCharCount<=0 )
22051747b8eSHerbert Dürr return false;
22151747b8eSHerbert Dürr
22251747b8eSHerbert Dürr // create the ATSUI layout
22351747b8eSHerbert Dürr UniCharCount nRunLengths[1] = { mnCharCount };
22451747b8eSHerbert Dürr const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths);
22551747b8eSHerbert Dürr OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr,
22651747b8eSHerbert Dürr rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength,
227*e26449d3SHerbert Dürr nRunCount, &nRunLengths[0], &mrATSUStyle, &maATSULayout);
22851747b8eSHerbert Dürr
22951747b8eSHerbert Dürr DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n");
23051747b8eSHerbert Dürr if( eStatus != noErr )
23151747b8eSHerbert Dürr return false;
23251747b8eSHerbert Dürr
23351747b8eSHerbert Dürr // prepare setting of layout controls
23451747b8eSHerbert Dürr static const int nMaxTagCount = 1;
23551747b8eSHerbert Dürr ATSUAttributeTag aTagAttrs[ nMaxTagCount ];
23651747b8eSHerbert Dürr ByteCount aTagSizes[ nMaxTagCount ];
23751747b8eSHerbert Dürr ATSUAttributeValuePtr aTagValues[ nMaxTagCount ];
23851747b8eSHerbert Dürr
23951747b8eSHerbert Dürr // prepare control of "glyph fallback"
24051747b8eSHerbert Dürr const SalData* pSalData = GetSalData();
24151747b8eSHerbert Dürr ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks;
24251747b8eSHerbert Dürr aTagAttrs[0] = kATSULineFontFallbacksTag;
24351747b8eSHerbert Dürr aTagSizes[0] = sizeof( ATSUFontFallbacks );
24451747b8eSHerbert Dürr aTagValues[0] = &aFontFallbacks;
24551747b8eSHerbert Dürr
24651747b8eSHerbert Dürr // set paragraph layout controls
24751747b8eSHerbert Dürr ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
24851747b8eSHerbert Dürr
24951747b8eSHerbert Dürr // enable "glyph fallback"
25051747b8eSHerbert Dürr ATSUSetTransientFontMatching( maATSULayout, true );
25151747b8eSHerbert Dürr
25251747b8eSHerbert Dürr // control run-specific layout controls
25351747b8eSHerbert Dürr if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 )
25451747b8eSHerbert Dürr {
25551747b8eSHerbert Dürr // control BiDi defaults
25651747b8eSHerbert Dürr BOOL nLineDirTag = kATSULeftToRightBaseDirection;
25751747b8eSHerbert Dürr if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 )
25851747b8eSHerbert Dürr nLineDirTag = kATSURightToLeftBaseDirection;
25951747b8eSHerbert Dürr aTagAttrs[0] = kATSULineDirectionTag;
26051747b8eSHerbert Dürr aTagSizes[0] = sizeof( nLineDirTag );
26151747b8eSHerbert Dürr aTagValues[0] = &nLineDirTag;
26251747b8eSHerbert Dürr // set run-specific layout controls
263*e26449d3SHerbert Dürr #if 0 // why don't line-controls work as reliable as layout-controls???
26451747b8eSHerbert Dürr ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues );
26551747b8eSHerbert Dürr #else
26651747b8eSHerbert Dürr ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
26751747b8eSHerbert Dürr #endif
26851747b8eSHerbert Dürr }
26951747b8eSHerbert Dürr
27051747b8eSHerbert Dürr return true;
27151747b8eSHerbert Dürr }
27251747b8eSHerbert Dürr
27351747b8eSHerbert Dürr // -----------------------------------------------------------------------
27451747b8eSHerbert Dürr /**
27551747b8eSHerbert Dürr * ATSLayout::AdjustLayout : Adjust layout style
27651747b8eSHerbert Dürr *
27751747b8eSHerbert Dürr * @param rArgs: contains attributes relevant to do a text specific layout
27851747b8eSHerbert Dürr *
27951747b8eSHerbert Dürr * Adjust text layout by moving glyphs to match the requested logical widths
28051747b8eSHerbert Dürr *
28151747b8eSHerbert Dürr * @return : none
28251747b8eSHerbert Dürr **/
AdjustLayout(ImplLayoutArgs & rArgs)28351747b8eSHerbert Dürr void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs )
28451747b8eSHerbert Dürr {
28551747b8eSHerbert Dürr int nOrigWidth = GetTextWidth();
28651747b8eSHerbert Dürr int nPixelWidth = rArgs.mnLayoutWidth;
28751747b8eSHerbert Dürr if( !nPixelWidth && rArgs.mpDXArray ) {
28851747b8eSHerbert Dürr // for now we are only interested in the layout width
28951747b8eSHerbert Dürr // TODO: use all mpDXArray elements for layouting
29051747b8eSHerbert Dürr nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ];
29151747b8eSHerbert Dürr
29251747b8eSHerbert Dürr // workaround for ATSUI not using trailing spaces for justification
29351747b8eSHerbert Dürr int i = mnCharCount;
29451747b8eSHerbert Dürr while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {}
29551747b8eSHerbert Dürr if( i < 0 ) // nothing to do if the text is all spaces
29651747b8eSHerbert Dürr return;
29751747b8eSHerbert Dürr // #i91685# trailing letters are left aligned (right aligned for RTL)
29851747b8eSHerbert Dürr mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ];
29951747b8eSHerbert Dürr if( i > 0 )
30051747b8eSHerbert Dürr mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ];
30151747b8eSHerbert Dürr InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead?
30251747b8eSHerbert Dürr mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] );
30351747b8eSHerbert Dürr // ignore trailing space for calculating the available width
30451747b8eSHerbert Dürr nOrigWidth -= mnTrailingSpaceWidth;
30551747b8eSHerbert Dürr nPixelWidth -= mnTrailingSpaceWidth;
30651747b8eSHerbert Dürr // in RTL-layouts trailing spaces are leftmost
30751747b8eSHerbert Dürr // TODO: use BiDi-algorithm to thoroughly check this assumption
30851747b8eSHerbert Dürr if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
30951747b8eSHerbert Dürr mnBaseAdv = mnTrailingSpaceWidth;
31051747b8eSHerbert Dürr }
31151747b8eSHerbert Dürr // return early if there is nothing to do
31251747b8eSHerbert Dürr if( !nPixelWidth )
31351747b8eSHerbert Dürr return;
31451747b8eSHerbert Dürr
31551747b8eSHerbert Dürr // HACK: justification requests which change the width by just one pixel were probably
31651747b8eSHerbert Dürr // #i86038# introduced by lossy conversions between integer based coordinate system
31751747b8eSHerbert Dürr // => ignoring such requests has many more benefits than eventual drawbacks
31851747b8eSHerbert Dürr if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
31951747b8eSHerbert Dürr return;
32051747b8eSHerbert Dürr
32151747b8eSHerbert Dürr // changing the layout will make all previous measurements invalid
32251747b8eSHerbert Dürr InvalidateMeasurements();
32351747b8eSHerbert Dürr
32451747b8eSHerbert Dürr ATSUAttributeTag nTags[3];
32551747b8eSHerbert Dürr ATSUAttributeValuePtr nVals[3];
32651747b8eSHerbert Dürr ByteCount nBytes[3];
32751747b8eSHerbert Dürr
32851747b8eSHerbert Dürr Fixed nFixedWidth = Vcl2Fixed( nPixelWidth );
32951747b8eSHerbert Dürr mnCachedWidth = nFixedWidth;
33051747b8eSHerbert Dürr Fract nFractFactor = kATSUFullJustification;
33151747b8eSHerbert Dürr ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter;
33251747b8eSHerbert Dürr
33351747b8eSHerbert Dürr nTags[0] = kATSULineWidthTag;
33451747b8eSHerbert Dürr nVals[0] = &nFixedWidth;
33551747b8eSHerbert Dürr nBytes[0] = sizeof(Fixed);
33651747b8eSHerbert Dürr nTags[1] = kATSULineLayoutOptionsTag;
33751747b8eSHerbert Dürr nVals[1] = &nLineLayoutOptions;
33851747b8eSHerbert Dürr nBytes[1] = sizeof(ATSLineLayoutOptions);
33951747b8eSHerbert Dürr nTags[2] = kATSULineJustificationFactorTag;
34051747b8eSHerbert Dürr nVals[2] = &nFractFactor;
34151747b8eSHerbert Dürr nBytes[2] = sizeof(Fract);
34251747b8eSHerbert Dürr
34351747b8eSHerbert Dürr OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals );
34451747b8eSHerbert Dürr if( eStatus != noErr )
34551747b8eSHerbert Dürr return;
34651747b8eSHerbert Dürr
34751747b8eSHerbert Dürr // update the measurements of the justified layout to match the justification request
34851747b8eSHerbert Dürr if( rArgs.mpDXArray )
34951747b8eSHerbert Dürr InitGIA( &rArgs );
35051747b8eSHerbert Dürr }
35151747b8eSHerbert Dürr
35251747b8eSHerbert Dürr // -----------------------------------------------------------------------
35351747b8eSHerbert Dürr /**
35451747b8eSHerbert Dürr * ATSLayout::DrawText : Draw text to screen
35551747b8eSHerbert Dürr *
35651747b8eSHerbert Dürr * @param rGraphics: device to draw to
35751747b8eSHerbert Dürr *
35851747b8eSHerbert Dürr * Draw the layouted text to the CGContext
35951747b8eSHerbert Dürr *
36051747b8eSHerbert Dürr * @return : none
36151747b8eSHerbert Dürr **/
DrawText(SalGraphics & rGraphics) const36251747b8eSHerbert Dürr void ATSLayout::DrawText( SalGraphics& rGraphics ) const
36351747b8eSHerbert Dürr {
36451747b8eSHerbert Dürr AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
36551747b8eSHerbert Dürr
36651747b8eSHerbert Dürr // short circuit if there is nothing to do
36751747b8eSHerbert Dürr if( (mnCharCount <= 0)
36851747b8eSHerbert Dürr || !rAquaGraphics.CheckContext() )
36951747b8eSHerbert Dürr return;
37051747b8eSHerbert Dürr
37151747b8eSHerbert Dürr // the view is vertically flipped => flipped glyphs
37251747b8eSHerbert Dürr // so apply a temporary transformation that it flips back
37351747b8eSHerbert Dürr // also compensate if the font was size limited
37451747b8eSHerbert Dürr CGContextSaveGState( rAquaGraphics.mrContext );
37551747b8eSHerbert Dürr CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
37651747b8eSHerbert Dürr CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
37751747b8eSHerbert Dürr
37851747b8eSHerbert Dürr // prepare ATSUI drawing attributes
37951747b8eSHerbert Dürr static const ItemCount nMaxControls = 8;
38051747b8eSHerbert Dürr ATSUAttributeTag theTags[ nMaxControls ];
38151747b8eSHerbert Dürr ByteCount theSizes[ nMaxControls];
38251747b8eSHerbert Dürr ATSUAttributeValuePtr theValues[ nMaxControls ];
38351747b8eSHerbert Dürr ItemCount numcontrols = 0;
38451747b8eSHerbert Dürr
38551747b8eSHerbert Dürr // Tell ATSUI to use CoreGraphics
38651747b8eSHerbert Dürr theTags[numcontrols] = kATSUCGContextTag;
38751747b8eSHerbert Dürr theSizes[numcontrols] = sizeof( CGContextRef );
38851747b8eSHerbert Dürr theValues[numcontrols++] = &rAquaGraphics.mrContext;
38951747b8eSHerbert Dürr
39051747b8eSHerbert Dürr // Rotate if necessary
39151747b8eSHerbert Dürr if( rAquaGraphics.mnATSUIRotation != 0 )
39251747b8eSHerbert Dürr {
39351747b8eSHerbert Dürr Fixed theAngle = rAquaGraphics.mnATSUIRotation;
39451747b8eSHerbert Dürr theTags[numcontrols] = kATSULineRotationTag;
39551747b8eSHerbert Dürr theSizes[numcontrols] = sizeof( Fixed );
39651747b8eSHerbert Dürr theValues[numcontrols++] = &theAngle;
39751747b8eSHerbert Dürr }
39851747b8eSHerbert Dürr
39951747b8eSHerbert Dürr DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" );
40051747b8eSHerbert Dürr OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues);
40151747b8eSHerbert Dürr DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" );
40251747b8eSHerbert Dürr
40351747b8eSHerbert Dürr // Draw the text
40451747b8eSHerbert Dürr const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) );
40551747b8eSHerbert Dürr const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
40651747b8eSHerbert Dürr const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring
40751747b8eSHerbert Dürr if( maSubPortions.empty() )
40851747b8eSHerbert Dürr ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY );
40951747b8eSHerbert Dürr else
41051747b8eSHerbert Dürr {
41151747b8eSHerbert Dürr // draw the sub-portions and apply individual adjustments
41251747b8eSHerbert Dürr SubPortionVector::const_iterator it = maSubPortions.begin();
41351747b8eSHerbert Dürr for(; it != maSubPortions.end(); ++it )
41451747b8eSHerbert Dürr {
41551747b8eSHerbert Dürr const SubPortion& rSubPortion = *it;
41651747b8eSHerbert Dürr // calculate sub-portion offset for rotated text
41751747b8eSHerbert Dürr Fixed nXOfsFixed = 0, nYOfsFixed = 0;
41851747b8eSHerbert Dürr if( rAquaGraphics.mnATSUIRotation != 0 )
41951747b8eSHerbert Dürr {
42051747b8eSHerbert Dürr const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000);
42151747b8eSHerbert Dürr nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians ));
42251747b8eSHerbert Dürr nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians ));
42351747b8eSHerbert Dürr }
42451747b8eSHerbert Dürr
42551747b8eSHerbert Dürr // draw sub-portions
42651747b8eSHerbert Dürr ATSUDrawText( maATSULayout,
42751747b8eSHerbert Dürr rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos,
42851747b8eSHerbert Dürr nFixedX + nXOfsFixed, nFixedY + nYOfsFixed );
42951747b8eSHerbert Dürr }
43051747b8eSHerbert Dürr }
43151747b8eSHerbert Dürr
43251747b8eSHerbert Dürr // request an update of the changed window area
43351747b8eSHerbert Dürr if( rAquaGraphics.IsWindowGraphics() )
43451747b8eSHerbert Dürr {
43551747b8eSHerbert Dürr Rect drawRect; // rectangle of the changed area
43651747b8eSHerbert Dürr theErr = ATSUMeasureTextImage( maATSULayout,
43751747b8eSHerbert Dürr mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect );
43851747b8eSHerbert Dürr if( theErr == noErr )
43951747b8eSHerbert Dürr {
44051747b8eSHerbert Dürr // FIXME: transformation from baseline to top left
44151747b8eSHerbert Dürr // with the simple approach below we invalidate too much
44251747b8eSHerbert Dürr short d = drawRect.bottom - drawRect.top;
44351747b8eSHerbert Dürr drawRect.top -= d;
44451747b8eSHerbert Dürr drawRect.bottom += d;
44551747b8eSHerbert Dürr CGRect aRect = CGRectMake( drawRect.left, drawRect.top,
44651747b8eSHerbert Dürr drawRect.right - drawRect.left,
44751747b8eSHerbert Dürr drawRect.bottom - drawRect.top );
44851747b8eSHerbert Dürr aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect );
44951747b8eSHerbert Dürr rAquaGraphics.RefreshRect( aRect );
45051747b8eSHerbert Dürr }
45151747b8eSHerbert Dürr }
45251747b8eSHerbert Dürr
45351747b8eSHerbert Dürr // restore the original graphic context transformations
45451747b8eSHerbert Dürr CGContextRestoreGState( rAquaGraphics.mrContext );
45551747b8eSHerbert Dürr }
45651747b8eSHerbert Dürr
45751747b8eSHerbert Dürr // -----------------------------------------------------------------------
45851747b8eSHerbert Dürr /**
45951747b8eSHerbert Dürr * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout
46051747b8eSHerbert Dürr *
46151747b8eSHerbert Dürr * @param nLen: max number of char
46251747b8eSHerbert Dürr * @param pGlyphs: returned array of glyph ids
46351747b8eSHerbert Dürr * @param rPos: returned x starting position
46451747b8eSHerbert Dürr * @param nStart: index of the first requested glyph
46551747b8eSHerbert Dürr * @param pGlyphAdvances: returned array of glyphs advances
46651747b8eSHerbert Dürr * @param pCharIndexes: returned array of char indexes
46751747b8eSHerbert Dürr *
46851747b8eSHerbert Dürr * Returns infos about the next glyphs in the text layout
46951747b8eSHerbert Dürr *
47051747b8eSHerbert Dürr * @return : number of glyph details that were provided
47151747b8eSHerbert Dürr **/
GetNextGlyphs(int nLen,sal_GlyphId * pOutGlyphIds,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvances,int * pCharIndexes) const47251747b8eSHerbert Dürr int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
47351747b8eSHerbert Dürr sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
47451747b8eSHerbert Dürr {
47551747b8eSHerbert Dürr if( nStart < 0 ) // first glyph requested?
47651747b8eSHerbert Dürr nStart = 0;
47751747b8eSHerbert Dürr
47851747b8eSHerbert Dürr // get glyph measurements
47951747b8eSHerbert Dürr InitGIA();
48051747b8eSHerbert Dürr // some measurements are only needed for multi-glyph results
48151747b8eSHerbert Dürr if( nLen > 1 )
48251747b8eSHerbert Dürr {
48351747b8eSHerbert Dürr GetIdealX();
48451747b8eSHerbert Dürr GetDeltaY();
48551747b8eSHerbert Dürr }
48651747b8eSHerbert Dürr
48751747b8eSHerbert Dürr if( nStart >= mnGlyphCount ) // no glyph left?
48851747b8eSHerbert Dürr return 0;
48951747b8eSHerbert Dürr
49051747b8eSHerbert Dürr // calculate glyph position relative to layout base
49151747b8eSHerbert Dürr // TODO: avoid for nStart!=0 case by reusing rPos
49251747b8eSHerbert Dürr Fixed nXOffset = mnBaseAdv;
49351747b8eSHerbert Dürr for( int i = 0; i < nStart; ++i )
49451747b8eSHerbert Dürr nXOffset += mpGlyphAdvances[ i ];
49551747b8eSHerbert Dürr // if sub-portion offsets are involved there is an additional x-offset
49651747b8eSHerbert Dürr if( !maSubPortions.empty() )
49751747b8eSHerbert Dürr {
49851747b8eSHerbert Dürr // prepare to find the sub-portion
49951747b8eSHerbert Dürr int nCharPos = nStart + mnMinCharPos;
50051747b8eSHerbert Dürr if( mpGlyphs2Chars )
50151747b8eSHerbert Dürr nCharPos = mpGlyphs2Chars[nStart];
50251747b8eSHerbert Dürr
50351747b8eSHerbert Dürr // find the matching subportion
50451747b8eSHerbert Dürr // TODO: is a non-linear search worth it?
50551747b8eSHerbert Dürr SubPortionVector::const_iterator it = maSubPortions.begin();
50651747b8eSHerbert Dürr for(; it != maSubPortions.end(); ++it) {
50751747b8eSHerbert Dürr const SubPortion& r = *it;
50851747b8eSHerbert Dürr if( nCharPos < r.mnMinCharPos )
50951747b8eSHerbert Dürr continue;
51051747b8eSHerbert Dürr if( nCharPos >= r.mnEndCharPos )
51151747b8eSHerbert Dürr continue;
51251747b8eSHerbert Dürr // apply the sub-portion xoffset
51351747b8eSHerbert Dürr nXOffset += r.mnXOffset;
51451747b8eSHerbert Dürr break;
51551747b8eSHerbert Dürr }
51651747b8eSHerbert Dürr }
51751747b8eSHerbert Dürr
51851747b8eSHerbert Dürr Fixed nYOffset = 0;
51951747b8eSHerbert Dürr if( mpDeltaY )
52051747b8eSHerbert Dürr nYOffset = mpDeltaY[ nStart ];
52151747b8eSHerbert Dürr
52251747b8eSHerbert Dürr // calculate absolute position in pixel units
52351747b8eSHerbert Dürr const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) );
52451747b8eSHerbert Dürr rPos = GetDrawPosition( aRelativePos );
52551747b8eSHerbert Dürr
52651747b8eSHerbert Dürr // update return values
52751747b8eSHerbert Dürr int nCount = 0;
52851747b8eSHerbert Dürr while( nCount < nLen )
52951747b8eSHerbert Dürr {
53051747b8eSHerbert Dürr ++nCount;
53151747b8eSHerbert Dürr sal_GlyphId aGlyphId = mpGlyphIds[nStart];
53251747b8eSHerbert Dürr
53351747b8eSHerbert Dürr // check if glyph fallback is needed for this glyph
53451747b8eSHerbert Dürr // TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead?
53551747b8eSHerbert Dürr const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos;
53651747b8eSHerbert Dürr ATSUFontID nFallbackFontID = kATSUInvalidFontID;
53751747b8eSHerbert Dürr UniCharArrayOffset nChangedOffset = 0;
53851747b8eSHerbert Dürr UniCharCount nChangedLength = 0;
53951747b8eSHerbert Dürr OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd,
54051747b8eSHerbert Dürr &nFallbackFontID, &nChangedOffset, &nChangedLength );
54151747b8eSHerbert Dürr if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) )
54251747b8eSHerbert Dürr {
54351747b8eSHerbert Dürr // fallback is needed
54451747b8eSHerbert Dürr if( !mpFallbackInfo )
54551747b8eSHerbert Dürr mpFallbackInfo = new FallbackInfo;
54651747b8eSHerbert Dürr // register fallback font
54751747b8eSHerbert Dürr const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID );
54851747b8eSHerbert Dürr // update sal_GlyphId with fallback level
54951747b8eSHerbert Dürr aGlyphId |= (nLevel << GF_FONTSHIFT);
55051747b8eSHerbert Dürr }
55151747b8eSHerbert Dürr
55251747b8eSHerbert Dürr // update resulting glyphid array
55351747b8eSHerbert Dürr *(pOutGlyphIds++) = aGlyphId;
55451747b8eSHerbert Dürr
55551747b8eSHerbert Dürr // update returned glyph advance array
55651747b8eSHerbert Dürr if( pGlyphAdvances )
55751747b8eSHerbert Dürr *(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] );
55851747b8eSHerbert Dürr
55951747b8eSHerbert Dürr // update returned index-into-string array
56051747b8eSHerbert Dürr if( pCharIndexes )
56151747b8eSHerbert Dürr {
56251747b8eSHerbert Dürr int nCharPos;
56351747b8eSHerbert Dürr if( mpGlyphs2Chars )
56451747b8eSHerbert Dürr nCharPos = mpGlyphs2Chars[nStart];
56551747b8eSHerbert Dürr else
56651747b8eSHerbert Dürr nCharPos = nStart + mnMinCharPos;
56751747b8eSHerbert Dürr *(pCharIndexes++) = nCharPos;
56851747b8eSHerbert Dürr }
56951747b8eSHerbert Dürr
57051747b8eSHerbert Dürr // stop at last glyph
57151747b8eSHerbert Dürr if( ++nStart >= mnGlyphCount )
57251747b8eSHerbert Dürr break;
57351747b8eSHerbert Dürr
57451747b8eSHerbert Dürr // stop when next the x-position is unexpected
57551747b8eSHerbert Dürr if( !maSubPortions.empty() )
57651747b8eSHerbert Dürr break; // TODO: finish the complete sub-portion
57751747b8eSHerbert Dürr if( !pGlyphAdvances && mpGlyphOrigAdvs )
57851747b8eSHerbert Dürr if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
57951747b8eSHerbert Dürr break;
58051747b8eSHerbert Dürr
58151747b8eSHerbert Dürr // stop when the next y-position is unexpected
58251747b8eSHerbert Dürr if( mpDeltaY )
58351747b8eSHerbert Dürr if( mpDeltaY[nStart-1] != mpDeltaY[nStart] )
58451747b8eSHerbert Dürr break;
58551747b8eSHerbert Dürr }
58651747b8eSHerbert Dürr
58751747b8eSHerbert Dürr return nCount;
58851747b8eSHerbert Dürr }
58951747b8eSHerbert Dürr
59051747b8eSHerbert Dürr // -----------------------------------------------------------------------
59151747b8eSHerbert Dürr /**
59251747b8eSHerbert Dürr * ATSLayout::GetTextWidth : Get typographic width of layouted text
59351747b8eSHerbert Dürr *
59451747b8eSHerbert Dürr * Get typographic bounds of the text
59551747b8eSHerbert Dürr *
59651747b8eSHerbert Dürr * @return : text width
59751747b8eSHerbert Dürr **/
GetTextWidth() const59851747b8eSHerbert Dürr long ATSLayout::GetTextWidth() const
59951747b8eSHerbert Dürr {
60051747b8eSHerbert Dürr if( mnCharCount <= 0 )
60151747b8eSHerbert Dürr return 0;
60251747b8eSHerbert Dürr
60351747b8eSHerbert Dürr DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n");
60451747b8eSHerbert Dürr if( !maATSULayout )
60551747b8eSHerbert Dürr return 0;
60651747b8eSHerbert Dürr
60751747b8eSHerbert Dürr if( !mnCachedWidth )
60851747b8eSHerbert Dürr {
60951747b8eSHerbert Dürr // prepare precise measurements on pixel based or reference-device
61051747b8eSHerbert Dürr const UInt16 eTypeOfBounds = kATSUseFractionalOrigins;
61151747b8eSHerbert Dürr
61251747b8eSHerbert Dürr // determine number of needed measurement trapezoids
61351747b8eSHerbert Dürr ItemCount nMaxBounds = 0;
61451747b8eSHerbert Dürr OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
61551747b8eSHerbert Dürr eTypeOfBounds, 0, NULL, &nMaxBounds );
61651747b8eSHerbert Dürr if( (err != noErr)
61751747b8eSHerbert Dürr || (nMaxBounds <= 0) )
61851747b8eSHerbert Dürr return 0;
61951747b8eSHerbert Dürr
62051747b8eSHerbert Dürr // get the trapezoids
62151747b8eSHerbert Dürr typedef std::vector<ATSTrapezoid> TrapezoidVector;
62251747b8eSHerbert Dürr TrapezoidVector aTrapezoidVector( nMaxBounds );
62351747b8eSHerbert Dürr ItemCount nBoundsCount = 0;
62451747b8eSHerbert Dürr err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
62551747b8eSHerbert Dürr eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount );
62651747b8eSHerbert Dürr if( err != noErr )
62751747b8eSHerbert Dürr return 0;
62851747b8eSHerbert Dürr
62951747b8eSHerbert Dürr DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n");
63051747b8eSHerbert Dürr
63151747b8eSHerbert Dürr // find the bound extremas
63251747b8eSHerbert Dürr Fixed nLeftBound = 0;
63351747b8eSHerbert Dürr Fixed nRightBound = 0;
63451747b8eSHerbert Dürr for( ItemCount i = 0; i < nBoundsCount; ++i )
63551747b8eSHerbert Dürr {
63651747b8eSHerbert Dürr const ATSTrapezoid& rTrap = aTrapezoidVector[i];
63751747b8eSHerbert Dürr if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) )
63851747b8eSHerbert Dürr nLeftBound = rTrap.lowerLeft.x;
63951747b8eSHerbert Dürr if( (i == 0) || (nRightBound > rTrap.lowerRight.x) )
64051747b8eSHerbert Dürr nRightBound = rTrap.lowerRight.x;
64151747b8eSHerbert Dürr }
64251747b8eSHerbert Dürr
64351747b8eSHerbert Dürr // measure the bound extremas
64451747b8eSHerbert Dürr mnCachedWidth = nRightBound - nLeftBound;
64551747b8eSHerbert Dürr // adjust for eliminated trailing space widths
64651747b8eSHerbert Dürr }
64751747b8eSHerbert Dürr
64851747b8eSHerbert Dürr int nScaledWidth = Fixed2Vcl( mnCachedWidth );
64951747b8eSHerbert Dürr nScaledWidth += mnTrailingSpaceWidth;
65051747b8eSHerbert Dürr return nScaledWidth;
65151747b8eSHerbert Dürr }
65251747b8eSHerbert Dürr
65351747b8eSHerbert Dürr // -----------------------------------------------------------------------
65451747b8eSHerbert Dürr /**
65551747b8eSHerbert Dürr * ATSLayout::FillDXArray : Get Char widths
65651747b8eSHerbert Dürr *
65751747b8eSHerbert Dürr * @param pDXArray: array to be filled with x-advances
65851747b8eSHerbert Dürr *
65951747b8eSHerbert Dürr * Fill the pDXArray with horizontal deltas : CharWidths
66051747b8eSHerbert Dürr *
66151747b8eSHerbert Dürr * @return : typographical width of the complete text layout
66251747b8eSHerbert Dürr **/
FillDXArray(long * pDXArray) const66351747b8eSHerbert Dürr long ATSLayout::FillDXArray( long* pDXArray ) const
66451747b8eSHerbert Dürr {
66551747b8eSHerbert Dürr // short circuit requests which don't need full details
66651747b8eSHerbert Dürr if( !pDXArray )
66751747b8eSHerbert Dürr return GetTextWidth();
66851747b8eSHerbert Dürr
66951747b8eSHerbert Dürr // check assumptions
67051747b8eSHerbert Dürr DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" );
67151747b8eSHerbert Dürr
67251747b8eSHerbert Dürr // initialize details about the resulting layout
67351747b8eSHerbert Dürr InitGIA();
67451747b8eSHerbert Dürr
67551747b8eSHerbert Dürr // distribute the widths among the string elements
67651747b8eSHerbert Dürr int nPixWidth = 0;
67751747b8eSHerbert Dürr mnCachedWidth = 0;
67851747b8eSHerbert Dürr for( int i = 0; i < mnCharCount; ++i )
67951747b8eSHerbert Dürr {
68051747b8eSHerbert Dürr // convert and adjust for accumulated rounding errors
68151747b8eSHerbert Dürr mnCachedWidth += mpCharWidths[i];
68251747b8eSHerbert Dürr const int nOldPixWidth = nPixWidth;
68351747b8eSHerbert Dürr nPixWidth = Fixed2Vcl( mnCachedWidth );
68451747b8eSHerbert Dürr pDXArray[i] = nPixWidth - nOldPixWidth;
68551747b8eSHerbert Dürr }
68651747b8eSHerbert Dürr
68751747b8eSHerbert Dürr return nPixWidth;
68851747b8eSHerbert Dürr }
68951747b8eSHerbert Dürr
69051747b8eSHerbert Dürr // -----------------------------------------------------------------------
69151747b8eSHerbert Dürr /**
69251747b8eSHerbert Dürr * ATSLayout::GetTextBreak : Find line break depending on width
69351747b8eSHerbert Dürr *
69451747b8eSHerbert Dürr * @param nMaxWidth : maximal logical text width in subpixel units
69551747b8eSHerbert Dürr * @param nCharExtra: expanded/condensed spacing in subpixel units
69651747b8eSHerbert Dürr * @param nFactor: number of subpixel units per pixel
69751747b8eSHerbert Dürr *
69851747b8eSHerbert Dürr * Measure the layouted text to find the typographical line break
69951747b8eSHerbert Dürr * the result is needed by the language specific line breaking
70051747b8eSHerbert Dürr *
70151747b8eSHerbert Dürr * @return : string index corresponding to the suggested line break
70251747b8eSHerbert Dürr **/
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const70351747b8eSHerbert Dürr int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
70451747b8eSHerbert Dürr {
70551747b8eSHerbert Dürr if( !maATSULayout )
70651747b8eSHerbert Dürr return STRING_LEN;
70751747b8eSHerbert Dürr
70851747b8eSHerbert Dürr // the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine()
70951747b8eSHerbert Dürr if( nCharExtra != 0 )
71051747b8eSHerbert Dürr {
71151747b8eSHerbert Dürr // prepare the measurement by layouting and measuring the un-expanded/un-condensed text
71251747b8eSHerbert Dürr if( !InitGIA() )
71351747b8eSHerbert Dürr return STRING_LEN;
71451747b8eSHerbert Dürr
71551747b8eSHerbert Dürr // TODO: use a better way than by testing each the char position
71651747b8eSHerbert Dürr ATSUTextMeasurement nATSUSumWidth = 0;
71751747b8eSHerbert Dürr const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor );
71851747b8eSHerbert Dürr const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor;
71951747b8eSHerbert Dürr for( int i = 0; i < mnCharCount; ++i )
72051747b8eSHerbert Dürr {
72151747b8eSHerbert Dürr nATSUSumWidth += mpCharWidths[i];
72251747b8eSHerbert Dürr if( nATSUSumWidth >= nATSUMaxWidth )
72351747b8eSHerbert Dürr return (mnMinCharPos + i);
72451747b8eSHerbert Dürr nATSUSumWidth += nATSUExtraWidth;
72551747b8eSHerbert Dürr if( nATSUSumWidth >= nATSUMaxWidth )
72651747b8eSHerbert Dürr if( i+1 < mnCharCount )
72751747b8eSHerbert Dürr return (mnMinCharPos + i);
72851747b8eSHerbert Dürr }
72951747b8eSHerbert Dürr
73051747b8eSHerbert Dürr return STRING_LEN;
73151747b8eSHerbert Dürr }
73251747b8eSHerbert Dürr
73351747b8eSHerbert Dürr // get a quick overview on what could fit
73451747b8eSHerbert Dürr const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
73551747b8eSHerbert Dürr if( nPixelWidth <= 0 )
73651747b8eSHerbert Dürr return mnMinCharPos;
73751747b8eSHerbert Dürr
73851747b8eSHerbert Dürr // check assumptions
73951747b8eSHerbert Dürr DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" );
74051747b8eSHerbert Dürr
74151747b8eSHerbert Dürr // initial measurement of text break position
74251747b8eSHerbert Dürr UniCharArrayOffset nBreakPos = mnMinCharPos;
74351747b8eSHerbert Dürr const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth );
74451747b8eSHerbert Dürr if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
74551747b8eSHerbert Dürr return mnMinCharPos; // or do ATSUMaxWidth=0x10000;
74651747b8eSHerbert Dürr OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos,
74751747b8eSHerbert Dürr nATSUMaxWidth, false, &nBreakPos );
74851747b8eSHerbert Dürr if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) )
74951747b8eSHerbert Dürr return STRING_LEN;
75051747b8eSHerbert Dürr
75151747b8eSHerbert Dürr // the result from ATSUBreakLine() doesn't match the semantics expected by its
75251747b8eSHerbert Dürr // application layer callers from SW+SVX+I18N. Adjust the results to the expectations:
75351747b8eSHerbert Dürr
75451747b8eSHerbert Dürr // ATSU reports that everything fits even when trailing spaces would break the line
75551747b8eSHerbert Dürr // #i89789# OOo's application layers expect STRING_LEN if everything fits
75651747b8eSHerbert Dürr if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) )
75751747b8eSHerbert Dürr return STRING_LEN;
75851747b8eSHerbert Dürr
75951747b8eSHerbert Dürr // GetTextBreak()'s callers expect it to return the "stupid visual line break".
76051747b8eSHerbert Dürr // Returning anything else result.s in subtle problems in the application layers.
76151747b8eSHerbert Dürr static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method
76251747b8eSHerbert Dürr if( !bInWord )
76351747b8eSHerbert Dürr return nBreakPos;
76451747b8eSHerbert Dürr
76551747b8eSHerbert Dürr // emulate stupid visual line breaking by line breaking for the remaining width
76651747b8eSHerbert Dürr ATSUTextMeasurement nLeft, nRight, nDummy;
76751747b8eSHerbert Dürr eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos,
76851747b8eSHerbert Dürr &nLeft, &nRight, &nDummy, &nDummy );
76951747b8eSHerbert Dürr if( eStatus != noErr )
77051747b8eSHerbert Dürr return nBreakPos;
77151747b8eSHerbert Dürr const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft);
77251747b8eSHerbert Dürr if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
77351747b8eSHerbert Dürr return nBreakPos;
77451747b8eSHerbert Dürr UniCharArrayOffset nBreakPosInWord = nBreakPos;
77551747b8eSHerbert Dürr eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord );
77651747b8eSHerbert Dürr return nBreakPosInWord;
77751747b8eSHerbert Dürr }
77851747b8eSHerbert Dürr
77951747b8eSHerbert Dürr // -----------------------------------------------------------------------
78051747b8eSHerbert Dürr /**
78151747b8eSHerbert Dürr * ATSLayout::GetCaretPositions : Find positions of carets
78251747b8eSHerbert Dürr *
78351747b8eSHerbert Dürr * @param nMaxIndex position to which we want to find the carets
78451747b8eSHerbert Dürr *
78551747b8eSHerbert Dürr * Fill the array of positions of carets (for cursors and selections)
78651747b8eSHerbert Dürr *
78751747b8eSHerbert Dürr * @return : none
78851747b8eSHerbert Dürr **/
GetCaretPositions(int nMaxIndex,long * pCaretXArray) const78951747b8eSHerbert Dürr void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
79051747b8eSHerbert Dürr {
79151747b8eSHerbert Dürr DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
79251747b8eSHerbert Dürr "ATSLayout::GetCaretPositions() : invalid number of caret pairs requested");
79351747b8eSHerbert Dürr
79451747b8eSHerbert Dürr // initialize the caret positions
79551747b8eSHerbert Dürr for( int i = 0; i < nMaxIndex; ++i )
79651747b8eSHerbert Dürr pCaretXArray[ i ] = -1;
79751747b8eSHerbert Dürr
79851747b8eSHerbert Dürr for( int n = 0; n <= mnCharCount; ++n )
79951747b8eSHerbert Dürr {
80051747b8eSHerbert Dürr // measure the characters cursor position
80151747b8eSHerbert Dürr typedef unsigned char Boolean;
80251747b8eSHerbert Dürr const Boolean bIsLeading = true;
80351747b8eSHerbert Dürr ATSUCaret aCaret0, aCaret1;
80451747b8eSHerbert Dürr Boolean bIsSplit;
80551747b8eSHerbert Dürr OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout,
80651747b8eSHerbert Dürr mnMinCharPos + n, bIsLeading, kATSUByCharacter,
80751747b8eSHerbert Dürr &aCaret0, &aCaret1, &bIsSplit );
80851747b8eSHerbert Dürr if( eStatus != noErr )
80951747b8eSHerbert Dürr continue;
81051747b8eSHerbert Dürr const Fixed nFixedPos = mnBaseAdv + aCaret0.fX;
81151747b8eSHerbert Dürr // convert the measurement to pixel units
81251747b8eSHerbert Dürr const int nPixelPos = Fixed2Vcl( nFixedPos );
81351747b8eSHerbert Dürr // update previous trailing position
81451747b8eSHerbert Dürr if( n > 0 )
81551747b8eSHerbert Dürr pCaretXArray[2*n-1] = nPixelPos;
81651747b8eSHerbert Dürr // update current leading position
81751747b8eSHerbert Dürr if( 2*n >= nMaxIndex )
81851747b8eSHerbert Dürr break;
81951747b8eSHerbert Dürr pCaretXArray[2*n+0] = nPixelPos;
82051747b8eSHerbert Dürr }
82151747b8eSHerbert Dürr }
82251747b8eSHerbert Dürr
82351747b8eSHerbert Dürr // -----------------------------------------------------------------------
82451747b8eSHerbert Dürr /**
82551747b8eSHerbert Dürr * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text
82651747b8eSHerbert Dürr *
82751747b8eSHerbert Dürr * @param rVCLRect: rectangle of text image (layout) measures
82851747b8eSHerbert Dürr *
82951747b8eSHerbert Dürr * Get ink bounds of the text
83051747b8eSHerbert Dürr *
83151747b8eSHerbert Dürr * @return : measurement valid
83251747b8eSHerbert Dürr **/
GetBoundRect(SalGraphics &,Rectangle & rVCLRect) const83351747b8eSHerbert Dürr bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const
83451747b8eSHerbert Dürr {
83551747b8eSHerbert Dürr const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
83651747b8eSHerbert Dürr const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
83751747b8eSHerbert Dürr const Fixed nFixedY = Vcl2Fixed( +aPos.Y() );
83851747b8eSHerbert Dürr
83951747b8eSHerbert Dürr Rect aMacRect;
84051747b8eSHerbert Dürr OSStatus eStatus = ATSUMeasureTextImage( maATSULayout,
84151747b8eSHerbert Dürr mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect );
84251747b8eSHerbert Dürr if( eStatus != noErr )
84351747b8eSHerbert Dürr return false;
84451747b8eSHerbert Dürr
84551747b8eSHerbert Dürr // ATSU top-bottom are vertically flipped from a VCL aspect
84651747b8eSHerbert Dürr rVCLRect.Left() = AtsuPix2Vcl( aMacRect.left );
84751747b8eSHerbert Dürr rVCLRect.Top() = AtsuPix2Vcl( aMacRect.top );
84851747b8eSHerbert Dürr rVCLRect.Right() = AtsuPix2Vcl( aMacRect.right );
84951747b8eSHerbert Dürr rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom );
85051747b8eSHerbert Dürr return true;
85151747b8eSHerbert Dürr }
85251747b8eSHerbert Dürr
85351747b8eSHerbert Dürr // -----------------------------------------------------------------------
85451747b8eSHerbert Dürr /**
85551747b8eSHerbert Dürr * ATSLayout::InitGIA() : get many informations about layouted text
85651747b8eSHerbert Dürr *
85751747b8eSHerbert Dürr * Fills arrays of information about the gylph layout previously done
85851747b8eSHerbert Dürr * in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline),
85951747b8eSHerbert Dürr * mapping between glyph index and character index, chars widths
86051747b8eSHerbert Dürr *
86151747b8eSHerbert Dürr * @return : true if everything could be computed, otherwise false
86251747b8eSHerbert Dürr **/
InitGIA(ImplLayoutArgs * pArgs) const86351747b8eSHerbert Dürr bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const
86451747b8eSHerbert Dürr {
86551747b8eSHerbert Dürr // no need to run InitGIA more than once on the same ATSLayout object
86651747b8eSHerbert Dürr if( mnGlyphCount >= 0 )
86751747b8eSHerbert Dürr return true;
86851747b8eSHerbert Dürr mnGlyphCount = 0;
86951747b8eSHerbert Dürr
87051747b8eSHerbert Dürr // Workaround a bug in ATSUI with empty string
87151747b8eSHerbert Dürr if( mnCharCount <= 0 )
87251747b8eSHerbert Dürr return false;
87351747b8eSHerbert Dürr
87451747b8eSHerbert Dürr // initialize character details
87551747b8eSHerbert Dürr mpCharWidths = new Fixed[ mnCharCount ];
87651747b8eSHerbert Dürr mpChars2Glyphs = new int[ mnCharCount ];
87751747b8eSHerbert Dürr for( int n = 0; n < mnCharCount; ++n )
87851747b8eSHerbert Dürr {
87951747b8eSHerbert Dürr mpCharWidths[ n ] = 0;
88051747b8eSHerbert Dürr mpChars2Glyphs[ n ] = -1;
88151747b8eSHerbert Dürr }
88251747b8eSHerbert Dürr
88351747b8eSHerbert Dürr // get details about the glyph layout
88451747b8eSHerbert Dürr ItemCount iLayoutDataCount;
88551747b8eSHerbert Dürr const ATSLayoutRecord* pALR;
88651747b8eSHerbert Dürr OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
88751747b8eSHerbert Dürr maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
88851747b8eSHerbert Dürr (void**)&pALR, &iLayoutDataCount );
88951747b8eSHerbert Dürr DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n");
89051747b8eSHerbert Dürr if( (eStatus != noErr)
89151747b8eSHerbert Dürr || (iLayoutDataCount <= 1) )
89251747b8eSHerbert Dürr return false;
89351747b8eSHerbert Dürr
89451747b8eSHerbert Dürr // initialize glyph details
89551747b8eSHerbert Dürr mpGlyphIds = new ATSGlyphRef[ iLayoutDataCount ];
89651747b8eSHerbert Dürr mpGlyphAdvances = new Fixed[ iLayoutDataCount ];
89751747b8eSHerbert Dürr mpGlyphs2Chars = new int[ iLayoutDataCount ];
89851747b8eSHerbert Dürr
89951747b8eSHerbert Dürr // measure details of the glyph layout
90051747b8eSHerbert Dürr Fixed nLeftPos = 0;
90151747b8eSHerbert Dürr for( ItemCount i = 0; i < iLayoutDataCount; ++i )
90251747b8eSHerbert Dürr {
90351747b8eSHerbert Dürr const ATSLayoutRecord& rALR = pALR[i];
90451747b8eSHerbert Dürr
90551747b8eSHerbert Dürr // distribute the widths as fairly as possible among the chars
90651747b8eSHerbert Dürr const int nRelativeIdx = (rALR.originalOffset / 2);
90751747b8eSHerbert Dürr if( i+1 < iLayoutDataCount )
90851747b8eSHerbert Dürr mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos;
90951747b8eSHerbert Dürr
91051747b8eSHerbert Dürr // new glyph is available => finish measurement of old glyph
91151747b8eSHerbert Dürr if( mnGlyphCount > 0 )
91251747b8eSHerbert Dürr mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos;
91351747b8eSHerbert Dürr
91451747b8eSHerbert Dürr // ignore marker or deleted glyphs
91551747b8eSHerbert Dürr enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF};
91651747b8eSHerbert Dürr if( rALR.glyphID >= MARKED_OUTGLYPH )
91751747b8eSHerbert Dürr continue;
91851747b8eSHerbert Dürr
91951747b8eSHerbert Dürr DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph),
92051747b8eSHerbert Dürr "ATSLayout::InitGIA(): terminator glyph not marked as deleted!" );
92151747b8eSHerbert Dürr
92251747b8eSHerbert Dürr // store details of the visible glyphs
92351747b8eSHerbert Dürr nLeftPos = rALR.realPos;
92451747b8eSHerbert Dürr mpGlyphIds[ mnGlyphCount ] = rALR.glyphID;
92551747b8eSHerbert Dürr
92651747b8eSHerbert Dürr // map visible glyphs to their counterparts in the UTF16-character array
92751747b8eSHerbert Dürr mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos;
92851747b8eSHerbert Dürr mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount;
92951747b8eSHerbert Dürr
93051747b8eSHerbert Dürr ++mnGlyphCount;
93151747b8eSHerbert Dürr }
93251747b8eSHerbert Dürr
93351747b8eSHerbert Dürr // measure complete width
93451747b8eSHerbert Dürr mnCachedWidth = mnBaseAdv;
93551747b8eSHerbert Dürr mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos;
93651747b8eSHerbert Dürr
93751747b8eSHerbert Dürr #if (OSL_DEBUG_LEVEL > 1)
93851747b8eSHerbert Dürr Fixed nWidthSum = mnBaseAdv;
93951747b8eSHerbert Dürr for( int n = 0; n < mnCharCount; ++n )
94051747b8eSHerbert Dürr nWidthSum += mpCharWidths[ n ];
94151747b8eSHerbert Dürr DBG_ASSERT( (nWidthSum==mnCachedWidth),
94251747b8eSHerbert Dürr "ATSLayout::InitGIA(): measured widths do not match!\n" );
94351747b8eSHerbert Dürr #endif
94451747b8eSHerbert Dürr
94551747b8eSHerbert Dürr // #i91183# we need to split up the portion into sub-portions
94651747b8eSHerbert Dürr // if the ATSU-layout differs too much from the requested layout
94751747b8eSHerbert Dürr if( pArgs && pArgs->mpDXArray )
94851747b8eSHerbert Dürr {
94951747b8eSHerbert Dürr // TODO: non-strong-LTR case cases should be handled too
95051747b8eSHerbert Dürr if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG)
95151747b8eSHerbert Dürr && !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) )
95251747b8eSHerbert Dürr {
95351747b8eSHerbert Dürr Fixed nSumCharWidths = 0;
95451747b8eSHerbert Dürr SubPortion aSubPortion = { mnMinCharPos, 0, 0 };
95551747b8eSHerbert Dürr for( int i = 0; i < mnCharCount; ++i )
95651747b8eSHerbert Dürr {
95751747b8eSHerbert Dürr // calculate related logical position
95851747b8eSHerbert Dürr nSumCharWidths += mpCharWidths[i];
95951747b8eSHerbert Dürr
96051747b8eSHerbert Dürr // start new sub-portion if needed
96151747b8eSHerbert Dürr const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]);
96251747b8eSHerbert Dürr const Fixed nNextXOffset = nNextXPos - nSumCharWidths;
96351747b8eSHerbert Dürr const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset;
96451747b8eSHerbert Dürr if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) {
96551747b8eSHerbert Dürr // get to the end of the current sub-portion
96651747b8eSHerbert Dürr // prevent splitting up at diacritics etc.
96751747b8eSHerbert Dürr int j = i;
96851747b8eSHerbert Dürr while( (++j < mnCharCount) && !mpCharWidths[j] );
96951747b8eSHerbert Dürr aSubPortion.mnEndCharPos = mnMinCharPos + j;
97051747b8eSHerbert Dürr // emit current sub-portion
97151747b8eSHerbert Dürr maSubPortions.push_back( aSubPortion );
97251747b8eSHerbert Dürr // prepare next sub-portion
97351747b8eSHerbert Dürr aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos;
97451747b8eSHerbert Dürr aSubPortion.mnXOffset = nNextXOffset;
97551747b8eSHerbert Dürr }
97651747b8eSHerbert Dürr }
97751747b8eSHerbert Dürr
97851747b8eSHerbert Dürr // emit the remaining sub-portion
97951747b8eSHerbert Dürr if( !maSubPortions.empty() )
98051747b8eSHerbert Dürr {
98151747b8eSHerbert Dürr aSubPortion.mnEndCharPos = mnEndCharPos;
98251747b8eSHerbert Dürr if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos )
98351747b8eSHerbert Dürr maSubPortions.push_back( aSubPortion );
98451747b8eSHerbert Dürr }
98551747b8eSHerbert Dürr }
98651747b8eSHerbert Dürr
98751747b8eSHerbert Dürr // override layouted charwidths with requested charwidths
98851747b8eSHerbert Dürr for( int n = 0; n < mnCharCount; ++n )
98951747b8eSHerbert Dürr mpCharWidths[ n ] = pArgs->mpDXArray[ n ];
99051747b8eSHerbert Dürr }
99151747b8eSHerbert Dürr
99251747b8eSHerbert Dürr // release the ATSU layout records
99351747b8eSHerbert Dürr ATSUDirectReleaseLayoutDataArrayPtr(NULL,
99451747b8eSHerbert Dürr kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR );
99551747b8eSHerbert Dürr
99651747b8eSHerbert Dürr return true;
99751747b8eSHerbert Dürr }
99851747b8eSHerbert Dürr
99951747b8eSHerbert Dürr // -----------------------------------------------------------------------
100051747b8eSHerbert Dürr
GetIdealX() const100151747b8eSHerbert Dürr bool ATSLayout::GetIdealX() const
100251747b8eSHerbert Dürr {
100351747b8eSHerbert Dürr // compute the ideal advance widths only once
100451747b8eSHerbert Dürr if( mpGlyphOrigAdvs != NULL )
100551747b8eSHerbert Dürr return true;
100651747b8eSHerbert Dürr
100751747b8eSHerbert Dürr DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" );
100851747b8eSHerbert Dürr DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" );
100951747b8eSHerbert Dürr
101051747b8eSHerbert Dürr // TODO: cache ideal metrics per glyph?
101151747b8eSHerbert Dürr std::vector<ATSGlyphIdealMetrics> aIdealMetrics;
101251747b8eSHerbert Dürr aIdealMetrics.resize( mnGlyphCount );
101351747b8eSHerbert Dürr OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle,
101451747b8eSHerbert Dürr mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] );
101551747b8eSHerbert Dürr DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!");
101651747b8eSHerbert Dürr if( theErr != noErr )
101751747b8eSHerbert Dürr return false;
101851747b8eSHerbert Dürr
101951747b8eSHerbert Dürr mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ];
102051747b8eSHerbert Dürr for( int i = 0;i < mnGlyphCount;++i )
102151747b8eSHerbert Dürr mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x );
102251747b8eSHerbert Dürr
102351747b8eSHerbert Dürr return true;
102451747b8eSHerbert Dürr }
102551747b8eSHerbert Dürr
102651747b8eSHerbert Dürr // -----------------------------------------------------------------------
102751747b8eSHerbert Dürr
GetDeltaY() const102851747b8eSHerbert Dürr bool ATSLayout::GetDeltaY() const
102951747b8eSHerbert Dürr {
103051747b8eSHerbert Dürr // don't bother to get the same delta-y-array more than once
103151747b8eSHerbert Dürr if( mpDeltaY != NULL )
103251747b8eSHerbert Dürr return true;
103351747b8eSHerbert Dürr
103451747b8eSHerbert Dürr #if 1
103551747b8eSHerbert Dürr if( !maATSULayout )
103651747b8eSHerbert Dürr return false;
103751747b8eSHerbert Dürr
103851747b8eSHerbert Dürr // get and keep the y-deltas in the mpDeltaY member variable
103951747b8eSHerbert Dürr // => release it in the destructor
104051747b8eSHerbert Dürr ItemCount nDeltaCount = 0;
104151747b8eSHerbert Dürr OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
104251747b8eSHerbert Dürr maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray,
104351747b8eSHerbert Dürr (void**)&mpDeltaY, &nDeltaCount );
104451747b8eSHerbert Dürr
104551747b8eSHerbert Dürr DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n");
104651747b8eSHerbert Dürr if( theErr != noErr )
104751747b8eSHerbert Dürr return false;
104851747b8eSHerbert Dürr
104951747b8eSHerbert Dürr if( mpDeltaY == NULL )
105051747b8eSHerbert Dürr return true;
105151747b8eSHerbert Dürr
105251747b8eSHerbert Dürr if( nDeltaCount != (ItemCount)mnGlyphCount )
105351747b8eSHerbert Dürr {
105451747b8eSHerbert Dürr DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" );
105551747b8eSHerbert Dürr ATSUDirectReleaseLayoutDataArrayPtr( NULL,
105651747b8eSHerbert Dürr kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
105751747b8eSHerbert Dürr mpDeltaY = NULL;
105851747b8eSHerbert Dürr return false;
105951747b8eSHerbert Dürr }
106051747b8eSHerbert Dürr #endif
106151747b8eSHerbert Dürr
106251747b8eSHerbert Dürr return true;
106351747b8eSHerbert Dürr }
106451747b8eSHerbert Dürr
106551747b8eSHerbert Dürr // -----------------------------------------------------------------------
106651747b8eSHerbert Dürr
106751747b8eSHerbert Dürr #define DELETEAZ( X ) { delete[] X; X = NULL; }
106851747b8eSHerbert Dürr
InvalidateMeasurements()106951747b8eSHerbert Dürr void ATSLayout::InvalidateMeasurements()
107051747b8eSHerbert Dürr {
107151747b8eSHerbert Dürr mnGlyphCount = -1;
107251747b8eSHerbert Dürr DELETEAZ( mpGlyphIds );
107351747b8eSHerbert Dürr DELETEAZ( mpCharWidths );
107451747b8eSHerbert Dürr DELETEAZ( mpChars2Glyphs );
107551747b8eSHerbert Dürr DELETEAZ( mpGlyphs2Chars );
107651747b8eSHerbert Dürr DELETEAZ( mpGlyphRTLFlags );
107751747b8eSHerbert Dürr DELETEAZ( mpGlyphAdvances );
107851747b8eSHerbert Dürr DELETEAZ( mpGlyphOrigAdvs );
107951747b8eSHerbert Dürr DELETEAZ( mpDeltaY );
108051747b8eSHerbert Dürr }
108151747b8eSHerbert Dürr
108251747b8eSHerbert Dürr // =======================================================================
108351747b8eSHerbert Dürr
108451747b8eSHerbert Dürr #if 0
108551747b8eSHerbert Dürr // helper class to convert ATSUI outlines to VCL PolyPolygons
108651747b8eSHerbert Dürr class PolyArgs
108751747b8eSHerbert Dürr {
108851747b8eSHerbert Dürr public:
108951747b8eSHerbert Dürr PolyArgs();
109051747b8eSHerbert Dürr ~PolyArgs();
109151747b8eSHerbert Dürr
109251747b8eSHerbert Dürr void Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset );
109351747b8eSHerbert Dürr void AddPoint( const Float32Point&, PolyFlags );
109451747b8eSHerbert Dürr void ClosePolygon();
109551747b8eSHerbert Dürr
109651747b8eSHerbert Dürr private:
109751747b8eSHerbert Dürr PolyPolygon* mpPolyPoly;
109851747b8eSHerbert Dürr long mnXOffset, mnYOffset;
109951747b8eSHerbert Dürr
110051747b8eSHerbert Dürr Point* mpPointAry;
110151747b8eSHerbert Dürr BYTE* mpFlagAry;
110251747b8eSHerbert Dürr USHORT mnMaxPoints;
110351747b8eSHerbert Dürr
110451747b8eSHerbert Dürr USHORT mnPointCount;
110551747b8eSHerbert Dürr USHORT mnPolyCount;
110651747b8eSHerbert Dürr bool mbHasOffline;
110751747b8eSHerbert Dürr };
110851747b8eSHerbert Dürr
110951747b8eSHerbert Dürr // -----------------------------------------------------------------------
111051747b8eSHerbert Dürr
111151747b8eSHerbert Dürr PolyArgs::PolyArgs()
111251747b8eSHerbert Dürr : mpPolyPoly(NULL),
111351747b8eSHerbert Dürr mnPointCount(0),
111451747b8eSHerbert Dürr mnPolyCount(0),
111551747b8eSHerbert Dürr mbHasOffline(false)
111651747b8eSHerbert Dürr {
111751747b8eSHerbert Dürr mnMaxPoints = 256;
111851747b8eSHerbert Dürr mpPointAry = new Point[ mnMaxPoints ];
111951747b8eSHerbert Dürr mpFlagAry = new BYTE [ mnMaxPoints ];
112051747b8eSHerbert Dürr }
112151747b8eSHerbert Dürr
112251747b8eSHerbert Dürr // -----------------------------------------------------------------------
112351747b8eSHerbert Dürr
112451747b8eSHerbert Dürr PolyArgs::~PolyArgs()
112551747b8eSHerbert Dürr {
112651747b8eSHerbert Dürr delete[] mpFlagAry;
112751747b8eSHerbert Dürr delete[] mpPointAry;
112851747b8eSHerbert Dürr }
112951747b8eSHerbert Dürr
113051747b8eSHerbert Dürr // -----------------------------------------------------------------------
113151747b8eSHerbert Dürr
113251747b8eSHerbert Dürr void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset )
113351747b8eSHerbert Dürr {
113451747b8eSHerbert Dürr mnXOffset = nXOffset;
113551747b8eSHerbert Dürr mnYOffset = nYOffset;
113651747b8eSHerbert Dürr mpPolyPoly = pPolyPoly;
113751747b8eSHerbert Dürr
113851747b8eSHerbert Dürr mpPolyPoly->Clear();
113951747b8eSHerbert Dürr mnPointCount = 0;
114051747b8eSHerbert Dürr mnPolyCount = 0;
114151747b8eSHerbert Dürr }
114251747b8eSHerbert Dürr
114351747b8eSHerbert Dürr // -----------------------------------------------------------------------
114451747b8eSHerbert Dürr
114551747b8eSHerbert Dürr void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags )
114651747b8eSHerbert Dürr {
114751747b8eSHerbert Dürr if( mnPointCount >= mnMaxPoints )
114851747b8eSHerbert Dürr {
114951747b8eSHerbert Dürr // resize if needed (TODO: use STL?)
115051747b8eSHerbert Dürr mnMaxPoints *= 4;
115151747b8eSHerbert Dürr Point* mpNewPoints = new Point[ mnMaxPoints ];
115251747b8eSHerbert Dürr BYTE* mpNewFlags = new BYTE[ mnMaxPoints ];
115351747b8eSHerbert Dürr for( int i = 0; i < mnPointCount; ++i )
115451747b8eSHerbert Dürr {
115551747b8eSHerbert Dürr mpNewPoints[ i ] = mpPointAry[ i ];
115651747b8eSHerbert Dürr mpNewFlags[ i ] = mpFlagAry[ i ];
115751747b8eSHerbert Dürr }
115851747b8eSHerbert Dürr delete[] mpFlagAry;
115951747b8eSHerbert Dürr delete[] mpPointAry;
116051747b8eSHerbert Dürr mpPointAry = mpNewPoints;
116151747b8eSHerbert Dürr mpFlagAry = mpNewFlags;
116251747b8eSHerbert Dürr }
116351747b8eSHerbert Dürr
116451747b8eSHerbert Dürr // convert to pixels and add startpoint offset
116551747b8eSHerbert Dürr int nXPos = Float32ToInt( rPoint.x );
116651747b8eSHerbert Dürr int nYPos = Float32ToInt( rPoint.y );
116751747b8eSHerbert Dürr mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset );
116851747b8eSHerbert Dürr // set point flags
116951747b8eSHerbert Dürr mpFlagAry[ mnPointCount++ ]= eFlags;
117051747b8eSHerbert Dürr mbHasOffline |= (eFlags != POLY_NORMAL);
117151747b8eSHerbert Dürr }
117251747b8eSHerbert Dürr
117351747b8eSHerbert Dürr // -----------------------------------------------------------------------
117451747b8eSHerbert Dürr
117551747b8eSHerbert Dürr void PolyArgs::ClosePolygon()
117651747b8eSHerbert Dürr {
117751747b8eSHerbert Dürr if( !mnPolyCount++ )
117851747b8eSHerbert Dürr return;
117951747b8eSHerbert Dürr
118051747b8eSHerbert Dürr // append finished polygon
118151747b8eSHerbert Dürr Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) );
118251747b8eSHerbert Dürr mpPolyPoly->Insert( aPoly );
118351747b8eSHerbert Dürr
118451747b8eSHerbert Dürr // prepare for new polygon
118551747b8eSHerbert Dürr mnPointCount = 0;
118651747b8eSHerbert Dürr mbHasOffline = false;
118751747b8eSHerbert Dürr }
118851747b8eSHerbert Dürr #endif
118951747b8eSHerbert Dürr // =======================================================================
119051747b8eSHerbert Dürr
119151747b8eSHerbert Dürr // glyph fallback is supported directly by Aqua
119251747b8eSHerbert Dürr // so methods used only by MultiSalLayout can be dummy implementated
GetGlyphOutlines(SalGraphics &,PolyPolyVector &) const119351747b8eSHerbert Dürr bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
InitFont()119451747b8eSHerbert Dürr void ATSLayout::InitFont() {}
MoveGlyph(int,long)119551747b8eSHerbert Dürr void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
DropGlyph(int)119651747b8eSHerbert Dürr void ATSLayout::DropGlyph( int /*nStart*/ ) {}
Simplify(bool)119751747b8eSHerbert Dürr void ATSLayout::Simplify( bool /*bIsBase*/ ) {}
119851747b8eSHerbert Dürr
119951747b8eSHerbert Dürr // get the ImplFontData for a glyph fallback font
120051747b8eSHerbert Dürr // for a glyphid that was returned by ATSLayout::GetNextGlyphs()
GetFallbackFontData(sal_GlyphId aGlyphId) const120151747b8eSHerbert Dürr const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const
120251747b8eSHerbert Dürr {
120351747b8eSHerbert Dürr // check if any fallback fonts were needed
120451747b8eSHerbert Dürr if( !mpFallbackInfo )
120551747b8eSHerbert Dürr return NULL;
120651747b8eSHerbert Dürr // check if the current glyph needs a fallback font
120751747b8eSHerbert Dürr int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
120851747b8eSHerbert Dürr if( !nFallbackLevel )
120951747b8eSHerbert Dürr return NULL;
121051747b8eSHerbert Dürr return mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
121151747b8eSHerbert Dürr }
121251747b8eSHerbert Dürr
121351747b8eSHerbert Dürr // =======================================================================
121451747b8eSHerbert Dürr
AddFallback(ATSUFontID nFontId)121551747b8eSHerbert Dürr int FallbackInfo::AddFallback( ATSUFontID nFontId )
121651747b8eSHerbert Dürr {
121751747b8eSHerbert Dürr // check if the fallback font is already known
121851747b8eSHerbert Dürr for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel )
121951747b8eSHerbert Dürr if( maATSUFontId[ nLevel ] == nFontId )
122051747b8eSHerbert Dürr return (nLevel + 1);
122151747b8eSHerbert Dürr
122251747b8eSHerbert Dürr // append new fallback font if possible
122351747b8eSHerbert Dürr if( mnMaxLevel >= MAX_FALLBACK-1 )
122451747b8eSHerbert Dürr return 0;
122551747b8eSHerbert Dürr // keep ATSU font id of fallback font
122651747b8eSHerbert Dürr maATSUFontId[ mnMaxLevel ] = nFontId;
122751747b8eSHerbert Dürr // find and cache the corresponding ImplFontData pointer
122851747b8eSHerbert Dürr const SystemFontList* pSFL = GetSalData()->mpFontList;
122951747b8eSHerbert Dürr const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId );
123051747b8eSHerbert Dürr maFontData[ mnMaxLevel ] = pFontData;
123151747b8eSHerbert Dürr // increase fallback level by one
123251747b8eSHerbert Dürr return (++mnMaxLevel);
123351747b8eSHerbert Dürr }
123451747b8eSHerbert Dürr
123551747b8eSHerbert Dürr // -----------------------------------------------------------------------
123651747b8eSHerbert Dürr
GetFallbackFontData(int nFallbackLevel) const123751747b8eSHerbert Dürr const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const
123851747b8eSHerbert Dürr {
123951747b8eSHerbert Dürr const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ];
124051747b8eSHerbert Dürr return pFallbackFont;
124151747b8eSHerbert Dürr }
124251747b8eSHerbert Dürr
124351747b8eSHerbert Dürr // =======================================================================
124451747b8eSHerbert Dürr
GetTextLayout(ImplLayoutArgs &,int)124551747b8eSHerbert Dürr SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ )
124651747b8eSHerbert Dürr {
124751747b8eSHerbert Dürr ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale );
124851747b8eSHerbert Dürr return pATSLayout;
124951747b8eSHerbert Dürr }
125051747b8eSHerbert Dürr
125151747b8eSHerbert Dürr // =======================================================================
125251747b8eSHerbert Dürr
1253