harfbuzzjs 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Makefile +1 -1
- package/README.md +6 -3
- package/em.runtime +2 -0
- package/hb-subset.wasm +0 -0
- package/hb.js +1 -1
- package/hb.symbols +6 -1
- package/hb.wasm +0 -0
- package/hbjs.js +408 -179
- package/package.json +1 -1
- package/test/index.js +234 -4
- package/.vscode/settings.json +0 -3
package/package.json
CHANGED
package/test/index.js
CHANGED
|
@@ -569,6 +569,195 @@ describe('Buffer', function () {
|
|
|
569
569
|
const glyphs = buffer.json();
|
|
570
570
|
expect(glyphs[0].g).not.to.equal(3 /* space */);
|
|
571
571
|
});
|
|
572
|
+
|
|
573
|
+
it('setFlags ignores invalid flags', function () {
|
|
574
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
575
|
+
face = hb.createFace(blob);
|
|
576
|
+
font = hb.createFont(face);
|
|
577
|
+
buffer = hb.createBuffer();
|
|
578
|
+
buffer.addText('abc');
|
|
579
|
+
buffer.setFlags(['invalidFlag']);
|
|
580
|
+
buffer.guessSegmentProperties();
|
|
581
|
+
hb.shape(font, buffer)
|
|
582
|
+
const glyphs = buffer.json();
|
|
583
|
+
expect(glyphs[0].g).to.equal(68 /* a */);
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('setFlags with PRODUCE_SAFE_TO_INSERT_TATWEEL affects glyph flags', function () {
|
|
587
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSansArabic-Variable.ttf')));
|
|
588
|
+
face = hb.createFace(blob);
|
|
589
|
+
font = hb.createFont(face);
|
|
590
|
+
buffer = hb.createBuffer();
|
|
591
|
+
buffer.addText('بلا');
|
|
592
|
+
buffer.setFlags(['PRODUCE_SAFE_TO_INSERT_TATWEEL']);
|
|
593
|
+
buffer.guessSegmentProperties();
|
|
594
|
+
hb.shape(font, buffer)
|
|
595
|
+
const flags = Array.from(buffer.json().map(g => g.flags));
|
|
596
|
+
expect(flags).to.deep.equal([5, 0]);
|
|
597
|
+
|
|
598
|
+
buffer.clearContents();
|
|
599
|
+
buffer.addText('بلا');
|
|
600
|
+
buffer.setFlags([]);
|
|
601
|
+
buffer.guessSegmentProperties();
|
|
602
|
+
hb.shape(font, buffer)
|
|
603
|
+
const flags2 = Array.from(buffer.json().map(g => g.flags));
|
|
604
|
+
expect(flags2).to.deep.equal([1, 0]);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
it('serialize ignores invalid flags', function () {
|
|
608
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
609
|
+
face = hb.createFace(blob);
|
|
610
|
+
font = hb.createFont(face);
|
|
611
|
+
buffer = hb.createBuffer();
|
|
612
|
+
buffer.addText('abc');
|
|
613
|
+
buffer.guessSegmentProperties();
|
|
614
|
+
hb.shape(font, buffer)
|
|
615
|
+
const glyphs = buffer.serialize(font, 0, null, "TEXT", ["invalidFlag"]);
|
|
616
|
+
expect(glyphs).to.deep.equal("[a=0+561|b=1+615|c=2+480]");
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('reset resets the buffer', function () {
|
|
620
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
621
|
+
face = hb.createFace(blob);
|
|
622
|
+
font = hb.createFont(face);
|
|
623
|
+
buffer = hb.createBuffer();
|
|
624
|
+
buffer.addText('abc');
|
|
625
|
+
buffer.guessSegmentProperties();
|
|
626
|
+
expect(buffer.getContentType()).to.equal("UNICODE");
|
|
627
|
+
hb.shape(font, buffer)
|
|
628
|
+
expect(buffer.getContentType()).to.equal("GLYPHS");
|
|
629
|
+
buffer.reset();
|
|
630
|
+
expect(buffer.getContentType()).to.equal("INVALID");
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it('getLength gets the length before and after shaping', function () {
|
|
634
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
635
|
+
face = hb.createFace(blob);
|
|
636
|
+
font = hb.createFont(face);
|
|
637
|
+
buffer = hb.createBuffer();
|
|
638
|
+
buffer.addText('fi');
|
|
639
|
+
expect(buffer.getLength()).to.equal(2);
|
|
640
|
+
buffer.guessSegmentProperties();
|
|
641
|
+
hb.shape(font, buffer)
|
|
642
|
+
expect(buffer.getLength()).to.equal(1);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it('getInfos and getPositions return empty arrays for empty buffer', function () {
|
|
646
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
647
|
+
face = hb.createFace(blob);
|
|
648
|
+
font = hb.createFont(face);
|
|
649
|
+
buffer = hb.createBuffer();
|
|
650
|
+
expect(buffer.getGlyphInfos()).to.deep.equal([]);
|
|
651
|
+
expect(buffer.getGlyphPositions()).to.deep.equal([]);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it('getInfos and getPositions return non empty arrays for non empty buffer', function () {
|
|
655
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
656
|
+
face = hb.createFace(blob);
|
|
657
|
+
font = hb.createFont(face);
|
|
658
|
+
buffer = hb.createBuffer();
|
|
659
|
+
buffer.addText('x\u0300fi'); // combining mark and ligature to make the test more interesting
|
|
660
|
+
buffer.guessSegmentProperties();
|
|
661
|
+
|
|
662
|
+
// before shaping
|
|
663
|
+
expect(buffer.getGlyphInfos()).to.deep.equal([
|
|
664
|
+
{ codepoint: 120, cluster: 0 },
|
|
665
|
+
{ codepoint: 768, cluster: 1 },
|
|
666
|
+
{ codepoint: 102, cluster: 2 },
|
|
667
|
+
{ codepoint: 105, cluster: 3 }
|
|
668
|
+
]);
|
|
669
|
+
expect(buffer.getGlyphPositions()).to.deep.equal([
|
|
670
|
+
{ x_advance: 0, y_advance: 0, x_offset: 0, y_offset: 0 },
|
|
671
|
+
{ x_advance: 0, y_advance: 0, x_offset: 0, y_offset: 0 },
|
|
672
|
+
{ x_advance: 0, y_advance: 0, x_offset: 0, y_offset: 0 },
|
|
673
|
+
{ x_advance: 0, y_advance: 0, x_offset: 0, y_offset: 0 }
|
|
674
|
+
]);
|
|
675
|
+
|
|
676
|
+
hb.shape(font, buffer);
|
|
677
|
+
// after shaping
|
|
678
|
+
expect(buffer.getGlyphInfos()).to.deep.equal([
|
|
679
|
+
{ codepoint: 91, cluster: 0 },
|
|
680
|
+
{ codepoint: 2662, cluster: 0 },
|
|
681
|
+
{ codepoint: 1652, cluster: 2 }
|
|
682
|
+
]);
|
|
683
|
+
expect(buffer.getGlyphPositions()).to.deep.equal([
|
|
684
|
+
{ x_advance: 529, y_advance: 0, x_offset: 0, y_offset: 0 },
|
|
685
|
+
{ x_advance: 0, y_advance: 0, x_offset: 97, y_offset: 0 },
|
|
686
|
+
{ x_advance: 602, y_advance: 0, x_offset: 0, y_offset: 0 }
|
|
687
|
+
]);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('getPositions returns empty array for buffer without positions', function () {
|
|
691
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
692
|
+
face = hb.createFace(blob);
|
|
693
|
+
font = hb.createFont(face);
|
|
694
|
+
buffer = hb.createBuffer();
|
|
695
|
+
buffer.addText('abc');
|
|
696
|
+
buffer.guessSegmentProperties();
|
|
697
|
+
var currentPhase = "";
|
|
698
|
+
buffer.setMessageFunc((buffer, font, message) => {
|
|
699
|
+
if (message.startsWith("start table GSUB"))
|
|
700
|
+
currentPhase = "GSUB";
|
|
701
|
+
else if (message.startsWith("start table GPOS"))
|
|
702
|
+
currentPhase = "GPOS";
|
|
703
|
+
|
|
704
|
+
if (currentPhase === "GSUB")
|
|
705
|
+
expect(buffer.getGlyphPositions()).to.deep.equal([]);
|
|
706
|
+
else if (currentPhase === "GPOS")
|
|
707
|
+
expect(buffer.getGlyphPositions()).to.not.deep.equal([]);
|
|
708
|
+
|
|
709
|
+
return true;
|
|
710
|
+
});
|
|
711
|
+
hb.shape(font, buffer);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('updateGlyphPositions updates the glyph positions', function () {
|
|
715
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
716
|
+
face = hb.createFace(blob);
|
|
717
|
+
font = hb.createFont(face);
|
|
718
|
+
buffer = hb.createBuffer();
|
|
719
|
+
|
|
720
|
+
// text with a base glyph and two marks to test mkmk after manually updating glyph positions
|
|
721
|
+
var text = 'x\u0302\u0300';
|
|
722
|
+
|
|
723
|
+
// without updateGlyphPositions
|
|
724
|
+
buffer.addText(text);
|
|
725
|
+
buffer.guessSegmentProperties();
|
|
726
|
+
hb.shape(font, buffer);
|
|
727
|
+
var positions = buffer.getGlyphPositions();
|
|
728
|
+
expect(positions[1].y_offset).to.equal(0);
|
|
729
|
+
expect(positions[2].y_offset).to.equal(229);
|
|
730
|
+
|
|
731
|
+
// with updateGlyphPositions inside buffer message callback
|
|
732
|
+
buffer.clearContents();
|
|
733
|
+
buffer.addText(text);
|
|
734
|
+
buffer.guessSegmentProperties();
|
|
735
|
+
var currentPhase = "";
|
|
736
|
+
buffer.setMessageFunc((buffer, font, message) => {
|
|
737
|
+
if (message.startsWith("start table GSUB"))
|
|
738
|
+
currentPhase = "GSUB";
|
|
739
|
+
else if (message.startsWith("start table GPOS"))
|
|
740
|
+
currentPhase = "GPOS";
|
|
741
|
+
|
|
742
|
+
// modify the 2nd glyph y_offset after the last mark lookup and before mkmk lookups
|
|
743
|
+
if (currentPhase === "GPOS" && message.startsWith("end lookup 4 feature 'mark'")) {
|
|
744
|
+
var positions = buffer.getGlyphPositions();
|
|
745
|
+
expect(positions[1].y_offset).to.equal(0);
|
|
746
|
+
expect(positions[2].y_offset).to.equal(0);
|
|
747
|
+
positions[1].y_offset += 10;
|
|
748
|
+
buffer.updateGlyphPositions(positions);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return true;
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
hb.shape(font, buffer);
|
|
755
|
+
var positions = buffer.getGlyphPositions();
|
|
756
|
+
|
|
757
|
+
// both mark glyphs now be offset vertically by 10, since the second mark attaches to the first mark
|
|
758
|
+
expect(positions[1].y_offset).to.equal(10);
|
|
759
|
+
expect(positions[2].y_offset).to.equal(239);
|
|
760
|
+
});
|
|
572
761
|
});
|
|
573
762
|
|
|
574
763
|
describe('shape', function () {
|
|
@@ -714,9 +903,8 @@ describe('shape', function () {
|
|
|
714
903
|
var glyphs = buffer.json();
|
|
715
904
|
expect(glyphs).to.have.lengthOf(2);
|
|
716
905
|
expect(glyphs[0].g).to.equal(118);
|
|
717
|
-
buffer.destroy();
|
|
718
906
|
|
|
719
|
-
buffer
|
|
907
|
+
buffer.clearContents();
|
|
720
908
|
buffer.addText('५ल');
|
|
721
909
|
buffer.setLanguage('dty');
|
|
722
910
|
buffer.guessSegmentProperties();
|
|
@@ -737,9 +925,8 @@ describe('shape', function () {
|
|
|
737
925
|
var glyphs = buffer.json();
|
|
738
926
|
expect(glyphs).to.have.lengthOf(2);
|
|
739
927
|
expect(glyphs[0].g).to.equal(118);
|
|
740
|
-
buffer.destroy();
|
|
741
928
|
|
|
742
|
-
buffer
|
|
929
|
+
buffer.clearContents();
|
|
743
930
|
buffer.addText('५ल');
|
|
744
931
|
buffer.setLanguage('x-hbot-4e455020'); // 'NEP '
|
|
745
932
|
buffer.guessSegmentProperties();
|
|
@@ -758,8 +945,51 @@ describe('misc', function () {
|
|
|
758
945
|
expect(version).to.have.property('micro').that.is.a('number');
|
|
759
946
|
expect(version.major).to.be.at.least(10);
|
|
760
947
|
});
|
|
948
|
+
|
|
761
949
|
it('get version string', function () {
|
|
762
950
|
const version_string = hb.version_string();
|
|
763
951
|
expect(version_string).to.match(/^\d+\.\d+\.\d+$/);
|
|
764
952
|
});
|
|
953
|
+
|
|
954
|
+
it('convert OpenType tag to script', function () {
|
|
955
|
+
expect(hb.otTagToScript('arab')).to.equal('Arab');
|
|
956
|
+
expect(hb.otTagToScript('latn')).to.equal('Latn');
|
|
957
|
+
expect(hb.otTagToScript('dev2')).to.equal('Deva');
|
|
958
|
+
expect(hb.otTagToScript('nko ')).to.equal('Nkoo');
|
|
959
|
+
expect(hb.otTagToScript('DFLT')).to.equal('\0\0\0\0');
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
it('convert OpenType tag to language', function () {
|
|
963
|
+
expect(hb.otTagToLanguage('ARA ')).to.equal('ar');
|
|
964
|
+
expect(hb.otTagToLanguage('ENG ')).to.equal('en');
|
|
965
|
+
expect(hb.otTagToLanguage('BAD0')).to.equal('bad');
|
|
966
|
+
expect(hb.otTagToLanguage('SYRE')).to.equal('und-syre');
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
it("test that calling functions repeatedly doesn't exhaust memory", function () {
|
|
970
|
+
blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf')));
|
|
971
|
+
face = hb.createFace(blob);
|
|
972
|
+
font = hb.createFont(face);
|
|
973
|
+
for (let i = 0; i < 10000; i++) {
|
|
974
|
+
expect(face.listNames()).to.not.be.null;
|
|
975
|
+
expect(face.getName(0, "en")).to.not.be.null;
|
|
976
|
+
expect(font.hExtents()).to.not.be.null;
|
|
977
|
+
expect(font.vExtents()).to.not.be.null;
|
|
978
|
+
expect(font.glyphHOrigin(0)).to.not.be.null;
|
|
979
|
+
expect(font.glyphVOrigin(0)).to.be.null;
|
|
980
|
+
expect(font.glyphExtents(0)).to.not.be.null;
|
|
981
|
+
expect(font.glyphFromName("a")).to.not.be.null;
|
|
982
|
+
for (let tableTag of ["GSUB", "GPOS"]) {
|
|
983
|
+
expect(face.getTableFeatureTags(tableTag)).to.not.be.null;
|
|
984
|
+
expect(face.getTableScriptTags(tableTag)).to.not.be.null;
|
|
985
|
+
expect(face.getScriptLanguageTags(tableTag, 0)).to.not.be.null;
|
|
986
|
+
expect(face.getLanguageFeatureTags(tableTag, 0, 0)).to.not.be.null;
|
|
987
|
+
if (tableTag === "GSUB") {
|
|
988
|
+
expect(face.getFeatureNameIds(tableTag, 50)).to.not.be.null;
|
|
989
|
+
} else {
|
|
990
|
+
expect(face.getFeatureNameIds(tableTag, 50)).to.be.null;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
});
|
|
765
995
|
});
|
package/.vscode/settings.json
DELETED