Update ooo320-m1
[ooovba.git] / vcl / source / glyphs / gcach_layout.cxx
blob00b8a816c2392969f5665ca9681c057d0b470bbf
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 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
31 #define ENABLE_ICU_LAYOUT
32 #include <gcach_ftyp.hxx>
33 #include <vcl/sallayout.hxx>
34 #include <vcl/salgdi.hxx>
36 #include <vcl/svapp.hxx>
38 #include <sal/alloca.h>
40 #if OSL_DEBUG_LEVEL > 1
41 #include <cstdio>
42 #endif
43 #include <rtl/instance.hxx>
45 namespace { struct SimpleLayoutEngine : public rtl::Static< ServerFontLayoutEngine, SimpleLayoutEngine > {}; }
47 // =======================================================================
48 // layout implementation for ServerFont
49 // =======================================================================
51 ServerFontLayout::ServerFontLayout( ServerFont& rFont )
52 : mrServerFont( rFont )
55 void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
57 rSalGraphics.DrawServerFontLayout( *this );
60 // -----------------------------------------------------------------------
62 bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
64 ServerFontLayoutEngine* pLE = NULL;
65 if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) )
66 pLE = mrServerFont.GetLayoutEngine();
67 if( !pLE )
68 pLE = &SimpleLayoutEngine::get();
70 bool bRet = (*pLE)( *this, rArgs );
71 return bRet;
74 // -----------------------------------------------------------------------
76 void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
78 GenericSalLayout::AdjustLayout( rArgs );
80 // apply asian kerning if the glyphs are not already formatted
81 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN)
82 && !(rArgs.mnFlags & SAL_LAYOUT_VERTICAL) )
83 if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
84 ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
86 // insert kashidas where requested by the formatting array
87 if( (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) && rArgs.mpDXArray )
89 int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
90 if( nKashidaIndex != 0 )
92 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
93 KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
94 // TODO: kashida-GSUB/GPOS
99 // =======================================================================
101 bool ServerFontLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
103 FreetypeServerFont& rFont = static_cast<FreetypeServerFont&>(rLayout.GetServerFont());
105 Point aNewPos( 0, 0 );
106 int nOldGlyphId = -1;
107 int nGlyphWidth = 0;
108 GlyphItem aPrevItem;
109 bool bRightToLeft;
110 for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
112 sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
113 if( (cChar >= 0xD800) && (cChar <= 0xDFFF) )
115 if( cChar >= 0xDC00 ) // this part of a surrogate pair was already processed
116 continue;
117 cChar = 0x10000 + ((cChar - 0xD800) << 10)
118 + (rArgs.mpStr[ nCharPos+1 ] - 0xDC00);
121 if( bRightToLeft )
122 cChar = GetMirroredChar( cChar );
123 int nGlyphIndex = rFont.GetGlyphIndex( cChar );
124 // when glyph fallback is needed update LayoutArgs
125 if( !nGlyphIndex ) {
126 rArgs.NeedFallback( nCharPos, bRightToLeft );
127 if( cChar >= 0x10000 ) // handle surrogate pairs
128 rArgs.NeedFallback( nCharPos+1, bRightToLeft );
131 // apply pair kerning to prev glyph if requested
132 if( SAL_LAYOUT_KERNING_PAIRS & rArgs.mnFlags )
134 int nKernValue = rFont.GetGlyphKernValue( nOldGlyphId, nGlyphIndex );
135 nGlyphWidth += nKernValue;
136 aPrevItem.mnNewWidth = nGlyphWidth;
139 // finish previous glyph
140 if( nOldGlyphId >= 0 )
141 rLayout.AppendGlyph( aPrevItem );
142 aNewPos.X() += nGlyphWidth;
144 // prepare GlyphItem for appending it in next round
145 nOldGlyphId = nGlyphIndex;
146 const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
147 nGlyphWidth = rGM.GetCharWidth();
148 int nGlyphFlags = bRightToLeft ? GlyphItem::IS_RTL_GLYPH : 0;
149 aPrevItem = GlyphItem( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
152 // append last glyph item if any
153 if( nOldGlyphId >= 0 )
154 rLayout.AppendGlyph( aPrevItem );
156 return true;
159 // =======================================================================
160 // bridge to ICU LayoutEngine
161 // =======================================================================
163 #ifdef ENABLE_ICU_LAYOUT
165 #define bool_t signed char
167 // disable warnings in icu layout headers
168 #if defined __SUNPRO_CC
169 #pragma disable_warn
170 #endif
172 #include <layout/LayoutEngine.h>
173 #include <layout/LEFontInstance.h>
174 #include <layout/LEScripts.h>
176 // enable warnings again
177 #if defined __SUNPRO_CC
178 #pragma enable_warn
179 #endif
181 #include <unicode/uscript.h>
182 #include <unicode/ubidi.h>
184 using namespace U_ICU_NAMESPACE;
186 static const LEGlyphID ICU_DELETED_GLYPH = 0xFFFF;
187 static const LEGlyphID ICU_MARKED_GLYPH = 0xFFFE;
189 // -----------------------------------------------------------------------
191 class IcuFontFromServerFont
192 : public LEFontInstance
194 private:
195 FreetypeServerFont& mrServerFont;
197 public:
198 IcuFontFromServerFont( FreetypeServerFont& rFont )
199 : mrServerFont( rFont )
202 virtual const void* getFontTable(LETag tableTag) const;
203 virtual le_int32 getUnitsPerEM() const;
204 virtual float getXPixelsPerEm() const;
205 virtual float getYPixelsPerEm() const;
206 virtual float getScaleFactorX() const;
207 virtual float getScaleFactorY() const;
209 using LEFontInstance::mapCharToGlyph;
210 virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch ) const;
212 virtual le_int32 getAscent() const;
213 virtual le_int32 getDescent() const;
214 virtual le_int32 getLeading() const;
216 virtual void getGlyphAdvance( LEGlyphID glyph, LEPoint &advance ) const;
217 virtual le_bool getGlyphPoint( LEGlyphID glyph, le_int32 pointNumber, LEPoint& point ) const;
220 // -----------------------------------------------------------------------
222 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag ) const
224 char pTagName[5];
225 pTagName[0] = (char)(nICUTableTag >> 24);
226 pTagName[1] = (char)(nICUTableTag >> 16);
227 pTagName[2] = (char)(nICUTableTag >> 8);
228 pTagName[3] = (char)(nICUTableTag);
229 pTagName[4] = 0;
231 ULONG nLength;
232 const unsigned char* pBuffer = mrServerFont.GetTable( pTagName, &nLength );
233 #ifdef VERBOSE_DEBUG
234 fprintf(stderr,"IcuGetTable(\"%s\") => %p\n", pTagName, pBuffer);
235 int mnHeight = mrServerFont.GetFontSelData().mnHeight;
236 const char* pName = mrServerFont.GetFontFileName()->getStr();
237 fprintf(stderr,"font( h=%d, \"%s\" )\n", mnHeight, pName );
238 #endif
239 return (const void*)pBuffer;
242 // -----------------------------------------------------------------------
244 le_int32 IcuFontFromServerFont::getUnitsPerEM() const
246 return mrServerFont.GetEmUnits();
249 // -----------------------------------------------------------------------
251 float IcuFontFromServerFont::getXPixelsPerEm() const
253 const ImplFontSelectData& r = mrServerFont.GetFontSelData();
254 float fX = r.mnWidth ? r.mnWidth : r.mnHeight;
255 return fX;
258 // -----------------------------------------------------------------------
260 float IcuFontFromServerFont::getYPixelsPerEm() const
262 float fY = mrServerFont.GetFontSelData().mnHeight;
263 return fY;
266 // -----------------------------------------------------------------------
268 float IcuFontFromServerFont::getScaleFactorX() const
270 return 1.0;
273 // -----------------------------------------------------------------------
275 float IcuFontFromServerFont::getScaleFactorY() const
277 return 1.0;
280 // -----------------------------------------------------------------------
282 LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch ) const
284 LEGlyphID nGlyphIndex = mrServerFont.GetRawGlyphIndex( ch );
285 return nGlyphIndex;
288 // -----------------------------------------------------------------------
290 le_int32 IcuFontFromServerFont::getAscent() const
292 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
293 le_int32 nAscent = (+rMetrics.ascender + 32) >> 6;
294 return nAscent;
297 // -----------------------------------------------------------------------
299 le_int32 IcuFontFromServerFont::getDescent() const
301 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
302 le_int32 nDescent = (-rMetrics.descender + 32) >> 6;
303 return nDescent;
306 // -----------------------------------------------------------------------
308 le_int32 IcuFontFromServerFont::getLeading() const
310 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
311 le_int32 nLeading = ((rMetrics.height - rMetrics.ascender + rMetrics.descender) + 32) >> 6;
312 return nLeading;
315 // -----------------------------------------------------------------------
317 void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex,
318 LEPoint &advance ) const
320 if( (nGlyphIndex == ICU_MARKED_GLYPH)
321 || (nGlyphIndex == ICU_DELETED_GLYPH) )
323 // deleted glyph or mark glyph has not advance
324 advance.fX = 0;
326 else
328 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nGlyphIndex );
329 advance.fX = rGM.GetCharWidth();
332 advance.fY = 0;
335 // -----------------------------------------------------------------------
337 le_bool IcuFontFromServerFont::getGlyphPoint( LEGlyphID,
338 le_int32
339 #if OSL_DEBUG_LEVEL > 1
340 pointNumber
341 #endif
343 LEPoint& ) const
345 //TODO: replace dummy implementation
346 #if OSL_DEBUG_LEVEL > 1
347 fprintf(stderr,"getGlyphPoint(%d)\n", pointNumber );
348 #endif
349 return false;
352 // =======================================================================
354 class IcuLayoutEngine : public ServerFontLayoutEngine
356 private:
357 IcuFontFromServerFont maIcuFont;
359 le_int32 meScriptCode;
360 LayoutEngine* mpIcuLE;
362 public:
363 IcuLayoutEngine( FreetypeServerFont& );
364 virtual ~IcuLayoutEngine();
366 virtual bool operator()( ServerFontLayout&, ImplLayoutArgs& );
369 // -----------------------------------------------------------------------
371 IcuLayoutEngine::IcuLayoutEngine( FreetypeServerFont& rServerFont )
372 : maIcuFont( rServerFont ),
373 meScriptCode( USCRIPT_INVALID_CODE ),
374 mpIcuLE( NULL )
377 // -----------------------------------------------------------------------
379 IcuLayoutEngine::~IcuLayoutEngine()
381 if( mpIcuLE )
382 delete mpIcuLE;
385 // -----------------------------------------------------------------------
387 static bool lcl_CharIsJoiner(sal_Unicode cChar)
389 return ((cChar == 0x200C) || (cChar == 0x200D));
392 bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
394 LEUnicode* pIcuChars;
395 if( sizeof(LEUnicode) == sizeof(*rArgs.mpStr) )
396 pIcuChars = (LEUnicode*)rArgs.mpStr;
397 else
399 // this conversion will only be needed when either
400 // ICU's or OOo's unicodes stop being unsigned shorts
401 // TODO: watch out for surrogates!
402 pIcuChars = (LEUnicode*)alloca( rArgs.mnLength * sizeof(LEUnicode) );
403 for( xub_StrLen ic = 0; ic < rArgs.mnLength; ++ic )
404 pIcuChars[ic] = static_cast<LEUnicode>( rArgs.mpStr[ic] );
407 // allocate temporary arrays, note: round to even
408 int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos ) | 15) + 1;
410 struct IcuPosition{ float fX, fY; };
411 const int nAllocSize = sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(IcuPosition);
412 LEGlyphID* pIcuGlyphs = (LEGlyphID*)alloca( (nGlyphCapacity * nAllocSize) + sizeof(IcuPosition) );
413 le_int32* pCharIndices = (le_int32*)((char*)pIcuGlyphs + nGlyphCapacity * sizeof(LEGlyphID) );
414 IcuPosition* pGlyphPositions = (IcuPosition*)((char*)pCharIndices + nGlyphCapacity * sizeof(le_int32) );
416 FreetypeServerFont& rFont = reinterpret_cast<FreetypeServerFont&>(rLayout.GetServerFont());
418 UErrorCode rcI18n = U_ZERO_ERROR;
419 LEErrorCode rcIcu = LE_NO_ERROR;
420 Point aNewPos( 0, 0 );
421 for( int nGlyphCount = 0;; )
423 int nMinRunPos, nEndRunPos;
424 bool bRightToLeft;
425 if( !rArgs.GetNextRun( &nMinRunPos, &nEndRunPos, &bRightToLeft ) )
426 break;
428 // find matching script
429 // TODO: split up bidi run into script runs
430 le_int32 eScriptCode = -1;
431 for( int i = nMinRunPos; i < nEndRunPos; ++i )
433 eScriptCode = uscript_getScript( pIcuChars[i], &rcI18n );
434 if( (eScriptCode > 0) && (eScriptCode != latnScriptCode) )
435 break;
437 if( eScriptCode < 0 ) // TODO: handle errors better
438 eScriptCode = latnScriptCode;
440 // get layout engine matching to this script
441 // no engine change necessary if script is latin
442 if( !mpIcuLE || ((eScriptCode != meScriptCode) && (eScriptCode > USCRIPT_INHERITED)) )
444 // TODO: cache multiple layout engines when multiple scripts are used
445 delete mpIcuLE;
446 meScriptCode = eScriptCode;
447 le_int32 eLangCode = 0; // TODO: get better value
448 mpIcuLE = LayoutEngine::layoutEngineFactory( &maIcuFont, eScriptCode, eLangCode, rcIcu );
449 if( LE_FAILURE(rcIcu) )
451 delete mpIcuLE;
452 mpIcuLE = NULL;
456 // fall back to default layout if needed
457 if( !mpIcuLE )
458 break;
460 // run ICU layout engine
461 // TODO: get enough context, remove extra glyps below
462 int nRawRunGlyphCount = mpIcuLE->layoutChars( pIcuChars,
463 nMinRunPos, nEndRunPos - nMinRunPos, rArgs.mnLength,
464 bRightToLeft, aNewPos.X(), aNewPos.Y(), rcIcu );
465 if( LE_FAILURE(rcIcu) )
466 return false;
468 // import layout info from icu
469 mpIcuLE->getGlyphs( pIcuGlyphs, rcIcu );
470 mpIcuLE->getCharIndices( pCharIndices, rcIcu );
471 mpIcuLE->getGlyphPositions( &pGlyphPositions->fX, rcIcu );
472 mpIcuLE->reset(); // TODO: get rid of this, PROBLEM: crash at exit when removed
473 if( LE_FAILURE(rcIcu) )
474 return false;
476 // layout bidi/script runs and export them to a ServerFontLayout
477 // convert results to GlyphItems
478 int nLastCharPos = -1;
479 int nClusterMinPos = -1;
480 int nClusterMaxPos = -1;
481 bool bClusterStart = true;
482 int nFilteredRunGlyphCount = 0;
483 const IcuPosition* pPos = pGlyphPositions;
484 for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
486 LEGlyphID nGlyphIndex = pIcuGlyphs[i];
487 // ignore glyphs which were marked or deleted by ICU
488 if( (nGlyphIndex == ICU_MARKED_GLYPH)
489 || (nGlyphIndex == ICU_DELETED_GLYPH) )
490 continue;
492 // adjust the relative char pos
493 int nCharPos = pCharIndices[i];
494 if( nCharPos >= 0 ) {
495 nCharPos += nMinRunPos;
496 // ICU seems to return bad pCharIndices
497 // for some combinations of ICU+font+text
498 // => better give up now than crash later
499 if( nCharPos >= nEndRunPos )
500 continue;
503 // if needed request glyph fallback by updating LayoutArgs
504 if( !nGlyphIndex )
506 if( nCharPos >= 0 )
508 rArgs.NeedFallback( nCharPos, bRightToLeft );
509 if ( (nCharPos > 0) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos-1]) )
510 rArgs.NeedFallback( nCharPos-1, bRightToLeft );
511 else if ( (nCharPos + 1 < nEndRunPos) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos+1]) )
512 rArgs.NeedFallback( nCharPos+1, bRightToLeft );
515 if( SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags )
516 continue;
520 // apply vertical flags, etc.
521 bool bDiacritic = false;
522 if( nCharPos >= 0 )
524 sal_UCS4 aChar = rArgs.mpStr[ nCharPos ];
525 #if 0 // TODO: enable if some unicodes>0xFFFF should need glyph flags!=0
526 if( (aChar >= 0xD800) && (aChar <= 0xDFFF) )
528 if( cChar >= 0xDC00 ) // this part of a surrogate pair was already processed
529 continue;
530 // calculate unicode scalar value of surrogate pair
531 aChar = 0x10000 + ((aChar - 0xD800) << 10);
532 sal_UCS4 aLow = rArgs.mpStr[ nCharPos+1 ];
533 aChar += aLow & 0x03FF;
535 #endif
536 nGlyphIndex = rFont.FixupGlyphIndex( nGlyphIndex, aChar );
538 // #i99367# HACK: try to detect all diacritics
539 if( aChar>=0x0300 && aChar<0x2100 )
540 bDiacritic = IsDiacritic( aChar );
543 // get glyph position and its metrics
544 aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
545 const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
546 int nGlyphWidth = rGM.GetCharWidth();
547 if( nGlyphWidth <= 0 )
548 bDiacritic |= true;
549 // #i99367# force all diacritics to zero width
550 // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
551 else if( bDiacritic )
552 nGlyphWidth = 0;
554 // heuristic to detect glyph clusters
555 bool bInCluster = true;
556 if( nLastCharPos == -1 )
558 nClusterMinPos = nClusterMaxPos = nCharPos;
559 bInCluster = false;
561 else if( !bRightToLeft )
563 // left-to-right case
564 if( nClusterMinPos > nCharPos )
565 nClusterMinPos = nCharPos; // extend cluster
566 else if( nCharPos <= nClusterMaxPos )
567 /*NOTHING*/; // inside cluster
568 else if( bDiacritic )
569 nClusterMaxPos = nCharPos; // add diacritic to cluster
570 else {
571 nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
572 bInCluster = false;
575 else
577 // right-to-left case
578 if( nClusterMaxPos < nCharPos )
579 nClusterMaxPos = nCharPos; // extend cluster
580 else if( nCharPos >= nClusterMinPos )
581 /*NOTHING*/; // inside cluster
582 else if( bDiacritic )
584 nClusterMinPos = nCharPos; // ICU often has [diacritic* baseglyph*]
585 if( bClusterStart ) {
586 nClusterMaxPos = nCharPos;
587 bInCluster = false;
590 else
592 nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
593 bInCluster = !bClusterStart;
597 long nGlyphFlags = 0;
598 if( bInCluster )
599 nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
600 if( bRightToLeft )
601 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
602 if( bDiacritic )
603 nGlyphFlags |= GlyphItem::IS_DIACRITIC;
605 // add resulting glyph item to layout
606 const GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
607 rLayout.AppendGlyph( aGI );
608 ++nFilteredRunGlyphCount;
609 nLastCharPos = nCharPos;
610 bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
612 aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
613 nGlyphCount += nFilteredRunGlyphCount;
616 // sort glyphs in visual order
617 // and then in logical order (e.g. diacritics after cluster start)
618 rLayout.SortGlyphItems();
620 // determine need for kashida justification
621 if( (rArgs.mpDXArray || rArgs.mnLayoutWidth)
622 && ((meScriptCode == arabScriptCode) || (meScriptCode == syrcScriptCode)) )
623 rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
625 return true;
628 #endif // ENABLE_ICU_LAYOUT
630 // =======================================================================
632 ServerFontLayoutEngine* FreetypeServerFont::GetLayoutEngine()
634 // find best layout engine for font, platform, script and language
635 #ifdef ENABLE_ICU_LAYOUT
636 if( !mpLayoutEngine && FT_IS_SFNT( maFaceFT ) )
637 mpLayoutEngine = new IcuLayoutEngine( *this );
638 #endif // ENABLE_ICU_LAYOUT
640 return mpLayoutEngine;
643 // =======================================================================