Skip to content

Commit

Permalink
Refactor filter.js
Browse files Browse the repository at this point in the history
Don't remove class `keep-size` or attributes with keys `width`, height`,
and `scale-factor` if they are present. This only complicates the code,
and may be considered unexpected behavior in some cases.

Move all of the resizing logic into `resize.js`. This makes it easier to
use pandoc-svg in API form for HTML post processing.
  • Loading branch information
rnwst committed Apr 2, 2024
1 parent c27dbd9 commit 65c80b3
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 47 deletions.
36 changes: 5 additions & 31 deletions lib/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export default function filter({t: key, c: value}, format, meta) {

const imageValue = utils.isFigure(key, value) ? value[0]['c'] : value;
// https://hackage.haskell.org/package/pandoc-types-1.22.2.1/docs/Text-Pandoc-Definition.html#t:Inline
let [[id, classes, keyvals], caption, [fname, title]] = imageValue;
/* eslint-disable-next-line no-unused-vars */
const [[id, classes, keyvals], caption, [fname, title]] = imageValue;

// If image is not an SVG or if it has class 'ignore', return.
if (!utils.isSVG(fname) || classes.includes('ignore')) {
Expand All @@ -48,38 +49,11 @@ export default function filter({t: key, c: value}, format, meta) {

// Load SVG, then load the SVG's DOM using jsdom.
let svgString = utils.loadSVG(fname);
if (!svgString) {
return;
}
if (!svgString) return;
const svgDOM = utils.createDOM(svgString);

// Resize SVG?
const dimKeyvals = [];
keyvals = keyvals.filter( ([key, val]) => {
if (key === 'width' || key === 'height') {
dimKeyvals.push([key, val]);
return false;
}
return true;
});
if (dimKeyvals.length !== 0) {
const dimObj = Object.fromEntries(dimKeyvals);
resize(svgDOM, dimObj);
} else {
if (!classes.includes('keep-size')) {
let scaleFactor;
keyvals = keyvals.filter( ([key, val]) => {
if (key === 'scale-factor') {
scaleFactor = val;
return false;
}
return true;
});
resize(svgDOM, {scaleFactor: scaleFactor});
} else {
classes.splice(classes.indexOf('keep-size'), 1);
}
}
// Resize SVG. Convert keyvals to more convenient object representation.
resize(svgDOM, classes, utils.fromEntries(keyvals));

if (format === 'html') {
// Optimize SVG. This needs to be done before text2foreignObject is called,
Expand Down
23 changes: 13 additions & 10 deletions lib/resize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
import {round} from './utils.js';

/**
* Resize SVG `width` and `height` units to `em`. No unit implies `px`.
* If `dimObject` contains `width` or `height`, apply those values instead,
* maintaining aspect ratio. If the SVG is already sized in relative units,
* only apply `scaleFactor`.
* Resize SVG `width` and `height` units to `em`. No unit implies `px`. If
* `attrs` contains keys `width` or `height`, apply the corresponding values
* instead, maintaining aspect ratio. If the SVG is already sized in relative
* units, only apply `scaleFactor`.
* @param {object} svgDOM - SVG DOM
* @param {object} dimObj - Object containing information for dimensioning
* @param {array} classes - Class list
* @param {object} attrs - Object containing information for dimensioning
*/
export default function resize(svgDOM, dimObj) {
let {scaleFactor, width, height} = dimObj;
export default function resize(svgDOM, classes, attrs) {
let {'scale-factor': scaleFactor, width, height} = attrs;

if (typeof scaleFactor === 'undefined') {
scaleFactor = 1;
}
// If class `keep-size` is set, and neither width nor height have been
// supplied, don't resize SVG.
if (classes.includes('keep-size') && !width && !height) return;

if (typeof scaleFactor === 'undefined') scaleFactor = 1;

const dimRegex = /^(?<val>[\d\.]+)(?<unit>[^\d\.]*)$/;

Expand Down
14 changes: 14 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,20 @@ export function round(number) {
return parseFloat(number.toPrecision(10));
}

/**
* Convert keyvals to JS object.
* @param {array} keyvals - Array of key-value pairs
* @return {object} - Corresponding object
*/
export function fromEntries(keyvals) {
// When only one pair of keyvals is provided, for some reason pandoc creates
// an array ['key', 'val'] rather than [['key', 'val']].
if (keyvals.length === 2 && typeof keyvals[0] === 'string') {
keyvals = [keyvals];
}
return Object.fromEntries(keyvals);
}

/**
* Flatten children of passed DOM element, by replacing child elements with
* their content.
Expand Down
12 changes: 6 additions & 6 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ describe('resize', () => {
beforeEach( () => svgDOM = utils.createDOM(svg));

it('converts units to `em`', () => {
resize(svgDOM, {});
resize(svgDOM, [], {});
const widthPX = utils.round(widthMM * 0.1 * 37.8);
const heightPX = utils.round(heightMM * 0.1 * 37.8);
expect(svgDOM.querySelector('svg').getAttribute('width'))
Expand All @@ -250,34 +250,34 @@ describe('resize', () => {
});

it('applies width', () => {
resize(svgDOM, {width: '1.5in'});
resize(svgDOM, [], {width: '1.5in'});
expect(svgDOM.querySelector('svg').getAttribute('width')).toEqual('1.5in');
expect(svgDOM.querySelector('svg').getAttribute('height'))
.toEqual(utils.round(1.5 / aspectRatio) + 'in');
});

it('applies height', () => {
resize(svgDOM, {height: '1.5in'});
resize(svgDOM, [], {height: '1.5in'});
expect(svgDOM.querySelector('svg').getAttribute('width'))
.toEqual(utils.round(1.5 * aspectRatio) + 'in');
expect(svgDOM.querySelector('svg').getAttribute('height')).toEqual('1.5in');
});

it('applies width and height', () => {
resize(svgDOM, {width: '1inch', height: '2inch'});
resize(svgDOM, [], {width: '1inch', height: '2inch'});
expect(svgDOM.querySelector('svg').getAttribute('width')).toEqual('1in');
expect(svgDOM.querySelector('svg').getAttribute('height')).toEqual('2in');
});

it('excludes SVGs with mixed relative/absolute units', () => {
const svg = '<svg width="50%" height="50mm"></svg>';
const svgDOM = utils.createDOM(svg);
resize(svgDOM, {});
resize(svgDOM, [], {});
expect(svgDOM.querySelector('svg').outerHTML).toEqual(svg);
});

it('leaves SVG unmodified when given invalid height/width', () => {
resize(svgDOM, {height: "50ducks"});
resize(svgDOM, [], {height: "50ducks"});
expect(svgDOM.querySelector('svg').outerHTML).toEqual(svg);
});
});
Expand Down

0 comments on commit 65c80b3

Please sign in to comment.