merge the formfield patch from ooo-build
[ooovba.git] / vcl / os2 / source / gdi / os2layout.cxx
blobfbd8b7d2319d2fd5aa6e08b6b4b876853f8a874e
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: os2layout.cxx,v $
10 * $Revision: 1.6 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include <tools/svwin.h>
33 #include <rtl/ustring.hxx>
34 #include <osl/module.h>
35 #include <salgdi.h>
36 #include <saldata.hxx>
37 #include <vcl/sallayout.hxx>
39 #ifndef __H_FT2LIB
40 #include <wingdi.h>
41 #include <ft2lib.h>
42 #endif
44 #include <cstdio>
45 #include <malloc.h>
47 #ifdef GCP_KERN_HACK
48 #include <algorithm>
49 #endif // GCP_KERN_HACK
51 // for GetMirroredChar
52 #include <vcl/svapp.hxx>
54 #include <hash_map>
55 typedef std::hash_map<int,int> IntMap;
57 #define DROPPED_OUTGLYPH 0xFFFF
59 using namespace rtl;
61 // =======================================================================
63 // OS/2 specific physical font instance
64 class ImplOs2FontEntry : public ImplFontEntry
66 public:
67 ImplOs2FontEntry( ImplFontSelectData& );
68 ~ImplOs2FontEntry();
70 private:
71 // TODO: also add HFONT??? Watch out for issues with too many active fonts...
73 #ifdef GCP_KERN_HACK
74 public:
75 bool HasKernData() const;
76 void SetKernData( int, const KERNINGPAIRS* );
77 int GetKerning( sal_Unicode, sal_Unicode ) const;
78 private:
79 KERNINGPAIRS* mpKerningPairs;
80 int mnKerningPairs;
81 #endif // GCP_KERN_HACK
83 public:
84 int GetCachedGlyphWidth( int nCharCode ) const;
85 void CacheGlyphWidth( int nCharCode, int nCharWidth );
86 private:
87 IntMap maWidthMap;
90 // -----------------------------------------------------------------------
92 inline void ImplOs2FontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
94 maWidthMap[ nCharCode ] = nCharWidth;
97 inline int ImplOs2FontEntry::GetCachedGlyphWidth( int nCharCode ) const
99 IntMap::const_iterator it = maWidthMap.find( nCharCode );
100 if( it == maWidthMap.end() )
101 return -1;
102 return it->second;
105 // =======================================================================
107 class Os2Layout : public SalLayout
109 public:
110 Os2Layout( HDC, const ImplOs2FontData&, ImplOs2FontEntry& );
111 virtual void InitFont() const;
112 void SetFontScale( float f ) { mfFontScale = f; }
113 float GetFontScale() const { return mfFontScale; }
115 protected:
116 HPS mhPS; // OS2 device handle
117 FATTRS mhFont;
118 int mnBaseAdv; // x-offset relative to Layout origin
119 float mfFontScale; // allows metrics emulation of huge font sizes
121 const ImplOs2FontData& mrOs2FontData;
122 ImplOs2FontEntry& mrOs2FontEntry;
125 // =======================================================================
127 class Os2SalLayout : public Os2Layout
129 public:
130 Os2SalLayout( HPS, BYTE nCharSet, const ImplOs2FontData&, ImplOs2FontEntry& );
131 virtual ~Os2SalLayout();
133 virtual bool LayoutText( ImplLayoutArgs& );
134 virtual void AdjustLayout( ImplLayoutArgs& );
135 virtual void DrawText( SalGraphics& ) const;
137 virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
138 sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
140 virtual long FillDXArray( long* pDXArray ) const;
141 virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
142 virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
144 // for glyph+font+script fallback
145 virtual void MoveGlyph( int nStart, long nNewXPos );
146 virtual void DropGlyph( int nStart );
147 virtual void Simplify( bool bIsBase );
149 protected:
150 void Justify( long nNewWidth );
151 void ApplyDXArray( const ImplLayoutArgs& );
153 protected:
155 private:
156 int mnGlyphCount;
157 int mnCharCount;
158 sal_Unicode* mpOutGlyphs;
159 int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[]
160 int* mpGlyphOrigAdvs;
161 int* mpCharWidths; // map rel char pos to char width
162 int* mpChars2Glyphs; // map rel char pos to abs glyph pos
163 int* mpGlyphs2Chars; // map abs glyph pos to abs char pos
164 bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL
165 mutable long mnWidth;
166 bool mbDisableGlyphs;
168 int mnNotdefWidth;
169 BYTE mnCharSet;
173 // =======================================================================
175 Os2Layout::Os2Layout( HPS hPS, const ImplOs2FontData& rWFD, ImplOs2FontEntry& rWFE )
176 : mhPS( hPS ),
177 mnBaseAdv( 0 ),
178 mfFontScale( 1.0 ),
179 mrOs2FontData( rWFD ),
180 mrOs2FontEntry( rWFE )
182 BOOL fSuccess;
183 fSuccess = Ft2QueryLogicalFont( mhPS, LCID_BASE, NULL, &mhFont, sizeof(FATTRS));
186 // -----------------------------------------------------------------------
188 void Os2Layout::InitFont() const
190 // select fallback level 0 font
191 APIRET rc = Ft2CreateLogFont( mhPS, NULL, LCID_BASE, (PFATTRS)&mhFont);
194 // =======================================================================
196 Os2SalLayout::Os2SalLayout( HPS hPS, BYTE nCharSet,
197 const ImplOs2FontData& rOs2FontData, ImplOs2FontEntry& rOs2FontEntry )
198 : Os2Layout( hPS, rOs2FontData, rOs2FontEntry ),
199 mnGlyphCount( 0 ),
200 mnCharCount( 0 ),
201 mpOutGlyphs( NULL ),
202 mpGlyphAdvances( NULL ),
203 mpGlyphOrigAdvs( NULL ),
204 mpCharWidths( NULL ),
205 mpChars2Glyphs( NULL ),
206 mpGlyphs2Chars( NULL ),
207 mpGlyphRTLFlags( NULL ),
208 mnWidth( 0 ),
209 mnNotdefWidth( -1 ),
210 mnCharSet( nCharSet ),
211 mbDisableGlyphs( false )
213 mbDisableGlyphs = true;
216 // -----------------------------------------------------------------------
218 Os2SalLayout::~Os2SalLayout()
220 delete[] mpGlyphRTLFlags;
221 delete[] mpGlyphs2Chars;
222 delete[] mpChars2Glyphs;
223 if( mpCharWidths != mpGlyphAdvances )
224 delete[] mpCharWidths;
225 delete[] mpGlyphOrigAdvs;
226 delete[] mpGlyphAdvances;
227 delete[] mpOutGlyphs;
230 // -----------------------------------------------------------------------
232 bool Os2SalLayout::LayoutText( ImplLayoutArgs& rArgs )
234 // prepare layout
235 // TODO: fix case when recyclying old Os2SalLayout object
236 mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0);
237 mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
239 if( !mbDisableGlyphs )
241 // Win32 glyph APIs have serious problems with vertical layout
242 // => workaround is to use the unicode methods then
243 if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL )
244 mbDisableGlyphs = true;
245 else
246 // use cached value from font face
247 mbDisableGlyphs = mrOs2FontData.IsGlyphApiDisabled();
250 // TODO: use a cached value for bDisableAsianKern from upper layers
251 #if 0
252 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
254 TEXTMETRICA aTextMetricA;
255 if( ::GetTextMetricsA( mhDC, &aTextMetricA )
256 && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) )
257 rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN;
259 #endif
261 // layout text
262 int i, j;
264 mnGlyphCount = 0;
265 bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0;
267 // count the number of chars to process if no RTL run
268 rArgs.ResetPos();
269 bool bHasRTL = false;
270 while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
271 mnGlyphCount += j - i;
273 // if there are RTL runs we need room to remember individual BiDi flags
274 if( bHasRTL )
276 mpGlyphRTLFlags = new bool[ mnCharCount ];
277 for( i = 0; i < mnCharCount; ++i )
278 mpGlyphRTLFlags[i] = false;
281 // rewrite the logical string if needed to prepare for the API calls
282 const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos;
283 if( (mnGlyphCount != mnCharCount) || bVertical )
285 // we need to rewrite the pBidiStr when any of
286 // - BiDirectional layout
287 // - vertical layout
288 // - partial runs (e.g. with control chars or for glyph fallback)
289 // are involved
290 sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
291 pBidiStr = pRewrittenStr;
293 // note: glyph to char mapping is relative to first character
294 mpChars2Glyphs = new int[ mnCharCount ];
295 mpGlyphs2Chars = new int[ mnCharCount ];
296 for( i = 0; i < mnCharCount; ++i )
297 mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
299 mnGlyphCount = 0;
300 rArgs.ResetPos();
301 bool bIsRTL = false;
302 while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
306 // get the next leftmost character in this run
307 int nCharPos = bIsRTL ? --j : i++;
308 sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
310 // in the RTL case mirror the character and remember its RTL status
311 if( bIsRTL )
313 cChar = ::GetMirroredChar( cChar );
314 mpGlyphRTLFlags[ mnGlyphCount ] = true;
317 // for vertical writing use vertical alternatives
318 if( bVertical )
320 sal_Unicode cVert = ::GetVerticalChar( cChar );
321 if( cVert )
322 cChar = cVert;
325 // rewrite the original string
326 // update the mappings between original and rewritten string
327 pRewrittenStr[ mnGlyphCount ] = cChar;
328 mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
329 mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
330 ++mnGlyphCount;
331 } while( i < j );
335 mpOutGlyphs = new sal_Unicode[ mnGlyphCount ];
336 mpGlyphAdvances = new int[ mnGlyphCount ];
338 if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) )
339 mpGlyphOrigAdvs = new int[ mnGlyphCount ];
341 #ifndef GCP_KERN_HACK
342 DWORD nGcpOption = 0;
343 // enable kerning if requested
344 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
345 nGcpOption |= GCP_USEKERNING;
346 #endif // GCP_KERN_HACK
348 LONG lLcid = Ft2QueryCharSet( mhPS);
350 for( i = 0; i < mnGlyphCount; ++i )
351 mpOutGlyphs[i] = pBidiStr[ i ];
352 mnWidth = 0;
353 for( i = 0; i < mnGlyphCount; ++i )
355 const sal_Unicode* pCodes = &pBidiStr[i];
356 // check for surrogate pairs
357 if( (pCodes[0] & 0xFC00) == 0xDC00 )
358 continue;
359 bool bSurrogate = ((pCodes[0] & 0xFC00) == 0xD800);
361 // get the width of the corresponding code point
362 int nCharCode = pCodes[0];
363 if( bSurrogate )
364 nCharCode = 0x10000 + ((pCodes[0] & 0x03FF) << 10) + (pCodes[1] & 0x03FF);
365 int nGlyphWidth = mrOs2FontEntry.GetCachedGlyphWidth( nCharCode );
366 if( nGlyphWidth == -1 )
368 if (!Ft2QueryStringWidthW( mhPS, (LPWSTR)&pCodes[0], 1, (LONG*)&nGlyphWidth))
369 nGlyphWidth = 0;
370 mrOs2FontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
372 mpGlyphAdvances[ i ] = nGlyphWidth;
373 mnWidth += nGlyphWidth;
375 // remaining codes of surrogate pair get a zero width
376 if( bSurrogate )
377 mpGlyphAdvances[ i+1 ] = 0;
379 // check with the font face if glyph fallback is needed
380 if( mrOs2FontData.HasChar( nCharCode ) )
381 continue;
382 // Type1 charmaps are not complete (or buggy), use FT2 to check again
383 if (Ft2FontSupportsUnicodeChar( mhPS, lLcid, TRUE, nCharCode))
384 continue;
386 #if OSL_DEBUG_LEVEL>0
387 debug_printf("Os2SalLayout::LayoutText font does not support unicode char\n");
388 #endif
389 // request glyph fallback at this position in the string
390 bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
391 int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
392 rArgs.NeedFallback( nCharPos, bRTL );
393 if( bSurrogate )
394 rArgs.NeedFallback( nCharPos+1, bRTL );
396 if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
398 // when we already are layouting for glyph fallback
399 // then a new unresolved glyph is not interesting
400 mnNotdefWidth = 0;
401 mpOutGlyphs[i] = DROPPED_OUTGLYPH;
402 if( mbDisableGlyphs && bSurrogate )
403 mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
405 else
407 if( mnNotdefWidth < 0 )
409 // get the width of the NotDef glyph
410 LONG aExtent;
411 mnNotdefWidth = 0;
412 if (Ft2QueryStringWidthW( mhPS, (LPWSTR)&rArgs.mpStr[ nCharPos ], 1, &aExtent))
413 mnNotdefWidth = aExtent;
415 // use a better NotDef glyph
416 if( !mbDisableGlyphs )
417 mpOutGlyphs[i] = 0;
420 // replace the current glyph with the NotDef glyph
421 mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
422 mpGlyphAdvances[i] = mnNotdefWidth;
423 if( mpGlyphOrigAdvs )
424 mpGlyphOrigAdvs[i] = mnNotdefWidth;
427 #ifdef GCP_KERN_HACK
428 // apply kerning if the layout engine has not yet done it
429 if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) )
431 #else // GCP_KERN_HACK
432 // apply just asian kerning
433 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
435 if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) )
436 #endif // GCP_KERN_HACK
437 for( i = 0; i < mnGlyphCount; ++i )
438 mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
440 // #99658# also apply asian kerning on the substring border
441 int nLen = mnGlyphCount;
442 if( rArgs.mnMinCharPos + nLen < rArgs.mnLength )
443 ++nLen;
444 for( i = 1; i < nLen; ++i )
446 #ifdef GCP_KERN_HACK
447 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
449 int nKernAmount = mrOs2FontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
450 mpGlyphAdvances[ i-1 ] += nKernAmount;
451 mnWidth += nKernAmount;
453 else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
454 #endif // GCP_KERN_HACK
456 if( (0x3000 == (0xFF00 & pBidiStr[i-1]))
457 && (0x3000 == (0xFF00 & pBidiStr[i])) )
459 long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
460 long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical );
462 long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
463 if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
465 nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
466 mpGlyphAdvances[i-1] += nDelta;
467 mnWidth += nDelta;
473 // calculate virtual char widths
474 if( !mpGlyphs2Chars )
475 mpCharWidths = mpGlyphAdvances;
476 else
478 mpCharWidths = new int[ mnCharCount ];
479 for( i = 0; i < mnCharCount; ++i )
480 mpCharWidths[ i ] = 0;
481 for( i = 0; i < mnGlyphCount; ++i )
483 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
484 if( j >= 0 )
485 mpCharWidths[ j ] += mpGlyphAdvances[ i ];
489 // scale layout metrics if needed
490 if( mfFontScale != 1.0 )
492 mnWidth *= mfFontScale;
493 mnBaseAdv *= mfFontScale;
494 for( i = 0; i < mnCharCount; ++i )
495 mpCharWidths[ i ] *= mfFontScale;
496 if( mpGlyphAdvances != mpCharWidths )
497 for( i = 0; i < mnGlyphCount; ++i )
498 mpGlyphAdvances[ i ] *= mfFontScale;
499 if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
500 for( i = 0; i < mnGlyphCount; ++i )
501 mpGlyphOrigAdvs[ i ] *= mfFontScale;
504 return true;
507 // -----------------------------------------------------------------------
509 int Os2SalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int& nStart,
510 sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
512 // return zero if no more glyph found
513 if( nStart >= mnGlyphCount )
514 return 0;
516 // calculate glyph position relative to layout base
517 // TODO: avoid for nStart!=0 case by reusing rPos
518 long nXOffset = mnBaseAdv;
519 for( int i = 0; i < nStart; ++i )
520 nXOffset += mpGlyphAdvances[ i ];
522 // calculate absolute position in pixel units
523 Point aRelativePos( nXOffset, 0 );
524 rPos = GetDrawPosition( aRelativePos );
526 int nCount = 0;
527 while( nCount < nLen )
529 // update return values {nGlyphIndex,nCharPos,nGlyphAdvance}
530 long nGlyphIndex = mpOutGlyphs[ nStart ];
531 if( mbDisableGlyphs )
533 if( mnLayoutFlags & SAL_LAYOUT_VERTICAL )
535 sal_Unicode cChar = (sal_Unicode)(nGlyphIndex & GF_IDXMASK);
536 #ifdef GNG_VERT_HACK
537 if( mrOs2FontData.HasGSUBstitutions( mhPS )
538 && mrOs2FontData.IsGSUBstituted( cChar ) )
539 nGlyphIndex |= GF_ROTL | GF_GSUB;
540 else
541 #endif // GNG_VERT_HACK
543 nGlyphIndex |= GetVerticalFlags( cChar );
544 if( !(nGlyphIndex & GF_ROTMASK) )
545 nGlyphIndex |= GF_VERT;
548 nGlyphIndex |= GF_ISCHAR;
550 ++nCount;
551 *(pGlyphs++) = nGlyphIndex;
552 if( pGlyphAdvances )
553 *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
554 if( pCharIndexes )
556 int nCharPos;
557 if( !mpGlyphs2Chars )
558 nCharPos = nStart + mnMinCharPos;
559 else
560 nCharPos = mpGlyphs2Chars[nStart];
561 *(pCharIndexes++) = nCharPos;
564 // stop at last glyph
565 if( ++nStart >= mnGlyphCount )
566 break;
568 // stop when next x-position is unexpected
569 if( !pGlyphAdvances && mpGlyphOrigAdvs )
570 if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
571 break;
574 return nCount;
577 // -----------------------------------------------------------------------
579 void Os2SalLayout::DrawText( SalGraphics& rGraphics ) const
581 if( mnGlyphCount <= 0 )
582 return;
584 Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
585 POINTL aPt;
586 APIRET rc;
588 aPt.x = aPos.X();
589 aPt.y = static_cast<Os2SalGraphics&>(rGraphics).mnHeight - aPos.Y();
591 // ft2lib doesn't work with printer hps, so we fallback to codepage printing
592 // until cp1200 support will work.
593 if (static_cast<Os2SalGraphics&>(rGraphics).mbPrinter) {
594 // convert to codepage
595 ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() );
596 // gliph size is not recalculated, so it could be wrong!
597 rc = Ft2CharStringPosAtA( static_cast<Os2SalGraphics&>(rGraphics).mhPS,
598 &aPt, NULL, CHS_VECTOR, mnGlyphCount, (PSZ)str.GetBuffer(),
599 (LONG*)mpGlyphAdvances, 0);
600 } else {
601 // try unicode rendering to screen
602 rc = Ft2CharStringPosAtW( static_cast<Os2SalGraphics&>(rGraphics).mhPS,
603 &aPt, NULL, CHS_VECTOR, mnGlyphCount, (LPWSTR)mpOutGlyphs,
604 (LONG*)mpGlyphAdvances, 0);
605 if (rc == GPI_ERROR) {
606 // if *W fails, convert to codepage and use *A (fallback to GPI into ft2)
607 ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() );
608 #if OSL_DEBUG_LEVEL>10
609 debug_printf("Os2SalLayout::DrawText HPS %08x PosAtW failed '%s'!\n",static_cast<Os2SalGraphics&>(rGraphics).mhPS,str.GetBuffer());
610 #endif
611 // gliph size is not recalculated, so it could be wrong!
612 rc = Ft2CharStringPosAtA( static_cast<Os2SalGraphics&>(rGraphics).mhPS,
613 &aPt, NULL, CHS_VECTOR, mnGlyphCount, (PSZ)str.GetBuffer(),
614 (LONG*)mpGlyphAdvances, 0);
619 // -----------------------------------------------------------------------
621 long Os2SalLayout::FillDXArray( long* pDXArray ) const
623 if( !mnWidth )
625 long mnWidth = mnBaseAdv;
626 for( int i = 0; i < mnGlyphCount; ++i )
627 mnWidth += mpGlyphAdvances[ i ];
630 if( pDXArray != NULL )
632 for( int i = 0; i < mnCharCount; ++i )
633 pDXArray[ i ] = mpCharWidths[ i ];
636 return mnWidth;
639 // -----------------------------------------------------------------------
641 int Os2SalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
642 // NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
644 if( mnWidth )
645 if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
646 return STRING_LEN;
648 long nExtraWidth = mnBaseAdv * nFactor;
649 for( int n = 0; n < mnCharCount; ++n )
651 // skip unused characters
652 if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
653 continue;
654 // add char widths until max
655 nExtraWidth += mpCharWidths[ n ] * nFactor;
656 if( nExtraWidth >= nMaxWidth )
657 return (mnMinCharPos + n);
658 nExtraWidth += nCharExtra;
661 return STRING_LEN;
664 // -----------------------------------------------------------------------
666 void Os2SalLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
668 long nXPos = mnBaseAdv;
670 if( !mpGlyphs2Chars )
672 for( int i = 0; i < nMaxIdx; i += 2 )
674 pCaretXArray[ i ] = nXPos;
675 nXPos += mpGlyphAdvances[ i>>1 ];
676 pCaretXArray[ i+1 ] = nXPos;
679 else
681 int i;
682 for( i = 0; i < nMaxIdx; ++i )
683 pCaretXArray[ i ] = -1;
685 // assign glyph positions to character positions
686 for( i = 0; i < mnGlyphCount; ++i )
688 int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
689 long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
690 nCurrIdx *= 2;
691 if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
693 // normal positions for LTR case
694 pCaretXArray[ nCurrIdx ] = nXPos;
695 pCaretXArray[ nCurrIdx+1 ] = nXRight;
697 else
699 // reverse positions for RTL case
700 pCaretXArray[ nCurrIdx ] = nXRight;
701 pCaretXArray[ nCurrIdx+1 ] = nXPos;
703 nXPos += mpGlyphAdvances[ i ];
708 // -----------------------------------------------------------------------
710 void Os2SalLayout::Justify( long nNewWidth )
712 long nOldWidth = mnWidth;
713 mnWidth = nNewWidth;
715 if( mnGlyphCount <= 0 )
716 return;
718 if( nNewWidth == nOldWidth )
719 return;
721 // the rightmost glyph cannot be stretched
722 const int nRight = mnGlyphCount - 1;
723 nOldWidth -= mpGlyphAdvances[ nRight ];
724 nNewWidth -= mpGlyphAdvances[ nRight ];
726 // count stretchable glyphs
727 int nStretchable = 0, i;
728 for( i = 0; i < nRight; ++i )
729 if( mpGlyphAdvances[i] >= 0 )
730 ++nStretchable;
732 // stretch these glyphs
733 int nDiffWidth = nNewWidth - nOldWidth;
734 for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
736 if( mpGlyphAdvances[i] <= 0 )
737 continue;
738 int nDeltaWidth = nDiffWidth / nStretchable;
739 mpGlyphAdvances[i] += nDeltaWidth;
740 --nStretchable;
741 nDiffWidth -= nDeltaWidth;
745 // -----------------------------------------------------------------------
747 void Os2SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
749 SalLayout::AdjustLayout( rArgs );
751 // adjust positions if requested
752 if( rArgs.mpDXArray )
753 ApplyDXArray( rArgs );
754 else if( rArgs.mnLayoutWidth )
755 Justify( rArgs.mnLayoutWidth );
756 else
757 return;
759 // recalculate virtual char widths if they were changed
760 if( mpCharWidths != mpGlyphAdvances )
762 int i;
763 if( !mpGlyphs2Chars )
765 // standard LTR case
766 for( i = 0; i < mnGlyphCount; ++i )
767 mpCharWidths[ i ] = mpGlyphAdvances[ i ];
769 else
771 // BiDi or complex case
772 for( i = 0; i < mnCharCount; ++i )
773 mpCharWidths[ i ] = 0;
774 for( i = 0; i < mnGlyphCount; ++i )
776 int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
777 if( j >= 0 )
778 mpCharWidths[ j ] += mpGlyphAdvances[ i ];
784 // -----------------------------------------------------------------------
786 void Os2SalLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
788 // try to avoid disturbance of text flow for LSB rounding case;
789 const long* pDXArray = rArgs.mpDXArray;
791 int i = 0;
792 long nOldWidth = mnBaseAdv;
793 for(; i < mnCharCount; ++i )
795 int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
796 if( j >= 0 )
798 nOldWidth += mpGlyphAdvances[ j ];
799 int nDiff = nOldWidth - pDXArray[ i ];
801 // disabled because of #104768#
802 // works great for static text, but problems when typing
803 // if( nDiff>+1 || nDiff<-1 )
804 // only bother with changing anything when something moved
805 if( nDiff != 0 )
806 break;
809 if( i >= mnCharCount )
810 return;
812 if( !mpGlyphOrigAdvs )
814 mpGlyphOrigAdvs = new int[ mnGlyphCount ];
815 for( i = 0; i < mnGlyphCount; ++i )
816 mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
819 mnWidth = mnBaseAdv;
820 for( i = 0; i < mnCharCount; ++i )
822 int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
823 if( j >= 0 )
824 mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
825 mnWidth = pDXArray[i];
829 // -----------------------------------------------------------------------
831 void Os2SalLayout::MoveGlyph( int nStart, long nNewXPos )
833 if( nStart > mnGlyphCount )
834 return;
836 // calculate the current x-position of the requested glyph
837 // TODO: cache absolute positions
838 int nXPos = mnBaseAdv;
839 for( int i = 0; i < nStart; ++i )
840 nXPos += mpGlyphAdvances[i];
842 // calculate the difference to the current glyph position
843 int nDelta = nNewXPos - nXPos;
845 // adjust the width of the layout if it was already cached
846 if( mnWidth )
847 mnWidth += nDelta;
849 // depending on whether the requested glyph is leftmost in the layout
850 // adjust either the layout's or the requested glyph's relative position
851 if( nStart > 0 )
852 mpGlyphAdvances[ nStart-1 ] += nDelta;
853 else
854 mnBaseAdv += nDelta;
857 // -----------------------------------------------------------------------
859 void Os2SalLayout::DropGlyph( int nStart )
861 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
864 // -----------------------------------------------------------------------
866 void Os2SalLayout::Simplify( bool bIsBase )
868 // return early if no glyph has been dropped
869 int i = mnGlyphCount;
870 while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
871 if( i < 0 )
872 return;
874 // convert the layout to a sparse layout if it is not already
875 if( !mpGlyphs2Chars )
877 mpGlyphs2Chars = new int[ mnGlyphCount ];
878 mpCharWidths = new int[ mnCharCount ];
879 // assertion: mnGlyphCount == mnCharCount
880 for( int k = 0; k < mnGlyphCount; ++k )
882 mpGlyphs2Chars[ k ] = mnMinCharPos + k;
883 mpCharWidths[ k ] = mpGlyphAdvances[ k ];
887 // remove dropped glyphs that are rightmost in the layout
888 for( i = mnGlyphCount; --i >= 0; )
890 if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
891 break;
892 if( mnWidth )
893 mnWidth -= mpGlyphAdvances[ i ];
894 int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
895 if( nRelCharPos >= 0 )
896 mpCharWidths[ nRelCharPos ] = 0;
898 mnGlyphCount = i + 1;
900 // keep original glyph widths around
901 if( !mpGlyphOrigAdvs )
903 mpGlyphOrigAdvs = new int[ mnGlyphCount ];
904 for( int k = 0; k < mnGlyphCount; ++k )
905 mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
908 // remove dropped glyphs inside the layout
909 int nNewGC = 0;
910 for( i = 0; i < mnGlyphCount; ++i )
912 if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
914 // adjust relative position to last valid glyph
915 int nDroppedWidth = mpGlyphAdvances[ i ];
916 mpGlyphAdvances[ i ] = 0;
917 if( nNewGC > 0 )
918 mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
919 else
920 mnBaseAdv += nDroppedWidth;
922 // zero the virtual char width for the char that has a fallback
923 int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
924 if( nRelCharPos >= 0 )
925 mpCharWidths[ nRelCharPos ] = 0;
927 else
929 if( nNewGC != i )
931 // rearrange the glyph array to get rid of the dropped glyph
932 mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ];
933 mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
934 mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
935 mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ];
937 ++nNewGC;
941 mnGlyphCount = nNewGC;
942 if( mnGlyphCount <= 0 )
943 mnWidth = mnBaseAdv = 0;
946 // =======================================================================
948 SalLayout* Os2SalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
950 Os2SalLayout* pLayout = NULL;
951 DBG_ASSERT( mpOs2FontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL");
953 const ImplOs2FontData& rFontFace = *mpOs2FontData[ nFallbackLevel ];
954 ImplOs2FontEntry& rFontInstance = *mpOs2FontEntry[ nFallbackLevel ];
957 #ifdef GCP_KERN_HACK
958 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() )
960 // TODO: directly cache kerning info in the rFontInstance
961 // TODO: get rid of kerning methods+data in WinSalGraphics object
962 GetKernPairs( 0, NULL );
963 rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
965 #endif // GCP_KERN_HACK
967 //BYTE eCharSet = ANSI_CHARSET;
968 //if( mpLogFont )
969 // eCharSet = mpLogFont->lfCharSet;
970 pLayout = new Os2SalLayout( mhPS, 0, rFontFace, rFontInstance );
973 if( mfFontScale != 1.0 )
974 pLayout->SetFontScale( mfFontScale );
976 return pLayout;
979 // =======================================================================
981 ImplOs2FontEntry::ImplOs2FontEntry( ImplFontSelectData& rFSD )
982 : ImplFontEntry( rFSD ),
983 maWidthMap( 512 )
984 #ifdef GCP_KERN_HACK
985 ,mpKerningPairs( NULL )
986 ,mnKerningPairs( -1 )
987 #endif // GCP_KERN_HACK
991 // -----------------------------------------------------------------------
993 ImplOs2FontEntry::~ImplOs2FontEntry()
995 #ifdef GCP_KERN_HACK
996 delete[] mpKerningPairs;
997 #endif // GCP_KERN_HACK
1000 // -----------------------------------------------------------------------
1002 #ifdef GCP_KERN_HACK
1003 bool ImplOs2FontEntry::HasKernData() const
1005 return (mnKerningPairs >= 0);
1008 // -----------------------------------------------------------------------
1010 void ImplOs2FontEntry::SetKernData( int nPairCount, const KERNINGPAIRS* pPairData )
1012 mnKerningPairs = nPairCount;
1013 mpKerningPairs = new KERNINGPAIRS[ mnKerningPairs ];
1014 ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIRS) );
1017 // -----------------------------------------------------------------------
1019 int ImplOs2FontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
1021 int nKernAmount = 0;
1022 if( mpKerningPairs )
1024 const KERNINGPAIRS aRefPair = { cLeft, cRight, 0 };
1025 const KERNINGPAIRS* pFirstPair = mpKerningPairs;
1026 const KERNINGPAIRS* pEndPair = mpKerningPairs + mnKerningPairs;
1027 const KERNINGPAIRS* pPair = std::lower_bound( pFirstPair,
1028 pEndPair, aRefPair, ImplCmpKernData );
1029 if( (pPair != pEndPair)
1030 && (pPair->sFirstChar == aRefPair.sFirstChar)
1031 && (pPair->sSecondChar == aRefPair.sSecondChar) )
1032 nKernAmount = pPair->lKerningAmount;
1035 return nKernAmount;
1037 #endif // GCP_KERN_HACK
1039 // =======================================================================
1041 ImplFontData* ImplOs2FontData::Clone() const
1043 if( mpUnicodeMap )
1044 mpUnicodeMap->AddReference();
1045 ImplFontData* pClone = new ImplOs2FontData( *this );
1046 return pClone;
1049 // -----------------------------------------------------------------------
1051 ImplFontEntry* ImplOs2FontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
1053 //debug_printf("ImplOs2FontData::CreateFontInstance\n");
1054 ImplFontEntry* pEntry = new ImplOs2FontEntry( rFSD );
1055 return pEntry;
1058 // =======================================================================