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 implemented
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