From 528ae873d7712038755e6bf02119c1038b6eac3d Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 16 May 2018 15:00:04 -0400 Subject: [PATCH 1/6] Remove speech bubbles' reliance on svg quirks mode's _transformText --- src/Silhouette.js | 4 +-- src/util/svg-text-bubble.js | 29 +++++++++++++++------- src/util/svg-text-wrapper.js | 47 ++++++++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/Silhouette.js b/src/Silhouette.js index ceef8ab..ac2f3f9 100644 --- a/src/Silhouette.js +++ b/src/Silhouette.js @@ -59,11 +59,11 @@ class Silhouette { const height = this._height = canvas.height = bitmapData.height; const ctx = canvas.getContext('2d'); - ctx.clearRect(0, 0, width, height); - ctx.drawImage(bitmapData, 0, 0, width, height); if (!(width && height)) { return; } + ctx.clearRect(0, 0, width, height); + ctx.drawImage(bitmapData, 0, 0, width, height); const imageData = ctx.getImageData(0, 0, width, height); this._data = new Uint8ClampedArray(imageData.data.length / 4); diff --git a/src/util/svg-text-bubble.js b/src/util/svg-text-bubble.js index d0288b4..21dc393 100644 --- a/src/util/svg-text-bubble.js +++ b/src/util/svg-text-bubble.js @@ -1,6 +1,5 @@ const SVGTextWrapper = require('./svg-text-wrapper'); const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; -const xmlescape = require('xml-escape'); const MAX_LINE_LENGTH = 170; const MIN_WIDTH = 50; @@ -8,10 +7,23 @@ const MIN_WIDTH = 50; class SVGTextBubble { constructor () { this.svgRenderer = new SvgRenderer(); - this.svgTextWrapper = new SVGTextWrapper(); + this.svgTextWrapper = new SVGTextWrapper(this.makeSvgTextElement); this._textSizeCache = {}; } + /** + * @return {SVGElement} an SVG text node with the properties that we want for speech bubbles. + */ + makeSvgTextElement () { + const svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + svgText.setAttribute('alignment-baseline', 'text-before-edge'); + svgText.setAttribute('font-size', '14'); + svgText.setAttribute('fill', '#575E75'); + // TODO Do we want to use the new default sans font instead of Helvetica? + svgText.setAttribute('font-family', 'Helvetica'); + return svgText; + } + _speechBubble (w, h, radius, pointsLeft) { let pathString = ` M 0 ${radius} @@ -133,7 +145,7 @@ class SVGTextBubble { _getTextSize () { - const svgString = this._wrapSvgFragment(this._textFragment()); + const svgString = this._wrapSvgFragment(this._textFragment); if (!this._textSizeCache[svgString]) { this._textSizeCache[svgString] = this.svgRenderer.measure(svgString); } @@ -141,6 +153,7 @@ class SVGTextBubble { } _wrapSvgFragment (fragment) { + // @todo generate view box return ` ${fragment} @@ -148,14 +161,12 @@ class SVGTextBubble { `; } - _textFragment () { - return `${xmlescape(this.lines.join('\n'))}`; - } - buildString (type, text, pointsLeft) { this.type = type; this.pointsLeft = pointsLeft; - this.lines = this.svgTextWrapper.wrapText(MAX_LINE_LENGTH, text); + const textNode = this.svgTextWrapper.wrapText(MAX_LINE_LENGTH, text); + const serializer = new XMLSerializer(); + this._textFragment = serializer.serializeToString(textNode); let fragment = ''; @@ -169,7 +180,7 @@ class SVGTextBubble { } else { fragment += this._thinkBubble(fullWidth, fullHeight, radius, this.pointsLeft); } - fragment += `${this._textFragment()}`; + fragment += `${this._textFragment}`; return this._wrapSvgFragment(fragment); } } diff --git a/src/util/svg-text-wrapper.js b/src/util/svg-text-wrapper.js index e7b2028..3ad4cf5 100644 --- a/src/util/svg-text-wrapper.js +++ b/src/util/svg-text-wrapper.js @@ -1,13 +1,19 @@ const TextWrapper = require('./text-wrapper'); +const xmlescape = require('xml-escape'); /** * Measure text by using a hidden SVG attached to the DOM. * For use with TextWrapper. */ class SVGMeasurementProvider { - constructor () { + /** + * @param {function} makeTextElement - provides a text node of an SVGElement + * with the style of the text to be wrapped. + */ + constructor (makeTextElement) { this._svgRoot = null; this._cache = {}; + this.makeTextElement = makeTextElement; } /** @@ -61,16 +67,7 @@ class SVGMeasurementProvider { const svgRoot = document.createElementNS(svgNamespace, 'svg'); const svgGroup = document.createElementNS(svgNamespace, 'g'); - const svgText = document.createElementNS(svgNamespace, 'text'); - - // Normalize text attributes to match what the svg-renderer does. - // @TODO This code should be shared with the svg-renderer. - svgText.setAttribute('alignment-baseline', 'text-before-edge'); - svgText.setAttribute('font-size', '14'); - - // TODO Do we want to use the new default sans font instead of Helvetica? - // This change intentionally subverts the svg-renderer auto font conversion. - svgText.setAttribute('font-family', 'Helvetica, Arial, sans-serif'); + const svgText = this.makeTextElement(); // hide from the user, including screen readers svgRoot.setAttribute('style', 'position:absolute;visibility:hidden'); @@ -99,8 +96,32 @@ class SVGMeasurementProvider { * TextWrapper specialized for SVG text. */ class SVGTextWrapper extends TextWrapper { - constructor () { - super(new SVGMeasurementProvider()); + /** + * @param {function} makeTextElement - provides a text node of an SVGElement + * with the style of the text to be wrapped. + */ + constructor (makeTextElement) { + super(new SVGMeasurementProvider(makeTextElement)); + this.makeTextElement = makeTextElement; + } + + /** + * Wrap the provided text into lines restricted to a maximum width. See Unicode Standard Annex (UAX) #14. + * @param {number} maxWidth - the maximum allowed width of a line. + * @param {string} text - the text to be wrapped. Will be split on whitespace. + * @returns {SVGElement} wrapped text node + */ + wrapText (maxWidth, text) { + const lines = super.wrapText(maxWidth, text); + const textElement = this.makeTextElement(); + for (const line of lines) { + const tspanNode = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + tspanNode.setAttribute('x', '0'); + tspanNode.setAttribute('dy', '1.2em'); + tspanNode.textContent = xmlescape(line); + textElement.appendChild(tspanNode); + } + return textElement; } } From 9d84dfdd0ecfe43ba25d23cc01f1775beb6c1795 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Thu, 17 May 2018 14:26:13 -0400 Subject: [PATCH 2/6] Publish specific files and folders to NPM Users of `scratch-render` do not need the `playground` directory installed as a npm package. If they need those directories they can clone scratch-render and do a local build. This saves downloading 3.5MB (`playground`) when `npm install`ing `scratch-render`. --- .npmignore | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.npmignore b/.npmignore index e69de29..676de7c 100644 --- a/.npmignore +++ b/.npmignore @@ -0,0 +1,16 @@ +# Development files +.eslintrc.js +/.editorconfig +/.eslintignore +/.gitattributes +/.github +/.jsdoc.json +/.travis.yml +/test +/webpack.config.js + +# Build created files +/playground + +# Exclude already built packages from testing with npm pack +/scratch-render-*.{tar,tgz} From 8abffc7f0bd6267d9974f000302546ad02e1ff83 Mon Sep 17 00:00:00 2001 From: DD Date: Mon, 21 May 2018 14:42:32 -0400 Subject: [PATCH 3/6] Add viewbox to speech bubble svgs --- src/util/svg-text-bubble.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/util/svg-text-bubble.js b/src/util/svg-text-bubble.js index 21dc393..1324a90 100644 --- a/src/util/svg-text-bubble.js +++ b/src/util/svg-text-bubble.js @@ -3,6 +3,7 @@ const SvgRenderer = require('scratch-svg-renderer').SVGRenderer; const MAX_LINE_LENGTH = 170; const MIN_WIDTH = 50; +const STROKE_WIDTH = 4; class SVGTextBubble { constructor () { @@ -57,7 +58,7 @@ class SVGTextBubble { @@ -113,7 +114,7 @@ class SVGTextBubble { rx="${rx}" ry="${ry}" fill="rgba(0, 0, 0, 0.15)" stroke="rgba(0, 0, 0, 0.15)" - stroke-width="4" + stroke-width="${STROKE_WIDTH}" /> - + ${ellipses.join('\n')} `; @@ -152,13 +154,16 @@ class SVGTextBubble { return this._textSizeCache[svgString]; } - _wrapSvgFragment (fragment) { - // @todo generate view box - return ` - - ${fragment} - - `; + _wrapSvgFragment (fragment, width, height) { + let svgString = ``; + } else { + svgString = `${svgString}>`; + } + svgString = `${svgString} ${fragment} `; + return svgString; } buildString (type, text, pointsLeft) { @@ -181,7 +186,7 @@ class SVGTextBubble { fragment += this._thinkBubble(fullWidth, fullHeight, radius, this.pointsLeft); } fragment += `${this._textFragment}`; - return this._wrapSvgFragment(fragment); + return this._wrapSvgFragment(fragment, fullWidth, fullHeight); } } From 88d78b5f9ff9a7eccc8ded8f28038d9b9d73c2f6 Mon Sep 17 00:00:00 2001 From: DD Date: Mon, 21 May 2018 15:53:14 -0400 Subject: [PATCH 4/6] Update vm and svg renderer with removing quirks mode --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b53fd2f..14a6e4c 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "linebreak": "0.3.0", "raw-loader": "^0.5.1", "scratch-storage": "^0.4.0", - "scratch-svg-renderer": "0.1.0-prerelease.20180514170126", - "scratch-vm": "0.1.0-prerelease.1525975472", + "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", + "scratch-vm": "0.1.0-prerelease.1526929817", "tap": "^11.0.0", "travis-after-all": "^1.4.4", "twgl.js": "4.4.0", From 5009692426f6e86082fafeda27fba6cbbb09bf5f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 22 May 2018 09:35:41 -0400 Subject: [PATCH 5/6] Add an explicit width and height to fix bubbles on retina screens --- src/util/svg-text-bubble.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/util/svg-text-bubble.js b/src/util/svg-text-bubble.js index 1324a90..328c3a2 100644 --- a/src/util/svg-text-bubble.js +++ b/src/util/svg-text-bubble.js @@ -157,8 +157,11 @@ class SVGTextBubble { _wrapSvgFragment (fragment, width, height) { let svgString = ``; + ${-STROKE_WIDTH / 2} ${-STROKE_WIDTH / 2} ${fullWidth} ${fullHeight}" + width="${fullWidth}" height="${fullHeight}">`; } else { svgString = `${svgString}>`; } From 75ad498f3ca714f04609a32abfb8fd0b0ef41bf4 Mon Sep 17 00:00:00 2001 From: DD Date: Thu, 24 May 2018 17:59:34 -0400 Subject: [PATCH 6/6] Update deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 14a6e4c..29a7941 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "linebreak": "0.3.0", "raw-loader": "^0.5.1", "scratch-storage": "^0.4.0", - "scratch-svg-renderer": "0.1.0-prerelease.20180521194642", - "scratch-vm": "0.1.0-prerelease.1526929817", + "scratch-svg-renderer": "0.1.0-prerelease.20180524210316", + "scratch-vm": "0.1.0-prerelease.1527198751", "tap": "^11.0.0", "travis-after-all": "^1.4.4", "twgl.js": "4.4.0",