Include XML header in exported SVGs

This commit is contained in:
David Evans 2018-05-12 12:52:55 +01:00
parent e85890563c
commit 4551df9395
10 changed files with 133 additions and 10 deletions

View File

@ -1296,6 +1296,8 @@
typeof window.InstallTrigger !== 'undefined'
);
const FIRST_SVG_TAG = /<svg ?/;
class Exporter {
constructor() {
this.latestSVG = null;
@ -1316,12 +1318,12 @@
* exporting from any environment, in case it is opened in FireFox
*/
code = code.replace(
(/^<svg ?/),
FIRST_SVG_TAG,
'<svg width="' + (renderer.width || 1) +
'" height="' + (renderer.height || 1) + '" '
);
return code;
return '<?xml version="1.0" encoding="UTF-8" ?>' + code;
}
getSVGBlob(renderer) {

File diff suppressed because one or more lines are too long

View File

@ -1296,6 +1296,8 @@
typeof window.InstallTrigger !== 'undefined'
);
const FIRST_SVG_TAG = /<svg ?/;
class Exporter {
constructor() {
this.latestSVG = null;
@ -1316,12 +1318,12 @@
* exporting from any environment, in case it is opened in FireFox
*/
code = code.replace(
(/^<svg ?/),
FIRST_SVG_TAG,
'<svg width="' + (renderer.width || 1) +
'" height="' + (renderer.height || 1) + '" '
);
return code;
return '<?xml version="1.0" encoding="UTF-8" ?>' + code;
}
getSVGBlob(renderer) {

View File

@ -178,4 +178,11 @@ describe('SequenceDiagram', () => {
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="');
});
});

View File

@ -1,5 +1,7 @@
import {safari} from '../../core/browser.mjs';
const FIRST_SVG_TAG = /<svg ?/;
export default class Exporter {
constructor() {
this.latestSVG = null;
@ -20,12 +22,12 @@ export default class Exporter {
* exporting from any environment, in case it is opened in FireFox
*/
code = code.replace(
(/^<svg ?/),
FIRST_SVG_TAG,
'<svg width="' + (renderer.width || 1) +
'" height="' + (renderer.height || 1) + '" '
);
return code;
return '<?xml version="1.0" encoding="UTF-8" ?>' + code;
}
getSVGBlob(renderer) {

View File

@ -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">');
});
});
});

View File

@ -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();
});
});
});
});

View File

@ -997,7 +997,7 @@
.then((response) => response.text())
.then((content) => {
let path = content.trim();
if(!path || path.startsWith('<svg')) {
if(!path || path.startsWith('<')) {
path = relativePath;
}
this.renderService = new URL(path, window.location.href).href;

File diff suppressed because one or more lines are too long

View File

@ -441,7 +441,7 @@ export default class Interface {
.then((response) => response.text())
.then((content) => {
let path = content.trim();
if(!path || path.startsWith('<svg')) {
if(!path || path.startsWith('<')) {
path = relativePath;
}
this.renderService = new URL(path, window.location.href).href;