2 * Opentype font interfaces for the Uniscribe Script Processor (usp10.dll)
4 * Copyright 2012 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "usp10_internal.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe
);
38 #ifdef WORDS_BIGENDIAN
39 #define GET_BE_WORD(x) (x)
40 #define GET_BE_DWORD(x) (x)
42 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
43 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
46 #define round(x) (((x) < 0) ? (int)((x) - 0.5) : (int)((x) + 0.5))
48 /* These are all structures needed for the cmap format 12 table */
49 #define CMAP_TAG MS_MAKE_TAG('c', 'm', 'a', 'p')
55 } CMAP_EncodingRecord
;
60 CMAP_EncodingRecord tables
[1];
67 } CMAP_SegmentedCoverage_group
;
75 CMAP_SegmentedCoverage_group groups
[1];
76 } CMAP_SegmentedCoverage
;
78 /* These are all structures needed for the GDEF table */
79 enum {BaseGlyph
=1, LigatureGlyph
, MarkGlyph
, ComponentGlyph
};
86 WORD MarkAttachClassDef
;
93 WORD ClassValueArray
[1];
100 } OT_ClassRangeRecord
;
104 WORD ClassRangeCount
;
105 OT_ClassRangeRecord ClassRangeRecord
[1];
106 } OT_ClassDefFormat2
;
108 /* These are all structures needed for the GSUB table */
124 OT_ScriptRecord ScriptRecord
[1];
135 OT_LangSysRecord LangSysRecord
[1];
139 WORD LookupOrder
; /* Reserved */
140 WORD ReqFeatureIndex
;
142 WORD FeatureIndex
[1];
152 OT_FeatureRecord FeatureRecord
[1];
156 WORD FeatureParams
; /* Reserved */
158 WORD LookupListIndex
[1];
177 } OT_CoverageFormat1
;
182 WORD StartCoverageIndex
;
188 OT_RangeRecord RangeRecord
[1];
189 } OT_CoverageFormat2
;
192 WORD SubstFormat
; /* = 1 */
195 } GSUB_SingleSubstFormat1
;
198 WORD SubstFormat
; /* = 2 */
202 }GSUB_SingleSubstFormat2
;
205 WORD SubstFormat
; /* = 1 */
209 }GSUB_MultipleSubstFormat1
;
217 WORD SubstFormat
; /* = 1 */
221 }GSUB_LigatureSubstFormat1
;
236 WORD LookupListIndex
;
238 }GSUB_SubstLookupRecord
;
241 WORD SubstFormat
; /* = 1 */
243 WORD ChainSubRuleSetCount
;
244 WORD ChainSubRuleSet
[1];
245 }GSUB_ChainContextSubstFormat1
;
248 WORD SubstFormat
; /* = 3 */
249 WORD BacktrackGlyphCount
;
251 }GSUB_ChainContextSubstFormat3_1
;
254 WORD InputGlyphCount
;
256 }GSUB_ChainContextSubstFormat3_2
;
259 WORD LookaheadGlyphCount
;
261 }GSUB_ChainContextSubstFormat3_3
;
265 GSUB_SubstLookupRecord SubstLookupRecord
[1];
266 }GSUB_ChainContextSubstFormat3_4
;
269 WORD SubstFormat
; /* = 1 */
271 WORD AlternateSetCount
;
272 WORD AlternateSet
[1];
273 } GSUB_AlternateSubstFormat1
;
282 WORD ExtensionLookupType
;
283 DWORD ExtensionOffset
;
284 } GSUB_ExtensionPosFormat1
;
286 /* These are all structures needed for the GPOS table */
306 } GPOS_AnchorFormat1
;
313 } GPOS_AnchorFormat2
;
321 } GPOS_AnchorFormat3
;
339 } GPOS_SinglePosFormat1
;
347 } GPOS_SinglePosFormat2
;
355 WORD PairSetOffset
[1];
356 } GPOS_PairPosFormat1
;
367 WORD Class1Record
[1];
368 } GPOS_PairPosFormat2
;
374 } GPOS_PairValueRecord
;
378 GPOS_PairValueRecord PairValueRecord
[1];
384 } GPOS_EntryExitRecord
;
390 GPOS_EntryExitRecord EntryExitRecord
[1];
391 } GPOS_CursivePosFormat1
;
400 } GPOS_MarkBasePosFormat1
;
408 GPOS_BaseRecord BaseRecord
[1];
418 GPOS_MarkRecord MarkRecord
[1];
424 WORD LigatureCoverage
;
428 } GPOS_MarkLigPosFormat1
;
432 WORD LigatureAttach
[1];
433 } GPOS_LigatureArray
;
436 WORD LigatureAnchor
[1];
437 } GPOS_ComponentRecord
;
441 GPOS_ComponentRecord ComponentRecord
[1];
442 } GPOS_LigatureAttach
;
451 } GPOS_MarkMarkPosFormat1
;
459 GPOS_Mark2Record Mark2Record
[1];
464 WORD LookupListIndex
;
465 } GPOS_PosLookupRecord
;
469 WORD BacktrackGlyphCount
;
471 } GPOS_ChainContextPosFormat3_1
;
474 WORD InputGlyphCount
;
476 } GPOS_ChainContextPosFormat3_2
;
479 WORD LookaheadGlyphCount
;
481 } GPOS_ChainContextPosFormat3_3
;
485 GPOS_PosLookupRecord PosLookupRecord
[1];
486 } GPOS_ChainContextPosFormat3_4
;
490 WORD ExtensionLookupType
;
491 DWORD ExtensionOffset
;
492 } GPOS_ExtensionPosFormat1
;
498 static VOID
*load_CMAP_format12_table(HDC hdc
, ScriptCache
*psc
)
500 CMAP_Header
*CMAP_Table
= NULL
;
504 if (!psc
->CMAP_Table
)
506 length
= GetFontData(hdc
, CMAP_TAG
, 0, NULL
, 0);
507 if (length
!= GDI_ERROR
)
509 psc
->CMAP_Table
= HeapAlloc(GetProcessHeap(),0,length
);
510 GetFontData(hdc
, CMAP_TAG
, 0, psc
->CMAP_Table
, length
);
511 TRACE("Loaded cmap table of %i bytes\n",length
);
517 CMAP_Table
= psc
->CMAP_Table
;
519 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
); i
++)
521 if ( (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) == 3) &&
522 (GET_BE_WORD(CMAP_Table
->tables
[i
].encodingID
) == 10) )
524 CMAP_SegmentedCoverage
*format
= (CMAP_SegmentedCoverage
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
525 if (GET_BE_WORD(format
->format
) == 12)
532 static int compare_group(const void *a
, const void* b
)
534 const DWORD
*chr
= a
;
535 const CMAP_SegmentedCoverage_group
*group
= b
;
537 if (*chr
< GET_BE_DWORD(group
->startCharCode
))
539 if (*chr
> GET_BE_DWORD(group
->endCharCode
))
544 DWORD
OpenType_CMAP_GetGlyphIndex(HDC hdc
, ScriptCache
*psc
, DWORD utf32c
, LPWORD pgi
, DWORD flags
)
546 /* BMP: use gdi32 for ease */
547 if (utf32c
< 0x10000)
550 return GetGlyphIndicesW(hdc
,&ch
, 1, pgi
, flags
);
553 if (!psc
->CMAP_format12_Table
)
554 psc
->CMAP_format12_Table
= load_CMAP_format12_table(hdc
, psc
);
556 if (flags
& GGI_MARK_NONEXISTING_GLYPHS
)
561 if (psc
->CMAP_format12_Table
)
563 CMAP_SegmentedCoverage
*format
= NULL
;
564 CMAP_SegmentedCoverage_group
*group
= NULL
;
566 format
= (CMAP_SegmentedCoverage
*)psc
->CMAP_format12_Table
;
568 group
= bsearch(&utf32c
, format
->groups
, GET_BE_DWORD(format
->nGroups
),
569 sizeof(CMAP_SegmentedCoverage_group
), compare_group
);
573 DWORD offset
= utf32c
- GET_BE_DWORD(group
->startCharCode
);
574 *pgi
= GET_BE_DWORD(group
->startGlyphID
) + offset
;
585 static WORD
OT_get_glyph_class(const void *table
, WORD glyph
)
588 const OT_ClassDefFormat1
*cf1
= table
;
590 if (!table
) return 0;
592 if (GET_BE_WORD(cf1
->ClassFormat
) == 1)
594 if (glyph
>= GET_BE_WORD(cf1
->StartGlyph
))
596 int index
= glyph
- GET_BE_WORD(cf1
->StartGlyph
);
597 if (index
< GET_BE_WORD(cf1
->GlyphCount
))
598 class = GET_BE_WORD(cf1
->ClassValueArray
[index
]);
601 else if (GET_BE_WORD(cf1
->ClassFormat
) == 2)
603 const OT_ClassDefFormat2
*cf2
= table
;
605 top
= GET_BE_WORD(cf2
->ClassRangeCount
);
606 for (i
= 0; i
< top
; i
++)
608 if (glyph
>= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Start
) &&
609 glyph
<= GET_BE_WORD(cf2
->ClassRangeRecord
[i
].End
))
611 class = GET_BE_WORD(cf2
->ClassRangeRecord
[i
].Class
);
617 ERR("Unknown Class Format %i\n",GET_BE_WORD(cf1
->ClassFormat
));
622 void OpenType_GDEF_UpdateGlyphProps(ScriptCache
*psc
, const WORD
*pwGlyphs
, const WORD cGlyphs
, WORD
* pwLogClust
, const WORD cChars
, SCRIPT_GLYPHPROP
*pGlyphProp
)
625 void *glyph_class_table
= NULL
;
629 const GDEF_Header
*header
= psc
->GDEF_Table
;
630 WORD offset
= GET_BE_WORD( header
->GlyphClassDef
);
632 glyph_class_table
= (BYTE
*)psc
->GDEF_Table
+ offset
;
635 for (i
= 0; i
< cGlyphs
; i
++)
641 k
= USP10_FindGlyphInLogClust(pwLogClust
, cChars
, i
);
644 for (; k
< cChars
&& pwLogClust
[k
] == i
; k
++)
648 class = OT_get_glyph_class( glyph_class_table
, pwGlyphs
[i
] );
654 pGlyphProp
[i
].sva
.fClusterStart
= 1;
655 pGlyphProp
[i
].sva
.fDiacritic
= 0;
656 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
659 pGlyphProp
[i
].sva
.fClusterStart
= 1;
660 pGlyphProp
[i
].sva
.fDiacritic
= 0;
661 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
664 pGlyphProp
[i
].sva
.fClusterStart
= 0;
665 pGlyphProp
[i
].sva
.fDiacritic
= 1;
666 pGlyphProp
[i
].sva
.fZeroWidth
= 1;
669 pGlyphProp
[i
].sva
.fClusterStart
= 0;
670 pGlyphProp
[i
].sva
.fDiacritic
= 0;
671 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
674 ERR("Unknown glyph class %i\n",class);
675 pGlyphProp
[i
].sva
.fClusterStart
= 1;
676 pGlyphProp
[i
].sva
.fDiacritic
= 0;
677 pGlyphProp
[i
].sva
.fZeroWidth
= 0;
681 pGlyphProp
[i
].sva
.fClusterStart
= 0;
688 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
);
690 static INT
GSUB_is_glyph_covered(LPCVOID table
, UINT glyph
)
692 const OT_CoverageFormat1
* cf1
;
696 if (GET_BE_WORD(cf1
->CoverageFormat
) == 1)
698 int count
= GET_BE_WORD(cf1
->GlyphCount
);
700 TRACE("Coverage Format 1, %i glyphs\n",count
);
701 for (i
= 0; i
< count
; i
++)
702 if (glyph
== GET_BE_WORD(cf1
->GlyphArray
[i
]))
706 else if (GET_BE_WORD(cf1
->CoverageFormat
) == 2)
708 const OT_CoverageFormat2
* cf2
;
711 cf2
= (const OT_CoverageFormat2
*)cf1
;
713 count
= GET_BE_WORD(cf2
->RangeCount
);
714 TRACE("Coverage Format 2, %i ranges\n",count
);
715 for (i
= 0; i
< count
; i
++)
717 if (glyph
< GET_BE_WORD(cf2
->RangeRecord
[i
].Start
))
719 if ((glyph
>= GET_BE_WORD(cf2
->RangeRecord
[i
].Start
)) &&
720 (glyph
<= GET_BE_WORD(cf2
->RangeRecord
[i
].End
)))
722 return (GET_BE_WORD(cf2
->RangeRecord
[i
].StartCoverageIndex
) +
723 glyph
- GET_BE_WORD(cf2
->RangeRecord
[i
].Start
));
729 ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1
->CoverageFormat
));
734 static const BYTE
*GSUB_get_subtable(const OT_LookupTable
*look
, int index
)
736 int offset
= GET_BE_WORD(look
->SubTable
[index
]);
738 if (GET_BE_WORD(look
->LookupType
) == 7)
740 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)look
+ offset
);
741 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
743 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
747 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext
->SubstFormat
));
750 return (const BYTE
*)look
+ offset
;
753 static INT
GSUB_apply_SingleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
756 TRACE("Single Substitution Subtable\n");
758 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
760 const GSUB_SingleSubstFormat1
*ssf1
= (const GSUB_SingleSubstFormat1
*)GSUB_get_subtable(look
, j
);
761 if (GET_BE_WORD(ssf1
->SubstFormat
) == 1)
763 int offset
= GET_BE_WORD(ssf1
->Coverage
);
764 TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1
->DeltaGlyphID
));
765 if (GSUB_is_glyph_covered((const BYTE
*)ssf1
+offset
, glyphs
[glyph_index
]) != -1)
767 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
768 glyphs
[glyph_index
] = glyphs
[glyph_index
] + GET_BE_WORD(ssf1
->DeltaGlyphID
);
769 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
770 return glyph_index
+ write_dir
;
775 const GSUB_SingleSubstFormat2
*ssf2
;
779 ssf2
= (const GSUB_SingleSubstFormat2
*)ssf1
;
780 offset
= GET_BE_WORD(ssf1
->Coverage
);
781 TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2
->GlyphCount
));
782 index
= GSUB_is_glyph_covered((const BYTE
*)ssf2
+offset
, glyphs
[glyph_index
]);
783 TRACE(" Coverage index %i\n",index
);
786 if (glyphs
[glyph_index
] == GET_BE_WORD(ssf2
->Substitute
[index
]))
787 return GSUB_E_NOGLYPH
;
789 TRACE(" Glyph is 0x%x ->",glyphs
[glyph_index
]);
790 glyphs
[glyph_index
] = GET_BE_WORD(ssf2
->Substitute
[index
]);
791 TRACE("0x%x\n",glyphs
[glyph_index
]);
792 return glyph_index
+ write_dir
;
796 return GSUB_E_NOGLYPH
;
799 static INT
GSUB_apply_MultipleSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
802 TRACE("Multiple Substitution Subtable\n");
804 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
807 const GSUB_MultipleSubstFormat1
*msf1
;
808 msf1
= (const GSUB_MultipleSubstFormat1
*)GSUB_get_subtable(look
, j
);
810 offset
= GET_BE_WORD(msf1
->Coverage
);
811 index
= GSUB_is_glyph_covered((const BYTE
*)msf1
+offset
, glyphs
[glyph_index
]);
814 const GSUB_Sequence
*seq
;
817 offset
= GET_BE_WORD(msf1
->Sequence
[index
]);
818 seq
= (const GSUB_Sequence
*)((const BYTE
*)msf1
+offset
);
819 sub_count
= GET_BE_WORD(seq
->GlyphCount
);
820 TRACE(" Glyph 0x%x (+%i)->",glyphs
[glyph_index
],(sub_count
-1));
822 for (j
= (*glyph_count
)+(sub_count
-1); j
> glyph_index
; j
--)
823 glyphs
[j
] =glyphs
[j
-(sub_count
-1)];
825 for (j
= 0; j
< sub_count
; j
++)
827 glyphs
[glyph_index
+ (sub_count
-1) - j
] = GET_BE_WORD(seq
->Substitute
[j
]);
829 glyphs
[glyph_index
+ j
] = GET_BE_WORD(seq
->Substitute
[j
]);
831 *glyph_count
= *glyph_count
+ (sub_count
- 1);
833 if (TRACE_ON(uniscribe
))
835 for (j
= 0; j
< sub_count
; j
++)
836 TRACE(" 0x%x",glyphs
[glyph_index
+j
]);
840 return glyph_index
+ (sub_count
* write_dir
);
843 return GSUB_E_NOGLYPH
;
846 static INT
GSUB_apply_AlternateSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
849 TRACE("Alternate Substitution Subtable\n");
851 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
854 const GSUB_AlternateSubstFormat1
*asf1
;
857 asf1
= (const GSUB_AlternateSubstFormat1
*)GSUB_get_subtable(look
, j
);
858 offset
= GET_BE_WORD(asf1
->Coverage
);
860 index
= GSUB_is_glyph_covered((const BYTE
*)asf1
+offset
, glyphs
[glyph_index
]);
863 const GSUB_AlternateSet
*as
;
864 offset
= GET_BE_WORD(asf1
->AlternateSet
[index
]);
865 as
= (const GSUB_AlternateSet
*)((const BYTE
*)asf1
+offset
);
866 FIXME("%i alternates, picking index 0\n",GET_BE_WORD(as
->GlyphCount
));
867 if (glyphs
[glyph_index
] == GET_BE_WORD(as
->Alternate
[0]))
868 return GSUB_E_NOGLYPH
;
870 TRACE(" Glyph 0x%x ->",glyphs
[glyph_index
]);
871 glyphs
[glyph_index
] = GET_BE_WORD(as
->Alternate
[0]);
872 TRACE(" 0x%x\n",glyphs
[glyph_index
]);
873 return glyph_index
+ write_dir
;
876 return GSUB_E_NOGLYPH
;
879 static INT
GSUB_apply_LigatureSubst(const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
883 TRACE("Ligature Substitution Subtable\n");
884 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
886 const GSUB_LigatureSubstFormat1
*lsf1
;
889 lsf1
= (const GSUB_LigatureSubstFormat1
*)GSUB_get_subtable(look
, j
);
890 offset
= GET_BE_WORD(lsf1
->Coverage
);
891 index
= GSUB_is_glyph_covered((const BYTE
*)lsf1
+offset
, glyphs
[glyph_index
]);
892 TRACE(" Coverage index %i\n",index
);
895 const GSUB_LigatureSet
*ls
;
898 offset
= GET_BE_WORD(lsf1
->LigatureSet
[index
]);
899 ls
= (const GSUB_LigatureSet
*)((const BYTE
*)lsf1
+offset
);
900 count
= GET_BE_WORD(ls
->LigatureCount
);
901 TRACE(" LigatureSet has %i members\n",count
);
902 for (k
= 0; k
< count
; k
++)
904 const GSUB_Ligature
*lig
;
905 int CompCount
,l
,CompIndex
;
907 offset
= GET_BE_WORD(ls
->Ligature
[k
]);
908 lig
= (const GSUB_Ligature
*)((const BYTE
*)ls
+offset
);
909 CompCount
= GET_BE_WORD(lig
->CompCount
) - 1;
910 CompIndex
= glyph_index
+write_dir
;
911 for (l
= 0; l
< CompCount
&& CompIndex
>= 0 && CompIndex
< *glyph_count
; l
++)
914 CompGlyph
= GET_BE_WORD(lig
->Component
[l
]);
915 if (CompGlyph
!= glyphs
[CompIndex
])
917 CompIndex
+= write_dir
;
921 int replaceIdx
= glyph_index
;
923 replaceIdx
= glyph_index
- CompCount
;
925 TRACE(" Glyph is 0x%x (+%i) ->",glyphs
[glyph_index
],CompCount
);
926 glyphs
[replaceIdx
] = GET_BE_WORD(lig
->LigGlyph
);
927 TRACE("0x%x\n",glyphs
[replaceIdx
]);
931 for (j
= replaceIdx
+ 1; j
< *glyph_count
; j
++)
932 glyphs
[j
] =glyphs
[j
+CompCount
];
933 *glyph_count
= *glyph_count
- CompCount
;
935 return replaceIdx
+ write_dir
;
940 return GSUB_E_NOGLYPH
;
943 static INT
GSUB_apply_ChainContextSubst(const OT_LookupList
* lookup
, const OT_LookupTable
*look
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
947 TRACE("Chaining Contextual Substitution Subtable\n");
948 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
950 const GSUB_ChainContextSubstFormat1
*ccsf1
;
952 int dirLookahead
= write_dir
;
953 int dirBacktrack
= -1 * write_dir
;
955 ccsf1
= (const GSUB_ChainContextSubstFormat1
*)GSUB_get_subtable(look
, j
);
956 if (GET_BE_WORD(ccsf1
->SubstFormat
) == 1)
960 FIXME(" TODO: subtype 1 (Simple context glyph substitution)\n");
963 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 2)
967 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
970 else if (GET_BE_WORD(ccsf1
->SubstFormat
) == 3)
974 const GSUB_ChainContextSubstFormat3_1
*ccsf3_1
;
975 const GSUB_ChainContextSubstFormat3_2
*ccsf3_2
;
976 const GSUB_ChainContextSubstFormat3_3
*ccsf3_3
;
977 const GSUB_ChainContextSubstFormat3_4
*ccsf3_4
;
978 int newIndex
= glyph_index
;
980 ccsf3_1
= (const GSUB_ChainContextSubstFormat3_1
*)ccsf1
;
982 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
984 for (k
= 0; k
< GET_BE_WORD(ccsf3_1
->BacktrackGlyphCount
); k
++)
986 offset
= GET_BE_WORD(ccsf3_1
->Coverage
[k
]);
987 if (GSUB_is_glyph_covered((const BYTE
*)ccsf3_1
+offset
, glyphs
[glyph_index
+ (dirBacktrack
* (k
+1))]) == -1)
990 if (k
!= GET_BE_WORD(ccsf3_1
->BacktrackGlyphCount
))
992 TRACE("Matched Backtrack\n");
994 ccsf3_2
= (const GSUB_ChainContextSubstFormat3_2
*)((BYTE
*)ccsf1
+
995 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_1
, Coverage
[GET_BE_WORD(ccsf3_1
->BacktrackGlyphCount
)]));
997 indexGlyphs
= GET_BE_WORD(ccsf3_2
->InputGlyphCount
);
998 for (k
= 0; k
< indexGlyphs
; k
++)
1000 offset
= GET_BE_WORD(ccsf3_2
->Coverage
[k
]);
1001 if (GSUB_is_glyph_covered((const BYTE
*)ccsf3_1
+offset
, glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
1004 if (k
!= indexGlyphs
)
1006 TRACE("Matched IndexGlyphs\n");
1008 ccsf3_3
= (const GSUB_ChainContextSubstFormat3_3
*)((BYTE
*)ccsf3_2
+
1009 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_2
, Coverage
[GET_BE_WORD(ccsf3_2
->InputGlyphCount
)]));
1011 for (k
= 0; k
< GET_BE_WORD(ccsf3_3
->LookaheadGlyphCount
); k
++)
1013 offset
= GET_BE_WORD(ccsf3_3
->Coverage
[k
]);
1014 if (GSUB_is_glyph_covered((const BYTE
*)ccsf3_1
+offset
, glyphs
[glyph_index
+ (dirLookahead
* (indexGlyphs
+ k
))]) == -1)
1017 if (k
!= GET_BE_WORD(ccsf3_3
->LookaheadGlyphCount
))
1019 TRACE("Matched LookAhead\n");
1021 ccsf3_4
= (const GSUB_ChainContextSubstFormat3_4
*)((BYTE
*)ccsf3_3
+
1022 FIELD_OFFSET(GSUB_ChainContextSubstFormat3_3
, Coverage
[GET_BE_WORD(ccsf3_3
->LookaheadGlyphCount
)]));
1024 if (GET_BE_WORD(ccsf3_4
->SubstCount
))
1026 for (k
= 0; k
< GET_BE_WORD(ccsf3_4
->SubstCount
); k
++)
1028 int lookupIndex
= GET_BE_WORD(ccsf3_4
->SubstLookupRecord
[k
].LookupListIndex
);
1029 int SequenceIndex
= GET_BE_WORD(ccsf3_4
->SubstLookupRecord
[k
].SequenceIndex
) * write_dir
;
1031 TRACE("SUBST: %i -> %i %i\n",k
, SequenceIndex
, lookupIndex
);
1032 newIndex
= GSUB_apply_lookup(lookup
, lookupIndex
, glyphs
, glyph_index
+ SequenceIndex
, write_dir
, glyph_count
);
1035 ERR("Chain failed to generate a glyph\n");
1041 else return GSUB_E_NOGLYPH
;
1047 static INT
GSUB_apply_lookup(const OT_LookupList
* lookup
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1051 const OT_LookupTable
*look
;
1053 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
1054 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
1055 type
= GET_BE_WORD(look
->LookupType
);
1056 TRACE("type %i, flag %x, subtables %i\n",type
,GET_BE_WORD(look
->LookupFlag
),GET_BE_WORD(look
->SubTableCount
));
1059 if (GET_BE_WORD(look
->SubTableCount
))
1061 const GSUB_ExtensionPosFormat1
*ext
= (const GSUB_ExtensionPosFormat1
*)((const BYTE
*)look
+ GET_BE_WORD(look
->SubTable
[0]));
1062 if (GET_BE_WORD(ext
->SubstFormat
) == 1)
1064 type
= GET_BE_WORD(ext
->ExtensionLookupType
);
1065 TRACE("extension type %i\n",type
);
1069 FIXME("Unhandled Extension Substitution Format %i\n",GET_BE_WORD(ext
->SubstFormat
));
1074 WARN("lookup type is Extension Substitution but no extension subtable exists\n");
1080 return GSUB_apply_SingleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1082 return GSUB_apply_MultipleSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1084 return GSUB_apply_AlternateSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1086 return GSUB_apply_LigatureSubst(look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1088 return GSUB_apply_ChainContextSubst(lookup
, look
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1090 FIXME("Extension Substitution types not valid here\n");
1093 FIXME("We do not handle SubType %i\n",type
);
1095 return GSUB_E_NOGLYPH
;
1098 INT
OpenType_apply_GSUB_lookup(LPCVOID table
, INT lookup_index
, WORD
*glyphs
, INT glyph_index
, INT write_dir
, INT
*glyph_count
)
1100 const GSUB_Header
*header
= (const GSUB_Header
*)table
;
1101 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
1103 return GSUB_apply_lookup(lookup
, lookup_index
, glyphs
, glyph_index
, write_dir
, glyph_count
);
1109 static INT
GPOS_apply_lookup(ScriptCache
*psc
, LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, const SCRIPT_ANALYSIS
*analysis
, INT
* piAdvance
,
1110 const OT_LookupList
* lookup
, INT lookup_index
, const WORD
*glyphs
, INT glyph_index
, INT glyph_count
, GOFFSET
*pGoffset
);
1112 static INT
GPOS_get_device_table_value(const OT_DeviceTable
*DeviceTable
, WORD ppem
)
1114 static const WORD mask
[3] = {3,0xf,0xff};
1115 if (DeviceTable
&& ppem
>= GET_BE_WORD(DeviceTable
->StartSize
) && ppem
<= GET_BE_WORD(DeviceTable
->EndSize
))
1117 int format
= GET_BE_WORD(DeviceTable
->DeltaFormat
);
1118 int index
= ppem
- GET_BE_WORD(DeviceTable
->StartSize
);
1120 TRACE("device table, format %i, index %i\n",format
, index
);
1121 index
= index
<< format
;
1122 value
= (DeviceTable
->DeltaValue
[index
/sizeof(WORD
)] << (index
%sizeof(WORD
)))&mask
[format
-1];
1123 TRACE("offset %i, value %i\n",index
, value
);
1124 if (value
> mask
[format
-1]/2)
1125 value
= -1 * ((mask
[format
-1]+1) - value
);
1131 static VOID
GPOS_get_anchor_values(LPCVOID table
, LPPOINT pt
, WORD ppem
)
1133 const GPOS_AnchorFormat1
* anchor1
= (const GPOS_AnchorFormat1
*)table
;
1135 switch (GET_BE_WORD(anchor1
->AnchorFormat
))
1139 TRACE("Anchor Format 1\n");
1140 pt
->x
= (short)GET_BE_WORD(anchor1
->XCoordinate
);
1141 pt
->y
= (short)GET_BE_WORD(anchor1
->YCoordinate
);
1146 const GPOS_AnchorFormat2
* anchor2
= (const GPOS_AnchorFormat2
*)table
;
1147 TRACE("Anchor Format 2\n");
1148 pt
->x
= (short)GET_BE_WORD(anchor2
->XCoordinate
);
1149 pt
->y
= (short)GET_BE_WORD(anchor2
->YCoordinate
);
1155 const GPOS_AnchorFormat3
* anchor3
= (const GPOS_AnchorFormat3
*)table
;
1156 TRACE("Anchor Format 3\n");
1157 pt
->x
= (short)GET_BE_WORD(anchor3
->XCoordinate
);
1158 pt
->y
= (short)GET_BE_WORD(anchor3
->YCoordinate
);
1159 offset
= GET_BE_WORD(anchor3
->XDeviceTable
);
1160 TRACE("ppem %i\n",ppem
);
1163 const OT_DeviceTable
* DeviceTableX
= NULL
;
1164 DeviceTableX
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1165 pt
->x
+= GPOS_get_device_table_value(DeviceTableX
, ppem
);
1167 offset
= GET_BE_WORD(anchor3
->YDeviceTable
);
1170 const OT_DeviceTable
* DeviceTableY
= NULL
;
1171 DeviceTableY
= (const OT_DeviceTable
*)((const BYTE
*)anchor3
+ offset
);
1172 pt
->y
+= GPOS_get_device_table_value(DeviceTableY
, ppem
);
1177 ERR("Unknown Anchor Format %i\n",GET_BE_WORD(anchor1
->AnchorFormat
));
1183 static void GPOS_convert_design_units_to_device(LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, int desX
, int desY
, double *devX
, double *devY
)
1185 int emHeight
= lpotm
->otmTextMetrics
.tmAscent
+ lpotm
->otmTextMetrics
.tmDescent
- lpotm
->otmTextMetrics
.tmInternalLeading
;
1187 TRACE("emHeight %i lfWidth %i\n",emHeight
, lplogfont
->lfWidth
);
1188 *devX
= (desX
* emHeight
) / (double)lpotm
->otmEMSquare
;
1189 *devY
= (desY
* emHeight
) / (double)lpotm
->otmEMSquare
;
1190 if (lplogfont
->lfWidth
)
1191 FIXME("Font with lfWidth set not handled properly\n");
1194 static INT
GPOS_get_value_record(WORD ValueFormat
, const WORD data
[], GPOS_ValueRecord
*record
)
1197 if (ValueFormat
& 0x0001) { if (data
) record
->XPlacement
= GET_BE_WORD(data
[offset
]); offset
++; }
1198 if (ValueFormat
& 0x0002) { if (data
) record
->YPlacement
= GET_BE_WORD(data
[offset
]); offset
++; }
1199 if (ValueFormat
& 0x0004) { if (data
) record
->XAdvance
= GET_BE_WORD(data
[offset
]); offset
++; }
1200 if (ValueFormat
& 0x0008) { if (data
) record
->YAdvance
= GET_BE_WORD(data
[offset
]); offset
++; }
1201 if (ValueFormat
& 0x0010) { if (data
) record
->XPlaDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1202 if (ValueFormat
& 0x0020) { if (data
) record
->YPlaDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1203 if (ValueFormat
& 0x0040) { if (data
) record
->XAdvDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1204 if (ValueFormat
& 0x0080) { if (data
) record
->YAdvDevice
= GET_BE_WORD(data
[offset
]); offset
++; }
1208 static VOID
GPOS_get_value_record_offsets(const BYTE
* head
, GPOS_ValueRecord
*ValueRecord
, WORD ValueFormat
, INT ppem
, LPPOINT ptPlacement
, LPPOINT ptAdvance
)
1210 if (ValueFormat
& 0x0001) ptPlacement
->x
+= (short)ValueRecord
->XPlacement
;
1211 if (ValueFormat
& 0x0002) ptPlacement
->y
+= (short)ValueRecord
->YPlacement
;
1212 if (ValueFormat
& 0x0004) ptAdvance
->x
+= (short)ValueRecord
->XAdvance
;
1213 if (ValueFormat
& 0x0008) ptAdvance
->y
+= (short)ValueRecord
->YAdvance
;
1214 if (ValueFormat
& 0x0010) ptPlacement
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XPlaDevice
), ppem
);
1215 if (ValueFormat
& 0x0020) ptPlacement
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YPlaDevice
), ppem
);
1216 if (ValueFormat
& 0x0040) ptAdvance
->x
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->XAdvDevice
), ppem
);
1217 if (ValueFormat
& 0x0080) ptAdvance
->y
+= GPOS_get_device_table_value((const OT_DeviceTable
*)(head
+ ValueRecord
->YAdvDevice
), ppem
);
1218 if (ValueFormat
& 0xFF00) FIXME("Unhandled Value Format %x\n",ValueFormat
&0xFF00);
1221 static const BYTE
*GPOS_get_subtable(const OT_LookupTable
*look
, int index
)
1223 int offset
= GET_BE_WORD(look
->SubTable
[index
]);
1225 if (GET_BE_WORD(look
->LookupType
) == 9)
1227 const GPOS_ExtensionPosFormat1
*ext
= (const GPOS_ExtensionPosFormat1
*)((const BYTE
*)look
+ offset
);
1228 if (GET_BE_WORD(ext
->PosFormat
) == 1)
1230 offset
+= GET_BE_DWORD(ext
->ExtensionOffset
);
1234 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext
->PosFormat
));
1237 return (const BYTE
*)look
+ offset
;
1240 static VOID
GPOS_apply_SingleAdjustment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, INT glyph_index
,
1241 INT glyph_count
, INT ppem
, LPPOINT ptAdjust
, LPPOINT ptAdvance
)
1245 TRACE("Single Adjustment Positioning Subtable\n");
1247 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1249 const GPOS_SinglePosFormat1
*spf1
= (const GPOS_SinglePosFormat1
*)GPOS_get_subtable(look
, j
);
1251 if (GET_BE_WORD(spf1
->PosFormat
) == 1)
1253 offset
= GET_BE_WORD(spf1
->Coverage
);
1254 if (GSUB_is_glyph_covered((const BYTE
*)spf1
+offset
, glyphs
[glyph_index
]) != -1)
1256 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1257 WORD ValueFormat
= GET_BE_WORD(spf1
->ValueFormat
);
1258 GPOS_get_value_record(ValueFormat
, spf1
->Value
, &ValueRecord
);
1259 GPOS_get_value_record_offsets((const BYTE
*)spf1
, &ValueRecord
, ValueFormat
, ppem
, ptAdjust
, ptAdvance
);
1260 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1263 else if (GET_BE_WORD(spf1
->PosFormat
) == 2)
1266 const GPOS_SinglePosFormat2
*spf2
;
1267 spf2
= (const GPOS_SinglePosFormat2
*)spf1
;
1268 offset
= GET_BE_WORD(spf2
->Coverage
);
1269 index
= GSUB_is_glyph_covered((const BYTE
*)spf2
+offset
, glyphs
[glyph_index
]);
1273 GPOS_ValueRecord ValueRecord
= {0,0,0,0,0,0,0,0};
1274 WORD ValueFormat
= GET_BE_WORD(spf2
->ValueFormat
);
1275 size
= GPOS_get_value_record(ValueFormat
, spf2
->Value
, &ValueRecord
);
1278 offset
= size
* index
;
1279 GPOS_get_value_record(ValueFormat
, &spf2
->Value
[offset
], &ValueRecord
);
1281 GPOS_get_value_record_offsets((const BYTE
*)spf2
, &ValueRecord
, ValueFormat
, ppem
, ptAdjust
, ptAdvance
);
1282 TRACE("Glyph Adjusted by %i,%i\n",ValueRecord
.XPlacement
,ValueRecord
.YPlacement
);
1286 FIXME("Single Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(spf1
->PosFormat
));
1290 static void apply_pair_value( const void *pos_table
, WORD val_fmt1
, WORD val_fmt2
, const WORD
*pair
,
1291 INT ppem
, POINT
*adjust
, POINT
*advance
)
1293 GPOS_ValueRecord val_rec1
= {0,0,0,0,0,0,0,0};
1294 GPOS_ValueRecord val_rec2
= {0,0,0,0,0,0,0,0};
1297 size
= GPOS_get_value_record( val_fmt1
, pair
, &val_rec1
);
1298 GPOS_get_value_record( val_fmt2
, pair
+ size
, &val_rec2
);
1302 GPOS_get_value_record_offsets( pos_table
, &val_rec1
, val_fmt1
, ppem
, adjust
, advance
);
1303 TRACE( "Glyph 1 resulting cumulative offset is %i,%i design units\n", adjust
[0].x
, adjust
[0].y
);
1304 TRACE( "Glyph 1 resulting cumulative advance is %i,%i design units\n", advance
[0].x
, advance
[0].y
);
1308 GPOS_get_value_record_offsets( pos_table
, &val_rec2
, val_fmt2
, ppem
, adjust
+ 1, advance
+ 1 );
1309 TRACE( "Glyph 2 resulting cumulative offset is %i,%i design units\n", adjust
[1].x
, adjust
[1].y
);
1310 TRACE( "Glyph 2 resulting cumulative advance is %i,%i design units\n", advance
[1].x
, advance
[1].y
);
1314 static INT
GPOS_apply_PairAdjustment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, INT glyph_index
,
1315 INT glyph_count
, INT ppem
, LPPOINT ptAdjust
, LPPOINT ptAdvance
)
1318 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1320 if (glyph_index
+ write_dir
< 0 || glyph_index
+ write_dir
>= glyph_count
) return glyph_index
+ 1;
1322 TRACE("Pair Adjustment Positioning Subtable\n");
1324 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1326 const GPOS_PairPosFormat1
*ppf1
= (const GPOS_PairPosFormat1
*)GPOS_get_subtable(look
, j
);
1328 if (GET_BE_WORD(ppf1
->PosFormat
) == 1)
1331 WORD ValueFormat1
= GET_BE_WORD(ppf1
->ValueFormat1
);
1332 WORD ValueFormat2
= GET_BE_WORD(ppf1
->ValueFormat2
);
1333 INT val_fmt1_size
= GPOS_get_value_record( ValueFormat1
, NULL
, NULL
);
1334 INT val_fmt2_size
= GPOS_get_value_record( ValueFormat2
, NULL
, NULL
);
1335 offset
= GET_BE_WORD(ppf1
->Coverage
);
1336 index
= GSUB_is_glyph_covered((const BYTE
*)ppf1
+offset
, glyphs
[glyph_index
]);
1337 if (index
!= -1 && index
< GET_BE_WORD(ppf1
->PairSetCount
))
1341 const GPOS_PairSet
*ps
;
1342 const GPOS_PairValueRecord
*pair_val_rec
;
1343 offset
= GET_BE_WORD(ppf1
->PairSetOffset
[index
]);
1344 ps
= (const GPOS_PairSet
*)((const BYTE
*)ppf1
+offset
);
1345 pair_count
= GET_BE_WORD(ps
->PairValueCount
);
1346 pair_val_rec
= ps
->PairValueRecord
;
1347 for (k
= 0; k
< pair_count
; k
++)
1349 WORD second_glyph
= GET_BE_WORD(pair_val_rec
->SecondGlyph
);
1350 if (glyphs
[glyph_index
+write_dir
] == second_glyph
)
1353 TRACE("Format 1: Found Pair %x,%x\n",glyphs
[glyph_index
],glyphs
[glyph_index
+write_dir
]);
1354 apply_pair_value( ppf1
, ValueFormat1
, ValueFormat2
, pair_val_rec
->Value1
, ppem
, ptAdjust
, ptAdvance
);
1355 if (ValueFormat2
) next
++;
1356 return glyph_index
+ next
;
1358 pair_val_rec
= (const GPOS_PairValueRecord
*)(pair_val_rec
->Value1
+ val_fmt1_size
+ val_fmt2_size
);
1362 else if (GET_BE_WORD(ppf1
->PosFormat
) == 2)
1364 const GPOS_PairPosFormat2
*ppf2
= (const GPOS_PairPosFormat2
*)ppf1
;
1366 WORD ValueFormat1
= GET_BE_WORD( ppf2
->ValueFormat1
);
1367 WORD ValueFormat2
= GET_BE_WORD( ppf2
->ValueFormat2
);
1368 INT val_fmt1_size
= GPOS_get_value_record( ValueFormat1
, NULL
, NULL
);
1369 INT val_fmt2_size
= GPOS_get_value_record( ValueFormat2
, NULL
, NULL
);
1370 WORD class1_count
= GET_BE_WORD( ppf2
->Class1Count
);
1371 WORD class2_count
= GET_BE_WORD( ppf2
->Class2Count
);
1373 offset
= GET_BE_WORD( ppf2
->Coverage
);
1374 index
= GSUB_is_glyph_covered( (const BYTE
*)ppf2
+ offset
, glyphs
[glyph_index
] );
1377 WORD class1
, class2
;
1378 class1
= OT_get_glyph_class( (const BYTE
*)ppf2
+ GET_BE_WORD(ppf2
->ClassDef1
), glyphs
[glyph_index
] );
1379 class2
= OT_get_glyph_class( (const BYTE
*)ppf2
+ GET_BE_WORD(ppf2
->ClassDef2
), glyphs
[glyph_index
+ write_dir
] );
1380 if (class1
< class1_count
&& class2
< class2_count
)
1382 const WORD
*pair_val
= ppf2
->Class1Record
+ (class1
* class2_count
+ class2
) * (val_fmt1_size
+ val_fmt2_size
);
1385 TRACE( "Format 2: Found Pair %x,%x\n", glyphs
[glyph_index
], glyphs
[glyph_index
+ write_dir
] );
1387 apply_pair_value( ppf2
, ValueFormat1
, ValueFormat2
, pair_val
, ppem
, ptAdjust
, ptAdvance
);
1388 if (ValueFormat2
) next
++;
1389 return glyph_index
+ next
;
1394 FIXME("Pair Adjustment Positioning: Format %i Unhandled\n",GET_BE_WORD(ppf1
->PosFormat
));
1396 return glyph_index
+1;
1399 static VOID
GPOS_apply_CursiveAttachment(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, INT glyph_index
,
1400 INT glyph_count
, INT ppem
, LPPOINT pt
)
1403 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1405 if (glyph_index
+ write_dir
< 0 || glyph_index
+ write_dir
>= glyph_count
) return;
1407 TRACE("Cursive Attachment Positioning Subtable\n");
1409 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1411 const GPOS_CursivePosFormat1
*cpf1
= (const GPOS_CursivePosFormat1
*)GPOS_get_subtable(look
, j
);
1412 if (GET_BE_WORD(cpf1
->PosFormat
) == 1)
1414 int index_exit
, index_entry
;
1415 WORD offset
= GET_BE_WORD( cpf1
->Coverage
);
1416 index_exit
= GSUB_is_glyph_covered((const BYTE
*)cpf1
+offset
, glyphs
[glyph_index
]);
1417 if (index_exit
!= -1 && cpf1
->EntryExitRecord
[index_exit
].ExitAnchor
!= 0)
1419 index_entry
= GSUB_is_glyph_covered((const BYTE
*)cpf1
+offset
, glyphs
[glyph_index
+write_dir
]);
1420 if (index_entry
!= -1 && cpf1
->EntryExitRecord
[index_entry
].EntryAnchor
!= 0)
1422 POINT exit_pt
, entry_pt
;
1423 offset
= GET_BE_WORD(cpf1
->EntryExitRecord
[index_exit
].ExitAnchor
);
1424 GPOS_get_anchor_values((const BYTE
*)cpf1
+ offset
, &exit_pt
, ppem
);
1425 offset
= GET_BE_WORD(cpf1
->EntryExitRecord
[index_entry
].EntryAnchor
);
1426 GPOS_get_anchor_values((const BYTE
*)cpf1
+ offset
, &entry_pt
, ppem
);
1427 TRACE("Found linkage %x[%i,%i] %x[%i,%i]\n",glyphs
[glyph_index
], exit_pt
.x
,exit_pt
.y
, glyphs
[glyph_index
+write_dir
], entry_pt
.x
, entry_pt
.y
);
1428 pt
->x
= entry_pt
.x
- exit_pt
.x
;
1429 pt
->y
= entry_pt
.y
- exit_pt
.y
;
1435 FIXME("Cursive Attachment Positioning: Format %i Unhandled\n",GET_BE_WORD(cpf1
->PosFormat
));
1440 static int GPOS_apply_MarkToBase(ScriptCache
*psc
, const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, INT glyph_index
, INT glyph_count
, INT ppem
, LPPOINT pt
)
1443 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1444 void *glyph_class_table
= NULL
;
1447 if (psc
->GDEF_Table
)
1449 const GDEF_Header
*header
= psc
->GDEF_Table
;
1450 WORD offset
= GET_BE_WORD( header
->GlyphClassDef
);
1452 glyph_class_table
= (BYTE
*)psc
->GDEF_Table
+ offset
;
1455 TRACE("MarkToBase Attachment Positioning Subtable\n");
1457 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1459 const GPOS_MarkBasePosFormat1
*mbpf1
= (const GPOS_MarkBasePosFormat1
*)GPOS_get_subtable(look
, j
);
1460 if (GET_BE_WORD(mbpf1
->PosFormat
) == 1)
1462 int offset
= GET_BE_WORD(mbpf1
->MarkCoverage
);
1464 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[glyph_index
]);
1465 if (mark_index
!= -1)
1468 int base_glyph
= glyph_index
- write_dir
;
1470 if (glyph_class_table
)
1472 while (OT_get_glyph_class(glyph_class_table
, glyphs
[base_glyph
]) == MarkGlyph
&& base_glyph
> 0 && base_glyph
< glyph_count
)
1473 base_glyph
-= write_dir
;
1476 offset
= GET_BE_WORD(mbpf1
->BaseCoverage
);
1477 base_index
= GSUB_is_glyph_covered((const BYTE
*)mbpf1
+offset
, glyphs
[base_glyph
]);
1478 if (base_index
!= -1)
1480 const GPOS_MarkArray
*ma
;
1481 const GPOS_MarkRecord
*mr
;
1482 const GPOS_BaseArray
*ba
;
1483 const GPOS_BaseRecord
*br
;
1485 int class_count
= GET_BE_WORD(mbpf1
->ClassCount
);
1486 int baserecord_size
;
1489 TRACE("Mark %x(%i) and base %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[base_glyph
], base_index
);
1490 offset
= GET_BE_WORD(mbpf1
->MarkArray
);
1491 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mbpf1
+ offset
);
1492 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
1494 ERR("Mark index exceeded mark count\n");
1497 mr
= &ma
->MarkRecord
[mark_index
];
1498 mark_class
= GET_BE_WORD(mr
->Class
);
1499 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
1500 offset
= GET_BE_WORD(mbpf1
->BaseArray
);
1501 ba
= (const GPOS_BaseArray
*)((const BYTE
*)mbpf1
+ offset
);
1502 baserecord_size
= class_count
* sizeof(WORD
);
1503 br
= (const GPOS_BaseRecord
*)((const BYTE
*)ba
+ sizeof(WORD
) + (baserecord_size
* base_index
));
1504 offset
= GET_BE_WORD(br
->BaseAnchor
[mark_class
]);
1505 GPOS_get_anchor_values((const BYTE
*)ba
+ offset
, &base_pt
, ppem
);
1506 offset
= GET_BE_WORD(mr
->MarkAnchor
);
1507 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
1508 TRACE("Offset on base is %i,%i design units\n",base_pt
.x
,base_pt
.y
);
1509 TRACE("Offset on mark is %i,%i design units\n",mark_pt
.x
, mark_pt
.y
);
1510 pt
->x
+= base_pt
.x
- mark_pt
.x
;
1511 pt
->y
+= base_pt
.y
- mark_pt
.y
;
1512 TRACE("Resulting cumulative offset is %i,%i design units\n",pt
->x
,pt
->y
);
1518 FIXME("Unhandled Mark To Base Format %i\n",GET_BE_WORD(mbpf1
->PosFormat
));
1523 static VOID
GPOS_apply_MarkToLigature(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, INT glyph_index
,
1524 INT glyph_count
, INT ppem
, LPPOINT pt
)
1527 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1529 TRACE("MarkToLigature Attachment Positioning Subtable\n");
1531 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1533 const GPOS_MarkLigPosFormat1
*mlpf1
= (const GPOS_MarkLigPosFormat1
*)GPOS_get_subtable(look
, j
);
1534 if (GET_BE_WORD(mlpf1
->PosFormat
) == 1)
1536 int offset
= GET_BE_WORD(mlpf1
->MarkCoverage
);
1538 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mlpf1
+offset
, glyphs
[glyph_index
]);
1539 if (mark_index
!= -1)
1542 offset
= GET_BE_WORD(mlpf1
->LigatureCoverage
);
1543 ligature_index
= GSUB_is_glyph_covered((const BYTE
*)mlpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
1544 if (ligature_index
!= -1)
1546 const GPOS_MarkArray
*ma
;
1547 const GPOS_MarkRecord
*mr
;
1549 const GPOS_LigatureArray
*la
;
1550 const GPOS_LigatureAttach
*lt
;
1552 int class_count
= GET_BE_WORD(mlpf1
->ClassCount
);
1553 int component_count
;
1559 TRACE("Mark %x(%i) and ligature %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], ligature_index
);
1560 offset
= GET_BE_WORD(mlpf1
->MarkArray
);
1561 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mlpf1
+ offset
);
1562 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
1564 ERR("Mark index exceeded mark count\n");
1567 mr
= &ma
->MarkRecord
[mark_index
];
1568 mark_class
= GET_BE_WORD(mr
->Class
);
1569 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
1570 offset
= GET_BE_WORD(mlpf1
->LigatureArray
);
1571 la
= (const GPOS_LigatureArray
*)((const BYTE
*)mlpf1
+ offset
);
1572 if (ligature_index
> GET_BE_WORD(la
->LigatureCount
))
1574 ERR("Ligature index exceeded ligature count\n");
1577 offset
= GET_BE_WORD(la
->LigatureAttach
[ligature_index
]);
1578 lt
= (const GPOS_LigatureAttach
*)((const BYTE
*)la
+ offset
);
1580 component_count
= GET_BE_WORD(lt
->ComponentCount
);
1581 component_size
= class_count
* sizeof(WORD
);
1583 for (i
= 0; i
< component_count
&& !offset
; i
++)
1586 const GPOS_ComponentRecord
*cr
= (const GPOS_ComponentRecord
*)((const BYTE
*)lt
->ComponentRecord
+ (component_size
* i
));
1587 for (k
= 0; k
< class_count
&& !offset
; k
++)
1588 offset
= GET_BE_WORD(cr
->LigatureAnchor
[k
]);
1589 cr
= (const GPOS_ComponentRecord
*)((const BYTE
*)cr
+ component_size
);
1593 ERR("Failed to find avalible ligature connection point\n");
1597 GPOS_get_anchor_values((const BYTE
*)lt
+ offset
, &ligature_pt
, ppem
);
1598 offset
= GET_BE_WORD(mr
->MarkAnchor
);
1599 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
1600 TRACE("Offset on ligature is %i,%i design units\n",ligature_pt
.x
,ligature_pt
.y
);
1601 TRACE("Offset on mark is %i,%i design units\n",mark_pt
.x
, mark_pt
.y
);
1602 pt
->x
+= ligature_pt
.x
- mark_pt
.x
;
1603 pt
->y
+= ligature_pt
.y
- mark_pt
.y
;
1604 TRACE("Resulting cumulative offset is %i,%i design units\n",pt
->x
,pt
->y
);
1609 FIXME("Unhandled Mark To Ligature Format %i\n",GET_BE_WORD(mlpf1
->PosFormat
));
1613 static BOOL
GPOS_apply_MarkToMark(const OT_LookupTable
*look
, const SCRIPT_ANALYSIS
*analysis
, const WORD
*glyphs
, INT glyph_index
,
1614 INT glyph_count
, INT ppem
, LPPOINT pt
)
1618 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1620 TRACE("MarkToMark Attachment Positioning Subtable\n");
1622 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1624 const GPOS_MarkMarkPosFormat1
*mmpf1
= (const GPOS_MarkMarkPosFormat1
*)GPOS_get_subtable(look
, j
);
1625 if (GET_BE_WORD(mmpf1
->PosFormat
) == 1)
1627 int offset
= GET_BE_WORD(mmpf1
->Mark1Coverage
);
1629 mark_index
= GSUB_is_glyph_covered((const BYTE
*)mmpf1
+offset
, glyphs
[glyph_index
]);
1630 if (mark_index
!= -1)
1633 offset
= GET_BE_WORD(mmpf1
->Mark2Coverage
);
1634 mark2_index
= GSUB_is_glyph_covered((const BYTE
*)mmpf1
+offset
, glyphs
[glyph_index
- write_dir
]);
1635 if (mark2_index
!= -1)
1637 const GPOS_MarkArray
*ma
;
1638 const GPOS_MarkRecord
*mr
;
1639 const GPOS_Mark2Array
*m2a
;
1640 const GPOS_Mark2Record
*m2r
;
1642 int class_count
= GET_BE_WORD(mmpf1
->ClassCount
);
1643 int mark2record_size
;
1646 TRACE("Mark %x(%i) and Mark2 %x(%i)\n",glyphs
[glyph_index
], mark_index
, glyphs
[glyph_index
- write_dir
], mark2_index
);
1647 offset
= GET_BE_WORD(mmpf1
->Mark1Array
);
1648 ma
= (const GPOS_MarkArray
*)((const BYTE
*)mmpf1
+ offset
);
1649 if (mark_index
> GET_BE_WORD(ma
->MarkCount
))
1651 ERR("Mark index exceeded mark count\n");
1654 mr
= &ma
->MarkRecord
[mark_index
];
1655 mark_class
= GET_BE_WORD(mr
->Class
);
1656 TRACE("Mark Class %i total classes %i\n",mark_class
,class_count
);
1657 offset
= GET_BE_WORD(mmpf1
->Mark2Array
);
1658 m2a
= (const GPOS_Mark2Array
*)((const BYTE
*)mmpf1
+ offset
);
1659 mark2record_size
= class_count
* sizeof(WORD
);
1660 m2r
= (const GPOS_Mark2Record
*)((const BYTE
*)m2a
+ sizeof(WORD
) + (mark2record_size
* mark2_index
));
1661 offset
= GET_BE_WORD(m2r
->Mark2Anchor
[mark_class
]);
1662 GPOS_get_anchor_values((const BYTE
*)m2a
+ offset
, &mark2_pt
, ppem
);
1663 offset
= GET_BE_WORD(mr
->MarkAnchor
);
1664 GPOS_get_anchor_values((const BYTE
*)ma
+ offset
, &mark_pt
, ppem
);
1665 TRACE("Offset on mark2 is %i,%i design units\n",mark2_pt
.x
,mark2_pt
.y
);
1666 TRACE("Offset on mark is %i,%i design units\n",mark_pt
.x
, mark_pt
.y
);
1667 pt
->x
+= mark2_pt
.x
- mark_pt
.x
;
1668 pt
->y
+= mark2_pt
.y
- mark_pt
.y
;
1669 TRACE("Resulting cumulative offset is %i,%i design units\n",pt
->x
,pt
->y
);
1675 FIXME("Unhandled Mark To Mark Format %i\n",GET_BE_WORD(mmpf1
->PosFormat
));
1680 static INT
GPOS_apply_ChainContextPos(ScriptCache
*psc
, LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, const SCRIPT_ANALYSIS
*analysis
, INT
* piAdvance
,
1681 const OT_LookupList
*lookup
, const OT_LookupTable
*look
, const WORD
*glyphs
, INT glyph_index
,
1682 INT glyph_count
, INT ppem
, GOFFSET
*pGoffset
)
1685 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1687 TRACE("Chaining Contextual Positioning Subtable\n");
1689 for (j
= 0; j
< GET_BE_WORD(look
->SubTableCount
); j
++)
1692 const GPOS_ChainContextPosFormat3_1
*ccpf3
= (GPOS_ChainContextPosFormat3_1
*)GPOS_get_subtable(look
, j
);
1693 int dirLookahead
= write_dir
;
1694 int dirBacktrack
= -1 * write_dir
;
1696 if (GET_BE_WORD(ccpf3
->PosFormat
) == 1)
1700 FIXME(" TODO: subtype 1 (Simple Chaining Context Glyph Positioning)\n");
1703 else if (GET_BE_WORD(ccpf3
->PosFormat
) == 2)
1707 FIXME(" TODO: subtype 2 (Class-based Chaining Context Glyph Positioning)\n");
1710 else if (GET_BE_WORD(ccpf3
->PosFormat
) == 3)
1714 const GPOS_ChainContextPosFormat3_2
*ccpf3_2
;
1715 const GPOS_ChainContextPosFormat3_3
*ccpf3_3
;
1716 const GPOS_ChainContextPosFormat3_4
*ccpf3_4
;
1718 TRACE(" subtype 3 (Coverage-based Chaining Context Glyph Positioning)\n");
1720 for (k
= 0; k
< GET_BE_WORD(ccpf3
->BacktrackGlyphCount
); k
++)
1722 offset
= GET_BE_WORD(ccpf3
->Coverage
[k
]);
1723 if (GSUB_is_glyph_covered((const BYTE
*)ccpf3
+offset
, glyphs
[glyph_index
+ (dirBacktrack
* (k
+1))]) == -1)
1726 if (k
!= GET_BE_WORD(ccpf3
->BacktrackGlyphCount
))
1728 TRACE("Matched Backtrack\n");
1730 ccpf3_2
= (const GPOS_ChainContextPosFormat3_2
*)((BYTE
*)ccpf3
+
1731 FIELD_OFFSET(GPOS_ChainContextPosFormat3_1
, Coverage
[GET_BE_WORD(ccpf3
->BacktrackGlyphCount
)]));
1733 indexGlyphs
= GET_BE_WORD(ccpf3_2
->InputGlyphCount
);
1734 for (k
= 0; k
< indexGlyphs
; k
++)
1736 offset
= GET_BE_WORD(ccpf3_2
->Coverage
[k
]);
1737 if (GSUB_is_glyph_covered((const BYTE
*)ccpf3
+offset
, glyphs
[glyph_index
+ (write_dir
* k
)]) == -1)
1740 if (k
!= indexGlyphs
)
1742 TRACE("Matched IndexGlyphs\n");
1744 ccpf3_3
= (const GPOS_ChainContextPosFormat3_3
*)((BYTE
*)ccpf3_2
+
1745 FIELD_OFFSET(GPOS_ChainContextPosFormat3_2
, Coverage
[GET_BE_WORD(ccpf3_2
->InputGlyphCount
)]));
1747 for (k
= 0; k
< GET_BE_WORD(ccpf3_3
->LookaheadGlyphCount
); k
++)
1749 offset
= GET_BE_WORD(ccpf3_3
->Coverage
[k
]);
1750 if (GSUB_is_glyph_covered((const BYTE
*)ccpf3
+offset
, glyphs
[glyph_index
+ (dirLookahead
* (indexGlyphs
+ k
))]) == -1)
1753 if (k
!= GET_BE_WORD(ccpf3_3
->LookaheadGlyphCount
))
1755 TRACE("Matched LookAhead\n");
1757 ccpf3_4
= (const GPOS_ChainContextPosFormat3_4
*)((BYTE
*)ccpf3_3
+
1758 FIELD_OFFSET(GPOS_ChainContextPosFormat3_3
, Coverage
[GET_BE_WORD(ccpf3_3
->LookaheadGlyphCount
)]));
1760 if (GET_BE_WORD(ccpf3_4
->PosCount
))
1762 for (k
= 0; k
< GET_BE_WORD(ccpf3_4
->PosCount
); k
++)
1764 int lookupIndex
= GET_BE_WORD(ccpf3_4
->PosLookupRecord
[k
].LookupListIndex
);
1765 int SequenceIndex
= GET_BE_WORD(ccpf3_4
->PosLookupRecord
[k
].SequenceIndex
) * write_dir
;
1767 TRACE("Position: %i -> %i %i\n",k
, SequenceIndex
, lookupIndex
);
1768 GPOS_apply_lookup(psc
, lpotm
, lplogfont
, analysis
, piAdvance
, lookup
, lookupIndex
, glyphs
, glyph_index
+ SequenceIndex
, glyph_count
, pGoffset
);
1770 return glyph_index
+ indexGlyphs
+ GET_BE_WORD(ccpf3_3
->LookaheadGlyphCount
);
1772 else return glyph_index
+ 1;
1775 FIXME("Unhandled Chaining Contextual Positioning Format %i\n",GET_BE_WORD(ccpf3
->PosFormat
));
1777 return glyph_index
+ 1;
1780 static INT
GPOS_apply_lookup(ScriptCache
*psc
, LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, const SCRIPT_ANALYSIS
*analysis
, INT
* piAdvance
, const OT_LookupList
* lookup
, INT lookup_index
, const WORD
*glyphs
, INT glyph_index
, INT glyph_count
, GOFFSET
*pGoffset
)
1783 const OT_LookupTable
*look
;
1784 int ppem
= lpotm
->otmTextMetrics
.tmAscent
+ lpotm
->otmTextMetrics
.tmDescent
- lpotm
->otmTextMetrics
.tmInternalLeading
;
1787 offset
= GET_BE_WORD(lookup
->Lookup
[lookup_index
]);
1788 look
= (const OT_LookupTable
*)((const BYTE
*)lookup
+ offset
);
1789 type
= GET_BE_WORD(look
->LookupType
);
1790 TRACE("type %i, flag %x, subtables %i\n",type
,GET_BE_WORD(look
->LookupFlag
),GET_BE_WORD(look
->SubTableCount
));
1793 if (GET_BE_WORD(look
->SubTableCount
))
1795 const GPOS_ExtensionPosFormat1
*ext
= (const GPOS_ExtensionPosFormat1
*)((const BYTE
*)look
+ GET_BE_WORD(look
->SubTable
[0]));
1796 if (GET_BE_WORD(ext
->PosFormat
) == 1)
1798 type
= GET_BE_WORD(ext
->ExtensionLookupType
);
1799 TRACE("extension type %i\n",type
);
1803 FIXME("Unhandled Extension Positioning Format %i\n",GET_BE_WORD(ext
->PosFormat
));
1808 WARN("lookup type is Extension Positioning but no extension subtable exists\n");
1816 POINT adjust
= {0,0};
1817 POINT advance
= {0,0};
1818 GPOS_apply_SingleAdjustment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &adjust
, &advance
);
1819 if (adjust
.x
|| adjust
.y
)
1821 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
.x
, adjust
.y
, &devX
, &devY
);
1822 pGoffset
[glyph_index
].du
+= round(devX
);
1823 pGoffset
[glyph_index
].dv
+= round(devY
);
1825 if (advance
.x
|| advance
.y
)
1827 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
.x
, advance
.y
, &devX
, &devY
);
1828 piAdvance
[glyph_index
] += round(devX
);
1830 FIXME("Unhandled adjustment to Y advancement\n");
1836 POINT advance
[2]= {{0,0},{0,0}};
1837 POINT adjust
[2]= {{0,0},{0,0}};
1840 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1841 int offset_sign
= (analysis
->fRTL
&& analysis
->fLogicalOrder
) ? -1 : 1;
1843 index
= GPOS_apply_PairAdjustment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, adjust
, advance
);
1844 if (adjust
[0].x
|| adjust
[0].y
)
1846 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
[0].x
, adjust
[0].y
, &devX
, &devY
);
1847 pGoffset
[glyph_index
].du
+= round(devX
) * offset_sign
;
1848 pGoffset
[glyph_index
].dv
+= round(devY
);
1850 if (advance
[0].x
|| advance
[0].y
)
1852 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
[0].x
, advance
[0].y
, &devX
, &devY
);
1853 piAdvance
[glyph_index
] += round(devX
);
1855 if (adjust
[1].x
|| adjust
[1].y
)
1857 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, adjust
[1].x
, adjust
[1].y
, &devX
, &devY
);
1858 pGoffset
[glyph_index
+ write_dir
].du
+= round(devX
) * offset_sign
;
1859 pGoffset
[glyph_index
+ write_dir
].dv
+= round(devY
);
1861 if (advance
[1].x
|| advance
[1].y
)
1863 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, advance
[1].x
, advance
[1].y
, &devX
, &devY
);
1864 piAdvance
[glyph_index
+ write_dir
] += round(devX
);
1872 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1874 GPOS_apply_CursiveAttachment(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
1875 if (desU
.x
|| desU
.y
)
1877 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
1878 /* Windows does not appear to apply X offsets here */
1879 pGoffset
[glyph_index
].dv
= round(devY
) + pGoffset
[glyph_index
+write_dir
].dv
;
1887 int base_index
= GPOS_apply_MarkToBase(psc
, look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
1888 if (base_index
!= -1)
1890 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
1891 if (!analysis
->fRTL
) pGoffset
[glyph_index
].du
= round(devX
) - piAdvance
[base_index
];
1894 if (analysis
->fLogicalOrder
) devX
*= -1;
1895 pGoffset
[glyph_index
].du
= round(devX
);
1897 pGoffset
[glyph_index
].dv
= round(devY
);
1905 GPOS_apply_MarkToLigature(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
);
1906 if (desU
.x
|| desU
.y
)
1908 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
1909 pGoffset
[glyph_index
].du
= (round(devX
) - piAdvance
[glyph_index
-1]);
1910 pGoffset
[glyph_index
].dv
= round(devY
);
1918 int write_dir
= (analysis
->fRTL
&& !analysis
->fLogicalOrder
) ? -1 : 1;
1919 if (GPOS_apply_MarkToMark(look
, analysis
, glyphs
, glyph_index
, glyph_count
, ppem
, &desU
))
1921 GPOS_convert_design_units_to_device(lpotm
, lplogfont
, desU
.x
, desU
.y
, &devX
, &devY
);
1922 if (analysis
->fRTL
&& analysis
->fLogicalOrder
) devX
*= -1;
1923 pGoffset
[glyph_index
].du
= round(devX
) + pGoffset
[glyph_index
- write_dir
].du
;
1924 pGoffset
[glyph_index
].dv
= round(devY
) + pGoffset
[glyph_index
- write_dir
].dv
;
1930 return GPOS_apply_ChainContextPos(psc
, lpotm
, lplogfont
, analysis
, piAdvance
, lookup
, look
, glyphs
, glyph_index
, glyph_count
, ppem
, pGoffset
);
1933 FIXME("We do not handle SubType %i\n",type
);
1935 return glyph_index
+1;
1938 INT
OpenType_apply_GPOS_lookup(ScriptCache
*psc
, LPOUTLINETEXTMETRICW lpotm
, LPLOGFONTW lplogfont
, const SCRIPT_ANALYSIS
*analysis
, INT
* piAdvance
, INT lookup_index
, const WORD
*glyphs
, INT glyph_index
, INT glyph_count
, GOFFSET
*pGoffset
)
1940 const GPOS_Header
*header
= (const GPOS_Header
*)psc
->GPOS_Table
;
1941 const OT_LookupList
*lookup
= (const OT_LookupList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->LookupList
));
1943 return GPOS_apply_lookup(psc
, lpotm
, lplogfont
, analysis
, piAdvance
, lookup
, lookup_index
, glyphs
, glyph_index
, glyph_count
, pGoffset
);
1946 static void GSUB_initialize_script_cache(ScriptCache
*psc
)
1950 if (psc
->GSUB_Table
)
1952 const OT_ScriptList
*script
;
1953 const GSUB_Header
* header
= (const GSUB_Header
*)psc
->GSUB_Table
;
1954 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
1955 psc
->script_count
= GET_BE_WORD(script
->ScriptCount
);
1956 TRACE("initializing %i scripts in this font\n",psc
->script_count
);
1957 if (psc
->script_count
)
1959 psc
->scripts
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(LoadedScript
) * psc
->script_count
);
1960 for (i
= 0; i
< psc
->script_count
; i
++)
1962 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
1963 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]);
1964 psc
->scripts
[i
].gsub_table
= ((const BYTE
*)script
+ offset
);
1970 static void GPOS_expand_script_cache(ScriptCache
*psc
)
1973 const OT_ScriptList
*script
;
1974 const GPOS_Header
* header
= (const GPOS_Header
*)psc
->GPOS_Table
;
1979 script
= (const OT_ScriptList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->ScriptList
));
1980 count
= GET_BE_WORD(script
->ScriptCount
);
1985 if (!psc
->script_count
)
1987 psc
->script_count
= count
;
1988 TRACE("initializing %i scripts in this font\n",psc
->script_count
);
1989 if (psc
->script_count
)
1991 psc
->scripts
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(LoadedScript
) * psc
->script_count
);
1992 for (i
= 0; i
< psc
->script_count
; i
++)
1994 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
1995 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]);
1996 psc
->scripts
[i
].gpos_table
= ((const BYTE
*)script
+ offset
);
2002 for (i
= 0; i
< count
; i
++)
2005 int offset
= GET_BE_WORD(script
->ScriptRecord
[i
].Script
);
2006 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]);
2007 for (j
= 0; j
< psc
->script_count
; j
++)
2009 if (psc
->scripts
[j
].tag
== tag
)
2011 psc
->scripts
[j
].gpos_table
= ((const BYTE
*)script
+ offset
);
2015 if (j
== psc
->script_count
)
2017 psc
->script_count
++;
2018 psc
->scripts
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,psc
->scripts
, sizeof(LoadedScript
) * psc
->script_count
);
2019 psc
->scripts
[j
].tag
= tag
;
2020 psc
->scripts
[j
].gpos_table
= ((const BYTE
*)script
+ offset
);
2026 static void _initialize_script_cache(ScriptCache
*psc
)
2028 if (!psc
->scripts_initialized
)
2030 GSUB_initialize_script_cache(psc
);
2031 GPOS_expand_script_cache(psc
);
2032 psc
->scripts_initialized
= TRUE
;
2036 HRESULT
OpenType_GetFontScriptTags(ScriptCache
*psc
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pScriptTags
, int *pcTags
)
2041 _initialize_script_cache(psc
);
2043 *pcTags
= psc
->script_count
;
2045 if (!searchingFor
&& cMaxTags
< *pcTags
)
2047 else if (searchingFor
)
2048 rc
= USP_E_SCRIPT_NOT_IN_FONT
;
2050 for (i
= 0; i
< psc
->script_count
; i
++)
2053 pScriptTags
[i
] = psc
->scripts
[i
].tag
;
2057 if (searchingFor
== psc
->scripts
[i
].tag
)
2059 pScriptTags
[0] = psc
->scripts
[i
].tag
;
2069 static void GSUB_initialize_language_cache(LoadedScript
*script
)
2073 if (script
->gsub_table
)
2076 const OT_Script
* table
= script
->gsub_table
;
2077 script
->language_count
= GET_BE_WORD(table
->LangSysCount
);
2078 offset
= GET_BE_WORD(table
->DefaultLangSys
);
2081 script
->default_language
.tag
= MS_MAKE_TAG('d','f','l','t');
2082 script
->default_language
.gsub_table
= (const BYTE
*)table
+ offset
;
2085 if (script
->language_count
)
2087 TRACE("Deflang %p, LangCount %i\n",script
->default_language
.gsub_table
, script
->language_count
);
2089 script
->languages
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LoadedLanguage
) * script
->language_count
);
2091 for (i
= 0; i
< script
->language_count
; i
++)
2093 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2094 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]);
2095 script
->languages
[i
].gsub_table
= ((const BYTE
*)table
+ offset
);
2101 static void GPOS_expand_language_cache(LoadedScript
*script
)
2104 const OT_Script
* table
= script
->gpos_table
;
2110 offset
= GET_BE_WORD(table
->DefaultLangSys
);
2112 script
->default_language
.gpos_table
= (const BYTE
*)table
+ offset
;
2114 count
= GET_BE_WORD(table
->LangSysCount
);
2116 TRACE("Deflang %p, LangCount %i\n",script
->default_language
.gpos_table
, count
);
2121 if (!script
->language_count
)
2124 script
->language_count
= count
;
2126 script
->languages
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LoadedLanguage
) * script
->language_count
);
2128 for (i
= 0; i
< script
->language_count
; i
++)
2130 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2131 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]);
2132 script
->languages
[i
].gpos_table
= ((const BYTE
*)table
+ offset
);
2138 for (i
= 0; i
< count
; i
++)
2140 int offset
= GET_BE_WORD(table
->LangSysRecord
[i
].LangSys
);
2141 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]);
2143 for (j
= 0; j
< script
->language_count
; j
++)
2145 if (script
->languages
[j
].tag
== tag
)
2147 script
->languages
[j
].gpos_table
= ((const BYTE
*)table
+ offset
);
2151 if (j
== script
->language_count
)
2153 script
->language_count
++;
2154 script
->languages
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,script
->languages
, sizeof(LoadedLanguage
) * script
->language_count
);
2155 script
->languages
[j
].tag
= tag
;
2156 script
->languages
[j
].gpos_table
= ((const BYTE
*)table
+ offset
);
2162 static void _initialize_language_cache(LoadedScript
*script
)
2164 if (!script
->languages_initialized
)
2166 GSUB_initialize_language_cache(script
);
2167 GPOS_expand_language_cache(script
);
2168 script
->languages_initialized
= TRUE
;
2172 HRESULT
OpenType_GetFontLanguageTags(ScriptCache
*psc
, OPENTYPE_TAG script_tag
, OPENTYPE_TAG searchingFor
, int cMaxTags
, OPENTYPE_TAG
*pLanguageTags
, int *pcTags
)
2176 LoadedScript
*script
= NULL
;
2178 _initialize_script_cache(psc
);
2180 for (i
= 0; i
< psc
->script_count
; i
++)
2182 if (psc
->scripts
[i
].tag
== script_tag
)
2184 script
= &psc
->scripts
[i
];
2190 return E_INVALIDARG
;
2192 _initialize_language_cache(script
);
2194 if (!searchingFor
&& cMaxTags
< script
->language_count
)
2196 else if (searchingFor
)
2199 *pcTags
= script
->language_count
;
2201 for (i
= 0; i
< script
->language_count
; i
++)
2204 pLanguageTags
[i
] = script
->languages
[i
].tag
;
2208 if (searchingFor
== script
->languages
[i
].tag
)
2210 pLanguageTags
[0] = script
->languages
[i
].tag
;
2218 if (script
->default_language
.gsub_table
)
2221 pLanguageTags
[i
] = script
->default_language
.tag
;
2223 if (searchingFor
&& FAILED(rc
))
2225 pLanguageTags
[0] = script
->default_language
.tag
;
2228 *pcTags
= (*pcTags
) + 1;
2235 static void GSUB_initialize_feature_cache(LPCVOID table
, LoadedLanguage
*language
)
2239 if (language
->gsub_table
)
2241 const OT_LangSys
*lang
= language
->gsub_table
;
2242 const GSUB_Header
*header
= (const GSUB_Header
*)table
;
2243 const OT_FeatureList
*feature_list
;
2245 language
->feature_count
= GET_BE_WORD(lang
->FeatureCount
);
2246 TRACE("%i features\n",language
->feature_count
);
2248 if (language
->feature_count
)
2250 language
->features
= HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature
)*language
->feature_count
);
2252 feature_list
= (const OT_FeatureList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->FeatureList
));
2254 for (i
= 0; i
< language
->feature_count
; i
++)
2256 const OT_Feature
*feature
;
2258 int index
= GET_BE_WORD(lang
->FeatureIndex
[i
]);
2260 language
->features
[i
].tag
= MS_MAKE_TAG(feature_list
->FeatureRecord
[index
].FeatureTag
[0], feature_list
->FeatureRecord
[index
].FeatureTag
[1], feature_list
->FeatureRecord
[index
].FeatureTag
[2], feature_list
->FeatureRecord
[index
].FeatureTag
[3]);
2261 language
->features
[i
].feature
= ((const BYTE
*)feature_list
+ GET_BE_WORD(feature_list
->FeatureRecord
[index
].Feature
));
2262 feature
= (const OT_Feature
*)language
->features
[i
].feature
;
2263 language
->features
[i
].lookup_count
= GET_BE_WORD(feature
->LookupCount
);
2264 language
->features
[i
].lookups
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * language
->features
[i
].lookup_count
);
2265 for (j
= 0; j
< language
->features
[i
].lookup_count
; j
++)
2266 language
->features
[i
].lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
2267 language
->features
[i
].tableType
= FEATURE_GSUB_TABLE
;
2273 static void GPOS_expand_feature_cache(LPCVOID table
, LoadedLanguage
*language
)
2276 const OT_LangSys
*lang
= language
->gpos_table
;
2277 const GPOS_Header
*header
= (const GPOS_Header
*)table
;
2278 const OT_FeatureList
*feature_list
;
2283 count
= GET_BE_WORD(lang
->FeatureCount
);
2284 feature_list
= (const OT_FeatureList
*)((const BYTE
*)header
+ GET_BE_WORD(header
->FeatureList
));
2286 TRACE("%i features\n",count
);
2291 if (!language
->feature_count
)
2293 language
->feature_count
= count
;
2295 if (language
->feature_count
)
2297 language
->features
= HeapAlloc(GetProcessHeap(),0,sizeof(LoadedFeature
)*language
->feature_count
);
2299 for (i
= 0; i
< language
->feature_count
; i
++)
2301 const OT_Feature
*feature
;
2303 int index
= GET_BE_WORD(lang
->FeatureIndex
[i
]);
2305 language
->features
[i
].tag
= MS_MAKE_TAG(feature_list
->FeatureRecord
[index
].FeatureTag
[0], feature_list
->FeatureRecord
[index
].FeatureTag
[1], feature_list
->FeatureRecord
[index
].FeatureTag
[2], feature_list
->FeatureRecord
[index
].FeatureTag
[3]);
2306 language
->features
[i
].feature
= ((const BYTE
*)feature_list
+ GET_BE_WORD(feature_list
->FeatureRecord
[index
].Feature
));
2307 feature
= (const OT_Feature
*)language
->features
[i
].feature
;
2308 language
->features
[i
].lookup_count
= GET_BE_WORD(feature
->LookupCount
);
2309 language
->features
[i
].lookups
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * language
->features
[i
].lookup_count
);
2310 for (j
= 0; j
< language
->features
[i
].lookup_count
; j
++)
2311 language
->features
[i
].lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
2312 language
->features
[i
].tableType
= FEATURE_GPOS_TABLE
;
2318 language
->features
= HeapReAlloc(GetProcessHeap(),0,language
->features
, sizeof(LoadedFeature
)*(language
->feature_count
+ count
));
2320 for (i
= 0; i
< count
; i
++)
2322 const OT_Feature
*feature
;
2324 int index
= GET_BE_WORD(lang
->FeatureIndex
[i
]);
2325 int idx
= language
->feature_count
+ i
;
2327 language
->features
[idx
].tag
= MS_MAKE_TAG(feature_list
->FeatureRecord
[index
].FeatureTag
[0], feature_list
->FeatureRecord
[index
].FeatureTag
[1], feature_list
->FeatureRecord
[index
].FeatureTag
[2], feature_list
->FeatureRecord
[index
].FeatureTag
[3]);
2328 language
->features
[idx
].feature
= ((const BYTE
*)feature_list
+ GET_BE_WORD(feature_list
->FeatureRecord
[index
].Feature
));
2329 feature
= (const OT_Feature
*)language
->features
[idx
].feature
;
2330 language
->features
[idx
].lookup_count
= GET_BE_WORD(feature
->LookupCount
);
2331 language
->features
[idx
].lookups
= HeapAlloc(GetProcessHeap(),0,sizeof(WORD
) * language
->features
[idx
].lookup_count
);
2332 for (j
= 0; j
< language
->features
[idx
].lookup_count
; j
++)
2333 language
->features
[idx
].lookups
[j
] = GET_BE_WORD(feature
->LookupListIndex
[j
]);
2334 language
->features
[idx
].tableType
= FEATURE_GPOS_TABLE
;
2336 language
->feature_count
+= count
;
2340 static void _initialize_feature_cache(ScriptCache
*psc
, LoadedLanguage
*language
)
2342 if (!language
->features_initialized
)
2344 GSUB_initialize_feature_cache(psc
->GSUB_Table
, language
);
2345 GPOS_expand_feature_cache(psc
->GPOS_Table
, language
);
2346 language
->features_initialized
= TRUE
;
2350 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
)
2354 LoadedScript
*script
= NULL
;
2355 LoadedLanguage
*language
= NULL
;
2357 _initialize_script_cache(psc
);
2359 for (i
= 0; i
< psc
->script_count
; i
++)
2361 if (psc
->scripts
[i
].tag
== script_tag
)
2363 script
= &psc
->scripts
[i
];
2374 return E_INVALIDARG
;
2377 _initialize_language_cache(script
);
2379 if ((script
->default_language
.gsub_table
|| script
->default_language
.gpos_table
) && script
->default_language
.tag
== language_tag
)
2380 language
= &script
->default_language
;
2383 for (i
= 0; i
< script
->language_count
; i
++)
2385 if (script
->languages
[i
].tag
== language_tag
)
2387 language
= &script
->languages
[i
];
2399 _initialize_feature_cache(psc
, language
);
2404 for (i
= 0; i
< language
->feature_count
; i
++)
2405 if (language
->features
[i
].tableType
== tableType
)
2406 *pcTags
= (*pcTags
)+1;
2409 *pcTags
= language
->feature_count
;
2411 if (!searchingFor
&& cMaxTags
< *pcTags
)
2413 else if (searchingFor
)
2416 for (i
= 0; i
< language
->feature_count
; i
++)
2420 if (!tableType
|| language
->features
[i
].tableType
== tableType
)
2421 pFeatureTags
[i
] = language
->features
[i
].tag
;
2426 if ((searchingFor
== language
->features
[i
].tag
) &&
2427 (!tableType
|| language
->features
[i
].tableType
== tableType
))
2429 pFeatureTags
[0] = language
->features
[i
].tag
;
2432 *feature
= &language
->features
[i
];