Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow a RenderContext to be passed directly to the Renderer constructor #1161

Merged
merged 5 commits into from
Oct 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions demos/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ You can use VexFlow in Node JS by calling `const Vex = require(...)` on the JS l
`node canvas.js > output.html` creates an HTML page containing the VexFlow output.

`node svg.js > output.svg` creates a SVG image file containing the VexFlow output.

`node customcontext.js` demonstrates how to use VexFlow with a custom RenderContext.
192 changes: 192 additions & 0 deletions demos/node/customcontext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// node customcontext.js

/* eslint-disable no-console */

const { createCanvas } = require('canvas');
const Vex = require('../../build/vexflow-debug');
const VF = Vex.Flow;

// A custom Vex.Flow.RenderContext implementation.
// This is just a stub for demonstration purposes that console.logs all method
// calls and arguments.
class CustomContext {
constructor() {
this.font = '';
this.fillStyle = '';
this.strokeStyle = '';
}

log(func, ...args) {
for (let i = 0; i < args.length; ++i) {
if (typeof args[i] == 'string') {
args[i] = `"${args[i]}"`;
}
}
console.log(`${func}(${args.join(', ')})`);
}

clear() {
this.log('clear');
}

setFont(family, size, weight = '') {
this.log('setFont', family, size, weight);
return this;
}

setRawFont(font) {
this.log('setRawFont', font);
return this;
}

setFillStyle(style) {
this.log('setFillStyle', style);
return this;
}

setBackgroundFillStyle(style) {
this.log('setBackgroundFillStyle', style);
return this;
}

setStrokeStyle(style) {
this.log('setStrokeStyle', style);
return this;
}

setShadowColor(color) {
this.log('setShadowColor', color);
return this;
}

setShadowBlur(blur) {
this.log('setShadowBlur', blur);
return this;
}

setLineWidth(width) {
this.log('setLineWidth', width);
return this;
}

setLineCap(capType) {
this.log('setLineCap', capType);
return this;
}

setLineDash(dashPattern) {
this.log('setLineDash', `[${dashPattern.join(', ')}]`);
return this;
}

scale(x, y) {
this.log('scale', x, y);
return this;
}

rect(x, y, width, height) {
this.log('rect', x, y, width, height);
return this;
}

resize(width, height) {
this.log('resize', width, height);
return this;
}

fillRect(x, y, width, height) {
this.log('fillRect', x, y, width, height);
return this;
}

clearRect(x, y, width, height) {
this.log('clearRect', x, y, width, height);
return this;
}

beginPath() {
this.log('beginPath');
return this;
}

moveTo(x, y) {
this.log('moveTo', x, y);
return this;
}

lineTo(x, y) {
this.log('lineTo', x, y);
return this;
}

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
this.log('bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y);
return this;
}

quadraticCurveTo(cpx, cpy, x, y) {
this.log('quadraticCurveTo', cpx, cpy, x, y);
return this;
}

arc(x, y, radius, startAngle, endAngle, antiClockwise) {
this.log('arc', x, y, radius, startAngle, endAngle, antiClockwise);
return this;
}

fill(attributes) {
this.log('fill');
return this;
}

stroke() {
this.log('stroke');
return this;
}

closePath() {
this.log('closePath');
return this;
}

fillText(text, x, y) {
this.log('fillText', text, x, y);
return this;
}

save() {
this.log('save');
return this;
}

restore() {
this.log('restore');
return this;
}

openGroup(cls, id, attrs) {
this.log('openGroup', cls, id);
}

closeGroup() {
this.log('closeGroup');
}

add(child) {
this.log('add');
}

measureText(text) {
this.log('measureText', text);
return { width: 0, height: 10 };
}
}

const renderer = new VF.Renderer(new CustomContext());
const context = renderer.getContext();
context.setFont('Arial', 10, '').setBackgroundFillStyle('#eed');

const stave = new VF.Stave(10, 40, 400);
stave.addClef('treble');
stave.addTimeSignature('4/4');
stave.setContext(context).draw();
23 changes: 17 additions & 6 deletions src/canvascontext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,29 @@ export class CanvasContext implements RenderContext {
return this;
}

// Only called if Renderer.USE_CANVAS_PROXY is true.
scale(x: number, y: number): this {
this.vexFlowCanvasContext.scale(x, y);
return this;
}

// CanvasRenderingContext2D does not have a resize function.
// renderer.ts calls ctx.scale() instead, so this method is never used.
// eslint-disable-next-line
resize(width: number, height: number): this {
// DO NOTHING.
return this;
const canvasElement = this.vexFlowCanvasContext.canvas;
const devicePixelRatio = window.devicePixelRatio || 1;

// Scale the canvas size by the device pixel ratio clamping to the maximum
// supported size.
[width, height] = CanvasContext.SanitizeCanvasDims(width * devicePixelRatio, height * devicePixelRatio);

// Divide back down by the pixel ratio and convert to integers.
width = (width / devicePixelRatio) | 0;
height = (height / devicePixelRatio) | 0;

canvasElement.width = width * devicePixelRatio;
canvasElement.height = height * devicePixelRatio;
canvasElement.style.width = width + 'px';
canvasElement.style.height = height + 'px';

return this.scale(devicePixelRatio, devicePixelRatio);
}

rect(x: number, y: number, width: number, height: number): this {
Expand Down
27 changes: 15 additions & 12 deletions src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class Factory {
* `const vf: Factory = Vex.Flow.Factory.newFromElementId('boo', 1200, 600 );`
*/
static newFromElementId(elementId: string | null, width = 500, height = 200): Factory {
return new Factory({ renderer: { elementId, width, height, backend: Renderer.Backends.SVG } });
return new Factory({ renderer: { elementId, width, height } });
}

protected options: Required<FactoryOptions>;
Expand All @@ -120,7 +120,6 @@ export class Factory {
},
renderer: {
elementId: '',
backend: Renderer.Backends.SVG,
width: 500,
height: 200,
background: '#FFF',
Expand Down Expand Up @@ -150,23 +149,27 @@ export class Factory {
}

initRenderer(): void {
const { elementId, backend, width, height, background } = this.options.renderer;
if (elementId === null) {
const { elementId, width, height, background } = this.options.renderer;
if (elementId == null) {
return;
}

if (elementId === '') {
if (elementId == '') {
L(this);
throw new RuntimeError('renderer.elementId not set in FactoryOptions');
}

this.context = Renderer.buildContext(
elementId as string,
backend ?? Renderer.Backends.SVG,
width,
height,
background
);
let backend = this.options.renderer.backend;
if (backend === undefined) {
const elem = document.getElementById(elementId);
if (elem instanceof window.HTMLCanvasElement) {
backend = Renderer.Backends.CANVAS;
} else {
backend = Renderer.Backends.SVG;
}
}

this.context = Renderer.buildContext(elementId as string, backend, width, height, background);
}

getContext(): RenderContext {
Expand Down
Loading