Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / vcl / generic / glyphs / gcach_layout.cxx
bloba8ddc4965b608b15b3f9595790ec0996d4a3fa00
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <gcach_ftyp.hxx>
21 #include <sallayout.hxx>
22 #include <salgdi.hxx>
24 #include <vcl/svapp.hxx>
26 #include <sal/alloca.h>
27 #include <rtl/instance.hxx>
29 #include <layout/LayoutEngine.h>
30 #include <layout/LEFontInstance.h>
31 #include <layout/LEScripts.h>
33 #include <unicode/uscript.h>
34 #include <unicode/ubidi.h>
36 // =======================================================================
37 // layout implementation for ServerFont
38 // =======================================================================
40 ServerFontLayout::ServerFontLayout( ServerFont& rFont )
41 : mrServerFont( rFont )
44 void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
46 rSalGraphics.DrawServerFontLayout( *this );
49 // -----------------------------------------------------------------------
51 bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
53 ServerFontLayoutEngine* pLE = mrServerFont.GetLayoutEngine();
54 assert(pLE);
55 bool bRet = pLE ? pLE->layout(*this, rArgs) : false;
56 return bRet;
59 // -----------------------------------------------------------------------
61 void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
63 GenericSalLayout::AdjustLayout( rArgs );
65 // apply asian kerning if the glyphs are not already formatted
66 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN)
67 && !(rArgs.mnFlags & SAL_LAYOUT_VERTICAL) )
68 if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
69 ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
71 // insert kashidas where requested by the formatting array
72 if( (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) && rArgs.mpDXArray )
74 int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
75 if( nKashidaIndex != 0 )
77 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
78 KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
79 // TODO: kashida-GSUB/GPOS
84 // =======================================================================
85 // bridge to ICU LayoutEngine
86 // =======================================================================
88 using namespace U_ICU_NAMESPACE;
90 static const LEGlyphID ICU_DELETED_GLYPH = 0xFFFF;
91 static const LEGlyphID ICU_MARKED_GLYPH = 0xFFFE;
93 // -----------------------------------------------------------------------
95 class IcuFontFromServerFont
96 : public LEFontInstance
98 private:
99 ServerFont& mrServerFont;
101 public:
102 IcuFontFromServerFont( ServerFont& rFont )
103 : mrServerFont( rFont )
106 virtual const void* getFontTable(LETag tableTag) const;
107 virtual le_int32 getUnitsPerEM() const;
108 virtual float getXPixelsPerEm() const;
109 virtual float getYPixelsPerEm() const;
110 virtual float getScaleFactorX() const;
111 virtual float getScaleFactorY() const;
113 using LEFontInstance::mapCharToGlyph;
114 virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch ) const;
115 virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch, const LECharMapper *mapper, le_bool filterZeroWidth ) const;
117 virtual le_int32 getAscent() const;
118 virtual le_int32 getDescent() const;
119 virtual le_int32 getLeading() const;
121 virtual void getGlyphAdvance( LEGlyphID glyph, LEPoint &advance ) const;
122 virtual le_bool getGlyphPoint( LEGlyphID glyph, le_int32 pointNumber, LEPoint& point ) const;
125 // -----------------------------------------------------------------------
127 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag ) const
129 char pTagName[5];
130 pTagName[0] = (char)(nICUTableTag >> 24);
131 pTagName[1] = (char)(nICUTableTag >> 16);
132 pTagName[2] = (char)(nICUTableTag >> 8);
133 pTagName[3] = (char)(nICUTableTag);
134 pTagName[4] = 0;
136 sal_uLong nLength;
137 const unsigned char* pBuffer = mrServerFont.GetTable( pTagName, &nLength );
138 SAL_INFO("vcl", "IcuGetTable(\"" << pTagName << "\") => " << pBuffer);
139 SAL_INFO(
140 "vcl",
141 "font( h=" << mrServerFont.GetFontSelData().mnHeight << ", \""
142 << mrServerFont.GetFontFileName()->getStr() << "\" )");
143 return pBuffer;
146 // -----------------------------------------------------------------------
148 le_int32 IcuFontFromServerFont::getUnitsPerEM() const
150 return mrServerFont.GetEmUnits();
153 // -----------------------------------------------------------------------
155 float IcuFontFromServerFont::getXPixelsPerEm() const
157 const FontSelectPattern& r = mrServerFont.GetFontSelData();
158 float fX = r.mnWidth ? r.mnWidth : r.mnHeight;
159 return fX;
162 // -----------------------------------------------------------------------
164 float IcuFontFromServerFont::getYPixelsPerEm() const
166 float fY = mrServerFont.GetFontSelData().mnHeight;
167 return fY;
170 // -----------------------------------------------------------------------
172 float IcuFontFromServerFont::getScaleFactorX() const
174 return 1.0;
177 // -----------------------------------------------------------------------
179 float IcuFontFromServerFont::getScaleFactorY() const
181 return 1.0;
184 // -----------------------------------------------------------------------
186 LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch ) const
188 LEGlyphID nGlyphIndex = mrServerFont.GetRawGlyphIndex( ch );
189 return nGlyphIndex;
192 LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch, const LECharMapper *mapper, le_bool /*filterZeroWidth*/ ) const
195 fdo#31821, icu has...
196 >│93 if (filterZeroWidth && (mappedChar == 0x200C || mappedChar == 0x200D)) { │
197 │94 return canDisplay(mappedChar) ? 0x0001 : 0xFFFF; │
198 │95 }
199 so only the Indic layouts allow the joiners to get mapped to glyphs
201 return LEFontInstance::mapCharToGlyph( ch, mapper, false );
204 // -----------------------------------------------------------------------
206 le_int32 IcuFontFromServerFont::getAscent() const
208 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
209 le_int32 nAscent = (+rMetrics.ascender + 32) >> 6;
210 return nAscent;
213 // -----------------------------------------------------------------------
215 le_int32 IcuFontFromServerFont::getDescent() const
217 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
218 le_int32 nDescent = (-rMetrics.descender + 32) >> 6;
219 return nDescent;
222 // -----------------------------------------------------------------------
224 le_int32 IcuFontFromServerFont::getLeading() const
226 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
227 le_int32 nLeading = ((rMetrics.height - rMetrics.ascender + rMetrics.descender) + 32) >> 6;
228 return nLeading;
231 // -----------------------------------------------------------------------
233 void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex,
234 LEPoint &advance ) const
236 if( (nGlyphIndex == ICU_MARKED_GLYPH)
237 || (nGlyphIndex == ICU_DELETED_GLYPH) )
239 // deleted glyph or mark glyph has not advance
240 advance.fX = 0;
242 else
244 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nGlyphIndex );
245 advance.fX = rGM.GetCharWidth();
248 advance.fY = 0;
251 // -----------------------------------------------------------------------
253 le_bool IcuFontFromServerFont::getGlyphPoint( LEGlyphID,
254 le_int32 pointNumber, LEPoint& ) const
256 //TODO: replace dummy implementation
257 SAL_INFO("vcl", "getGlyphPoint(" << pointNumber << ")");
258 return false;
261 // =======================================================================
263 class IcuLayoutEngine : public ServerFontLayoutEngine
265 private:
266 IcuFontFromServerFont maIcuFont;
268 le_int32 meScriptCode;
269 le_int32 mnLayoutFlags;
270 LayoutEngine* mpIcuLE;
272 public:
273 IcuLayoutEngine( ServerFont& );
274 virtual ~IcuLayoutEngine();
276 virtual bool layout( ServerFontLayout&, ImplLayoutArgs& );
279 // -----------------------------------------------------------------------
281 IcuLayoutEngine::IcuLayoutEngine( ServerFont& rServerFont )
282 : maIcuFont( rServerFont ),
283 meScriptCode( USCRIPT_INVALID_CODE ),
284 mnLayoutFlags( 0 ),
285 mpIcuLE( NULL )
288 // -----------------------------------------------------------------------
290 IcuLayoutEngine::~IcuLayoutEngine()
292 delete mpIcuLE;
295 // -----------------------------------------------------------------------
297 static bool lcl_CharIsJoiner(sal_Unicode cChar)
299 return ((cChar == 0x200C) || (cChar == 0x200D));
302 //See https://bugs.freedesktop.org/show_bug.cgi?id=31016
303 #define ARABIC_BANDAID
305 bool IcuLayoutEngine::layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
307 le_int32 nLayoutFlags = 0;
308 #if (U_ICU_VERSION_MAJOR_NUM > 4)
309 if (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS)
310 nLayoutFlags |= LayoutEngine::kTypoFlagKern;
311 if (rArgs.mnFlags & SAL_LAYOUT_ENABLE_LIGATURES)
312 nLayoutFlags |= LayoutEngine::kTypoFlagLiga;
313 #else
314 if (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS)
315 nLayoutFlags |= 0x01;
316 if (rArgs.mnFlags & SAL_LAYOUT_ENABLE_LIGATURES)
317 nLayoutFlags |= 0x10;
318 #endif
320 LEUnicode* pIcuChars;
321 if( sizeof(LEUnicode) == sizeof(*rArgs.mpStr) )
322 pIcuChars = (LEUnicode*)rArgs.mpStr;
323 else
325 // this conversion will only be needed when either
326 // ICU's or OOo's unicodes stop being unsigned shorts
327 // TODO: watch out for surrogates!
328 pIcuChars = (LEUnicode*)alloca( rArgs.mnLength * sizeof(LEUnicode) );
329 for( xub_StrLen ic = 0; ic < rArgs.mnLength; ++ic )
330 pIcuChars[ic] = static_cast<LEUnicode>( rArgs.mpStr[ic] );
333 // allocate temporary arrays, note: round to even
334 int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos ) | 15) + 1;
336 rLayout.Reserve(nGlyphCapacity);
338 struct IcuPosition{ float fX, fY; };
339 const int nAllocSize = sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(IcuPosition);
340 LEGlyphID* pIcuGlyphs = (LEGlyphID*)alloca( (nGlyphCapacity * nAllocSize) + sizeof(IcuPosition) );
341 le_int32* pCharIndices = (le_int32*)((char*)pIcuGlyphs + nGlyphCapacity * sizeof(LEGlyphID) );
342 IcuPosition* pGlyphPositions = (IcuPosition*)((char*)pCharIndices + nGlyphCapacity * sizeof(le_int32) );
344 ServerFont& rFont = rLayout.GetServerFont();
346 UErrorCode rcI18n = U_ZERO_ERROR;
347 LEErrorCode rcIcu = LE_NO_ERROR;
348 Point aNewPos( 0, 0 );
349 for( int nGlyphCount = 0;; )
351 int nMinRunPos, nEndRunPos;
352 bool bRightToLeft;
353 if( !rArgs.GetNextRun( &nMinRunPos, &nEndRunPos, &bRightToLeft ) )
354 break;
356 // find matching script
357 // TODO: split up bidi run into script runs
358 le_int32 eScriptCode = -1;
359 for( int i = nMinRunPos; i < nEndRunPos; ++i )
361 le_int32 eNextScriptCode = uscript_getScript( pIcuChars[i], &rcI18n );
362 if( (eNextScriptCode > USCRIPT_INHERITED) )
364 eScriptCode = eNextScriptCode;
365 if (eNextScriptCode != latnScriptCode)
366 break;
369 if( eScriptCode < 0 ) // TODO: handle errors better
370 eScriptCode = latnScriptCode;
372 // get layout engine matching to this script and ligature/kerning combination
373 // no engine change necessary if script is latin
374 if( !mpIcuLE || ((eScriptCode != meScriptCode) && (eScriptCode > USCRIPT_INHERITED)) || (mnLayoutFlags != nLayoutFlags) )
376 // TODO: cache multiple layout engines when multiple scripts are used
377 delete mpIcuLE;
378 meScriptCode = eScriptCode;
379 mnLayoutFlags = nLayoutFlags;
380 le_int32 eLangCode = 0; // TODO: get better value
381 mpIcuLE = LayoutEngine::layoutEngineFactory( &maIcuFont, eScriptCode, eLangCode, nLayoutFlags, rcIcu );
382 if( LE_FAILURE(rcIcu) )
384 delete mpIcuLE;
385 mpIcuLE = NULL;
389 // fall back to default layout if needed
390 if( !mpIcuLE )
391 break;
393 // run ICU layout engine
394 // TODO: get enough context, remove extra glyps below
395 int nRawRunGlyphCount = mpIcuLE->layoutChars( pIcuChars,
396 nMinRunPos, nEndRunPos - nMinRunPos, rArgs.mnLength,
397 bRightToLeft, aNewPos.X(), aNewPos.Y(), rcIcu );
398 if( LE_FAILURE(rcIcu) )
399 return false;
401 // import layout info from icu
402 mpIcuLE->getGlyphs( pIcuGlyphs, rcIcu );
403 mpIcuLE->getCharIndices( pCharIndices, rcIcu );
404 mpIcuLE->getGlyphPositions( &pGlyphPositions->fX, rcIcu );
405 mpIcuLE->reset(); // TODO: get rid of this, PROBLEM: crash at exit when removed
406 if( LE_FAILURE(rcIcu) )
407 return false;
409 // layout bidi/script runs and export them to a ServerFontLayout
410 // convert results to GlyphItems
411 int nLastCharPos = -1;
412 int nClusterMinPos = -1;
413 int nClusterMaxPos = -1;
414 bool bClusterStart = true;
415 int nFilteredRunGlyphCount = 0;
416 const IcuPosition* pPos = pGlyphPositions;
417 for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
419 LEGlyphID nGlyphIndex = pIcuGlyphs[i];
420 // ignore glyphs which were marked or deleted by ICU
421 if( (nGlyphIndex == ICU_MARKED_GLYPH)
422 || (nGlyphIndex == ICU_DELETED_GLYPH) )
423 continue;
425 // adjust the relative char pos
426 int nCharPos = pCharIndices[i];
427 if( nCharPos >= 0 ) {
428 nCharPos += nMinRunPos;
429 // ICU seems to return bad pCharIndices
430 // for some combinations of ICU+font+text
431 // => better give up now than crash later
432 if( nCharPos >= nEndRunPos )
433 continue;
436 // if needed request glyph fallback by updating LayoutArgs
437 if( !nGlyphIndex )
439 if( nCharPos >= 0 )
441 rArgs.NeedFallback( nCharPos, bRightToLeft );
442 if ( (nCharPos > 0) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos-1]) )
443 rArgs.NeedFallback( nCharPos-1, bRightToLeft );
444 else if ( (nCharPos + 1 < nEndRunPos) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos+1]) )
445 rArgs.NeedFallback( nCharPos+1, bRightToLeft );
448 if( SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags )
449 continue;
453 // apply vertical flags, etc.
454 bool bDiacritic = false;
455 if( nCharPos >= 0 )
457 sal_UCS4 aChar = rArgs.mpStr[ nCharPos ];
458 nGlyphIndex = rFont.FixupGlyphIndex( nGlyphIndex, aChar );
460 // #i99367# HACK: try to detect all diacritics
461 if( aChar>=0x0300 && aChar<0x2100 )
462 bDiacritic = IsDiacritic( aChar );
465 // get glyph position and its metrics
466 aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
467 const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
468 int nGlyphWidth = rGM.GetCharWidth();
469 int nNewWidth = nGlyphWidth;
470 if( nGlyphWidth <= 0 )
471 bDiacritic |= true;
472 // #i99367# force all diacritics to zero width
473 // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
474 else if( bDiacritic )
475 nGlyphWidth = nNewWidth = 0;
476 else
478 // Hack, find next +ve width glyph and calculate current
479 // glyph width by substracting the two posituons
480 const IcuPosition* pNextPos = pPos+1;
481 for ( int j = i + 1; j <= nRawRunGlyphCount; ++j, ++pNextPos )
483 if ( j == nRawRunGlyphCount )
485 nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
486 break;
489 LEGlyphID nNextGlyphIndex = pIcuGlyphs[j];
490 if( (nNextGlyphIndex == ICU_MARKED_GLYPH)
491 || (nNextGlyphIndex == ICU_DELETED_GLYPH) )
492 continue;
494 const GlyphMetric& rNextGM = rFont.GetGlyphMetric( nNextGlyphIndex );
495 int nNextGlyphWidth = rNextGM.GetCharWidth();
496 if ( nNextGlyphWidth > 0 )
498 nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
499 break;
504 // heuristic to detect glyph clusters
505 bool bInCluster = true;
506 if( nLastCharPos == -1 )
508 nClusterMinPos = nClusterMaxPos = nCharPos;
509 bInCluster = false;
511 else if( !bRightToLeft )
513 // left-to-right case
514 if( nClusterMinPos > nCharPos )
515 nClusterMinPos = nCharPos; // extend cluster
516 else if( nCharPos <= nClusterMaxPos )
517 /*NOTHING*/; // inside cluster
518 else if( bDiacritic )
519 nClusterMaxPos = nCharPos; // add diacritic to cluster
520 else {
521 nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
522 bInCluster = false;
525 else
527 // right-to-left case
528 if( nClusterMaxPos < nCharPos )
529 nClusterMaxPos = nCharPos; // extend cluster
530 else if( nCharPos >= nClusterMinPos )
531 /*NOTHING*/; // inside cluster
532 else if( bDiacritic )
534 nClusterMinPos = nCharPos; // ICU often has [diacritic* baseglyph*]
535 if( bClusterStart ) {
536 nClusterMaxPos = nCharPos;
537 bInCluster = false;
540 else
542 nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
543 bInCluster = !bClusterStart;
547 long nGlyphFlags = 0;
548 if( bInCluster )
549 nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
550 if( bRightToLeft )
551 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
552 if( bDiacritic )
553 nGlyphFlags |= GlyphItem::IS_DIACRITIC;
555 // add resulting glyph item to layout
556 GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
557 #ifdef ARABIC_BANDAID
558 aGI.mnNewWidth = nNewWidth;
559 #endif
560 rLayout.AppendGlyph( aGI );
561 ++nFilteredRunGlyphCount;
562 nLastCharPos = nCharPos;
563 bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
565 aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
566 nGlyphCount += nFilteredRunGlyphCount;
569 // sort glyphs in visual order
570 // and then in logical order (e.g. diacritics after cluster start)
571 rLayout.SortGlyphItems();
573 // determine need for kashida justification
574 if( (rArgs.mpDXArray || rArgs.mnLayoutWidth)
575 && ((meScriptCode == arabScriptCode) || (meScriptCode == syrcScriptCode)) )
576 rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
578 return true;
581 // =======================================================================
583 ServerFontLayoutEngine* ServerFont::GetLayoutEngine()
585 // find best layout engine for font, platform, script and language
586 if (!mpLayoutEngine)
587 mpLayoutEngine = new IcuLayoutEngine(*this);
588 return mpLayoutEngine;
591 // =======================================================================
593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */