-
Notifications
You must be signed in to change notification settings - Fork 7
/
ot_layout.go
2046 lines (1848 loc) · 70.6 KB
/
ot_layout.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package harfbuzz
import (
"github.com/benoitkugler/textlayout/fonts"
tt "github.com/benoitkugler/textlayout/fonts/truetype"
)
// ported from src/hb-ot-layout.cc, hb-ot-layout.hh
// Copyright © 1998-2004 David Turner and Werner Lemberg
// Copyright © 2006 2007,2008,2009 Red Hat, Inc. 2012,2013 Google, Inc. Behdad Esfahbod
// /**
// * SECTION:hb-ot-layout
// * @title: hb-ot-layout
// * @short_description: OpenType Layout
// * @include: hb-ot.h
// *
// * Functions for querying OpenType Layout features in the font face.
// **/
const maxNestingLevel = 6
func (c *otApplyContext) applyString(proxy otProxyMeta, accel *otLayoutLookupAccelerator) {
buffer := c.buffer
lookup := accel.lookup
if len(buffer.Info) == 0 || c.lookupMask == 0 {
return
}
c.setLookupProps(lookup.Props())
if !lookup.isReverse() {
// in/out forward substitution/positioning
if !proxy.inplace {
buffer.clearOutput()
}
buffer.idx = 0
c.applyForward(accel)
if !proxy.inplace {
buffer.swapBuffers()
}
} else {
/* in-place backward substitution/positioning */
// assert (!buffer->have_output);
buffer.idx = len(buffer.Info) - 1
c.applyBackward(accel)
}
}
func (c *otApplyContext) applyForward(accel *otLayoutLookupAccelerator) bool {
ret := false
buffer := c.buffer
for buffer.idx < len(buffer.Info) {
applied := false
if accel.digest.mayHave(buffer.cur(0).Glyph) &&
(buffer.cur(0).Mask&c.lookupMask) != 0 &&
c.checkGlyphProperty(buffer.cur(0), c.lookupProps) {
applied = accel.apply(c)
}
if applied {
ret = true
} else {
buffer.nextGlyph()
}
}
return ret
}
func (c *otApplyContext) applyBackward(accel *otLayoutLookupAccelerator) bool {
ret := false
buffer := c.buffer
for do := true; do; do = buffer.idx >= 0 {
if accel.digest.mayHave(buffer.cur(0).Glyph) &&
(buffer.cur(0).Mask&c.lookupMask != 0) &&
c.checkGlyphProperty(buffer.cur(0), c.lookupProps) {
applied := accel.apply(c)
ret = ret || applied
}
// the reverse lookup doesn't "advance" cursor (for good reason).
buffer.idx--
}
return ret
}
/*
* kern
*/
// tests whether a face includes any state-machine kerning in the 'kern' table.
//
// Does NOT examine the GPOS table.
func hasMachineKerning(kern tt.TableKernx) bool {
for _, subtable := range kern {
if _, isType1 := subtable.Data.(tt.Kern1); isType1 {
return true
}
}
return false
}
// tests whether a face has any cross-stream kerning (i.e., kerns
// that make adjustments perpendicular to the direction of the text
// flow: Y adjustments in horizontal text or X adjustments in
// vertical text) in the 'kern' table.
//
// Does NOT examine the GPOS table.
func hasCrossKerning(kern tt.TableKernx) bool {
for _, subtable := range kern {
if subtable.IsCrossStream() {
return true
}
}
return false
}
func (sp *otShapePlan) otLayoutKern(font *Font, buffer *Buffer) {
kern := font.otTables.Kern
c := newAatApplyContext(sp, font, buffer)
c.applyKernx(kern)
}
// /*
// * GDEF
// */
// bool
// OT::GDEF::is_blocklisted (hb_blob_t *blob,
// Face *face) const
// {
// #ifdef HB_NO_OT_LAYOUT_BLACKLIST
// return false;
// #endif
// /* The ugly business of blocklisting individual fonts' tables happen here!
// * See this thread for why we finally had to bend in and do this:
// * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
// *
// * In certain versions of Times New Roman Italic and Bold Italic,
// * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark)
// * in GDEF. Many versions of Tahoma have bad GDEF tables that
// * incorrectly classify some spacing marks such as certain IPA
// * symbols as glyph class 3. So do older versions of Microsoft
// * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04.
// *
// * Nuke the GDEF tables of to avoid unwanted width-zeroing.
// *
// * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
// * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
// * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
// */
// switch HB_CODEPOINT_ENCODE3(blob.length,
// face.table.GSUB.table.get_length (),
// face.table.GPOS.table.get_length ())
// {
// /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
// case HB_CODEPOINT_ENCODE3 (442, 2874, 42038):
// /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
// case HB_CODEPOINT_ENCODE3 (430, 2874, 40662):
// /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
// case HB_CODEPOINT_ENCODE3 (442, 2874, 39116):
// /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
// case HB_CODEPOINT_ENCODE3 (430, 2874, 39374):
// /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
// case HB_CODEPOINT_ENCODE3 (490, 3046, 41638):
// /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
// case HB_CODEPOINT_ENCODE3 (478, 3046, 41902):
// /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */
// case HB_CODEPOINT_ENCODE3 (898, 12554, 46470):
// /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */
// case HB_CODEPOINT_ENCODE3 (910, 12566, 47732):
// /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */
// case HB_CODEPOINT_ENCODE3 (928, 23298, 59332):
// /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */
// case HB_CODEPOINT_ENCODE3 (940, 23310, 60732):
// /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
// case HB_CODEPOINT_ENCODE3 (964, 23836, 60072):
// /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
// case HB_CODEPOINT_ENCODE3 (976, 23832, 61456):
// /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */
// case HB_CODEPOINT_ENCODE3 (994, 24474, 60336):
// /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */
// case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740):
// /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
// case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346):
// /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
// case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828):
// /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */
// case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352):
// /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */
// case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834):
// /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */
// case HB_CODEPOINT_ENCODE3 (832, 7324, 47162):
// /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */
// case HB_CODEPOINT_ENCODE3 (844, 7302, 45474):
// /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */
// case HB_CODEPOINT_ENCODE3 (180, 13054, 7254):
// /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */
// case HB_CODEPOINT_ENCODE3 (192, 12638, 7254):
// /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */
// case HB_CODEPOINT_ENCODE3 (192, 12690, 7254):
// /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
// /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
// case HB_CODEPOINT_ENCODE3 (188, 248, 3852):
// /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
// /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
// case HB_CODEPOINT_ENCODE3 (188, 264, 3426):
// /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
// case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818):
// /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
// case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600):
// /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
// case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770):
// /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
// case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862):
// /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
// case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112):
// /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
// case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514):
// /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
// case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938):
// /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
// case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972):
// /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf
// * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
// case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836):
// return true;
// }
// return false;
// }
// /* Public API */
// /**
// * hb_ot_layout_has_glyph_classes:
// * @face: #Face to work upon
// *
// * Tests whether a face has any glyph classes defined in its GDEF table.
// *
// * Return value: %true if data found, %false otherwise
// *
// **/
// hb_bool_t
// hb_ot_layout_has_glyph_classes (Face *face)
// {
// return face.table.GDEF.table.has_glyph_classes ();
// }
// /**
// * hb_ot_layout_get_glyph_class:
// * @face: The #Face to work on
// * @glyph: The #hb_codepoint_t code point to query
// *
// * Fetches the GDEF class of the requested glyph in the specified face.
// *
// * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code
// * point in the GDEF table of the face.
// *
// * Since: 0.9.7
// **/
// hb_ot_layout_glyph_class_t
// hb_ot_layout_get_glyph_class (Face *face,
// hb_codepoint_t glyph)
// {
// return (hb_ot_layout_glyph_class_t) face.table.GDEF.table.get_glyph_class (glyph);
// }
// /**
// * hb_ot_layout_get_glyphs_in_class:
// * @face: The #Face to work on
// * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve
// * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested
// * class.
// *
// * Retrieves the set of all glyphs from the face that belong to the requested
// * glyph class in the face's GDEF table.
// *
// * Since: 0.9.7
// **/
// void
// hb_ot_layout_get_glyphs_in_class (Face *face,
// hb_ot_layout_glyph_class_t klass,
// hb_set_t *glyphs /* OUT */)
// {
// return face.table.GDEF.table.get_glyphs_in_class (klass, glyphs);
// }
// #ifndef HB_NO_LAYOUT_UNUSED
// /**
// * hb_ot_layout_get_attach_points:
// * @face: The #Face to work on
// * @glyph: The #hb_codepoint_t code point to query
// * @start_offset: offset of the first attachment point to retrieve
// * @point_count: (inout) (optional): Input = the maximum number of attachment points to return;
// * Output = the actual number of attachment points returned (may be zero)
// * @point_array: (out) (array length=point_count): The array of attachment points found for the query
// *
// * Fetches a list of all attachment points for the specified glyph in the GDEF
// * table of the face. The list returned will begin at the offset provided.
// *
// * Useful if the client program wishes to cache the list.
// *
// **/
// uint
// hb_ot_layout_get_attach_points (Face *face,
// hb_codepoint_t glyph,
// uint start_offset,
// uint *point_count /* IN/OUT */,
// uint *point_array /* OUT */)
// {
// return face.table.GDEF.table.get_attach_points (glyph,
// start_offset,
// point_count,
// point_array);
// }
// /**
// * hb_ot_layout_get_ligature_carets:
// * @font: The #Font to work on
// * @direction: The #Direction text direction to use
// * @glyph: The #hb_codepoint_t code point to query
// * @start_offset: offset of the first caret position to retrieve
// * @caret_count: (inout) (optional): Input = the maximum number of caret positions to return;
// * Output = the actual number of caret positions returned (may be zero)
// * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query
// *
// * Fetches a list of the caret positions defined for a ligature glyph in the GDEF
// * table of the font. The list returned will begin at the offset provided.
// *
// **/
// uint
// hb_ot_layout_get_ligature_carets (Font *font,
// Direction direction,
// hb_codepoint_t glyph,
// uint start_offset,
// uint *caret_count /* IN/OUT */,
// Position *caret_array /* OUT */)
// {
// return font.face.table.GDEF.table.get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
// }
// #endif
// /*
// * GSUB/GPOS
// */
// bool
// OT::GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED,
// Face *face) const
// {
// #ifdef HB_NO_OT_LAYOUT_BLACKLIST
// return false;
// #endif
// return false;
// }
// bool
// OT::GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED,
// Face *face HB_UNUSED) const
// {
// #ifdef HB_NO_OT_LAYOUT_BLACKLIST
// return false;
// #endif
// return false;
// }
// static const OT::GSUBGPOS&
// get_gsubgpos_table (Face *face,
// hb_tag_t table_tag)
// {
// switch (table_tag) {
// case HB_OT_TAG_GSUB: return *face.table.GSUB.table;
// case HB_OT_TAG_GPOS: return *face.table.GPOS.table;
// default: return Null (OT::GSUBGPOS);
// }
// }
// /**
// * hb_ot_layout_table_get_script_tags:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @start_offset: offset of the first script tag to retrieve
// * @script_count: (inout) (optional): Input = the maximum number of script tags to return;
// * Output = the actual number of script tags returned (may be zero)
// * @scriptTags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query
// *
// * Fetches a list of all scripts enumerated in the specified face's GSUB table
// * or GPOS table. The list returned will begin at the offset provided.
// *
// **/
// uint
// hb_ot_layout_table_get_script_tags (Face *face,
// hb_tag_t table_tag,
// uint start_offset,
// uint *script_count /* IN/OUT */,
// hb_tag_t *scriptTags /* OUT */)
// {
// const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
// return g.get_script_tags (start_offset, script_count, scriptTags);
// }
// #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n')
// /**
// * hb_ot_layout_table_find_script:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @script_tag: #hb_tag_t of the script tag requested
// * @scriptIndex: (out): The index of the requested script tag
// *
// * Fetches the index if a given script tag in the specified face's GSUB table
// * or GPOS table.
// *
// * Return value: %true if the script is found, %false otherwise
// *
// **/
// hb_bool_t
// hb_ot_layout_table_find_script (Face *face,
// hb_tag_t table_tag,
// hb_tag_t script_tag,
// uint *scriptIndex /* OUT */)
// {
// static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
// const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
// if (g.find_script_index (script_tag, scriptIndex))
// return true;
// /* try finding 'DFLT' */
// if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, scriptIndex))
// return false;
// /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
// * including many versions of DejaVu Sans Mono! */
// if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, scriptIndex))
// return false;
// /* try with 'latn'; some old fonts put their features there even though
// they're really trying to support Thai, for example :( */
// if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, scriptIndex))
// return false;
// if (scriptIndex) *scriptIndex = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
// return false;
// }
// #ifndef HB_DISABLE_DEPRECATED
// /**
// * hb_ot_layout_table_choose_script:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scriptTags: Array of #hb_tag_t script tags
// * @scriptIndex: (out): The index of the requested script tag
// * @chosen_script: (out): #hb_tag_t of the script tag requested
// *
// * Deprecated since 2.0.0
// **/
// hb_bool_t
// hb_ot_layout_table_choose_script (Face *face,
// hb_tag_t table_tag,
// const hb_tag_t *scriptTags,
// uint *scriptIndex /* OUT */,
// hb_tag_t *chosen_script /* OUT */)
// {
// const hb_tag_t *t;
// for (t = scriptTags; *t; t++);
// return hb_ot_layout_table_select_script (face, table_tag, t - scriptTags, scriptTags, scriptIndex, chosen_script);
// }
// #endif
var otTagLatinScript = newTag('l', 'a', 't', 'n')
/**
* selectScript:
* @face: #Face to work upon
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @script_count: Number of script tags in the array
* @scriptTags: Array of #hb_tag_t script tags
* @scriptIndex: (out) (optional): The index of the requested script
* @chosen_script: (out) (optional): #hb_tag_t of the requested script
*
* Selects an OpenType script for @table_tag from the @scriptTags array.
*
* If the table does not have any of the requested scripts, then `DFLT`,
* `dflt`, and `latn` tags are tried in that order. If the table still does not
* have any of these scripts, @scriptIndex and @chosen_script are set to
* #HB_OT_LAYOUT_NO_SCRIPT_INDEX.
*
* Return value:
* %true if one of the requested scripts is selected, %false if a fallback
* script is selected or if no scripts are selected.
**/
func selectScript(g *tt.TableLayout, scriptTags []tt.Tag) (int, tt.Tag, bool) {
for _, tag := range scriptTags {
if scriptIndex := g.FindScript(tag); scriptIndex != -1 {
return scriptIndex, tag, true
}
}
// try finding 'DFLT'
if scriptIndex := g.FindScript(tagDefaultScript); scriptIndex != -1 {
return scriptIndex, tagDefaultScript, false
}
// try with 'dflt'; MS site has had typos and many fonts use it now :(
if scriptIndex := g.FindScript(tagDefaultLanguage); scriptIndex != -1 {
return scriptIndex, tagDefaultLanguage, false
}
/* try with 'latn'; some old fonts put their features there even though
they're really trying to support Thai, for example :( */
if scriptIndex := g.FindScript(otTagLatinScript); scriptIndex != -1 {
return scriptIndex, otTagLatinScript, false
}
return noScriptIndex, noScriptIndex, false
}
// /**
// * hb_ot_layout_table_get_feature_tags:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @start_offset: offset of the first feature tag to retrieve
// * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
// * Output = the actual number of feature tags returned (may be zero)
// * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table
// *
// * Fetches a list of all feature tags in the given face's GSUB or GPOS table.
// *
// **/
// uint
// hb_ot_layout_table_get_feature_tags (Face *face,
// hb_tag_t table_tag,
// uint start_offset,
// uint *feature_count /* IN/OUT */,
// hb_tag_t *feature_tags /* OUT */)
// {
// const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
// return g.get_feature_tags (start_offset, feature_count, feature_tags);
// }
func findFeature(g *tt.TableLayout, featureTag tt.Tag) uint16 {
if index, ok := g.FindFeatureIndex(featureTag); ok {
return index
}
return noFeatureIndex
}
// Fetches the index of a given feature tag in the specified face's GSUB table
// or GPOS table, underneath the specified script and language.
// Return `noFeatureIndex` it the the feature is not found.
func findFeatureLang(g *tt.TableLayout, scriptIndex, languageIndex int, featureTag tt.Tag) uint16 {
if scriptIndex == noScriptIndex {
return noFeatureIndex
}
l := g.Scripts[scriptIndex].GetLangSys(uint16(languageIndex))
for _, fIndex := range l.Features {
if featureTag == g.Features[fIndex].Tag {
return fIndex
}
}
return noFeatureIndex
}
// /**
// * hb_ot_layout_script_get_language_tags:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scriptIndex: The index of the requested script tag
// * @start_offset: offset of the first language tag to retrieve
// * @language_count: (inout) (optional): Input = the maximum number of language tags to return;
// * Output = the actual number of language tags returned (may be zero)
// * @language_tags: (out) (array length=language_count): Array of language tags found in the table
// *
// * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath
// * the specified script index. The list returned will begin at the offset provided.
// *
// **/
// uint
// hb_ot_layout_script_get_language_tags (Face *face,
// hb_tag_t table_tag,
// uint scriptIndex,
// uint start_offset,
// uint *language_count /* IN/OUT */,
// hb_tag_t *language_tags /* OUT */)
// {
// const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (scriptIndex);
// return s.get_lang_sys_tags (start_offset, language_count, language_tags);
// }
// #ifndef HB_DISABLE_DEPRECATED
// /**
// * hb_ot_layout_script_find_language:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scriptIndex: The index of the requested script tag
// * @language_tag: The #hb_tag_t of the requested language
// * @languageIndex: The index of the requested language
// *
// * Fetches the index of a given language tag in the specified face's GSUB table
// * or GPOS table, underneath the specified script tag.
// *
// * Return value: %true if the language tag is found, %false otherwise
// *
// * Since: ??
// * Deprecated: ??
// **/
// hb_bool_t
// hb_ot_layout_script_find_language (Face *face,
// hb_tag_t table_tag,
// uint scriptIndex,
// hb_tag_t language_tag,
// uint *languageIndex)
// {
// return hb_ot_layout_script_select_language (face,
// table_tag,
// scriptIndex,
// 1,
// &language_tag,
// languageIndex);
// }
// #endif
// Fetches the index of a given language tag in the specified layout table,
// underneath `scriptIndex`.
// Return `true` if the language tag is found, `false` otherwise.
func selectLanguage(g *tt.TableLayout, scriptIndex int, languageTags []tt.Tag) (int, bool) {
if scriptIndex == noScriptIndex {
return defaultLanguageIndex, false
}
s := g.Scripts[scriptIndex]
for _, lang := range languageTags {
if languageIndex := s.FindLanguage(lang); languageIndex != -1 {
return languageIndex, true
}
}
// try finding 'dflt'
if languageIndex := s.FindLanguage(tagDefaultLanguage); languageIndex != -1 {
return languageIndex, false
}
return defaultLanguageIndex, false
}
// /**
// * hb_ot_layout_language_get_required_feature_index:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scriptIndex: The index of the requested script tag
// * @languageIndex: The index of the requested language tag
// * @feature_index: (out): The index of the requested feature
// *
// * Fetches the index of a requested feature in the given face's GSUB or GPOS table,
// * underneath the specified script and language.
// *
// * Return value: %true if the feature is found, %false otherwise
// *
// **/
// hb_bool_t
// hb_ot_layout_language_get_required_feature_index (Face *face,
// hb_tag_t table_tag,
// uint scriptIndex,
// uint languageIndex,
// uint *feature_index /* OUT */)
// {
// return getRequiredFeature (face,
// table_tag,
// scriptIndex,
// languageIndex,
// feature_index,
// nullptr);
// }
// Fetches the tag of a requested feature index in the given layout table,
// underneath the specified script and language. Returns -1 if no feature is requested.
func getRequiredFeature(g *tt.TableLayout, scriptIndex, languageIndex int) (uint16, tt.Tag) {
if scriptIndex == noScriptIndex || languageIndex == defaultLanguageIndex {
return noFeatureIndex, 0
}
l := g.Scripts[scriptIndex].Languages[languageIndex]
if l.RequiredFeatureIndex == 0xFFFF {
return noFeatureIndex, 0
}
index := l.RequiredFeatureIndex
return index, g.Features[index].Tag
}
// /**
// * hb_ot_layout_language_get_feature_indexes:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scriptIndex: The index of the requested script tag
// * @languageIndex: The index of the requested language tag
// * @start_offset: offset of the first feature tag to retrieve
// * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
// * Output: the actual number of feature tags returned (may be zero)
// * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query
// *
// * Fetches a list of all features in the specified face's GSUB table
// * or GPOS table, underneath the specified script and language. The list
// * returned will begin at the offset provided.
// **/
// uint
// hb_ot_layout_language_get_feature_indexes (Face *face,
// hb_tag_t table_tag,
// uint scriptIndex,
// uint languageIndex,
// uint start_offset,
// uint *feature_count /* IN/OUT */,
// uint *feature_indexes /* OUT */)
// {
// const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
// const OT::LangSys &l = g.get_script (scriptIndex).get_lang_sys (languageIndex);
// return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
// }
// /**
// * hb_ot_layout_language_get_feature_tags:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scriptIndex: The index of the requested script tag
// * @languageIndex: The index of the requested language tag
// * @start_offset: offset of the first feature tag to retrieve
// * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
// * Output = the actual number of feature tags returned (may be zero)
// * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query
// *
// * Fetches a list of all features in the specified face's GSUB table
// * or GPOS table, underneath the specified script and language. The list
// * returned will begin at the offset provided.
// *
// **/
// uint
// hb_ot_layout_language_get_feature_tags (Face *face,
// hb_tag_t table_tag,
// uint scriptIndex,
// uint languageIndex,
// uint start_offset,
// uint *feature_count /* IN/OUT */,
// hb_tag_t *feature_tags /* OUT */)
// {
// const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
// const OT::LangSys &l = g.get_script (scriptIndex).get_lang_sys (languageIndex);
// static_assert ((sizeof (uint) == sizeof (hb_tag_t)), "");
// uint ret = l.get_feature_indexes (start_offset, feature_count, (uint *) feature_tags);
// if (feature_tags) {
// uint count = *feature_count;
// for (uint i = 0; i < count; i++)
// feature_tags[i] = g.get_feature_tag ((uint) feature_tags[i]);
// }
// return ret;
// }
// /**
// * hb_ot_layout_feature_get_lookups:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @feature_index: The index of the requested feature
// * @start_offset: offset of the first lookup to retrieve
// * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
// * Output = the actual number of lookups returned (may be zero)
// * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query
// *
// * Fetches a list of all lookups enumerated for the specified feature, in
// * the specified face's GSUB table or GPOS table. The list returned will
// * begin at the offset provided.
// *
// * Since: 0.9.7
// **/
// uint
// hb_ot_layout_feature_get_lookups (Face *face,
// hb_tag_t table_tag,
// uint feature_index,
// uint start_offset,
// uint *lookup_count /* IN/OUT */,
// uint *lookup_indexes /* OUT */)
// {
// return getFeatureLookupsWithVar (face,
// table_tag,
// feature_index,
// HB_OT_LAYOUT_NO_VARIATIONS_INDEX,
// start_offset,
// lookup_count,
// lookup_indexes);
// }
// /**
// * hb_ot_layout_table_get_lookup_count:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// *
// * Fetches the total number of lookups enumerated in the specified
// * face's GSUB table or GPOS table.
// *
// * Since: 0.9.22
// **/
// uint
// hb_ot_layout_table_get_lookup_count (Face *face,
// hb_tag_t table_tag)
// {
// return get_gsubgpos_table (face, table_tag).get_lookup_count ();
// }
// struct hb_collect_features_context_t
// {
// hb_collect_features_context_t (Face *face,
// hb_tag_t table_tag,
// hb_set_t *feature_indexes_)
// : g (get_gsubgpos_table (face, table_tag)),
// feature_indexes (feature_indexes_),
// script_count (0),langsys_count (0), feature_index_count (0) {}
// bool visited (const OT::Script &s)
// {
// /* We might have Null() object here. Don't want to involve
// * that in the memoize. So, detect empty objects and return. */
// if (unlikely (!s.has_default_lang_sys () &&
// !s.get_lang_sys_count ()))
// return true;
// if (script_count++ > HB_MAX_SCRIPTS)
// return true;
// return visited (s, visited_script);
// }
// bool visited (const OT::LangSys &l)
// {
// /* We might have Null() object here. Don't want to involve
// * that in the memoize. So, detect empty objects and return. */
// if (unlikely (!l.has_required_feature () &&
// !l.get_feature_count ()))
// return true;
// if (langsys_count++ > HB_MAX_LANGSYS)
// return true;
// return visited (l, visited_langsys);
// }
// bool visited_feature_indices (unsigned count)
// {
// feature_index_count += count;
// return feature_index_count > HB_MAX_FEATURE_INDICES;
// }
// private:
// template <typename T>
// bool visited (const T &p, hb_set_t &visited_set)
// {
// hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g);
// if (visited_set.has (delta))
// return true;
// visited_set.add (delta);
// return false;
// }
// public:
// const OT::GSUBGPOS &g;
// hb_set_t *feature_indexes;
// private:
// hb_set_t visited_script;
// hb_set_t visited_langsys;
// uint script_count;
// uint langsys_count;
// uint feature_index_count;
// };
// static void
// langsys_collect_features (hb_collect_features_context_t *c,
// const OT::LangSys &l,
// const hb_tag_t *features)
// {
// if (c.visited (l)) return;
// if (!features)
// {
// /* All features. */
// if (l.has_required_feature () && !c.visited_feature_indices (1))
// c.feature_indexes.add (l.get_required_feature_index ());
// if (!c.visited_feature_indices (l.featureIndex.len))
// l.add_feature_indexes_to (c.feature_indexes);
// }
// else
// {
// /* Ugh. Any faster way? */
// for (; *features; features++)
// {
// hb_tag_t feature_tag = *features;
// uint num_features = l.get_feature_count ();
// for (uint i = 0; i < num_features; i++)
// {
// uint feature_index = l.get_feature_index (i);
// if (feature_tag == c.g.get_feature_tag (feature_index))
// {
// c.feature_indexes.add (feature_index);
// break;
// }
// }
// }
// }
// }
// static void
// script_collect_features (hb_collect_features_context_t *c,
// const OT::Script &s,
// const hb_tag_t *languages,
// const hb_tag_t *features)
// {
// if (c.visited (s)) return;
// if (!languages)
// {
// /* All languages. */
// if (s.has_default_lang_sys ())
// langsys_collect_features (c,
// s.get_default_lang_sys (),
// features);
// uint count = s.get_lang_sys_count ();
// for (uint languageIndex = 0; languageIndex < count; languageIndex++)
// langsys_collect_features (c,
// s.get_lang_sys (languageIndex),
// features);
// }
// else
// {
// for (; *languages; languages++)
// {
// uint languageIndex;
// if (s.find_lang_sys_index (*languages, &languageIndex))
// langsys_collect_features (c,
// s.get_lang_sys (languageIndex),
// features);
// }
// }
// }
// /**
// * hb_ot_layout_collect_features:
// * @face: #Face to work upon
// * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
// * @scripts: The array of scripts to collect features for
// * @languages: The array of languages to collect features for
// * @features: The array of features to collect
// * @feature_indexes: (out): The array of feature indexes found for the query
// *
// * Fetches a list of all feature indexes in the specified face's GSUB table
// * or GPOS table, underneath the specified scripts, languages, and features.
// * If no list of scripts is provided, all scripts will be queried. If no list
// * of languages is provided, all languages will be queried. If no list of
// * features is provided, all features will be queried.
// *
// * Since: 1.8.5
// **/
// void
// hb_ot_layout_collect_features (Face *face,
// hb_tag_t table_tag,
// const hb_tag_t *scripts,
// const hb_tag_t *languages,
// const hb_tag_t *features,
// hb_set_t *feature_indexes /* OUT */)
// {
// hb_collect_features_context_t c (face, table_tag, feature_indexes);
// if (!scripts)
// {
// /* All scripts. */
// uint count = c.g.get_script_count ();
// for (uint scriptIndex = 0; scriptIndex < count; scriptIndex++)
// script_collect_features (&c,
// c.g.get_script (scriptIndex),
// languages,
// features);
// }
// else
// {
// for (; *scripts; scripts++)
// {
// uint scriptIndex;
// if (c.g.find_script_index (*scripts, &scriptIndex))
// script_collect_features (&c,
// c.g.get_script (scriptIndex),