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"
35 #include "wine/heap.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe
);
39 #ifdef WORDS_BIGENDIAN
40 #define GET_BE_WORD(x) (x)
41 #define GET_BE_DWORD(x) (x)
43 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
44 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
47 #define round(x) (((x) < 0) ? (int)((x) - 0.5) : (int)((x) + 0.5))
49 /* These are all structures needed for the cmap format 12 table */
50 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
54 GPOS_LOOKUP_ADJUST_SINGLE
= 0x1,
55 GPOS_LOOKUP_ADJUST_PAIR
= 0x2,
56 GPOS_LOOKUP_ATTACH_CURSIVE
= 0x3,
57 GPOS_LOOKUP_ATTACH_MARK_TO_BASE
= 0x4,
58 GPOS_LOOKUP_ATTACH_MARK_TO_LIGATURE
= 0x5,
59 GPOS_LOOKUP_ATTACH_MARK_TO_MARK
= 0x6,
60 GPOS_LOOKUP_POSITION_CONTEXT
= 0x7,
61 GPOS_LOOKUP_POSITION_CONTEXT_CHAINED
= 0x8,
62 GPOS_LOOKUP_POSITION_EXTENSION
= 0x9,
67 GSUB_LOOKUP_SINGLE
= 0x1,
68 GSUB_LOOKUP_MULTIPLE
= 0x2,
69 GSUB_LOOKUP_ALTERNATE
= 0x3,
70 GSUB_LOOKUP_LIGATURE
= 0x4,
71 GSUB_LOOKUP_CONTEXT
= 0x5,
72 GSUB_LOOKUP_CONTEXT_CHAINED
= 0x6,
73 GSUB_LOOKUP_EXTENSION
= 0x7,
74 GSUB_LOOKUP_CONTEXT_CHAINED_REVERSE
= 0x8,
81 } CMAP_EncodingRecord
;
86 CMAP_EncodingRecord tables
[1];
93 } CMAP_SegmentedCoverage_group
;
101 CMAP_SegmentedCoverage_group groups
[1];
102 } CMAP_SegmentedCoverage
;
104 /* These are all structures needed for the GDEF table */
105 enum {BaseGlyph
=1, LigatureGlyph
, MarkGlyph
, ComponentGlyph
};
112 WORD MarkAttachClassDef
;
119 WORD ClassValueArray
[1];
120 } OT_ClassDefFormat1
;
126 } OT_ClassRangeRecord
;
130 WORD ClassRangeCount
;
131 OT_ClassRangeRecord ClassRangeRecord
[1];
132 } OT_ClassDefFormat2
;
134 /* These are all structures needed for the GSUB table */
150 OT_ScriptRecord ScriptRecord
[1];
161 OT_LangSysRecord LangSysRecord
[1];
165 WORD LookupOrder
; /* Reserved */
166 WORD ReqFeatureIndex
;
168 WORD FeatureIndex
[1];
178 OT_FeatureRecord FeatureRecord
[1];
182 WORD FeatureParams
; /* Reserved */
184 WORD LookupListIndex
[1];
203 } OT_CoverageFormat1
;
208 WORD StartCoverageIndex
;
214 OT_RangeRecord RangeRecord
[1];
215 } OT_CoverageFormat2
;
218 WORD SubstFormat
; /* = 1 */
221 } GSUB_SingleSubstFormat1
;
224 WORD SubstFormat
; /* = 2 */
228 }GSUB_SingleSubstFormat2
;
231 WORD SubstFormat
; /* = 1 */
235 }GSUB_MultipleSubstFormat1
;
243 WORD SubstFormat
; /* = 1 */
247 }GSUB_LigatureSubstFormat1
;
262 WORD LookupListIndex
;
264 }GSUB_SubstLookupRecord
;
269 WORD SubRuleSetCount
;
271 }GSUB_ContextSubstFormat1
;
285 GSUB_SubstLookupRecord SubstLookupRecord
[1];
294 }GSUB_ContextSubstFormat2
;
297 WORD SubClassRuleCnt
;
298 WORD SubClassRule
[1];
305 }GSUB_SubClassRule_1
;
308 GSUB_SubstLookupRecord SubstLookupRecord
[1];
309 }GSUB_SubClassRule_2
;
312 WORD SubstFormat
; /* = 1 */
314 WORD ChainSubRuleSetCount
;
315 WORD ChainSubRuleSet
[1];
316 }GSUB_ChainContextSubstFormat1
;
319 WORD SubstFormat
; /* = 2 */
321 WORD BacktrackClassDef
;
323 WORD LookaheadClassDef
;
324 WORD ChainSubClassSetCnt
;
325 WORD ChainSubClassSet
[1];
326 }GSUB_ChainContextSubstFormat2
;
329 WORD ChainSubClassRuleCnt
;
330 WORD ChainSubClassRule
[1];
331 }GSUB_ChainSubClassSet
;
334 WORD BacktrackGlyphCount
;
336 }GSUB_ChainSubClassRule_1
;
339 WORD InputGlyphCount
;
341 }GSUB_ChainSubClassRule_2
;
344 WORD LookaheadGlyphCount
;
346 }GSUB_ChainSubClassRule_3
;
350 GSUB_SubstLookupRecord SubstLookupRecord
[1];
351 }GSUB_ChainSubClassRule_4
;
354 WORD SubstFormat
; /* = 3 */
355 WORD BacktrackGlyphCount
;
357 }GSUB_ChainContextSubstFormat3_1
;
360 WORD InputGlyphCount
;
362 }GSUB_ChainContextSubstFormat3_2
;
365 WORD LookaheadGlyphCount
;
367 }GSUB_ChainContextSubstFormat3_3
;
371 GSUB_SubstLookupRecord SubstLookupRecord
[1];
372 }GSUB_ChainContextSubstFormat3_4
;
375 WORD SubstFormat
; /* = 1 */
377 WORD AlternateSetCount
;
378 WORD AlternateSet
[1];
379 } GSUB_AlternateSubstFormat1
;
388 WORD ExtensionLookupType
;
389 DWORD ExtensionOffset
;
390 } GSUB_ExtensionPosFormat1
;
392 /* These are all structures needed for the GPOS table */
412 } GPOS_AnchorFormat1
;
419 } GPOS_AnchorFormat2
;
427 } GPOS_AnchorFormat3
;
445 } GPOS_SinglePosFormat1
;
453 } GPOS_SinglePosFormat2
;
461 WORD PairSetOffset
[1];
462 } GPOS_PairPosFormat1
;
473 WORD Class1Record
[1];
474 } GPOS_PairPosFormat2
;
480 } GPOS_PairValueRecord
;
484 GPOS_PairValueRecord PairValueRecord
[1];
490 } GPOS_EntryExitRecord
;
496 GPOS_EntryExitRecord EntryExitRecord
[1];
497 } GPOS_CursivePosFormat1
;
506 } GPOS_MarkBasePosFormat1
;
514 GPOS_BaseRecord BaseRecord
[1];
524 GPOS_MarkRecord MarkRecord
[1];
530 WORD LigatureCoverage
;
534 } GPOS_MarkLigPosFormat1
;
538 WORD LigatureAttach
[1];
539 } GPOS_LigatureArray
;
542 WORD LigatureAnchor
[1];
543 } GPOS_ComponentRecord
;
547 GPOS_ComponentRecord ComponentRecord
[1];
548 } GPOS_LigatureAttach
;
557 } GPOS_MarkMarkPosFormat1
;
565 GPOS_Mark2Record Mark2Record
[1];
570 WORD LookupListIndex
;
571 } GPOS_PosLookupRecord
;
579 } GPOS_ContextPosFormat2
;
582 WORD PosClassRuleCnt
;
583 WORD PosClassRule
[1];
590 } GPOS_PosClassRule_1
;
593 GPOS_PosLookupRecord PosLookupRecord
[1];
594 } GPOS_PosClassRule_2
;
598 WORD BacktrackGlyphCount
;
600 } GPOS_ChainContextPosFormat3_1
;
603 WORD InputGlyphCount
;
605 } GPOS_ChainContextPosFormat3_2
;
608 WORD LookaheadGlyphCount
;
610 } GPOS_ChainContextPosFormat3_3
;
614 GPOS_PosLookupRecord PosLookupRecord
[1];
615 } GPOS_ChainContextPosFormat3_4
;
619 WORD ExtensionLookupType
;
620 DWORD ExtensionOffset
;
621 } GPOS_ExtensionPosFormat1
;
627 static VOID
*load_CMAP_format12_table(HDC hdc
, ScriptCache
*psc
)
629 CMAP_Header
*CMAP_Table
= NULL
;
633 if (!psc
->CMAP_Table
)
635 length
= GetFontData(hdc
, CMAP_TAG
, 0, NULL
, 0);
636 if (length
!= GDI_ERROR
)
638 psc
->CMAP_Table
= heap_alloc(length
);
639 GetFontData(hdc
, CMAP_TAG
, 0, psc
->CMAP_Table
, length
);
640 TRACE("Loaded cmap table of %i bytes\n",length
);
646 CMAP_Table
= psc
->CMAP_Table
;
648 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
); i
++)
650 if ( (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) == 3) &&
651 (GET_BE_WORD(CMAP_Table
->tables
[i
].encodingID
) == 10) )
653 CMAP_SegmentedCoverage
*format
= (CMAP_SegmentedCoverage
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
654 if (GET_BE_WORD(format
->format
) == 12)
661 static int compare_group(const void *a
, const void* b
)
663 const DWORD
*chr
= a
;
664 const CMAP_SegmentedCoverage_group
*group
= b
;
666 if (*chr
< GET_BE_DWORD(group
->startCharCode
))
668 if (*chr
> GET_BE_DWORD(group
->endCharCode
))
673 DWORD
OpenType_CMAP_GetGlyphIndex(HDC hdc
, ScriptCache
*psc
, DWORD utf32c
, WORD
*glyph_index
, DWORD flags
)
675 /* BMP: use gdi32 for ease */
676 if (utf32c
< 0x10000)
679 return GetGlyphIndicesW(hdc
, &ch
, 1, glyph_index
, flags
);
682 if (!psc
->CMAP_format12_Table
)
683 psc
->CMAP_format12_Table
= load_CMAP_format12_table(hdc
, psc
);
685 if (flags
& GGI_MARK_NONEXISTING_GLYPHS
)
686 *glyph_index
= 0xffffu
;
690 if (psc
->CMAP_format12_Table
)
692 CMAP_SegmentedCoverage
*format
= NULL
;
693 CMAP_SegmentedCoverage_group
*group
= NULL
;
695 format
= (CMAP_SegmentedCoverage
*)psc
->CMAP_format12_Table
;
697 group
= bsearch(&utf32c
, format
->groups
, GET_BE_DWORD(format
->nGroups
),
698 sizeof(CMAP_SegmentedCoverage_group
), compare_group
);
702 DWORD offset
= utf32c
- GET_BE_DWORD(group
->startCharCode
);
703 *glyph_index
= GET_BE_DWORD(group
->startGlyphID
) + offset
;
714 static WORD
OT_get_glyph_class(const void *table
, WORD glyph
)
717 const OT_ClassDefFormat1
*cf1
= table
;
719 if (!table
) return 0;
721 if (GET_BE_WORD(cf1
->ClassFormat
) == 1)
723 if (glyph
>= GET_BE_WORD(cf1
->StartGlyph
))
725 int index
= glyph
- GET_BE_WORD(cf1
->StartGlyph
);
726 if (index
< GET_BE_WORD(cf1
->GlyphCount
))
727 class = GET_BE_WORD(cf1
->ClassValueArray
[index
]);
730 else if (GET_BE_WORD(cf1
->ClassFormat
) == 2)
732 const OT_ClassDefFormat2
*cf2
= table
;
734 top
= GET_BE_WORD(cf2
->ClassRangeCount
);
735 for (i
= 0; i
< top
; i
++)
737 if (glyph
>= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Start
) &&
738 glyph
<= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].End
))
740 class = GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Class
);
746 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1
->ClassFormat
));
751 void OpenType_GDEF_UpdateGlyphProps(ScriptCache
*psc
, const WORD
*pwGlyphs
, const WORD cGlyphs
, WORD
* pwLogClust
, const WORD cChars
, SCRIPT_GLYPHPROP
*pGlyphProp
)
754 void *glyph_class_table
= NULL
;
758 const GDEF_Header
*header
= psc
->GDEF_Table
;
759 WORD offset
= GET_BE_WORD( header
->GlyphClassDef
);
761 glyph_class_table
= (BYTE
*)psc
->GDEF_Table
+ offset
;
764 for (i
= 0; i
< cGlyphs
; i
++)
770 k
= USP10_FindGlyphInLogClust(pwLogClust
, cChars
, i
);
773 for (; k
< cChars
&& pwLogClust
[k
] == i
; k
++)
777 class = OT_get_glyph_class( glyph_class_table
, pwGlyphs
[i
] );
783 pGlyphProp
[i
].sva
.fClusterStart
= 1;
784 pGlyphProp
[i
].sva
.fDiacritic
= 0;
785 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
788 pGlyphProp
[i
].sva
.fClusterStart
= 1;
789 pGlyphProp
[i
].sva
.fDiacritic
= 0;
790 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
793 pGlyphProp
[i
].sva
.fClusterStart
= 0;
794 pGlyphProp
[i
].sva
.fDiacritic
= 1;
795 pGlyphProp
[i
].sva
.fZeroWidth
= 1;
798 pGlyphProp
[i
].sva
.fClusterStart
= 0;
799 pGlyphProp
[i
].sva
.fDiacritic
= 0;
800 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
803 ERR("Unknown glyph class %i\n",class);
804 pGlyphProp
[i
].sva
.fClusterStart
= 1;
805 pGlyphProp
[i
].sva
.fDiacritic
= 0;
806 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
810 pGlyphProp
[i
].sva
.fClusterStart
= 0;
817 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
);
819 static int GSUB_is_glyph_covered(const void *table
, unsigned int glyph
)
821 const OT_CoverageFormat1
* cf1
;
825 if (GET_BE_WORD(cf1
->CoverageFormat
) == 1)
827 int count
= GET_BE_WORD(cf1
->GlyphCount
);
829 TRACE("Coverage Format 1, %i glyphs\n",count
);
830 for (i
= 0; i
< count
; i
++)
831 if (glyph
== GET_BE_WORD(cf1
->GlyphArray
[i
]))
835 else if (GET_BE_WORD(cf1
->CoverageFormat
) == 2)
837 const OT_CoverageFormat2
* cf2
;
840 cf2
= (const OT_CoverageFormat2
*)cf1
;
842 count
= GET_BE_WORD(cf2
->RangeCount
);
843 TRACE("Coverage Format 2, %i ranges\n",count
);
844 for (i
= 0; i
< count
; i
++)
846 if (glyph
< GET_BE_WORD(cf2
->RangeRecord
[i
].Start
))
848 if ((glyph
>= GET_BE_WORD(cf2
->RangeRecord
[i
].Start
)) &&
849 (glyph
<= GET_BE_WORD(cf2
->RangeRecord
[i
].End
)))
851 return (GET_BE_WORD(cf2
->RangeRecord
[i
].StartCoverageIndex
) +
852 glyph
- GET_BE_WORD(cf2
->RangeRecord
[i
].Start
));
858 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1
->CoverageFormat
));
863 static const BYTE
*GSUB_get_subtable(const OT_LookupTable
*look
, int index
)
865 int offset
= GET_BE_WORD(look
->SubTable
[index
]);
867 if (GET_BE_WORD(look
->LookupType
) == GSUB_LOOKUP_EXTENSION
)
869 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)look
+ offset
);
870 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
872 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
876 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext
->SubstFormat
));
879 return (const BYTE
*)look
+ offset
;
882 static INT
GSUB_apply_SingleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
885 TRACE("Single Substitution Subtable\n");
887 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
889 const GSUB_SingleSubstFormat1
*ssf1
= (const GSUB_SingleSubstFormat1
*)GSUB_get_subtable(look
, j
);
890 if (GET_BE_WORD(ssf1
->SubstFormat
) == 1)
892 int offset
= GET_BE_WORD(ssf1
->Coverage
);
893 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1
->DeltaGlyphID
));
894 if (GSUB_is_glyph_covered((const BYTE
*)ssf1
+offset
, glyphs
[glyph_index
]) != -1)
896 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
897 glyphs
[glyph_index
] = glyphs
[glyph_index
] + GET_BE_WORD(ssf1
->DeltaGlyphID
);
898 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
899 return glyph_index
+ write_dir
;
904 const GSUB_SingleSubstFormat2
*ssf2
;
908 ssf2
= (const GSUB_SingleSubstFormat2
*)ssf1
;
909 offset
= GET_BE_WORD(ssf1
->Coverage
);
910 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2
->GlyphCount
));
911 index
= GSUB_is_glyph_covered((const BYTE
*)ssf2
+offset
, glyphs
[glyph_index
]);
912 TRACE(" Coverage index %i\n",index
);
915 if (glyphs
[glyph_index
] == GET_BE_WORD(ssf2
->Substitute
[index
]))
916 return GSUB_E_NOGLYPH
;
918 TRACE(" Glyph is 0x%x ->",glyphs
[glyph_index
]);
919 glyphs
[glyph_index
] = GET_BE_WORD(ssf2
->Substitute
[index
]);
920 TRACE("0x%x\n",glyphs
[glyph_index
]);
921 return glyph_index
+ write_dir
;
925 return GSUB_E_NOGLYPH
;
928 static INT
GSUB_apply_MultipleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
931 TRACE("Multiple Substitution Subtable\n");
933 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
936 const GSUB_MultipleSubstFormat1
*msf1
;
937 msf1
= (const GSUB_MultipleSubstFormat1
*)GSUB_get_subtable(look
, j
);
939 offset
= GET_BE_WORD(msf1
->Coverage
);
940 index
= GSUB_is_glyph_covered((const BYTE
*)msf1
+offset
, glyphs
[glyph_index
]);
943 const GSUB_Sequence
*seq
;
946 offset
= GET_BE_WORD(msf1
->Sequence
[index
]);
947 seq
= (const GSUB_Sequence
*)((const BYTE
*)msf1
+offset
);
948 sub_count
= GET_BE_WORD(seq
->GlyphCount
);
949 TRACE(" Glyph 0x%x (+%i)->",glyphs
[glyph_index
],(sub_count
-1));
951 for (j
= (*glyph_count
)+(sub_count
-1); j
> glyph_index
; j
--)
952 glyphs
[j
] =glyphs
[j
-(sub_count
-1)];
954 for (j
= 0; j
< sub_count
; j
++)
956 glyphs
[glyph_index
+ (sub_count
-1) - j
] = GET_BE_WORD(seq
->Substitute
[j
]);
958 glyphs
[glyph_index
+ j
] = GET_BE_WORD(seq
->Substitute
[j
]);
960 *glyph_count
= *glyph_count
+ (sub_count
- 1);
962 if (TRACE_ON(uniscribe
))
964 for (j
= 0; j
< sub_count
; j
++)
965 TRACE(" 0x%x",glyphs
[glyph_index
+j
]);
970 return glyph_index
+ sub_count
;
972 return glyph_index
- 1;
975 return GSUB_E_NOGLYPH
;
978 static INT
GSUB_apply_AlternateSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
981 TRACE("Alternate Substitution Subtable\n");
983 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
986 const GSUB_AlternateSubstFormat1
*asf1
;
989 asf1
= (const GSUB_AlternateSubstFormat1
*)GSUB_get_subtable(look
, j
);
990 offset
= GET_BE_WORD(asf1
->Coverage
);
992 index
= GSUB_is_glyph_covered((const BYTE
*)asf1
+offset
, glyphs
[glyph_index
]);
995 const GSUB_AlternateSet
*as
;
996 offset
= GET_BE_WORD(asf1
->AlternateSet
[index
]);
997 as
= (const GSUB_AlternateSet
*)((const BYTE
*)asf1
+offset
);
998 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as
->GlyphCount
));
999 if (glyphs
[glyph_index
] == GET_BE_WORD(as
->Alternate
[0]))
1000 return GSUB_E_NOGLYPH
;
1002 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
1003 glyphs
[glyph_index
] = GET_BE_WORD(as
->Alternate
[0]);
1004 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
1005 return glyph_index
+ write_dir
;
1008 return GSUB_E_NOGLYPH
;
1011 static INT
GSUB_apply_LigatureSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1015 TRACE("Ligature Substitution Subtable\n");
1016 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1018 const GSUB_LigatureSubstFormat1
*lsf1
;
1021 lsf1
= (const GSUB_LigatureSubstFormat1
*)GSUB_get_subtable(look
, j
);
1022 offset
= GET_BE_WORD(lsf1
->Coverage
);
1023 index
= GSUB_is_glyph_covered((const BYTE
*)lsf1
+offset
, glyphs
[glyph_index
]);
1024 TRACE(" Coverage index %i\n",index
);
1027 const GSUB_LigatureSet
*ls
;
1030 offset
= GET_BE_WORD(lsf1
->LigatureSet
[index
]);
1031 ls
= (const GSUB_LigatureSet
*)((const BYTE
*)lsf1
+offset
);
1032 count
= GET_BE_WORD(ls
->LigatureCount
);
1033 TRACE(" LigatureSet has %i members\n",count
);
1034 for (k
= 0; k
< count
; k
++)
1036 const GSUB_Ligature
*lig
;
1037 int CompCount
,l
,CompIndex
;
1039 offset
= GET_BE_WORD(ls
->Ligature
[k
]);
1040 lig
= (const GSUB_Ligature
*)((const BYTE
*)ls
+offset
);
1041 CompCount
= GET_BE_WORD(lig
->CompCount
) - 1;
1042 CompIndex
= glyph_index
+write_dir
;
1043 for (l
= 0; l
< CompCount
&& CompIndex
>= 0 && CompIndex
< *glyph_count
; l
++)
1046 CompGlyph
= GET_BE_WORD(lig
->Component
[l
]);
1047 if (CompGlyph
!= glyphs
[CompIndex
])
1049 CompIndex
+= write_dir
;
1053 int replaceIdx
= glyph_index
;
1055 replaceIdx
= glyph_index
- CompCount
;
1057 TRACE(" Glyph is 0x%x (+%i) ->",glyphs
[glyph_index
],CompCount
);
1058 glyphs
[replaceIdx
] = GET_BE_WORD(lig
->LigGlyph
);
1059 TRACE("0x%x\n",glyphs
[replaceIdx
]);
1062 unsigned int j
= replaceIdx
+ 1;
1063 memmove(&glyphs
[j
], &glyphs
[j
+ CompCount
], (*glyph_count
- j
) * sizeof(*glyphs
));
1064 *glyph_count
= *glyph_count
- CompCount
;
1066 return replaceIdx
+ write_dir
;
1071 return GSUB_E_NOGLYPH
;
1074 static INT
GSUB_apply_ContextSubst(const OT_LookupList
* lookup
, const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1077 TRACE("Context Substitution Subtable\n");
1078 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1080 const GSUB_ContextSubstFormat1
*csf1
;
1082 csf1
= (const GSUB_ContextSubstFormat1
*)GSUB_get_subtable(look
, j
);
1083 if (GET_BE_WORD(csf1
->SubstFormat
) == 1)
1086 TRACE("Context Substitution Subtable: Class 1\n");
1087 offset
= GET_BE_WORD(csf1
->Coverage
);
1088 index
= GSUB_is_glyph_covered((const BYTE
*)csf1
+offset
, glyphs
[glyph_index
]);
1089 TRACE(" Coverage index %i\n",index
);
1093 const GSUB_SubRuleSet
*srs
;
1094 offset
= GET_BE_WORD(csf1
->SubRuleSet
[index
]);
1095 srs
= (const GSUB_SubRuleSet
*)((const BYTE
*)csf1
+offset
);
1096 count
= GET_BE_WORD(srs
->SubRuleCount
);
1097 TRACE(" SubRuleSet has %i members\n",count
);
1098 for (k
= 0; k
< count
; k
++)
1100 const GSUB_SubRule_1
*sr
;
1101 const GSUB_SubRule_2
*sr_2
;
1104 int newIndex
= glyph_index
;
1106 offset
= GET_BE_WORD(srs
->SubRule
[k
]);
1107 sr
= (const GSUB_SubRule_1
*)((const BYTE
*)srs
+offset
);
1108 g_count
= GET_BE_WORD(sr
->GlyphCount
);
1109 TRACE(" SubRule has %i glyphs\n",g_count
);
1111 g
= glyph_index
+ write_dir
* (g_count
- 1);
1112 if (g
>= *glyph_count
)
1115 for (l
= 0; l
< g_count
-1; l
++)
1116 if (glyphs
[glyph_index
+ (write_dir
* (l
+1))] != GET_BE_WORD(sr
->Input
[l
])) break;
1120 TRACE(" Rule does not match\n");
1124 TRACE(" Rule matches\n");
1125 sr_2
= (const GSUB_SubRule_2
*)&sr
->Input
[g_count
- 1];
1127 for (l
= 0; l
< GET_BE_WORD(sr
->SubstCount
); l
++)
1129 unsigned int lookup_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].LookupListIndex
);
1130 unsigned int sequence_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].SequenceIndex
);
1132 g
= glyph_index
+ write_dir
* sequence_index
;
1133 if (g
>= *glyph_count
)
1135 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n",
1136 sequence_index
, glyph_index
, write_dir
);
1140 TRACE(" SUBST: %u -> %u %u.\n", l
, sequence_index
, lookup_index
);
1141 newIndex
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1142 if (newIndex
== GSUB_E_NOGLYPH
)
1144 ERR(" Chain failed to generate a glyph\n");
1152 else if (GET_BE_WORD(csf1
->SubstFormat
) == 2)
1154 const GSUB_ContextSubstFormat2
*csf2
;
1155 const void *glyph_class_table
;
1158 csf2
= (const GSUB_ContextSubstFormat2
*)csf1
;
1159 TRACE("Context Substitution Subtable: Class 2\n");
1160 offset
= GET_BE_WORD(csf2
->Coverage
);
1161 index
= GSUB_is_glyph_covered((const BYTE
*)csf2
+offset
, glyphs
[glyph_index
]);
1162 TRACE(" Coverage index %i\n",index
);
1165 int k
, count
, class;
1166 const GSUB_SubClassSet
*scs
;
1168 offset
= GET_BE_WORD(csf2
->ClassDef
);
1169 glyph_class_table
= (const BYTE
*)csf2
+ offset
;
1171 class = OT_get_glyph_class(glyph_class_table
,glyphs
[glyph_index
]);
1173 offset
= GET_BE_WORD(csf2
->SubClassSet
[class]);
1176 TRACE(" No class rule table for class %i\n",class);
1179 scs
= (const GSUB_SubClassSet
*)((const BYTE
*)csf2
+offset
);
1180 count
= GET_BE_WORD(scs
->SubClassRuleCnt
);
1181 TRACE(" SubClassSet has %i members\n",count
);
1182 for (k
= 0; k
< count
; k
++)
1184 const GSUB_SubClassRule_1
*sr
;
1185 const GSUB_SubClassRule_2
*sr_2
;
1188 int newIndex
= glyph_index
;
1190 offset
= GET_BE_WORD(scs
->SubClassRule
[k
]);
1191 sr
= (const GSUB_SubClassRule_1
*)((const BYTE
*)scs
+offset
);
1192 g_count
= GET_BE_WORD(sr
->GlyphCount
);
1193 TRACE(" SubClassRule has %i glyphs classes\n",g_count
);
1195 g
= glyph_index
+ write_dir
* (g_count
- 1);
1196 if (g
>= *glyph_count
)
1199 for (l
= 0; l
< g_count
-1; l
++)
1201 int g_class
= OT_get_glyph_class(glyph_class_table
, glyphs
[glyph_index
+ (write_dir
* (l
+1))]);
1202 if (g_class
!= GET_BE_WORD(sr
->Class
[l
])) break;
1207 TRACE(" Rule does not match\n");
1211 TRACE(" Rule matches\n");
1212 sr_2
= (const GSUB_SubClassRule_2
*)&sr
->Class
[g_count
- 1];
1214 for (l
= 0; l
< GET_BE_WORD(sr
->SubstCount
); l
++)
1216 unsigned int lookup_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].LookupListIndex
);
1217 unsigned int sequence_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].SequenceIndex
);
1219 g
= glyph_index
+ write_dir
* sequence_index
;
1220 if (g
>= *glyph_count
)
1222 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n",
1223 sequence_index
, glyph_index
, write_dir
);
1227 TRACE(" SUBST: %u -> %u %u.\n", l
, sequence_index
, lookup_index
);
1228 newIndex
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1229 if (newIndex
== GSUB_E_NOGLYPH
)
1231 ERR(" Chain failed to generate a glyph\n");
1240 FIXME("Unhandled Context Substitution Format %i\n", GET_BE_WORD(csf1
->SubstFormat
));
1242 return GSUB_E_NOGLYPH
;
1245 static INT
GSUB_apply_ChainContextSubst(const OT_LookupList
* lookup
, const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1249 TRACE("Chaining Contextual Substitution Subtable\n");
1250 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1252 const GSUB_ChainContextSubstFormat1
*ccsf1
;
1254 int dirLookahead
= write_dir
;
1255 int dirBacktrack
= -1 * write_dir
;
1257 ccsf1
= (const GSUB_ChainContextSubstFormat1
*)GSUB_get_subtable(look
, j
);
1258 if (GET_BE_WORD(ccsf1
->SubstFormat
) == 1)
1262 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
1265 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 2)
1268 const void *backtrack_class_table
;
1269 const void *input_class_table
;
1270 const void *lookahead_class_table
;
1274 const GSUB_ChainContextSubstFormat2
*ccsf2
= (const GSUB_ChainContextSubstFormat2
*)ccsf1
;
1275 const GSUB_ChainSubClassSet
*csc
;
1277 TRACE(" subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
1279 offset
= GET_BE_WORD(ccsf2
->Coverage
);
1281 if (GSUB_is_glyph_covered((const BYTE
*)ccsf2
+offset
, glyphs
[glyph_index
]) == -1)
1283 TRACE("Glyph not covered\n");
1286 offset
= GET_BE_WORD(ccsf2
->BacktrackClassDef
);
1287 backtrack_class_table
= (const BYTE
*)ccsf2
+offset
;
1288 offset
= GET_BE_WORD(ccsf2
->InputClassDef
);
1289 input_class_table
= (const BYTE
*)ccsf2
+offset
;
1290 offset
= GET_BE_WORD(ccsf2
->LookaheadClassDef
);
1291 lookahead_class_table
= (const BYTE
*)ccsf2
+offset
;
1292 count
= GET_BE_WORD(ccsf2
->ChainSubClassSetCnt
);
1294 class = OT_get_glyph_class(input_class_table
, glyphs
[glyph_index
]);
1295 offset
= GET_BE_WORD(ccsf2
->ChainSubClassSet
[class]);
1299 TRACE("No rules for class\n");
1303 csc
= (const GSUB_ChainSubClassSet
*)((BYTE
*)ccsf2
+offset
);
1304 count
= GET_BE_WORD(csc
->ChainSubClassRuleCnt
);
1306 TRACE("%i rules to check\n",count
);
1308 for (i
= 0; i
< count
; i
++)
1310 WORD backtrack_count
, input_count
, lookahead_count
, substitute_count
;
1312 const GSUB_ChainSubClassRule_1
*backtrack
;
1313 const GSUB_ChainSubClassRule_2
*input
;
1314 const GSUB_ChainSubClassRule_3
*lookahead
;
1315 const GSUB_ChainSubClassRule_4
*substitute
;
1316 int new_index
= GSUB_E_NOGLYPH
;
1318 offset
= GET_BE_WORD(csc
->ChainSubClassRule
[i
]);
1319 backtrack
= (const GSUB_ChainSubClassRule_1
*)((BYTE
*)csc
+ offset
);
1320 backtrack_count
= GET_BE_WORD(backtrack
->BacktrackGlyphCount
);
1321 k
= glyph_index
+ dirBacktrack
* backtrack_count
;
1322 if (k
< 0 || k
>= *glyph_count
)
1325 input
= (const GSUB_ChainSubClassRule_2
*)&backtrack
->Backtrack
[backtrack_count
];
1326 input_count
= GET_BE_WORD(input
->InputGlyphCount
) - 1;
1327 k
= glyph_index
+ write_dir
* input_count
;
1328 if (k
< 0 || k
>= *glyph_count
)
1331 lookahead
= (const GSUB_ChainSubClassRule_3
*)&input
->Input
[input_count
];
1332 lookahead_count
= GET_BE_WORD(lookahead
->LookaheadGlyphCount
);
1333 k
= glyph_index
+ dirLookahead
* (input_count
+ lookahead_count
);
1334 if (k
< 0 || k
>= *glyph_count
)
1337 substitute
= (const GSUB_ChainSubClassRule_4
*)&lookahead
->LookAhead
[lookahead_count
];
1339 for (k
= 0; k
< backtrack_count
; ++k
)
1341 WORD target_class
= GET_BE_WORD(backtrack
->Backtrack
[k
]);
1342 WORD glyph_class
= OT_get_glyph_class(backtrack_class_table
, glyphs
[glyph_index
+ (dirBacktrack
* (k
+1))]);
1343 if (target_class
!= glyph_class
)
1346 if (k
!= backtrack_count
)
1348 TRACE("Matched Backtrack\n");
1350 for (k
= 0; k
< input_count
; ++k
)
1352 WORD target_class
= GET_BE_WORD(input
->Input
[k
]);
1353 WORD glyph_class
= OT_get_glyph_class(input_class_table
, glyphs
[glyph_index
+ (write_dir
* (k
+1))]);
1354 if (target_class
!= glyph_class
)
1357 if (k
!= input_count
)
1359 TRACE("Matched IndexGlyphs\n");
1361 for (k
= 0; k
< lookahead_count
; ++k
)
1363 WORD target_class
= GET_BE_WORD(lookahead
->LookAhead
[k
]);
1364 WORD glyph_class
= OT_get_glyph_class(lookahead_class_table
,
1365 glyphs
[glyph_index
+ (dirLookahead
* (input_count
+ k
+ 1))]);
1366 if (target_class
!= glyph_class
)
1369 if (k
!= lookahead_count
)
1371 TRACE("Matched LookAhead\n");
1373 substitute_count
= GET_BE_WORD(substitute
->SubstCount
);
1374 for (k
= 0; k
< substitute_count
; ++k
)
1376 unsigned int lookup_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].LookupListIndex
);
1377 unsigned int sequence_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].SequenceIndex
);
1378 unsigned int g
= glyph_index
+ write_dir
* sequence_index
;
1380 if (g
>= *glyph_count
)
1382 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n",
1383 sequence_index
, glyph_index
, write_dir
);
1387 TRACE("SUBST: %u -> %u %u.\n", k
, sequence_index
, lookup_index
);
1388 new_index
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1389 if (new_index
== GSUB_E_NOGLYPH
)
1390 ERR("Chain failed to generate a glyph.\n");
1395 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 3)
1397 WORD backtrack_count
, input_count
, lookahead_count
, substitution_count
;
1399 const GSUB_ChainContextSubstFormat3_1
*backtrack
;
1400 const GSUB_ChainContextSubstFormat3_2
*input
;
1401 const GSUB_ChainContextSubstFormat3_3
*lookahead
;
1402 const GSUB_ChainContextSubstFormat3_4
*substitute
;
1403 int new_index
= GSUB_E_NOGLYPH
;
1405 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
1407 backtrack
= (const GSUB_ChainContextSubstFormat3_1
*)ccsf1
;
1408 backtrack_count
= GET_BE_WORD(backtrack
->BacktrackGlyphCount
);
1409 k
= glyph_index
+ dirBacktrack
* backtrack_count
;
1410 if (k
< 0 || k
>= *glyph_count
)
1413 input
= (const GSUB_ChainContextSubstFormat3_2
*)&backtrack
->Coverage
[backtrack_count
];
1414 input_count
= GET_BE_WORD(input
->InputGlyphCount
);
1415 k
= glyph_index
+ write_dir
* (input_count
- 1);
1416 if (k
< 0 || k
>= *glyph_count
)
1419 lookahead
= (const GSUB_ChainContextSubstFormat3_3
*)&input
->Coverage
[input_count
];
1420 lookahead_count
= GET_BE_WORD(lookahead
->LookaheadGlyphCount
);
1421 k
= glyph_index
+ dirLookahead
* (input_count
+ lookahead_count
- 1);
1422 if (k
< 0 || k
>= *glyph_count
)
1425 substitute
= (const GSUB_ChainContextSubstFormat3_4
*)&lookahead
->Coverage
[lookahead_count
];
1427 for (k
= 0; k
< backtrack_count
; ++k
)
1429 offset
= GET_BE_WORD(backtrack
->Coverage
[k
]);
1430 if (GSUB_is_glyph_covered((const BYTE
*)ccsf1
+ offset
,
1431 glyphs
[glyph_index
+ (dirBacktrack
* (k
+ 1))]) == -1)
1434 if (k
!= backtrack_count
)
1436 TRACE("Matched Backtrack\n");
1438 for (k
= 0; k
< input_count
; ++k
)
1440 offset
= GET_BE_WORD(input
->Coverage
[k
]);
1441 if (GSUB_is_glyph_covered((const BYTE
*)ccsf1
+ offset
,
1442 glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
1445 if (k
!= input_count
)
1447 TRACE("Matched IndexGlyphs\n");
1449 for (k
= 0; k
< lookahead_count
; ++k
)
1451 offset
= GET_BE_WORD(lookahead
->Coverage
[k
]);
1452 if (GSUB_is_glyph_covered((const BYTE
*)ccsf1
+ offset
,
1453 glyphs
[glyph_index
+ (dirLookahead
* (input_count
+ k
))]) == -1)
1456 if (k
!= lookahead_count
)
1458 TRACE("Matched LookAhead\n");
1460 substitution_count
= GET_BE_WORD(substitute
->SubstCount
);
1461 for (k
= 0; k
< substitution_count
; ++k
)
1463 unsigned int lookup_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].LookupListIndex
);
1464 unsigned int sequence_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].SequenceIndex
);
1465 unsigned int g
= glyph_index
+ write_dir
* sequence_index
;
1467 if (g
>= *glyph_count
)
1469 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n",
1470 sequence_index
, glyph_index
, write_dir
);
1474 TRACE("SUBST: %u -> %u %u.\n", k
, sequence_index
, lookup_index
);
1475 new_index
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1476 if (new_index
== GSUB_E_NOGLYPH
)
1477 ERR("Chain failed to generate a glyph.\n");
1482 return GSUB_E_NOGLYPH
;
1485 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1488 enum gsub_lookup_type type
;
1489 const OT_LookupTable
*look
;
1491 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
1492 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
1493 type
= GET_BE_WORD(look
->LookupType
);
1494 TRACE("type %#x, flag %#x, subtables %u.\n", type
,
1495 GET_BE_WORD(look
->LookupFlag
),GET_BE_WORD(look
->SubTableCount
));
1497 if (type
== GSUB_LOOKUP_EXTENSION
)
1499 if (GET_BE_WORD(look
->SubTableCount
))
1501 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)look
+ GET_BE_WORD(look
->SubTable
[0]));
1502 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
1504 type
= GET_BE_WORD(ext
->ExtensionLookupType
);
1505 TRACE("extension type %i\n",type
);
1509 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext
->SubstFormat
));
1514 WARN("lookup type is Extension Substitution but no extension subtable exists\n");
1519 case GSUB_LOOKUP_SINGLE
:
1520 return GSUB_apply_SingleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1521 case GSUB_LOOKUP_MULTIPLE
:
1522 return GSUB_apply_MultipleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1523 case GSUB_LOOKUP_ALTERNATE
:
1524 return GSUB_apply_AlternateSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1525 case GSUB_LOOKUP_LIGATURE
:
1526 return GSUB_apply_LigatureSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1527 case GSUB_LOOKUP_CONTEXT
:
1528 return GSUB_apply_ContextSubst(lookup
, look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1529 case GSUB_LOOKUP_CONTEXT_CHAINED
:
1530 return GSUB_apply_ChainContextSubst(lookup
, look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1531 case GSUB_LOOKUP_EXTENSION
:
1532 FIXME("Extension Substitution types not valid here\n");
1535 FIXME("Unhandled GSUB lookup type %#x.\n", type
);
1537 return GSUB_E_NOGLYPH
;
1540 int OpenType_apply_GSUB_lookup(const void *table
, unsigned int lookup_index
, WORD
*glyphs
,
1541 unsigned int glyph_index
, int write_dir
, int *glyph_count
)
1543 const GSUB_Header
*header
= (const GSUB_Header
*)table
;
1544 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
1546 return GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1552 static unsigned int GPOS_apply_lookup(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
1553 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, const OT_LookupList
*lookup
,
1554 unsigned int lookup_index
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
1557 static INT
GPOS_get_device_table_value(const OT_DeviceTable
*DeviceTable
, WORD ppem
)
1559 static const WORD mask
[3] = {3,0xf,0xff};
1560 if (DeviceTable
&& ppem
>= GET_BE_WORD(DeviceTable
->StartSize
) && ppem
<= GET_BE_WORD(DeviceTable
->EndSize
))
1562 WORD format
= GET_BE_WORD(DeviceTable
->DeltaFormat
);
1563 int index
= ppem
- GET_BE_WORD(DeviceTable
->StartSize
);
1566 TRACE("device table, format %#x, index %i\n", format
, index
);
1568 if (format
< 1 || format
> 3)
1570 WARN("invalid delta format %#x\n", format
);
1574 index
= index
<< format
;
1575 value
= (DeviceTable
->DeltaValue
[index
/sizeof(WORD
)] << (index
%sizeof(WORD
)))&mask
[format
-1];
1576 TRACE("offset %i, value %i\n",index
, value
);
1577 if (value
> mask
[format
-1]/2)
1578 value
= -1 * ((mask
[format
-1]+1) - value
);
1584 static void GPOS_get_anchor_values(const void *table
, POINT
*pt
, WORD ppem
)
1586 const GPOS_AnchorFormat1
* anchor1
= (const GPOS_AnchorFormat1
*)table
;
1588 switch (GET_BE_WORD(anchor1
->AnchorFormat
))
1592 TRACE("Anchor Format 1\n");
1593 pt
->x
= (short)GET_BE_WORD(anchor1
->XCoordinate
);
1594 pt
->y
= (short)GET_BE_WORD(anchor1
->YCoordinate
);
1599 const GPOS_AnchorFormat2
* anchor2
= (const GPOS_AnchorFormat2
*)table
;
1600 TRACE("Anchor Format 2\n");
1601 pt
->x
= (short)GET_BE_WORD(anchor2
->XCoordinate
);
1602 pt
->y
= (short)GET_BE_WORD(anchor2
->YCoordinate
);
1608 const GPOS_AnchorFormat3
* anchor3
= (const GPOS_AnchorFormat3
*)table
;
1609 TRACE("Anchor Format 3\n");
1610 pt
->x
= (short)GET_BE_WORD(anchor3
->XCoordinate
);
1611 pt
->y
= (short)GET_BE_WORD(anchor3
->YCoordinate
);
1612 offset
= GET_BE_WORD(anchor3
->XDeviceTable
);
1613 TRACE("ppem %i\n",ppem
);
1616 const OT_DeviceTable
* DeviceTableX
= NULL
;
1617 DeviceTableX
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1618 pt
->x
+= GPOS_get_device_table_value(DeviceTableX
, ppem
);
1620 offset
= GET_BE_WORD(anchor3
->YDeviceTable
);
1623 const OT_DeviceTable
* DeviceTableY
= NULL
;
1624 DeviceTableY
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1625 pt
->y
+= GPOS_get_device_table_value(DeviceTableY
, ppem
);
1630 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1
->AnchorFormat
));
1636 static void GPOS_convert_design_units_to_device(const OUTLINETEXTMETRICW
*otm
, const LOGFONTW
*logfont
,
1637 int desX
, int desY
, double *devX
, double *devY
)
1639 int emHeight
= otm
->otmTextMetrics
.tmAscent
+ otm
->otmTextMetrics
.tmDescent
- otm
->otmTextMetrics
.tmInternalLeading
;
1641 TRACE("emHeight %i lfWidth %i\n",emHeight
, logfont
->lfWidth
);
1642 *devX
= (desX
* emHeight
) / (double)otm
->otmEMSquare
;
1643 *devY
= (desY
* emHeight
) / (double)otm
->otmEMSquare
;
1644 if (logfont
->lfWidth
)
1645 FIXME("Font with lfWidth set not handled properly.\n");
1648 static INT
GPOS_get_value_record(WORD ValueFormat
, const WORD data
[], GPOS_ValueRecord
*record
)
1651 if (ValueFormat
& 0x0001) { if (data
) record
->XPlacement
= GET_BE_WORD(data
[offset
]); offset
++; }
1652 if (ValueFormat
& 0x0002) { if (data
) record
->YPlacement
= GET_BE_WORD(data
[offset
]); offset
++; }
1653 if (ValueFormat
& 0x0004) { if (data
) record
->XAdvance
= GET_BE_WORD(data
[offset
]); offset
++; }
1654 if (ValueFormat
& 0x0008) { if (data
) record
->YAdvance
= GET_BE_WORD(data
[offset
]); offset
++; }
1655 if (ValueFormat
& 0x0010) { if (data
) record
->XPlaDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1656 if (ValueFormat
& 0x0020) { if (data
) record
->YPlaDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1657 if (ValueFormat
& 0x0040) { if (data
) record
->XAdvDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1658 if (ValueFormat
& 0x0080) { if (data
) record
->YAdvDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1662 static void GPOS_get_value_record_offsets(const BYTE
*head
, GPOS_ValueRecord
*ValueRecord
,
1663 WORD ValueFormat
, unsigned int ppem
, POINT
*ptPlacement
, POINT
*ptAdvance
)
1665 if (ValueFormat
& 0x0001) ptPlacement
->x
+= (short)ValueRecord
->XPlacement
;
1666 if (ValueFormat
& 0x0002) ptPlacement
->y
+= (short)ValueRecord
->YPlacement
;
1667 if (ValueFormat
& 0x0004) ptAdvance
->x
+= (short)ValueRecord
->XAdvance
;
1668 if (ValueFormat
& 0x0008) ptAdvance
->y
+= (short)ValueRecord
->YAdvance
;
1669 if (ValueFormat
& 0x0010) ptPlacement
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XPlaDevice
), ppem
);
1670 if (ValueFormat
& 0x0020) ptPlacement
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YPlaDevice
), ppem
);
1671 if (ValueFormat
& 0x0040) ptAdvance
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XAdvDevice
), ppem
);
1672 if (ValueFormat
& 0x0080) ptAdvance
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YAdvDevice
), ppem
);
1673 if (ValueFormat
& 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat
&0xFF00);
1676 static const BYTE
*GPOS_get_subtable(const OT_LookupTable
*look
, int index
)
1678 int offset
= GET_BE_WORD(look
->SubTable
[index
]);
1680 if (GET_BE_WORD(look
->LookupType
) == GPOS_LOOKUP_POSITION_EXTENSION
)
1682 const GPOS_ExtensionPosFormat1
*ext
= (const GPOS_ExtensionPosFormat1
*)((const BYTE
*)look
+ offset
);
1683 if (GET_BE_WORD(ext
->PosFormat
) == 1)
1685 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
1689 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext
->PosFormat
));
1692 return (const BYTE
*)look
+ offset
;
1695 static void GPOS_apply_SingleAdjustment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1696 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
,
1697 POINT
*adjust
, POINT
*advance
)
1701 TRACE("Single Adjustment Positioning Subtable\n");
1703 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1705 const GPOS_SinglePosFormat1
*spf1
= (const GPOS_SinglePosFormat1
*)GPOS_get_subtable(look
, j
);
1707 if (GET_BE_WORD(spf1
->PosFormat
) == 1)
1709 offset
= GET_BE_WORD(spf1
->Coverage
);
1710 if (GSUB_is_glyph_covered((const BYTE
*)spf1
+offset
, glyphs
[glyph_index
]) != -1)
1712 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1713 WORD ValueFormat
= GET_BE_WORD(spf1
->ValueFormat
);
1714 GPOS_get_value_record(ValueFormat
, spf1
->Value
, &ValueRecord
);
1715 GPOS_get_value_record_offsets((const BYTE
*)spf1
, &ValueRecord
, ValueFormat
, ppem
, adjust
, advance
);
1716 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1719 else if (GET_BE_WORD(spf1
->PosFormat
) == 2)
1722 const GPOS_SinglePosFormat2
*spf2
;
1723 spf2
= (const GPOS_SinglePosFormat2
*)spf1
;
1724 offset
= GET_BE_WORD(spf2
->Coverage
);
1725 index
= GSUB_is_glyph_covered((const BYTE
*)spf2
+offset
, glyphs
[glyph_index
]);
1729 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1730 WORD ValueFormat
= GET_BE_WORD(spf2
->ValueFormat
);
1731 size
= GPOS_get_value_record(ValueFormat
, spf2
->Value
, &ValueRecord
);
1734 offset
= size
* index
;
1735 GPOS_get_value_record(ValueFormat
, &spf2
->Value
[offset
], &ValueRecord
);
1737 GPOS_get_value_record_offsets((const BYTE
*)spf2
, &ValueRecord
, ValueFormat
, ppem
, adjust
, advance
);
1738 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1742 FIXME("Single Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(spf1
->PosFormat
));
1746 static void apply_pair_value( const void *pos_table
, WORD val_fmt1
, WORD val_fmt2
, const WORD
*pair
,
1747 INT ppem
, POINT
*adjust
, POINT
*advance
)
1749 GPOS_ValueRecord val_rec1
= {0,0,0,0,0,0,0,0};
1750 GPOS_ValueRecord val_rec2
= {0,0,0,0,0,0,0,0};
1753 size
= GPOS_get_value_record( val_fmt1
, pair
, &val_rec1
);
1754 GPOS_get_value_record( val_fmt2
, pair
+ size
, &val_rec2
);
1758 GPOS_get_value_record_offsets( pos_table
, &val_rec1
, val_fmt1
, ppem
, adjust
, advance
);
1759 TRACE( "Glyph 1 resulting cumulative offset is %s design units\n", wine_dbgstr_point(&adjust
[0]) );
1760 TRACE( "Glyph 1 resulting cumulative advance is %s design units\n", wine_dbgstr_point(&advance
[0]) );
1764 GPOS_get_value_record_offsets( pos_table
, &val_rec2
, val_fmt2
, ppem
, adjust
+ 1, advance
+ 1 );
1765 TRACE( "Glyph 2 resulting cumulative offset is %s design units\n", wine_dbgstr_point(&adjust
[1]) );
1766 TRACE( "Glyph 2 resulting cumulative advance is %s design units\n", wine_dbgstr_point(&advance
[1]) );
1770 static int GPOS_apply_PairAdjustment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1771 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
,
1772 POINT
*adjust
, POINT
*advance
)
1775 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1777 if (glyph_index
+ write_dir
>= glyph_count
)
1780 TRACE("Pair Adjustment Positioning Subtable\n");
1782 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1784 const GPOS_PairPosFormat1
*ppf1
= (const GPOS_PairPosFormat1
*)GPOS_get_subtable(look
, j
);
1786 if (GET_BE_WORD(ppf1
->PosFormat
) == 1)
1789 WORD ValueFormat1
= GET_BE_WORD(ppf1
->ValueFormat1
);
1790 WORD ValueFormat2
= GET_BE_WORD(ppf1
->ValueFormat2
);
1791 INT val_fmt1_size
= GPOS_get_value_record( ValueFormat1
, NULL
, NULL
);
1792 INT val_fmt2_size
= GPOS_get_value_record( ValueFormat2
, NULL
, NULL
);
1793 offset
= GET_BE_WORD(ppf1
->Coverage
);
1794 index
= GSUB_is_glyph_covered((const BYTE
*)ppf1
+offset
, glyphs
[glyph_index
]);
1795 if (index
!= -1 && index
< GET_BE_WORD(ppf1
->PairSetCount
))
1799 const GPOS_PairSet
*ps
;
1800 const GPOS_PairValueRecord
*pair_val_rec
;
1801 offset
= GET_BE_WORD(ppf1
->PairSetOffset
[index
]);
1802 ps
= (const GPOS_PairSet
*)((const BYTE
*)ppf1
+offset
);
1803 pair_count
= GET_BE_WORD(ps
->PairValueCount
);
1804 pair_val_rec
= ps
->PairValueRecord
;
1805 for (k
= 0; k
< pair_count
; k
++)
1807 WORD second_glyph
= GET_BE_WORD(pair_val_rec
->SecondGlyph
);
1808 if (glyphs
[glyph_index
+write_dir
] == second_glyph
)
1811 TRACE("Format 1: Found Pair %x,%x\n",glyphs
[glyph_index
],glyphs
[glyph_index
+write_dir
]);
1812 apply_pair_value(ppf1
, ValueFormat1
, ValueFormat2
,
1813 pair_val_rec
->Value1
, ppem
, adjust
, advance
);
1814 if (ValueFormat2
) next
++;
1817 pair_val_rec
= (const GPOS_PairValueRecord
*)(pair_val_rec
->Value1
+ val_fmt1_size
+ val_fmt2_size
);
1821 else if (GET_BE_WORD(ppf1
->PosFormat
) == 2)
1823 const GPOS_PairPosFormat2
*ppf2
= (const GPOS_PairPosFormat2
*)ppf1
;
1825 WORD ValueFormat1
= GET_BE_WORD( ppf2
->ValueFormat1
);
1826 WORD ValueFormat2
= GET_BE_WORD( ppf2
->ValueFormat2
);
1827 INT val_fmt1_size
= GPOS_get_value_record( ValueFormat1
, NULL
, NULL
);
1828 INT val_fmt2_size
= GPOS_get_value_record( ValueFormat2
, NULL
, NULL
);
1829 WORD class1_count
= GET_BE_WORD( ppf2
->Class1Count
);
1830 WORD class2_count
= GET_BE_WORD( ppf2
->Class2Count
);
1832 offset
= GET_BE_WORD( ppf2
->Coverage
);
1833 index
= GSUB_is_glyph_covered( (const BYTE
*)ppf2
+ offset
, glyphs
[glyph_index
] );
1836 WORD class1
, class2
;
1837 class1
= OT_get_glyph_class( (const BYTE
*)ppf2
+ GET_BE_WORD(ppf2
->ClassDef1
), glyphs
[glyph_index
] );
1838 class2
= OT_get_glyph_class( (const BYTE
*)ppf2
+ GET_BE_WORD(ppf2
->ClassDef2
), glyphs
[glyph_index
+ write_dir
] );
1839 if (class1
< class1_count
&& class2
< class2_count
)
1841 const WORD
*pair_val
= ppf2
->Class1Record
+ (class1
* class2_count
+ class2
) * (val_fmt1_size
+ val_fmt2_size
);
1844 TRACE( "Format 2: Found Pair %x,%x\n", glyphs
[glyph_index
], glyphs
[glyph_index
+ write_dir
] );
1846 apply_pair_value(ppf2
, ValueFormat1
, ValueFormat2
, pair_val
, ppem
, adjust
, advance
);
1847 if (ValueFormat2
) next
++;
1853 FIXME("Pair Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(ppf1
->PosFormat
));
1858 static void GPOS_apply_CursiveAttachment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1859 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
1862 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1864 if (glyph_index
+ write_dir
>= glyph_count
)
1867 TRACE("Cursive Attachment Positioning Subtable\n");
1869 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1871 const GPOS_CursivePosFormat1
*cpf1
= (const GPOS_CursivePosFormat1
*)GPOS_get_subtable(look
, j
);
1872 if (GET_BE_WORD(cpf1
->PosFormat
) == 1)
1874 int index_exit
, index_entry
;
1875 WORD offset
= GET_BE_WORD( cpf1
->Coverage
);
1876 index_exit
= GSUB_is_glyph_covered((const BYTE
*)cpf1
+offset
, glyphs
[glyph_index
]);
1877 if (index_exit
!= -1 && cpf1
->EntryExitRecord
[index_exit
].ExitAnchor
!= 0)
1879 index_entry
= GSUB_is_glyph_covered((const BYTE
*)cpf1
+offset
, glyphs
[glyph_index
+write_dir
]);
1880 if (index_entry
!= -1 && cpf1
->EntryExitRecord
[index_entry
].EntryAnchor
!= 0)
1882 POINT exit_pt
, entry_pt
;
1883 offset
= GET_BE_WORD(cpf1
->EntryExitRecord
[index_exit
].ExitAnchor
);
1884 GPOS_get_anchor_values((const BYTE
*)cpf1
+ offset
, &exit_pt
, ppem
);
1885 offset
= GET_BE_WORD(cpf1
->EntryExitRecord
[index_entry
].EntryAnchor
);
1886 GPOS_get_anchor_values((const BYTE
*)cpf1
+ offset
, &entry_pt
, ppem
);
1887 TRACE("Found linkage %x[%s] %x[%s]\n",glyphs
[glyph_index
], wine_dbgstr_point(&exit_pt
), glyphs
[glyph_index
+write_dir
], wine_dbgstr_point(&entry_pt
));
1888 pt
->x
= entry_pt
.x
- exit_pt
.x
;
1889 pt
->y
= entry_pt
.y
- exit_pt
.y
;
1895 FIXME("Cursive Attachment Positioning: Format %i Unhandled\n",GET_BE_WORD(cpf1
->PosFormat
));
1900 static int GPOS_apply_MarkToBase(const ScriptCache
*script_cache
, const OT_LookupTable
*look
,
1901 const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, unsigned int glyph_index
,
1902 unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
1905 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1906 const void *glyph_class_table
= NULL
;
1909 if (script_cache
->GDEF_Table
)
1911 const GDEF_Header
*header
= script_cache
->GDEF_Table
;
1912 WORD offset
= GET_BE_WORD( header
->GlyphClassDef
);
1914 glyph_class_table
= (const BYTE
*)script_cache
->GDEF_Table
+ offset
;
1917 TRACE("MarkToBase Attachment Positioning Subtable\n");
1919 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1921 const GPOS_MarkBasePosFormat1
*mbpf1
= (const GPOS_MarkBasePosFormat1
*)GPOS_get_subtable(look
, j
);
1922 if (GET_BE_WORD(mbpf1
->PosFormat
) == 1)
1924 int offset
= GET_BE_WORD(mbpf1
->MarkCoverage
);
1926 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[glyph_index
]);
1927 if (mark_index
!= -1)
1930 int base_glyph
= glyph_index
- write_dir
;
1932 if (glyph_class_table
)
1934 while (OT_get_glyph_class(glyph_class_table
, glyphs
[base_glyph
]) == MarkGlyph
&& base_glyph
> 0 && base_glyph
< glyph_count
)
1935 base_glyph
-= write_dir
;
1938 offset
= GET_BE_WORD(mbpf1
->BaseCoverage
);
1939 base_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[base_glyph
]);
1940 if (base_index
!= -1)
1942 const GPOS_MarkArray
*ma
;
1943 const GPOS_MarkRecord
*mr
;
1944 const GPOS_BaseArray
*ba
;
1945 const GPOS_BaseRecord
*br
;
1947 int class_count
= GET_BE_WORD(mbpf1
->ClassCount
);
1948 int baserecord_size
;
1951 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[base_glyph
], base_index
);
1952 offset
= GET_BE_WORD(mbpf1
->MarkArray
);
1953 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mbpf1
+ offset
);
1954 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
1956 ERR("Mark index exceeded mark count\n");
1959 mr
= &ma
->MarkRecord
[mark_index
];
1960 mark_class
= GET_BE_WORD(mr
->Class
);
1961 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
1962 offset
= GET_BE_WORD(mbpf1
->BaseArray
);
1963 ba
= (const GPOS_BaseArray
*)((const BYTE
*)mbpf1
+ offset
);
1964 baserecord_size
= class_count
* sizeof(WORD
);
1965 br
= (const GPOS_BaseRecord
*)((const BYTE
*)ba
+ sizeof(WORD
) + (baserecord_size
* base_index
));
1966 offset
= GET_BE_WORD(br
->BaseAnchor
[mark_class
]);
1967 GPOS_get_anchor_values((const BYTE
*)ba
+ offset
, &base_pt
, ppem
);
1968 offset
= GET_BE_WORD(mr
->MarkAnchor
);
1969 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
1970 TRACE("Offset on base is %s design units\n",wine_dbgstr_point(&base_pt
));
1971 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt
));
1972 pt
->x
+= base_pt
.x
- mark_pt
.x
;
1973 pt
->y
+= base_pt
.y
- mark_pt
.y
;
1974 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt
));
1980 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1
->PosFormat
));
1985 static void GPOS_apply_MarkToLigature(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1986 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
1989 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1991 TRACE("MarkToLigature Attachment Positioning Subtable\n");
1993 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1995 const GPOS_MarkLigPosFormat1
*mlpf1
= (const GPOS_MarkLigPosFormat1
*)GPOS_get_subtable(look
, j
);
1996 if (GET_BE_WORD(mlpf1
->PosFormat
) == 1)
1998 int offset
= GET_BE_WORD(mlpf1
->MarkCoverage
);
2000 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mlpf1
+offset
, glyphs
[glyph_index
]);
2001 if (mark_index
!= -1)
2004 offset
= GET_BE_WORD(mlpf1
->LigatureCoverage
);
2005 ligature_index
= GSUB_is_glyph_covered((const BYTE
*)mlpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
2006 if (ligature_index
!= -1)
2008 const GPOS_MarkArray
*ma
;
2009 const GPOS_MarkRecord
*mr
;
2011 const GPOS_LigatureArray
*la
;
2012 const GPOS_LigatureAttach
*lt
;
2014 int class_count
= GET_BE_WORD(mlpf1
->ClassCount
);
2015 int component_count
;
2021 TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], ligature_index
);
2022 offset
= GET_BE_WORD(mlpf1
->MarkArray
);
2023 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mlpf1
+ offset
);
2024 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
2026 ERR("Mark index exceeded mark count\n");
2029 mr
= &ma
->MarkRecord
[mark_index
];
2030 mark_class
= GET_BE_WORD(mr
->Class
);
2031 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
2032 offset
= GET_BE_WORD(mlpf1
->LigatureArray
);
2033 la
= (const GPOS_LigatureArray
*)((const BYTE
*)mlpf1
+ offset
);
2034 if (ligature_index
> GET_BE_WORD(la
->LigatureCount
))
2036 ERR("Ligature index exceeded ligature count\n");
2039 offset
= GET_BE_WORD(la
->LigatureAttach
[ligature_index
]);
2040 lt
= (const GPOS_LigatureAttach
*)((const BYTE
*)la
+ offset
);
2042 component_count
= GET_BE_WORD(lt
->ComponentCount
);
2043 component_size
= class_count
* sizeof(WORD
);
2045 for (i
= 0; i
< component_count
&& !offset
; i
++)
2048 const GPOS_ComponentRecord
*cr
= (const GPOS_ComponentRecord
*)((const BYTE
*)lt
->ComponentRecord
+ (component_size
* i
));
2049 for (k
= 0; k
< class_count
&& !offset
; k
++)
2050 offset
= GET_BE_WORD(cr
->LigatureAnchor
[k
]);
2051 cr
= (const GPOS_ComponentRecord
*)((const BYTE
*)cr
+ component_size
);
2055 ERR("Failed to find available ligature connection point\n");
2059 GPOS_get_anchor_values((const BYTE
*)lt
+ offset
, &ligature_pt
, ppem
);
2060 offset
= GET_BE_WORD(mr
->MarkAnchor
);
2061 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
2062 TRACE("Offset on ligature is %s design units\n",wine_dbgstr_point(&ligature_pt
));
2063 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt
));
2064 pt
->x
+= ligature_pt
.x
- mark_pt
.x
;
2065 pt
->y
+= ligature_pt
.y
- mark_pt
.y
;
2066 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt
));
2071 FIXME("Unhandled Mark To Ligature Format %i\n",GET_BE_WORD(mlpf1
->PosFormat
));
2075 static BOOL
GPOS_apply_MarkToMark(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
2076 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
2080 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2082 TRACE("MarkToMark Attachment Positioning Subtable\n");
2084 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
2086 const GPOS_MarkMarkPosFormat1
*mmpf1
= (const GPOS_MarkMarkPosFormat1
*)GPOS_get_subtable(look
, j
);
2087 if (GET_BE_WORD(mmpf1
->PosFormat
) == 1)
2089 int offset
= GET_BE_WORD(mmpf1
->Mark1Coverage
);
2091 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mmpf1
+offset
, glyphs
[glyph_index
]);
2092 if (mark_index
!= -1)
2095 offset
= GET_BE_WORD(mmpf1
->Mark2Coverage
);
2096 mark2_index
= GSUB_is_glyph_covered((const BYTE
*)mmpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
2097 if (mark2_index
!= -1)
2099 const GPOS_MarkArray
*ma
;
2100 const GPOS_MarkRecord
*mr
;
2101 const GPOS_Mark2Array
*m2a
;
2102 const GPOS_Mark2Record
*m2r
;
2104 int class_count
= GET_BE_WORD(mmpf1
->ClassCount
);
2105 int mark2record_size
;
2108 TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], mark2_index
);
2109 offset
= GET_BE_WORD(mmpf1
->Mark1Array
);
2110 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mmpf1
+ offset
);
2111 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
2113 ERR("Mark index exceeded mark count\n");
2116 mr
= &ma
->MarkRecord
[mark_index
];
2117 mark_class
= GET_BE_WORD(mr
->Class
);
2118 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
2119 offset
= GET_BE_WORD(mmpf1
->Mark2Array
);
2120 m2a
= (const GPOS_Mark2Array
*)((const BYTE
*)mmpf1
+ offset
);
2121 mark2record_size
= class_count
* sizeof(WORD
);
2122 m2r
= (const GPOS_Mark2Record
*)((const BYTE
*)m2a
+ sizeof(WORD
) + (mark2record_size
* mark2_index
));
2123 offset
= GET_BE_WORD(m2r
->Mark2Anchor
[mark_class
]);
2124 GPOS_get_anchor_values((const BYTE
*)m2a
+ offset
, &mark2_pt
, ppem
);
2125 offset
= GET_BE_WORD(mr
->MarkAnchor
);
2126 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
2127 TRACE("Offset on mark2 is %s design units\n",wine_dbgstr_point(&mark2_pt
));
2128 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt
));
2129 pt
->x
+= mark2_pt
.x
- mark_pt
.x
;
2130 pt
->y
+= mark2_pt
.y
- mark_pt
.y
;
2131 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt
));
2137 FIXME("Unhandled Mark To Mark Format %i\n",GET_BE_WORD(mmpf1
->PosFormat
));
2142 static unsigned int GPOS_apply_ContextPos(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
2143 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, const OT_LookupList
*lookup
,
2144 const OT_LookupTable
*look
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
2148 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2150 TRACE("Contextual Positioning Subtable\n");
2152 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
2154 const GPOS_ContextPosFormat2
*cpf2
= (GPOS_ContextPosFormat2
*)GPOS_get_subtable(look
, j
);
2156 if (GET_BE_WORD(cpf2
->PosFormat
) == 1)
2160 FIXME(" TODO: subtype 1\n");
2163 else if (GET_BE_WORD(cpf2
->PosFormat
) == 2)
2165 WORD offset
= GET_BE_WORD(cpf2
->Coverage
);
2168 TRACE("Contextual Positioning Subtable: Format 2\n");
2170 index
= GSUB_is_glyph_covered((const BYTE
*)cpf2
+offset
, glyphs
[glyph_index
]);
2171 TRACE("Coverage index %i\n",index
);
2174 int k
, count
, class;
2175 const GPOS_PosClassSet
*pcs
;
2176 const void *glyph_class_table
= NULL
;
2178 offset
= GET_BE_WORD(cpf2
->ClassDef
);
2179 glyph_class_table
= (const BYTE
*)cpf2
+ offset
;
2181 class = OT_get_glyph_class(glyph_class_table
,glyphs
[glyph_index
]);
2183 offset
= GET_BE_WORD(cpf2
->PosClassSet
[class]);
2186 TRACE("No class rule table for class %i\n",class);
2189 pcs
= (const GPOS_PosClassSet
*)((const BYTE
*)cpf2
+offset
);
2190 count
= GET_BE_WORD(pcs
->PosClassRuleCnt
);
2191 TRACE("PosClassSet has %i members\n",count
);
2192 for (k
= 0; k
< count
; k
++)
2194 const GPOS_PosClassRule_1
*pr
;
2195 const GPOS_PosClassRule_2
*pr_2
;
2199 offset
= GET_BE_WORD(pcs
->PosClassRule
[k
]);
2200 pr
= (const GPOS_PosClassRule_1
*)((const BYTE
*)pcs
+offset
);
2201 g_count
= GET_BE_WORD(pr
->GlyphCount
);
2202 TRACE("PosClassRule has %i glyphs classes\n",g_count
);
2204 g
= glyph_index
+ write_dir
* (g_count
- 1);
2205 if (g
>= glyph_count
)
2208 for (l
= 0; l
< g_count
-1; l
++)
2210 int g_class
= OT_get_glyph_class(glyph_class_table
, glyphs
[glyph_index
+ (write_dir
* (l
+1))]);
2211 if (g_class
!= GET_BE_WORD(pr
->Class
[l
])) break;
2216 TRACE("Rule does not match\n");
2220 TRACE("Rule matches\n");
2221 pr_2
= (const GPOS_PosClassRule_2
*)&pr
->Class
[g_count
- 1];
2223 for (l
= 0; l
< GET_BE_WORD(pr
->PosCount
); l
++)
2225 unsigned int lookup_index
= GET_BE_WORD(pr_2
->PosLookupRecord
[l
].LookupListIndex
);
2226 unsigned int sequence_index
= GET_BE_WORD(pr_2
->PosLookupRecord
[l
].SequenceIndex
);
2228 g
= glyph_index
+ write_dir
* sequence_index
;
2229 if (g
>= glyph_count
)
2231 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n",
2232 sequence_index
, glyph_index
, write_dir
);
2236 TRACE("Position: %u -> %u %u.\n", l
, sequence_index
, lookup_index
);
2238 GPOS_apply_lookup(script_cache
, otm
, logfont
, analysis
, advance
,
2239 lookup
, lookup_index
, glyphs
, g
, glyph_count
, goffset
);
2245 TRACE("Not covered\n");
2248 else if (GET_BE_WORD(cpf2
->PosFormat
) == 3)
2252 FIXME(" TODO: subtype 3\n");
2256 FIXME("Unhandled Contextual Positioning Format %i\n",GET_BE_WORD(cpf2
->PosFormat
));
2261 static unsigned int GPOS_apply_ChainContextPos(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
2262 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, const OT_LookupList
*lookup
,
2263 const OT_LookupTable
*look
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
2267 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2269 TRACE("Chaining Contextual Positioning Subtable\n");
2271 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
2274 const GPOS_ChainContextPosFormat3_1
*backtrack
= (GPOS_ChainContextPosFormat3_1
*)GPOS_get_subtable(look
, j
);
2275 int dirLookahead
= write_dir
;
2276 int dirBacktrack
= -1 * write_dir
;
2278 if (GET_BE_WORD(backtrack
->PosFormat
) == 1)
2282 FIXME(" TODO: subtype 1 (Simple Chaining Context Glyph Positioning)\n");
2285 else if (GET_BE_WORD(backtrack
->PosFormat
) == 2)
2289 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Positioning)\n");
2292 else if (GET_BE_WORD(backtrack
->PosFormat
) == 3)
2294 WORD backtrack_count
, input_count
, lookahead_count
, positioning_count
;
2296 const GPOS_ChainContextPosFormat3_2
*input
;
2297 const GPOS_ChainContextPosFormat3_3
*lookahead
;
2298 const GPOS_ChainContextPosFormat3_4
*positioning
;
2300 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Positioning)\n");
2302 backtrack_count
= GET_BE_WORD(backtrack
->BacktrackGlyphCount
);
2303 k
= glyph_index
+ dirBacktrack
* backtrack_count
;
2304 if (k
< 0 || k
>= glyph_count
)
2307 input
= (const GPOS_ChainContextPosFormat3_2
*)&backtrack
->Coverage
[backtrack_count
];
2308 input_count
= GET_BE_WORD(input
->InputGlyphCount
);
2309 k
= glyph_index
+ write_dir
* (input_count
- 1);
2310 if (k
< 0 || k
>= glyph_count
)
2313 lookahead
= (const GPOS_ChainContextPosFormat3_3
*)&input
->Coverage
[input_count
];
2314 lookahead_count
= GET_BE_WORD(lookahead
->LookaheadGlyphCount
);
2315 k
= glyph_index
+ dirLookahead
* (input_count
+ lookahead_count
- 1);
2316 if (k
< 0 || k
>= glyph_count
)
2319 positioning
= (const GPOS_ChainContextPosFormat3_4
*)&lookahead
->Coverage
[lookahead_count
];
2321 for (k
= 0; k
< backtrack_count
; ++k
)
2323 offset
= GET_BE_WORD(backtrack
->Coverage
[k
]);
2324 if (GSUB_is_glyph_covered((const BYTE
*)backtrack
+ offset
,
2325 glyphs
[glyph_index
+ (dirBacktrack
* (k
+ 1))]) == -1)
2328 if (k
!= backtrack_count
)
2330 TRACE("Matched Backtrack\n");
2332 for (k
= 0; k
< input_count
; ++k
)
2334 offset
= GET_BE_WORD(input
->Coverage
[k
]);
2335 if (GSUB_is_glyph_covered((const BYTE
*)backtrack
+ offset
,
2336 glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
2339 if (k
!= input_count
)
2341 TRACE("Matched IndexGlyphs\n");
2343 for (k
= 0; k
< lookahead_count
; ++k
)
2345 offset
= GET_BE_WORD(lookahead
->Coverage
[k
]);
2346 if (GSUB_is_glyph_covered((const BYTE
*)backtrack
+ offset
,
2347 glyphs
[glyph_index
+ (dirLookahead
* (input_count
+ k
))]) == -1)
2350 if (k
!= lookahead_count
)
2352 TRACE("Matched LookAhead\n");
2354 if (!(positioning_count
= GET_BE_WORD(positioning
->PosCount
)))
2357 for (k
= 0; k
< positioning_count
; ++k
)
2359 unsigned int lookup_index
= GET_BE_WORD(positioning
->PosLookupRecord
[k
].LookupListIndex
);
2360 unsigned int sequence_index
= GET_BE_WORD(positioning
->PosLookupRecord
[k
].SequenceIndex
);
2361 unsigned int g
= glyph_index
+ write_dir
* sequence_index
;
2363 if (g
>= glyph_count
)
2365 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n",
2366 sequence_index
, glyph_index
, write_dir
);
2370 TRACE("Position: %u -> %u %u.\n", k
, sequence_index
, lookup_index
);
2371 GPOS_apply_lookup(script_cache
, otm
, logfont
, analysis
, advance
, lookup
, lookup_index
,
2372 glyphs
, g
, glyph_count
, goffset
);
2374 return input_count
+ lookahead_count
;
2377 FIXME("Unhandled Chaining Contextual Positioning Format %#x.\n", GET_BE_WORD(backtrack
->PosFormat
));
2382 static unsigned int GPOS_apply_lookup(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*lpotm
,
2383 const LOGFONTW
*lplogfont
, const SCRIPT_ANALYSIS
*analysis
, int *piAdvance
, const OT_LookupList
*lookup
,
2384 unsigned int lookup_index
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
2388 const OT_LookupTable
*look
;
2389 int ppem
= lpotm
->otmTextMetrics
.tmAscent
+ lpotm
->otmTextMetrics
.tmDescent
- lpotm
->otmTextMetrics
.tmInternalLeading
;
2390 enum gpos_lookup_type type
;
2392 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
2393 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
2394 type
= GET_BE_WORD(look
->LookupType
);
2395 TRACE("type %#x, flag %#x, subtables %u.\n", type
,
2396 GET_BE_WORD(look
->LookupFlag
), GET_BE_WORD(look
->SubTableCount
));
2398 if (type
== GPOS_LOOKUP_POSITION_EXTENSION
)
2400 if (GET_BE_WORD(look
->SubTableCount
))
2402 const GPOS_ExtensionPosFormat1
*ext
= (const GPOS_ExtensionPosFormat1
*)((const BYTE
*)look
+ GET_BE_WORD(look
->SubTable
[0]));
2403 if (GET_BE_WORD(ext
->PosFormat
) == 1)
2405 type
= GET_BE_WORD(ext
->ExtensionLookupType
);
2406 TRACE("extension type %i\n",type
);
2410 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext
->PosFormat
));
2415 WARN("lookup type is Extension Positioning but no extension subtable exists\n");
2420 case GPOS_LOOKUP_ADJUST_SINGLE
:
2423 POINT adjust
= {0,0};
2424 POINT advance
= {0,0};
2425 GPOS_apply_SingleAdjustment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &adjust
, &advance
);
2426 if (adjust
.x
|| adjust
.y
)
2428 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
.x
, adjust
.y
, &devX
, &devY
);
2429 pGoffset
[glyph_index
].du
+= round(devX
);
2430 pGoffset
[glyph_index
].dv
+= round(devY
);
2432 if (advance
.x
|| advance
.y
)
2434 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
.x
, advance
.y
, &devX
, &devY
);
2435 piAdvance
[glyph_index
] += round(devX
);
2437 FIXME("Unhandled adjustment to Y advancement\n");
2442 case GPOS_LOOKUP_ADJUST_PAIR
:
2444 POINT advance
[2]= {{0,0},{0,0}};
2445 POINT adjust
[2]= {{0,0},{0,0}};
2448 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2449 int offset_sign
= (analysis
->fRTL
&& analysis
->fLogicalOrder
) ? -1 : 1;
2451 index_offset
= GPOS_apply_PairAdjustment(look
, analysis
, glyphs
,
2452 glyph_index
, glyph_count
, ppem
, adjust
, advance
);
2453 if (adjust
[0].x
|| adjust
[0].y
)
2455 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
[0].x
, adjust
[0].y
, &devX
, &devY
);
2456 pGoffset
[glyph_index
].du
+= round(devX
) * offset_sign
;
2457 pGoffset
[glyph_index
].dv
+= round(devY
);
2459 if (advance
[0].x
|| advance
[0].y
)
2461 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
[0].x
, advance
[0].y
, &devX
, &devY
);
2462 piAdvance
[glyph_index
] += round(devX
);
2464 if (adjust
[1].x
|| adjust
[1].y
)
2466 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
[1].x
, adjust
[1].y
, &devX
, &devY
);
2467 pGoffset
[glyph_index
+ write_dir
].du
+= round(devX
) * offset_sign
;
2468 pGoffset
[glyph_index
+ write_dir
].dv
+= round(devY
);
2470 if (advance
[1].x
|| advance
[1].y
)
2472 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
[1].x
, advance
[1].y
, &devX
, &devY
);
2473 piAdvance
[glyph_index
+ write_dir
] += round(devX
);
2475 return index_offset
;
2478 case GPOS_LOOKUP_ATTACH_CURSIVE
:
2482 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2484 GPOS_apply_CursiveAttachment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
2485 if (desU
.x
|| desU
.y
)
2487 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2488 /* Windows does not appear to apply X offsets here */
2489 pGoffset
[glyph_index
].dv
= round(devY
) + pGoffset
[glyph_index
+write_dir
].dv
;
2494 case GPOS_LOOKUP_ATTACH_MARK_TO_BASE
:
2498 int base_index
= GPOS_apply_MarkToBase(script_cache
, look
, analysis
,
2499 glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
2500 if (base_index
!= -1)
2502 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2503 if (!analysis
->fRTL
) pGoffset
[glyph_index
].du
= round(devX
) - piAdvance
[base_index
];
2506 if (analysis
->fLogicalOrder
) devX
*= -1;
2507 pGoffset
[glyph_index
].du
= round(devX
);
2509 pGoffset
[glyph_index
].dv
= round(devY
);
2514 case GPOS_LOOKUP_ATTACH_MARK_TO_LIGATURE
:
2518 GPOS_apply_MarkToLigature(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
2519 if (desU
.x
|| desU
.y
)
2521 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2522 pGoffset
[glyph_index
].du
= (round(devX
) - piAdvance
[glyph_index
-1]);
2523 pGoffset
[glyph_index
].dv
= round(devY
);
2528 case GPOS_LOOKUP_ATTACH_MARK_TO_MARK
:
2532 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2533 if (GPOS_apply_MarkToMark(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
))
2535 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2536 if (analysis
->fRTL
&& analysis
->fLogicalOrder
) devX
*= -1;
2537 pGoffset
[glyph_index
].du
= round(devX
) + pGoffset
[glyph_index
- write_dir
].du
;
2538 pGoffset
[glyph_index
].dv
= round(devY
) + pGoffset
[glyph_index
- write_dir
].dv
;
2543 case GPOS_LOOKUP_POSITION_CONTEXT
:
2544 return GPOS_apply_ContextPos(script_cache
, lpotm
, lplogfont
, analysis
, piAdvance
,
2545 lookup
, look
, glyphs
, glyph_index
, glyph_count
, pGoffset
);
2547 case GPOS_LOOKUP_POSITION_CONTEXT_CHAINED
:
2548 return GPOS_apply_ChainContextPos(script_cache
, lpotm
, lplogfont
, analysis
, piAdvance
,
2549 lookup
, look
, glyphs
, glyph_index
, glyph_count
, pGoffset
);
2552 FIXME("Unhandled GPOS lookup type %#x.\n", type
);
2557 unsigned int OpenType_apply_GPOS_lookup(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
2558 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, unsigned int lookup_index
,
2559 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, GOFFSET
*goffset
)
2561 const GPOS_Header
*header
= (const GPOS_Header
*)script_cache
->GPOS_Table
;
2562 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
2564 return GPOS_apply_lookup(script_cache
, otm
, logfont
, analysis
, advance
, lookup
,
2565 lookup_index
, glyphs
, glyph_index
, glyph_count
, goffset
);
2568 static LoadedScript
*usp10_script_cache_get_script(ScriptCache
*script_cache
, OPENTYPE_TAG tag
)
2572 for (i
= 0; i
< script_cache
->script_count
; ++i
)
2574 if (script_cache
->scripts
[i
].tag
== tag
)
2575 return &script_cache
->scripts
[i
];
2581 static void GSUB_initialize_script_cache(ScriptCache
*psc
)
2585 if (psc
->GSUB_Table
)
2587 const OT_ScriptList
*script
;
2588 const GSUB_Header
* header
= (const GSUB_Header
*)psc
->GSUB_Table
;
2589 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
2590 psc
->script_count
= GET_BE_WORD(script
->ScriptCount
);
2591 TRACE("initializing %li scripts in this font\n",psc
->script_count
);
2592 if (psc
->script_count
)
2594 psc
->scripts
= heap_alloc_zero(psc
->script_count
* sizeof(*psc
->scripts
));
2595 for (i
= 0; i
< psc
->script_count
; i
++)
2597 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2598 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]);
2599 psc
->scripts
[i
].gsub_table
= ((const BYTE
*)script
+ offset
);
2605 static void GPOS_expand_script_cache(ScriptCache
*psc
)
2608 const OT_ScriptList
*script
;
2609 const GPOS_Header
* header
= (const GPOS_Header
*)psc
->GPOS_Table
;
2610 LoadedScript
*loaded_script
;
2615 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
2616 count
= GET_BE_WORD(script
->ScriptCount
);
2621 if (!psc
->script_count
)
2623 psc
->script_count
= count
;
2624 TRACE("initializing %li scripts in this font\n",psc
->script_count
);
2625 if (psc
->script_count
)
2627 psc
->scripts
= heap_alloc_zero(psc
->script_count
* sizeof(*psc
->scripts
));
2628 for (i
= 0; i
< psc
->script_count
; i
++)
2630 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2631 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]);
2632 psc
->scripts
[i
].gpos_table
= ((const BYTE
*)script
+ offset
);
2638 for (i
= 0; i
< count
; i
++)
2640 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2641 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]);
2643 if (!(loaded_script
= usp10_script_cache_get_script(psc
, tag
)))
2645 if (!usp10_array_reserve((void **)&psc
->scripts
, &psc
->scripts_size
,
2646 psc
->script_count
+ 1, sizeof(*psc
->scripts
)))
2648 ERR("Failed grow scripts array.\n");
2652 loaded_script
= &psc
->scripts
[psc
->script_count
];
2653 ++psc
->script_count
;
2654 loaded_script
->tag
= tag
;
2656 loaded_script
->gpos_table
= (const BYTE
*)script
+ offset
;
2661 static void _initialize_script_cache(ScriptCache
*psc
)
2663 if (!psc
->scripts_initialized
)
2665 GSUB_initialize_script_cache(psc
);
2666 GPOS_expand_script_cache(psc
);
2667 psc
->scripts_initialized
= TRUE
;
2671 HRESULT
OpenType_GetFontScriptTags(ScriptCache
*psc
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pScriptTags
, int *pcTags
)
2674 const LoadedScript
*script
;
2677 _initialize_script_cache(psc
);
2679 *pcTags
= psc
->script_count
;
2683 if (!(script
= usp10_script_cache_get_script(psc
, searchingFor
)))
2684 return USP_E_SCRIPT_NOT_IN_FONT
;
2686 *pScriptTags
= script
->tag
;
2691 if (cMaxTags
< *pcTags
)
2694 cMaxTags
= min(cMaxTags
, psc
->script_count
);
2695 for (i
= 0; i
< cMaxTags
; ++i
)
2697 pScriptTags
[i
] = psc
->scripts
[i
].tag
;
2702 static LoadedLanguage
*usp10_script_get_language(LoadedScript
*script
, OPENTYPE_TAG tag
)
2706 for (i
= 0; i
< script
->language_count
; ++i
)
2708 if (script
->languages
[i
].tag
== tag
)
2709 return &script
->languages
[i
];
2715 static void GSUB_initialize_language_cache(LoadedScript
*script
)
2719 if (script
->gsub_table
)
2722 const OT_Script
* table
= script
->gsub_table
;
2723 script
->language_count
= GET_BE_WORD(table
->LangSysCount
);
2724 offset
= GET_BE_WORD(table
->DefaultLangSys
);
2727 script
->default_language
.tag
= MS_MAKE_TAG('d','f','l','t');
2728 script
->default_language
.gsub_table
= (const BYTE
*)table
+ offset
;
2731 if (script
->language_count
)
2733 TRACE("Deflang %p, LangCount %li\n",script
->default_language
.gsub_table
, script
->language_count
);
2735 script
->languages
= heap_alloc_zero(script
->language_count
* sizeof(*script
->languages
));
2737 for (i
= 0; i
< script
->language_count
; i
++)
2739 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2740 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]);
2741 script
->languages
[i
].gsub_table
= ((const BYTE
*)table
+ offset
);
2747 static void GPOS_expand_language_cache(LoadedScript
*script
)
2750 const OT_Script
* table
= script
->gpos_table
;
2751 LoadedLanguage
*language
;
2757 offset
= GET_BE_WORD(table
->DefaultLangSys
);
2759 script
->default_language
.gpos_table
= (const BYTE
*)table
+ offset
;
2761 count
= GET_BE_WORD(table
->LangSysCount
);
2763 TRACE("Deflang %p, LangCount %i\n",script
->default_language
.gpos_table
, count
);
2768 if (!script
->language_count
)
2771 script
->language_count
= count
;
2773 script
->languages
= heap_alloc_zero(script
->language_count
* sizeof(*script
->languages
));
2775 for (i
= 0; i
< script
->language_count
; i
++)
2777 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2778 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]);
2779 script
->languages
[i
].gpos_table
= ((const BYTE
*)table
+ offset
);
2785 for (i
= 0; i
< count
; i
++)
2787 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2788 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]);
2790 if (!(language
= usp10_script_get_language(script
, tag
)))
2792 if (!usp10_array_reserve((void **)&script
->languages
, &script
->languages_size
,
2793 script
->language_count
+ 1, sizeof(*script
->languages
)))
2795 ERR("Failed grow languages array.\n");
2799 language
= &script
->languages
[script
->language_count
];
2800 ++script
->language_count
;
2801 language
->tag
= tag
;
2803 language
->gpos_table
= (const BYTE
*)table
+ offset
;
2808 static void _initialize_language_cache(LoadedScript
*script
)
2810 if (!script
->languages_initialized
)
2812 GSUB_initialize_language_cache(script
);
2813 GPOS_expand_language_cache(script
);
2814 script
->languages_initialized
= TRUE
;
2818 HRESULT
OpenType_GetFontLanguageTags(ScriptCache
*psc
, OPENTYPE_TAG script_tag
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pLanguageTags
, int *pcTags
)
2822 LoadedScript
*script
= NULL
;
2824 _initialize_script_cache(psc
);
2825 if (!(script
= usp10_script_cache_get_script(psc
, script_tag
)))
2826 return E_INVALIDARG
;
2828 _initialize_language_cache(script
);
2830 if (!searchingFor
&& cMaxTags
< script
->language_count
)
2832 else if (searchingFor
)
2835 *pcTags
= script
->language_count
;
2837 for (i
= 0; i
< script
->language_count
; i
++)
2840 pLanguageTags
[i
] = script
->languages
[i
].tag
;
2844 if (searchingFor
== script
->languages
[i
].tag
)
2846 pLanguageTags
[0] = script
->languages
[i
].tag
;
2854 if (script
->default_language
.gsub_table
)
2857 pLanguageTags
[i
] = script
->default_language
.tag
;
2859 if (searchingFor
&& FAILED(rc
))
2861 pLanguageTags
[0] = script
->default_language
.tag
;
2864 *pcTags
= (*pcTags
) + 1;
2870 static void usp10_language_add_feature_list(LoadedLanguage
*language
, char table_type
,
2871 const OT_LangSys
*lang
, const OT_FeatureList
*feature_list
)
2873 unsigned int count
= GET_BE_WORD(lang
->FeatureCount
);
2876 TRACE("table_type %#x, %u features.\n", table_type
, count
);
2881 if (!language
->feature_count
)
2882 language
->features
= heap_alloc(count
* sizeof(*language
->features
));
2884 language
->features
= HeapReAlloc(GetProcessHeap(), 0, language
->features
,
2885 (language
->feature_count
+ count
) * sizeof(*language
->features
));
2887 for (i
= 0; i
< count
; ++i
)
2889 const OT_FeatureRecord
*record
;
2890 LoadedFeature
*loaded_feature
;
2891 const OT_Feature
*feature
;
2893 record
= &feature_list
->FeatureRecord
[GET_BE_WORD(lang
->FeatureIndex
[i
])];
2894 feature
= (const OT_Feature
*)((const BYTE
*)feature_list
+ GET_BE_WORD(record
->Feature
));
2896 loaded_feature
= &language
->features
[language
->feature_count
+ i
];
2897 loaded_feature
->tag
= MS_MAKE_TAG(record
->FeatureTag
[0], record
->FeatureTag
[1],
2898 record
->FeatureTag
[2], record
->FeatureTag
[3]);
2899 loaded_feature
->tableType
= table_type
;
2900 loaded_feature
->feature
= feature
;
2901 loaded_feature
->lookup_count
= GET_BE_WORD(feature
->LookupCount
);
2902 loaded_feature
->lookups
= heap_alloc(loaded_feature
->lookup_count
* sizeof(*loaded_feature
->lookups
));
2903 for (j
= 0; j
< loaded_feature
->lookup_count
; ++j
)
2904 loaded_feature
->lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
2906 language
->feature_count
+= count
;
2909 static void _initialize_feature_cache(ScriptCache
*psc
, LoadedLanguage
*language
)
2911 const GSUB_Header
*gsub_header
= psc
->GSUB_Table
;
2912 const GPOS_Header
*gpos_header
= psc
->GPOS_Table
;
2913 const OT_FeatureList
*feature_list
;
2914 const OT_LangSys
*lang
;
2916 if (language
->features_initialized
)
2919 if ((lang
= language
->gsub_table
))
2921 feature_list
= (const OT_FeatureList
*)((const BYTE
*)gsub_header
+ GET_BE_WORD(gsub_header
->FeatureList
));
2922 usp10_language_add_feature_list(language
, FEATURE_GSUB_TABLE
, lang
, feature_list
);
2925 if ((lang
= language
->gpos_table
))
2927 feature_list
= (const OT_FeatureList
*)((const BYTE
*)gpos_header
+ GET_BE_WORD(gpos_header
->FeatureList
));
2928 usp10_language_add_feature_list(language
, FEATURE_GPOS_TABLE
, lang
, feature_list
);
2931 language
->features_initialized
= TRUE
;
2934 HRESULT
OpenType_GetFontFeatureTags(ScriptCache
*psc
, OPENTYPE_TAG script_tag
, OPENTYPE_TAG language_tag
, BOOL filtered
, OPENTYPE_TAG searchingFor
, char tableType
, int cMaxTags
, OPENTYPE_TAG
*pFeatureTags
, int *pcTags
, LoadedFeature
** feature
)
2937 LoadedScript
*script
;
2939 LoadedLanguage
*language
= NULL
;
2941 _initialize_script_cache(psc
);
2942 if (!(script
= usp10_script_cache_get_script(psc
, script_tag
)))
2948 return E_INVALIDARG
;
2951 _initialize_language_cache(script
);
2953 if ((script
->default_language
.gsub_table
|| script
->default_language
.gpos_table
) && script
->default_language
.tag
== language_tag
)
2954 language
= &script
->default_language
;
2956 language
= usp10_script_get_language(script
, language_tag
);
2964 _initialize_feature_cache(psc
, language
);
2969 for (i
= 0; i
< language
->feature_count
; i
++)
2970 if (language
->features
[i
].tableType
== tableType
)
2971 *pcTags
= (*pcTags
)+1;
2974 *pcTags
= language
->feature_count
;
2976 if (!searchingFor
&& cMaxTags
< *pcTags
)
2978 else if (searchingFor
)
2981 for (i
= 0; i
< language
->feature_count
; i
++)
2985 if (!tableType
|| language
->features
[i
].tableType
== tableType
)
2986 pFeatureTags
[i
] = language
->features
[i
].tag
;
2991 if ((searchingFor
== language
->features
[i
].tag
) &&
2992 (!tableType
|| language
->features
[i
].tableType
== tableType
))
2994 pFeatureTags
[0] = language
->features
[i
].tag
;
2997 *feature
= &language
->features
[i
];