2 * Opentype font interfaces for the Uniscribe Script Processor (usp10.dll)
4 * Copyright 2012 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "usp10_internal.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe
);
38 #ifdef WORDS_BIGENDIAN
39 #define GET_BE_WORD(x) (x)
40 #define GET_BE_DWORD(x) (x)
42 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
43 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
46 /* These are all structures needed for the cmap format 12 table */
47 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
53 } CMAP_EncodingRecord
;
58 CMAP_EncodingRecord tables
[1];
65 } CMAP_SegmentedCoverage_group
;
73 CMAP_SegmentedCoverage_group groups
[1];
74 } CMAP_SegmentedCoverage
;
76 /* These are all structures needed for the GDEF table */
77 #define GDEF_TAG MS_MAKE_TAG('G', 'D', 'E', 'F')
79 enum {BaseGlyph
=1, LigatureGlyph
, MarkGlyph
, ComponentGlyph
};
86 WORD MarkAttachClassDef
;
93 WORD ClassValueArray
[1];
94 } GDEF_ClassDefFormat1
;
100 } GDEF_ClassRangeRecord
;
104 WORD ClassRangeCount
;
105 GDEF_ClassRangeRecord ClassRangeRecord
[1];
106 } GDEF_ClassDefFormat2
;
108 /* These are all structures needed for the GSUB table */
124 OT_ScriptRecord ScriptRecord
[1];
135 OT_LangSysRecord LangSysRecord
[1];
139 WORD LookupOrder
; /* Reserved */
140 WORD ReqFeatureIndex
;
142 WORD FeatureIndex
[1];
152 OT_FeatureRecord FeatureRecord
[1];
156 WORD FeatureParams
; /* Reserved */
158 WORD LookupListIndex
[1];
177 } OT_CoverageFormat1
;
182 WORD StartCoverageIndex
;
188 OT_RangeRecord RangeRecord
[1];
189 } OT_CoverageFormat2
;
192 WORD SubstFormat
; /* = 1 */
195 } GSUB_SingleSubstFormat1
;
198 WORD SubstFormat
; /* = 2 */
202 }GSUB_SingleSubstFormat2
;
205 WORD SubstFormat
; /* = 1 */
209 }GSUB_MultipleSubstFormat1
;
217 WORD SubstFormat
; /* = 1 */
221 }GSUB_LigatureSubstFormat1
;
236 WORD LookupListIndex
;
238 }GSUB_SubstLookupRecord
;
241 WORD SubstFormat
; /* = 1 */
243 WORD ChainSubRuleSetCount
;
244 WORD ChainSubRuleSet
[1];
245 }GSUB_ChainContextSubstFormat1
;
248 WORD SubstFormat
; /* = 3 */
249 WORD BacktrackGlyphCount
;
251 }GSUB_ChainContextSubstFormat3_1
;
254 WORD InputGlyphCount
;
256 }GSUB_ChainContextSubstFormat3_2
;
259 WORD LookaheadGlyphCount
;
261 }GSUB_ChainContextSubstFormat3_3
;
265 GSUB_SubstLookupRecord SubstLookupRecord
[1];
266 }GSUB_ChainContextSubstFormat3_4
;
269 WORD SubstFormat
; /* = 1 */
271 WORD AlternateSetCount
;
272 WORD AlternateSet
[1];
273 } GSUB_AlternateSubstFormat1
;
280 /* These are all structures needed for the GPOS table */
300 } GPOS_AnchorFormat1
;
307 } GPOS_AnchorFormat2
;
315 } GPOS_AnchorFormat3
;
333 } GPOS_SinglePosFormat1
;
342 } GPOS_MarkBasePosFormat1
;
350 GPOS_BaseRecord BaseRecord
[1];
360 GPOS_MarkRecord MarkRecord
[1];
367 static VOID
*load_CMAP_format12_table(HDC hdc
, ScriptCache
*psc
)
369 CMAP_Header
*CMAP_Table
= NULL
;
373 if (!psc
->CMAP_Table
)
375 length
= GetFontData(hdc
, CMAP_TAG
, 0, NULL
, 0);
376 if (length
!= GDI_ERROR
)
378 psc
->CMAP_Table
= HeapAlloc(GetProcessHeap(),0,length
);
379 GetFontData(hdc
, CMAP_TAG
, 0, psc
->CMAP_Table
, length
);
380 TRACE("Loaded cmap table of %i bytes\n",length
);
386 CMAP_Table
= psc
->CMAP_Table
;
388 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
); i
++)
390 if ( (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) == 3) &&
391 (GET_BE_WORD(CMAP_Table
->tables
[i
].encodingID
) == 10) )
393 CMAP_SegmentedCoverage
*format
= (CMAP_SegmentedCoverage
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
394 if (GET_BE_WORD(format
->format
) == 12)
401 static int compare_group(const void *a
, const void* b
)
403 const DWORD
*chr
= a
;
404 const CMAP_SegmentedCoverage_group
*group
= b
;
406 if (*chr
< GET_BE_DWORD(group
->startCharCode
))
408 if (*chr
> GET_BE_DWORD(group
->endCharCode
))
413 DWORD
OpenType_CMAP_GetGlyphIndex(HDC hdc
, ScriptCache
*psc
, DWORD utf32c
, LPWORD pgi
, DWORD flags
)
415 /* BMP: use gdi32 for ease */
416 if (utf32c
< 0x10000)
419 return GetGlyphIndicesW(hdc
,&ch
, 1, pgi
, flags
);
422 if (!psc
->CMAP_format12_Table
)
423 psc
->CMAP_format12_Table
= load_CMAP_format12_table(hdc
, psc
);
425 if (flags
& GGI_MARK_NONEXISTING_GLYPHS
)
430 if (psc
->CMAP_format12_Table
)
432 CMAP_SegmentedCoverage
*format
= NULL
;
433 CMAP_SegmentedCoverage_group
*group
= NULL
;
435 format
= (CMAP_SegmentedCoverage
*)psc
->CMAP_format12_Table
;
437 group
= bsearch(&utf32c
, format
->groups
, GET_BE_DWORD(format
->nGroups
),
438 sizeof(CMAP_SegmentedCoverage_group
), compare_group
);
442 DWORD offset
= utf32c
- GET_BE_DWORD(group
->startCharCode
);
443 *pgi
= GET_BE_DWORD(group
->startGlyphID
) + offset
;
454 static WORD
GDEF_get_glyph_class(const GDEF_Header
*header
, WORD glyph
)
458 const GDEF_ClassDefFormat1
*cf1
;
463 offset
= GET_BE_WORD(header
->GlyphClassDef
);
467 cf1
= (GDEF_ClassDefFormat1
*)(((BYTE
*)header
)+offset
);
468 if (GET_BE_WORD(cf1
->ClassFormat
) == 1)
470 if (glyph
>= GET_BE_WORD(cf1
->StartGlyph
))
472 int index
= glyph
- GET_BE_WORD(cf1
->StartGlyph
);
473 if (index
< GET_BE_WORD(cf1
->GlyphCount
))
474 class = GET_BE_WORD(cf1
->ClassValueArray
[index
]);
477 else if (GET_BE_WORD(cf1
->ClassFormat
) == 2)
479 const GDEF_ClassDefFormat2
*cf2
= (GDEF_ClassDefFormat2
*)cf1
;
481 top
= GET_BE_WORD(cf2
->ClassRangeCount
);
482 for (i
= 0; i
< top
; i
++)
484 if (glyph
>= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Start
) &&
485 glyph
<= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].End
))
487 class = GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Class
);
493 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1
->ClassFormat
));
498 static VOID
*load_gdef_table(HDC hdc
)
500 VOID
* GDEF_Table
= NULL
;
501 int length
= GetFontData(hdc
, GDEF_TAG
, 0, NULL
, 0);
502 if (length
!= GDI_ERROR
)
504 GDEF_Table
= HeapAlloc(GetProcessHeap(),0,length
);
505 GetFontData(hdc
, GDEF_TAG
, 0, GDEF_Table
, length
);
506 TRACE("Loaded GDEF table of %i bytes\n",length
);
511 void OpenType_GDEF_UpdateGlyphProps(HDC hdc
, ScriptCache
*psc
, const WORD
*pwGlyphs
, const WORD cGlyphs
, WORD
* pwLogClust
, const WORD cChars
, SCRIPT_GLYPHPROP
*pGlyphProp
)
515 if (!psc
->GDEF_Table
)
516 psc
->GDEF_Table
= load_gdef_table(hdc
);
518 for (i
= 0; i
< cGlyphs
; i
++)
524 k
= USP10_FindGlyphInLogClust(pwLogClust
, cChars
, i
);
527 for (; k
< cChars
&& pwLogClust
[k
] == i
; k
++)
531 class = GDEF_get_glyph_class(psc
->GDEF_Table
, pwGlyphs
[i
]);
537 pGlyphProp
[i
].sva
.fClusterStart
= 1;
538 pGlyphProp
[i
].sva
.fDiacritic
= 0;
539 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
542 pGlyphProp
[i
].sva
.fClusterStart
= 1;
543 pGlyphProp
[i
].sva
.fDiacritic
= 0;
544 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
547 pGlyphProp
[i
].sva
.fClusterStart
= 0;
548 pGlyphProp
[i
].sva
.fDiacritic
= 1;
549 pGlyphProp
[i
].sva
.fZeroWidth
= 1;
552 pGlyphProp
[i
].sva
.fClusterStart
= 0;
553 pGlyphProp
[i
].sva
.fDiacritic
= 0;
554 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
557 ERR("Unknown glyph class %i\n",class);
558 pGlyphProp
[i
].sva
.fClusterStart
= 1;
559 pGlyphProp
[i
].sva
.fDiacritic
= 0;
560 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
564 pGlyphProp
[i
].sva
.fClusterStart
= 0;
571 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
);
573 static INT
GSUB_is_glyph_covered(LPCVOID table
, UINT glyph
)
575 const OT_CoverageFormat1
* cf1
;
579 if (GET_BE_WORD(cf1
->CoverageFormat
) == 1)
581 int count
= GET_BE_WORD(cf1
->GlyphCount
);
583 TRACE("Coverage Format 1, %i glyphs\n",count
);
584 for (i
= 0; i
< count
; i
++)
585 if (glyph
== GET_BE_WORD(cf1
->GlyphArray
[i
]))
589 else if (GET_BE_WORD(cf1
->CoverageFormat
) == 2)
591 const OT_CoverageFormat2
* cf2
;
594 cf2
= (const OT_CoverageFormat2
*)cf1
;
596 count
= GET_BE_WORD(cf2
->RangeCount
);
597 TRACE("Coverage Format 2, %i ranges\n",count
);
598 for (i
= 0; i
< count
; i
++)
600 if (glyph
< GET_BE_WORD(cf2
->RangeRecord
[i
].Start
))
602 if ((glyph
>= GET_BE_WORD(cf2
->RangeRecord
[i
].Start
)) &&
603 (glyph
<= GET_BE_WORD(cf2
->RangeRecord
[i
].End
)))
605 return (GET_BE_WORD(cf2
->RangeRecord
[i
].StartCoverageIndex
) +
606 glyph
- GET_BE_WORD(cf2
->RangeRecord
[i
].Start
));
612 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1
->CoverageFormat
));
617 static INT
GSUB_apply_SingleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
620 TRACE("Single Substitution Subtable\n");
622 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
625 const GSUB_SingleSubstFormat1
*ssf1
;
626 offset
= GET_BE_WORD(look
->SubTable
[j
]);
627 ssf1
= (const GSUB_SingleSubstFormat1
*)((const BYTE
*)look
+offset
);
628 if (GET_BE_WORD(ssf1
->SubstFormat
) == 1)
630 int offset
= GET_BE_WORD(ssf1
->Coverage
);
631 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1
->DeltaGlyphID
));
632 if (GSUB_is_glyph_covered((const BYTE
*)ssf1
+offset
, glyphs
[glyph_index
]) != -1)
634 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
635 glyphs
[glyph_index
] = glyphs
[glyph_index
] + GET_BE_WORD(ssf1
->DeltaGlyphID
);
636 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
637 return glyph_index
+ write_dir
;
642 const GSUB_SingleSubstFormat2
*ssf2
;
646 ssf2
= (const GSUB_SingleSubstFormat2
*)ssf1
;
647 offset
= GET_BE_WORD(ssf1
->Coverage
);
648 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2
->GlyphCount
));
649 index
= GSUB_is_glyph_covered((const BYTE
*)ssf2
+offset
, glyphs
[glyph_index
]);
650 TRACE(" Coverage index %i\n",index
);
653 if (glyphs
[glyph_index
] == GET_BE_WORD(ssf2
->Substitute
[index
]))
654 return GSUB_E_NOGLYPH
;
656 TRACE(" Glyph is 0x%x ->",glyphs
[glyph_index
]);
657 glyphs
[glyph_index
] = GET_BE_WORD(ssf2
->Substitute
[index
]);
658 TRACE("0x%x\n",glyphs
[glyph_index
]);
659 return glyph_index
+ write_dir
;
663 return GSUB_E_NOGLYPH
;
666 static INT
GSUB_apply_MultipleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
669 TRACE("Multiple Substitution Subtable\n");
671 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
674 const GSUB_MultipleSubstFormat1
*msf1
;
675 offset
= GET_BE_WORD(look
->SubTable
[j
]);
676 msf1
= (const GSUB_MultipleSubstFormat1
*)((const BYTE
*)look
+offset
);
678 offset
= GET_BE_WORD(msf1
->Coverage
);
679 index
= GSUB_is_glyph_covered((const BYTE
*)msf1
+offset
, glyphs
[glyph_index
]);
682 const GSUB_Sequence
*seq
;
685 offset
= GET_BE_WORD(msf1
->Sequence
[index
]);
686 seq
= (const GSUB_Sequence
*)((const BYTE
*)msf1
+offset
);
687 sub_count
= GET_BE_WORD(seq
->GlyphCount
);
688 TRACE(" Glyph 0x%x (+%i)->",glyphs
[glyph_index
],(sub_count
-1));
690 for (j
= (*glyph_count
)+(sub_count
-1); j
> glyph_index
; j
--)
691 glyphs
[j
] =glyphs
[j
-(sub_count
-1)];
693 for (j
= 0; j
< sub_count
; j
++)
695 glyphs
[glyph_index
+ (sub_count
-1) - j
] = GET_BE_WORD(seq
->Substitute
[j
]);
697 glyphs
[glyph_index
+ j
] = GET_BE_WORD(seq
->Substitute
[j
]);
699 *glyph_count
= *glyph_count
+ (sub_count
- 1);
701 if (TRACE_ON(uniscribe
))
703 for (j
= 0; j
< sub_count
; j
++)
704 TRACE(" 0x%x",glyphs
[glyph_index
+j
]);
708 return glyph_index
+ (sub_count
* write_dir
);
711 return GSUB_E_NOGLYPH
;
714 static INT
GSUB_apply_AlternateSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
717 TRACE("Alternate Substitution Subtable\n");
719 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
722 const GSUB_AlternateSubstFormat1
*asf1
;
725 offset
= GET_BE_WORD(look
->SubTable
[j
]);
726 asf1
= (const GSUB_AlternateSubstFormat1
*)((const BYTE
*)look
+offset
);
727 offset
= GET_BE_WORD(asf1
->Coverage
);
729 index
= GSUB_is_glyph_covered((const BYTE
*)asf1
+offset
, glyphs
[glyph_index
]);
732 const GSUB_AlternateSet
*as
;
733 offset
= GET_BE_WORD(asf1
->AlternateSet
[index
]);
734 as
= (const GSUB_AlternateSet
*)((const BYTE
*)asf1
+offset
);
735 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as
->GlyphCount
));
736 if (glyphs
[glyph_index
] == GET_BE_WORD(as
->Alternate
[0]))
737 return GSUB_E_NOGLYPH
;
739 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
740 glyphs
[glyph_index
] = GET_BE_WORD(as
->Alternate
[0]);
741 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
742 return glyph_index
+ write_dir
;
745 return GSUB_E_NOGLYPH
;
748 static INT
GSUB_apply_LigatureSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
752 TRACE("Ligature Substitution Subtable\n");
753 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
755 const GSUB_LigatureSubstFormat1
*lsf1
;
758 offset
= GET_BE_WORD(look
->SubTable
[j
]);
759 lsf1
= (const GSUB_LigatureSubstFormat1
*)((const BYTE
*)look
+offset
);
760 offset
= GET_BE_WORD(lsf1
->Coverage
);
761 index
= GSUB_is_glyph_covered((const BYTE
*)lsf1
+offset
, glyphs
[glyph_index
]);
762 TRACE(" Coverage index %i\n",index
);
765 const GSUB_LigatureSet
*ls
;
768 offset
= GET_BE_WORD(lsf1
->LigatureSet
[index
]);
769 ls
= (const GSUB_LigatureSet
*)((const BYTE
*)lsf1
+offset
);
770 count
= GET_BE_WORD(ls
->LigatureCount
);
771 TRACE(" LigatureSet has %i members\n",count
);
772 for (k
= 0; k
< count
; k
++)
774 const GSUB_Ligature
*lig
;
775 int CompCount
,l
,CompIndex
;
777 offset
= GET_BE_WORD(ls
->Ligature
[k
]);
778 lig
= (const GSUB_Ligature
*)((const BYTE
*)ls
+offset
);
779 CompCount
= GET_BE_WORD(lig
->CompCount
) - 1;
780 CompIndex
= glyph_index
+write_dir
;
781 for (l
= 0; l
< CompCount
&& CompIndex
>= 0 && CompIndex
< *glyph_count
; l
++)
784 CompGlyph
= GET_BE_WORD(lig
->Component
[l
]);
785 if (CompGlyph
!= glyphs
[CompIndex
])
787 CompIndex
+= write_dir
;
791 int replaceIdx
= glyph_index
;
793 replaceIdx
= glyph_index
- CompCount
;
795 TRACE(" Glyph is 0x%x (+%i) ->",glyphs
[glyph_index
],CompCount
);
796 glyphs
[replaceIdx
] = GET_BE_WORD(lig
->LigGlyph
);
797 TRACE("0x%x\n",glyphs
[replaceIdx
]);
801 for (j
= replaceIdx
+ 1; j
< *glyph_count
; j
++)
802 glyphs
[j
] =glyphs
[j
+CompCount
];
803 *glyph_count
= *glyph_count
- CompCount
;
805 return replaceIdx
+ write_dir
;
810 return GSUB_E_NOGLYPH
;
813 static INT
GSUB_apply_ChainContextSubst(const OT_LookupList
* lookup
, const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
818 TRACE("Chaining Contextual Substitution Subtable\n");
819 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
) && !done
; j
++)
821 const GSUB_ChainContextSubstFormat1
*ccsf1
;
823 int dirLookahead
= write_dir
;
824 int dirBacktrack
= -1 * write_dir
;
826 offset
= GET_BE_WORD(look
->SubTable
[j
]);
827 ccsf1
= (const GSUB_ChainContextSubstFormat1
*)((const BYTE
*)look
+offset
);
828 if (GET_BE_WORD(ccsf1
->SubstFormat
) == 1)
830 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
833 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 2)
835 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
838 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 3)
842 const GSUB_ChainContextSubstFormat3_1
*ccsf3_1
;
843 const GSUB_ChainContextSubstFormat3_2
*ccsf3_2
;
844 const GSUB_ChainContextSubstFormat3_3
*ccsf3_3
;
845 const GSUB_ChainContextSubstFormat3_4
*ccsf3_4
;
846 int newIndex
= glyph_index
;
848 ccsf3_1
= (const GSUB_ChainContextSubstFormat3_1
*)ccsf1
;
850 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
852 for (k
= 0; k
< GET_BE_WORD(ccsf3_1
->BacktrackGlyphCount
); k
++)
854 offset
= GET_BE_WORD(ccsf3_1
->Coverage
[k
]);
855 if (GSUB_is_glyph_covered((const BYTE
*)ccsf3_1
+offset
, glyphs
[glyph_index
+ (dirBacktrack
* (k
+1))]) == -1)
858 if (k
!= GET_BE_WORD(ccsf3_1
->BacktrackGlyphCount
))
860 TRACE("Matched Backtrack\n");
862 ccsf3_2
= (const GSUB_ChainContextSubstFormat3_2
*)(((LPBYTE
)ccsf1
)+sizeof(GSUB_ChainContextSubstFormat3_1
) + (sizeof(WORD
) * (GET_BE_WORD(ccsf3_1
->BacktrackGlyphCount
)-1)));
864 indexGlyphs
= GET_BE_WORD(ccsf3_2
->InputGlyphCount
);
865 for (k
= 0; k
< indexGlyphs
; k
++)
867 offset
= GET_BE_WORD(ccsf3_2
->Coverage
[k
]);
868 if (GSUB_is_glyph_covered((const BYTE
*)ccsf3_1
+offset
, glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
871 if (k
!= indexGlyphs
)
873 TRACE("Matched IndexGlyphs\n");
875 ccsf3_3
= (const GSUB_ChainContextSubstFormat3_3
*)(((LPBYTE
)ccsf3_2
)+sizeof(GSUB_ChainContextSubstFormat3_2
) + (sizeof(WORD
) * (GET_BE_WORD(ccsf3_2
->InputGlyphCount
)-1)));
877 for (k
= 0; k
< GET_BE_WORD(ccsf3_3
->LookaheadGlyphCount
); k
++)
879 offset
= GET_BE_WORD(ccsf3_3
->Coverage
[k
]);
880 if (GSUB_is_glyph_covered((const BYTE
*)ccsf3_1
+offset
, glyphs
[glyph_index
+ (dirLookahead
* (indexGlyphs
+ k
))]) == -1)
883 if (k
!= GET_BE_WORD(ccsf3_3
->LookaheadGlyphCount
))
885 TRACE("Matched LookAhead\n");
887 ccsf3_4
= (const GSUB_ChainContextSubstFormat3_4
*)(((LPBYTE
)ccsf3_3
)+sizeof(GSUB_ChainContextSubstFormat3_3
) + (sizeof(WORD
) * (GET_BE_WORD(ccsf3_3
->LookaheadGlyphCount
)-1)));
889 if (GET_BE_WORD(ccsf3_4
->SubstCount
))
891 for (k
= 0; k
< GET_BE_WORD(ccsf3_4
->SubstCount
); k
++)
893 int lookupIndex
= GET_BE_WORD(ccsf3_4
->SubstLookupRecord
[k
].LookupListIndex
);
894 int SequenceIndex
= GET_BE_WORD(ccsf3_4
->SubstLookupRecord
[k
].SequenceIndex
) * write_dir
;
896 TRACE("SUBST: %i -> %i %i\n",k
, SequenceIndex
, lookupIndex
);
897 newIndex
= GSUB_apply_lookup(lookup
, lookupIndex
, glyphs
, glyph_index
+ SequenceIndex
, write_dir
, glyph_count
);
900 ERR("Chain failed to generate a glyph\n");
906 else return GSUB_E_NOGLYPH
;
912 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
915 const OT_LookupTable
*look
;
917 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
918 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
919 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look
->LookupType
),GET_BE_WORD(look
->LookupFlag
),GET_BE_WORD(look
->SubTableCount
));
920 switch(GET_BE_WORD(look
->LookupType
))
923 return GSUB_apply_SingleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
925 return GSUB_apply_MultipleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
927 return GSUB_apply_AlternateSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
929 return GSUB_apply_LigatureSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
931 return GSUB_apply_ChainContextSubst(lookup
, look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
933 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look
->LookupType
));
935 return GSUB_E_NOGLYPH
;
938 INT
OpenType_apply_GSUB_lookup(LPCVOID table
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
940 const GSUB_Header
*header
= (const GSUB_Header
*)table
;
941 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
943 return GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, glyph_index
, write_dir
, glyph_count
);
949 static INT
GPOS_get_device_table_value(const OT_DeviceTable
*DeviceTable
, WORD ppem
)
951 static const WORD mask
[3] = {3,0xf,0xff};
952 if (DeviceTable
&& ppem
>= GET_BE_WORD(DeviceTable
->StartSize
) && ppem
<= GET_BE_WORD(DeviceTable
->EndSize
))
954 int format
= GET_BE_WORD(DeviceTable
->DeltaFormat
);
955 int index
= ppem
- GET_BE_WORD(DeviceTable
->StartSize
);
957 TRACE("device table, format %i, index %i\n",format
, index
);
958 index
= index
<< format
;
959 value
= (DeviceTable
->DeltaValue
[index
/sizeof(WORD
)] << (index
%sizeof(WORD
)))&mask
[format
-1];
960 TRACE("offset %i, value %i\n",index
, value
);
961 if (value
> mask
[format
-1]/2)
962 value
= -1 * ((mask
[format
-1]+1) - value
);
968 static VOID
GPOS_get_anchor_values(LPCVOID table
, LPPOINT pt
, WORD ppem
)
970 const GPOS_AnchorFormat1
* anchor1
= (const GPOS_AnchorFormat1
*)table
;
972 switch (GET_BE_WORD(anchor1
->AnchorFormat
))
976 TRACE("Anchor Format 1\n");
977 pt
->x
= (short)GET_BE_WORD(anchor1
->XCoordinate
);
978 pt
->y
= (short)GET_BE_WORD(anchor1
->YCoordinate
);
983 const GPOS_AnchorFormat2
* anchor2
= (const GPOS_AnchorFormat2
*)table
;
984 TRACE("Anchor Format 2\n");
985 pt
->x
= (short)GET_BE_WORD(anchor2
->XCoordinate
);
986 pt
->y
= (short)GET_BE_WORD(anchor2
->YCoordinate
);
992 const GPOS_AnchorFormat3
* anchor3
= (const GPOS_AnchorFormat3
*)table
;
993 TRACE("Anchor Format 3\n");
994 pt
->x
= (short)GET_BE_WORD(anchor3
->XCoordinate
);
995 pt
->y
= (short)GET_BE_WORD(anchor3
->YCoordinate
);
996 offset
= GET_BE_WORD(anchor3
->XDeviceTable
);
997 TRACE("ppem %i\n",ppem
);
1000 const OT_DeviceTable
* DeviceTableX
= NULL
;
1001 DeviceTableX
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1002 pt
->x
+= GPOS_get_device_table_value(DeviceTableX
, ppem
);
1004 offset
= GET_BE_WORD(anchor3
->YDeviceTable
);
1007 const OT_DeviceTable
* DeviceTableY
= NULL
;
1008 DeviceTableY
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1009 pt
->y
+= GPOS_get_device_table_value(DeviceTableY
, ppem
);
1014 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1
->AnchorFormat
));
1020 static void GPOS_convert_design_units_to_device(LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, int desX
, int desY
, double *devX
, double *devY
)
1022 int emHeight
= lpotm
->otmTextMetrics
.tmAscent
+ lpotm
->otmTextMetrics
.tmDescent
- lpotm
->otmTextMetrics
.tmInternalLeading
;
1024 TRACE("emHeight %i lfWidth %i\n",emHeight
, lplogfont
->lfWidth
);
1025 *devX
= (desX
* emHeight
) / (double)lpotm
->otmEMSquare
;
1026 *devY
= (desY
* emHeight
) / (double)lpotm
->otmEMSquare
;
1027 if (lplogfont
->lfWidth
)
1028 FIXME("Font with lfWidth set no handled properly\n");
1031 static INT
GPOS_get_value_record(WORD ValueFormat
, const WORD data
[], GPOS_ValueRecord
*record
)
1034 if (ValueFormat
& 0x0001) record
->XPlacement
= GET_BE_WORD(data
[offset
++]);
1035 if (ValueFormat
& 0x0002) record
->YPlacement
= GET_BE_WORD(data
[offset
++]);
1036 if (ValueFormat
& 0x0004) record
->XAdvance
= GET_BE_WORD(data
[offset
++]);
1037 if (ValueFormat
& 0x0008) record
->YAdvance
= GET_BE_WORD(data
[offset
++]);
1038 if (ValueFormat
& 0x0010) record
->XPlaDevice
= GET_BE_WORD(data
[offset
++]);
1039 if (ValueFormat
& 0x0020) record
->YPlaDevice
= GET_BE_WORD(data
[offset
++]);
1040 if (ValueFormat
& 0x0040) record
->XAdvDevice
= GET_BE_WORD(data
[offset
++]);
1041 if (ValueFormat
& 0x0080) record
->YAdvDevice
= GET_BE_WORD(data
[offset
++]);
1045 static VOID
GPOS_get_value_record_offsets(const BYTE
* head
, GPOS_ValueRecord
*ValueRecord
, WORD ValueFormat
, INT ppem
, LPPOINT ptPlacement
, LPPOINT ptAdvance
)
1047 if (ValueFormat
& 0x0001) ptPlacement
->x
+= (short)ValueRecord
->XPlacement
;
1048 if (ValueFormat
& 0x0002) ptPlacement
->y
+= (short)ValueRecord
->YPlacement
;
1049 if (ValueFormat
& 0x0004) ptAdvance
->x
+= (short)ValueRecord
->XAdvance
;
1050 if (ValueFormat
& 0x0008) ptAdvance
->y
+= (short)ValueRecord
->YAdvance
;
1051 if (ValueFormat
& 0x0010) ptPlacement
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XPlaDevice
), ppem
);
1052 if (ValueFormat
& 0x0020) ptPlacement
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YPlaDevice
), ppem
);
1053 if (ValueFormat
& 0x0040) ptAdvance
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XAdvDevice
), ppem
);
1054 if (ValueFormat
& 0x0080) ptAdvance
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YAdvDevice
), ppem
);
1055 if (ValueFormat
& 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat
&0xFF00);
1058 static VOID
GPOS_apply_SingleAdjustment(const OT_LookupTable
*look
, const WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT glyph_count
, INT ppem
, LPPOINT ptAdjust
, LPPOINT ptAdvance
)
1062 TRACE("Single Adjustment Positioning Subtable\n");
1064 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1066 const GPOS_SinglePosFormat1
*spf1
;
1067 WORD offset
= GET_BE_WORD(look
->SubTable
[j
]);
1068 spf1
= (const GPOS_SinglePosFormat1
*)((const BYTE
*)look
+offset
);
1069 if (GET_BE_WORD(spf1
->PosFormat
== 1))
1071 offset
= GET_BE_WORD(spf1
->Coverage
);
1072 if (GSUB_is_glyph_covered((const BYTE
*)spf1
+offset
, glyphs
[glyph_index
]) != -1)
1074 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1075 WORD ValueFormat
= GET_BE_WORD(spf1
->ValueFormat
);
1076 GPOS_get_value_record(ValueFormat
, spf1
->Value
, &ValueRecord
);
1077 GPOS_get_value_record_offsets((const BYTE
*)spf1
, &ValueRecord
, ValueFormat
, ppem
, ptAdjust
, ptAdvance
);
1078 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1082 FIXME("Single Adjustment Positioning: Format 2 Unhandled\n");
1086 static VOID
GPOS_apply_MarkToBase(const OT_LookupTable
*look
, const WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT glyph_count
, INT ppem
, LPPOINT pt
)
1090 TRACE("MarkToBase Attachment Positioning Subtable\n");
1092 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1095 const GPOS_MarkBasePosFormat1
*mbpf1
;
1096 offset
= GET_BE_WORD(look
->SubTable
[j
]);
1097 mbpf1
= (const GPOS_MarkBasePosFormat1
*)((const BYTE
*)look
+offset
);
1098 if (GET_BE_WORD(mbpf1
->PosFormat
) == 1)
1100 int offset
= GET_BE_WORD(mbpf1
->MarkCoverage
);
1102 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[glyph_index
]);
1103 if (mark_index
!= -1)
1106 offset
= GET_BE_WORD(mbpf1
->BaseCoverage
);
1107 base_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
1108 if (base_index
!= -1)
1110 const GPOS_MarkArray
*ma
;
1111 const GPOS_MarkRecord
*mr
;
1112 const GPOS_BaseArray
*ba
;
1113 const GPOS_BaseRecord
*br
;
1115 int class_count
= GET_BE_WORD(mbpf1
->ClassCount
);
1116 int baserecord_size
;
1119 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], base_index
);
1120 offset
= GET_BE_WORD(mbpf1
->MarkArray
);
1121 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mbpf1
+ offset
);
1122 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
1124 ERR("Mark index exeeded mark count\n");
1127 mr
= &ma
->MarkRecord
[mark_index
];
1128 mark_class
= GET_BE_WORD(mr
->Class
);
1129 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
1130 offset
= GET_BE_WORD(mbpf1
->BaseArray
);
1131 ba
= (const GPOS_BaseArray
*)((const BYTE
*)mbpf1
+ offset
);
1132 baserecord_size
= class_count
* sizeof(WORD
);
1133 br
= (const GPOS_BaseRecord
*)((const BYTE
*)ba
+ sizeof(WORD
) + (baserecord_size
* base_index
));
1134 offset
= GET_BE_WORD(br
->BaseAnchor
[mark_class
]);
1135 GPOS_get_anchor_values((const BYTE
*)ba
+ offset
, &base_pt
, ppem
);
1136 offset
= GET_BE_WORD(mr
->MarkAnchor
);
1137 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
1138 TRACE("Offset on base is %i,%i design units\n",base_pt
.x
,base_pt
.y
);
1139 TRACE("Offset on mark is %i,%i design units\n",mark_pt
.x
, mark_pt
.y
);
1140 pt
->x
+= base_pt
.x
- mark_pt
.x
;
1141 pt
->y
+= base_pt
.y
- mark_pt
.y
;
1142 TRACE("Resulting cumulative offset is %i,%i design units\n",pt
->x
,pt
->y
);
1147 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1
->PosFormat
));
1151 static INT
GPOS_apply_lookup(LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, INT
* piAdvance
, const OT_LookupList
* lookup
, INT lookup_index
, const WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT glyph_count
, GOFFSET
*pGoffset
)
1154 const OT_LookupTable
*look
;
1155 int ppem
= lpotm
->otmTextMetrics
.tmAscent
+ lpotm
->otmTextMetrics
.tmDescent
- lpotm
->otmTextMetrics
.tmInternalLeading
;
1157 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
1158 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
1159 TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look
->LookupType
),GET_BE_WORD(look
->LookupFlag
),GET_BE_WORD(look
->SubTableCount
));
1160 switch(GET_BE_WORD(look
->LookupType
))
1165 POINT adjust
= {0,0};
1166 POINT advance
= {0,0};
1167 GPOS_apply_SingleAdjustment(look
, glyphs
, glyph_index
, write_dir
, glyph_count
, ppem
, &adjust
, &advance
);
1168 if (adjust
.x
|| adjust
.y
)
1170 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
.x
, adjust
.y
, &devX
, &devY
);
1171 pGoffset
[glyph_index
].du
+= (int)(devX
+0.5);
1172 pGoffset
[glyph_index
].dv
+= (int)(devY
+0.5);
1174 if (advance
.x
|| advance
.y
)
1176 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
.x
, advance
.y
, &devX
, &devY
);
1177 piAdvance
[glyph_index
] += (int)(devX
+0.5);
1179 FIXME("Unhandled adjustment to Y advancement\n");
1186 GPOS_apply_MarkToBase(look
, glyphs
, glyph_index
, write_dir
, glyph_count
, ppem
, &desU
);
1187 if (desU
.x
|| desU
.y
)
1189 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
1190 pGoffset
[glyph_index
].du
+= ((int)(devX
+0.5) - piAdvance
[glyph_index
-1]);
1191 pGoffset
[glyph_index
].dv
+= (int)(devY
+0.5);
1196 FIXME("We do not handle SubType %i\n",GET_BE_WORD(look
->LookupType
));
1198 return glyph_index
+1;
1201 INT
OpenType_apply_GPOS_lookup(LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, INT
* piAdvance
, LPCVOID table
, INT lookup_index
, const WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT glyph_count
, GOFFSET
*pGoffset
)
1203 const GPOS_Header
*header
= (const GPOS_Header
*)table
;
1204 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
1206 return GPOS_apply_lookup(lpotm
, lplogfont
, piAdvance
, lookup
, lookup_index
, glyphs
, glyph_index
, write_dir
, glyph_count
, pGoffset
);
1209 static void GSUB_initialize_script_cache(ScriptCache
*psc
)
1213 if (psc
->GSUB_Table
)
1215 const OT_ScriptList
*script
;
1216 const GSUB_Header
* header
= (const GSUB_Header
*)psc
->GSUB_Table
;
1217 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
1218 psc
->script_count
= GET_BE_WORD(script
->ScriptCount
);
1219 TRACE("initializing %i scripts in this font\n",psc
->script_count
);
1220 if (psc
->script_count
)
1222 psc
->scripts
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(LoadedScript
) * psc
->script_count
);
1223 for (i
= 0; i
< psc
->script_count
; i
++)
1225 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
1226 psc
->scripts
[i
].tag
= MS_MAKE_TAG(script
->ScriptRecord
[i
].ScriptTag
[0], script
->ScriptRecord
[i
].ScriptTag
[1], script
->ScriptRecord
[i
].ScriptTag
[2], script
->ScriptRecord
[i
].ScriptTag
[3]);
1227 psc
->scripts
[i
].gsub_table
= ((const BYTE
*)script
+ offset
);
1233 static void GPOS_expand_script_cache(ScriptCache
*psc
)
1236 const OT_ScriptList
*script
;
1237 const GPOS_Header
* header
= (const GPOS_Header
*)psc
->GPOS_Table
;
1242 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
1243 count
= GET_BE_WORD(script
->ScriptCount
);
1245 if (!psc
->script_count
)
1247 psc
->script_count
= count
;
1248 TRACE("initializing %i scripts in this font\n",psc
->script_count
);
1249 if (psc
->script_count
)
1251 psc
->scripts
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(LoadedScript
) * psc
->script_count
);
1252 for (i
= 0; i
< psc
->script_count
; i
++)
1254 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
1255 psc
->scripts
[i
].tag
= MS_MAKE_TAG(script
->ScriptRecord
[i
].ScriptTag
[0], script
->ScriptRecord
[i
].ScriptTag
[1], script
->ScriptRecord
[i
].ScriptTag
[2], script
->ScriptRecord
[i
].ScriptTag
[3]);
1256 psc
->scripts
[i
].gpos_table
= ((const BYTE
*)script
+ offset
);
1262 for (i
= 0; i
< count
; i
++)
1265 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
1266 OPENTYPE_TAG tag
= MS_MAKE_TAG(script
->ScriptRecord
[i
].ScriptTag
[0], script
->ScriptRecord
[i
].ScriptTag
[1], script
->ScriptRecord
[i
].ScriptTag
[2], script
->ScriptRecord
[i
].ScriptTag
[3]);
1267 for (j
= 0; j
< psc
->script_count
; j
++)
1269 if (psc
->scripts
[j
].tag
== tag
)
1271 psc
->scripts
[j
].gpos_table
= ((const BYTE
*)script
+ offset
);
1275 if (j
== psc
->script_count
)
1277 psc
->script_count
++;
1278 psc
->scripts
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,psc
->scripts
, sizeof(LoadedScript
) * psc
->script_count
);
1279 psc
->scripts
[j
].tag
= tag
;
1280 psc
->scripts
[j
].gpos_table
= ((const BYTE
*)script
+ offset
);
1286 static void _initialize_script_cache(ScriptCache
*psc
)
1288 if (!psc
->script_count
)
1290 GSUB_initialize_script_cache(psc
);
1291 GPOS_expand_script_cache(psc
);
1295 HRESULT
OpenType_GetFontScriptTags(ScriptCache
*psc
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pScriptTags
, int *pcTags
)
1300 _initialize_script_cache(psc
);
1302 *pcTags
= psc
->script_count
;
1304 if (!searchingFor
&& cMaxTags
< *pcTags
)
1306 else if (searchingFor
)
1307 rc
= USP_E_SCRIPT_NOT_IN_FONT
;
1309 for (i
= 0; i
< psc
->script_count
; i
++)
1312 pScriptTags
[i
] = psc
->scripts
[i
].tag
;
1316 if (searchingFor
== psc
->scripts
[i
].tag
)
1318 pScriptTags
[0] = psc
->scripts
[i
].tag
;
1328 static void GSUB_initialize_language_cache(LoadedScript
*script
)
1332 if (script
->gsub_table
)
1335 const OT_Script
* table
= script
->gsub_table
;
1336 script
->language_count
= GET_BE_WORD(table
->LangSysCount
);
1337 offset
= GET_BE_WORD(table
->DefaultLangSys
);
1340 script
->default_language
.tag
= MS_MAKE_TAG('d','f','l','t');
1341 script
->default_language
.gsub_table
= (const BYTE
*)table
+ offset
;
1344 if (script
->language_count
)
1346 TRACE("Deflang %p, LangCount %i\n",script
->default_language
.gsub_table
, script
->language_count
);
1348 script
->languages
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LoadedLanguage
) * script
->language_count
);
1350 for (i
= 0; i
< script
->language_count
; i
++)
1352 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
1353 script
->languages
[i
].tag
= MS_MAKE_TAG(table
->LangSysRecord
[i
].LangSysTag
[0], table
->LangSysRecord
[i
].LangSysTag
[1], table
->LangSysRecord
[i
].LangSysTag
[2], table
->LangSysRecord
[i
].LangSysTag
[3]);
1354 script
->languages
[i
].gsub_table
= ((const BYTE
*)table
+ offset
);
1360 static void GPOS_expand_language_cache(LoadedScript
*script
)
1363 const OT_Script
* table
= script
->gpos_table
;
1369 offset
= GET_BE_WORD(table
->DefaultLangSys
);
1371 script
->default_language
.gpos_table
= (const BYTE
*)table
+ offset
;
1373 count
= GET_BE_WORD(table
->LangSysCount
);
1375 TRACE("Deflang %p, LangCount %i\n",script
->default_language
.gpos_table
, count
);
1376 if (!script
->language_count
)
1379 script
->language_count
= count
;
1381 script
->languages
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LoadedLanguage
) * script
->language_count
);
1383 for (i
= 0; i
< script
->language_count
; i
++)
1385 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
1386 script
->languages
[i
].tag
= MS_MAKE_TAG(table
->LangSysRecord
[i
].LangSysTag
[0], table
->LangSysRecord
[i
].LangSysTag
[1], table
->LangSysRecord
[i
].LangSysTag
[2], table
->LangSysRecord
[i
].LangSysTag
[3]);
1387 script
->languages
[i
].gpos_table
= ((const BYTE
*)table
+ offset
);
1393 for (i
= 0; i
< count
; i
++)
1395 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
1396 OPENTYPE_TAG tag
= MS_MAKE_TAG(table
->LangSysRecord
[i
].LangSysTag
[0], table
->LangSysRecord
[i
].LangSysTag
[1], table
->LangSysRecord
[i
].LangSysTag
[2], table
->LangSysRecord
[i
].LangSysTag
[3]);
1398 for (j
= 0; j
< script
->language_count
; j
++)
1400 if (script
->languages
[j
].tag
== tag
)
1402 script
->languages
[j
].gpos_table
= ((const BYTE
*)table
+ offset
);
1406 if (j
== script
->language_count
)
1408 script
->language_count
++;
1409 script
->languages
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,script
->languages
, sizeof(LoadedLanguage
) * script
->language_count
);
1410 script
->languages
[j
].tag
= tag
;
1411 script
->languages
[j
].gpos_table
= ((const BYTE
*)table
+ offset
);
1417 static void _initialize_language_cache(LoadedScript
*script
)
1419 if (!script
->language_count
)
1421 GSUB_initialize_language_cache(script
);
1422 GPOS_expand_language_cache(script
);
1426 HRESULT
OpenType_GetFontLanguageTags(ScriptCache
*psc
, OPENTYPE_TAG script_tag
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pLanguageTags
, int *pcTags
)
1430 LoadedScript
*script
= NULL
;
1432 _initialize_script_cache(psc
);
1434 for (i
= 0; i
< psc
->script_count
; i
++)
1436 if (psc
->scripts
[i
].tag
== script_tag
)
1438 script
= &psc
->scripts
[i
];
1444 return E_INVALIDARG
;
1446 _initialize_language_cache(script
);
1448 if (!searchingFor
&& cMaxTags
< script
->language_count
)
1450 else if (searchingFor
)
1453 *pcTags
= script
->language_count
;
1455 for (i
= 0; i
< script
->language_count
; i
++)
1458 pLanguageTags
[i
] = script
->languages
[i
].tag
;
1462 if (searchingFor
== script
->languages
[i
].tag
)
1464 pLanguageTags
[0] = script
->languages
[i
].tag
;
1472 if (script
->default_language
.gsub_table
)
1475 pLanguageTags
[i
] = script
->default_language
.tag
;
1477 if (searchingFor
&& FAILED(rc
))
1479 pLanguageTags
[0] = script
->default_language
.tag
;
1482 *pcTags
= (*pcTags
) + 1;
1489 static void GSUB_initialize_feature_cache(LPCVOID table
, LoadedLanguage
*language
)
1493 if (language
->gsub_table
)
1495 const OT_LangSys
*lang
= language
->gsub_table
;
1496 const GSUB_Header
*header
= (const GSUB_Header
*)table
;
1497 const OT_FeatureList
*feature_list
;
1499 language
->feature_count
= GET_BE_WORD(lang
->FeatureCount
);
1500 TRACE("%i features\n",language
->feature_count
);
1502 if (language
->feature_count
)
1504 language
->features
= HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature
)*language
->feature_count
);
1506 feature_list
= (const OT_FeatureList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->FeatureList
));
1508 for (i
= 0; i
< language
->feature_count
; i
++)
1510 const OT_Feature
*feature
;
1512 int index
= GET_BE_WORD(lang
->FeatureIndex
[i
]);
1514 language
->features
[i
].tag
= MS_MAKE_TAG(feature_list
->FeatureRecord
[index
].FeatureTag
[0], feature_list
->FeatureRecord
[index
].FeatureTag
[1], feature_list
->FeatureRecord
[index
].FeatureTag
[2], feature_list
->FeatureRecord
[index
].FeatureTag
[3]);
1515 language
->features
[i
].feature
= ((const BYTE
*)feature_list
+ GET_BE_WORD(feature_list
->FeatureRecord
[index
].Feature
));
1516 feature
= (const OT_Feature
*)language
->features
[i
].feature
;
1517 language
->features
[i
].lookup_count
= GET_BE_WORD(feature
->LookupCount
);
1518 language
->features
[i
].lookups
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * language
->features
[i
].lookup_count
);
1519 for (j
= 0; j
< language
->features
[i
].lookup_count
; j
++)
1520 language
->features
[i
].lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
1526 static void GPOS_expand_feature_cache(LPCVOID table
, LoadedLanguage
*language
)
1529 const OT_LangSys
*lang
= language
->gpos_table
;
1530 const GPOS_Header
*header
= (const GPOS_Header
*)table
;
1531 const OT_FeatureList
*feature_list
;
1536 count
= GET_BE_WORD(lang
->FeatureCount
);
1537 feature_list
= (const OT_FeatureList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->FeatureList
));
1539 TRACE("%i features\n",count
);
1540 if (!language
->feature_count
)
1542 language
->feature_count
= count
;
1544 if (language
->feature_count
)
1546 language
->features
= HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature
)*language
->feature_count
);
1548 for (i
= 0; i
< language
->feature_count
; i
++)
1550 const OT_Feature
*feature
;
1552 int index
= GET_BE_WORD(lang
->FeatureIndex
[i
]);
1554 language
->features
[i
].tag
= MS_MAKE_TAG(feature_list
->FeatureRecord
[index
].FeatureTag
[0], feature_list
->FeatureRecord
[index
].FeatureTag
[1], feature_list
->FeatureRecord
[index
].FeatureTag
[2], feature_list
->FeatureRecord
[index
].FeatureTag
[3]);
1555 language
->features
[i
].feature
= ((const BYTE
*)feature_list
+ GET_BE_WORD(feature_list
->FeatureRecord
[index
].Feature
));
1556 feature
= (const OT_Feature
*)language
->features
[i
].feature
;
1557 language
->features
[i
].lookup_count
= GET_BE_WORD(feature
->LookupCount
);
1558 language
->features
[i
].lookups
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * language
->features
[i
].lookup_count
);
1559 for (j
= 0; j
< language
->features
[i
].lookup_count
; j
++)
1560 language
->features
[i
].lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
1566 language
->features
= HeapReAlloc(GetProcessHeap(),0,language
->features
, sizeof(LoadedFeature
)*(language
->feature_count
+ count
));
1568 for (i
= 0; i
< count
; i
++)
1570 const OT_Feature
*feature
;
1572 int index
= GET_BE_WORD(lang
->FeatureIndex
[i
]);
1573 int idx
= language
->feature_count
+ i
;
1575 language
->features
[idx
].tag
= MS_MAKE_TAG(feature_list
->FeatureRecord
[index
].FeatureTag
[0], feature_list
->FeatureRecord
[index
].FeatureTag
[1], feature_list
->FeatureRecord
[index
].FeatureTag
[2], feature_list
->FeatureRecord
[index
].FeatureTag
[3]);
1576 language
->features
[idx
].feature
= ((const BYTE
*)feature_list
+ GET_BE_WORD(feature_list
->FeatureRecord
[index
].Feature
));
1577 feature
= (const OT_Feature
*)language
->features
[idx
].feature
;
1578 language
->features
[idx
].lookup_count
= GET_BE_WORD(feature
->LookupCount
);
1579 language
->features
[idx
].lookups
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * language
->features
[idx
].lookup_count
);
1580 for (j
= 0; j
< language
->features
[idx
].lookup_count
; j
++)
1581 language
->features
[idx
].lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
1583 language
->feature_count
+= count
;
1587 static void _initialize_feature_cache(ScriptCache
*psc
, LoadedLanguage
*language
)
1589 if (!language
->feature_count
)
1591 GSUB_initialize_feature_cache(psc
->GSUB_Table
, language
);
1592 GPOS_expand_feature_cache(psc
->GPOS_Table
, language
);
1596 HRESULT
OpenType_GetFontFeatureTags(ScriptCache
*psc
, OPENTYPE_TAG script_tag
, OPENTYPE_TAG language_tag
, BOOL filtered
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pFeatureTags
, int *pcTags
, LoadedFeature
** feature
)
1600 LoadedScript
*script
= NULL
;
1601 LoadedLanguage
*language
= NULL
;
1603 _initialize_script_cache(psc
);
1605 for (i
= 0; i
< psc
->script_count
; i
++)
1607 if (psc
->scripts
[i
].tag
== script_tag
)
1609 script
= &psc
->scripts
[i
];
1620 return E_INVALIDARG
;
1623 _initialize_language_cache(script
);
1625 if ((script
->default_language
.gsub_table
|| script
->default_language
.gpos_table
) && script
->default_language
.tag
== language_tag
)
1626 language
= &script
->default_language
;
1629 for (i
= 0; i
< script
->language_count
; i
++)
1631 if (script
->languages
[i
].tag
== language_tag
)
1633 language
= &script
->languages
[i
];
1645 _initialize_feature_cache(psc
, language
);
1647 *pcTags
= language
->feature_count
;
1649 if (!searchingFor
&& cMaxTags
< *pcTags
)
1651 else if (searchingFor
)
1654 for (i
= 0; i
< language
->feature_count
; i
++)
1657 pFeatureTags
[i
] = language
->features
[i
].tag
;
1661 if (searchingFor
== language
->features
[i
].tag
)
1663 pFeatureTags
[0] = language
->features
[i
].tag
;
1666 *feature
= &language
->features
[i
];