1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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();
55 bool bRet
= pLE
? pLE
->layout(*this, rArgs
) : false;
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
99 ServerFont
& mrServerFont
;
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
130 pTagName
[0] = (char)(nICUTableTag
>> 24);
131 pTagName
[1] = (char)(nICUTableTag
>> 16);
132 pTagName
[2] = (char)(nICUTableTag
>> 8);
133 pTagName
[3] = (char)(nICUTableTag
);
137 const unsigned char* pBuffer
= mrServerFont
.GetTable( pTagName
, &nLength
);
138 SAL_INFO("vcl", "IcuGetTable(\"" << pTagName
<< "\") => " << pBuffer
);
141 "font( h=" << mrServerFont
.GetFontSelData().mnHeight
<< ", \""
142 << mrServerFont
.GetFontFileName()->getStr() << "\" )");
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
;
162 // -----------------------------------------------------------------------
164 float IcuFontFromServerFont::getYPixelsPerEm() const
166 float fY
= mrServerFont
.GetFontSelData().mnHeight
;
170 // -----------------------------------------------------------------------
172 float IcuFontFromServerFont::getScaleFactorX() const
177 // -----------------------------------------------------------------------
179 float IcuFontFromServerFont::getScaleFactorY() const
184 // -----------------------------------------------------------------------
186 LEGlyphID
IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch
) const
188 LEGlyphID nGlyphIndex
= mrServerFont
.GetRawGlyphIndex( ch
);
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; │
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;
213 // -----------------------------------------------------------------------
215 le_int32
IcuFontFromServerFont::getDescent() const
217 const FT_Size_Metrics
& rMetrics
= mrServerFont
.GetMetricsFT();
218 le_int32 nDescent
= (-rMetrics
.descender
+ 32) >> 6;
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;
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
244 const GlyphMetric
& rGM
= mrServerFont
.GetGlyphMetric( nGlyphIndex
);
245 advance
.fX
= rGM
.GetCharWidth();
251 // -----------------------------------------------------------------------
253 le_bool
IcuFontFromServerFont::getGlyphPoint( LEGlyphID
,
254 le_int32 pointNumber
, LEPoint
& ) const
256 //TODO: replace dummy implementation
257 SAL_INFO("vcl", "getGlyphPoint(" << pointNumber
<< ")");
261 // =======================================================================
263 class IcuLayoutEngine
: public ServerFontLayoutEngine
266 IcuFontFromServerFont maIcuFont
;
268 le_int32 meScriptCode
;
269 le_int32 mnLayoutFlags
;
270 LayoutEngine
* mpIcuLE
;
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
),
288 // -----------------------------------------------------------------------
290 IcuLayoutEngine::~IcuLayoutEngine()
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
;
314 if (rArgs
.mnFlags
& SAL_LAYOUT_KERNING_PAIRS
)
315 nLayoutFlags
|= 0x01;
316 if (rArgs
.mnFlags
& SAL_LAYOUT_ENABLE_LIGATURES
)
317 nLayoutFlags
|= 0x10;
320 LEUnicode
* pIcuChars
;
321 if( sizeof(LEUnicode
) == sizeof(*rArgs
.mpStr
) )
322 pIcuChars
= (LEUnicode
*)rArgs
.mpStr
;
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
;
353 if( !rArgs
.GetNextRun( &nMinRunPos
, &nEndRunPos
, &bRightToLeft
) )
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
)
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
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
) )
389 // fall back to default layout if needed
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
) )
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
) )
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
) )
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
)
436 // if needed request glyph fallback by updating LayoutArgs
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
)
453 // apply vertical flags, etc.
454 bool bDiacritic
= false;
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 )
472 // #i99367# force all diacritics to zero width
473 // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
474 else if( bDiacritic
)
475 nGlyphWidth
= nNewWidth
= 0;
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
);
489 LEGlyphID nNextGlyphIndex
= pIcuGlyphs
[j
];
490 if( (nNextGlyphIndex
== ICU_MARKED_GLYPH
)
491 || (nNextGlyphIndex
== ICU_DELETED_GLYPH
) )
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
);
504 // heuristic to detect glyph clusters
505 bool bInCluster
= true;
506 if( nLastCharPos
== -1 )
508 nClusterMinPos
= nClusterMaxPos
= nCharPos
;
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
521 nClusterMinPos
= nClusterMaxPos
= nCharPos
; // new cluster
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
;
542 nClusterMinPos
= nClusterMaxPos
= nCharPos
; // new cluster
543 bInCluster
= !bClusterStart
;
547 long nGlyphFlags
= 0;
549 nGlyphFlags
|= GlyphItem::IS_IN_CLUSTER
;
551 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
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
;
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
;
581 // =======================================================================
583 ServerFontLayoutEngine
* ServerFont::GetLayoutEngine()
585 // find best layout engine for font, platform, script and language
587 mpLayoutEngine
= new IcuLayoutEngine(*this);
588 return mpLayoutEngine
;
591 // =======================================================================
593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */