diff --git a/bundles/pixi.js-node/package.json b/bundles/pixi.js-node/package.json index 58c6f483ca..2e16c20a97 100644 --- a/bundles/pixi.js-node/package.json +++ b/bundles/pixi.js-node/package.json @@ -67,11 +67,10 @@ "@pixi/text": "7.1.0-alpha", "@pixi/text-bitmap": "7.1.0-alpha", "@types/gl": "^6.0.2", - "@types/xml2js": "^0.4.11", + "@xmldom/xmldom": "^0.8.6", "canvas": "^2.9.1", "cross-fetch": "^3.1.5", - "gl": "^6.0.1", - "xml2js": "^0.4.23" + "gl": "^6.0.1" }, "nodeDependencies": [ "fs", diff --git a/bundles/pixi.js-node/src/adapter/adapter.ts b/bundles/pixi.js-node/src/adapter/adapter.ts index e5799acf78..9ba10d88b2 100644 --- a/bundles/pixi.js-node/src/adapter/adapter.ts +++ b/bundles/pixi.js-node/src/adapter/adapter.ts @@ -3,6 +3,7 @@ import fs from 'fs'; import { WebGLRenderingContext } from 'gl'; import { settings, utils } from '@pixi/core'; import { NodeCanvasElement } from './NodeCanvasElement'; +import { DOMParser } from '@xmldom/xmldom'; import type { IAdapter } from '@pixi/core'; @@ -53,6 +54,12 @@ export const NodeAdapter = { }); }); }, + parseXML: (xml: string) => + { + const parser = new DOMParser(); + + return parser.parseFromString(xml, 'text/xml'); + }, } as IAdapter; settings.ADAPTER = NodeAdapter; diff --git a/bundles/pixi.js-node/src/adapter/index.ts b/bundles/pixi.js-node/src/adapter/index.ts index 233773f583..66c018ec5b 100644 --- a/bundles/pixi.js-node/src/adapter/index.ts +++ b/bundles/pixi.js-node/src/adapter/index.ts @@ -2,7 +2,6 @@ import './polyfills'; export * from './NodeCanvasElement'; export * from './NodeCanvasResource'; -export * from './loadNodeBitmapFont'; export * from './loadNodeFont'; export * from './loadNodeTexture'; export * from './loadNodeBase64'; diff --git a/bundles/pixi.js-node/src/adapter/loadNodeBitmapFont.ts b/bundles/pixi.js-node/src/adapter/loadNodeBitmapFont.ts deleted file mode 100644 index 44c396a3a9..0000000000 --- a/bundles/pixi.js-node/src/adapter/loadNodeBitmapFont.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { parseStringPromise } from 'xml2js'; -import { extensions, ExtensionType, settings, utils } from '@pixi/core'; -import { BitmapFont, BitmapFontData, TextFormat, XMLStringFormat } from '@pixi/text-bitmap'; - -import type { LoadAsset, Loader, LoaderParser } from '@pixi/assets'; -import type { Texture } from '@pixi/core'; -import type { IBitmapFontRawData } from '@pixi/text-bitmap'; - -interface XMLRawJson -{ - font: { - info: Array<{$: IBitmapFontRawData['info'][0]}>, - common: Array<{$: IBitmapFontRawData['common'][0]}>, - pages: Array<{page: [{ $: IBitmapFontRawData['page'][0]}]}> - chars: Array<{$: {count: number}, char: [{ $: IBitmapFontRawData['char'][0]}]}> - kernings?: Array<{$: {count: number}, kerning: [{ $: IBitmapFontRawData['kerning'][0]}]}> - distanceField?: Array<{$: IBitmapFontRawData['distanceField'][0]}>, - } -} - -/** - * Parses a xml json into a BitmapFontData object. - * @param xml - The xml data to parse. - */ -function xmlJsonParser(xml: XMLRawJson) -{ - const data = new BitmapFontData(); - const font = xml.font; - const info = font.info; - const common = font.common; - const pages = font.pages; - const chars = font.chars; - const kernings = font.kernings; - const distanceField = font.distanceField; - - info.forEach((element) => - { - data.info.push({ - face: element.$.face, - size: parseInt(element.$.size, 10), - }); - }); - - common.forEach((element) => - { - data.common.push({ - lineHeight: parseInt(element.$.lineHeight, 10) - }); - }); - - pages.forEach((element) => - { - element.page.forEach((page) => - { - data.page.push({ - id: parseInt(page.$.id, 10), - file: page.$.file, - }); - }); - }); - - chars.forEach((info) => - { - const charArr = info.char; - - charArr.forEach((char) => - { - data.char.push({ - id: parseInt(char.$.id, 10), - page: parseInt(char.$.page, 10), - x: parseInt(char.$.x, 10), - y: parseInt(char.$.y, 10), - width: parseInt(char.$.width, 10), - height: parseInt(char.$.height, 10), - xoffset: parseInt(char.$.xoffset, 10), - yoffset: parseInt(char.$.yoffset, 10), - xadvance: parseInt(char.$.xadvance, 10), - }); - }); - }); - - kernings?.forEach((info) => - { - info.kerning?.forEach((kerning) => - data.kerning.push({ - first: parseInt(kerning.$.first, 10), - second: parseInt(kerning.$.second, 10), - amount: parseInt(kerning.$.amount, 10), - }) - ); - }); - - distanceField?.forEach((df) => - { - data.distanceField.push({ - distanceRange: parseInt(df.$.distanceRange, 10), - fieldType: df.$.fieldType, - }); - }); - - return data; -} - -/** - * Does the actual loading of the bitmap font data. - * @param src - The url of the font file. - * @param data - The bitmap font data for the file. - * @param loader - The loader instance. - */ -async function _loadBitmap(src: string, data: BitmapFontData, loader: Loader) -{ - const pages = data.page; - - const textureUrls = []; - - for (let i = 0; i < pages.length; ++i) - { - const pageFile = pages[i].file; - const url = utils.path.join(utils.path.dirname(src), pageFile); - - textureUrls.push(url); - } - - const loadedTextures = await loader.load(textureUrls); - const textures = textureUrls.map((url) => loadedTextures[url]); - - return BitmapFont.install(data, textures, true); -} - -/** - * Checks if the given string is an xml string. - * Performs the same check as the XMLStringFormat/XMLFormat class. - * @param data - the string data to test. - */ -async function xmlStringFormatTest(data: string): Promise -{ - if (typeof data === 'string' && data.includes('')) - { - const xml = xmlJsonParser(await parseStringPromise(data)); - - return xml.page.length > 0 && xml.info[0].face !== null; - } - - return false; -} - -const validExtensions = ['.xml', '.fnt']; - -/** simple loader plugin for loading in bitmap fonts! */ -export const loadNodeBitmapFont = { - extension: ExtensionType.LoadParser, - - test(url: string): boolean - { - return validExtensions.includes(utils.path.extname(url)); - }, - - async testParse(data: string): Promise - { - const isText = TextFormat.test(data); - const isXMLText = await xmlStringFormatTest(data); - - return isText || isXMLText; - }, - - async parse(asset: string, data: LoadAsset, loader: Loader): Promise - { - const isText = TextFormat.test(asset); - - if (isText) - { - const parsed = TextFormat.parse(asset); - - return await _loadBitmap(data.src, parsed, loader); - } - - return await _loadBitmap(data.src, XMLStringFormat.parse(asset), loader); - }, - - async load(url: string, _asset: LoadAsset, loader: Loader): Promise - { - const response = await settings.ADAPTER.fetch(url); - - const text = await response.text(); - - const data = xmlJsonParser(await parseStringPromise(text)); - - return await _loadBitmap(url, data, loader); - }, - - unload(bitmapFont: BitmapFont): void - { - bitmapFont.destroy(); - } -} as LoaderParser; - -extensions.add(loadNodeBitmapFont); diff --git a/bundles/pixi.js-node/src/index.ts b/bundles/pixi.js-node/src/index.ts index bb5cafc3f4..6fafab5f2c 100644 --- a/bundles/pixi.js-node/src/index.ts +++ b/bundles/pixi.js-node/src/index.ts @@ -14,13 +14,11 @@ import '@pixi/mixin-get-global-position'; import { NodeCanvasResource } from './adapter'; import { loadTextures, loadWebFont } from '@pixi/assets'; import { ResizePlugin } from '@pixi/app'; -import { loadBitmapFont } from '@pixi/text-bitmap'; // Remove the default loader plugins extensions.remove( loadTextures, loadWebFont, - loadBitmapFont, ResizePlugin ); diff --git a/bundles/pixi.js-webworker/package.json b/bundles/pixi.js-webworker/package.json index 64a3ea1498..1367bb01b3 100644 --- a/bundles/pixi.js-webworker/package.json +++ b/bundles/pixi.js-webworker/package.json @@ -62,7 +62,8 @@ "@pixi/sprite-tiling": "7.1.0-alpha", "@pixi/spritesheet": "7.1.0-alpha", "@pixi/text": "7.1.0-alpha", - "@pixi/text-bitmap": "7.1.0-alpha" + "@pixi/text-bitmap": "7.1.0-alpha", + "@xmldom/xmldom": "^0.8.6" }, "publishConfig": { "access": "public" diff --git a/bundles/pixi.js-webworker/src/adapter.ts b/bundles/pixi.js-webworker/src/adapter.ts index 635fc35ec5..b99f28e63c 100644 --- a/bundles/pixi.js-webworker/src/adapter.ts +++ b/bundles/pixi.js-webworker/src/adapter.ts @@ -1,4 +1,5 @@ import { settings } from '@pixi/core'; +import { DOMParser } from '@xmldom/xmldom'; import type { IAdapter } from '@pixi/core'; @@ -15,6 +16,13 @@ export const WebWorkerAdapter = { getBaseUrl: () => globalThis.location.href, getFontFaceSet: () => (globalThis as unknown as WorkerGlobalScope).fonts, fetch: (url: RequestInfo, options?: RequestInit) => fetch(url, options), + parseXML: (xml: string) => + { + const parser = new DOMParser(); + + return parser.parseFromString(xml, 'text/xml'); + }, + } as IAdapter; settings.ADAPTER = WebWorkerAdapter; diff --git a/package-lock.json b/package-lock.json index db03b2432e..9e082077e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,11 +146,10 @@ "@pixi/text": "7.1.0-alpha", "@pixi/text-bitmap": "7.1.0-alpha", "@types/gl": "^6.0.2", - "@types/xml2js": "^0.4.11", + "@xmldom/xmldom": "^0.8.6", "canvas": "^2.9.1", "cross-fetch": "^3.1.5", - "gl": "^6.0.1", - "xml2js": "^0.4.23" + "gl": "^6.0.1" }, "engines": { "node": ">=16" @@ -187,7 +186,8 @@ "@pixi/sprite-tiling": "7.1.0-alpha", "@pixi/spritesheet": "7.1.0-alpha", "@pixi/text": "7.1.0-alpha", - "@pixi/text-bitmap": "7.1.0-alpha" + "@pixi/text-bitmap": "7.1.0-alpha", + "@xmldom/xmldom": "^0.8.6" } }, "node_modules/@ampproject/remapping": { @@ -6183,6 +6183,7 @@ }, "node_modules/@types/node": { "version": "12.20.24", + "dev": true, "license": "MIT" }, "node_modules/@types/normalize-package-data": { @@ -6217,13 +6218,6 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, - "node_modules/@types/xml2js": { - "version": "0.4.11", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", @@ -7844,6 +7838,14 @@ "@babel/plugin-transform-flow-comments": "7.17.12" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz", + "integrity": "sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -23330,6 +23332,7 @@ }, "node_modules/sax": { "version": "1.2.4", + "dev": true, "license": "ISC" }, "node_modules/saxes": { @@ -25761,24 +25764,6 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, - "node_modules/xml2js": { - "version": "0.4.23", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -30261,11 +30246,10 @@ "@pixi/text": "7.1.0-alpha", "@pixi/text-bitmap": "7.1.0-alpha", "@types/gl": "^6.0.2", - "@types/xml2js": "^0.4.11", + "@xmldom/xmldom": "^0.8.6", "canvas": "^2.9.1", "cross-fetch": "^3.1.5", - "gl": "^6.0.1", - "xml2js": "^0.4.23" + "gl": "^6.0.1" } }, "@pixi/particle-container": { @@ -30374,7 +30358,8 @@ "@pixi/sprite-tiling": "7.1.0-alpha", "@pixi/spritesheet": "7.1.0-alpha", "@pixi/text": "7.1.0-alpha", - "@pixi/text-bitmap": "7.1.0-alpha" + "@pixi/text-bitmap": "7.1.0-alpha", + "@xmldom/xmldom": "^0.8.6" } }, "@rollup/plugin-commonjs": { @@ -30687,7 +30672,8 @@ "dev": true }, "@types/node": { - "version": "12.20.24" + "version": "12.20.24", + "dev": true }, "@types/normalize-package-data": { "version": "2.4.0", @@ -30718,12 +30704,6 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, - "@types/xml2js": { - "version": "0.4.11", - "requires": { - "@types/node": "*" - } - }, "@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", @@ -31849,6 +31829,11 @@ "@babel/plugin-transform-flow-comments": "7.17.12" } }, + "@xmldom/xmldom": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz", + "integrity": "sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==" + }, "abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -42731,7 +42716,8 @@ } }, "sax": { - "version": "1.2.4" + "version": "1.2.4", + "dev": true }, "saxes": { "version": "5.0.1", @@ -44379,16 +44365,6 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, - "xml2js": { - "version": "0.4.23", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1" - }, "xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/packages/settings/src/adapter.ts b/packages/settings/src/adapter.ts index ac06beef3f..e533929ec1 100644 --- a/packages/settings/src/adapter.ts +++ b/packages/settings/src/adapter.ts @@ -18,6 +18,7 @@ export interface IAdapter getBaseUrl: () => string; getFontFaceSet: () => FontFaceSet | null; fetch: (url: RequestInfo, options?: RequestInit) => Promise; + parseXML: (xml: string) => Document; } export const BrowserAdapter = { @@ -41,4 +42,10 @@ export const BrowserAdapter = { getBaseUrl: () => (document.baseURI ?? window.location.href), getFontFaceSet: () => document.fonts, fetch: (url: RequestInfo, options?: RequestInit) => fetch(url, options), + parseXML: (xml: string) => + { + const parser = new DOMParser(); + + return parser.parseFromString(xml, 'text/xml'); + }, } as IAdapter; diff --git a/packages/text-bitmap/src/formats/XMLFormat.ts b/packages/text-bitmap/src/formats/XMLFormat.ts index adc33e4367..b7f0433997 100644 --- a/packages/text-bitmap/src/formats/XMLFormat.ts +++ b/packages/text-bitmap/src/formats/XMLFormat.ts @@ -13,9 +13,11 @@ export class XMLFormat */ static test(data: unknown): boolean { - return data instanceof XMLDocument - && data.getElementsByTagName('page').length - && data.getElementsByTagName('info')[0].getAttribute('face') !== null; + const xml = data as Document; + + return 'getElementsByTagName' in xml + && xml.getElementsByTagName('page').length + && xml.getElementsByTagName('info')[0].getAttribute('face') !== null; } /** @@ -23,7 +25,7 @@ export class XMLFormat * @param xml * @returns - Data to use for BitmapFont */ - static parse(xml: XMLDocument): BitmapFontData + static parse(xml: Document): BitmapFontData { const data = new BitmapFontData(); const info = xml.getElementsByTagName('info'); diff --git a/packages/text-bitmap/src/formats/XMLStringFormat.ts b/packages/text-bitmap/src/formats/XMLStringFormat.ts index cffc0c2177..5ae9e228ec 100644 --- a/packages/text-bitmap/src/formats/XMLStringFormat.ts +++ b/packages/text-bitmap/src/formats/XMLStringFormat.ts @@ -1,3 +1,4 @@ +import { settings } from '@pixi/core'; import type { BitmapFontData } from '../BitmapFontData'; import { XMLFormat } from './XMLFormat'; @@ -16,9 +17,7 @@ export class XMLStringFormat { if (typeof data === 'string' && data.includes('')) { - const xml = new globalThis.DOMParser().parseFromString(data, 'text/xml'); - - return XMLFormat.test(xml); + return XMLFormat.test(settings.ADAPTER.parseXML(data)); } return false; @@ -31,8 +30,6 @@ export class XMLStringFormat */ static parse(xmlTxt: string): BitmapFontData { - const xml = new globalThis.DOMParser().parseFromString(xmlTxt, 'text/xml'); - - return XMLFormat.parse(xml); + return XMLFormat.parse(settings.ADAPTER.parseXML(xmlTxt)); } }