diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 96d9f44..b505250 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -503,10 +503,10 @@ * * @static * @param {XMLDocument} xml - The XML document data. - * @param {PIXI.Texture} texture - Texture with all symbols. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. * @return {Object} Result font object with font, size, lineHeight and char fields. */ - static registerFont(xml, texture) + static registerFont(xml, textures) { const data = {}; const info = xml.getElementsByTagName('info')[0]; @@ -518,29 +518,44 @@ data.size = parseInt(info.getAttribute('size'), 10); data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res; data.chars = {}; + if (!(textures instanceof Array)) + { + textures = [textures]; + } // parse letters const letters = xml.getElementsByTagName('char'); + let page; for (let i = 0; i < letters.length; i++) { const letter = letters[i]; const charCode = parseInt(letter.getAttribute('id'), 10); + let textureRect; - const textureRect = new core.Rectangle( - (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res), - (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res), - parseInt(letter.getAttribute('width'), 10) / res, - parseInt(letter.getAttribute('height'), 10) / res - ); + page = parseInt(letter.getAttribute('page'), 10); + if (isNaN(page)) + { + textureRect = new core.Rectangle(0, 0, 0, 0); + page = 0; + } + else + { + textureRect = new core.Rectangle( + (parseInt(letter.getAttribute('x'), 10) / res) + (textures[page].frame.x / res), + (parseInt(letter.getAttribute('y'), 10) / res) + (textures[page].frame.y / res), + parseInt(letter.getAttribute('width'), 10) / res, + parseInt(letter.getAttribute('height'), 10) / res + ); + } data.chars[charCode] = { xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res, yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res, xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res, kerning: {}, - texture: new core.Texture(texture.baseTexture, textureRect), - + texture: new core.Texture(textures[page].baseTexture, textureRect), + page, }; } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 96d9f44..b505250 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -503,10 +503,10 @@ * * @static * @param {XMLDocument} xml - The XML document data. - * @param {PIXI.Texture} texture - Texture with all symbols. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. * @return {Object} Result font object with font, size, lineHeight and char fields. */ - static registerFont(xml, texture) + static registerFont(xml, textures) { const data = {}; const info = xml.getElementsByTagName('info')[0]; @@ -518,29 +518,44 @@ data.size = parseInt(info.getAttribute('size'), 10); data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res; data.chars = {}; + if (!(textures instanceof Array)) + { + textures = [textures]; + } // parse letters const letters = xml.getElementsByTagName('char'); + let page; for (let i = 0; i < letters.length; i++) { const letter = letters[i]; const charCode = parseInt(letter.getAttribute('id'), 10); + let textureRect; - const textureRect = new core.Rectangle( - (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res), - (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res), - parseInt(letter.getAttribute('width'), 10) / res, - parseInt(letter.getAttribute('height'), 10) / res - ); + page = parseInt(letter.getAttribute('page'), 10); + if (isNaN(page)) + { + textureRect = new core.Rectangle(0, 0, 0, 0); + page = 0; + } + else + { + textureRect = new core.Rectangle( + (parseInt(letter.getAttribute('x'), 10) / res) + (textures[page].frame.x / res), + (parseInt(letter.getAttribute('y'), 10) / res) + (textures[page].frame.y / res), + parseInt(letter.getAttribute('width'), 10) / res, + parseInt(letter.getAttribute('height'), 10) / res + ); + } data.chars[charCode] = { xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res, yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res, xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res, kerning: {}, - texture: new core.Texture(texture.baseTexture, textureRect), - + texture: new core.Texture(textures[page].baseTexture, textureRect), + page, }; } diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 9691dd0..99e0c02 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -9,11 +9,11 @@ * @function parseBitmapFontData * @memberof PIXI.loaders * @param {PIXI.loaders.Resource} resource - Loader resource. - * @param {PIXI.Texture} texture - Reference to texture. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. */ -export function parse(resource, texture) +export function parse(resource, textures) { - resource.bitmapFont = BitmapText.registerFont(resource.data, texture); + resource.bitmapFont = BitmapText.registerFont(resource.data, textures); } export default function () @@ -67,29 +67,53 @@ xmlUrl += '/'; } - const textureUrl = xmlUrl + resource.data.getElementsByTagName('page')[0].getAttribute('file'); + const pages = resource.data.getElementsByTagName('page'); + const textures = []; + const loadOptions = { + crossOrigin: resource.crossOrigin, + loadType: Resource.LOAD_TYPE.IMAGE, + metadata: resource.metadata.imageMetadata, + parentResource: resource, + }; - if (utils.TextureCache[textureUrl]) + for (let x = 0; x < pages.length; ++x) { - // reuse existing texture - parse(resource, utils.TextureCache[textureUrl]); - next(); - } - else - { - const loadOptions = { - crossOrigin: resource.crossOrigin, - loadType: Resource.LOAD_TYPE.IMAGE, - metadata: resource.metadata.imageMetadata, - parentResource: resource, - }; + const textureUrl = xmlUrl + pages[x].getAttribute('file'); - // load the texture for the font - this.add(`${resource.name}_image`, textureUrl, loadOptions, (res) => + if (utils.TextureCache[textureUrl]) { - parse(resource, res.texture); - next(); - }); + textures.push(utils.TextureCache[textureUrl]); + } + else + { + // load the texture for the font + this.add(`${resource.name}_image${x}`, textureUrl, loadOptions, () => + { + const nextTextures = []; + + for (let x = 0; x < pages.length; ++x) + { + const nextTextureUrl = xmlUrl + pages[x].getAttribute('file'); + + if (utils.TextureCache[nextTextureUrl]) + { + nextTextures.push(utils.TextureCache[nextTextureUrl]); + } + else + { + return; + } + } + parse(resource, nextTextures); + next(); + }); + } + } + + if (textures.length === pages.length) + { + parse(resource, textures); + next(); } }; } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 96d9f44..b505250 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -503,10 +503,10 @@ * * @static * @param {XMLDocument} xml - The XML document data. - * @param {PIXI.Texture} texture - Texture with all symbols. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. * @return {Object} Result font object with font, size, lineHeight and char fields. */ - static registerFont(xml, texture) + static registerFont(xml, textures) { const data = {}; const info = xml.getElementsByTagName('info')[0]; @@ -518,29 +518,44 @@ data.size = parseInt(info.getAttribute('size'), 10); data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res; data.chars = {}; + if (!(textures instanceof Array)) + { + textures = [textures]; + } // parse letters const letters = xml.getElementsByTagName('char'); + let page; for (let i = 0; i < letters.length; i++) { const letter = letters[i]; const charCode = parseInt(letter.getAttribute('id'), 10); + let textureRect; - const textureRect = new core.Rectangle( - (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res), - (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res), - parseInt(letter.getAttribute('width'), 10) / res, - parseInt(letter.getAttribute('height'), 10) / res - ); + page = parseInt(letter.getAttribute('page'), 10); + if (isNaN(page)) + { + textureRect = new core.Rectangle(0, 0, 0, 0); + page = 0; + } + else + { + textureRect = new core.Rectangle( + (parseInt(letter.getAttribute('x'), 10) / res) + (textures[page].frame.x / res), + (parseInt(letter.getAttribute('y'), 10) / res) + (textures[page].frame.y / res), + parseInt(letter.getAttribute('width'), 10) / res, + parseInt(letter.getAttribute('height'), 10) / res + ); + } data.chars[charCode] = { xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res, yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res, xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res, kerning: {}, - texture: new core.Texture(texture.baseTexture, textureRect), - + texture: new core.Texture(textures[page].baseTexture, textureRect), + page, }; } diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 9691dd0..99e0c02 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -9,11 +9,11 @@ * @function parseBitmapFontData * @memberof PIXI.loaders * @param {PIXI.loaders.Resource} resource - Loader resource. - * @param {PIXI.Texture} texture - Reference to texture. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. */ -export function parse(resource, texture) +export function parse(resource, textures) { - resource.bitmapFont = BitmapText.registerFont(resource.data, texture); + resource.bitmapFont = BitmapText.registerFont(resource.data, textures); } export default function () @@ -67,29 +67,53 @@ xmlUrl += '/'; } - const textureUrl = xmlUrl + resource.data.getElementsByTagName('page')[0].getAttribute('file'); + const pages = resource.data.getElementsByTagName('page'); + const textures = []; + const loadOptions = { + crossOrigin: resource.crossOrigin, + loadType: Resource.LOAD_TYPE.IMAGE, + metadata: resource.metadata.imageMetadata, + parentResource: resource, + }; - if (utils.TextureCache[textureUrl]) + for (let x = 0; x < pages.length; ++x) { - // reuse existing texture - parse(resource, utils.TextureCache[textureUrl]); - next(); - } - else - { - const loadOptions = { - crossOrigin: resource.crossOrigin, - loadType: Resource.LOAD_TYPE.IMAGE, - metadata: resource.metadata.imageMetadata, - parentResource: resource, - }; + const textureUrl = xmlUrl + pages[x].getAttribute('file'); - // load the texture for the font - this.add(`${resource.name}_image`, textureUrl, loadOptions, (res) => + if (utils.TextureCache[textureUrl]) { - parse(resource, res.texture); - next(); - }); + textures.push(utils.TextureCache[textureUrl]); + } + else + { + // load the texture for the font + this.add(`${resource.name}_image${x}`, textureUrl, loadOptions, () => + { + const nextTextures = []; + + for (let x = 0; x < pages.length; ++x) + { + const nextTextureUrl = xmlUrl + pages[x].getAttribute('file'); + + if (utils.TextureCache[nextTextureUrl]) + { + nextTextures.push(utils.TextureCache[nextTextureUrl]); + } + else + { + return; + } + } + parse(resource, nextTextures); + next(); + }); + } + } + + if (textures.length === pages.length) + { + parse(resource, textures); + next(); } }; } diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 70703fc..eb2e34f 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -307,6 +307,69 @@ done(); }); }); + + it('should properly register bitmap font having more than one texture', function (done) + { + const ldr = new PIXI.loaders.Loader(); + + ldr.add(`${__dirname}/resources/split_font.fnt`); + ldr.load(() => + { + const font = PIXI.extras.BitmapText.fonts.split_font; + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.split_font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + let src = charA.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + src = charB.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + src = charC.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charC.texture.frame.x).to.equal(2); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + src = charD.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charD.texture.frame.x).to.equal(2); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 96d9f44..b505250 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -503,10 +503,10 @@ * * @static * @param {XMLDocument} xml - The XML document data. - * @param {PIXI.Texture} texture - Texture with all symbols. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. * @return {Object} Result font object with font, size, lineHeight and char fields. */ - static registerFont(xml, texture) + static registerFont(xml, textures) { const data = {}; const info = xml.getElementsByTagName('info')[0]; @@ -518,29 +518,44 @@ data.size = parseInt(info.getAttribute('size'), 10); data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res; data.chars = {}; + if (!(textures instanceof Array)) + { + textures = [textures]; + } // parse letters const letters = xml.getElementsByTagName('char'); + let page; for (let i = 0; i < letters.length; i++) { const letter = letters[i]; const charCode = parseInt(letter.getAttribute('id'), 10); + let textureRect; - const textureRect = new core.Rectangle( - (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res), - (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res), - parseInt(letter.getAttribute('width'), 10) / res, - parseInt(letter.getAttribute('height'), 10) / res - ); + page = parseInt(letter.getAttribute('page'), 10); + if (isNaN(page)) + { + textureRect = new core.Rectangle(0, 0, 0, 0); + page = 0; + } + else + { + textureRect = new core.Rectangle( + (parseInt(letter.getAttribute('x'), 10) / res) + (textures[page].frame.x / res), + (parseInt(letter.getAttribute('y'), 10) / res) + (textures[page].frame.y / res), + parseInt(letter.getAttribute('width'), 10) / res, + parseInt(letter.getAttribute('height'), 10) / res + ); + } data.chars[charCode] = { xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res, yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res, xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res, kerning: {}, - texture: new core.Texture(texture.baseTexture, textureRect), - + texture: new core.Texture(textures[page].baseTexture, textureRect), + page, }; } diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 9691dd0..99e0c02 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -9,11 +9,11 @@ * @function parseBitmapFontData * @memberof PIXI.loaders * @param {PIXI.loaders.Resource} resource - Loader resource. - * @param {PIXI.Texture} texture - Reference to texture. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. */ -export function parse(resource, texture) +export function parse(resource, textures) { - resource.bitmapFont = BitmapText.registerFont(resource.data, texture); + resource.bitmapFont = BitmapText.registerFont(resource.data, textures); } export default function () @@ -67,29 +67,53 @@ xmlUrl += '/'; } - const textureUrl = xmlUrl + resource.data.getElementsByTagName('page')[0].getAttribute('file'); + const pages = resource.data.getElementsByTagName('page'); + const textures = []; + const loadOptions = { + crossOrigin: resource.crossOrigin, + loadType: Resource.LOAD_TYPE.IMAGE, + metadata: resource.metadata.imageMetadata, + parentResource: resource, + }; - if (utils.TextureCache[textureUrl]) + for (let x = 0; x < pages.length; ++x) { - // reuse existing texture - parse(resource, utils.TextureCache[textureUrl]); - next(); - } - else - { - const loadOptions = { - crossOrigin: resource.crossOrigin, - loadType: Resource.LOAD_TYPE.IMAGE, - metadata: resource.metadata.imageMetadata, - parentResource: resource, - }; + const textureUrl = xmlUrl + pages[x].getAttribute('file'); - // load the texture for the font - this.add(`${resource.name}_image`, textureUrl, loadOptions, (res) => + if (utils.TextureCache[textureUrl]) { - parse(resource, res.texture); - next(); - }); + textures.push(utils.TextureCache[textureUrl]); + } + else + { + // load the texture for the font + this.add(`${resource.name}_image${x}`, textureUrl, loadOptions, () => + { + const nextTextures = []; + + for (let x = 0; x < pages.length; ++x) + { + const nextTextureUrl = xmlUrl + pages[x].getAttribute('file'); + + if (utils.TextureCache[nextTextureUrl]) + { + nextTextures.push(utils.TextureCache[nextTextureUrl]); + } + else + { + return; + } + } + parse(resource, nextTextures); + next(); + }); + } + } + + if (textures.length === pages.length) + { + parse(resource, textures); + next(); } }; } diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 70703fc..eb2e34f 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -307,6 +307,69 @@ done(); }); }); + + it('should properly register bitmap font having more than one texture', function (done) + { + const ldr = new PIXI.loaders.Loader(); + + ldr.add(`${__dirname}/resources/split_font.fnt`); + ldr.load(() => + { + const font = PIXI.extras.BitmapText.fonts.split_font; + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.split_font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + let src = charA.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + src = charB.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + src = charC.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charC.texture.frame.x).to.equal(2); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + src = charD.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charD.texture.frame.x).to.equal(2); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/split_font.fnt b/test/loaders/resources/split_font.fnt new file mode 100644 index 0000000..35cbf49 --- /dev/null +++ b/test/loaders/resources/split_font.fnt @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 96d9f44..b505250 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -503,10 +503,10 @@ * * @static * @param {XMLDocument} xml - The XML document data. - * @param {PIXI.Texture} texture - Texture with all symbols. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. * @return {Object} Result font object with font, size, lineHeight and char fields. */ - static registerFont(xml, texture) + static registerFont(xml, textures) { const data = {}; const info = xml.getElementsByTagName('info')[0]; @@ -518,29 +518,44 @@ data.size = parseInt(info.getAttribute('size'), 10); data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res; data.chars = {}; + if (!(textures instanceof Array)) + { + textures = [textures]; + } // parse letters const letters = xml.getElementsByTagName('char'); + let page; for (let i = 0; i < letters.length; i++) { const letter = letters[i]; const charCode = parseInt(letter.getAttribute('id'), 10); + let textureRect; - const textureRect = new core.Rectangle( - (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res), - (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res), - parseInt(letter.getAttribute('width'), 10) / res, - parseInt(letter.getAttribute('height'), 10) / res - ); + page = parseInt(letter.getAttribute('page'), 10); + if (isNaN(page)) + { + textureRect = new core.Rectangle(0, 0, 0, 0); + page = 0; + } + else + { + textureRect = new core.Rectangle( + (parseInt(letter.getAttribute('x'), 10) / res) + (textures[page].frame.x / res), + (parseInt(letter.getAttribute('y'), 10) / res) + (textures[page].frame.y / res), + parseInt(letter.getAttribute('width'), 10) / res, + parseInt(letter.getAttribute('height'), 10) / res + ); + } data.chars[charCode] = { xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res, yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res, xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res, kerning: {}, - texture: new core.Texture(texture.baseTexture, textureRect), - + texture: new core.Texture(textures[page].baseTexture, textureRect), + page, }; } diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 9691dd0..99e0c02 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -9,11 +9,11 @@ * @function parseBitmapFontData * @memberof PIXI.loaders * @param {PIXI.loaders.Resource} resource - Loader resource. - * @param {PIXI.Texture} texture - Reference to texture. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. */ -export function parse(resource, texture) +export function parse(resource, textures) { - resource.bitmapFont = BitmapText.registerFont(resource.data, texture); + resource.bitmapFont = BitmapText.registerFont(resource.data, textures); } export default function () @@ -67,29 +67,53 @@ xmlUrl += '/'; } - const textureUrl = xmlUrl + resource.data.getElementsByTagName('page')[0].getAttribute('file'); + const pages = resource.data.getElementsByTagName('page'); + const textures = []; + const loadOptions = { + crossOrigin: resource.crossOrigin, + loadType: Resource.LOAD_TYPE.IMAGE, + metadata: resource.metadata.imageMetadata, + parentResource: resource, + }; - if (utils.TextureCache[textureUrl]) + for (let x = 0; x < pages.length; ++x) { - // reuse existing texture - parse(resource, utils.TextureCache[textureUrl]); - next(); - } - else - { - const loadOptions = { - crossOrigin: resource.crossOrigin, - loadType: Resource.LOAD_TYPE.IMAGE, - metadata: resource.metadata.imageMetadata, - parentResource: resource, - }; + const textureUrl = xmlUrl + pages[x].getAttribute('file'); - // load the texture for the font - this.add(`${resource.name}_image`, textureUrl, loadOptions, (res) => + if (utils.TextureCache[textureUrl]) { - parse(resource, res.texture); - next(); - }); + textures.push(utils.TextureCache[textureUrl]); + } + else + { + // load the texture for the font + this.add(`${resource.name}_image${x}`, textureUrl, loadOptions, () => + { + const nextTextures = []; + + for (let x = 0; x < pages.length; ++x) + { + const nextTextureUrl = xmlUrl + pages[x].getAttribute('file'); + + if (utils.TextureCache[nextTextureUrl]) + { + nextTextures.push(utils.TextureCache[nextTextureUrl]); + } + else + { + return; + } + } + parse(resource, nextTextures); + next(); + }); + } + } + + if (textures.length === pages.length) + { + parse(resource, textures); + next(); } }; } diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 70703fc..eb2e34f 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -307,6 +307,69 @@ done(); }); }); + + it('should properly register bitmap font having more than one texture', function (done) + { + const ldr = new PIXI.loaders.Loader(); + + ldr.add(`${__dirname}/resources/split_font.fnt`); + ldr.load(() => + { + const font = PIXI.extras.BitmapText.fonts.split_font; + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.split_font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + let src = charA.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + src = charB.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + src = charC.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charC.texture.frame.x).to.equal(2); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + src = charD.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charD.texture.frame.x).to.equal(2); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/split_font.fnt b/test/loaders/resources/split_font.fnt new file mode 100644 index 0000000..35cbf49 --- /dev/null +++ b/test/loaders/resources/split_font.fnt @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/loaders/resources/split_font_ab.png b/test/loaders/resources/split_font_ab.png new file mode 100644 index 0000000..dca6137 --- /dev/null +++ b/test/loaders/resources/split_font_ab.png Binary files differ diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 96d9f44..b505250 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -503,10 +503,10 @@ * * @static * @param {XMLDocument} xml - The XML document data. - * @param {PIXI.Texture} texture - Texture with all symbols. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. * @return {Object} Result font object with font, size, lineHeight and char fields. */ - static registerFont(xml, texture) + static registerFont(xml, textures) { const data = {}; const info = xml.getElementsByTagName('info')[0]; @@ -518,29 +518,44 @@ data.size = parseInt(info.getAttribute('size'), 10); data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res; data.chars = {}; + if (!(textures instanceof Array)) + { + textures = [textures]; + } // parse letters const letters = xml.getElementsByTagName('char'); + let page; for (let i = 0; i < letters.length; i++) { const letter = letters[i]; const charCode = parseInt(letter.getAttribute('id'), 10); + let textureRect; - const textureRect = new core.Rectangle( - (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res), - (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res), - parseInt(letter.getAttribute('width'), 10) / res, - parseInt(letter.getAttribute('height'), 10) / res - ); + page = parseInt(letter.getAttribute('page'), 10); + if (isNaN(page)) + { + textureRect = new core.Rectangle(0, 0, 0, 0); + page = 0; + } + else + { + textureRect = new core.Rectangle( + (parseInt(letter.getAttribute('x'), 10) / res) + (textures[page].frame.x / res), + (parseInt(letter.getAttribute('y'), 10) / res) + (textures[page].frame.y / res), + parseInt(letter.getAttribute('width'), 10) / res, + parseInt(letter.getAttribute('height'), 10) / res + ); + } data.chars[charCode] = { xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res, yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res, xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res, kerning: {}, - texture: new core.Texture(texture.baseTexture, textureRect), - + texture: new core.Texture(textures[page].baseTexture, textureRect), + page, }; } diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 9691dd0..99e0c02 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -9,11 +9,11 @@ * @function parseBitmapFontData * @memberof PIXI.loaders * @param {PIXI.loaders.Resource} resource - Loader resource. - * @param {PIXI.Texture} texture - Reference to texture. + * @param {PIXI.Texture|PIXI.Texture[]} textures - List of textures for each page. */ -export function parse(resource, texture) +export function parse(resource, textures) { - resource.bitmapFont = BitmapText.registerFont(resource.data, texture); + resource.bitmapFont = BitmapText.registerFont(resource.data, textures); } export default function () @@ -67,29 +67,53 @@ xmlUrl += '/'; } - const textureUrl = xmlUrl + resource.data.getElementsByTagName('page')[0].getAttribute('file'); + const pages = resource.data.getElementsByTagName('page'); + const textures = []; + const loadOptions = { + crossOrigin: resource.crossOrigin, + loadType: Resource.LOAD_TYPE.IMAGE, + metadata: resource.metadata.imageMetadata, + parentResource: resource, + }; - if (utils.TextureCache[textureUrl]) + for (let x = 0; x < pages.length; ++x) { - // reuse existing texture - parse(resource, utils.TextureCache[textureUrl]); - next(); - } - else - { - const loadOptions = { - crossOrigin: resource.crossOrigin, - loadType: Resource.LOAD_TYPE.IMAGE, - metadata: resource.metadata.imageMetadata, - parentResource: resource, - }; + const textureUrl = xmlUrl + pages[x].getAttribute('file'); - // load the texture for the font - this.add(`${resource.name}_image`, textureUrl, loadOptions, (res) => + if (utils.TextureCache[textureUrl]) { - parse(resource, res.texture); - next(); - }); + textures.push(utils.TextureCache[textureUrl]); + } + else + { + // load the texture for the font + this.add(`${resource.name}_image${x}`, textureUrl, loadOptions, () => + { + const nextTextures = []; + + for (let x = 0; x < pages.length; ++x) + { + const nextTextureUrl = xmlUrl + pages[x].getAttribute('file'); + + if (utils.TextureCache[nextTextureUrl]) + { + nextTextures.push(utils.TextureCache[nextTextureUrl]); + } + else + { + return; + } + } + parse(resource, nextTextures); + next(); + }); + } + } + + if (textures.length === pages.length) + { + parse(resource, textures); + next(); } }; } diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 70703fc..eb2e34f 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -307,6 +307,69 @@ done(); }); }); + + it('should properly register bitmap font having more than one texture', function (done) + { + const ldr = new PIXI.loaders.Loader(); + + ldr.add(`${__dirname}/resources/split_font.fnt`); + ldr.load(() => + { + const font = PIXI.extras.BitmapText.fonts.split_font; + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.split_font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + let src = charA.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + src = charB.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_ab.png'); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + src = charC.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charC.texture.frame.x).to.equal(2); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + src = charD.texture.baseTexture.source.src; + + src = src.substring(src.length - 17); + expect(src).to.equal('split_font_cd.png'); + expect(charD.texture.frame.x).to.equal(2); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/split_font.fnt b/test/loaders/resources/split_font.fnt new file mode 100644 index 0000000..35cbf49 --- /dev/null +++ b/test/loaders/resources/split_font.fnt @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/loaders/resources/split_font_ab.png b/test/loaders/resources/split_font_ab.png new file mode 100644 index 0000000..dca6137 --- /dev/null +++ b/test/loaders/resources/split_font_ab.png Binary files differ diff --git a/test/loaders/resources/split_font_cd.png b/test/loaders/resources/split_font_cd.png new file mode 100644 index 0000000..8f3c77e --- /dev/null +++ b/test/loaders/resources/split_font_cd.png Binary files differ