4 * (C) Copyright IBM Corp. 1998-2009 - All Rights Reserved
10 #include "LELanguages.h"
12 #include "LayoutEngine.h"
13 #include "ArabicLayoutEngine.h"
14 #include "CanonShaping.h"
15 #include "HanLayoutEngine.h"
16 #include "HangulLayoutEngine.h"
17 #include "IndicLayoutEngine.h"
18 #include "KhmerLayoutEngine.h"
19 #include "ThaiLayoutEngine.h"
20 #include "TibetanLayoutEngine.h"
21 #include "GXLayoutEngine.h"
22 #include "ScriptAndLanguageTags.h"
23 #include "CharSubstitutionFilter.h"
25 #include "LEGlyphStorage.h"
27 #include "OpenTypeUtilities.h"
28 #include "GlyphSubstitutionTables.h"
29 #include "GlyphDefinitionTables.h"
30 #include "MorphTables.h"
32 #include "DefaultCharMapper.h"
34 #include "KernTable.h"
38 /* Leave this copyright notice here! It needs to go somewhere in this library. */
39 static const char copyright
[] = U_COPYRIGHT_STRING
;
41 const LEUnicode32
DefaultCharMapper::controlChars
[] = {
42 0x0009, 0x000A, 0x000D,
43 /*0x200C, 0x200D,*/ 0x200E, 0x200F,
44 0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E,
45 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F
48 const le_int32
DefaultCharMapper::controlCharsCount
= LE_ARRAY_SIZE(controlChars
);
50 LEUnicode32
DefaultCharMapper::mapChar(LEUnicode32 ch
) const
52 if (fFilterControls
) {
53 le_int32 index
= OpenTypeUtilities::search((le_uint32
)ch
, (le_uint32
*)controlChars
, controlCharsCount
);
55 if (controlChars
[index
] == ch
) {
61 le_int32 index
= OpenTypeUtilities::search((le_uint32
) ch
, (le_uint32
*)DefaultCharMapper::mirroredChars
, DefaultCharMapper::mirroredCharsCount
);
63 if (mirroredChars
[index
] == ch
) {
64 return DefaultCharMapper::srahCderorrim
[index
];
71 // This is here to get it out of LEGlyphFilter.h.
72 // No particular reason to put it here, other than
73 // this is a good central location...
74 LEGlyphFilter::~LEGlyphFilter()
79 CharSubstitutionFilter::CharSubstitutionFilter(const LEFontInstance
*fontInstance
)
80 : fFontInstance(fontInstance
)
85 CharSubstitutionFilter::~CharSubstitutionFilter()
90 class CanonMarkFilter
: public UMemory
, public LEGlyphFilter
93 const GlyphClassDefinitionTable
*classDefTable
;
95 CanonMarkFilter(const CanonMarkFilter
&other
); // forbid copying of this class
96 CanonMarkFilter
&operator=(const CanonMarkFilter
&other
); // forbid copying of this class
99 CanonMarkFilter(const GlyphDefinitionTableHeader
*gdefTable
);
100 virtual ~CanonMarkFilter();
102 virtual le_bool
accept(LEGlyphID glyph
) const;
105 CanonMarkFilter::CanonMarkFilter(const GlyphDefinitionTableHeader
*gdefTable
)
107 classDefTable
= gdefTable
->getMarkAttachClassDefinitionTable();
110 CanonMarkFilter::~CanonMarkFilter()
115 le_bool
CanonMarkFilter::accept(LEGlyphID glyph
) const
117 le_int32 glyphClass
= classDefTable
->getGlyphClass(glyph
);
119 return glyphClass
!= 0;
122 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LayoutEngine
)
124 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG
126 #define ccmpFeatureMask 0x80000000UL
128 #define canonFeatures (ccmpFeatureMask)
130 static const FeatureMap canonFeatureMap
[] =
132 {ccmpFeatureTag
, ccmpFeatureMask
}
135 static const le_int32 canonFeatureMapCount
= LE_ARRAY_SIZE(canonFeatureMap
);
137 LayoutEngine::LayoutEngine(const LEFontInstance
*fontInstance
,
139 le_int32 languageCode
,
141 LEErrorCode
&success
)
142 : fGlyphStorage(NULL
), fFontInstance(fontInstance
), fScriptCode(scriptCode
), fLanguageCode(languageCode
),
143 fTypoFlags(typoFlags
), fFilterZeroWidth(TRUE
)
145 if (LE_FAILURE(success
)) {
149 fGlyphStorage
= new LEGlyphStorage();
150 if (fGlyphStorage
== NULL
) {
151 success
= LE_MEMORY_ALLOCATION_ERROR
;
155 le_int32
LayoutEngine::getGlyphCount() const
157 return fGlyphStorage
->getGlyphCount();
160 void LayoutEngine::getCharIndices(le_int32 charIndices
[], le_int32 indexBase
, LEErrorCode
&success
) const
162 fGlyphStorage
->getCharIndices(charIndices
, indexBase
, success
);
165 void LayoutEngine::getCharIndices(le_int32 charIndices
[], LEErrorCode
&success
) const
167 fGlyphStorage
->getCharIndices(charIndices
, success
);
170 // Copy the glyphs into caller's (32-bit) glyph array, OR in extraBits
171 void LayoutEngine::getGlyphs(le_uint32 glyphs
[], le_uint32 extraBits
, LEErrorCode
&success
) const
173 fGlyphStorage
->getGlyphs(glyphs
, extraBits
, success
);
176 void LayoutEngine::getGlyphs(LEGlyphID glyphs
[], LEErrorCode
&success
) const
178 fGlyphStorage
->getGlyphs(glyphs
, success
);
182 void LayoutEngine::getGlyphPositions(float positions
[], LEErrorCode
&success
) const
184 fGlyphStorage
->getGlyphPositions(positions
, success
);
187 void LayoutEngine::getGlyphPosition(le_int32 glyphIndex
, float &x
, float &y
, LEErrorCode
&success
) const
189 fGlyphStorage
->getGlyphPosition(glyphIndex
, x
, y
, success
);
192 le_int32
LayoutEngine::characterProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
193 LEUnicode
*&outChars
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
195 if (LE_FAILURE(success
)) {
199 if (offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
200 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
204 const GlyphSubstitutionTableHeader
*canonGSUBTable
= (GlyphSubstitutionTableHeader
*) CanonShaping::glyphSubstitutionTable
;
205 LETag scriptTag
= OpenTypeLayoutEngine::getScriptTag(fScriptCode
);
206 LETag langSysTag
= OpenTypeLayoutEngine::getLangSysTag(fLanguageCode
);
207 le_int32 i
, dir
= 1, out
= 0, outCharCount
= count
;
209 if (canonGSUBTable
->coversScript(scriptTag
)) {
210 CharSubstitutionFilter
*substitutionFilter
= new CharSubstitutionFilter(fFontInstance
);
211 if (substitutionFilter
== NULL
) {
212 success
= LE_MEMORY_ALLOCATION_ERROR
;
216 const LEUnicode
*inChars
= &chars
[offset
];
217 LEUnicode
*reordered
= NULL
;
218 LEGlyphStorage fakeGlyphStorage
;
220 fakeGlyphStorage
.allocateGlyphArray(count
, rightToLeft
, success
);
222 if (LE_FAILURE(success
)) {
223 delete substitutionFilter
;
227 // This is the cheapest way to get mark reordering only for Hebrew.
228 // We could just do the mark reordering for all scripts, but most
229 // of them probably don't need it...
230 if (fScriptCode
== hebrScriptCode
) {
231 reordered
= LE_NEW_ARRAY(LEUnicode
, count
);
233 if (reordered
== NULL
) {
234 delete substitutionFilter
;
235 success
= LE_MEMORY_ALLOCATION_ERROR
;
239 CanonShaping::reorderMarks(&chars
[offset
], count
, rightToLeft
, reordered
, fakeGlyphStorage
);
243 fakeGlyphStorage
.allocateAuxData(success
);
245 if (LE_FAILURE(success
)) {
246 delete substitutionFilter
;
255 for (i
= 0; i
< count
; i
+= 1, out
+= dir
) {
256 fakeGlyphStorage
[out
] = (LEGlyphID
) inChars
[i
];
257 fakeGlyphStorage
.setAuxData(out
, canonFeatures
, success
);
260 if (reordered
!= NULL
) {
261 LE_DELETE_ARRAY(reordered
);
264 outCharCount
= canonGSUBTable
->process(fakeGlyphStorage
, rightToLeft
, scriptTag
, langSysTag
, NULL
, substitutionFilter
, canonFeatureMap
, canonFeatureMapCount
, FALSE
, success
);
266 if (LE_FAILURE(success
)) {
267 delete substitutionFilter
;
271 out
= (rightToLeft
? outCharCount
- 1 : 0);
274 * The char indices array in fakeGlyphStorage has the correct mapping
275 * back to the original input characters. Save it in glyphStorage. The
276 * subsequent call to glyphStoratge.allocateGlyphArray will keep this
277 * array rather than allocating and initializing a new one.
279 glyphStorage
.adoptCharIndicesArray(fakeGlyphStorage
);
281 outChars
= LE_NEW_ARRAY(LEUnicode
, outCharCount
);
283 if (outChars
== NULL
) {
284 delete substitutionFilter
;
285 success
= LE_MEMORY_ALLOCATION_ERROR
;
289 for (i
= 0; i
< outCharCount
; i
+= 1, out
+= dir
) {
290 outChars
[out
] = (LEUnicode
) LE_GET_GLYPH(fakeGlyphStorage
[i
]);
293 delete substitutionFilter
;
299 le_int32
LayoutEngine::computeGlyphs(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
300 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
302 if (LE_FAILURE(success
)) {
306 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
307 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
311 LEUnicode
*outChars
= NULL
;
312 le_int32 outCharCount
= characterProcessing(chars
, offset
, count
, max
, rightToLeft
, outChars
, glyphStorage
, success
);
314 if (outChars
!= NULL
) {
315 mapCharsToGlyphs(outChars
, 0, outCharCount
, rightToLeft
, rightToLeft
, glyphStorage
, success
);
316 LE_DELETE_ARRAY(outChars
); // FIXME: a subclass may have allocated this, in which case this delete might not work...
318 mapCharsToGlyphs(chars
, offset
, count
, rightToLeft
, rightToLeft
, glyphStorage
, success
);
321 return glyphStorage
.getGlyphCount();
326 void LayoutEngine::positionGlyphs(LEGlyphStorage
&glyphStorage
, float x
, float y
, LEErrorCode
&success
)
328 if (LE_FAILURE(success
)) {
332 glyphStorage
.allocatePositions(success
);
334 if (LE_FAILURE(success
)) {
338 le_int32 i
, glyphCount
= glyphStorage
.getGlyphCount();
340 for (i
= 0; i
< glyphCount
; i
+= 1) {
343 glyphStorage
.setPosition(i
, x
, y
, success
);
345 fFontInstance
->getGlyphAdvance(glyphStorage
[i
], advance
);
350 glyphStorage
.setPosition(glyphCount
, x
, y
, success
);
353 void LayoutEngine::adjustGlyphPositions(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_bool reverse
,
354 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
356 if (LE_FAILURE(success
)) {
360 if (chars
== NULL
|| offset
< 0 || count
< 0) {
361 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
365 GlyphDefinitionTableHeader
*gdefTable
= (GlyphDefinitionTableHeader
*) CanonShaping::glyphDefinitionTable
;
366 CanonMarkFilter
filter(gdefTable
);
368 adjustMarkGlyphs(&chars
[offset
], count
, reverse
, glyphStorage
, &filter
, success
);
370 if (fTypoFlags
& 0x1) { /* kerning enabled */
371 static const le_uint32 kernTableTag
= LE_KERN_TABLE_TAG
;
373 KernTable
kt(fFontInstance
, getFontTable(kernTableTag
));
374 kt
.process(glyphStorage
);
377 // default is no adjustments
381 void LayoutEngine::adjustMarkGlyphs(LEGlyphStorage
&glyphStorage
, LEGlyphFilter
*markFilter
, LEErrorCode
&success
)
384 le_int32 p
, glyphCount
= glyphStorage
.getGlyphCount();
386 if (LE_FAILURE(success
)) {
390 if (markFilter
== NULL
) {
391 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
397 glyphStorage
.getGlyphPosition(0, prev
, ignore
, success
);
399 for (p
= 0; p
< glyphCount
; p
+= 1) {
400 float next
, xAdvance
;
402 glyphStorage
.getGlyphPosition(p
+ 1, next
, ignore
, success
);
404 xAdvance
= next
- prev
;
405 glyphStorage
.adjustPosition(p
, xAdjust
, 0, success
);
407 if (markFilter
->accept(glyphStorage
[p
])) {
414 glyphStorage
.adjustPosition(glyphCount
, xAdjust
, 0, success
);
417 void LayoutEngine::adjustMarkGlyphs(const LEUnicode chars
[], le_int32 charCount
, le_bool reverse
, LEGlyphStorage
&glyphStorage
, LEGlyphFilter
*markFilter
, LEErrorCode
&success
)
420 le_int32 c
= 0, direction
= 1, p
;
421 le_int32 glyphCount
= glyphStorage
.getGlyphCount();
423 if (LE_FAILURE(success
)) {
427 if (markFilter
== NULL
) {
428 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
439 glyphStorage
.getGlyphPosition(0, prev
, ignore
, success
);
441 for (p
= 0; p
< charCount
; p
+= 1, c
+= direction
) {
442 float next
, xAdvance
;
444 glyphStorage
.getGlyphPosition(p
+ 1, next
, ignore
, success
);
446 xAdvance
= next
- prev
;
447 glyphStorage
.adjustPosition(p
, xAdjust
, 0, success
);
449 if (markFilter
->accept(chars
[c
])) {
456 glyphStorage
.adjustPosition(glyphCount
, xAdjust
, 0, success
);
459 const void *LayoutEngine::getFontTable(LETag tableTag
) const
461 return fFontInstance
->getFontTable(tableTag
);
464 void LayoutEngine::mapCharsToGlyphs(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_bool reverse
, le_bool mirror
,
465 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
467 if (LE_FAILURE(success
)) {
471 glyphStorage
.allocateGlyphArray(count
, reverse
, success
);
473 DefaultCharMapper
charMapper(TRUE
, mirror
);
475 fFontInstance
->mapCharsToGlyphs(chars
, offset
, count
, reverse
, &charMapper
, fFilterZeroWidth
, glyphStorage
);
478 // Input: characters, font?
479 // Output: glyphs, positions, char indices
480 // Returns: number of glyphs
481 le_int32
LayoutEngine::layoutChars(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
482 float x
, float y
, LEErrorCode
&success
)
484 if (LE_FAILURE(success
)) {
488 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
489 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
495 if (fGlyphStorage
->getGlyphCount() > 0) {
496 fGlyphStorage
->reset();
499 glyphCount
= computeGlyphs(chars
, offset
, count
, max
, rightToLeft
, *fGlyphStorage
, success
);
500 positionGlyphs(*fGlyphStorage
, x
, y
, success
);
501 adjustGlyphPositions(chars
, offset
, count
, rightToLeft
, *fGlyphStorage
, success
);
506 void LayoutEngine::reset()
508 fGlyphStorage
->reset();
511 LayoutEngine
*LayoutEngine::layoutEngineFactory(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
, LEErrorCode
&success
)
513 // 3 -> kerning and ligatures
514 return LayoutEngine::layoutEngineFactory(fontInstance
, scriptCode
, languageCode
, 3, success
);
517 LayoutEngine
*LayoutEngine::layoutEngineFactory(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
, le_int32 typoFlags
, LEErrorCode
&success
)
519 static const le_uint32 gsubTableTag
= LE_GSUB_TABLE_TAG
;
520 static const le_uint32 mortTableTag
= LE_MORT_TABLE_TAG
;
522 if (LE_FAILURE(success
)) {
526 const GlyphSubstitutionTableHeader
*gsubTable
= (const GlyphSubstitutionTableHeader
*) fontInstance
->getFontTable(gsubTableTag
);
527 LayoutEngine
*result
= NULL
;
528 LETag scriptTag
= 0x00000000;
529 LETag languageTag
= 0x00000000;
530 LETag v2ScriptTag
= OpenTypeLayoutEngine::getV2ScriptTag(scriptCode
);
532 // Right now, only invoke V2 processing for Devanagari. TODO: Allow more V2 scripts as they are
535 if ( v2ScriptTag
== dev2ScriptTag
&& gsubTable
!= NULL
&& gsubTable
->coversScript( v2ScriptTag
)) {
536 result
= new IndicOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, TRUE
, gsubTable
, success
);
538 else if (gsubTable
!= NULL
&& gsubTable
->coversScript(scriptTag
= OpenTypeLayoutEngine::getScriptTag(scriptCode
))) {
539 switch (scriptCode
) {
550 result
= new IndicOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, FALSE
, gsubTable
, success
);
554 result
= new ArabicOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
558 result
= new HangulOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
562 languageTag
= OpenTypeLayoutEngine::getLangSysTag(languageCode
);
564 switch (languageCode
) {
565 case korLanguageCode
:
566 case janLanguageCode
:
567 case zhtLanguageCode
:
568 case zhsLanguageCode
:
569 if (gsubTable
->coversScriptAndLanguage(scriptTag
, languageTag
, TRUE
)) {
570 result
= new HanOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
574 // note: falling through to default case.
576 result
= new OpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
583 result
= new TibetanOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
587 result
= new KhmerOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
591 result
= new OpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, gsubTable
, success
);
595 const MorphTableHeader
*morphTable
= (MorphTableHeader
*) fontInstance
->getFontTable(mortTableTag
);
597 if (morphTable
!= NULL
) {
598 result
= new GXLayoutEngine(fontInstance
, scriptCode
, languageCode
, morphTable
, success
);
600 switch (scriptCode
) {
612 result
= new IndicOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
);
617 //case hebrScriptCode:
618 result
= new UnicodeArabicOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
);
621 //case hebrScriptCode:
622 // return new HebrewOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags);
625 result
= new ThaiLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
);
629 result
= new HangulOpenTypeLayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
);
633 result
= new LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
);
639 if (result
&& LE_FAILURE(success
)) {
644 if (result
== NULL
) {
645 success
= LE_MEMORY_ALLOCATION_ERROR
;
651 LayoutEngine::~LayoutEngine() {
652 delete fGlyphStorage
;