Include XML header in exported SVGs
This commit is contained in:
parent
e85890563c
commit
4551df9395
|
@ -1296,6 +1296,8 @@
|
||||||
typeof window.InstallTrigger !== 'undefined'
|
typeof window.InstallTrigger !== 'undefined'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const FIRST_SVG_TAG = /<svg ?/;
|
||||||
|
|
||||||
class Exporter {
|
class Exporter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.latestSVG = null;
|
this.latestSVG = null;
|
||||||
|
@ -1316,12 +1318,12 @@
|
||||||
* exporting from any environment, in case it is opened in FireFox
|
* exporting from any environment, in case it is opened in FireFox
|
||||||
*/
|
*/
|
||||||
code = code.replace(
|
code = code.replace(
|
||||||
(/^<svg ?/),
|
FIRST_SVG_TAG,
|
||||||
'<svg width="' + (renderer.width || 1) +
|
'<svg width="' + (renderer.width || 1) +
|
||||||
'" height="' + (renderer.height || 1) + '" '
|
'" height="' + (renderer.height || 1) + '" '
|
||||||
);
|
);
|
||||||
|
|
||||||
return code;
|
return '<?xml version="1.0" encoding="UTF-8" ?>' + code;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSVGBlob(renderer) {
|
getSVGBlob(renderer) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1296,6 +1296,8 @@
|
||||||
typeof window.InstallTrigger !== 'undefined'
|
typeof window.InstallTrigger !== 'undefined'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const FIRST_SVG_TAG = /<svg ?/;
|
||||||
|
|
||||||
class Exporter {
|
class Exporter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.latestSVG = null;
|
this.latestSVG = null;
|
||||||
|
@ -1316,12 +1318,12 @@
|
||||||
* exporting from any environment, in case it is opened in FireFox
|
* exporting from any environment, in case it is opened in FireFox
|
||||||
*/
|
*/
|
||||||
code = code.replace(
|
code = code.replace(
|
||||||
(/^<svg ?/),
|
FIRST_SVG_TAG,
|
||||||
'<svg width="' + (renderer.width || 1) +
|
'<svg width="' + (renderer.width || 1) +
|
||||||
'" height="' + (renderer.height || 1) + '" '
|
'" height="' + (renderer.height || 1) + '" '
|
||||||
);
|
);
|
||||||
|
|
||||||
return code;
|
return '<?xml version="1.0" encoding="UTF-8" ?>' + code;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSVGBlob(renderer) {
|
getSVGBlob(renderer) {
|
||||||
|
|
|
@ -178,4 +178,11 @@ describe('SequenceDiagram', () => {
|
||||||
|
|
||||||
expect(content).toContain('<filter id="highlight"');
|
expect(content).toContain('<filter id="highlight"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns a full SVG with explicit sizes when exported', () => {
|
||||||
|
const svg = makeDiagram('').getSVGCodeSynchronous();
|
||||||
|
|
||||||
|
expect(svg).toContain('<?xml version="1.0" encoding="UTF-8" ?>');
|
||||||
|
expect(svg).toContain('<svg width="');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import {safari} from '../../core/browser.mjs';
|
import {safari} from '../../core/browser.mjs';
|
||||||
|
|
||||||
|
const FIRST_SVG_TAG = /<svg ?/;
|
||||||
|
|
||||||
export default class Exporter {
|
export default class Exporter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.latestSVG = null;
|
this.latestSVG = null;
|
||||||
|
@ -20,12 +22,12 @@ export default class Exporter {
|
||||||
* exporting from any environment, in case it is opened in FireFox
|
* exporting from any environment, in case it is opened in FireFox
|
||||||
*/
|
*/
|
||||||
code = code.replace(
|
code = code.replace(
|
||||||
(/^<svg ?/),
|
FIRST_SVG_TAG,
|
||||||
'<svg width="' + (renderer.width || 1) +
|
'<svg width="' + (renderer.width || 1) +
|
||||||
'" height="' + (renderer.height || 1) + '" '
|
'" height="' + (renderer.height || 1) + '" '
|
||||||
);
|
);
|
||||||
|
|
||||||
return code;
|
return '<?xml version="1.0" encoding="UTF-8" ?>' + code;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSVGBlob(renderer) {
|
getSVGBlob(renderer) {
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Exporter from './Exporter.mjs';
|
||||||
|
|
||||||
|
function makeRenderer(code, width, height) {
|
||||||
|
return {dom: () => ({outerHTML: code}), height, width};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Exporter', () => {
|
||||||
|
const exporter = new Exporter();
|
||||||
|
|
||||||
|
const renderer = makeRenderer('<svg foo="bar"><abc></abc></svg>', 100, 200);
|
||||||
|
|
||||||
|
describe('.getSVGContent', () => {
|
||||||
|
it('returns the XML representation of the given renderer', () => {
|
||||||
|
const xml = exporter.getSVGContent(renderer);
|
||||||
|
|
||||||
|
expect(xml).toContain('<abc></abc>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes an XML header', () => {
|
||||||
|
const xml = exporter.getSVGContent(renderer);
|
||||||
|
|
||||||
|
expect(xml).toContain('<?xml version="1.0" encoding="UTF-8" ?>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds the width and height explicitly', () => {
|
||||||
|
const xml = exporter.getSVGContent(renderer);
|
||||||
|
|
||||||
|
expect(xml).toContain('<svg width="100" height="200" foo="bar">');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,79 @@
|
||||||
|
import Exporter from './Exporter.mjs';
|
||||||
|
|
||||||
|
function makeRenderer(code, width, height) {
|
||||||
|
return {dom: () => ({outerHTML: code}), height, width};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPixel(dat, x, y) {
|
||||||
|
/* eslint-disable no-bitwise */ // Pixel channel manipulation
|
||||||
|
const d = dat.data;
|
||||||
|
const p = (y * dat.width + x) * 4;
|
||||||
|
return ((d[p] << 24) | (d[p + 1] << 16) | (d[p + 2] << 8) | d[p + 3]) >>> 0;
|
||||||
|
/* eslint-enable no-bitwise */
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Browser-backed Exporter', () => {
|
||||||
|
const exporter = new Exporter();
|
||||||
|
|
||||||
|
const svg = (
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"' +
|
||||||
|
' viewBox="0 0 20 25">' +
|
||||||
|
'<rect fill="#C08040" x="5" y="5" width="10" height="15">' +
|
||||||
|
'</rect>' +
|
||||||
|
'</svg>'
|
||||||
|
);
|
||||||
|
const renderer = makeRenderer(svg, 20, 25);
|
||||||
|
|
||||||
|
describe('.getSVGBlob', () => {
|
||||||
|
it('returns an image/svg+xml blob containing the image', () => {
|
||||||
|
const blob = exporter.getSVGBlob(renderer);
|
||||||
|
|
||||||
|
expect(blob.type).toEqual('image/svg+xml');
|
||||||
|
expect(blob.size).toEqual(exporter.getSVGContent(renderer).length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.getSVGURL', () => {
|
||||||
|
it('returns an object URL containing the image in SVG format', () => {
|
||||||
|
const url = exporter.getSVGURL(renderer);
|
||||||
|
|
||||||
|
expect(url).toContain('blob:');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.getCanvas', () => {
|
||||||
|
it('asynchronously renders the SVG in a canvas', (done) => {
|
||||||
|
exporter.getCanvas(renderer, 2, (canvas) => {
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const dat = ctx.getImageData(0, 0, 40, 50);
|
||||||
|
|
||||||
|
expect(canvas.width).toEqual(40);
|
||||||
|
expect(canvas.height).toEqual(50);
|
||||||
|
expect(getPixel(dat, 0, 0)).toEqual(0x00000000);
|
||||||
|
expect(getPixel(dat, 25, 20)).toEqual(0xC08040FF);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.getPNGBlob', () => {
|
||||||
|
it('asynchronously creates an image/png blob', (done) => {
|
||||||
|
exporter.getPNGBlob(renderer, 2, (blob) => {
|
||||||
|
expect(blob.type).toEqual('image/png');
|
||||||
|
expect(blob.size).toBeGreaterThan(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.getPNGURL', () => {
|
||||||
|
it('asynchronously creates an object URL', (done) => {
|
||||||
|
exporter.getPNGURL(renderer, 2, (url, latest) => {
|
||||||
|
expect(latest).toEqual(true);
|
||||||
|
expect(url).toContain('blob:');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -997,7 +997,7 @@
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
let path = content.trim();
|
let path = content.trim();
|
||||||
if(!path || path.startsWith('<svg')) {
|
if(!path || path.startsWith('<')) {
|
||||||
path = relativePath;
|
path = relativePath;
|
||||||
}
|
}
|
||||||
this.renderService = new URL(path, window.location.href).href;
|
this.renderService = new URL(path, window.location.href).href;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -441,7 +441,7 @@ export default class Interface {
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
let path = content.trim();
|
let path = content.trim();
|
||||||
if(!path || path.startsWith('<svg')) {
|
if(!path || path.startsWith('<')) {
|
||||||
path = relativePath;
|
path = relativePath;
|
||||||
}
|
}
|
||||||
this.renderService = new URL(path, window.location.href).href;
|
this.renderService = new URL(path, window.location.href).href;
|
||||||
|
|
Loading…
Reference in New Issue