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