@breakside/jskit 2026.11.0 → 2026.27.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.
Files changed (87) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/JS/FNTOpenTypeFont.js +331 -22
  5. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  6. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  7. package/Frameworks/Foundation.jsframework/JS/JSAttributedString.js +56 -1
  8. package/Frameworks/Foundation.jsframework/JS/JSContext.js +44 -17
  9. package/Frameworks/Foundation.jsframework/JS/JSCubicBezier.js +54 -1
  10. package/Frameworks/Foundation.jsframework/JS/JSFont.js +8 -9
  11. package/Frameworks/Foundation.jsframework/JS/JSGradient.js +28 -2
  12. package/Frameworks/Foundation.jsframework/JS/JSImage+HTML.js +27 -1
  13. package/Frameworks/Foundation.jsframework/JS/JSNumberFormatter.js +23 -1
  14. package/Frameworks/Foundation.jsframework/JS/JSPath.js +194 -6
  15. package/Frameworks/Foundation.jsframework/JS/JSPoint.js +56 -0
  16. package/Frameworks/Foundation.jsframework/JS/JSTextRun.js +21 -4
  17. package/Frameworks/Foundation.jsframework/JS/JSTextStorage.js +5 -0
  18. package/Frameworks/Foundation.jsframework/JS/JSTextTypesetter.js +6 -1
  19. package/Frameworks/Foundation.jsframework/JS/JSXMLParser.js +1 -1
  20. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  21. package/Frameworks/Foundation.jsframework/sources.json +1 -0
  22. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  23. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  24. package/Info.json +2 -2
  25. package/Node/io.breakside.jskit-bundle.js +2 -2
  26. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  27. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  28. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  29. package/Root/Frameworks/CSSOM/CSSTokenizer.js +633 -470
  30. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  31. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  32. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  33. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  34. package/Root/Frameworks/DOM/Info.yaml +1 -1
  35. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  36. package/Root/Frameworks/FontKit/FNTOpenTypeFont.js +331 -22
  37. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  38. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  39. package/Root/Frameworks/Foundation/JSAttributedString.js +56 -1
  40. package/Root/Frameworks/Foundation/JSContext.js +44 -17
  41. package/Root/Frameworks/Foundation/JSCubicBezier.js +54 -1
  42. package/Root/Frameworks/Foundation/JSFont.js +8 -9
  43. package/Root/Frameworks/Foundation/JSGradient.js +28 -2
  44. package/Root/Frameworks/Foundation/JSImage+HTML.js +27 -1
  45. package/Root/Frameworks/Foundation/JSNumberFormatter.js +23 -1
  46. package/Root/Frameworks/Foundation/JSPath.js +194 -6
  47. package/Root/Frameworks/Foundation/JSPoint.js +56 -0
  48. package/Root/Frameworks/Foundation/JSTextRun.js +21 -4
  49. package/Root/Frameworks/Foundation/JSTextStorage.js +5 -0
  50. package/Root/Frameworks/Foundation/JSTextTypesetter.js +6 -1
  51. package/Root/Frameworks/Foundation/JSXMLParser.js +1 -1
  52. package/Root/Frameworks/ImageKit/IKHTMLCanvasBitmapContext.js +187 -101
  53. package/Root/Frameworks/ImageKit/IKNodeBitmapContext.js +4 -4
  54. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  55. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  56. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  57. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  58. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  59. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  60. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  61. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  62. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  63. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  64. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  65. package/Root/Frameworks/TestKit/TKAssert.js +4 -4
  66. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  67. package/Root/Frameworks/UIKit/UIApplication.js +9 -1
  68. package/Root/Frameworks/UIKit/UICollectionView.js +4 -0
  69. package/Root/Frameworks/UIKit/UIColorPanel.js +46 -0
  70. package/Root/Frameworks/UIKit/UIColorWell.js +8 -0
  71. package/Root/Frameworks/UIKit/UIHTMLDisplayServer.js +21 -2
  72. package/Root/Frameworks/UIKit/UIHTMLDisplayServerCanvasContext.js +150 -69
  73. package/Root/Frameworks/UIKit/UIHTMLTextRun.js +5 -0
  74. package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +18 -3
  75. package/Root/Frameworks/UIKit/UIListView.js +7 -5
  76. package/Root/Frameworks/UIKit/UIMenu.js +50 -32
  77. package/Root/Frameworks/UIKit/UIMenuItem.js +12 -0
  78. package/Root/Frameworks/UIKit/UIMenuView.js +27 -5
  79. package/Root/Frameworks/UIKit/UIPlatform.js +7 -0
  80. package/Root/Frameworks/UIKit/UIPopupButton.js +7 -1
  81. package/Root/Frameworks/UIKit/UIResponder.js +8 -0
  82. package/Root/Frameworks/UIKit/UITextEditor.js +31 -7
  83. package/Root/Frameworks/UIKit/UITextField.js +7 -0
  84. package/Root/Frameworks/UIKit/UITokenField.js +65 -0
  85. package/Root/Frameworks/UIKit/UIToolbar.js +1 -1
  86. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  87. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
