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
22 #include "usp10_internal.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe
);
28 #ifdef WORDS_BIGENDIAN
29 #define GET_BE_WORD(x) (x)
30 #define GET_BE_DWORD(x) (x)
32 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
33 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
36 #define round(x) (((x) < 0) ? (int)((x) - 0.5) : (int)((x) + 0.5))
38 /* These are all structures needed for the cmap format 12 table */
39 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
43 GPOS_LOOKUP_ADJUST_SINGLE
= 0x1,
44 GPOS_LOOKUP_ADJUST_PAIR
= 0x2,
45 GPOS_LOOKUP_ATTACH_CURSIVE
= 0x3,
46 GPOS_LOOKUP_ATTACH_MARK_TO_BASE
= 0x4,
47 GPOS_LOOKUP_ATTACH_MARK_TO_LIGATURE
= 0x5,
48 GPOS_LOOKUP_ATTACH_MARK_TO_MARK
= 0x6,
49 GPOS_LOOKUP_POSITION_CONTEXT
= 0x7,
50 GPOS_LOOKUP_POSITION_CONTEXT_CHAINED
= 0x8,
51 GPOS_LOOKUP_POSITION_EXTENSION
= 0x9,
56 GSUB_LOOKUP_SINGLE
= 0x1,
57 GSUB_LOOKUP_MULTIPLE
= 0x2,
58 GSUB_LOOKUP_ALTERNATE
= 0x3,
59 GSUB_LOOKUP_LIGATURE
= 0x4,
60 GSUB_LOOKUP_CONTEXT
= 0x5,
61 GSUB_LOOKUP_CONTEXT_CHAINED
= 0x6,
62 GSUB_LOOKUP_EXTENSION
= 0x7,
63 GSUB_LOOKUP_CONTEXT_CHAINED_REVERSE
= 0x8,
70 } CMAP_EncodingRecord
;
75 CMAP_EncodingRecord tables
[1];
82 } CMAP_SegmentedCoverage_group
;
90 CMAP_SegmentedCoverage_group groups
[1];
91 } CMAP_SegmentedCoverage
;
93 /* These are all structures needed for the GDEF table */
94 enum {BaseGlyph
=1, LigatureGlyph
, MarkGlyph
, ComponentGlyph
};
101 WORD MarkAttachClassDef
;
108 WORD ClassValueArray
[1];
109 } OT_ClassDefFormat1
;
115 } OT_ClassRangeRecord
;
119 WORD ClassRangeCount
;
120 OT_ClassRangeRecord ClassRangeRecord
[1];
121 } OT_ClassDefFormat2
;
123 /* These are all structures needed for the GSUB table */
139 OT_ScriptRecord ScriptRecord
[1];
150 OT_LangSysRecord LangSysRecord
[1];
154 WORD LookupOrder
; /* Reserved */
155 WORD ReqFeatureIndex
;
157 WORD FeatureIndex
[1];
167 OT_FeatureRecord FeatureRecord
[1];
171 WORD FeatureParams
; /* Reserved */
173 WORD LookupListIndex
[1];
192 } OT_CoverageFormat1
;
197 WORD StartCoverageIndex
;
203 OT_RangeRecord RangeRecord
[1];
204 } OT_CoverageFormat2
;
207 WORD SubstFormat
; /* = 1 */
210 } GSUB_SingleSubstFormat1
;
213 WORD SubstFormat
; /* = 2 */
217 }GSUB_SingleSubstFormat2
;
220 WORD SubstFormat
; /* = 1 */
224 }GSUB_MultipleSubstFormat1
;
232 WORD SubstFormat
; /* = 1 */
236 }GSUB_LigatureSubstFormat1
;
251 WORD LookupListIndex
;
253 }GSUB_SubstLookupRecord
;
258 WORD SubRuleSetCount
;
260 }GSUB_ContextSubstFormat1
;
274 GSUB_SubstLookupRecord SubstLookupRecord
[1];
283 }GSUB_ContextSubstFormat2
;
286 WORD SubClassRuleCnt
;
287 WORD SubClassRule
[1];
294 }GSUB_SubClassRule_1
;
297 GSUB_SubstLookupRecord SubstLookupRecord
[1];
298 }GSUB_SubClassRule_2
;
301 WORD SubstFormat
; /* = 1 */
303 WORD ChainSubRuleSetCount
;
304 WORD ChainSubRuleSet
[1];
305 }GSUB_ChainContextSubstFormat1
;
308 WORD SubstFormat
; /* = 2 */
310 WORD BacktrackClassDef
;
312 WORD LookaheadClassDef
;
313 WORD ChainSubClassSetCnt
;
314 WORD ChainSubClassSet
[1];
315 }GSUB_ChainContextSubstFormat2
;
318 WORD ChainSubClassRuleCnt
;
319 WORD ChainSubClassRule
[1];
320 }GSUB_ChainSubClassSet
;
323 WORD BacktrackGlyphCount
;
325 }GSUB_ChainSubClassRule_1
;
328 WORD InputGlyphCount
;
330 }GSUB_ChainSubClassRule_2
;
333 WORD LookaheadGlyphCount
;
335 }GSUB_ChainSubClassRule_3
;
339 GSUB_SubstLookupRecord SubstLookupRecord
[1];
340 }GSUB_ChainSubClassRule_4
;
343 WORD SubstFormat
; /* = 3 */
344 WORD BacktrackGlyphCount
;
346 }GSUB_ChainContextSubstFormat3_1
;
349 WORD InputGlyphCount
;
351 }GSUB_ChainContextSubstFormat3_2
;
354 WORD LookaheadGlyphCount
;
356 }GSUB_ChainContextSubstFormat3_3
;
360 GSUB_SubstLookupRecord SubstLookupRecord
[1];
361 }GSUB_ChainContextSubstFormat3_4
;
364 WORD SubstFormat
; /* = 1 */
366 WORD AlternateSetCount
;
367 WORD AlternateSet
[1];
368 } GSUB_AlternateSubstFormat1
;
377 WORD ExtensionLookupType
;
378 DWORD ExtensionOffset
;
379 } GSUB_ExtensionPosFormat1
;
381 /* These are all structures needed for the GPOS table */
401 } GPOS_AnchorFormat1
;
408 } GPOS_AnchorFormat2
;
416 } GPOS_AnchorFormat3
;
434 } GPOS_SinglePosFormat1
;
442 } GPOS_SinglePosFormat2
;
450 WORD PairSetOffset
[1];
451 } GPOS_PairPosFormat1
;
462 WORD Class1Record
[1];
463 } GPOS_PairPosFormat2
;
469 } GPOS_PairValueRecord
;
473 GPOS_PairValueRecord PairValueRecord
[1];
479 } GPOS_EntryExitRecord
;
485 GPOS_EntryExitRecord EntryExitRecord
[1];
486 } GPOS_CursivePosFormat1
;
495 } GPOS_MarkBasePosFormat1
;
503 GPOS_BaseRecord BaseRecord
[1];
513 GPOS_MarkRecord MarkRecord
[1];
519 WORD LigatureCoverage
;
523 } GPOS_MarkLigPosFormat1
;
527 WORD LigatureAttach
[1];
528 } GPOS_LigatureArray
;
531 WORD LigatureAnchor
[1];
532 } GPOS_ComponentRecord
;
536 GPOS_ComponentRecord ComponentRecord
[1];
537 } GPOS_LigatureAttach
;
546 } GPOS_MarkMarkPosFormat1
;
554 GPOS_Mark2Record Mark2Record
[1];
559 WORD LookupListIndex
;
560 } GPOS_PosLookupRecord
;
568 } GPOS_ContextPosFormat2
;
571 WORD PosClassRuleCnt
;
572 WORD PosClassRule
[1];
579 } GPOS_PosClassRule_1
;
582 GPOS_PosLookupRecord PosLookupRecord
[1];
583 } GPOS_PosClassRule_2
;
587 WORD BacktrackGlyphCount
;
589 } GPOS_ChainContextPosFormat3_1
;
592 WORD InputGlyphCount
;
594 } GPOS_ChainContextPosFormat3_2
;
597 WORD LookaheadGlyphCount
;
599 } GPOS_ChainContextPosFormat3_3
;
603 GPOS_PosLookupRecord PosLookupRecord
[1];
604 } GPOS_ChainContextPosFormat3_4
;
608 WORD ExtensionLookupType
;
609 DWORD ExtensionOffset
;
610 } GPOS_ExtensionPosFormat1
;
616 static VOID
*load_CMAP_format12_table(HDC hdc
, ScriptCache
*psc
)
618 CMAP_Header
*CMAP_Table
= NULL
;
622 if (!psc
->CMAP_Table
)
624 length
= GetFontData(hdc
, CMAP_TAG
, 0, NULL
, 0);
625 if (length
!= GDI_ERROR
)
627 psc
->CMAP_Table
= heap_alloc(length
);
628 GetFontData(hdc
, CMAP_TAG
, 0, psc
->CMAP_Table
, length
);
629 TRACE("Loaded cmap table of %i bytes\n",length
);
635 CMAP_Table
= psc
->CMAP_Table
;
637 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
); i
++)
639 if ( (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) == 3) &&
640 (GET_BE_WORD(CMAP_Table
->tables
[i
].encodingID
) == 10) )
642 CMAP_SegmentedCoverage
*format
= (CMAP_SegmentedCoverage
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
643 if (GET_BE_WORD(format
->format
) == 12)
650 static int compare_group(const void *a
, const void* b
)
652 const DWORD
*chr
= a
;
653 const CMAP_SegmentedCoverage_group
*group
= b
;
655 if (*chr
< GET_BE_DWORD(group
->startCharCode
))
657 if (*chr
> GET_BE_DWORD(group
->endCharCode
))
662 DWORD
OpenType_CMAP_GetGlyphIndex(HDC hdc
, ScriptCache
*psc
, DWORD utf32c
, WORD
*glyph_index
, DWORD flags
)
664 /* BMP: use gdi32 for ease */
665 if (utf32c
< 0x10000)
668 return GetGlyphIndicesW(hdc
, &ch
, 1, glyph_index
, flags
);
671 if (!psc
->CMAP_format12_Table
)
672 psc
->CMAP_format12_Table
= load_CMAP_format12_table(hdc
, psc
);
674 if (flags
& GGI_MARK_NONEXISTING_GLYPHS
)
675 *glyph_index
= 0xffffu
;
679 if (psc
->CMAP_format12_Table
)
681 CMAP_SegmentedCoverage
*format
= NULL
;
682 CMAP_SegmentedCoverage_group
*group
= NULL
;
684 format
= (CMAP_SegmentedCoverage
*)psc
->CMAP_format12_Table
;
686 group
= bsearch(&utf32c
, format
->groups
, GET_BE_DWORD(format
->nGroups
),
687 sizeof(CMAP_SegmentedCoverage_group
), compare_group
);
691 DWORD offset
= utf32c
- GET_BE_DWORD(group
->startCharCode
);
692 *glyph_index
= GET_BE_DWORD(group
->startGlyphID
) + offset
;
703 static WORD
OT_get_glyph_class(const void *table
, WORD glyph
)
706 const OT_ClassDefFormat1
*cf1
= table
;
708 if (!table
) return 0;
710 if (GET_BE_WORD(cf1
->ClassFormat
) == 1)
712 if (glyph
>= GET_BE_WORD(cf1
->StartGlyph
))
714 int index
= glyph
- GET_BE_WORD(cf1
->StartGlyph
);
715 if (index
< GET_BE_WORD(cf1
->GlyphCount
))
716 class = GET_BE_WORD(cf1
->ClassValueArray
[index
]);
719 else if (GET_BE_WORD(cf1
->ClassFormat
) == 2)
721 const OT_ClassDefFormat2
*cf2
= table
;
723 top
= GET_BE_WORD(cf2
->ClassRangeCount
);
724 for (i
= 0; i
< top
; i
++)
726 if (glyph
>= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Start
) &&
727 glyph
<= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].End
))
729 class = GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Class
);
735 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1
->ClassFormat
));
740 void OpenType_GDEF_UpdateGlyphProps(ScriptCache
*psc
, const WORD
*pwGlyphs
, const WORD cGlyphs
, WORD
* pwLogClust
, const WORD cChars
, SCRIPT_GLYPHPROP
*pGlyphProp
)
743 void *glyph_class_table
= NULL
;
747 const GDEF_Header
*header
= psc
->GDEF_Table
;
748 WORD offset
= GET_BE_WORD( header
->GlyphClassDef
);
750 glyph_class_table
= (BYTE
*)psc
->GDEF_Table
+ offset
;
753 for (i
= 0; i
< cGlyphs
; i
++)
759 k
= USP10_FindGlyphInLogClust(pwLogClust
, cChars
, i
);
762 for (; k
< cChars
&& pwLogClust
[k
] == i
; k
++)
766 class = OT_get_glyph_class( glyph_class_table
, pwGlyphs
[i
] );
772 pGlyphProp
[i
].sva
.fClusterStart
= 1;
773 pGlyphProp
[i
].sva
.fDiacritic
= 0;
774 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
777 pGlyphProp
[i
].sva
.fClusterStart
= 1;
778 pGlyphProp
[i
].sva
.fDiacritic
= 0;
779 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
782 pGlyphProp
[i
].sva
.fClusterStart
= 0;
783 pGlyphProp
[i
].sva
.fDiacritic
= 1;
784 pGlyphProp
[i
].sva
.fZeroWidth
= 1;
787 pGlyphProp
[i
].sva
.fClusterStart
= 0;
788 pGlyphProp
[i
].sva
.fDiacritic
= 0;
789 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
792 ERR("Unknown glyph class %i\n",class);
793 pGlyphProp
[i
].sva
.fClusterStart
= 1;
794 pGlyphProp
[i
].sva
.fDiacritic
= 0;
795 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
799 pGlyphProp
[i
].sva
.fClusterStart
= 0;
806 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
);
808 static int GSUB_is_glyph_covered(const void *table
, unsigned int glyph
)
810 const OT_CoverageFormat1
* cf1
;
814 if (GET_BE_WORD(cf1
->CoverageFormat
) == 1)
816 int count
= GET_BE_WORD(cf1
->GlyphCount
);
818 TRACE("Coverage Format 1, %i glyphs\n",count
);
819 for (i
= 0; i
< count
; i
++)
820 if (glyph
== GET_BE_WORD(cf1
->GlyphArray
[i
]))
824 else if (GET_BE_WORD(cf1
->CoverageFormat
) == 2)
826 const OT_CoverageFormat2
* cf2
;
829 cf2
= (const OT_CoverageFormat2
*)cf1
;
831 count
= GET_BE_WORD(cf2
->RangeCount
);
832 TRACE("Coverage Format 2, %i ranges\n",count
);
833 for (i
= 0; i
< count
; i
++)
835 if (glyph
< GET_BE_WORD(cf2
->RangeRecord
[i
].Start
))
837 if ((glyph
>= GET_BE_WORD(cf2
->RangeRecord
[i
].Start
)) &&
838 (glyph
<= GET_BE_WORD(cf2
->RangeRecord
[i
].End
)))
840 return (GET_BE_WORD(cf2
->RangeRecord
[i
].StartCoverageIndex
) +
841 glyph
- GET_BE_WORD(cf2
->RangeRecord
[i
].Start
));
847 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1
->CoverageFormat
));
852 static const BYTE
*GSUB_get_subtable(const OT_LookupTable
*look
, int index
)
854 int offset
= GET_BE_WORD(look
->SubTable
[index
]);
856 if (GET_BE_WORD(look
->LookupType
) == GSUB_LOOKUP_EXTENSION
)
858 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)look
+ offset
);
859 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
861 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
865 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext
->SubstFormat
));
868 return (const BYTE
*)look
+ offset
;
871 static INT
GSUB_apply_SingleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
874 TRACE("Single Substitution Subtable\n");
876 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
878 const GSUB_SingleSubstFormat1
*ssf1
= (const GSUB_SingleSubstFormat1
*)GSUB_get_subtable(look
, j
);
879 if (GET_BE_WORD(ssf1
->SubstFormat
) == 1)
881 int offset
= GET_BE_WORD(ssf1
->Coverage
);
882 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1
->DeltaGlyphID
));
883 if (GSUB_is_glyph_covered((const BYTE
*)ssf1
+offset
, glyphs
[glyph_index
]) != -1)
885 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
886 glyphs
[glyph_index
] = glyphs
[glyph_index
] + GET_BE_WORD(ssf1
->DeltaGlyphID
);
887 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
888 return glyph_index
+ write_dir
;
893 const GSUB_SingleSubstFormat2
*ssf2
;
897 ssf2
= (const GSUB_SingleSubstFormat2
*)ssf1
;
898 offset
= GET_BE_WORD(ssf1
->Coverage
);
899 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2
->GlyphCount
));
900 index
= GSUB_is_glyph_covered((const BYTE
*)ssf2
+offset
, glyphs
[glyph_index
]);
901 TRACE(" Coverage index %i\n",index
);
904 if (glyphs
[glyph_index
] == GET_BE_WORD(ssf2
->Substitute
[index
]))
905 return GSUB_E_NOGLYPH
;
907 TRACE(" Glyph is 0x%x ->",glyphs
[glyph_index
]);
908 glyphs
[glyph_index
] = GET_BE_WORD(ssf2
->Substitute
[index
]);
909 TRACE("0x%x\n",glyphs
[glyph_index
]);
910 return glyph_index
+ write_dir
;
914 return GSUB_E_NOGLYPH
;
917 static INT
GSUB_apply_MultipleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
920 TRACE("Multiple Substitution Subtable\n");
922 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
925 const GSUB_MultipleSubstFormat1
*msf1
;
926 msf1
= (const GSUB_MultipleSubstFormat1
*)GSUB_get_subtable(look
, j
);
928 offset
= GET_BE_WORD(msf1
->Coverage
);
929 index
= GSUB_is_glyph_covered((const BYTE
*)msf1
+offset
, glyphs
[glyph_index
]);
932 const GSUB_Sequence
*seq
;
935 offset
= GET_BE_WORD(msf1
->Sequence
[index
]);
936 seq
= (const GSUB_Sequence
*)((const BYTE
*)msf1
+offset
);
937 sub_count
= GET_BE_WORD(seq
->GlyphCount
);
938 TRACE(" Glyph 0x%x (+%i)->",glyphs
[glyph_index
],(sub_count
-1));
940 for (j
= (*glyph_count
)+(sub_count
-1); j
> glyph_index
; j
--)
941 glyphs
[j
] =glyphs
[j
-(sub_count
-1)];
943 for (j
= 0; j
< sub_count
; j
++)
945 glyphs
[glyph_index
+ (sub_count
-1) - j
] = GET_BE_WORD(seq
->Substitute
[j
]);
947 glyphs
[glyph_index
+ j
] = GET_BE_WORD(seq
->Substitute
[j
]);
949 *glyph_count
= *glyph_count
+ (sub_count
- 1);
951 if (TRACE_ON(uniscribe
))
953 for (j
= 0; j
< sub_count
; j
++)
954 TRACE(" 0x%x",glyphs
[glyph_index
+j
]);
959 return glyph_index
+ sub_count
;
961 return glyph_index
- 1;
964 return GSUB_E_NOGLYPH
;
967 static INT
GSUB_apply_AlternateSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
970 TRACE("Alternate Substitution Subtable\n");
972 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
975 const GSUB_AlternateSubstFormat1
*asf1
;
978 asf1
= (const GSUB_AlternateSubstFormat1
*)GSUB_get_subtable(look
, j
);
979 offset
= GET_BE_WORD(asf1
->Coverage
);
981 index
= GSUB_is_glyph_covered((const BYTE
*)asf1
+offset
, glyphs
[glyph_index
]);
984 const GSUB_AlternateSet
*as
;
985 offset
= GET_BE_WORD(asf1
->AlternateSet
[index
]);
986 as
= (const GSUB_AlternateSet
*)((const BYTE
*)asf1
+offset
);
987 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as
->GlyphCount
));
988 if (glyphs
[glyph_index
] == GET_BE_WORD(as
->Alternate
[0]))
989 return GSUB_E_NOGLYPH
;
991 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
992 glyphs
[glyph_index
] = GET_BE_WORD(as
->Alternate
[0]);
993 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
994 return glyph_index
+ write_dir
;
997 return GSUB_E_NOGLYPH
;
1000 static INT
GSUB_apply_LigatureSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1004 TRACE("Ligature Substitution Subtable\n");
1005 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1007 const GSUB_LigatureSubstFormat1
*lsf1
;
1010 lsf1
= (const GSUB_LigatureSubstFormat1
*)GSUB_get_subtable(look
, j
);
1011 offset
= GET_BE_WORD(lsf1
->Coverage
);
1012 index
= GSUB_is_glyph_covered((const BYTE
*)lsf1
+offset
, glyphs
[glyph_index
]);
1013 TRACE(" Coverage index %i\n",index
);
1016 const GSUB_LigatureSet
*ls
;
1019 offset
= GET_BE_WORD(lsf1
->LigatureSet
[index
]);
1020 ls
= (const GSUB_LigatureSet
*)((const BYTE
*)lsf1
+offset
);
1021 count
= GET_BE_WORD(ls
->LigatureCount
);
1022 TRACE(" LigatureSet has %i members\n",count
);
1023 for (k
= 0; k
< count
; k
++)
1025 const GSUB_Ligature
*lig
;
1026 int CompCount
,l
,CompIndex
;
1028 offset
= GET_BE_WORD(ls
->Ligature
[k
]);
1029 lig
= (const GSUB_Ligature
*)((const BYTE
*)ls
+offset
);
1030 CompCount
= GET_BE_WORD(lig
->CompCount
) - 1;
1031 CompIndex
= glyph_index
+write_dir
;
1032 for (l
= 0; l
< CompCount
&& CompIndex
>= 0 && CompIndex
< *glyph_count
; l
++)
1035 CompGlyph
= GET_BE_WORD(lig
->Component
[l
]);
1036 if (CompGlyph
!= glyphs
[CompIndex
])
1038 CompIndex
+= write_dir
;
1042 int replaceIdx
= glyph_index
;
1044 replaceIdx
= glyph_index
- CompCount
;
1046 TRACE(" Glyph is 0x%x (+%i) ->",glyphs
[glyph_index
],CompCount
);
1047 glyphs
[replaceIdx
] = GET_BE_WORD(lig
->LigGlyph
);
1048 TRACE("0x%x\n",glyphs
[replaceIdx
]);
1051 unsigned int j
= replaceIdx
+ 1;
1052 memmove(&glyphs
[j
], &glyphs
[j
+ CompCount
], (*glyph_count
- j
) * sizeof(*glyphs
));
1053 *glyph_count
= *glyph_count
- CompCount
;
1055 return replaceIdx
+ write_dir
;
1060 return GSUB_E_NOGLYPH
;
1063 static INT
GSUB_apply_ContextSubst(const OT_LookupList
* lookup
, const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1066 TRACE("Context Substitution Subtable\n");
1067 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1069 const GSUB_ContextSubstFormat1
*csf1
;
1071 csf1
= (const GSUB_ContextSubstFormat1
*)GSUB_get_subtable(look
, j
);
1072 if (GET_BE_WORD(csf1
->SubstFormat
) == 1)
1075 TRACE("Context Substitution Subtable: Class 1\n");
1076 offset
= GET_BE_WORD(csf1
->Coverage
);
1077 index
= GSUB_is_glyph_covered((const BYTE
*)csf1
+offset
, glyphs
[glyph_index
]);
1078 TRACE(" Coverage index %i\n",index
);
1082 const GSUB_SubRuleSet
*srs
;
1083 offset
= GET_BE_WORD(csf1
->SubRuleSet
[index
]);
1084 srs
= (const GSUB_SubRuleSet
*)((const BYTE
*)csf1
+offset
);
1085 count
= GET_BE_WORD(srs
->SubRuleCount
);
1086 TRACE(" SubRuleSet has %i members\n",count
);
1087 for (k
= 0; k
< count
; k
++)
1089 const GSUB_SubRule_1
*sr
;
1090 const GSUB_SubRule_2
*sr_2
;
1093 int newIndex
= glyph_index
;
1095 offset
= GET_BE_WORD(srs
->SubRule
[k
]);
1096 sr
= (const GSUB_SubRule_1
*)((const BYTE
*)srs
+offset
);
1097 g_count
= GET_BE_WORD(sr
->GlyphCount
);
1098 TRACE(" SubRule has %i glyphs\n",g_count
);
1100 g
= glyph_index
+ write_dir
* (g_count
- 1);
1101 if (g
>= *glyph_count
)
1104 for (l
= 0; l
< g_count
-1; l
++)
1105 if (glyphs
[glyph_index
+ (write_dir
* (l
+1))] != GET_BE_WORD(sr
->Input
[l
])) break;
1109 TRACE(" Rule does not match\n");
1113 TRACE(" Rule matches\n");
1114 sr_2
= (const GSUB_SubRule_2
*)&sr
->Input
[g_count
- 1];
1116 for (l
= 0; l
< GET_BE_WORD(sr
->SubstCount
); l
++)
1118 unsigned int lookup_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].LookupListIndex
);
1119 unsigned int sequence_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].SequenceIndex
);
1121 g
= glyph_index
+ write_dir
* sequence_index
;
1122 if (g
>= *glyph_count
)
1124 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n",
1125 sequence_index
, glyph_index
, write_dir
);
1129 TRACE(" SUBST: %u -> %u %u.\n", l
, sequence_index
, lookup_index
);
1130 newIndex
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1131 if (newIndex
== GSUB_E_NOGLYPH
)
1133 ERR(" Chain failed to generate a glyph\n");
1141 else if (GET_BE_WORD(csf1
->SubstFormat
) == 2)
1143 const GSUB_ContextSubstFormat2
*csf2
;
1144 const void *glyph_class_table
;
1147 csf2
= (const GSUB_ContextSubstFormat2
*)csf1
;
1148 TRACE("Context Substitution Subtable: Class 2\n");
1149 offset
= GET_BE_WORD(csf2
->Coverage
);
1150 index
= GSUB_is_glyph_covered((const BYTE
*)csf2
+offset
, glyphs
[glyph_index
]);
1151 TRACE(" Coverage index %i\n",index
);
1154 int k
, count
, class;
1155 const GSUB_SubClassSet
*scs
;
1157 offset
= GET_BE_WORD(csf2
->ClassDef
);
1158 glyph_class_table
= (const BYTE
*)csf2
+ offset
;
1160 class = OT_get_glyph_class(glyph_class_table
,glyphs
[glyph_index
]);
1162 offset
= GET_BE_WORD(csf2
->SubClassSet
[class]);
1165 TRACE(" No class rule table for class %i\n",class);
1168 scs
= (const GSUB_SubClassSet
*)((const BYTE
*)csf2
+offset
);
1169 count
= GET_BE_WORD(scs
->SubClassRuleCnt
);
1170 TRACE(" SubClassSet has %i members\n",count
);
1171 for (k
= 0; k
< count
; k
++)
1173 const GSUB_SubClassRule_1
*sr
;
1174 const GSUB_SubClassRule_2
*sr_2
;
1177 int newIndex
= glyph_index
;
1179 offset
= GET_BE_WORD(scs
->SubClassRule
[k
]);
1180 sr
= (const GSUB_SubClassRule_1
*)((const BYTE
*)scs
+offset
);
1181 g_count
= GET_BE_WORD(sr
->GlyphCount
);
1182 TRACE(" SubClassRule has %i glyphs classes\n",g_count
);
1184 g
= glyph_index
+ write_dir
* (g_count
- 1);
1185 if (g
>= *glyph_count
)
1188 for (l
= 0; l
< g_count
-1; l
++)
1190 int g_class
= OT_get_glyph_class(glyph_class_table
, glyphs
[glyph_index
+ (write_dir
* (l
+1))]);
1191 if (g_class
!= GET_BE_WORD(sr
->Class
[l
])) break;
1196 TRACE(" Rule does not match\n");
1200 TRACE(" Rule matches\n");
1201 sr_2
= (const GSUB_SubClassRule_2
*)&sr
->Class
[g_count
- 1];
1203 for (l
= 0; l
< GET_BE_WORD(sr
->SubstCount
); l
++)
1205 unsigned int lookup_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].LookupListIndex
);
1206 unsigned int sequence_index
= GET_BE_WORD(sr_2
->SubstLookupRecord
[l
].SequenceIndex
);
1208 g
= glyph_index
+ write_dir
* sequence_index
;
1209 if (g
>= *glyph_count
)
1211 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n",
1212 sequence_index
, glyph_index
, write_dir
);
1216 TRACE(" SUBST: %u -> %u %u.\n", l
, sequence_index
, lookup_index
);
1217 newIndex
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1218 if (newIndex
== GSUB_E_NOGLYPH
)
1220 ERR(" Chain failed to generate a glyph\n");
1229 FIXME("Unhandled Context Substitution Format %i\n", GET_BE_WORD(csf1
->SubstFormat
));
1231 return GSUB_E_NOGLYPH
;
1234 static INT
GSUB_apply_ChainContextSubst(const OT_LookupList
* lookup
, const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1238 TRACE("Chaining Contextual Substitution Subtable\n");
1239 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1241 const GSUB_ChainContextSubstFormat1
*ccsf1
;
1243 int dirLookahead
= write_dir
;
1244 int dirBacktrack
= -1 * write_dir
;
1246 ccsf1
= (const GSUB_ChainContextSubstFormat1
*)GSUB_get_subtable(look
, j
);
1247 if (GET_BE_WORD(ccsf1
->SubstFormat
) == 1)
1251 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
1254 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 2)
1257 const void *backtrack_class_table
;
1258 const void *input_class_table
;
1259 const void *lookahead_class_table
;
1263 const GSUB_ChainContextSubstFormat2
*ccsf2
= (const GSUB_ChainContextSubstFormat2
*)ccsf1
;
1264 const GSUB_ChainSubClassSet
*csc
;
1266 TRACE(" subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
1268 offset
= GET_BE_WORD(ccsf2
->Coverage
);
1270 if (GSUB_is_glyph_covered((const BYTE
*)ccsf2
+offset
, glyphs
[glyph_index
]) == -1)
1272 TRACE("Glyph not covered\n");
1275 offset
= GET_BE_WORD(ccsf2
->BacktrackClassDef
);
1276 backtrack_class_table
= (const BYTE
*)ccsf2
+offset
;
1277 offset
= GET_BE_WORD(ccsf2
->InputClassDef
);
1278 input_class_table
= (const BYTE
*)ccsf2
+offset
;
1279 offset
= GET_BE_WORD(ccsf2
->LookaheadClassDef
);
1280 lookahead_class_table
= (const BYTE
*)ccsf2
+offset
;
1281 count
= GET_BE_WORD(ccsf2
->ChainSubClassSetCnt
);
1283 class = OT_get_glyph_class(input_class_table
, glyphs
[glyph_index
]);
1284 offset
= GET_BE_WORD(ccsf2
->ChainSubClassSet
[class]);
1288 TRACE("No rules for class\n");
1292 csc
= (const GSUB_ChainSubClassSet
*)((BYTE
*)ccsf2
+offset
);
1293 count
= GET_BE_WORD(csc
->ChainSubClassRuleCnt
);
1295 TRACE("%i rules to check\n",count
);
1297 for (i
= 0; i
< count
; i
++)
1299 WORD backtrack_count
, input_count
, lookahead_count
, substitute_count
;
1301 const GSUB_ChainSubClassRule_1
*backtrack
;
1302 const GSUB_ChainSubClassRule_2
*input
;
1303 const GSUB_ChainSubClassRule_3
*lookahead
;
1304 const GSUB_ChainSubClassRule_4
*substitute
;
1305 int new_index
= GSUB_E_NOGLYPH
;
1307 offset
= GET_BE_WORD(csc
->ChainSubClassRule
[i
]);
1308 backtrack
= (const GSUB_ChainSubClassRule_1
*)((BYTE
*)csc
+ offset
);
1309 backtrack_count
= GET_BE_WORD(backtrack
->BacktrackGlyphCount
);
1310 k
= glyph_index
+ dirBacktrack
* backtrack_count
;
1311 if (k
< 0 || k
>= *glyph_count
)
1314 input
= (const GSUB_ChainSubClassRule_2
*)&backtrack
->Backtrack
[backtrack_count
];
1315 input_count
= GET_BE_WORD(input
->InputGlyphCount
) - 1;
1316 k
= glyph_index
+ write_dir
* input_count
;
1317 if (k
< 0 || k
>= *glyph_count
)
1320 lookahead
= (const GSUB_ChainSubClassRule_3
*)&input
->Input
[input_count
];
1321 lookahead_count
= GET_BE_WORD(lookahead
->LookaheadGlyphCount
);
1322 k
= glyph_index
+ dirLookahead
* (input_count
+ lookahead_count
);
1323 if (k
< 0 || k
>= *glyph_count
)
1326 substitute
= (const GSUB_ChainSubClassRule_4
*)&lookahead
->LookAhead
[lookahead_count
];
1328 for (k
= 0; k
< backtrack_count
; ++k
)
1330 WORD target_class
= GET_BE_WORD(backtrack
->Backtrack
[k
]);
1331 WORD glyph_class
= OT_get_glyph_class(backtrack_class_table
, glyphs
[glyph_index
+ (dirBacktrack
* (k
+1))]);
1332 if (target_class
!= glyph_class
)
1335 if (k
!= backtrack_count
)
1337 TRACE("Matched Backtrack\n");
1339 for (k
= 0; k
< input_count
; ++k
)
1341 WORD target_class
= GET_BE_WORD(input
->Input
[k
]);
1342 WORD glyph_class
= OT_get_glyph_class(input_class_table
, glyphs
[glyph_index
+ (write_dir
* (k
+1))]);
1343 if (target_class
!= glyph_class
)
1346 if (k
!= input_count
)
1348 TRACE("Matched IndexGlyphs\n");
1350 for (k
= 0; k
< lookahead_count
; ++k
)
1352 WORD target_class
= GET_BE_WORD(lookahead
->LookAhead
[k
]);
1353 WORD glyph_class
= OT_get_glyph_class(lookahead_class_table
,
1354 glyphs
[glyph_index
+ (dirLookahead
* (input_count
+ k
+ 1))]);
1355 if (target_class
!= glyph_class
)
1358 if (k
!= lookahead_count
)
1360 TRACE("Matched LookAhead\n");
1362 substitute_count
= GET_BE_WORD(substitute
->SubstCount
);
1363 for (k
= 0; k
< substitute_count
; ++k
)
1365 unsigned int lookup_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].LookupListIndex
);
1366 unsigned int sequence_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].SequenceIndex
);
1367 unsigned int g
= glyph_index
+ write_dir
* sequence_index
;
1369 if (g
>= *glyph_count
)
1371 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n",
1372 sequence_index
, glyph_index
, write_dir
);
1376 TRACE("SUBST: %u -> %u %u.\n", k
, sequence_index
, lookup_index
);
1377 new_index
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1378 if (new_index
== GSUB_E_NOGLYPH
)
1379 ERR("Chain failed to generate a glyph.\n");
1384 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 3)
1386 WORD backtrack_count
, input_count
, lookahead_count
, substitution_count
;
1388 const GSUB_ChainContextSubstFormat3_1
*backtrack
;
1389 const GSUB_ChainContextSubstFormat3_2
*input
;
1390 const GSUB_ChainContextSubstFormat3_3
*lookahead
;
1391 const GSUB_ChainContextSubstFormat3_4
*substitute
;
1392 int new_index
= GSUB_E_NOGLYPH
;
1394 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
1396 backtrack
= (const GSUB_ChainContextSubstFormat3_1
*)ccsf1
;
1397 backtrack_count
= GET_BE_WORD(backtrack
->BacktrackGlyphCount
);
1398 k
= glyph_index
+ dirBacktrack
* backtrack_count
;
1399 if (k
< 0 || k
>= *glyph_count
)
1402 input
= (const GSUB_ChainContextSubstFormat3_2
*)&backtrack
->Coverage
[backtrack_count
];
1403 input_count
= GET_BE_WORD(input
->InputGlyphCount
);
1404 k
= glyph_index
+ write_dir
* (input_count
- 1);
1405 if (k
< 0 || k
>= *glyph_count
)
1408 lookahead
= (const GSUB_ChainContextSubstFormat3_3
*)&input
->Coverage
[input_count
];
1409 lookahead_count
= GET_BE_WORD(lookahead
->LookaheadGlyphCount
);
1410 k
= glyph_index
+ dirLookahead
* (input_count
+ lookahead_count
- 1);
1411 if (k
< 0 || k
>= *glyph_count
)
1414 substitute
= (const GSUB_ChainContextSubstFormat3_4
*)&lookahead
->Coverage
[lookahead_count
];
1416 for (k
= 0; k
< backtrack_count
; ++k
)
1418 offset
= GET_BE_WORD(backtrack
->Coverage
[k
]);
1419 if (GSUB_is_glyph_covered((const BYTE
*)ccsf1
+ offset
,
1420 glyphs
[glyph_index
+ (dirBacktrack
* (k
+ 1))]) == -1)
1423 if (k
!= backtrack_count
)
1425 TRACE("Matched Backtrack\n");
1427 for (k
= 0; k
< input_count
; ++k
)
1429 offset
= GET_BE_WORD(input
->Coverage
[k
]);
1430 if (GSUB_is_glyph_covered((const BYTE
*)ccsf1
+ offset
,
1431 glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
1434 if (k
!= input_count
)
1436 TRACE("Matched IndexGlyphs\n");
1438 for (k
= 0; k
< lookahead_count
; ++k
)
1440 offset
= GET_BE_WORD(lookahead
->Coverage
[k
]);
1441 if (GSUB_is_glyph_covered((const BYTE
*)ccsf1
+ offset
,
1442 glyphs
[glyph_index
+ (dirLookahead
* (input_count
+ k
))]) == -1)
1445 if (k
!= lookahead_count
)
1447 TRACE("Matched LookAhead\n");
1449 substitution_count
= GET_BE_WORD(substitute
->SubstCount
);
1450 for (k
= 0; k
< substitution_count
; ++k
)
1452 unsigned int lookup_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].LookupListIndex
);
1453 unsigned int sequence_index
= GET_BE_WORD(substitute
->SubstLookupRecord
[k
].SequenceIndex
);
1454 unsigned int g
= glyph_index
+ write_dir
* sequence_index
;
1456 if (g
>= *glyph_count
)
1458 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n",
1459 sequence_index
, glyph_index
, write_dir
);
1463 TRACE("SUBST: %u -> %u %u.\n", k
, sequence_index
, lookup_index
);
1464 new_index
= GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, g
, write_dir
, glyph_count
);
1465 if (new_index
== GSUB_E_NOGLYPH
)
1466 ERR("Chain failed to generate a glyph.\n");
1471 return GSUB_E_NOGLYPH
;
1474 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1477 enum gsub_lookup_type type
;
1478 const OT_LookupTable
*look
;
1480 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
1481 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
1482 type
= GET_BE_WORD(look
->LookupType
);
1483 TRACE("type %#x, flag %#x, subtables %u.\n", type
,
1484 GET_BE_WORD(look
->LookupFlag
),GET_BE_WORD(look
->SubTableCount
));
1486 if (type
== GSUB_LOOKUP_EXTENSION
)
1488 if (GET_BE_WORD(look
->SubTableCount
))
1490 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)look
+ GET_BE_WORD(look
->SubTable
[0]));
1491 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
1493 type
= GET_BE_WORD(ext
->ExtensionLookupType
);
1494 TRACE("extension type %i\n",type
);
1498 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext
->SubstFormat
));
1503 WARN("lookup type is Extension Substitution but no extension subtable exists\n");
1508 case GSUB_LOOKUP_SINGLE
:
1509 return GSUB_apply_SingleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1510 case GSUB_LOOKUP_MULTIPLE
:
1511 return GSUB_apply_MultipleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1512 case GSUB_LOOKUP_ALTERNATE
:
1513 return GSUB_apply_AlternateSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1514 case GSUB_LOOKUP_LIGATURE
:
1515 return GSUB_apply_LigatureSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1516 case GSUB_LOOKUP_CONTEXT
:
1517 return GSUB_apply_ContextSubst(lookup
, look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1518 case GSUB_LOOKUP_CONTEXT_CHAINED
:
1519 return GSUB_apply_ChainContextSubst(lookup
, look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1520 case GSUB_LOOKUP_EXTENSION
:
1521 FIXME("Extension Substitution types not valid here\n");
1524 FIXME("Unhandled GSUB lookup type %#x.\n", type
);
1526 return GSUB_E_NOGLYPH
;
1529 int OpenType_apply_GSUB_lookup(const void *table
, unsigned int lookup_index
, WORD
*glyphs
,
1530 unsigned int glyph_index
, int write_dir
, int *glyph_count
)
1532 const GSUB_Header
*header
= (const GSUB_Header
*)table
;
1533 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
1535 return GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1541 static unsigned int GPOS_apply_lookup(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
1542 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, const OT_LookupList
*lookup
,
1543 unsigned int lookup_index
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
1546 static INT
GPOS_get_device_table_value(const OT_DeviceTable
*DeviceTable
, WORD ppem
)
1548 static const WORD mask
[3] = {3,0xf,0xff};
1549 if (DeviceTable
&& ppem
>= GET_BE_WORD(DeviceTable
->StartSize
) && ppem
<= GET_BE_WORD(DeviceTable
->EndSize
))
1551 WORD format
= GET_BE_WORD(DeviceTable
->DeltaFormat
);
1552 int index
= ppem
- GET_BE_WORD(DeviceTable
->StartSize
);
1555 TRACE("device table, format %#x, index %i\n", format
, index
);
1557 if (format
< 1 || format
> 3)
1559 WARN("invalid delta format %#x\n", format
);
1563 index
= index
<< format
;
1564 value
= (DeviceTable
->DeltaValue
[index
/sizeof(WORD
)] << (index
%sizeof(WORD
)))&mask
[format
-1];
1565 TRACE("offset %i, value %i\n",index
, value
);
1566 if (value
> mask
[format
-1]/2)
1567 value
= -1 * ((mask
[format
-1]+1) - value
);
1573 static void GPOS_get_anchor_values(const void *table
, POINT
*pt
, WORD ppem
)
1575 const GPOS_AnchorFormat1
* anchor1
= (const GPOS_AnchorFormat1
*)table
;
1577 switch (GET_BE_WORD(anchor1
->AnchorFormat
))
1581 TRACE("Anchor Format 1\n");
1582 pt
->x
= (short)GET_BE_WORD(anchor1
->XCoordinate
);
1583 pt
->y
= (short)GET_BE_WORD(anchor1
->YCoordinate
);
1588 const GPOS_AnchorFormat2
* anchor2
= (const GPOS_AnchorFormat2
*)table
;
1589 TRACE("Anchor Format 2\n");
1590 pt
->x
= (short)GET_BE_WORD(anchor2
->XCoordinate
);
1591 pt
->y
= (short)GET_BE_WORD(anchor2
->YCoordinate
);
1597 const GPOS_AnchorFormat3
* anchor3
= (const GPOS_AnchorFormat3
*)table
;
1598 TRACE("Anchor Format 3\n");
1599 pt
->x
= (short)GET_BE_WORD(anchor3
->XCoordinate
);
1600 pt
->y
= (short)GET_BE_WORD(anchor3
->YCoordinate
);
1601 offset
= GET_BE_WORD(anchor3
->XDeviceTable
);
1602 TRACE("ppem %i\n",ppem
);
1605 const OT_DeviceTable
* DeviceTableX
= NULL
;
1606 DeviceTableX
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1607 pt
->x
+= GPOS_get_device_table_value(DeviceTableX
, ppem
);
1609 offset
= GET_BE_WORD(anchor3
->YDeviceTable
);
1612 const OT_DeviceTable
* DeviceTableY
= NULL
;
1613 DeviceTableY
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1614 pt
->y
+= GPOS_get_device_table_value(DeviceTableY
, ppem
);
1619 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1
->AnchorFormat
));
1625 static void GPOS_convert_design_units_to_device(const OUTLINETEXTMETRICW
*otm
, const LOGFONTW
*logfont
,
1626 int desX
, int desY
, double *devX
, double *devY
)
1628 int emHeight
= otm
->otmTextMetrics
.tmAscent
+ otm
->otmTextMetrics
.tmDescent
- otm
->otmTextMetrics
.tmInternalLeading
;
1630 TRACE("emHeight %i lfWidth %i\n",emHeight
, logfont
->lfWidth
);
1631 *devX
= (desX
* emHeight
) / (double)otm
->otmEMSquare
;
1632 *devY
= (desY
* emHeight
) / (double)otm
->otmEMSquare
;
1633 if (logfont
->lfWidth
)
1634 FIXME("Font with lfWidth set not handled properly.\n");
1637 static INT
GPOS_get_value_record(WORD ValueFormat
, const WORD data
[], GPOS_ValueRecord
*record
)
1640 if (ValueFormat
& 0x0001) { if (data
) record
->XPlacement
= GET_BE_WORD(data
[offset
]); offset
++; }
1641 if (ValueFormat
& 0x0002) { if (data
) record
->YPlacement
= GET_BE_WORD(data
[offset
]); offset
++; }
1642 if (ValueFormat
& 0x0004) { if (data
) record
->XAdvance
= GET_BE_WORD(data
[offset
]); offset
++; }
1643 if (ValueFormat
& 0x0008) { if (data
) record
->YAdvance
= GET_BE_WORD(data
[offset
]); offset
++; }
1644 if (ValueFormat
& 0x0010) { if (data
) record
->XPlaDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1645 if (ValueFormat
& 0x0020) { if (data
) record
->YPlaDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1646 if (ValueFormat
& 0x0040) { if (data
) record
->XAdvDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1647 if (ValueFormat
& 0x0080) { if (data
) record
->YAdvDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1651 static void GPOS_get_value_record_offsets(const BYTE
*head
, GPOS_ValueRecord
*ValueRecord
,
1652 WORD ValueFormat
, unsigned int ppem
, POINT
*ptPlacement
, POINT
*ptAdvance
)
1654 if (ValueFormat
& 0x0001) ptPlacement
->x
+= (short)ValueRecord
->XPlacement
;
1655 if (ValueFormat
& 0x0002) ptPlacement
->y
+= (short)ValueRecord
->YPlacement
;
1656 if (ValueFormat
& 0x0004) ptAdvance
->x
+= (short)ValueRecord
->XAdvance
;
1657 if (ValueFormat
& 0x0008) ptAdvance
->y
+= (short)ValueRecord
->YAdvance
;
1658 if (ValueFormat
& 0x0010) ptPlacement
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XPlaDevice
), ppem
);
1659 if (ValueFormat
& 0x0020) ptPlacement
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YPlaDevice
), ppem
);
1660 if (ValueFormat
& 0x0040) ptAdvance
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XAdvDevice
), ppem
);
1661 if (ValueFormat
& 0x0080) ptAdvance
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YAdvDevice
), ppem
);
1662 if (ValueFormat
& 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat
&0xFF00);
1665 static const BYTE
*GPOS_get_subtable(const OT_LookupTable
*look
, int index
)
1667 int offset
= GET_BE_WORD(look
->SubTable
[index
]);
1669 if (GET_BE_WORD(look
->LookupType
) == GPOS_LOOKUP_POSITION_EXTENSION
)
1671 const GPOS_ExtensionPosFormat1
*ext
= (const GPOS_ExtensionPosFormat1
*)((const BYTE
*)look
+ offset
);
1672 if (GET_BE_WORD(ext
->PosFormat
) == 1)
1674 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
1678 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext
->PosFormat
));
1681 return (const BYTE
*)look
+ offset
;
1684 static void GPOS_apply_SingleAdjustment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1685 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
,
1686 POINT
*adjust
, POINT
*advance
)
1690 TRACE("Single Adjustment Positioning Subtable\n");
1692 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1694 const GPOS_SinglePosFormat1
*spf1
= (const GPOS_SinglePosFormat1
*)GPOS_get_subtable(look
, j
);
1696 if (GET_BE_WORD(spf1
->PosFormat
) == 1)
1698 offset
= GET_BE_WORD(spf1
->Coverage
);
1699 if (GSUB_is_glyph_covered((const BYTE
*)spf1
+offset
, glyphs
[glyph_index
]) != -1)
1701 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1702 WORD ValueFormat
= GET_BE_WORD(spf1
->ValueFormat
);
1703 GPOS_get_value_record(ValueFormat
, spf1
->Value
, &ValueRecord
);
1704 GPOS_get_value_record_offsets((const BYTE
*)spf1
, &ValueRecord
, ValueFormat
, ppem
, adjust
, advance
);
1705 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1708 else if (GET_BE_WORD(spf1
->PosFormat
) == 2)
1711 const GPOS_SinglePosFormat2
*spf2
;
1712 spf2
= (const GPOS_SinglePosFormat2
*)spf1
;
1713 offset
= GET_BE_WORD(spf2
->Coverage
);
1714 index
= GSUB_is_glyph_covered((const BYTE
*)spf2
+offset
, glyphs
[glyph_index
]);
1718 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1719 WORD ValueFormat
= GET_BE_WORD(spf2
->ValueFormat
);
1720 size
= GPOS_get_value_record(ValueFormat
, spf2
->Value
, &ValueRecord
);
1723 offset
= size
* index
;
1724 GPOS_get_value_record(ValueFormat
, &spf2
->Value
[offset
], &ValueRecord
);
1726 GPOS_get_value_record_offsets((const BYTE
*)spf2
, &ValueRecord
, ValueFormat
, ppem
, adjust
, advance
);
1727 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1731 FIXME("Single Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(spf1
->PosFormat
));
1735 static void apply_pair_value( const void *pos_table
, WORD val_fmt1
, WORD val_fmt2
, const WORD
*pair
,
1736 INT ppem
, POINT
*adjust
, POINT
*advance
)
1738 GPOS_ValueRecord val_rec1
= {0,0,0,0,0,0,0,0};
1739 GPOS_ValueRecord val_rec2
= {0,0,0,0,0,0,0,0};
1742 size
= GPOS_get_value_record( val_fmt1
, pair
, &val_rec1
);
1743 GPOS_get_value_record( val_fmt2
, pair
+ size
, &val_rec2
);
1747 GPOS_get_value_record_offsets( pos_table
, &val_rec1
, val_fmt1
, ppem
, adjust
, advance
);
1748 TRACE( "Glyph 1 resulting cumulative offset is %s design units\n", wine_dbgstr_point(&adjust
[0]) );
1749 TRACE( "Glyph 1 resulting cumulative advance is %s design units\n", wine_dbgstr_point(&advance
[0]) );
1753 GPOS_get_value_record_offsets( pos_table
, &val_rec2
, val_fmt2
, ppem
, adjust
+ 1, advance
+ 1 );
1754 TRACE( "Glyph 2 resulting cumulative offset is %s design units\n", wine_dbgstr_point(&adjust
[1]) );
1755 TRACE( "Glyph 2 resulting cumulative advance is %s design units\n", wine_dbgstr_point(&advance
[1]) );
1759 static int GPOS_apply_PairAdjustment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1760 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
,
1761 POINT
*adjust
, POINT
*advance
)
1764 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1766 if (glyph_index
+ write_dir
>= glyph_count
)
1769 TRACE("Pair Adjustment Positioning Subtable\n");
1771 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1773 const GPOS_PairPosFormat1
*ppf1
= (const GPOS_PairPosFormat1
*)GPOS_get_subtable(look
, j
);
1775 if (GET_BE_WORD(ppf1
->PosFormat
) == 1)
1778 WORD ValueFormat1
= GET_BE_WORD(ppf1
->ValueFormat1
);
1779 WORD ValueFormat2
= GET_BE_WORD(ppf1
->ValueFormat2
);
1780 INT val_fmt1_size
= GPOS_get_value_record( ValueFormat1
, NULL
, NULL
);
1781 INT val_fmt2_size
= GPOS_get_value_record( ValueFormat2
, NULL
, NULL
);
1782 offset
= GET_BE_WORD(ppf1
->Coverage
);
1783 index
= GSUB_is_glyph_covered((const BYTE
*)ppf1
+offset
, glyphs
[glyph_index
]);
1784 if (index
!= -1 && index
< GET_BE_WORD(ppf1
->PairSetCount
))
1788 const GPOS_PairSet
*ps
;
1789 const GPOS_PairValueRecord
*pair_val_rec
;
1790 offset
= GET_BE_WORD(ppf1
->PairSetOffset
[index
]);
1791 ps
= (const GPOS_PairSet
*)((const BYTE
*)ppf1
+offset
);
1792 pair_count
= GET_BE_WORD(ps
->PairValueCount
);
1793 pair_val_rec
= ps
->PairValueRecord
;
1794 for (k
= 0; k
< pair_count
; k
++)
1796 WORD second_glyph
= GET_BE_WORD(pair_val_rec
->SecondGlyph
);
1797 if (glyphs
[glyph_index
+write_dir
] == second_glyph
)
1800 TRACE("Format 1: Found Pair %x,%x\n",glyphs
[glyph_index
],glyphs
[glyph_index
+write_dir
]);
1801 apply_pair_value(ppf1
, ValueFormat1
, ValueFormat2
,
1802 pair_val_rec
->Value1
, ppem
, adjust
, advance
);
1803 if (ValueFormat2
) next
++;
1806 pair_val_rec
= (const GPOS_PairValueRecord
*)(pair_val_rec
->Value1
+ val_fmt1_size
+ val_fmt2_size
);
1810 else if (GET_BE_WORD(ppf1
->PosFormat
) == 2)
1812 const GPOS_PairPosFormat2
*ppf2
= (const GPOS_PairPosFormat2
*)ppf1
;
1814 WORD ValueFormat1
= GET_BE_WORD( ppf2
->ValueFormat1
);
1815 WORD ValueFormat2
= GET_BE_WORD( ppf2
->ValueFormat2
);
1816 INT val_fmt1_size
= GPOS_get_value_record( ValueFormat1
, NULL
, NULL
);
1817 INT val_fmt2_size
= GPOS_get_value_record( ValueFormat2
, NULL
, NULL
);
1818 WORD class1_count
= GET_BE_WORD( ppf2
->Class1Count
);
1819 WORD class2_count
= GET_BE_WORD( ppf2
->Class2Count
);
1821 offset
= GET_BE_WORD( ppf2
->Coverage
);
1822 index
= GSUB_is_glyph_covered( (const BYTE
*)ppf2
+ offset
, glyphs
[glyph_index
] );
1825 WORD class1
, class2
;
1826 class1
= OT_get_glyph_class( (const BYTE
*)ppf2
+ GET_BE_WORD(ppf2
->ClassDef1
), glyphs
[glyph_index
] );
1827 class2
= OT_get_glyph_class( (const BYTE
*)ppf2
+ GET_BE_WORD(ppf2
->ClassDef2
), glyphs
[glyph_index
+ write_dir
] );
1828 if (class1
< class1_count
&& class2
< class2_count
)
1830 const WORD
*pair_val
= ppf2
->Class1Record
+ (class1
* class2_count
+ class2
) * (val_fmt1_size
+ val_fmt2_size
);
1833 TRACE( "Format 2: Found Pair %x,%x\n", glyphs
[glyph_index
], glyphs
[glyph_index
+ write_dir
] );
1835 apply_pair_value(ppf2
, ValueFormat1
, ValueFormat2
, pair_val
, ppem
, adjust
, advance
);
1836 if (ValueFormat2
) next
++;
1842 FIXME("Pair Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(ppf1
->PosFormat
));
1847 static void GPOS_apply_CursiveAttachment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1848 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
1851 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1853 if (glyph_index
+ write_dir
>= glyph_count
)
1856 TRACE("Cursive Attachment Positioning Subtable\n");
1858 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1860 const GPOS_CursivePosFormat1
*cpf1
= (const GPOS_CursivePosFormat1
*)GPOS_get_subtable(look
, j
);
1861 if (GET_BE_WORD(cpf1
->PosFormat
) == 1)
1863 int index_exit
, index_entry
;
1864 WORD offset
= GET_BE_WORD( cpf1
->Coverage
);
1865 index_exit
= GSUB_is_glyph_covered((const BYTE
*)cpf1
+offset
, glyphs
[glyph_index
]);
1866 if (index_exit
!= -1 && cpf1
->EntryExitRecord
[index_exit
].ExitAnchor
!= 0)
1868 index_entry
= GSUB_is_glyph_covered((const BYTE
*)cpf1
+offset
, glyphs
[glyph_index
+write_dir
]);
1869 if (index_entry
!= -1 && cpf1
->EntryExitRecord
[index_entry
].EntryAnchor
!= 0)
1871 POINT exit_pt
, entry_pt
;
1872 offset
= GET_BE_WORD(cpf1
->EntryExitRecord
[index_exit
].ExitAnchor
);
1873 GPOS_get_anchor_values((const BYTE
*)cpf1
+ offset
, &exit_pt
, ppem
);
1874 offset
= GET_BE_WORD(cpf1
->EntryExitRecord
[index_entry
].EntryAnchor
);
1875 GPOS_get_anchor_values((const BYTE
*)cpf1
+ offset
, &entry_pt
, ppem
);
1876 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
));
1877 pt
->x
= entry_pt
.x
- exit_pt
.x
;
1878 pt
->y
= entry_pt
.y
- exit_pt
.y
;
1884 FIXME("Cursive Attachment Positioning: Format %i Unhandled\n",GET_BE_WORD(cpf1
->PosFormat
));
1889 static int GPOS_apply_MarkToBase(const ScriptCache
*script_cache
, const OT_LookupTable
*look
,
1890 const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, unsigned int glyph_index
,
1891 unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
1894 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1895 const void *glyph_class_table
= NULL
;
1898 if (script_cache
->GDEF_Table
)
1900 const GDEF_Header
*header
= script_cache
->GDEF_Table
;
1901 WORD offset
= GET_BE_WORD( header
->GlyphClassDef
);
1903 glyph_class_table
= (const BYTE
*)script_cache
->GDEF_Table
+ offset
;
1906 TRACE("MarkToBase Attachment Positioning Subtable\n");
1908 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1910 const GPOS_MarkBasePosFormat1
*mbpf1
= (const GPOS_MarkBasePosFormat1
*)GPOS_get_subtable(look
, j
);
1911 if (GET_BE_WORD(mbpf1
->PosFormat
) == 1)
1913 int offset
= GET_BE_WORD(mbpf1
->MarkCoverage
);
1915 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[glyph_index
]);
1916 if (mark_index
!= -1)
1919 int base_glyph
= glyph_index
- write_dir
;
1921 if (glyph_class_table
)
1923 while (OT_get_glyph_class(glyph_class_table
, glyphs
[base_glyph
]) == MarkGlyph
&& base_glyph
> 0 && base_glyph
< glyph_count
)
1924 base_glyph
-= write_dir
;
1927 offset
= GET_BE_WORD(mbpf1
->BaseCoverage
);
1928 base_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[base_glyph
]);
1929 if (base_index
!= -1)
1931 const GPOS_MarkArray
*ma
;
1932 const GPOS_MarkRecord
*mr
;
1933 const GPOS_BaseArray
*ba
;
1934 const GPOS_BaseRecord
*br
;
1936 int class_count
= GET_BE_WORD(mbpf1
->ClassCount
);
1937 int baserecord_size
;
1940 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[base_glyph
], base_index
);
1941 offset
= GET_BE_WORD(mbpf1
->MarkArray
);
1942 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mbpf1
+ offset
);
1943 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
1945 ERR("Mark index exceeded mark count\n");
1948 mr
= &ma
->MarkRecord
[mark_index
];
1949 mark_class
= GET_BE_WORD(mr
->Class
);
1950 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
1951 offset
= GET_BE_WORD(mbpf1
->BaseArray
);
1952 ba
= (const GPOS_BaseArray
*)((const BYTE
*)mbpf1
+ offset
);
1953 baserecord_size
= class_count
* sizeof(WORD
);
1954 br
= (const GPOS_BaseRecord
*)((const BYTE
*)ba
+ sizeof(WORD
) + (baserecord_size
* base_index
));
1955 offset
= GET_BE_WORD(br
->BaseAnchor
[mark_class
]);
1956 GPOS_get_anchor_values((const BYTE
*)ba
+ offset
, &base_pt
, ppem
);
1957 offset
= GET_BE_WORD(mr
->MarkAnchor
);
1958 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
1959 TRACE("Offset on base is %s design units\n",wine_dbgstr_point(&base_pt
));
1960 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt
));
1961 pt
->x
+= base_pt
.x
- mark_pt
.x
;
1962 pt
->y
+= base_pt
.y
- mark_pt
.y
;
1963 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt
));
1969 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1
->PosFormat
));
1974 static void GPOS_apply_MarkToLigature(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
1975 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
1978 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1980 TRACE("MarkToLigature Attachment Positioning Subtable\n");
1982 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1984 const GPOS_MarkLigPosFormat1
*mlpf1
= (const GPOS_MarkLigPosFormat1
*)GPOS_get_subtable(look
, j
);
1985 if (GET_BE_WORD(mlpf1
->PosFormat
) == 1)
1987 int offset
= GET_BE_WORD(mlpf1
->MarkCoverage
);
1989 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mlpf1
+offset
, glyphs
[glyph_index
]);
1990 if (mark_index
!= -1)
1993 offset
= GET_BE_WORD(mlpf1
->LigatureCoverage
);
1994 ligature_index
= GSUB_is_glyph_covered((const BYTE
*)mlpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
1995 if (ligature_index
!= -1)
1997 const GPOS_MarkArray
*ma
;
1998 const GPOS_MarkRecord
*mr
;
2000 const GPOS_LigatureArray
*la
;
2001 const GPOS_LigatureAttach
*lt
;
2003 int class_count
= GET_BE_WORD(mlpf1
->ClassCount
);
2004 int component_count
;
2010 TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], ligature_index
);
2011 offset
= GET_BE_WORD(mlpf1
->MarkArray
);
2012 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mlpf1
+ offset
);
2013 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
2015 ERR("Mark index exceeded mark count\n");
2018 mr
= &ma
->MarkRecord
[mark_index
];
2019 mark_class
= GET_BE_WORD(mr
->Class
);
2020 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
2021 offset
= GET_BE_WORD(mlpf1
->LigatureArray
);
2022 la
= (const GPOS_LigatureArray
*)((const BYTE
*)mlpf1
+ offset
);
2023 if (ligature_index
> GET_BE_WORD(la
->LigatureCount
))
2025 ERR("Ligature index exceeded ligature count\n");
2028 offset
= GET_BE_WORD(la
->LigatureAttach
[ligature_index
]);
2029 lt
= (const GPOS_LigatureAttach
*)((const BYTE
*)la
+ offset
);
2031 component_count
= GET_BE_WORD(lt
->ComponentCount
);
2032 component_size
= class_count
* sizeof(WORD
);
2034 for (i
= 0; i
< component_count
&& !offset
; i
++)
2037 const GPOS_ComponentRecord
*cr
= (const GPOS_ComponentRecord
*)((const BYTE
*)lt
->ComponentRecord
+ (component_size
* i
));
2038 for (k
= 0; k
< class_count
&& !offset
; k
++)
2039 offset
= GET_BE_WORD(cr
->LigatureAnchor
[k
]);
2040 cr
= (const GPOS_ComponentRecord
*)((const BYTE
*)cr
+ component_size
);
2044 ERR("Failed to find available ligature connection point\n");
2048 GPOS_get_anchor_values((const BYTE
*)lt
+ offset
, &ligature_pt
, ppem
);
2049 offset
= GET_BE_WORD(mr
->MarkAnchor
);
2050 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
2051 TRACE("Offset on ligature is %s design units\n",wine_dbgstr_point(&ligature_pt
));
2052 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt
));
2053 pt
->x
+= ligature_pt
.x
- mark_pt
.x
;
2054 pt
->y
+= ligature_pt
.y
- mark_pt
.y
;
2055 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt
));
2060 FIXME("Unhandled Mark To Ligature Format %i\n",GET_BE_WORD(mlpf1
->PosFormat
));
2064 static BOOL
GPOS_apply_MarkToMark(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
,
2065 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, unsigned int ppem
, POINT
*pt
)
2069 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2071 TRACE("MarkToMark Attachment Positioning Subtable\n");
2073 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
2075 const GPOS_MarkMarkPosFormat1
*mmpf1
= (const GPOS_MarkMarkPosFormat1
*)GPOS_get_subtable(look
, j
);
2076 if (GET_BE_WORD(mmpf1
->PosFormat
) == 1)
2078 int offset
= GET_BE_WORD(mmpf1
->Mark1Coverage
);
2080 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mmpf1
+offset
, glyphs
[glyph_index
]);
2081 if (mark_index
!= -1)
2084 offset
= GET_BE_WORD(mmpf1
->Mark2Coverage
);
2085 mark2_index
= GSUB_is_glyph_covered((const BYTE
*)mmpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
2086 if (mark2_index
!= -1)
2088 const GPOS_MarkArray
*ma
;
2089 const GPOS_MarkRecord
*mr
;
2090 const GPOS_Mark2Array
*m2a
;
2091 const GPOS_Mark2Record
*m2r
;
2093 int class_count
= GET_BE_WORD(mmpf1
->ClassCount
);
2094 int mark2record_size
;
2097 TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], mark2_index
);
2098 offset
= GET_BE_WORD(mmpf1
->Mark1Array
);
2099 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mmpf1
+ offset
);
2100 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
2102 ERR("Mark index exceeded mark count\n");
2105 mr
= &ma
->MarkRecord
[mark_index
];
2106 mark_class
= GET_BE_WORD(mr
->Class
);
2107 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
2108 offset
= GET_BE_WORD(mmpf1
->Mark2Array
);
2109 m2a
= (const GPOS_Mark2Array
*)((const BYTE
*)mmpf1
+ offset
);
2110 mark2record_size
= class_count
* sizeof(WORD
);
2111 m2r
= (const GPOS_Mark2Record
*)((const BYTE
*)m2a
+ sizeof(WORD
) + (mark2record_size
* mark2_index
));
2112 offset
= GET_BE_WORD(m2r
->Mark2Anchor
[mark_class
]);
2113 GPOS_get_anchor_values((const BYTE
*)m2a
+ offset
, &mark2_pt
, ppem
);
2114 offset
= GET_BE_WORD(mr
->MarkAnchor
);
2115 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
2116 TRACE("Offset on mark2 is %s design units\n",wine_dbgstr_point(&mark2_pt
));
2117 TRACE("Offset on mark is %s design units\n",wine_dbgstr_point(&mark_pt
));
2118 pt
->x
+= mark2_pt
.x
- mark_pt
.x
;
2119 pt
->y
+= mark2_pt
.y
- mark_pt
.y
;
2120 TRACE("Resulting cumulative offset is %s design units\n",wine_dbgstr_point(pt
));
2126 FIXME("Unhandled Mark To Mark Format %i\n",GET_BE_WORD(mmpf1
->PosFormat
));
2131 static unsigned int GPOS_apply_ContextPos(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
2132 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, const OT_LookupList
*lookup
,
2133 const OT_LookupTable
*look
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
2137 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2139 TRACE("Contextual Positioning Subtable\n");
2141 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
2143 const GPOS_ContextPosFormat2
*cpf2
= (GPOS_ContextPosFormat2
*)GPOS_get_subtable(look
, j
);
2145 if (GET_BE_WORD(cpf2
->PosFormat
) == 1)
2149 FIXME(" TODO: subtype 1\n");
2152 else if (GET_BE_WORD(cpf2
->PosFormat
) == 2)
2154 WORD offset
= GET_BE_WORD(cpf2
->Coverage
);
2157 TRACE("Contextual Positioning Subtable: Format 2\n");
2159 index
= GSUB_is_glyph_covered((const BYTE
*)cpf2
+offset
, glyphs
[glyph_index
]);
2160 TRACE("Coverage index %i\n",index
);
2163 int k
, count
, class;
2164 const GPOS_PosClassSet
*pcs
;
2165 const void *glyph_class_table
= NULL
;
2167 offset
= GET_BE_WORD(cpf2
->ClassDef
);
2168 glyph_class_table
= (const BYTE
*)cpf2
+ offset
;
2170 class = OT_get_glyph_class(glyph_class_table
,glyphs
[glyph_index
]);
2172 offset
= GET_BE_WORD(cpf2
->PosClassSet
[class]);
2175 TRACE("No class rule table for class %i\n",class);
2178 pcs
= (const GPOS_PosClassSet
*)((const BYTE
*)cpf2
+offset
);
2179 count
= GET_BE_WORD(pcs
->PosClassRuleCnt
);
2180 TRACE("PosClassSet has %i members\n",count
);
2181 for (k
= 0; k
< count
; k
++)
2183 const GPOS_PosClassRule_1
*pr
;
2184 const GPOS_PosClassRule_2
*pr_2
;
2188 offset
= GET_BE_WORD(pcs
->PosClassRule
[k
]);
2189 pr
= (const GPOS_PosClassRule_1
*)((const BYTE
*)pcs
+offset
);
2190 g_count
= GET_BE_WORD(pr
->GlyphCount
);
2191 TRACE("PosClassRule has %i glyphs classes\n",g_count
);
2193 g
= glyph_index
+ write_dir
* (g_count
- 1);
2194 if (g
>= glyph_count
)
2197 for (l
= 0; l
< g_count
-1; l
++)
2199 int g_class
= OT_get_glyph_class(glyph_class_table
, glyphs
[glyph_index
+ (write_dir
* (l
+1))]);
2200 if (g_class
!= GET_BE_WORD(pr
->Class
[l
])) break;
2205 TRACE("Rule does not match\n");
2209 TRACE("Rule matches\n");
2210 pr_2
= (const GPOS_PosClassRule_2
*)&pr
->Class
[g_count
- 1];
2212 for (l
= 0; l
< GET_BE_WORD(pr
->PosCount
); l
++)
2214 unsigned int lookup_index
= GET_BE_WORD(pr_2
->PosLookupRecord
[l
].LookupListIndex
);
2215 unsigned int sequence_index
= GET_BE_WORD(pr_2
->PosLookupRecord
[l
].SequenceIndex
);
2217 g
= glyph_index
+ write_dir
* sequence_index
;
2218 if (g
>= glyph_count
)
2220 WARN("Invalid sequence index %u (glyph index %u, write dir %d).\n",
2221 sequence_index
, glyph_index
, write_dir
);
2225 TRACE("Position: %u -> %u %u.\n", l
, sequence_index
, lookup_index
);
2227 GPOS_apply_lookup(script_cache
, otm
, logfont
, analysis
, advance
,
2228 lookup
, lookup_index
, glyphs
, g
, glyph_count
, goffset
);
2234 TRACE("Not covered\n");
2237 else if (GET_BE_WORD(cpf2
->PosFormat
) == 3)
2241 FIXME(" TODO: subtype 3\n");
2245 FIXME("Unhandled Contextual Positioning Format %i\n",GET_BE_WORD(cpf2
->PosFormat
));
2250 static unsigned int GPOS_apply_ChainContextPos(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
2251 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, const OT_LookupList
*lookup
,
2252 const OT_LookupTable
*look
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
2256 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2258 TRACE("Chaining Contextual Positioning Subtable\n");
2260 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
2263 const GPOS_ChainContextPosFormat3_1
*backtrack
= (GPOS_ChainContextPosFormat3_1
*)GPOS_get_subtable(look
, j
);
2264 int dirLookahead
= write_dir
;
2265 int dirBacktrack
= -1 * write_dir
;
2267 if (GET_BE_WORD(backtrack
->PosFormat
) == 1)
2271 FIXME(" TODO: subtype 1 (Simple Chaining Context Glyph Positioning)\n");
2274 else if (GET_BE_WORD(backtrack
->PosFormat
) == 2)
2278 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Positioning)\n");
2281 else if (GET_BE_WORD(backtrack
->PosFormat
) == 3)
2283 WORD backtrack_count
, input_count
, lookahead_count
, positioning_count
;
2285 const GPOS_ChainContextPosFormat3_2
*input
;
2286 const GPOS_ChainContextPosFormat3_3
*lookahead
;
2287 const GPOS_ChainContextPosFormat3_4
*positioning
;
2289 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Positioning)\n");
2291 backtrack_count
= GET_BE_WORD(backtrack
->BacktrackGlyphCount
);
2292 k
= glyph_index
+ dirBacktrack
* backtrack_count
;
2293 if (k
< 0 || k
>= glyph_count
)
2296 input
= (const GPOS_ChainContextPosFormat3_2
*)&backtrack
->Coverage
[backtrack_count
];
2297 input_count
= GET_BE_WORD(input
->InputGlyphCount
);
2298 k
= glyph_index
+ write_dir
* (input_count
- 1);
2299 if (k
< 0 || k
>= glyph_count
)
2302 lookahead
= (const GPOS_ChainContextPosFormat3_3
*)&input
->Coverage
[input_count
];
2303 lookahead_count
= GET_BE_WORD(lookahead
->LookaheadGlyphCount
);
2304 k
= glyph_index
+ dirLookahead
* (input_count
+ lookahead_count
- 1);
2305 if (k
< 0 || k
>= glyph_count
)
2308 positioning
= (const GPOS_ChainContextPosFormat3_4
*)&lookahead
->Coverage
[lookahead_count
];
2310 for (k
= 0; k
< backtrack_count
; ++k
)
2312 offset
= GET_BE_WORD(backtrack
->Coverage
[k
]);
2313 if (GSUB_is_glyph_covered((const BYTE
*)backtrack
+ offset
,
2314 glyphs
[glyph_index
+ (dirBacktrack
* (k
+ 1))]) == -1)
2317 if (k
!= backtrack_count
)
2319 TRACE("Matched Backtrack\n");
2321 for (k
= 0; k
< input_count
; ++k
)
2323 offset
= GET_BE_WORD(input
->Coverage
[k
]);
2324 if (GSUB_is_glyph_covered((const BYTE
*)backtrack
+ offset
,
2325 glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
2328 if (k
!= input_count
)
2330 TRACE("Matched IndexGlyphs\n");
2332 for (k
= 0; k
< lookahead_count
; ++k
)
2334 offset
= GET_BE_WORD(lookahead
->Coverage
[k
]);
2335 if (GSUB_is_glyph_covered((const BYTE
*)backtrack
+ offset
,
2336 glyphs
[glyph_index
+ (dirLookahead
* (input_count
+ k
))]) == -1)
2339 if (k
!= lookahead_count
)
2341 TRACE("Matched LookAhead\n");
2343 if (!(positioning_count
= GET_BE_WORD(positioning
->PosCount
)))
2346 for (k
= 0; k
< positioning_count
; ++k
)
2348 unsigned int lookup_index
= GET_BE_WORD(positioning
->PosLookupRecord
[k
].LookupListIndex
);
2349 unsigned int sequence_index
= GET_BE_WORD(positioning
->PosLookupRecord
[k
].SequenceIndex
);
2350 unsigned int g
= glyph_index
+ write_dir
* sequence_index
;
2352 if (g
>= glyph_count
)
2354 WARN("Skipping invalid sequence index %u (glyph index %u, write dir %d).\n",
2355 sequence_index
, glyph_index
, write_dir
);
2359 TRACE("Position: %u -> %u %u.\n", k
, sequence_index
, lookup_index
);
2360 GPOS_apply_lookup(script_cache
, otm
, logfont
, analysis
, advance
, lookup
, lookup_index
,
2361 glyphs
, g
, glyph_count
, goffset
);
2363 return input_count
+ lookahead_count
;
2366 FIXME("Unhandled Chaining Contextual Positioning Format %#x.\n", GET_BE_WORD(backtrack
->PosFormat
));
2371 static unsigned int GPOS_apply_lookup(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*lpotm
,
2372 const LOGFONTW
*lplogfont
, const SCRIPT_ANALYSIS
*analysis
, int *piAdvance
, const OT_LookupList
*lookup
,
2373 unsigned int lookup_index
, const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
,
2377 const OT_LookupTable
*look
;
2378 int ppem
= lpotm
->otmTextMetrics
.tmAscent
+ lpotm
->otmTextMetrics
.tmDescent
- lpotm
->otmTextMetrics
.tmInternalLeading
;
2379 enum gpos_lookup_type type
;
2381 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
2382 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
2383 type
= GET_BE_WORD(look
->LookupType
);
2384 TRACE("type %#x, flag %#x, subtables %u.\n", type
,
2385 GET_BE_WORD(look
->LookupFlag
), GET_BE_WORD(look
->SubTableCount
));
2387 if (type
== GPOS_LOOKUP_POSITION_EXTENSION
)
2389 if (GET_BE_WORD(look
->SubTableCount
))
2391 const GPOS_ExtensionPosFormat1
*ext
= (const GPOS_ExtensionPosFormat1
*)((const BYTE
*)look
+ GET_BE_WORD(look
->SubTable
[0]));
2392 if (GET_BE_WORD(ext
->PosFormat
) == 1)
2394 type
= GET_BE_WORD(ext
->ExtensionLookupType
);
2395 TRACE("extension type %i\n",type
);
2399 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext
->PosFormat
));
2404 WARN("lookup type is Extension Positioning but no extension subtable exists\n");
2409 case GPOS_LOOKUP_ADJUST_SINGLE
:
2412 POINT adjust
= {0,0};
2413 POINT advance
= {0,0};
2414 GPOS_apply_SingleAdjustment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &adjust
, &advance
);
2415 if (adjust
.x
|| adjust
.y
)
2417 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
.x
, adjust
.y
, &devX
, &devY
);
2418 pGoffset
[glyph_index
].du
+= round(devX
);
2419 pGoffset
[glyph_index
].dv
+= round(devY
);
2421 if (advance
.x
|| advance
.y
)
2423 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
.x
, advance
.y
, &devX
, &devY
);
2424 piAdvance
[glyph_index
] += round(devX
);
2426 FIXME("Unhandled adjustment to Y advancement\n");
2431 case GPOS_LOOKUP_ADJUST_PAIR
:
2433 POINT advance
[2]= {{0,0},{0,0}};
2434 POINT adjust
[2]= {{0,0},{0,0}};
2437 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2438 int offset_sign
= (analysis
->fRTL
&& analysis
->fLogicalOrder
) ? -1 : 1;
2440 index_offset
= GPOS_apply_PairAdjustment(look
, analysis
, glyphs
,
2441 glyph_index
, glyph_count
, ppem
, adjust
, advance
);
2442 if (adjust
[0].x
|| adjust
[0].y
)
2444 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
[0].x
, adjust
[0].y
, &devX
, &devY
);
2445 pGoffset
[glyph_index
].du
+= round(devX
) * offset_sign
;
2446 pGoffset
[glyph_index
].dv
+= round(devY
);
2448 if (advance
[0].x
|| advance
[0].y
)
2450 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
[0].x
, advance
[0].y
, &devX
, &devY
);
2451 piAdvance
[glyph_index
] += round(devX
);
2453 if (adjust
[1].x
|| adjust
[1].y
)
2455 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
[1].x
, adjust
[1].y
, &devX
, &devY
);
2456 pGoffset
[glyph_index
+ write_dir
].du
+= round(devX
) * offset_sign
;
2457 pGoffset
[glyph_index
+ write_dir
].dv
+= round(devY
);
2459 if (advance
[1].x
|| advance
[1].y
)
2461 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
[1].x
, advance
[1].y
, &devX
, &devY
);
2462 piAdvance
[glyph_index
+ write_dir
] += round(devX
);
2464 return index_offset
;
2467 case GPOS_LOOKUP_ATTACH_CURSIVE
:
2471 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2473 GPOS_apply_CursiveAttachment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
2474 if (desU
.x
|| desU
.y
)
2476 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2477 /* Windows does not appear to apply X offsets here */
2478 pGoffset
[glyph_index
].dv
= round(devY
) + pGoffset
[glyph_index
+write_dir
].dv
;
2483 case GPOS_LOOKUP_ATTACH_MARK_TO_BASE
:
2487 int base_index
= GPOS_apply_MarkToBase(script_cache
, look
, analysis
,
2488 glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
2489 if (base_index
!= -1)
2491 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2492 if (!analysis
->fRTL
) pGoffset
[glyph_index
].du
= round(devX
) - piAdvance
[base_index
];
2495 if (analysis
->fLogicalOrder
) devX
*= -1;
2496 pGoffset
[glyph_index
].du
= round(devX
);
2498 pGoffset
[glyph_index
].dv
= round(devY
);
2503 case GPOS_LOOKUP_ATTACH_MARK_TO_LIGATURE
:
2507 GPOS_apply_MarkToLigature(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
2508 if (desU
.x
|| desU
.y
)
2510 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2511 pGoffset
[glyph_index
].du
= (round(devX
) - piAdvance
[glyph_index
-1]);
2512 pGoffset
[glyph_index
].dv
= round(devY
);
2517 case GPOS_LOOKUP_ATTACH_MARK_TO_MARK
:
2521 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
2522 if (GPOS_apply_MarkToMark(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
))
2524 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
2525 if (analysis
->fRTL
&& analysis
->fLogicalOrder
) devX
*= -1;
2526 pGoffset
[glyph_index
].du
= round(devX
) + pGoffset
[glyph_index
- write_dir
].du
;
2527 pGoffset
[glyph_index
].dv
= round(devY
) + pGoffset
[glyph_index
- write_dir
].dv
;
2532 case GPOS_LOOKUP_POSITION_CONTEXT
:
2533 return GPOS_apply_ContextPos(script_cache
, lpotm
, lplogfont
, analysis
, piAdvance
,
2534 lookup
, look
, glyphs
, glyph_index
, glyph_count
, pGoffset
);
2536 case GPOS_LOOKUP_POSITION_CONTEXT_CHAINED
:
2537 return GPOS_apply_ChainContextPos(script_cache
, lpotm
, lplogfont
, analysis
, piAdvance
,
2538 lookup
, look
, glyphs
, glyph_index
, glyph_count
, pGoffset
);
2541 FIXME("Unhandled GPOS lookup type %#x.\n", type
);
2546 unsigned int OpenType_apply_GPOS_lookup(const ScriptCache
*script_cache
, const OUTLINETEXTMETRICW
*otm
,
2547 const LOGFONTW
*logfont
, const SCRIPT_ANALYSIS
*analysis
, int *advance
, unsigned int lookup_index
,
2548 const WORD
*glyphs
, unsigned int glyph_index
, unsigned int glyph_count
, GOFFSET
*goffset
)
2550 const GPOS_Header
*header
= (const GPOS_Header
*)script_cache
->GPOS_Table
;
2551 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
2553 return GPOS_apply_lookup(script_cache
, otm
, logfont
, analysis
, advance
, lookup
,
2554 lookup_index
, glyphs
, glyph_index
, glyph_count
, goffset
);
2557 static LoadedScript
*usp10_script_cache_get_script(ScriptCache
*script_cache
, OPENTYPE_TAG tag
)
2561 for (i
= 0; i
< script_cache
->script_count
; ++i
)
2563 if (script_cache
->scripts
[i
].tag
== tag
)
2564 return &script_cache
->scripts
[i
];
2570 static void GSUB_initialize_script_cache(ScriptCache
*psc
)
2574 if (psc
->GSUB_Table
)
2576 const OT_ScriptList
*script
;
2577 const GSUB_Header
* header
= (const GSUB_Header
*)psc
->GSUB_Table
;
2578 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
2579 psc
->script_count
= GET_BE_WORD(script
->ScriptCount
);
2580 TRACE("initializing %li scripts in this font\n",psc
->script_count
);
2581 if (psc
->script_count
)
2583 psc
->scripts
= heap_alloc_zero(psc
->script_count
* sizeof(*psc
->scripts
));
2584 for (i
= 0; i
< psc
->script_count
; i
++)
2586 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2587 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]);
2588 psc
->scripts
[i
].gsub_table
= ((const BYTE
*)script
+ offset
);
2594 static void GPOS_expand_script_cache(ScriptCache
*psc
)
2597 const OT_ScriptList
*script
;
2598 const GPOS_Header
* header
= (const GPOS_Header
*)psc
->GPOS_Table
;
2599 LoadedScript
*loaded_script
;
2604 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
2605 count
= GET_BE_WORD(script
->ScriptCount
);
2610 if (!psc
->script_count
)
2612 psc
->script_count
= count
;
2613 TRACE("initializing %li scripts in this font\n",psc
->script_count
);
2614 if (psc
->script_count
)
2616 psc
->scripts
= heap_alloc_zero(psc
->script_count
* sizeof(*psc
->scripts
));
2617 for (i
= 0; i
< psc
->script_count
; i
++)
2619 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2620 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]);
2621 psc
->scripts
[i
].gpos_table
= ((const BYTE
*)script
+ offset
);
2627 for (i
= 0; i
< count
; i
++)
2629 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2630 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]);
2632 if (!(loaded_script
= usp10_script_cache_get_script(psc
, tag
)))
2634 if (!usp10_array_reserve((void **)&psc
->scripts
, &psc
->scripts_size
,
2635 psc
->script_count
+ 1, sizeof(*psc
->scripts
)))
2637 ERR("Failed grow scripts array.\n");
2641 loaded_script
= &psc
->scripts
[psc
->script_count
];
2642 ++psc
->script_count
;
2643 loaded_script
->tag
= tag
;
2645 loaded_script
->gpos_table
= (const BYTE
*)script
+ offset
;
2650 static void _initialize_script_cache(ScriptCache
*psc
)
2652 if (!psc
->scripts_initialized
)
2654 GSUB_initialize_script_cache(psc
);
2655 GPOS_expand_script_cache(psc
);
2656 psc
->scripts_initialized
= TRUE
;
2660 HRESULT
OpenType_GetFontScriptTags(ScriptCache
*psc
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pScriptTags
, int *pcTags
)
2663 const LoadedScript
*script
;
2666 _initialize_script_cache(psc
);
2668 *pcTags
= psc
->script_count
;
2672 if (!(script
= usp10_script_cache_get_script(psc
, searchingFor
)))
2673 return USP_E_SCRIPT_NOT_IN_FONT
;
2675 *pScriptTags
= script
->tag
;
2680 if (cMaxTags
< *pcTags
)
2683 cMaxTags
= min(cMaxTags
, psc
->script_count
);
2684 for (i
= 0; i
< cMaxTags
; ++i
)
2686 pScriptTags
[i
] = psc
->scripts
[i
].tag
;
2691 static LoadedLanguage
*usp10_script_get_language(LoadedScript
*script
, OPENTYPE_TAG tag
)
2695 for (i
= 0; i
< script
->language_count
; ++i
)
2697 if (script
->languages
[i
].tag
== tag
)
2698 return &script
->languages
[i
];
2704 static void GSUB_initialize_language_cache(LoadedScript
*script
)
2708 if (script
->gsub_table
)
2711 const OT_Script
* table
= script
->gsub_table
;
2712 script
->language_count
= GET_BE_WORD(table
->LangSysCount
);
2713 offset
= GET_BE_WORD(table
->DefaultLangSys
);
2716 script
->default_language
.tag
= MS_MAKE_TAG('d','f','l','t');
2717 script
->default_language
.gsub_table
= (const BYTE
*)table
+ offset
;
2720 if (script
->language_count
)
2722 TRACE("Deflang %p, LangCount %li\n",script
->default_language
.gsub_table
, script
->language_count
);
2724 script
->languages
= heap_alloc_zero(script
->language_count
* sizeof(*script
->languages
));
2726 for (i
= 0; i
< script
->language_count
; i
++)
2728 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2729 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]);
2730 script
->languages
[i
].gsub_table
= ((const BYTE
*)table
+ offset
);
2736 static void GPOS_expand_language_cache(LoadedScript
*script
)
2739 const OT_Script
* table
= script
->gpos_table
;
2740 LoadedLanguage
*language
;
2746 offset
= GET_BE_WORD(table
->DefaultLangSys
);
2748 script
->default_language
.gpos_table
= (const BYTE
*)table
+ offset
;
2750 count
= GET_BE_WORD(table
->LangSysCount
);
2752 TRACE("Deflang %p, LangCount %i\n",script
->default_language
.gpos_table
, count
);
2757 if (!script
->language_count
)
2760 script
->language_count
= count
;
2762 script
->languages
= heap_alloc_zero(script
->language_count
* sizeof(*script
->languages
));
2764 for (i
= 0; i
< script
->language_count
; i
++)
2766 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2767 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]);
2768 script
->languages
[i
].gpos_table
= ((const BYTE
*)table
+ offset
);
2774 for (i
= 0; i
< count
; i
++)
2776 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2777 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]);
2779 if (!(language
= usp10_script_get_language(script
, tag
)))
2781 if (!usp10_array_reserve((void **)&script
->languages
, &script
->languages_size
,
2782 script
->language_count
+ 1, sizeof(*script
->languages
)))
2784 ERR("Failed grow languages array.\n");
2788 language
= &script
->languages
[script
->language_count
];
2789 ++script
->language_count
;
2790 language
->tag
= tag
;
2792 language
->gpos_table
= (const BYTE
*)table
+ offset
;
2797 static void _initialize_language_cache(LoadedScript
*script
)
2799 if (!script
->languages_initialized
)
2801 GSUB_initialize_language_cache(script
);
2802 GPOS_expand_language_cache(script
);
2803 script
->languages_initialized
= TRUE
;
2807 HRESULT
OpenType_GetFontLanguageTags(ScriptCache
*psc
, OPENTYPE_TAG script_tag
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pLanguageTags
, int *pcTags
)
2811 LoadedScript
*script
= NULL
;
2813 _initialize_script_cache(psc
);
2814 if (!(script
= usp10_script_cache_get_script(psc
, script_tag
)))
2815 return E_INVALIDARG
;
2817 _initialize_language_cache(script
);
2819 if (!searchingFor
&& cMaxTags
< script
->language_count
)
2821 else if (searchingFor
)
2824 *pcTags
= script
->language_count
;
2826 for (i
= 0; i
< script
->language_count
; i
++)
2829 pLanguageTags
[i
] = script
->languages
[i
].tag
;
2833 if (searchingFor
== script
->languages
[i
].tag
)
2835 pLanguageTags
[0] = script
->languages
[i
].tag
;
2843 if (script
->default_language
.gsub_table
)
2846 pLanguageTags
[i
] = script
->default_language
.tag
;
2848 if (searchingFor
&& FAILED(rc
))
2850 pLanguageTags
[0] = script
->default_language
.tag
;
2853 *pcTags
= (*pcTags
) + 1;
2859 static void usp10_language_add_feature_list(LoadedLanguage
*language
, char table_type
,
2860 const OT_LangSys
*lang
, const OT_FeatureList
*feature_list
)
2862 unsigned int count
= GET_BE_WORD(lang
->FeatureCount
);
2865 TRACE("table_type %#x, %u features.\n", table_type
, count
);
2870 if (!language
->feature_count
)
2871 language
->features
= heap_alloc(count
* sizeof(*language
->features
));
2873 language
->features
= HeapReAlloc(GetProcessHeap(), 0, language
->features
,
2874 (language
->feature_count
+ count
) * sizeof(*language
->features
));
2876 for (i
= 0; i
< count
; ++i
)
2878 const OT_FeatureRecord
*record
;
2879 LoadedFeature
*loaded_feature
;
2880 const OT_Feature
*feature
;
2882 record
= &feature_list
->FeatureRecord
[GET_BE_WORD(lang
->FeatureIndex
[i
])];
2883 feature
= (const OT_Feature
*)((const BYTE
*)feature_list
+ GET_BE_WORD(record
->Feature
));
2885 loaded_feature
= &language
->features
[language
->feature_count
+ i
];
2886 loaded_feature
->tag
= MS_MAKE_TAG(record
->FeatureTag
[0], record
->FeatureTag
[1],
2887 record
->FeatureTag
[2], record
->FeatureTag
[3]);
2888 loaded_feature
->tableType
= table_type
;
2889 loaded_feature
->feature
= feature
;
2890 loaded_feature
->lookup_count
= GET_BE_WORD(feature
->LookupCount
);
2891 loaded_feature
->lookups
= heap_alloc(loaded_feature
->lookup_count
* sizeof(*loaded_feature
->lookups
));
2892 for (j
= 0; j
< loaded_feature
->lookup_count
; ++j
)
2893 loaded_feature
->lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
2895 language
->feature_count
+= count
;
2898 static void _initialize_feature_cache(ScriptCache
*psc
, LoadedLanguage
*language
)
2900 const GSUB_Header
*gsub_header
= psc
->GSUB_Table
;
2901 const GPOS_Header
*gpos_header
= psc
->GPOS_Table
;
2902 const OT_FeatureList
*feature_list
;
2903 const OT_LangSys
*lang
;
2905 if (language
->features_initialized
)
2908 if ((lang
= language
->gsub_table
))
2910 feature_list
= (const OT_FeatureList
*)((const BYTE
*)gsub_header
+ GET_BE_WORD(gsub_header
->FeatureList
));
2911 usp10_language_add_feature_list(language
, FEATURE_GSUB_TABLE
, lang
, feature_list
);
2914 if ((lang
= language
->gpos_table
))
2916 feature_list
= (const OT_FeatureList
*)((const BYTE
*)gpos_header
+ GET_BE_WORD(gpos_header
->FeatureList
));
2917 usp10_language_add_feature_list(language
, FEATURE_GPOS_TABLE
, lang
, feature_list
);
2920 language
->features_initialized
= TRUE
;
2923 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
)
2926 LoadedScript
*script
;
2928 LoadedLanguage
*language
= NULL
;
2930 _initialize_script_cache(psc
);
2931 if (!(script
= usp10_script_cache_get_script(psc
, script_tag
)))
2937 return E_INVALIDARG
;
2940 _initialize_language_cache(script
);
2942 if ((script
->default_language
.gsub_table
|| script
->default_language
.gpos_table
) && script
->default_language
.tag
== language_tag
)
2943 language
= &script
->default_language
;
2945 language
= usp10_script_get_language(script
, language_tag
);
2953 _initialize_feature_cache(psc
, language
);
2958 for (i
= 0; i
< language
->feature_count
; i
++)
2959 if (language
->features
[i
].tableType
== tableType
)
2960 *pcTags
= (*pcTags
)+1;
2963 *pcTags
= language
->feature_count
;
2965 if (!searchingFor
&& cMaxTags
< *pcTags
)
2967 else if (searchingFor
)
2970 for (i
= 0; i
< language
->feature_count
; i
++)
2974 if (!tableType
|| language
->features
[i
].tableType
== tableType
)
2975 pFeatureTags
[i
] = language
->features
[i
].tag
;
2980 if ((searchingFor
== language
->features
[i
].tag
) &&
2981 (!tableType
|| language
->features
[i
].tableType
== tableType
))
2983 pFeatureTags
[0] = language
->features
[i
].tag
;
2986 *feature
= &language
->features
[i
];