diff --git a/packages/canvas-pixi/index.ts b/packages/canvas-pixi/index.ts index a241be2f..976db47d 100644 --- a/packages/canvas-pixi/index.ts +++ b/packages/canvas-pixi/index.ts @@ -1,7 +1,7 @@ import '@nativescript/canvas-polyfill'; import { Canvas } from '@nativescript/canvas'; import { knownFolders } from '@nativescript/core'; -import { Adapter, Assets, CanvasSource, DOMAdapter, EventSystem } from 'pixi.js'; +import { Adapter, Assets, CanvasSource, CanvasTextMetrics, DOMAdapter, EventSystem } from 'pixi.js'; const NSCAdapter: Adapter = { createCanvas(width?: number, height?: number) { @@ -65,6 +65,10 @@ if (typeof resizeCanvas === 'function') { Assets.setPreferences({ preferWorkers: false }); +// PixiJS may have picked Intl.Segmenter before the polyfill hid it. +if (typeof (Intl as any).Segmenter !== 'function') { + (CanvasTextMetrics as any).graphemeSegmenter = (text: string) => [...text]; + // Pixi listens for pointermove/up on document and globalThis, which a native canvas never raises: listen on it too. const events = EventSystem.prototype as any; const addEvents = events._addEvents; diff --git a/packages/canvas-polyfill/index.ts b/packages/canvas-polyfill/index.ts index 10343567..5a30f27b 100644 --- a/packages/canvas-polyfill/index.ts +++ b/packages/canvas-polyfill/index.ts @@ -65,6 +65,50 @@ if (typeof (global as any).Intl === 'undefined') { (global as any).window.Intl = (global as any).Intl; } +// Linux V8 ships Intl.Segmenter without ICU data and segment() crashes, so hide it. +{ + const intl = (global as any).Intl; + if (typeof intl?.Segmenter === 'function') { + let usable = false; + try { + usable = intl.Segmenter.supportedLocalesOf(['en']).length > 0; + } catch (e) {} + if (!usable) { + delete intl.Segmenter; + } + } +} + +// Without ICU data toLocaleString throws; fall back to en-US formatting. +{ + let icu = true; + try { + (1234.5).toLocaleString(); + } catch (e) { + icu = false; + } + if (!icu) { + Number.prototype.toLocaleString = function (this: number, _locales?: unknown, options?: Intl.NumberFormatOptions) { + let value = Number(this); + if (!Number.isFinite(value)) { + return Number.isNaN(value) ? 'NaN' : value > 0 ? '∞' : '-∞'; + } + const percent = options?.style === 'percent'; + if (percent) { + value *= 100; + } + const min = options?.minimumFractionDigits ?? 0; + const max = Math.max(min, options?.maximumFractionDigits ?? (percent ? 0 : 3)); + let [whole, fraction = ''] = Math.abs(value).toFixed(max).split('.'); + fraction = fraction.replace(/0+$/, '').padEnd(min, '0'); + if (options?.useGrouping !== false) { + whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ','); + } + return `${value < 0 ? '-' : ''}${whole}${fraction ? `.${fraction}` : ''}${percent ? '%' : ''}`; + }; + } +} + import { MutationObserver } from './MutationObserver'; if (!global.MutationObserver) {