1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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 <unicode/uchar.h>
38 #include <vcl/svapp.hxx>
40 #include <sal/alloca.h>
42 #if OSL_DEBUG_LEVEL > 1
45 #include <rtl/instance.hxx>
47 namespace { struct SimpleLayoutEngine
: public rtl::Static
< ServerFontLayoutEngine
, SimpleLayoutEngine
> {}; }
49 // =======================================================================
50 // layout implementation for ServerFont
51 // =======================================================================
53 ServerFontLayout::ServerFontLayout( ServerFont
& rFont
)
54 : mrServerFont( rFont
)
57 void ServerFontLayout::DrawText( SalGraphics
& rSalGraphics
) const
59 rSalGraphics
.DrawServerFontLayout( *this );
62 // -----------------------------------------------------------------------
64 bool ServerFontLayout::LayoutText( ImplLayoutArgs
& rArgs
)
66 ServerFontLayoutEngine
* pLE
= NULL
;
67 if( !(rArgs
.mnFlags
& SAL_LAYOUT_COMPLEX_DISABLED
) )
68 pLE
= mrServerFont
.GetLayoutEngine();
70 pLE
= &SimpleLayoutEngine::get();
72 bool bRet
= (*pLE
)( *this, rArgs
);
76 // -----------------------------------------------------------------------
78 void ServerFontLayout::AdjustLayout( ImplLayoutArgs
& rArgs
)
80 GenericSalLayout::AdjustLayout( rArgs
);
82 // apply asian kerning if the glyphs are not already formatted
83 if( (rArgs
.mnFlags
& SAL_LAYOUT_KERNING_ASIAN
)
84 && !(rArgs
.mnFlags
& SAL_LAYOUT_VERTICAL
) )
85 if( (rArgs
.mpDXArray
!= NULL
) || (rArgs
.mnLayoutWidth
!= 0) )
86 ApplyAsianKerning( rArgs
.mpStr
, rArgs
.mnLength
);
88 // insert kashidas where requested by the formatting array
89 if( (rArgs
.mnFlags
& SAL_LAYOUT_KASHIDA_JUSTIFICATON
) && rArgs
.mpDXArray
)
91 int nKashidaIndex
= mrServerFont
.GetGlyphIndex( 0x0640 );
92 if( nKashidaIndex
!= 0 )
94 const GlyphMetric
& rGM
= mrServerFont
.GetGlyphMetric( nKashidaIndex
);
95 KashidaJustify( nKashidaIndex
, rGM
.GetCharWidth() );
96 // TODO: kashida-GSUB/GPOS
101 // =======================================================================
103 bool ServerFontLayoutEngine::operator()( ServerFontLayout
& rLayout
, ImplLayoutArgs
& rArgs
)
105 FreetypeServerFont
& rFont
= static_cast<FreetypeServerFont
&>(rLayout
.GetServerFont());
107 Point
aNewPos( 0, 0 );
108 int nOldGlyphId
= -1;
114 bool hasNextPos
= rArgs
.GetNextPos( &nCharPos
, &bRightToLeft
);
115 bool isAfterEnd
= false;
116 bool bFoundPrintable
= false;
117 while ( hasNextPos
|| ( isAfterEnd
&& ( !bFoundPrintable
|| nCharPos
< rArgs
.mnLength
) ) )
119 sal_UCS4 cChar
= rArgs
.mpStr
[ nCharPos
];
120 if( (cChar
>= 0xD800) && (cChar
<= 0xDFFF) )
122 if( cChar
>= 0xDC00 ) // this part of a surrogate pair was already processed
124 cChar
= 0x10000 + ((cChar
- 0xD800) << 10)
125 + (rArgs
.mpStr
[ nCharPos
+1 ] - 0xDC00);
129 cChar
= GetMirroredChar( cChar
);
131 // Only handle the printable characters
132 if ( u_isprint( UChar32( cChar
) ) )
134 int nGlyphIndex
= rFont
.GetGlyphIndex( cChar
);
135 // when glyph fallback is needed update LayoutArgs
138 rArgs
.NeedFallback( nCharPos
, bRightToLeft
);
139 if( cChar
>= 0x10000 ) // handle surrogate pairs
140 rArgs
.NeedFallback( nCharPos
+1, bRightToLeft
);
143 // apply pair kerning to prev glyph if requested
144 if( SAL_LAYOUT_KERNING_PAIRS
& rArgs
.mnFlags
)
146 int nKernValue
= rFont
.GetGlyphKernValue( nOldGlyphId
, nGlyphIndex
);
147 nGlyphWidth
+= nKernValue
;
148 aPrevItem
.mnNewWidth
= nGlyphWidth
;
151 // finish previous glyph
152 if( nOldGlyphId
>= 0 )
154 rLayout
.AppendGlyph( aPrevItem
);
157 aNewPos
.X() += nGlyphWidth
;
159 // prepare GlyphItem for appending it in next round
162 nOldGlyphId
= nGlyphIndex
;
163 const GlyphMetric
& rGM
= rFont
.GetGlyphMetric( nGlyphIndex
);
164 nGlyphWidth
= rGM
.GetCharWidth();
165 int nGlyphFlags
= bRightToLeft
? GlyphItem::IS_RTL_GLYPH
: 0;
167 aPrevItem
= GlyphItem( nCharPos
, nGlyphIndex
, aNewPos
, nGlyphFlags
, nGlyphWidth
);
170 // No need to signal the printable chars before the end of the runs
171 bFoundPrintable
= isAfterEnd
;
176 // Check if we have some more characters
177 // Get the next position
178 hasNextPos
= rArgs
.GetNextPos( &nCharPos
, &bRightToLeft
);
180 if ( !hasNextPos
&& nCharPos
< rArgs
.mnLength
)
186 if ( bFoundPrintable
)
194 // append last glyph item if any
195 if( nOldGlyphId
>= 0 )
196 rLayout
.AppendGlyph( aPrevItem
);
201 // =======================================================================
202 // bridge to ICU LayoutEngine
203 // =======================================================================
205 #ifdef ENABLE_ICU_LAYOUT
207 #define bool_t signed char
209 // disable warnings in icu layout headers
210 #if defined __SUNPRO_CC
214 #include <layout/LayoutEngine.h>
215 #include <layout/LEFontInstance.h>
216 #include <layout/LEScripts.h>
218 // enable warnings again
219 #if defined __SUNPRO_CC
223 #include <unicode/uscript.h>
224 #include <unicode/ubidi.h>
226 using namespace U_ICU_NAMESPACE
;
228 static const LEGlyphID ICU_DELETED_GLYPH
= 0xFFFF;
229 static const LEGlyphID ICU_MARKED_GLYPH
= 0xFFFE;
231 // -----------------------------------------------------------------------
233 class IcuFontFromServerFont
234 : public LEFontInstance
237 FreetypeServerFont
& mrServerFont
;
240 IcuFontFromServerFont( FreetypeServerFont
& rFont
)
241 : mrServerFont( rFont
)
244 virtual const void* getFontTable(LETag tableTag
) const;
245 virtual le_int32
getUnitsPerEM() const;
246 virtual float getXPixelsPerEm() const;
247 virtual float getYPixelsPerEm() const;
248 virtual float getScaleFactorX() const;
249 virtual float getScaleFactorY() const;
251 using LEFontInstance::mapCharToGlyph
;
252 virtual LEGlyphID
mapCharToGlyph( LEUnicode32 ch
) const;
254 virtual le_int32
getAscent() const;
255 virtual le_int32
getDescent() const;
256 virtual le_int32
getLeading() const;
258 virtual void getGlyphAdvance( LEGlyphID glyph
, LEPoint
&advance
) const;
259 virtual le_bool
getGlyphPoint( LEGlyphID glyph
, le_int32 pointNumber
, LEPoint
& point
) const;
262 // -----------------------------------------------------------------------
264 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag
) const
267 pTagName
[0] = (char)(nICUTableTag
>> 24);
268 pTagName
[1] = (char)(nICUTableTag
>> 16);
269 pTagName
[2] = (char)(nICUTableTag
>> 8);
270 pTagName
[3] = (char)(nICUTableTag
);
274 const unsigned char* pBuffer
= mrServerFont
.GetTable( pTagName
, &nLength
);
276 fprintf(stderr
,"IcuGetTable(\"%s\") => %p\n", pTagName
, pBuffer
);
277 int mnHeight
= mrServerFont
.GetFontSelData().mnHeight
;
278 const char* pName
= mrServerFont
.GetFontFileName()->getStr();
279 fprintf(stderr
,"font( h=%d, \"%s\" )\n", mnHeight
, pName
);
281 return (const void*)pBuffer
;
284 // -----------------------------------------------------------------------
286 le_int32
IcuFontFromServerFont::getUnitsPerEM() const
288 return mrServerFont
.GetEmUnits();
291 // -----------------------------------------------------------------------
293 float IcuFontFromServerFont::getXPixelsPerEm() const
295 const ImplFontSelectData
& r
= mrServerFont
.GetFontSelData();
296 float fX
= r
.mnWidth
? r
.mnWidth
: r
.mnHeight
;
300 // -----------------------------------------------------------------------
302 float IcuFontFromServerFont::getYPixelsPerEm() const
304 float fY
= mrServerFont
.GetFontSelData().mnHeight
;
308 // -----------------------------------------------------------------------
310 float IcuFontFromServerFont::getScaleFactorX() const
315 // -----------------------------------------------------------------------
317 float IcuFontFromServerFont::getScaleFactorY() const
322 // -----------------------------------------------------------------------
324 LEGlyphID
IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch
) const
326 LEGlyphID nGlyphIndex
= mrServerFont
.GetRawGlyphIndex( ch
);
330 // -----------------------------------------------------------------------
332 le_int32
IcuFontFromServerFont::getAscent() const
334 const FT_Size_Metrics
& rMetrics
= mrServerFont
.GetMetricsFT();
335 le_int32 nAscent
= (+rMetrics
.ascender
+ 32) >> 6;
339 // -----------------------------------------------------------------------
341 le_int32
IcuFontFromServerFont::getDescent() const
343 const FT_Size_Metrics
& rMetrics
= mrServerFont
.GetMetricsFT();
344 le_int32 nDescent
= (-rMetrics
.descender
+ 32) >> 6;
348 // -----------------------------------------------------------------------
350 le_int32
IcuFontFromServerFont::getLeading() const
352 const FT_Size_Metrics
& rMetrics
= mrServerFont
.GetMetricsFT();
353 le_int32 nLeading
= ((rMetrics
.height
- rMetrics
.ascender
+ rMetrics
.descender
) + 32) >> 6;
357 // -----------------------------------------------------------------------
359 void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex
,
360 LEPoint
&advance
) const
362 if( (nGlyphIndex
== ICU_MARKED_GLYPH
)
363 || (nGlyphIndex
== ICU_DELETED_GLYPH
) )
365 // deleted glyph or mark glyph has not advance
370 const GlyphMetric
& rGM
= mrServerFont
.GetGlyphMetric( nGlyphIndex
);
371 advance
.fX
= rGM
.GetCharWidth();
377 // -----------------------------------------------------------------------
379 le_bool
IcuFontFromServerFont::getGlyphPoint( LEGlyphID
,
381 #if OSL_DEBUG_LEVEL > 1
387 //TODO: replace dummy implementation
388 #if OSL_DEBUG_LEVEL > 1
389 fprintf(stderr
,"getGlyphPoint(%d)\n", pointNumber
);
394 // =======================================================================
396 class IcuLayoutEngine
: public ServerFontLayoutEngine
399 IcuFontFromServerFont maIcuFont
;
401 le_int32 meScriptCode
;
402 LayoutEngine
* mpIcuLE
;
405 IcuLayoutEngine( FreetypeServerFont
& );
406 virtual ~IcuLayoutEngine();
408 virtual bool operator()( ServerFontLayout
&, ImplLayoutArgs
& );
411 // -----------------------------------------------------------------------
413 IcuLayoutEngine::IcuLayoutEngine( FreetypeServerFont
& rServerFont
)
414 : maIcuFont( rServerFont
),
415 meScriptCode( USCRIPT_INVALID_CODE
),
419 // -----------------------------------------------------------------------
421 IcuLayoutEngine::~IcuLayoutEngine()
427 // -----------------------------------------------------------------------
429 static bool lcl_CharIsJoiner(sal_Unicode cChar
)
431 return ((cChar
== 0x200C) || (cChar
== 0x200D));
434 bool IcuLayoutEngine::operator()( ServerFontLayout
& rLayout
, ImplLayoutArgs
& rArgs
)
436 LEUnicode
* pIcuChars
;
437 if( sizeof(LEUnicode
) == sizeof(*rArgs
.mpStr
) )
438 pIcuChars
= (LEUnicode
*)rArgs
.mpStr
;
441 // this conversion will only be needed when either
442 // ICU's or OOo's unicodes stop being unsigned shorts
443 // TODO: watch out for surrogates!
444 pIcuChars
= (LEUnicode
*)alloca( rArgs
.mnLength
* sizeof(LEUnicode
) );
445 for( xub_StrLen ic
= 0; ic
< rArgs
.mnLength
; ++ic
)
446 pIcuChars
[ic
] = static_cast<LEUnicode
>( rArgs
.mpStr
[ic
] );
449 // allocate temporary arrays, note: round to even
450 int nGlyphCapacity
= (3 * (rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
) | 15) + 1;
452 struct IcuPosition
{ float fX
, fY
; };
453 const int nAllocSize
= sizeof(LEGlyphID
) + sizeof(le_int32
) + sizeof(IcuPosition
);
454 LEGlyphID
* pIcuGlyphs
= (LEGlyphID
*)alloca( (nGlyphCapacity
* nAllocSize
) + sizeof(IcuPosition
) );
455 le_int32
* pCharIndices
= (le_int32
*)((char*)pIcuGlyphs
+ nGlyphCapacity
* sizeof(LEGlyphID
) );
456 IcuPosition
* pGlyphPositions
= (IcuPosition
*)((char*)pCharIndices
+ nGlyphCapacity
* sizeof(le_int32
) );
458 FreetypeServerFont
& rFont
= reinterpret_cast<FreetypeServerFont
&>(rLayout
.GetServerFont());
460 UErrorCode rcI18n
= U_ZERO_ERROR
;
461 LEErrorCode rcIcu
= LE_NO_ERROR
;
462 Point
aNewPos( 0, 0 );
463 for( int nGlyphCount
= 0;; )
465 int nMinRunPos
, nEndRunPos
;
467 if( !rArgs
.GetNextRun( &nMinRunPos
, &nEndRunPos
, &bRightToLeft
) )
470 // find matching script
471 // TODO: split up bidi run into script runs
472 le_int32 eScriptCode
= -1;
473 for( int i
= nMinRunPos
; i
< nEndRunPos
; ++i
)
475 eScriptCode
= uscript_getScript( pIcuChars
[i
], &rcI18n
);
476 if( (eScriptCode
> 0) && (eScriptCode
!= latnScriptCode
) )
479 if( eScriptCode
< 0 ) // TODO: handle errors better
480 eScriptCode
= latnScriptCode
;
482 // get layout engine matching to this script
483 // no engine change necessary if script is latin
484 if( !mpIcuLE
|| ((eScriptCode
!= meScriptCode
) && (eScriptCode
> USCRIPT_INHERITED
)) )
486 // TODO: cache multiple layout engines when multiple scripts are used
488 meScriptCode
= eScriptCode
;
489 le_int32 eLangCode
= 0; // TODO: get better value
490 mpIcuLE
= LayoutEngine::layoutEngineFactory( &maIcuFont
, eScriptCode
, eLangCode
, rcIcu
);
491 if( LE_FAILURE(rcIcu
) )
498 // fall back to default layout if needed
502 // run ICU layout engine
503 // TODO: get enough context, remove extra glyps below
504 int nRawRunGlyphCount
= mpIcuLE
->layoutChars( pIcuChars
,
505 nMinRunPos
, nEndRunPos
- nMinRunPos
, rArgs
.mnLength
,
506 bRightToLeft
, aNewPos
.X(), aNewPos
.Y(), rcIcu
);
507 if( LE_FAILURE(rcIcu
) )
510 // import layout info from icu
511 mpIcuLE
->getGlyphs( pIcuGlyphs
, rcIcu
);
512 mpIcuLE
->getCharIndices( pCharIndices
, rcIcu
);
513 mpIcuLE
->getGlyphPositions( &pGlyphPositions
->fX
, rcIcu
);
514 mpIcuLE
->reset(); // TODO: get rid of this, PROBLEM: crash at exit when removed
515 if( LE_FAILURE(rcIcu
) )
518 // layout bidi/script runs and export them to a ServerFontLayout
519 // convert results to GlyphItems
520 int nLastCharPos
= -1;
521 int nClusterMinPos
= -1;
522 int nClusterMaxPos
= -1;
523 bool bClusterStart
= true;
524 int nFilteredRunGlyphCount
= 0;
525 const IcuPosition
* pPos
= pGlyphPositions
;
526 for( int i
= 0; i
< nRawRunGlyphCount
; ++i
, ++pPos
)
528 LEGlyphID nGlyphIndex
= pIcuGlyphs
[i
];
529 // ignore glyphs which were marked or deleted by ICU
530 if( (nGlyphIndex
== ICU_MARKED_GLYPH
)
531 || (nGlyphIndex
== ICU_DELETED_GLYPH
) )
534 // adjust the relative char pos
535 int nCharPos
= pCharIndices
[i
];
536 if( nCharPos
>= 0 ) {
537 nCharPos
+= nMinRunPos
;
538 // ICU seems to return bad pCharIndices
539 // for some combinations of ICU+font+text
540 // => better give up now than crash later
541 if( nCharPos
>= nEndRunPos
)
545 // if needed request glyph fallback by updating LayoutArgs
550 rArgs
.NeedFallback( nCharPos
, bRightToLeft
);
551 if ( (nCharPos
> 0) && lcl_CharIsJoiner(rArgs
.mpStr
[nCharPos
-1]) )
552 rArgs
.NeedFallback( nCharPos
-1, bRightToLeft
);
553 else if ( (nCharPos
+ 1 < nEndRunPos
) && lcl_CharIsJoiner(rArgs
.mpStr
[nCharPos
+1]) )
554 rArgs
.NeedFallback( nCharPos
+1, bRightToLeft
);
557 if( SAL_LAYOUT_FOR_FALLBACK
& rArgs
.mnFlags
)
562 // apply vertical flags, etc.
563 bool bDiacritic
= false;
566 sal_UCS4 aChar
= rArgs
.mpStr
[ nCharPos
];
567 #if 0 // TODO: enable if some unicodes>0xFFFF should need glyph flags!=0
568 if( (aChar
>= 0xD800) && (aChar
<= 0xDFFF) )
570 if( cChar
>= 0xDC00 ) // this part of a surrogate pair was already processed
572 // calculate unicode scalar value of surrogate pair
573 aChar
= 0x10000 + ((aChar
- 0xD800) << 10);
574 sal_UCS4 aLow
= rArgs
.mpStr
[ nCharPos
+1 ];
575 aChar
+= aLow
& 0x03FF;
578 nGlyphIndex
= rFont
.FixupGlyphIndex( nGlyphIndex
, aChar
);
580 // #i99367# HACK: try to detect all diacritics
581 if( aChar
>=0x0300 && aChar
<0x2100 )
582 bDiacritic
= IsDiacritic( aChar
);
585 // get glyph position and its metrics
586 aNewPos
= Point( (int)(pPos
->fX
+0.5), (int)(pPos
->fY
+0.5) );
587 const GlyphMetric
& rGM
= rFont
.GetGlyphMetric( nGlyphIndex
);
588 int nGlyphWidth
= rGM
.GetCharWidth();
589 if( nGlyphWidth
<= 0 )
591 // #i99367# force all diacritics to zero width
592 // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
593 else if( bDiacritic
)
596 // heuristic to detect glyph clusters
597 bool bInCluster
= true;
598 if( nLastCharPos
== -1 )
600 nClusterMinPos
= nClusterMaxPos
= nCharPos
;
603 else if( !bRightToLeft
)
605 // left-to-right case
606 if( nClusterMinPos
> nCharPos
)
607 nClusterMinPos
= nCharPos
; // extend cluster
608 else if( nCharPos
<= nClusterMaxPos
)
609 /*NOTHING*/; // inside cluster
610 else if( bDiacritic
)
611 nClusterMaxPos
= nCharPos
; // add diacritic to cluster
613 nClusterMinPos
= nClusterMaxPos
= nCharPos
; // new cluster
619 // right-to-left case
620 if( nClusterMaxPos
< nCharPos
)
621 nClusterMaxPos
= nCharPos
; // extend cluster
622 else if( nCharPos
>= nClusterMinPos
)
623 /*NOTHING*/; // inside cluster
624 else if( bDiacritic
)
626 nClusterMinPos
= nCharPos
; // ICU often has [diacritic* baseglyph*]
627 if( bClusterStart
) {
628 nClusterMaxPos
= nCharPos
;
634 nClusterMinPos
= nClusterMaxPos
= nCharPos
; // new cluster
635 bInCluster
= !bClusterStart
;
639 long nGlyphFlags
= 0;
641 nGlyphFlags
|= GlyphItem::IS_IN_CLUSTER
;
643 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
645 nGlyphFlags
|= GlyphItem::IS_DIACRITIC
;
647 // add resulting glyph item to layout
648 const GlyphItem
aGI( nCharPos
, nGlyphIndex
, aNewPos
, nGlyphFlags
, nGlyphWidth
);
649 rLayout
.AppendGlyph( aGI
);
650 ++nFilteredRunGlyphCount
;
651 nLastCharPos
= nCharPos
;
652 bClusterStart
= !aGI
.IsDiacritic(); // TODO: only needed in RTL-codepath
654 aNewPos
= Point( (int)(pPos
->fX
+0.5), (int)(pPos
->fY
+0.5) );
655 nGlyphCount
+= nFilteredRunGlyphCount
;
658 // sort glyphs in visual order
659 // and then in logical order (e.g. diacritics after cluster start)
660 rLayout
.SortGlyphItems();
662 // determine need for kashida justification
663 if( (rArgs
.mpDXArray
|| rArgs
.mnLayoutWidth
)
664 && ((meScriptCode
== arabScriptCode
) || (meScriptCode
== syrcScriptCode
)) )
665 rArgs
.mnFlags
|= SAL_LAYOUT_KASHIDA_JUSTIFICATON
;
670 #endif // ENABLE_ICU_LAYOUT
672 // =======================================================================
674 ServerFontLayoutEngine
* FreetypeServerFont::GetLayoutEngine()
676 // find best layout engine for font, platform, script and language
677 #ifdef ENABLE_ICU_LAYOUT
678 if( !mpLayoutEngine
&& FT_IS_SFNT( maFaceFT
) )
679 mpLayoutEngine
= new IcuLayoutEngine( *this );
680 #endif // ENABLE_ICU_LAYOUT
682 return mpLayoutEngine
;
685 // =======================================================================