microsoft / microsoft/maker.js

`exporter.toSVG` mutates models unexpectedly

Open
#628 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2k
Forks
302
Avg merge
21h 28m
Merged PRs (30d)
5

Description

It looks like the toSVG method mutates the models passed into it, which is expected. This came up while trying to cache submodels to improve performance.

Here's a minimal repro case:

const makerjs = require("makerjs");
const opentype = require("opentype.js");

const font = opentype.loadSync("./src/fonts/ReliefSingleLineCAD-Regular.ttf");

const text = new makerjs.models.Text(font, "Hi", 12);
const model = {
	models: {
		text1: text,
		text2: text,
	},
};

const svg = makerjs.exporter.toSVG(model);
console.log(svg);

The result is this exception:

TypeError: Cannot read properties of undefined (reading 'startT')
    at getActualBezierRange (/Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:8591:43)
    at /Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:8790:37
    at Array.forEach (<anonymous>)
    at /Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:8785:27
    at _loop_3 (/Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:6054:21)
    at Object.findChains (/Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:6058:17)
    at BezierCurve.getBezierSeeds (/Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:8773:31)
    at /Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:6170:73
    at Array.forEach (<anonymous>)
    at swapBezierPathsWithSeeds (/Users/xavi/code/maker-test/server/node_modules/makerjs/dist/index.js:6161:21)

I'm using node v22.11.0 with makerjs@0.18.1 and opentype.js@1.3.4

The issue seems to be somewhere in here: https://github.com/microsoft/maker.js/blob/1ed796b0a94d781eb050882e30fc52086d34aada/packages/maker.js/src/core/chain.ts#L405-L453

It's not obvious to me how to fix it, so I figured I'd open an issue

The exception doesn't occur when useSvgPathOnly option is enabled, which double confirms that the issue is related to the findChains function

Workaround
// Clone submodels
const model = {
	models: {
		text1: makerjs.cloneObject(text),
		text2: makerjs.cloneObject(text),
	},
};

This does works, but it add a bit of overhead. Also, it's surprising that an exporter mutates the underlying model

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with exporter.toSVG and the chain handling in packages/maker.js/src/core/chain.ts, especially lines 405-453 and findChains. Run the shared-submodel repro from the issue, then compare it with useSvgPathOnly enabled. Done means exporting a model that references the same submodel twice no longer mutates it or throws the reported startT exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
computer-graphics
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.