xref: /AOO41X/main/vcl/aqua/source/gdi/atslayout.cxx (revision e26449d3b24846fb7600c5866a6e7ae082e782f2)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "tools/debug.hxx"
25 
26 #include "aqua/saldata.hxx"
27 #include "aqua/salgdi.h"
28 #include "atsfonts.hxx"
29 
30 #include "sallayout.hxx"
31 #include "salgdi.hxx"
32 
33 #include <math.h>
34 
35 // =======================================================================
36 
37 class ATSLayout : public SalLayout
38 {
39 public:
40     explicit        ATSLayout( ATSUStyle&, float fFontScale );
41     virtual         ~ATSLayout();
42 
43     virtual bool    LayoutText( ImplLayoutArgs& );
44     virtual void    AdjustLayout( ImplLayoutArgs& );
45     virtual void    DrawText( SalGraphics& ) const;
46 
47     virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&,
48                         sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
49 
50     virtual long    GetTextWidth() const;
51     virtual long    FillDXArray( long* pDXArray ) const;
52     virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
53     virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
54     virtual bool    GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
55     virtual bool    GetBoundRect( SalGraphics&, Rectangle& ) const;
56 
57     const ImplFontData* GetFallbackFontData( sal_GlyphId ) const;
58 
59     virtual void    InitFont();
60     virtual void    MoveGlyph( int nStart, long nNewXPos );
61     virtual void    DropGlyph( int nStart );
62     virtual void    Simplify( bool bIsBase );
63 
64 private:
65     ATSUStyle&          mrATSUStyle;
66     ATSUTextLayout      maATSULayout;
67     int                 mnCharCount;        // ==mnEndCharPos-mnMinCharPos
68     // to prevent ATS overflowing the Fixed16.16 values
69     // ATS font requests get size limited by downscaling huge fonts
70     // in these cases the font scale becomes something bigger than 1.0
71     float               mfFontScale;
72 
73 private:
74     bool    InitGIA( ImplLayoutArgs* pArgs = NULL ) const;
75     bool    GetIdealX() const;
76     bool    GetDeltaY() const;
77     void    InvalidateMeasurements();
78 
79     int Fixed2Vcl( Fixed ) const;       // convert ATSU-Fixed units to VCL units
80     int AtsuPix2Vcl( int ) const;       // convert ATSU-Pixel units to VCL units
81     Fixed   Vcl2Fixed( int ) const;     // convert VCL units to ATSU-Fixed units
82 
83     // cached details about the resulting layout
84     // mutable members since these details are all lazy initialized
85     mutable int         mnGlyphCount;           // glyph count
86     mutable Fixed       mnCachedWidth;          // cached value of resulting typographical width
87     int                 mnTrailingSpaceWidth;   // in Pixels
88 
89     mutable ATSGlyphRef*    mpGlyphIds;         // ATSU glyph ids
90     mutable Fixed*          mpCharWidths;       // map relative charpos to charwidth
91     mutable int*            mpChars2Glyphs;     // map relative charpos to absolute glyphpos
92     mutable int*            mpGlyphs2Chars;     // map absolute glyphpos to absolute charpos
93     mutable bool*           mpGlyphRTLFlags;    // BiDi status for glyphs: true if RTL
94     mutable Fixed*          mpGlyphAdvances;    // contains glyph widths for the justified layout
95     mutable Fixed*          mpGlyphOrigAdvs;    // contains glyph widths for the unjustified layout
96     mutable Fixed*          mpDeltaY;           // vertical offset from the baseline
97 
98     struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; };
99     typedef std::vector<SubPortion> SubPortionVector;
100     mutable SubPortionVector    maSubPortions;      // Writer&ATSUI layouts can differ quite a bit...
101 
102     // storing details about fonts used in glyph-fallback for this layout
103     mutable class FallbackInfo* mpFallbackInfo;
104 
105     // x-offset relative to layout origin
106     // currently only used in RTL-layouts
107     mutable Fixed           mnBaseAdv;
108 };
109 
110 class FallbackInfo
111 {
112 public:
FallbackInfo()113     FallbackInfo() : mnMaxLevel(0) {}
114     int AddFallback( ATSUFontID );
115     const ImplFontData* GetFallbackFontData( int nLevel ) const;
116 
117 private:
118     const ImplMacFontData* maFontData[ MAX_FALLBACK ];
119     ATSUFontID             maATSUFontId[ MAX_FALLBACK ];
120     int                    mnMaxLevel;
121 };
122 
123 // =======================================================================
124 
ATSLayout(ATSUStyle & rATSUStyle,float fFontScale)125 ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
126 :   mrATSUStyle( rATSUStyle ),
127     maATSULayout( NULL ),
128     mnCharCount( 0 ),
129     mfFontScale( fFontScale ),
130     mnGlyphCount( -1 ),
131     mnCachedWidth( 0 ),
132     mnTrailingSpaceWidth( 0 ),
133     mpGlyphIds( NULL ),
134     mpCharWidths( NULL ),
135     mpChars2Glyphs( NULL ),
136     mpGlyphs2Chars( NULL ),
137     mpGlyphRTLFlags( NULL ),
138     mpGlyphAdvances( NULL ),
139     mpGlyphOrigAdvs( NULL ),
140     mpDeltaY( NULL ),
141     mpFallbackInfo( NULL ),
142     mnBaseAdv( 0 )
143 {}
144 
145 // -----------------------------------------------------------------------
146 
~ATSLayout()147 ATSLayout::~ATSLayout()
148 {
149     if( mpDeltaY )
150         ATSUDirectReleaseLayoutDataArrayPtr( NULL,
151             kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
152 
153     if( maATSULayout )
154         ATSUDisposeTextLayout( maATSULayout );
155 
156     delete[] mpGlyphRTLFlags;
157     delete[] mpGlyphs2Chars;
158     delete[] mpChars2Glyphs;
159     if( mpCharWidths != mpGlyphAdvances )
160         delete[] mpCharWidths;
161     delete[] mpGlyphIds;
162     delete[] mpGlyphOrigAdvs;
163     delete[] mpGlyphAdvances;
164 
165     delete mpFallbackInfo;
166 }
167 
168 // -----------------------------------------------------------------------
169 
Fixed2Vcl(Fixed nFixed) const170 inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const
171 {
172     float fFloat = mfFontScale * FixedToFloat( nFixed );
173     return static_cast<int>(fFloat + 0.5);
174 }
175 
176 // -----------------------------------------------------------------------
177 
AtsuPix2Vcl(int nAtsuPixel) const178 inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const
179 {
180     float fVclPixel = mfFontScale * nAtsuPixel;
181     fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5;  // prepare rounding to int
182     int nVclPixel = static_cast<int>( fVclPixel);
183     return nVclPixel;
184 }
185 
186 // -----------------------------------------------------------------------
187 
Vcl2Fixed(int nPixel) const188 inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const
189 {
190     return FloatToFixed( nPixel / mfFontScale );
191 }
192 
193 // -----------------------------------------------------------------------
194 /**
195  * ATSLayout::LayoutText : Manage text layouting
196  *
197  * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout
198  *
199  * Typographic layout of text by using the style maATSUStyle
200  *
201  * @return : true if everything is ok
202 **/
LayoutText(ImplLayoutArgs & rArgs)203 bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs )
204 {
205     if( maATSULayout )
206         ATSUDisposeTextLayout( maATSULayout );
207 
208     maATSULayout = NULL;
209 
210     // Layout text
211     // set up our locals, verify parameters...
212     DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!");
213     DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!");
214 
215     SalLayout::AdjustLayout( rArgs );
216     mnCharCount = mnEndCharPos - mnMinCharPos;
217 
218     // Workaround a bug in ATSUI with empty string
219     if( mnCharCount<=0 )
220         return false;
221 
222     // create the ATSUI layout
223     UniCharCount nRunLengths[1] = { mnCharCount };
224     const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths);
225     OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr,
226         rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength,
227         nRunCount, &nRunLengths[0], &mrATSUStyle, &maATSULayout);
228 
229     DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n");
230     if( eStatus != noErr )
231         return false;
232 
233     // prepare setting of layout controls
234     static const int nMaxTagCount = 1;
235     ATSUAttributeTag aTagAttrs[ nMaxTagCount ];
236     ByteCount aTagSizes[ nMaxTagCount ];
237     ATSUAttributeValuePtr aTagValues[ nMaxTagCount ];
238 
239     // prepare control of "glyph fallback"
240     const SalData* pSalData = GetSalData();
241     ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks;
242     aTagAttrs[0]  = kATSULineFontFallbacksTag;
243     aTagSizes[0]  = sizeof( ATSUFontFallbacks );
244     aTagValues[0] = &aFontFallbacks;
245 
246     // set paragraph layout controls
247     ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
248 
249     // enable "glyph fallback"
250     ATSUSetTransientFontMatching( maATSULayout, true );
251 
252     // control run-specific layout controls
253     if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 )
254     {
255         // control BiDi defaults
256         BOOL nLineDirTag = kATSULeftToRightBaseDirection;
257         if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 )
258             nLineDirTag = kATSURightToLeftBaseDirection;
259         aTagAttrs[0] = kATSULineDirectionTag;
260         aTagSizes[0] = sizeof( nLineDirTag );
261         aTagValues[0] = &nLineDirTag;
262         // set run-specific layout controls
263 #if 0 // why don't line-controls work as reliable as layout-controls???
264         ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues );
265 #else
266         ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues );
267 #endif
268     }
269 
270     return true;
271 }
272 
273 // -----------------------------------------------------------------------
274 /**
275  * ATSLayout::AdjustLayout : Adjust layout style
276  *
277  * @param rArgs: contains attributes relevant to do a text specific layout
278  *
279  * Adjust text layout by moving glyphs to match the requested logical widths
280  *
281  * @return : none
282 **/
AdjustLayout(ImplLayoutArgs & rArgs)283 void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs )
284 {
285     int nOrigWidth = GetTextWidth();
286     int nPixelWidth = rArgs.mnLayoutWidth;
287     if( !nPixelWidth && rArgs.mpDXArray ) {
288         // for now we are only interested in the layout width
289         // TODO: use all mpDXArray elements for layouting
290         nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ];
291 
292         // workaround for ATSUI not using trailing spaces for justification
293         int i = mnCharCount;
294         while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {}
295         if( i < 0 ) // nothing to do if the text is all spaces
296             return;
297         // #i91685# trailing letters are left aligned (right aligned for RTL)
298         mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ];
299         if( i > 0 )
300             mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ];
301         InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead?
302         mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] );
303         // ignore trailing space for calculating the available width
304         nOrigWidth -= mnTrailingSpaceWidth;
305         nPixelWidth -= mnTrailingSpaceWidth;
306         // in RTL-layouts trailing spaces are leftmost
307         // TODO: use BiDi-algorithm to thoroughly check this assumption
308         if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
309             mnBaseAdv = mnTrailingSpaceWidth;
310     }
311     // return early if there is nothing to do
312     if( !nPixelWidth )
313         return;
314 
315     // HACK: justification requests which change the width by just one pixel were probably
316     // #i86038# introduced by lossy conversions between integer based coordinate system
317     // => ignoring such requests has many more benefits than eventual drawbacks
318     if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) )
319         return;
320 
321     // changing the layout will make all previous measurements invalid
322     InvalidateMeasurements();
323 
324     ATSUAttributeTag nTags[3];
325     ATSUAttributeValuePtr nVals[3];
326     ByteCount nBytes[3];
327 
328     Fixed nFixedWidth = Vcl2Fixed( nPixelWidth );
329     mnCachedWidth = nFixedWidth;
330     Fract nFractFactor = kATSUFullJustification;
331     ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter;
332 
333     nTags[0] = kATSULineWidthTag;
334     nVals[0] = &nFixedWidth;
335     nBytes[0] = sizeof(Fixed);
336     nTags[1] = kATSULineLayoutOptionsTag;
337     nVals[1] = &nLineLayoutOptions;
338     nBytes[1] = sizeof(ATSLineLayoutOptions);
339     nTags[2] = kATSULineJustificationFactorTag;
340     nVals[2] = &nFractFactor;
341     nBytes[2] = sizeof(Fract);
342 
343     OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals );
344     if( eStatus != noErr )
345         return;
346 
347     // update the measurements of the justified layout to match the justification request
348     if( rArgs.mpDXArray )
349         InitGIA( &rArgs );
350 }
351 
352 // -----------------------------------------------------------------------
353 /**
354  * ATSLayout::DrawText : Draw text to screen
355  *
356  * @param rGraphics: device to draw to
357  *
358  * Draw the layouted text to the CGContext
359  *
360  * @return : none
361 **/
DrawText(SalGraphics & rGraphics) const362 void ATSLayout::DrawText( SalGraphics& rGraphics ) const
363 {
364     AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics);
365 
366     // short circuit if there is nothing to do
367     if( (mnCharCount <= 0)
368     ||  !rAquaGraphics.CheckContext() )
369         return;
370 
371     // the view is vertically flipped => flipped glyphs
372     // so apply a temporary transformation that it flips back
373     // also compensate if the font was size limited
374     CGContextSaveGState( rAquaGraphics.mrContext );
375     CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale );
376     CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText );
377 
378     // prepare ATSUI drawing attributes
379     static const ItemCount nMaxControls = 8;
380     ATSUAttributeTag theTags[ nMaxControls ];
381     ByteCount theSizes[ nMaxControls];
382     ATSUAttributeValuePtr theValues[ nMaxControls ];
383     ItemCount numcontrols = 0;
384 
385     // Tell ATSUI to use CoreGraphics
386     theTags[numcontrols] = kATSUCGContextTag;
387     theSizes[numcontrols] = sizeof( CGContextRef );
388     theValues[numcontrols++] = &rAquaGraphics.mrContext;
389 
390     // Rotate if necessary
391     if( rAquaGraphics.mnATSUIRotation != 0 )
392     {
393         Fixed theAngle = rAquaGraphics.mnATSUIRotation;
394         theTags[numcontrols] = kATSULineRotationTag;
395         theSizes[numcontrols] = sizeof( Fixed );
396         theValues[numcontrols++] = &theAngle;
397     }
398 
399     DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" );
400     OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues);
401     DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" );
402 
403     // Draw the text
404     const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) );
405     const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
406     const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring
407     if( maSubPortions.empty() )
408         ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY );
409     else
410     {
411         // draw the sub-portions and apply individual adjustments
412         SubPortionVector::const_iterator it = maSubPortions.begin();
413         for(; it != maSubPortions.end(); ++it )
414         {
415             const SubPortion& rSubPortion = *it;
416             // calculate sub-portion offset for rotated text
417             Fixed nXOfsFixed = 0, nYOfsFixed = 0;
418             if( rAquaGraphics.mnATSUIRotation != 0 )
419             {
420                 const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000);
421                 nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians ));
422                 nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians ));
423             }
424 
425             // draw sub-portions
426             ATSUDrawText( maATSULayout,
427                 rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos,
428                 nFixedX + nXOfsFixed, nFixedY + nYOfsFixed );
429         }
430     }
431 
432     // request an update of the changed window area
433     if( rAquaGraphics.IsWindowGraphics() )
434     {
435         Rect drawRect; // rectangle of the changed area
436         theErr = ATSUMeasureTextImage( maATSULayout,
437             mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect );
438         if( theErr == noErr )
439         {
440             // FIXME: transformation from baseline to top left
441             // with the simple approach below we invalidate too much
442             short d = drawRect.bottom - drawRect.top;
443             drawRect.top -= d;
444             drawRect.bottom += d;
445             CGRect aRect = CGRectMake( drawRect.left, drawRect.top,
446                     drawRect.right - drawRect.left,
447                     drawRect.bottom - drawRect.top );
448             aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect );
449             rAquaGraphics.RefreshRect( aRect );
450         }
451     }
452 
453     // restore the original graphic context transformations
454     CGContextRestoreGState( rAquaGraphics.mrContext );
455 }
456 
457 // -----------------------------------------------------------------------
458 /**
459  * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout
460  *
461  * @param nLen: max number of char
462  * @param pGlyphs: returned array of glyph ids
463  * @param rPos: returned x starting position
464  * @param nStart: index of the first requested glyph
465  * @param pGlyphAdvances: returned array of glyphs advances
466  * @param pCharIndexes: returned array of char indexes
467  *
468  * Returns infos about the next glyphs in the text layout
469  *
470  * @return : number of glyph details that were provided
471 **/
GetNextGlyphs(int nLen,sal_GlyphId * pOutGlyphIds,Point & rPos,int & nStart,sal_Int32 * pGlyphAdvances,int * pCharIndexes) const472 int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart,
473     sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
474 {
475     if( nStart < 0 )                // first glyph requested?
476         nStart = 0;
477 
478     // get glyph measurements
479     InitGIA();
480     // some measurements are only needed for multi-glyph results
481     if( nLen > 1 )
482     {
483         GetIdealX();
484         GetDeltaY();
485     }
486 
487     if( nStart >= mnGlyphCount )    // no glyph left?
488         return 0;
489 
490     // calculate glyph position relative to layout base
491     // TODO: avoid for nStart!=0 case by reusing rPos
492     Fixed nXOffset = mnBaseAdv;
493     for( int i = 0; i < nStart; ++i )
494         nXOffset += mpGlyphAdvances[ i ];
495     // if sub-portion offsets are involved there is an additional x-offset
496     if( !maSubPortions.empty() )
497     {
498         // prepare to find the sub-portion
499         int nCharPos = nStart + mnMinCharPos;
500         if( mpGlyphs2Chars )
501             nCharPos = mpGlyphs2Chars[nStart];
502 
503         // find the matching subportion
504         // TODO: is a non-linear search worth it?
505         SubPortionVector::const_iterator it = maSubPortions.begin();
506         for(; it != maSubPortions.end(); ++it) {
507             const SubPortion& r = *it;
508             if( nCharPos < r.mnMinCharPos )
509                 continue;
510             if( nCharPos >= r.mnEndCharPos )
511                 continue;
512             // apply the sub-portion xoffset
513             nXOffset += r.mnXOffset;
514             break;
515         }
516     }
517 
518     Fixed nYOffset = 0;
519     if( mpDeltaY )
520         nYOffset = mpDeltaY[ nStart ];
521 
522     // calculate absolute position in pixel units
523     const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) );
524     rPos = GetDrawPosition( aRelativePos );
525 
526     // update return values
527     int nCount = 0;
528     while( nCount < nLen )
529     {
530         ++nCount;
531         sal_GlyphId aGlyphId = mpGlyphIds[nStart];
532 
533         // check if glyph fallback is needed for this glyph
534         // TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead?
535         const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos;
536         ATSUFontID nFallbackFontID = kATSUInvalidFontID;
537         UniCharArrayOffset nChangedOffset = 0;
538         UniCharCount nChangedLength = 0;
539         OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd,
540             &nFallbackFontID, &nChangedOffset, &nChangedLength );
541         if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) )
542         {
543             // fallback is needed
544             if( !mpFallbackInfo )
545                 mpFallbackInfo = new FallbackInfo;
546             // register fallback font
547             const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID );
548             // update sal_GlyphId with fallback level
549             aGlyphId |= (nLevel << GF_FONTSHIFT);
550         }
551 
552         // update resulting glyphid array
553         *(pOutGlyphIds++) = aGlyphId;
554 
555         // update returned glyph advance array
556         if( pGlyphAdvances )
557             *(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] );
558 
559         // update returned index-into-string array
560         if( pCharIndexes )
561         {
562             int nCharPos;
563             if( mpGlyphs2Chars )
564                 nCharPos = mpGlyphs2Chars[nStart];
565             else
566                 nCharPos = nStart + mnMinCharPos;
567             *(pCharIndexes++) = nCharPos;
568         }
569 
570         // stop at last glyph
571         if( ++nStart >= mnGlyphCount )
572             break;
573 
574         // stop when next the x-position is unexpected
575         if( !maSubPortions.empty() )
576             break;   // TODO: finish the complete sub-portion
577         if( !pGlyphAdvances && mpGlyphOrigAdvs )
578             if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
579                 break;
580 
581         // stop when the next y-position is unexpected
582         if( mpDeltaY )
583             if( mpDeltaY[nStart-1] != mpDeltaY[nStart] )
584                 break;
585     }
586 
587     return nCount;
588 }
589 
590 // -----------------------------------------------------------------------
591 /**
592  * ATSLayout::GetTextWidth : Get typographic width of layouted text
593  *
594  * Get typographic bounds of the text
595  *
596  * @return : text width
597 **/
GetTextWidth() const598 long ATSLayout::GetTextWidth() const
599 {
600     if( mnCharCount <= 0 )
601         return 0;
602 
603     DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n");
604     if( !maATSULayout )
605         return 0;
606 
607     if( !mnCachedWidth )
608     {
609         // prepare precise measurements on pixel based or reference-device
610         const UInt16 eTypeOfBounds = kATSUseFractionalOrigins;
611 
612         // determine number of needed measurement trapezoids
613         ItemCount nMaxBounds = 0;
614         OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
615             eTypeOfBounds, 0, NULL, &nMaxBounds );
616         if( (err != noErr)
617         ||  (nMaxBounds <= 0) )
618             return 0;
619 
620         // get the trapezoids
621         typedef std::vector<ATSTrapezoid> TrapezoidVector;
622         TrapezoidVector aTrapezoidVector( nMaxBounds );
623         ItemCount nBoundsCount = 0;
624         err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount,
625             eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount );
626         if( err != noErr )
627             return 0;
628 
629         DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n");
630 
631         // find the bound extremas
632         Fixed nLeftBound = 0;
633         Fixed nRightBound = 0;
634         for( ItemCount i = 0; i < nBoundsCount; ++i )
635         {
636             const ATSTrapezoid& rTrap = aTrapezoidVector[i];
637             if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) )
638                 nLeftBound = rTrap.lowerLeft.x;
639             if( (i == 0) || (nRightBound > rTrap.lowerRight.x) )
640                 nRightBound = rTrap.lowerRight.x;
641         }
642 
643         // measure the bound extremas
644         mnCachedWidth = nRightBound - nLeftBound;
645         // adjust for eliminated trailing space widths
646     }
647 
648     int nScaledWidth = Fixed2Vcl( mnCachedWidth );
649     nScaledWidth += mnTrailingSpaceWidth;
650     return nScaledWidth;
651 }
652 
653 // -----------------------------------------------------------------------
654 /**
655  * ATSLayout::FillDXArray : Get Char widths
656  *
657  * @param pDXArray: array to be filled with x-advances
658  *
659  * Fill the pDXArray with horizontal deltas : CharWidths
660  *
661  * @return : typographical width of the complete text layout
662 **/
FillDXArray(long * pDXArray) const663 long ATSLayout::FillDXArray( long* pDXArray ) const
664 {
665     // short circuit requests which don't need full details
666     if( !pDXArray )
667         return GetTextWidth();
668 
669     // check assumptions
670     DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" );
671 
672     // initialize details about the resulting layout
673     InitGIA();
674 
675     // distribute the widths among the string elements
676     int nPixWidth = 0;
677     mnCachedWidth = 0;
678     for( int i = 0; i < mnCharCount; ++i )
679     {
680         // convert and adjust for accumulated rounding errors
681         mnCachedWidth += mpCharWidths[i];
682         const int nOldPixWidth = nPixWidth;
683         nPixWidth = Fixed2Vcl( mnCachedWidth );
684         pDXArray[i] = nPixWidth - nOldPixWidth;
685     }
686 
687     return nPixWidth;
688 }
689 
690 // -----------------------------------------------------------------------
691 /**
692  * ATSLayout::GetTextBreak : Find line break depending on width
693  *
694  * @param nMaxWidth : maximal logical text width in subpixel units
695  * @param nCharExtra: expanded/condensed spacing in subpixel units
696  * @param nFactor:    number of subpixel units per pixel
697  *
698  * Measure the layouted text to find the typographical line break
699  * the result is needed by the language specific line breaking
700  *
701  * @return : string index corresponding to the suggested line break
702 **/
GetTextBreak(long nMaxWidth,long nCharExtra,int nFactor) const703 int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
704 {
705     if( !maATSULayout )
706         return STRING_LEN;
707 
708     // the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine()
709     if( nCharExtra != 0 )
710     {
711         // prepare the measurement by layouting and measuring the un-expanded/un-condensed text
712         if( !InitGIA() )
713             return STRING_LEN;
714 
715         // TODO: use a better way than by testing each the char position
716         ATSUTextMeasurement nATSUSumWidth = 0;
717         const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor );
718         const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor;
719         for( int i = 0; i < mnCharCount; ++i )
720         {
721             nATSUSumWidth += mpCharWidths[i];
722             if( nATSUSumWidth >= nATSUMaxWidth )
723                 return (mnMinCharPos + i);
724             nATSUSumWidth += nATSUExtraWidth;
725             if( nATSUSumWidth >= nATSUMaxWidth )
726                 if( i+1 < mnCharCount )
727                     return (mnMinCharPos + i);
728         }
729 
730         return STRING_LEN;
731     }
732 
733     // get a quick overview on what could fit
734     const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
735     if( nPixelWidth <= 0 )
736         return mnMinCharPos;
737 
738     // check assumptions
739     DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" );
740 
741     // initial measurement of text break position
742     UniCharArrayOffset nBreakPos = mnMinCharPos;
743     const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth );
744     if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
745         return mnMinCharPos;      //           or do ATSUMaxWidth=0x10000;
746     OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos,
747         nATSUMaxWidth, false, &nBreakPos );
748     if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) )
749         return STRING_LEN;
750 
751     // the result from ATSUBreakLine() doesn't match the semantics expected by its
752     // application layer callers from SW+SVX+I18N. Adjust the results to the expectations:
753 
754     // ATSU reports that everything fits even when trailing spaces would break the line
755     // #i89789# OOo's application layers expect STRING_LEN if everything fits
756     if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) )
757         return STRING_LEN;
758 
759     // GetTextBreak()'s callers expect it to return the "stupid visual line break".
760     // Returning anything else result.s in subtle problems in the application layers.
761     static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method
762     if( !bInWord )
763         return nBreakPos;
764 
765     // emulate stupid visual line breaking by line breaking for the remaining width
766     ATSUTextMeasurement nLeft, nRight, nDummy;
767     eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos,
768         &nLeft, &nRight, &nDummy, &nDummy );
769     if( eStatus != noErr )
770         return nBreakPos;
771     const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft);
772     if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter
773         return nBreakPos;
774     UniCharArrayOffset nBreakPosInWord = nBreakPos;
775     eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord );
776     return nBreakPosInWord;
777 }
778 
779 // -----------------------------------------------------------------------
780 /**
781  * ATSLayout::GetCaretPositions : Find positions of carets
782  *
783  * @param nMaxIndex position to which we want to find the carets
784  *
785  * Fill the array of positions of carets (for cursors and selections)
786  *
787  * @return : none
788 **/
GetCaretPositions(int nMaxIndex,long * pCaretXArray) const789 void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
790 {
791     DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)),
792         "ATSLayout::GetCaretPositions() : invalid number of caret pairs requested");
793 
794     // initialize the caret positions
795     for( int i = 0; i < nMaxIndex; ++i )
796         pCaretXArray[ i ] = -1;
797 
798     for( int n = 0; n <= mnCharCount; ++n )
799     {
800         // measure the characters cursor position
801         typedef unsigned char Boolean;
802         const Boolean bIsLeading = true;
803         ATSUCaret aCaret0, aCaret1;
804         Boolean bIsSplit;
805         OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout,
806             mnMinCharPos + n, bIsLeading, kATSUByCharacter,
807             &aCaret0, &aCaret1, &bIsSplit );
808         if( eStatus != noErr )
809             continue;
810         const Fixed nFixedPos = mnBaseAdv + aCaret0.fX;
811         // convert the measurement to pixel units
812         const int nPixelPos = Fixed2Vcl( nFixedPos );
813         // update previous trailing position
814         if( n > 0 )
815             pCaretXArray[2*n-1] = nPixelPos;
816         // update current leading position
817         if( 2*n >= nMaxIndex )
818             break;
819         pCaretXArray[2*n+0] = nPixelPos;
820     }
821 }
822 
823 // -----------------------------------------------------------------------
824 /**
825  * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text
826  *
827  * @param rVCLRect: rectangle of text image (layout) measures
828  *
829  * Get ink bounds of the text
830  *
831  * @return : measurement valid
832 **/
GetBoundRect(SalGraphics &,Rectangle & rVCLRect) const833 bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const
834 {
835     const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) );
836     const Fixed nFixedX = Vcl2Fixed( +aPos.X() );
837     const Fixed nFixedY = Vcl2Fixed( +aPos.Y() );
838 
839     Rect aMacRect;
840     OSStatus eStatus = ATSUMeasureTextImage( maATSULayout,
841         mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect );
842     if( eStatus != noErr )
843         return false;
844 
845     // ATSU top-bottom are vertically flipped from a VCL aspect
846     rVCLRect.Left()   = AtsuPix2Vcl( aMacRect.left );
847     rVCLRect.Top()    = AtsuPix2Vcl( aMacRect.top );
848     rVCLRect.Right()  = AtsuPix2Vcl( aMacRect.right );
849     rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom );
850     return true;
851 }
852 
853 // -----------------------------------------------------------------------
854 /**
855  * ATSLayout::InitGIA() : get many informations about layouted text
856  *
857  * Fills arrays of information about the gylph layout previously done
858  *  in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline),
859  *  mapping between glyph index and character index, chars widths
860  *
861  * @return : true if everything could be computed, otherwise false
862 **/
InitGIA(ImplLayoutArgs * pArgs) const863 bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const
864 {
865     // no need to run InitGIA more than once on the same ATSLayout object
866     if( mnGlyphCount >= 0 )
867         return true;
868     mnGlyphCount = 0;
869 
870     // Workaround a bug in ATSUI with empty string
871     if( mnCharCount <=  0 )
872         return false;
873 
874     // initialize character details
875     mpCharWidths    = new Fixed[ mnCharCount ];
876     mpChars2Glyphs  = new int[ mnCharCount ];
877     for( int n = 0; n < mnCharCount; ++n )
878     {
879         mpCharWidths[ n ] = 0;
880         mpChars2Glyphs[ n ] = -1;
881     }
882 
883     // get details about the glyph layout
884     ItemCount iLayoutDataCount;
885     const ATSLayoutRecord* pALR;
886     OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
887         maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
888         (void**)&pALR, &iLayoutDataCount );
889     DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n");
890     if( (eStatus != noErr)
891     || (iLayoutDataCount <= 1) )
892         return false;
893 
894     // initialize glyph details
895     mpGlyphIds      = new ATSGlyphRef[ iLayoutDataCount ];
896     mpGlyphAdvances = new Fixed[ iLayoutDataCount ];
897     mpGlyphs2Chars  = new int[ iLayoutDataCount ];
898 
899     // measure details of the glyph layout
900     Fixed nLeftPos = 0;
901     for( ItemCount i = 0; i < iLayoutDataCount; ++i )
902     {
903         const ATSLayoutRecord& rALR = pALR[i];
904 
905         // distribute the widths as fairly as possible among the chars
906         const int nRelativeIdx = (rALR.originalOffset / 2);
907         if( i+1 < iLayoutDataCount )
908             mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos;
909 
910         // new glyph is available => finish measurement of old glyph
911         if( mnGlyphCount > 0 )
912             mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos;
913 
914         // ignore marker or deleted glyphs
915         enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF};
916         if( rALR.glyphID >= MARKED_OUTGLYPH )
917             continue;
918 
919         DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph),
920             "ATSLayout::InitGIA(): terminator glyph not marked as deleted!" );
921 
922         // store details of the visible glyphs
923         nLeftPos = rALR.realPos;
924         mpGlyphIds[ mnGlyphCount ] = rALR.glyphID;
925 
926         // map visible glyphs to their counterparts in the UTF16-character array
927         mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos;
928         mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount;
929 
930         ++mnGlyphCount;
931     }
932 
933     // measure complete width
934     mnCachedWidth = mnBaseAdv;
935     mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos;
936 
937 #if (OSL_DEBUG_LEVEL > 1)
938     Fixed nWidthSum = mnBaseAdv;
939     for( int n = 0; n < mnCharCount; ++n )
940         nWidthSum += mpCharWidths[ n ];
941     DBG_ASSERT( (nWidthSum==mnCachedWidth),
942         "ATSLayout::InitGIA(): measured widths do not match!\n" );
943 #endif
944 
945     // #i91183# we need to split up the portion into sub-portions
946     // if the ATSU-layout differs too much from the requested layout
947     if( pArgs && pArgs->mpDXArray )
948     {
949         // TODO: non-strong-LTR case cases should be handled too
950         if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG)
951         && !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) )
952         {
953             Fixed nSumCharWidths = 0;
954             SubPortion aSubPortion = { mnMinCharPos, 0, 0 };
955             for( int i = 0; i < mnCharCount; ++i )
956             {
957                 // calculate related logical position
958                 nSumCharWidths += mpCharWidths[i];
959 
960                 // start new sub-portion if needed
961                 const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]);
962                 const Fixed nNextXOffset = nNextXPos - nSumCharWidths;
963                 const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset;
964                 if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) {
965                     // get to the end of the current sub-portion
966                     // prevent splitting up at diacritics etc.
967                     int j = i;
968                     while( (++j < mnCharCount) && !mpCharWidths[j] );
969                     aSubPortion.mnEndCharPos = mnMinCharPos + j;
970                     // emit current sub-portion
971                     maSubPortions.push_back( aSubPortion );
972                     // prepare next sub-portion
973                     aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos;
974                     aSubPortion.mnXOffset = nNextXOffset;
975                 }
976             }
977 
978             // emit the remaining sub-portion
979             if( !maSubPortions.empty() )
980             {
981                 aSubPortion.mnEndCharPos = mnEndCharPos;
982                 if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos )
983                     maSubPortions.push_back( aSubPortion );
984             }
985         }
986 
987         // override layouted charwidths with requested charwidths
988         for( int n = 0; n < mnCharCount; ++n )
989             mpCharWidths[ n ] = pArgs->mpDXArray[ n ];
990     }
991 
992     // release the ATSU layout records
993     ATSUDirectReleaseLayoutDataArrayPtr(NULL,
994         kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR );
995 
996     return true;
997 }
998 
999 // -----------------------------------------------------------------------
1000 
GetIdealX() const1001 bool ATSLayout::GetIdealX() const
1002 {
1003     // compute the ideal advance widths only once
1004     if( mpGlyphOrigAdvs != NULL )
1005         return true;
1006 
1007     DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" );
1008     DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" );
1009 
1010     // TODO: cache ideal metrics per glyph?
1011     std::vector<ATSGlyphIdealMetrics> aIdealMetrics;
1012     aIdealMetrics.resize( mnGlyphCount );
1013     OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle,
1014         mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] );
1015     DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!");
1016     if( theErr != noErr )
1017         return false;
1018 
1019     mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ];
1020     for( int i = 0;i < mnGlyphCount;++i )
1021         mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x );
1022 
1023     return true;
1024 }
1025 
1026 // -----------------------------------------------------------------------
1027 
GetDeltaY() const1028 bool ATSLayout::GetDeltaY() const
1029 {
1030     // don't bother to get the same delta-y-array more than once
1031     if( mpDeltaY != NULL )
1032         return true;
1033 
1034 #if 1
1035     if( !maATSULayout )
1036         return false;
1037 
1038     // get and keep the y-deltas in the mpDeltaY member variable
1039     // => release it in the destructor
1040     ItemCount nDeltaCount = 0;
1041     OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
1042         maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray,
1043         (void**)&mpDeltaY, &nDeltaCount );
1044 
1045     DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n");
1046     if( theErr != noErr )
1047         return false;
1048 
1049     if( mpDeltaY == NULL )
1050         return true;
1051 
1052     if( nDeltaCount != (ItemCount)mnGlyphCount )
1053     {
1054         DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" );
1055         ATSUDirectReleaseLayoutDataArrayPtr( NULL,
1056             kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY );
1057         mpDeltaY = NULL;
1058         return false;
1059     }
1060 #endif
1061 
1062     return true;
1063 }
1064 
1065 // -----------------------------------------------------------------------
1066 
1067 #define DELETEAZ( X ) { delete[] X; X = NULL; }
1068 
InvalidateMeasurements()1069 void ATSLayout::InvalidateMeasurements()
1070 {
1071     mnGlyphCount = -1;
1072     DELETEAZ( mpGlyphIds );
1073     DELETEAZ( mpCharWidths );
1074     DELETEAZ( mpChars2Glyphs );
1075     DELETEAZ( mpGlyphs2Chars );
1076     DELETEAZ( mpGlyphRTLFlags );
1077     DELETEAZ( mpGlyphAdvances );
1078     DELETEAZ( mpGlyphOrigAdvs );
1079     DELETEAZ( mpDeltaY );
1080 }
1081 
1082 // =======================================================================
1083 
1084 #if 0
1085 // helper class to convert ATSUI outlines to VCL PolyPolygons
1086 class PolyArgs
1087 {
1088 public:
1089                 PolyArgs();
1090                 ~PolyArgs();
1091 
1092     void        Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset );
1093     void        AddPoint( const Float32Point&, PolyFlags );
1094     void        ClosePolygon();
1095 
1096 private:
1097     PolyPolygon* mpPolyPoly;
1098     long        mnXOffset, mnYOffset;
1099 
1100     Point*      mpPointAry;
1101     BYTE*       mpFlagAry;
1102     USHORT      mnMaxPoints;
1103 
1104     USHORT      mnPointCount;
1105     USHORT      mnPolyCount;
1106     bool        mbHasOffline;
1107 };
1108 
1109 // -----------------------------------------------------------------------
1110 
1111 PolyArgs::PolyArgs()
1112 :   mpPolyPoly(NULL),
1113     mnPointCount(0),
1114     mnPolyCount(0),
1115     mbHasOffline(false)
1116 {
1117     mnMaxPoints = 256;
1118     mpPointAry  = new Point[ mnMaxPoints ];
1119     mpFlagAry   = new BYTE [ mnMaxPoints ];
1120 }
1121 
1122 // -----------------------------------------------------------------------
1123 
1124 PolyArgs::~PolyArgs()
1125 {
1126     delete[] mpFlagAry;
1127     delete[] mpPointAry;
1128 }
1129 
1130 // -----------------------------------------------------------------------
1131 
1132 void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset )
1133 {
1134     mnXOffset = nXOffset;
1135     mnYOffset = nYOffset;
1136     mpPolyPoly = pPolyPoly;
1137 
1138     mpPolyPoly->Clear();
1139     mnPointCount = 0;
1140     mnPolyCount  = 0;
1141 }
1142 
1143 // -----------------------------------------------------------------------
1144 
1145 void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags )
1146 {
1147     if( mnPointCount >= mnMaxPoints )
1148     {
1149         // resize if needed (TODO: use STL?)
1150         mnMaxPoints *= 4;
1151         Point* mpNewPoints = new Point[ mnMaxPoints ];
1152         BYTE* mpNewFlags = new BYTE[ mnMaxPoints ];
1153         for( int i = 0; i < mnPointCount; ++i )
1154         {
1155             mpNewPoints[ i ] = mpPointAry[ i ];
1156             mpNewFlags[ i ] = mpFlagAry[ i ];
1157         }
1158         delete[] mpFlagAry;
1159         delete[] mpPointAry;
1160         mpPointAry = mpNewPoints;
1161         mpFlagAry = mpNewFlags;
1162     }
1163 
1164     // convert to pixels and add startpoint offset
1165     int nXPos = Float32ToInt( rPoint.x );
1166     int nYPos = Float32ToInt( rPoint.y );
1167     mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset );
1168     // set point flags
1169     mpFlagAry[ mnPointCount++ ]= eFlags;
1170     mbHasOffline |= (eFlags != POLY_NORMAL);
1171 }
1172 
1173 // -----------------------------------------------------------------------
1174 
1175 void PolyArgs::ClosePolygon()
1176 {
1177     if( !mnPolyCount++ )
1178          return;
1179 
1180     // append finished polygon
1181     Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) );
1182     mpPolyPoly->Insert( aPoly );
1183 
1184     // prepare for new polygon
1185     mnPointCount = 0;
1186     mbHasOffline = false;
1187 }
1188 #endif
1189 // =======================================================================
1190 
1191 // glyph fallback is supported directly by Aqua
1192 // so methods used only by MultiSalLayout can be dummy implementated
GetGlyphOutlines(SalGraphics &,PolyPolyVector &) const1193 bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; }
InitFont()1194 void ATSLayout::InitFont() {}
MoveGlyph(int,long)1195 void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {}
DropGlyph(int)1196 void ATSLayout::DropGlyph( int /*nStart*/ ) {}
Simplify(bool)1197 void ATSLayout::Simplify( bool /*bIsBase*/ ) {}
1198 
1199 // get the ImplFontData for a glyph fallback font
1200 // for a glyphid that was returned by ATSLayout::GetNextGlyphs()
GetFallbackFontData(sal_GlyphId aGlyphId) const1201 const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const
1202 {
1203     // check if any fallback fonts were needed
1204     if( !mpFallbackInfo )
1205         return NULL;
1206     // check if the current glyph needs a fallback font
1207     int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
1208     if( !nFallbackLevel )
1209         return NULL;
1210     return mpFallbackInfo->GetFallbackFontData( nFallbackLevel );
1211 }
1212 
1213 // =======================================================================
1214 
AddFallback(ATSUFontID nFontId)1215 int FallbackInfo::AddFallback( ATSUFontID nFontId )
1216 {
1217     // check if the fallback font is already known
1218     for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel )
1219         if( maATSUFontId[ nLevel ] == nFontId )
1220             return (nLevel + 1);
1221 
1222     // append new fallback font if possible
1223     if( mnMaxLevel >= MAX_FALLBACK-1 )
1224         return 0;
1225     // keep ATSU font id of fallback font
1226     maATSUFontId[ mnMaxLevel ] = nFontId;
1227     // find and cache the corresponding ImplFontData pointer
1228     const SystemFontList* pSFL = GetSalData()->mpFontList;
1229     const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId );
1230     maFontData[ mnMaxLevel ] = pFontData;
1231     // increase fallback level by one
1232     return (++mnMaxLevel);
1233 }
1234 
1235 // -----------------------------------------------------------------------
1236 
GetFallbackFontData(int nFallbackLevel) const1237 const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const
1238 {
1239     const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ];
1240     return pFallbackFont;
1241 }
1242 
1243 // =======================================================================
1244 
GetTextLayout(ImplLayoutArgs &,int)1245 SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ )
1246 {
1247     ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale );
1248     return pATSLayout;
1249 }
1250 
1251 // =======================================================================
1252 
1253