| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "tools/debug.hxx" |
| |
| #include "aqua/saldata.hxx" |
| #include "aqua/salgdi.h" |
| #include "atsfonts.hxx" |
| |
| #include "sallayout.hxx" |
| #include "salgdi.hxx" |
| |
| #include <math.h> |
| |
| // ======================================================================= |
| |
| class ATSLayout : public SalLayout |
| { |
| public: |
| explicit ATSLayout( ATSUStyle&, float fFontScale ); |
| virtual ~ATSLayout(); |
| |
| virtual bool LayoutText( ImplLayoutArgs& ); |
| virtual void AdjustLayout( ImplLayoutArgs& ); |
| virtual void DrawText( SalGraphics& ) const; |
| |
| virtual int GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int&, |
| sal_Int32* pGlyphAdvances, int* pCharIndexes ) const; |
| |
| virtual long GetTextWidth() const; |
| virtual long FillDXArray( long* pDXArray ) const; |
| virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; |
| virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; |
| virtual bool GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const; |
| virtual bool GetBoundRect( SalGraphics&, Rectangle& ) const; |
| |
| const ImplFontData* GetFallbackFontData( sal_GlyphId ) const; |
| |
| virtual void InitFont(); |
| virtual void MoveGlyph( int nStart, long nNewXPos ); |
| virtual void DropGlyph( int nStart ); |
| virtual void Simplify( bool bIsBase ); |
| |
| private: |
| ATSUStyle& mrATSUStyle; |
| ATSUTextLayout maATSULayout; |
| int mnCharCount; // ==mnEndCharPos-mnMinCharPos |
| // to prevent ATS overflowing the Fixed16.16 values |
| // ATS font requests get size limited by downscaling huge fonts |
| // in these cases the font scale becomes something bigger than 1.0 |
| float mfFontScale; |
| |
| private: |
| bool InitGIA( ImplLayoutArgs* pArgs = NULL ) const; |
| bool GetIdealX() const; |
| bool GetDeltaY() const; |
| void InvalidateMeasurements(); |
| |
| int Fixed2Vcl( Fixed ) const; // convert ATSU-Fixed units to VCL units |
| int AtsuPix2Vcl( int ) const; // convert ATSU-Pixel units to VCL units |
| Fixed Vcl2Fixed( int ) const; // convert VCL units to ATSU-Fixed units |
| |
| // cached details about the resulting layout |
| // mutable members since these details are all lazy initialized |
| mutable int mnGlyphCount; // glyph count |
| mutable Fixed mnCachedWidth; // cached value of resulting typographical width |
| int mnTrailingSpaceWidth; // in Pixels |
| |
| mutable ATSGlyphRef* mpGlyphIds; // ATSU glyph ids |
| mutable Fixed* mpCharWidths; // map relative charpos to charwidth |
| mutable int* mpChars2Glyphs; // map relative charpos to absolute glyphpos |
| mutable int* mpGlyphs2Chars; // map absolute glyphpos to absolute charpos |
| mutable bool* mpGlyphRTLFlags; // BiDi status for glyphs: true if RTL |
| mutable Fixed* mpGlyphAdvances; // contains glyph widths for the justified layout |
| mutable Fixed* mpGlyphOrigAdvs; // contains glyph widths for the unjustified layout |
| mutable Fixed* mpDeltaY; // vertical offset from the baseline |
| |
| struct SubPortion { int mnMinCharPos, mnEndCharPos; Fixed mnXOffset; }; |
| typedef std::vector<SubPortion> SubPortionVector; |
| mutable SubPortionVector maSubPortions; // Writer&ATSUI layouts can differ quite a bit... |
| |
| // storing details about fonts used in glyph-fallback for this layout |
| mutable class FallbackInfo* mpFallbackInfo; |
| |
| // x-offset relative to layout origin |
| // currently only used in RTL-layouts |
| mutable Fixed mnBaseAdv; |
| }; |
| |
| class FallbackInfo |
| { |
| public: |
| FallbackInfo() : mnMaxLevel(0) {} |
| int AddFallback( ATSUFontID ); |
| const ImplFontData* GetFallbackFontData( int nLevel ) const; |
| |
| private: |
| const ImplMacFontData* maFontData[ MAX_FALLBACK ]; |
| ATSUFontID maATSUFontId[ MAX_FALLBACK ]; |
| int mnMaxLevel; |
| }; |
| |
| // ======================================================================= |
| |
| ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale ) |
| : mrATSUStyle( rATSUStyle ), |
| maATSULayout( NULL ), |
| mnCharCount( 0 ), |
| mfFontScale( fFontScale ), |
| mnGlyphCount( -1 ), |
| mnCachedWidth( 0 ), |
| mnTrailingSpaceWidth( 0 ), |
| mpGlyphIds( NULL ), |
| mpCharWidths( NULL ), |
| mpChars2Glyphs( NULL ), |
| mpGlyphs2Chars( NULL ), |
| mpGlyphRTLFlags( NULL ), |
| mpGlyphAdvances( NULL ), |
| mpGlyphOrigAdvs( NULL ), |
| mpDeltaY( NULL ), |
| mpFallbackInfo( NULL ), |
| mnBaseAdv( 0 ) |
| {} |
| |
| // ----------------------------------------------------------------------- |
| |
| ATSLayout::~ATSLayout() |
| { |
| if( mpDeltaY ) |
| ATSUDirectReleaseLayoutDataArrayPtr( NULL, |
| kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY ); |
| |
| if( maATSULayout ) |
| ATSUDisposeTextLayout( maATSULayout ); |
| |
| delete[] mpGlyphRTLFlags; |
| delete[] mpGlyphs2Chars; |
| delete[] mpChars2Glyphs; |
| if( mpCharWidths != mpGlyphAdvances ) |
| delete[] mpCharWidths; |
| delete[] mpGlyphIds; |
| delete[] mpGlyphOrigAdvs; |
| delete[] mpGlyphAdvances; |
| |
| delete mpFallbackInfo; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| inline int ATSLayout::Fixed2Vcl( Fixed nFixed ) const |
| { |
| float fFloat = mfFontScale * FixedToFloat( nFixed ); |
| return static_cast<int>(fFloat + 0.5); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| inline int ATSLayout::AtsuPix2Vcl( int nAtsuPixel) const |
| { |
| float fVclPixel = mfFontScale * nAtsuPixel; |
| fVclPixel += (fVclPixel>=0) ? +0.5 : -0.5; // prepare rounding to int |
| int nVclPixel = static_cast<int>( fVclPixel); |
| return nVclPixel; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| inline Fixed ATSLayout::Vcl2Fixed( int nPixel ) const |
| { |
| return FloatToFixed( nPixel / mfFontScale ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::LayoutText : Manage text layouting |
| * |
| * @param rArgs: contains array of char to be layouted, starting and ending position of the text to layout |
| * |
| * Typographic layout of text by using the style maATSUStyle |
| * |
| * @return : true if everything is ok |
| **/ |
| bool ATSLayout::LayoutText( ImplLayoutArgs& rArgs ) |
| { |
| if( maATSULayout ) |
| ATSUDisposeTextLayout( maATSULayout ); |
| |
| maATSULayout = NULL; |
| |
| // Layout text |
| // set up our locals, verify parameters... |
| DBG_ASSERT( (rArgs.mpStr!=NULL), "ATSLayout::LayoutText() with rArgs.mpStr==NULL !!!"); |
| DBG_ASSERT( (mrATSUStyle!=NULL), "ATSLayout::LayoutText() with ATSUStyle==NULL !!!"); |
| |
| SalLayout::AdjustLayout( rArgs ); |
| mnCharCount = mnEndCharPos - mnMinCharPos; |
| |
| // Workaround a bug in ATSUI with empty string |
| if( mnCharCount<=0 ) |
| return false; |
| |
| // create the ATSUI layout |
| UniCharCount nRunLengths[1] = { mnCharCount }; |
| const int nRunCount = sizeof(nRunLengths)/sizeof(*nRunLengths); |
| OSStatus eStatus = ATSUCreateTextLayoutWithTextPtr( rArgs.mpStr, |
| rArgs.mnMinCharPos, mnCharCount, rArgs.mnLength, |
| nRunCount, &nRunLengths[0], &mrATSUStyle, &maATSULayout); |
| |
| DBG_ASSERT( (eStatus==noErr), "ATSUCreateTextLayoutWithTextPtr failed\n"); |
| if( eStatus != noErr ) |
| return false; |
| |
| // prepare setting of layout controls |
| static const int nMaxTagCount = 1; |
| ATSUAttributeTag aTagAttrs[ nMaxTagCount ]; |
| ByteCount aTagSizes[ nMaxTagCount ]; |
| ATSUAttributeValuePtr aTagValues[ nMaxTagCount ]; |
| |
| // prepare control of "glyph fallback" |
| const SalData* pSalData = GetSalData(); |
| ATSUFontFallbacks aFontFallbacks = pSalData->mpFontList->maFontFallbacks; |
| aTagAttrs[0] = kATSULineFontFallbacksTag; |
| aTagSizes[0] = sizeof( ATSUFontFallbacks ); |
| aTagValues[0] = &aFontFallbacks; |
| |
| // set paragraph layout controls |
| ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues ); |
| |
| // enable "glyph fallback" |
| ATSUSetTransientFontMatching( maATSULayout, true ); |
| |
| // control run-specific layout controls |
| if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG) != 0 ) |
| { |
| // control BiDi defaults |
| BOOL nLineDirTag = kATSULeftToRightBaseDirection; |
| if( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) != 0 ) |
| nLineDirTag = kATSURightToLeftBaseDirection; |
| aTagAttrs[0] = kATSULineDirectionTag; |
| aTagSizes[0] = sizeof( nLineDirTag ); |
| aTagValues[0] = &nLineDirTag; |
| // set run-specific layout controls |
| #if 0 // why don't line-controls work as reliable as layout-controls??? |
| ATSUSetLineControls( maATSULayout, rArgs.mnMinCharPos, 1, aTagAttrs, aTagSizes, aTagValues ); |
| #else |
| ATSUSetLayoutControls( maATSULayout, 1, aTagAttrs, aTagSizes, aTagValues ); |
| #endif |
| } |
| |
| return true; |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::AdjustLayout : Adjust layout style |
| * |
| * @param rArgs: contains attributes relevant to do a text specific layout |
| * |
| * Adjust text layout by moving glyphs to match the requested logical widths |
| * |
| * @return : none |
| **/ |
| void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs ) |
| { |
| int nOrigWidth = GetTextWidth(); |
| int nPixelWidth = rArgs.mnLayoutWidth; |
| if( !nPixelWidth && rArgs.mpDXArray ) { |
| // for now we are only interested in the layout width |
| // TODO: use all mpDXArray elements for layouting |
| nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ]; |
| |
| // workaround for ATSUI not using trailing spaces for justification |
| int i = mnCharCount; |
| while( (--i >= 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) ) {} |
| if( i < 0 ) // nothing to do if the text is all spaces |
| return; |
| // #i91685# trailing letters are left aligned (right aligned for RTL) |
| mnTrailingSpaceWidth = rArgs.mpDXArray[ mnCharCount-1 ]; |
| if( i > 0 ) |
| mnTrailingSpaceWidth -= rArgs.mpDXArray[ i-1 ]; |
| InitGIA(); // ensure valid mpCharWidths[], TODO: use GetIdealX() instead? |
| mnTrailingSpaceWidth -= Fixed2Vcl( mpCharWidths[i] ); |
| // ignore trailing space for calculating the available width |
| nOrigWidth -= mnTrailingSpaceWidth; |
| nPixelWidth -= mnTrailingSpaceWidth; |
| // in RTL-layouts trailing spaces are leftmost |
| // TODO: use BiDi-algorithm to thoroughly check this assumption |
| if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) |
| mnBaseAdv = mnTrailingSpaceWidth; |
| } |
| // return early if there is nothing to do |
| if( !nPixelWidth ) |
| return; |
| |
| // HACK: justification requests which change the width by just one pixel were probably |
| // #i86038# introduced by lossy conversions between integer based coordinate system |
| // => ignoring such requests has many more benefits than eventual drawbacks |
| if( (nOrigWidth >= nPixelWidth-1) && (nOrigWidth <= nPixelWidth+1) ) |
| return; |
| |
| // changing the layout will make all previous measurements invalid |
| InvalidateMeasurements(); |
| |
| ATSUAttributeTag nTags[3]; |
| ATSUAttributeValuePtr nVals[3]; |
| ByteCount nBytes[3]; |
| |
| Fixed nFixedWidth = Vcl2Fixed( nPixelWidth ); |
| mnCachedWidth = nFixedWidth; |
| Fract nFractFactor = kATSUFullJustification; |
| ATSLineLayoutOptions nLineLayoutOptions = kATSLineHasNoHangers | kATSLineHasNoOpticalAlignment | kATSLineBreakToNearestCharacter; |
| |
| nTags[0] = kATSULineWidthTag; |
| nVals[0] = &nFixedWidth; |
| nBytes[0] = sizeof(Fixed); |
| nTags[1] = kATSULineLayoutOptionsTag; |
| nVals[1] = &nLineLayoutOptions; |
| nBytes[1] = sizeof(ATSLineLayoutOptions); |
| nTags[2] = kATSULineJustificationFactorTag; |
| nVals[2] = &nFractFactor; |
| nBytes[2] = sizeof(Fract); |
| |
| OSStatus eStatus = ATSUSetLayoutControls( maATSULayout, 3, nTags, nBytes, nVals ); |
| if( eStatus != noErr ) |
| return; |
| |
| // update the measurements of the justified layout to match the justification request |
| if( rArgs.mpDXArray ) |
| InitGIA( &rArgs ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::DrawText : Draw text to screen |
| * |
| * @param rGraphics: device to draw to |
| * |
| * Draw the layouted text to the CGContext |
| * |
| * @return : none |
| **/ |
| void ATSLayout::DrawText( SalGraphics& rGraphics ) const |
| { |
| AquaSalGraphics& rAquaGraphics = static_cast<AquaSalGraphics&>(rGraphics); |
| |
| // short circuit if there is nothing to do |
| if( (mnCharCount <= 0) |
| || !rAquaGraphics.CheckContext() ) |
| return; |
| |
| // the view is vertically flipped => flipped glyphs |
| // so apply a temporary transformation that it flips back |
| // also compensate if the font was size limited |
| CGContextSaveGState( rAquaGraphics.mrContext ); |
| CGContextScaleCTM( rAquaGraphics.mrContext, +mfFontScale, -mfFontScale ); |
| CGContextSetShouldAntialias( rAquaGraphics.mrContext, !rAquaGraphics.mbNonAntialiasedText ); |
| |
| // prepare ATSUI drawing attributes |
| static const ItemCount nMaxControls = 8; |
| ATSUAttributeTag theTags[ nMaxControls ]; |
| ByteCount theSizes[ nMaxControls]; |
| ATSUAttributeValuePtr theValues[ nMaxControls ]; |
| ItemCount numcontrols = 0; |
| |
| // Tell ATSUI to use CoreGraphics |
| theTags[numcontrols] = kATSUCGContextTag; |
| theSizes[numcontrols] = sizeof( CGContextRef ); |
| theValues[numcontrols++] = &rAquaGraphics.mrContext; |
| |
| // Rotate if necessary |
| if( rAquaGraphics.mnATSUIRotation != 0 ) |
| { |
| Fixed theAngle = rAquaGraphics.mnATSUIRotation; |
| theTags[numcontrols] = kATSULineRotationTag; |
| theSizes[numcontrols] = sizeof( Fixed ); |
| theValues[numcontrols++] = &theAngle; |
| } |
| |
| DBG_ASSERT( (numcontrols <= nMaxControls), "ATSLayout::DrawText() numcontrols overflow" ); |
| OSStatus theErr = ATSUSetLayoutControls (maATSULayout, numcontrols, theTags, theSizes, theValues); |
| DBG_ASSERT( (theErr==noErr), "ATSLayout::DrawText ATSUSetLayoutControls failed!\n" ); |
| |
| // Draw the text |
| const Point aPos = GetDrawPosition( Point(mnBaseAdv,0) ); |
| const Fixed nFixedX = Vcl2Fixed( +aPos.X() ); |
| const Fixed nFixedY = Vcl2Fixed( -aPos.Y() ); // adjusted for y-mirroring |
| if( maSubPortions.empty() ) |
| ATSUDrawText( maATSULayout, mnMinCharPos, mnCharCount, nFixedX, nFixedY ); |
| else |
| { |
| // draw the sub-portions and apply individual adjustments |
| SubPortionVector::const_iterator it = maSubPortions.begin(); |
| for(; it != maSubPortions.end(); ++it ) |
| { |
| const SubPortion& rSubPortion = *it; |
| // calculate sub-portion offset for rotated text |
| Fixed nXOfsFixed = 0, nYOfsFixed = 0; |
| if( rAquaGraphics.mnATSUIRotation != 0 ) |
| { |
| const double fRadians = rAquaGraphics.mnATSUIRotation * (M_PI/0xB40000); |
| nXOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * cos( fRadians )); |
| nYOfsFixed = static_cast<Fixed>(static_cast<double>(+rSubPortion.mnXOffset) * sin( fRadians )); |
| } |
| |
| // draw sub-portions |
| ATSUDrawText( maATSULayout, |
| rSubPortion.mnMinCharPos, rSubPortion.mnEndCharPos - rSubPortion.mnMinCharPos, |
| nFixedX + nXOfsFixed, nFixedY + nYOfsFixed ); |
| } |
| } |
| |
| // request an update of the changed window area |
| if( rAquaGraphics.IsWindowGraphics() ) |
| { |
| Rect drawRect; // rectangle of the changed area |
| theErr = ATSUMeasureTextImage( maATSULayout, |
| mnMinCharPos, mnCharCount, nFixedX, nFixedY, &drawRect ); |
| if( theErr == noErr ) |
| { |
| // FIXME: transformation from baseline to top left |
| // with the simple approach below we invalidate too much |
| short d = drawRect.bottom - drawRect.top; |
| drawRect.top -= d; |
| drawRect.bottom += d; |
| CGRect aRect = CGRectMake( drawRect.left, drawRect.top, |
| drawRect.right - drawRect.left, |
| drawRect.bottom - drawRect.top ); |
| aRect = CGContextConvertRectToDeviceSpace( rAquaGraphics.mrContext, aRect ); |
| rAquaGraphics.RefreshRect( aRect ); |
| } |
| } |
| |
| // restore the original graphic context transformations |
| CGContextRestoreGState( rAquaGraphics.mrContext ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::GetNextGlyphs : Get info about next glyphs in the layout |
| * |
| * @param nLen: max number of char |
| * @param pGlyphs: returned array of glyph ids |
| * @param rPos: returned x starting position |
| * @param nStart: index of the first requested glyph |
| * @param pGlyphAdvances: returned array of glyphs advances |
| * @param pCharIndexes: returned array of char indexes |
| * |
| * Returns infos about the next glyphs in the text layout |
| * |
| * @return : number of glyph details that were provided |
| **/ |
| int ATSLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart, |
| sal_Int32* pGlyphAdvances, int* pCharIndexes ) const |
| { |
| if( nStart < 0 ) // first glyph requested? |
| nStart = 0; |
| |
| // get glyph measurements |
| InitGIA(); |
| // some measurements are only needed for multi-glyph results |
| if( nLen > 1 ) |
| { |
| GetIdealX(); |
| GetDeltaY(); |
| } |
| |
| if( nStart >= mnGlyphCount ) // no glyph left? |
| return 0; |
| |
| // calculate glyph position relative to layout base |
| // TODO: avoid for nStart!=0 case by reusing rPos |
| Fixed nXOffset = mnBaseAdv; |
| for( int i = 0; i < nStart; ++i ) |
| nXOffset += mpGlyphAdvances[ i ]; |
| // if sub-portion offsets are involved there is an additional x-offset |
| if( !maSubPortions.empty() ) |
| { |
| // prepare to find the sub-portion |
| int nCharPos = nStart + mnMinCharPos; |
| if( mpGlyphs2Chars ) |
| nCharPos = mpGlyphs2Chars[nStart]; |
| |
| // find the matching subportion |
| // TODO: is a non-linear search worth it? |
| SubPortionVector::const_iterator it = maSubPortions.begin(); |
| for(; it != maSubPortions.end(); ++it) { |
| const SubPortion& r = *it; |
| if( nCharPos < r.mnMinCharPos ) |
| continue; |
| if( nCharPos >= r.mnEndCharPos ) |
| continue; |
| // apply the sub-portion xoffset |
| nXOffset += r.mnXOffset; |
| break; |
| } |
| } |
| |
| Fixed nYOffset = 0; |
| if( mpDeltaY ) |
| nYOffset = mpDeltaY[ nStart ]; |
| |
| // calculate absolute position in pixel units |
| const Point aRelativePos( Fix2Long(static_cast<Fixed>(nXOffset*mfFontScale)), Fix2Long(static_cast<Fixed>(nYOffset*mfFontScale)) ); |
| rPos = GetDrawPosition( aRelativePos ); |
| |
| // update return values |
| int nCount = 0; |
| while( nCount < nLen ) |
| { |
| ++nCount; |
| sal_GlyphId aGlyphId = mpGlyphIds[nStart]; |
| |
| // check if glyph fallback is needed for this glyph |
| // TODO: use ATSUDirectGetLayoutDataArrayPtrFromTextLayout(kATSUDirectDataStyleIndex) API instead? |
| const int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[nStart] : nStart + mnMinCharPos; |
| ATSUFontID nFallbackFontID = kATSUInvalidFontID; |
| UniCharArrayOffset nChangedOffset = 0; |
| UniCharCount nChangedLength = 0; |
| OSStatus eStatus = ATSUMatchFontsToText( maATSULayout, nCharPos, kATSUToTextEnd, |
| &nFallbackFontID, &nChangedOffset, &nChangedLength ); |
| if( (eStatus == kATSUFontsMatched) && ((int)nChangedOffset == nCharPos) ) |
| { |
| // fallback is needed |
| if( !mpFallbackInfo ) |
| mpFallbackInfo = new FallbackInfo; |
| // register fallback font |
| const int nLevel = mpFallbackInfo->AddFallback( nFallbackFontID ); |
| // update sal_GlyphId with fallback level |
| aGlyphId |= (nLevel << GF_FONTSHIFT); |
| } |
| |
| // update resulting glyphid array |
| *(pOutGlyphIds++) = aGlyphId; |
| |
| // update returned glyph advance array |
| if( pGlyphAdvances ) |
| *(pGlyphAdvances++) = Fixed2Vcl( mpGlyphAdvances[nStart] ); |
| |
| // update returned index-into-string array |
| if( pCharIndexes ) |
| { |
| int nCharPos; |
| if( mpGlyphs2Chars ) |
| nCharPos = mpGlyphs2Chars[nStart]; |
| else |
| nCharPos = nStart + mnMinCharPos; |
| *(pCharIndexes++) = nCharPos; |
| } |
| |
| // stop at last glyph |
| if( ++nStart >= mnGlyphCount ) |
| break; |
| |
| // stop when next the x-position is unexpected |
| if( !maSubPortions.empty() ) |
| break; // TODO: finish the complete sub-portion |
| if( !pGlyphAdvances && mpGlyphOrigAdvs ) |
| if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] ) |
| break; |
| |
| // stop when the next y-position is unexpected |
| if( mpDeltaY ) |
| if( mpDeltaY[nStart-1] != mpDeltaY[nStart] ) |
| break; |
| } |
| |
| return nCount; |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::GetTextWidth : Get typographic width of layouted text |
| * |
| * Get typographic bounds of the text |
| * |
| * @return : text width |
| **/ |
| long ATSLayout::GetTextWidth() const |
| { |
| if( mnCharCount <= 0 ) |
| return 0; |
| |
| DBG_ASSERT( (maATSULayout!=NULL), "ATSLayout::GetTextWidth() with maATSULayout==NULL !\n"); |
| if( !maATSULayout ) |
| return 0; |
| |
| if( !mnCachedWidth ) |
| { |
| // prepare precise measurements on pixel based or reference-device |
| const UInt16 eTypeOfBounds = kATSUseFractionalOrigins; |
| |
| // determine number of needed measurement trapezoids |
| ItemCount nMaxBounds = 0; |
| OSStatus err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount, |
| eTypeOfBounds, 0, NULL, &nMaxBounds ); |
| if( (err != noErr) |
| || (nMaxBounds <= 0) ) |
| return 0; |
| |
| // get the trapezoids |
| typedef std::vector<ATSTrapezoid> TrapezoidVector; |
| TrapezoidVector aTrapezoidVector( nMaxBounds ); |
| ItemCount nBoundsCount = 0; |
| err = ATSUGetGlyphBounds( maATSULayout, 0, 0, mnMinCharPos, mnCharCount, |
| eTypeOfBounds, nMaxBounds, &aTrapezoidVector[0], &nBoundsCount ); |
| if( err != noErr ) |
| return 0; |
| |
| DBG_ASSERT( (nBoundsCount <= nMaxBounds), "ATSLayout::GetTextWidth() : too many trapezoids !\n"); |
| |
| // find the bound extremas |
| Fixed nLeftBound = 0; |
| Fixed nRightBound = 0; |
| for( ItemCount i = 0; i < nBoundsCount; ++i ) |
| { |
| const ATSTrapezoid& rTrap = aTrapezoidVector[i]; |
| if( (i == 0) || (nLeftBound < rTrap.lowerLeft.x) ) |
| nLeftBound = rTrap.lowerLeft.x; |
| if( (i == 0) || (nRightBound > rTrap.lowerRight.x) ) |
| nRightBound = rTrap.lowerRight.x; |
| } |
| |
| // measure the bound extremas |
| mnCachedWidth = nRightBound - nLeftBound; |
| // adjust for eliminated trailing space widths |
| } |
| |
| int nScaledWidth = Fixed2Vcl( mnCachedWidth ); |
| nScaledWidth += mnTrailingSpaceWidth; |
| return nScaledWidth; |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::FillDXArray : Get Char widths |
| * |
| * @param pDXArray: array to be filled with x-advances |
| * |
| * Fill the pDXArray with horizontal deltas : CharWidths |
| * |
| * @return : typographical width of the complete text layout |
| **/ |
| long ATSLayout::FillDXArray( long* pDXArray ) const |
| { |
| // short circuit requests which don't need full details |
| if( !pDXArray ) |
| return GetTextWidth(); |
| |
| // check assumptions |
| DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" ); |
| |
| // initialize details about the resulting layout |
| InitGIA(); |
| |
| // distribute the widths among the string elements |
| int nPixWidth = 0; |
| mnCachedWidth = 0; |
| for( int i = 0; i < mnCharCount; ++i ) |
| { |
| // convert and adjust for accumulated rounding errors |
| mnCachedWidth += mpCharWidths[i]; |
| const int nOldPixWidth = nPixWidth; |
| nPixWidth = Fixed2Vcl( mnCachedWidth ); |
| pDXArray[i] = nPixWidth - nOldPixWidth; |
| } |
| |
| return nPixWidth; |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::GetTextBreak : Find line break depending on width |
| * |
| * @param nMaxWidth : maximal logical text width in subpixel units |
| * @param nCharExtra: expanded/condensed spacing in subpixel units |
| * @param nFactor: number of subpixel units per pixel |
| * |
| * Measure the layouted text to find the typographical line break |
| * the result is needed by the language specific line breaking |
| * |
| * @return : string index corresponding to the suggested line break |
| **/ |
| int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const |
| { |
| if( !maATSULayout ) |
| return STRING_LEN; |
| |
| // the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine() |
| if( nCharExtra != 0 ) |
| { |
| // prepare the measurement by layouting and measuring the un-expanded/un-condensed text |
| if( !InitGIA() ) |
| return STRING_LEN; |
| |
| // TODO: use a better way than by testing each the char position |
| ATSUTextMeasurement nATSUSumWidth = 0; |
| const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor ); |
| const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor; |
| for( int i = 0; i < mnCharCount; ++i ) |
| { |
| nATSUSumWidth += mpCharWidths[i]; |
| if( nATSUSumWidth >= nATSUMaxWidth ) |
| return (mnMinCharPos + i); |
| nATSUSumWidth += nATSUExtraWidth; |
| if( nATSUSumWidth >= nATSUMaxWidth ) |
| if( i+1 < mnCharCount ) |
| return (mnMinCharPos + i); |
| } |
| |
| return STRING_LEN; |
| } |
| |
| // get a quick overview on what could fit |
| const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor; |
| if( nPixelWidth <= 0 ) |
| return mnMinCharPos; |
| |
| // check assumptions |
| DBG_ASSERT( !mnTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" ); |
| |
| // initial measurement of text break position |
| UniCharArrayOffset nBreakPos = mnMinCharPos; |
| const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth ); |
| if( nATSUMaxWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter |
| return mnMinCharPos; // or do ATSUMaxWidth=0x10000; |
| OSStatus eStatus = ATSUBreakLine( maATSULayout, mnMinCharPos, |
| nATSUMaxWidth, false, &nBreakPos ); |
| if( (eStatus != noErr) && (eStatus != kATSULineBreakInWord) ) |
| return STRING_LEN; |
| |
| // the result from ATSUBreakLine() doesn't match the semantics expected by its |
| // application layer callers from SW+SVX+I18N. Adjust the results to the expectations: |
| |
| // ATSU reports that everything fits even when trailing spaces would break the line |
| // #i89789# OOo's application layers expect STRING_LEN if everything fits |
| if( nBreakPos >= static_cast<UniCharArrayOffset>(mnEndCharPos) ) |
| return STRING_LEN; |
| |
| // GetTextBreak()'s callers expect it to return the "stupid visual line break". |
| // Returning anything else result.s in subtle problems in the application layers. |
| static const bool bInWord = true; // TODO: add as argument to GetTextBreak() method |
| if( !bInWord ) |
| return nBreakPos; |
| |
| // emulate stupid visual line breaking by line breaking for the remaining width |
| ATSUTextMeasurement nLeft, nRight, nDummy; |
| eStatus = ATSUGetUnjustifiedBounds( maATSULayout, mnMinCharPos, nBreakPos-mnMinCharPos, |
| &nLeft, &nRight, &nDummy, &nDummy ); |
| if( eStatus != noErr ) |
| return nBreakPos; |
| const ATSUTextMeasurement nATSURemWidth = nATSUMaxWidth - (nRight - nLeft); |
| if( nATSURemWidth <= 0xFFFF ) // #i108584# avoid ATSU rejecting the parameter |
| return nBreakPos; |
| UniCharArrayOffset nBreakPosInWord = nBreakPos; |
| eStatus = ATSUBreakLine( maATSULayout, nBreakPos, nATSURemWidth, false, &nBreakPosInWord ); |
| return nBreakPosInWord; |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::GetCaretPositions : Find positions of carets |
| * |
| * @param nMaxIndex position to which we want to find the carets |
| * |
| * Fill the array of positions of carets (for cursors and selections) |
| * |
| * @return : none |
| **/ |
| void ATSLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const |
| { |
| DBG_ASSERT( ((nMaxIndex>0)&&!(nMaxIndex&1)), |
| "ATSLayout::GetCaretPositions() : invalid number of caret pairs requested"); |
| |
| // initialize the caret positions |
| for( int i = 0; i < nMaxIndex; ++i ) |
| pCaretXArray[ i ] = -1; |
| |
| for( int n = 0; n <= mnCharCount; ++n ) |
| { |
| // measure the characters cursor position |
| typedef unsigned char Boolean; |
| const Boolean bIsLeading = true; |
| ATSUCaret aCaret0, aCaret1; |
| Boolean bIsSplit; |
| OSStatus eStatus = ATSUOffsetToCursorPosition( maATSULayout, |
| mnMinCharPos + n, bIsLeading, kATSUByCharacter, |
| &aCaret0, &aCaret1, &bIsSplit ); |
| if( eStatus != noErr ) |
| continue; |
| const Fixed nFixedPos = mnBaseAdv + aCaret0.fX; |
| // convert the measurement to pixel units |
| const int nPixelPos = Fixed2Vcl( nFixedPos ); |
| // update previous trailing position |
| if( n > 0 ) |
| pCaretXArray[2*n-1] = nPixelPos; |
| // update current leading position |
| if( 2*n >= nMaxIndex ) |
| break; |
| pCaretXArray[2*n+0] = nPixelPos; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::GetBoundRect : Get rectangle dim containing the layouted text |
| * |
| * @param rVCLRect: rectangle of text image (layout) measures |
| * |
| * Get ink bounds of the text |
| * |
| * @return : measurement valid |
| **/ |
| bool ATSLayout::GetBoundRect( SalGraphics&, Rectangle& rVCLRect ) const |
| { |
| const Point aPos = GetDrawPosition( Point(mnBaseAdv, 0) ); |
| const Fixed nFixedX = Vcl2Fixed( +aPos.X() ); |
| const Fixed nFixedY = Vcl2Fixed( +aPos.Y() ); |
| |
| Rect aMacRect; |
| OSStatus eStatus = ATSUMeasureTextImage( maATSULayout, |
| mnMinCharPos, mnCharCount, nFixedX, nFixedY, &aMacRect ); |
| if( eStatus != noErr ) |
| return false; |
| |
| // ATSU top-bottom are vertically flipped from a VCL aspect |
| rVCLRect.Left() = AtsuPix2Vcl( aMacRect.left ); |
| rVCLRect.Top() = AtsuPix2Vcl( aMacRect.top ); |
| rVCLRect.Right() = AtsuPix2Vcl( aMacRect.right ); |
| rVCLRect.Bottom() = AtsuPix2Vcl( aMacRect.bottom ); |
| return true; |
| } |
| |
| // ----------------------------------------------------------------------- |
| /** |
| * ATSLayout::InitGIA() : get many informations about layouted text |
| * |
| * Fills arrays of information about the gylph layout previously done |
| * in ASTLayout::LayoutText() : glyph advance (width), glyph delta Y (from baseline), |
| * mapping between glyph index and character index, chars widths |
| * |
| * @return : true if everything could be computed, otherwise false |
| **/ |
| bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const |
| { |
| // no need to run InitGIA more than once on the same ATSLayout object |
| if( mnGlyphCount >= 0 ) |
| return true; |
| mnGlyphCount = 0; |
| |
| // Workaround a bug in ATSUI with empty string |
| if( mnCharCount <= 0 ) |
| return false; |
| |
| // initialize character details |
| mpCharWidths = new Fixed[ mnCharCount ]; |
| mpChars2Glyphs = new int[ mnCharCount ]; |
| for( int n = 0; n < mnCharCount; ++n ) |
| { |
| mpCharWidths[ n ] = 0; |
| mpChars2Glyphs[ n ] = -1; |
| } |
| |
| // get details about the glyph layout |
| ItemCount iLayoutDataCount; |
| const ATSLayoutRecord* pALR; |
| OSStatus eStatus = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( |
| maATSULayout, mnMinCharPos, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, |
| (void**)&pALR, &iLayoutDataCount ); |
| DBG_ASSERT( (eStatus==noErr), "ATSLayout::InitGIA() : no ATSLayoutRecords!\n"); |
| if( (eStatus != noErr) |
| || (iLayoutDataCount <= 1) ) |
| return false; |
| |
| // initialize glyph details |
| mpGlyphIds = new ATSGlyphRef[ iLayoutDataCount ]; |
| mpGlyphAdvances = new Fixed[ iLayoutDataCount ]; |
| mpGlyphs2Chars = new int[ iLayoutDataCount ]; |
| |
| // measure details of the glyph layout |
| Fixed nLeftPos = 0; |
| for( ItemCount i = 0; i < iLayoutDataCount; ++i ) |
| { |
| const ATSLayoutRecord& rALR = pALR[i]; |
| |
| // distribute the widths as fairly as possible among the chars |
| const int nRelativeIdx = (rALR.originalOffset / 2); |
| if( i+1 < iLayoutDataCount ) |
| mpCharWidths[ nRelativeIdx ] += pALR[i+1].realPos - rALR.realPos; |
| |
| // new glyph is available => finish measurement of old glyph |
| if( mnGlyphCount > 0 ) |
| mpGlyphAdvances[ mnGlyphCount-1 ] = rALR.realPos - nLeftPos; |
| |
| // ignore marker or deleted glyphs |
| enum { MARKED_OUTGLYPH=0xFFFE, DROPPED_OUTGLYPH=0xFFFF}; |
| if( rALR.glyphID >= MARKED_OUTGLYPH ) |
| continue; |
| |
| DBG_ASSERT( !(rALR.flags & kATSGlyphInfoTerminatorGlyph), |
| "ATSLayout::InitGIA(): terminator glyph not marked as deleted!" ); |
| |
| // store details of the visible glyphs |
| nLeftPos = rALR.realPos; |
| mpGlyphIds[ mnGlyphCount ] = rALR.glyphID; |
| |
| // map visible glyphs to their counterparts in the UTF16-character array |
| mpGlyphs2Chars[ mnGlyphCount ] = nRelativeIdx + mnMinCharPos; |
| mpChars2Glyphs[ nRelativeIdx ] = mnGlyphCount; |
| |
| ++mnGlyphCount; |
| } |
| |
| // measure complete width |
| mnCachedWidth = mnBaseAdv; |
| mnCachedWidth += pALR[iLayoutDataCount-1].realPos - pALR[0].realPos; |
| |
| #if (OSL_DEBUG_LEVEL > 1) |
| Fixed nWidthSum = mnBaseAdv; |
| for( int n = 0; n < mnCharCount; ++n ) |
| nWidthSum += mpCharWidths[ n ]; |
| DBG_ASSERT( (nWidthSum==mnCachedWidth), |
| "ATSLayout::InitGIA(): measured widths do not match!\n" ); |
| #endif |
| |
| // #i91183# we need to split up the portion into sub-portions |
| // if the ATSU-layout differs too much from the requested layout |
| if( pArgs && pArgs->mpDXArray ) |
| { |
| // TODO: non-strong-LTR case cases should be handled too |
| if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG) |
| && !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) ) |
| { |
| Fixed nSumCharWidths = 0; |
| SubPortion aSubPortion = { mnMinCharPos, 0, 0 }; |
| for( int i = 0; i < mnCharCount; ++i ) |
| { |
| // calculate related logical position |
| nSumCharWidths += mpCharWidths[i]; |
| |
| // start new sub-portion if needed |
| const Fixed nNextXPos = Vcl2Fixed(pArgs->mpDXArray[i]); |
| const Fixed nNextXOffset = nNextXPos - nSumCharWidths; |
| const Fixed nFixedDiff = aSubPortion.mnXOffset - nNextXOffset; |
| if( (nFixedDiff < -0xC000) || (nFixedDiff > +0xC000) ) { |
| // get to the end of the current sub-portion |
| // prevent splitting up at diacritics etc. |
| int j = i; |
| while( (++j < mnCharCount) && !mpCharWidths[j] ); |
| aSubPortion.mnEndCharPos = mnMinCharPos + j; |
| // emit current sub-portion |
| maSubPortions.push_back( aSubPortion ); |
| // prepare next sub-portion |
| aSubPortion.mnMinCharPos = aSubPortion.mnEndCharPos; |
| aSubPortion.mnXOffset = nNextXOffset; |
| } |
| } |
| |
| // emit the remaining sub-portion |
| if( !maSubPortions.empty() ) |
| { |
| aSubPortion.mnEndCharPos = mnEndCharPos; |
| if( aSubPortion.mnEndCharPos != aSubPortion.mnMinCharPos ) |
| maSubPortions.push_back( aSubPortion ); |
| } |
| } |
| |
| // override layouted charwidths with requested charwidths |
| for( int n = 0; n < mnCharCount; ++n ) |
| mpCharWidths[ n ] = pArgs->mpDXArray[ n ]; |
| } |
| |
| // release the ATSU layout records |
| ATSUDirectReleaseLayoutDataArrayPtr(NULL, |
| kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void**)&pALR ); |
| |
| return true; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| bool ATSLayout::GetIdealX() const |
| { |
| // compute the ideal advance widths only once |
| if( mpGlyphOrigAdvs != NULL ) |
| return true; |
| |
| DBG_ASSERT( (mpGlyphIds!=NULL), "GetIdealX() called with mpGlyphIds==NULL !" ); |
| DBG_ASSERT( (mrATSUStyle!=NULL), "GetIdealX called with mrATSUStyle==NULL !" ); |
| |
| // TODO: cache ideal metrics per glyph? |
| std::vector<ATSGlyphIdealMetrics> aIdealMetrics; |
| aIdealMetrics.resize( mnGlyphCount ); |
| OSStatus theErr = ATSUGlyphGetIdealMetrics( mrATSUStyle, |
| mnGlyphCount, &mpGlyphIds[0], sizeof(*mpGlyphIds), &aIdealMetrics[0] ); |
| DBG_ASSERT( (theErr==noErr), "ATSUGlyphGetIdealMetrics failed!"); |
| if( theErr != noErr ) |
| return false; |
| |
| mpGlyphOrigAdvs = new Fixed[ mnGlyphCount ]; |
| for( int i = 0;i < mnGlyphCount;++i ) |
| mpGlyphOrigAdvs[i] = FloatToFixed( aIdealMetrics[i].advance.x ); |
| |
| return true; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| bool ATSLayout::GetDeltaY() const |
| { |
| // don't bother to get the same delta-y-array more than once |
| if( mpDeltaY != NULL ) |
| return true; |
| |
| #if 1 |
| if( !maATSULayout ) |
| return false; |
| |
| // get and keep the y-deltas in the mpDeltaY member variable |
| // => release it in the destructor |
| ItemCount nDeltaCount = 0; |
| OSStatus theErr = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( |
| maATSULayout, mnMinCharPos, kATSUDirectDataBaselineDeltaFixedArray, |
| (void**)&mpDeltaY, &nDeltaCount ); |
| |
| DBG_ASSERT( (theErr==noErr ), "mpDeltaY - ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed!\n"); |
| if( theErr != noErr ) |
| return false; |
| |
| if( mpDeltaY == NULL ) |
| return true; |
| |
| if( nDeltaCount != (ItemCount)mnGlyphCount ) |
| { |
| DBG_WARNING( "ATSLayout::GetDeltaY() : wrong deltaY count!" ); |
| ATSUDirectReleaseLayoutDataArrayPtr( NULL, |
| kATSUDirectDataBaselineDeltaFixedArray, (void**)&mpDeltaY ); |
| mpDeltaY = NULL; |
| return false; |
| } |
| #endif |
| |
| return true; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| #define DELETEAZ( X ) { delete[] X; X = NULL; } |
| |
| void ATSLayout::InvalidateMeasurements() |
| { |
| mnGlyphCount = -1; |
| DELETEAZ( mpGlyphIds ); |
| DELETEAZ( mpCharWidths ); |
| DELETEAZ( mpChars2Glyphs ); |
| DELETEAZ( mpGlyphs2Chars ); |
| DELETEAZ( mpGlyphRTLFlags ); |
| DELETEAZ( mpGlyphAdvances ); |
| DELETEAZ( mpGlyphOrigAdvs ); |
| DELETEAZ( mpDeltaY ); |
| } |
| |
| // ======================================================================= |
| |
| #if 0 |
| // helper class to convert ATSUI outlines to VCL PolyPolygons |
| class PolyArgs |
| { |
| public: |
| PolyArgs(); |
| ~PolyArgs(); |
| |
| void Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset ); |
| void AddPoint( const Float32Point&, PolyFlags ); |
| void ClosePolygon(); |
| |
| private: |
| PolyPolygon* mpPolyPoly; |
| long mnXOffset, mnYOffset; |
| |
| Point* mpPointAry; |
| BYTE* mpFlagAry; |
| USHORT mnMaxPoints; |
| |
| USHORT mnPointCount; |
| USHORT mnPolyCount; |
| bool mbHasOffline; |
| }; |
| |
| // ----------------------------------------------------------------------- |
| |
| PolyArgs::PolyArgs() |
| : mpPolyPoly(NULL), |
| mnPointCount(0), |
| mnPolyCount(0), |
| mbHasOffline(false) |
| { |
| mnMaxPoints = 256; |
| mpPointAry = new Point[ mnMaxPoints ]; |
| mpFlagAry = new BYTE [ mnMaxPoints ]; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| PolyArgs::~PolyArgs() |
| { |
| delete[] mpFlagAry; |
| delete[] mpPointAry; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void PolyArgs::Init( PolyPolygon* pPolyPoly, long nXOffset, long nYOffset ) |
| { |
| mnXOffset = nXOffset; |
| mnYOffset = nYOffset; |
| mpPolyPoly = pPolyPoly; |
| |
| mpPolyPoly->Clear(); |
| mnPointCount = 0; |
| mnPolyCount = 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void PolyArgs::AddPoint( const Float32Point& rPoint, PolyFlags eFlags ) |
| { |
| if( mnPointCount >= mnMaxPoints ) |
| { |
| // resize if needed (TODO: use STL?) |
| mnMaxPoints *= 4; |
| Point* mpNewPoints = new Point[ mnMaxPoints ]; |
| BYTE* mpNewFlags = new BYTE[ mnMaxPoints ]; |
| for( int i = 0; i < mnPointCount; ++i ) |
| { |
| mpNewPoints[ i ] = mpPointAry[ i ]; |
| mpNewFlags[ i ] = mpFlagAry[ i ]; |
| } |
| delete[] mpFlagAry; |
| delete[] mpPointAry; |
| mpPointAry = mpNewPoints; |
| mpFlagAry = mpNewFlags; |
| } |
| |
| // convert to pixels and add startpoint offset |
| int nXPos = Float32ToInt( rPoint.x ); |
| int nYPos = Float32ToInt( rPoint.y ); |
| mpPointAry[ mnPointCount ] = Point( nXPos + mnXOffset, nYPos + mnYOffset ); |
| // set point flags |
| mpFlagAry[ mnPointCount++ ]= eFlags; |
| mbHasOffline |= (eFlags != POLY_NORMAL); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void PolyArgs::ClosePolygon() |
| { |
| if( !mnPolyCount++ ) |
| return; |
| |
| // append finished polygon |
| Polygon aPoly( mnPointCount, mpPointAry, (mbHasOffline ? mpFlagAry : NULL) ); |
| mpPolyPoly->Insert( aPoly ); |
| |
| // prepare for new polygon |
| mnPointCount = 0; |
| mbHasOffline = false; |
| } |
| #endif |
| // ======================================================================= |
| |
| // glyph fallback is supported directly by Aqua |
| // so methods used only by MultiSalLayout can be dummy implementated |
| bool ATSLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const { return false; } |
| void ATSLayout::InitFont() {} |
| void ATSLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ ) {} |
| void ATSLayout::DropGlyph( int /*nStart*/ ) {} |
| void ATSLayout::Simplify( bool /*bIsBase*/ ) {} |
| |
| // get the ImplFontData for a glyph fallback font |
| // for a glyphid that was returned by ATSLayout::GetNextGlyphs() |
| const ImplFontData* ATSLayout::GetFallbackFontData( sal_GlyphId aGlyphId ) const |
| { |
| // check if any fallback fonts were needed |
| if( !mpFallbackInfo ) |
| return NULL; |
| // check if the current glyph needs a fallback font |
| int nFallbackLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; |
| if( !nFallbackLevel ) |
| return NULL; |
| return mpFallbackInfo->GetFallbackFontData( nFallbackLevel ); |
| } |
| |
| // ======================================================================= |
| |
| int FallbackInfo::AddFallback( ATSUFontID nFontId ) |
| { |
| // check if the fallback font is already known |
| for( int nLevel = 0; nLevel < mnMaxLevel; ++nLevel ) |
| if( maATSUFontId[ nLevel ] == nFontId ) |
| return (nLevel + 1); |
| |
| // append new fallback font if possible |
| if( mnMaxLevel >= MAX_FALLBACK-1 ) |
| return 0; |
| // keep ATSU font id of fallback font |
| maATSUFontId[ mnMaxLevel ] = nFontId; |
| // find and cache the corresponding ImplFontData pointer |
| const SystemFontList* pSFL = GetSalData()->mpFontList; |
| const ImplMacFontData* pFontData = pSFL->GetFontDataFromId( nFontId ); |
| maFontData[ mnMaxLevel ] = pFontData; |
| // increase fallback level by one |
| return (++mnMaxLevel); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| const ImplFontData* FallbackInfo::GetFallbackFontData( int nFallbackLevel ) const |
| { |
| const ImplMacFontData* pFallbackFont = maFontData[ nFallbackLevel-1 ]; |
| return pFallbackFont; |
| } |
| |
| // ======================================================================= |
| |
| SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ ) |
| { |
| ATSLayout* pATSLayout = new ATSLayout( maATSUStyle, mfFontScale ); |
| return pATSLayout; |
| } |
| |
| // ======================================================================= |
| |