4
- "JSBundleVersion": "2026.11.0",
4
+ "JSBundleVersion": "2026.27.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "DOM+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "812100aa4c3e5b1ec1d0d432be903bba4db01b0e"
12
+ "GitRevision": "f25bf439a7b48f4536a7f4a44d2f1ec39c46208d"
13
13
  }
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
6
- "JSBundleVersion": "2026.11.0",
6
+ "JSBundleVersion": "2026.27.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
11
11
  "node": "DOM+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "812100aa4c3e5b1ec1d0d432be903bba4db01b0e"
14
+ "GitRevision": "f25bf439a7b48f4536a7f4a44d2f1ec39c46208d"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
4
- "JSBundleVersion": "2026.11.0",
4
+ "JSBundleVersion": "2026.27.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
8
8
  "html": "FontKit+HTML.js"
9
9
  },
10
10
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
11
- "GitRevision": "812100aa4c3e5b1ec1d0d432be903bba4db01b0e"
11
+ "GitRevision": "f25bf439a7b48f4536a7f4a44d2f1ec39c46208d"
12
12
  }
@@ -14,7 +14,7 @@
14
14
  // limitations under the License.
15
15
 
16
16
  // #import Foundation
17
- /* global FNTOpenTypeConstructor */
17
+ /* global FNTOpenTypeConstructor, DataView */
18
18
  'use strict';
19
19
 
