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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harfbuzzjs",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Minimal version of HarfBuzz for JavaScript use",
5
5
  "main": "index.js",
6
6
  "scripts": {
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 = hb.createBuffer();
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 = hb.createBuffer();
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
  });
@@ -1,3 +0,0 @@
1
- {
2
- "makefile.configureOnOpen": true
3
- }