20
20
  (function(){
@@ -496,6 +496,10 @@ JSClass("FNTOpenTypeFont", JSObject, {
496
496
  return completion.promise;
497
497
  },
498
498
 
499
+ pathForGlyph: function(glyphIndex, transform){
500
+ return this.tables.glyf.pathForGlyph(glyphIndex, this.tables.loca, this.tables.head.indexToLocFormat, transform);
501
+ },
502
+
499
503
  });
500
504
 
501
505
  var tableChecksum = function(data){
@@ -1183,7 +1187,25 @@ JSClass("FNTOpenTypeFontTableCFF", FNTOpenTypeFontTable, {
1183
1187
  });
1184
1188
 
1185
1189
  JSClass("FNTOpenTypeFontTableGlyf", FNTOpenTypeFontTable, {
1186
- tag: 'glyf'
1190
+ tag: 'glyf',
1191
+
1192
+ glyph: function(glyphIndex, loca, format){
1193
+ var range = loca.rangeForGlyph(glyphIndex, format);
1194
+ var data = this.data.subdataInRange(range);
1195
+ var glyph = FNTOpenTypeGlyph.initWithData(data);
1196
+ if (glyph.isKindOfClass(FNTOpenTypeCompoundGlyph)){
1197
+ glyph.glyf = this;
1198
+ glyph.loca = loca;
1199
+ glyph.locaFormat = format;
1200
+ }
1201
+ return glyph;
1202
+ },
1203
+
1204
+ pathForGlyph: function(glyphIndex, loca, format, transform){
1205
+ var glyph = this.glyph(glyphIndex, loca, format);
1206
+ return glyph.getPath(transform);
1207
+ },
1208
+
1187
1209
  });
1188
1210
 
1189
1211
  JSClass("FNTOpenTypeFontTableLoca", FNTOpenTypeFontTable, {
@@ -1790,10 +1812,14 @@ JSClass("FNTOpenTypeFontCmap13", FNTOpenTypeFontCmap, {
1790
1812
  JSClass("FNTOpenTypeGlyph", JSObject, {
1791
1813
 
1792
1814
  initWithData: function(data){
1793
- if (data.length >= 2 && data.dataView().getInt16(0) < 0){
1794
- return FNTOpenTypeCompoundGlyph.initWithData(data);
1815
+ if (this.$class === FNTOpenTypeGlyph){
1816
+ if (data.length >= 2 && data.dataView().getInt16(0) < 0){
1817
+ return FNTOpenTypeCompoundGlyph.initWithData(data);
1818
+ }
1819
+ return FNTOpenTypeSimpleGlyph.initWithData(data);
1795
1820
  }
1796
- return FNTOpenTypeSimpleGlyph.initWithData(data);
1821
+ this.data = data;
1822
+ this.dataView = data.dataView();
1797
1823
  },
1798
1824
 
1799
1825
  data: null,
@@ -1802,27 +1828,184 @@ JSClass("FNTOpenTypeGlyph", JSObject, {
1802
1828
  xMin: DataBackedProperty(2, "int16"),
1803
1829
  yMin: DataBackedProperty(4, "int16"),
1804
1830
  xMax: DataBackedProperty(6, "int16"),
1805
- yMax: DataBackedProperty(8, "int16")
1831
+ yMax: DataBackedProperty(8, "int16"),
1832
+
1833
+ getPath: function(){
1834
+ return null;
1835
+ },
1806
1836
 
1807
1837
  });
1808
1838
 
1809
- JSClass("FNTOpenTypeSimpleGlyph", JSObject, {
1839
+ JSClass("FNTOpenTypeSimpleGlyph", FNTOpenTypeGlyph, {
1810
1840
 
1811
- initWithData: function(data){
1812
- this.data = data;
1813
- this.dataView = data.dataView();
1814
- },
1841
+ numberOfCountours: DataBackedProperty(0, "int16"),
1815
1842
 
1816
- numberOfCountours: DataBackedProperty(0, "int16")
1843
+ endPointIndexes: null,
1844
+ flags: null,
1845
+
1846
+ points: JSLazyInitProperty(function(){
1847
+ var points = [];
1848
+ var offset = 10;
1849
+ var end = this.data.length;
1850
+ var i, l;
1851
+ if (offset + this.numberOfCountours * 2 > end){
1852
+ throw new Error("not enough data for glyph end point indexes");
1853
+ }
1854
+ this.endPointIndexes = [];
1855
+ for (i = 0; i < this.numberOfCountours; ++i){
1856
+ this.endPointIndexes.push(this.dataView.getUint16(offset));
1857
+ offset += 2;
1858
+ }
1859
+ if (offset + 2 > end){
1860
+ throw new Error("not enough data for glyph instructions");
1861
+ }
1862
+ var instructionLength = this.dataView.getUint16(offset);
1863
+ offset += 2;
1864
+ if (offset + instructionLength > end){
1865
+ throw new Error("not enough data for glyph instructions");
1866
+ }
1867
+ offset += instructionLength;
1868
+ var numberOfPoints = this.endPointIndexes[this.endPointIndexes.length - 1] + 1;
1869
+ var repeatCount;
1870
+ var j, k;
1871
+ var flags;
1872
+ i = 0;
1873
+ this.flags = [];
1874
+ while (i < numberOfPoints){
1875
+ if (offset >= end){
1876
+ return null;
1877
+ }
1878
+ flags = this.data[offset];
1879
+ ++offset;
1880
+ this.flags.push(flags);
1881
+ ++i;
1882
+ if ((flags & 0x08) !== 0){
1883
+ if (offset >= end){
1884
+ return null;
1885
+ }
1886
+ repeatCount = this.data[offset];
1887
+ ++offset;
1888
+ for (j = 0; j < repeatCount; ++j){
1889
+ this.flags.push(flags);
1890
+ ++i;
1891
+ }
1892
+ }
1893
+ }
1894
+ i = 0;
1895
+ var point = JSPoint.Zero;
1896
+ var dx, dy;
1897
+ while (i < numberOfPoints){
1898
+ point = JSPoint(point);
1899
+ flags = this.flags[i];
1900
+ if ((flags & 0x02) === 0){
1901
+ if ((flags & 0x10) === 0){
1902
+ if (offset + 2 > end){
1903
+ throw new Error("not enough data for x delta");
1904
+ }
1905
+ dx = this.dataView.getInt16(offset);
1906
+ offset += 2;
1907
+ }else{
1908
+ dx = 0;
1909
+ }
1910
+ }else{
1911
+ if (offset >= end){
1912
+ throw new Error("not enough data for x delta");
1913
+ }
1914
+ dx = this.data[offset];
1915
+ ++offset;
1916
+ if ((flags & 0x10) === 0){
1917
+ dx = -dx;
1918
+ }
1919
+ }
1920
+ point.x += dx;
1921
+ points.push(point);
1922
+ ++i;
1923
+ }
1924
+ i = 0;
1925
+ while (i < numberOfPoints){
1926
+ points[i].y = point.y;
1927
+ point = points[i];
1928
+ flags = this.flags[i];
1929
+ if ((flags & 0x04) === 0){
1930
+ if ((flags & 0x20) === 0){
1931
+ if (offset + 2 > end){
1932
+ throw new Error("not enough data for y delta");
1933
+ }
1934
+ dy = this.dataView.getInt16(offset);
1935
+ offset += 2;
1936
+ }else{
1937
+ dy = 0;
1938
+ }
1939
+ }else{
1940
+ if (offset >= end){
1941
+ throw new Error("not enough data for y delta");
1942
+ }
1943
+ dy = this.data[offset];
1944
+ ++offset;
1945
+ if ((flags & 0x20) === 0){
1946
+ dy = -dy;
1947
+ }
1948
+ }
1949
+ point.y += dy;
1950
+ ++i;
1951
+ }
1952
+ return points;
1953
+ }),
1954
+
1955
+ getPath: function(transform){
1956
+ if (this.data.length < 2 || this.numberOfCountours === 0){
1957
+ return JSPath.init();
1958
+ }
1959
+ var flags;
1960
+ var point;
1961
+ var i, j, k;
1962
+ var p0;
1963
+ var path = JSPath.init();
1964
+ var cp = null;
1965
+ j = 0;
1966
+ var points = this.points;
1967
+ for (i = 0; i < this.numberOfCountours; ++i){
1968
+ flags = this.flags[j];
1969
+ point = points[j];
1970
+ p0 = point;
1971
+ if ((flags & 0x01) === 0){
1972
+ throw new Error("first point is off-curve");
1973
+ }
1974
+ path.moveToPoint(point, transform);
1975
+ ++j;
1976
+ for (k = this.endPointIndexes[i]; j <= k; ++j){
1977
+ flags = this.flags[j];
1978
+ point = points[j];
1979
+ if ((flags & 0x01) === 0){
1980
+ if (cp !== null){
1981
+ path.addQuadraticCurveToPoint(cp.interpolation(point, 0.5), cp, transform);
1982
+ }
1983
+ cp = point;
1984
+ }else{
1985
+ if (cp !== null){
1986
+ path.addQuadraticCurveToPoint(point, cp, transform);
1987
+ }else{
1988
+ path.addLineToPoint(point, transform);
1989
+ }
1990
+ cp = null;
1991
+ }
1992
+ }
1993
+ if (cp !== null){
1994
+ path.addQuadraticCurveToPoint(p0, cp, transform);
1995
+ cp = null;
1996
+ }
1997
+ path.closeSubpath();
1998
+ }
1999
+ return path;
2000
+ },
1817
2001
 
1818
2002
  });
1819
2003
 
1820
- JSClass("FNTOpenTypeCompoundGlyph", JSObject, {
2004
+ JSClass("FNTOpenTypeCompoundGlyph", FNTOpenTypeGlyph, {
1821
2005
 
1822
- initWithData: function(data){
1823
- this.data = data;
1824
- this.dataView = data.dataView();
1825
- },
2006
+ glyf: null,
2007
+ loca: null,
2008
+ locaFormat: null,
1826
2009
 
1827
2010
  updateReferencedGlyphs: function(glyphIndexByOriginalIndex, nextGlyphIndex){
1828
2011
  this.data = JSData.initWithCopyOfData(this.data);
@@ -1859,16 +2042,142 @@ JSClass("FNTOpenTypeCompoundGlyph", JSObject, {
1859
2042
  }
1860
2043
  }while ((flags & 0x20) == 0x20);
1861
2044
  return newGlyphs;
1862
- }
2045
+ },
2046
+
2047
+ getPath: function(transform){
2048
+ if (!transform){
2049
+ transform = JSAffineTransform.Identity;
2050
+ }
2051
+ var points = [];
2052
+ var path = JSPath.init();
2053
+ var offset = 10;
2054
+ var end = this.data.length;
2055
+ var flags;
2056
+ var glyphIndex;
2057
+ var arg1;
2058
+ var arg2;
2059
+ var glyph;
2060
+ var glyphTransform;
2061
+ var glyphPath;
2062
+ var hasMore = true;
2063
+ var p1;
2064
+ var p2;
2065
+ while (hasMore && offset < end){
2066
+ if (offset + 4 > end){
2067
+ throw new Error("not enough data for compound glyph");
2068
+ }
2069
+ flags = this.dataView.getUint16(offset);
2070
+ hasMore = (flags & 0x20) !== 0;
2071
+ offset += 2;
2072
+ glyphIndex = this.dataView.getUint16(offset);
2073
+ offset += 2;
2074
+ glyph = this.glyf.glyph(glyphIndex, this.loca, this.locaFormat);
2075
+ glyphPath = glyph.getPath();
2076
+ glyphTransform = JSAffineTransform.Identity;
2077
+ points = points.concat(glyph.points);
2078
+ if ((flags & 0x01) !== 0){
2079
+ if (offset + 4 > end){
2080
+ throw new Error("not enough data for compound glyph args");
2081
+ }
2082
+ if ((flags & 0x02) !== 0){
2083
+ // x-y values
2084
+ arg1 = this.dataView.getInt16(offset);
2085
+ offset += 2;
2086
+ arg2 = this.dataView.getInt16(offset);
2087
+ offset += 2;
2088
+ }else{
2089
+ // point indexes
2090
+ arg1 = this.dataView.getUint16(offset);
2091
+ offset += 2;
2092
+ arg2 = this.dataView.getUint16(offset);
2093
+ offset += 2;
2094
+ }
2095
+ }else{
2096
+ if (offset + 2 > end){
2097
+ throw new Error("not enough data for compound glyph args");
2098
+ }
2099
+ if ((flags & 0x02) !== 0){
2100
+ // x-y values
2101
+ arg1 = this.dataView.getInt8(offset);
2102
+ ++offset;
2103
+ arg2 = this.dataView.getInt8(offset);
2104
+ ++offset;
2105
+ }else{
2106
+ // point indexes
2107
+ arg1 = this.data[offset];
2108
+ ++offset;
2109
+ arg2 = this.data[offset];
2110
+ ++offset;
2111
+ }
2112
+ }
2113
+ if ((flags & 0x08) !== 0){
2114
+ if (offset + 2 > end){
2115
+ throw new Error("not enough data for compound glyph scale");
2116
+ }
2117
+ glyphTransform.a = this.dataView.getOTFFixedGlyphTranform(offset);
2118
+ glyphTransform.d = glyphTransform.a;
2119
+ offset += 2;
2120
+ }
2121
+ if ((flags & 0x40) !== 0){
2122
+ if (offset + 4 > end){
2123
+ throw new Error("not enough data for compound glyph scales");
2124
+ }
2125
+ glyphTransform.a = this.dataView.getOTFFixedGlyphTranform(offset);
2126
+ offset += 2;
2127
+ glyphTransform.d = this.dataView.getOTFFixedGlyphTranform(offset + 2);
2128
+ offset += 2;
2129
+ }
2130
+ if ((flags & 0x80) !== 0){
2131
+ if (offset + 8 > end){
2132
+ throw new Error("not enough data for compound glyph scale 2x2");
2133
+ }
2134
+ glyphTransform.a = this.dataView.getOTFFixedGlyphTranform(offset);
2135
+ offset += 2;
2136
+ glyphTransform.b = this.dataView.getOTFFixedGlyphTranform(offset);
2137
+ offset += 2;
2138
+ glyphTransform.c = this.dataView.getOTFFixedGlyphTranform(offset);
2139
+ offset += 2;
2140
+ glyphTransform.d = this.dataView.getOTFFixedGlyphTranform(offset);
2141
+ offset += 2;
2142
+ }
2143
+
2144
+ if ((flags & 0x02) !== 0){
2145
+ // x-y values
2146
+ glyphTransform = glyphTransform.translatedBy(arg1, arg2);
2147
+ }else{
2148
+ // point indexes
2149
+ p1 = points[arg1];
2150
+ p2 = glyph.points[arg2];
2151
+ glyphTransform.tx = p1.x - p2.x;
2152
+ glyphTransform.ty = p1.y - p2.y;
2153
+ }
2154
+ path.addPath(glyphPath, glyphTransform.concatenatedWith(transform));
2155
+ }
2156
+ return path;
2157
+ },
1863
2158
 
1864
2159
  });
1865
2160
 
1866
2161
  Object.defineProperties(DataView.prototype, {
1867
2162
 
1868
- getOTFFixed: function(offset){
1869
- var whole = this.getUint16(offset);
1870
- var frac = this.getUint16(offset + 2);
1871
- return whole + frac / 0x10000;
2163
+ getOTFFixed: {
2164
+ value: function DataView_getOTFFixed(offset){
2165
+ var whole = this.getUint16(offset);
2166
+ var frac = this.getUint16(offset + 2);
2167
+ return whole + frac / 0x10000;
2168
+ }
2169
+ },
2170
+
2171
+ getOTFFixedGlyphTranform: {
2172
+ value: function DataView_getOTFFixedGlyphTransform(offset){
2173
+ var n = this.getUint16(offset);
2174
+ var whole = ((n & 0x4000) !== 0) ? 1 : 0;
2175
+ if ((n & 0x8000) !== 0){
2176
+ whole = -whole;
2177
+ }
2178
+ var frac = n & 0x3FFF;
2179
+ return whole + frac / 0x4000;
2180
+ }
1872
2181
  }
1873
2182
 
1874
2183
  });
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.JSKit.FontKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
6
- "JSBundleVersion": "2026.11.0",
6
+ "JSBundleVersion": "2026.27.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
10
10
  "html": "FontKit+HTML.js"
11
11
  },
12
12
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
13
- "GitRevision": "812100aa4c3e5b1ec1d0d432be903bba4db01b0e"
13
+ "GitRevision": "f25bf439a7b48f4536a7f4a44d2f1ec39c46208d"
14
14
  },
15
15
  "Resources": [],
16
16
  "ResourceLookup": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
4
- "JSBundleVersion": "2026.11.0",
4
+ "JSBundleVersion": "2026.27.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "Foundation+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "812100aa4c3e5b1ec1d0d432be903bba4db01b0e"
12
+ "GitRevision": "f25bf439a7b48f4536a7f4a44d2f1ec39c46208d"
13
13
  }
@@ -65,6 +65,7 @@ JSClass("JSAttributedString", JSObject, {
65
65
  case JSAttributedString.Attribute.font:
66
66
  return JSAttributedString.AttributeDecoder.font(value);
67
67
  case JSAttributedString.Attribute.textColor:
68
+ case JSAttributedString.Attribute.strokeColor:
68
69
  case JSAttributedString.Attribute.backgroundColor:
69
70
  return JSAttributedString.AttributeDecoder.color(value);
70
71
  case JSAttributedString.Attribute.link:
@@ -90,6 +91,7 @@ JSClass("JSAttributedString", JSObject, {
90
91
  case JSAttributedString.Attribute.headIndent:
91
92
  case JSAttributedString.Attribute.tailIndent:
92
93
  case JSAttributedString.Attribute.minimumLineHeight:
94
+ case JSAttributedString.Attribute.strokeWidth:
93
95
  return JSAttributedString.AttributeDecoder.number(value);
94
96
  case JSAttributedString.Attribute.textAlignment:
95
97
  return JSAttributedString.AttributeDecoder.enumValue(JSTextAlignment)(value);
@@ -137,6 +139,7 @@ JSClass("JSAttributedString", JSObject, {
137
139
  case JSAttributedString.Attribute.font:
138
140
  return JSAttributedString.AttributeEncoder.font(value);
139
141
  case JSAttributedString.Attribute.textColor:
142
+ case JSAttributedString.Attribute.strokeColor:
140
143
  case JSAttributedString.Attribute.backgroundColor:
141
144
  return JSAttributedString.AttributeEncoder.color(value);
142
145
  case JSAttributedString.Attribute.link:
@@ -162,6 +165,7 @@ JSClass("JSAttributedString", JSObject, {
162
165
  case JSAttributedString.Attribute.lineBreakMode:
163
166
  case JSAttributedString.Attribute.maskCharacter:
164
167
  case JSAttributedString.Attribute.paragraphStyleName:
168
+ case JSAttributedString.Attribute.strokeWidth:
165
169
  default:
166
170
  return value;
167
171
  }
@@ -391,6 +395,49 @@ JSClass("JSAttributedString", JSObject, {
391
395
  this._fixParagraphAttributesAtIndex(range.location + attributedString.string.length);
392
396
  },
393
397
 
398
+ stringWithFallbackFonts: function(fallbackFontDescriptors, defaultFont, usedDescriptors){
399
+ var attributedString = this;
400
+ var runIterator = attributedString.runIterator();
401
+ var unicodeIterator;
402
+ var end = attributedString.string.length;
403
+ var font;
404
+ var glyph;
405
+ var descriptor;
406
+ var i, l;
407
+ var descriptorFonts = [];
408
+ for (i = 0, l = fallbackFontDescriptors.length; i < l; ++i){
409
+ descriptorFonts.push({});
410
+ }
411
+ for (; runIterator.range.location < end; runIterator.increment()){
412
+ font = runIterator.attributes.font || defaultFont;
413
+ unicodeIterator = attributedString.string.unicodeIterator(0);
414
+ for (; unicodeIterator.character !== null; unicodeIterator.increment()){
415
+ glyph = font.glyphForCharacter(unicodeIterator.character);
416
+ if (glyph === 0){
417
+ for (i = 0, l = fallbackFontDescriptors.length && glyph === 0; i < l; ++i){
418
+ descriptor = fallbackFontDescriptors[i];
419
+ glyph = descriptor.glyphForCharacter(unicodeIterator.character);
420
+ if (glyph !== 0){
421
+ if (usedDescriptors !== undefined){
422
+ if (usedDescriptors.indexOf(descriptor) < 0){
423
+ usedDescriptors.push(descriptor);
424
+ }
425
+ }
426
+ if (attributedString === this){
427
+ attributedString = JSAttributedString.initWithAttributedString(attributedString);
428
+ }
429
+ if (descriptorFonts[i][font.pointSize] === undefined){
430
+ descriptorFonts[i][font.pointSize] = JSFont.initWithDescriptor(descriptor, font.pointSize);
431
+ }
432
+ attributedString.addAttributeInRange("font", descriptorFonts[i][font.pointSize], JSRange(unicodeIterator.index, unicodeIterator.nextIndex - unicodeIterator.index));
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ return attributedString;
439
+ },
440
+
394
441
  // MARK: - Attribute mutations
395
442
 
396
443
  setAttributesInRange: function(attributes, range){
@@ -680,6 +727,7 @@ JSClass("JSAttributedString", JSObject, {
680
727
  JSAttributedString.Attribute = {
681
728
  font: "font",
682
729
  textColor: "textColor",
730
+ strokeColor: "strokeColor",
683
731
  backgroundColor: "backgroundColor",
684
732
  bold: "bold",
685
733
  italic: "italic",
@@ -689,6 +737,7 @@ JSAttributedString.Attribute = {
689
737
  maskCharacter: "maskCharacter",
690
738
  cursor: "cursor",
691
739
  link: "link",
740
+ strokeWidth: "strokeWidth",
692
741
 
693
742
  // Paragraph
694
743
  paragraphStyleName: "paragraphStyleName",
@@ -745,7 +794,13 @@ JSAttributedString.AttributeDecoder = {
745
794
  dataBase64URL: function(value){ return value.dataByDecodingBase64URL(); },
746
795
  dataHex: function(value){ return value.dataByDecodingHex(); },
747
796
  url: function(value){ return JSURL.initWithString(value); },
748
- font: function(value){ return JSFont.initFromDictionary(value); },
797
+ font: function(value){
798
+ var font = JSFont.initFromDictionary(value);
799
+ if (font === null){
800
+ font = JSFont.initWithDescriptor(JSFontDescriptor.system, value.size);
801
+ }
802
+ return font;
803
+ },
749
804
  color: function(value){ return JSColor.initFromDictionary(value); },
750
805
  boolean: function(value){ return typeof(value) === "boolean" ? value : undefined; },
751
806
  number: function(value){ return typeof(value) === "number" ? value : undefined; },