Skip to content

Add Formatter utility class #13

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

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
101 changes: 101 additions & 0 deletions examples/Formatter/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<title>txt: Formatter example</title>

<script src="../../dist/easeljs.js"></script>
<script type="text/javascript" src="../../dist/txt.js"></script>

<style>
input {
border: 2px solid black;
padding: 15px;
margin: 0 0 10px 0;
}
</style>
<script type="text/javascript">
var canvas;
var stage;
var domText;
var text;
var formatter;

var PIXEL_RATIO = (function () {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();

createHiDPICanvas = function (w, h, ratio) {
if (!ratio) {
ratio = PIXEL_RATIO;
}
var can = document.createElement("canvas");
can.width = w * ratio;
can.height = h * ratio;
can.style.width = w + "px";
can.style.height = h + "px";
can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
return can;
}

function domKeyPress() {
formatter.setRichText(text, domText.value);
}

function init() {
domText = document.getElementById("domText");
domText.onkeyup = domKeyPress;
canvas = createHiDPICanvas(1500, 1000, 1);
document.body.appendChild(canvas);
stage = new createjs.Stage(canvas);
stage.density = 2;

text = new txt.Text({
font:'sourcesanspro',
align: txt.Align.TOP_RIGHT,
lineHeight: 36,
width: 850,
size: 36,
x: 10,
y: 80
});

txt.Formatter.globalShortcuts = {
red: {
fillColor: 'red'
}
}

formatter = new txt.Formatter();
formatter.shortcuts = {
fun: {
fillColor: '#ce6666',
strokeColor: '#34ba23',
},
smallLobster: {
size: 30,
font: 'lobster'
}
}
domKeyPress();

stage.addChild(text);
stage.update();
}

</script>

</head>
<body onload="init()">
<div>
<textarea id="domText" rows="4" cols="60">{red}This{/} {fun, smallLobster}part{/} is {fillColor: 'pink', strokeColor: 'red'}pink with a red stroke{/}, and this part is {size: 60}bigger, with {fillColor: '#00ff0088'}green words{/}{/}. Amazing.
</textarea>
</div>
</body>
</html>
84 changes: 84 additions & 0 deletions examples/RichText/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<title>txt: RichText example</title>

<script src="../../dist/easeljs.js"></script>
<script type="text/javascript" src="../../dist/txt.js"></script>

<style>
input {
border: 2px solid black;
padding: 15px;
margin: 0 0 10px 0;
}
</style>
<script type="text/javascript">
var canvas;
var stage;
var domText;
var text2;
var formatter;

var PIXEL_RATIO = (function () {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();

createHiDPICanvas = function (w, h, ratio) {
if (!ratio) {
ratio = PIXEL_RATIO;
}
var can = document.createElement("canvas");
can.width = w * ratio;
can.height = h * ratio;
can.style.width = w + "px";
can.style.height = h + "px";
can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
return can;
}

function domKeyPress() {
text2.richText = domText.value;
}

function init() {
domText = document.getElementById("domText");
domText.onkeyup = domKeyPress;
canvas = createHiDPICanvas(1500, 1000, 1);
document.body.appendChild(canvas);
stage = new createjs.Stage(canvas);
stage.density = 2;

text2 = new txt.RichText({
font: 'lobster',
size: 36,
fillColor: "#6666FF",
lineHeight: 50,
width: 850,
x: 10,
y: 280,
});

domKeyPress();

stage.addChild(text2);

stage.update();
}

</script>

</head>
<body onload="init()">
<div>
<textarea id="domText" rows="4" cols="60">This part is {fillColor: 'pink', strokeColor: 'red'}pink with a red stroke{/}, and this part is {size: 60}bigger, with {fillColor: '#00ff0088'}green words{/}{/}. Amazing.</textarea>
</div>
</body>
</html>
699 changes: 356 additions & 343 deletions font/lobster.txt

Large diffs are not rendered by default.

111 changes: 111 additions & 0 deletions src/Formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import TextContainer from "./TextContainer";

export default class Formatter {

static globalShortcuts = {};
shortcuts = {};

getObject(s: string) {
return this._richTextToTextAndStyle(s);
}

setRichText(container: TextContainer, s: string, layout = true) {
const o = this.getObject(s);
container.text = o.text;
container.style = o.style;
if (layout) {
container.layout();
}
}

_richTextToTextAndStyle(s) {
let text = '';
const style = [];

const concatenatedStyles = {};
let currentStyleString = '';
const stylePropsTrail = [];
let inStyleTag = false;

for (let i = 0; i < s.length; i++) {
const c = s[i];
if (inStyleTag && c == '{' && s[i + 1] == '/' && s[i + 2] == '}') {
inStyleTag = false;
i += 2;
continue;
}
if (inStyleTag && c == '}') {
inStyleTag = false;

// Closing tag
if (currentStyleString == '/') {
if (stylePropsTrail.length == 0) {
console.warn('Extra closing tag found');
continue;
}
const currentStyleProps = stylePropsTrail.pop();
for (const styleName of currentStyleProps) {
concatenatedStyles[styleName].pop();
}
continue;
}

let currentStylesObject = {};
const currentStyleProps = [];

const shortcuts = Object.assign({}, this.shortcuts, Formatter.globalShortcuts);
const styleParts = currentStyleString.split(',');
const mapped = styleParts.map((part) => {
part = part.trim();
if (shortcuts.hasOwnProperty(part)) {
part = JSON.stringify(shortcuts[part]);
part = part.substring(1, part.length - 1);
}
return part;
});
currentStyleString = mapped.join(',');

try {
currentStylesObject = (new Function(`return {${currentStyleString}}`))();
} catch (e) {
console.warn(`Could not parse "${currentStyleString}"`);
}

for (const k in currentStylesObject) {
if (!concatenatedStyles.hasOwnProperty(k)) {
concatenatedStyles[k] = [];
}
concatenatedStyles[k].push(currentStylesObject[k]);
currentStyleProps.push(k);
}

stylePropsTrail.push(currentStyleProps);
continue;
}
if (inStyleTag) {
currentStyleString += c;
}
if (!inStyleTag && c == '{') {
inStyleTag = true;
currentStyleString = '';
continue;
}
if (!inStyleTag) {
text += c;

const computedStyles: any = {};
for (const k in concatenatedStyles) {
const styleValues = concatenatedStyles[k];
const computedValue = styleValues[styleValues.length - 1];
if (computedValue !== undefined) {
computedStyles[k] = computedValue;
}
}

style.push(computedStyles);
}
}
return {text, style};
}

}
34 changes: 34 additions & 0 deletions src/RichText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Text from "./Text";
import Formatter from "./Formatter";
import {ConstructObj} from "./Interfaces";

export default class RichText extends Text {

_formatter: Formatter;
_richText = '';

constructor(props: ConstructObj = null) {
super(props);
if (props.text) {
this._richText = props.text;
}
this._formatter = new Formatter();
}

get formatter() {
return this._formatter;
}

get richText() {
return this._richText;
}

set richText(s) {
if (s === this._richText) {
return;
}
this._richText = s;
this._formatter.setRichText(this, s);
}

}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export { default as Path, PathAlign, PathFit } from "./Path";
export { default as PathText } from "./PathText";
export { default as VerticalAlign } from "./VerticalAlign";
export { default as Word } from "./Word";
export { default as Formatter } from "./Formatter";
export { default as RichText } from "./RichText";

import copyEventListeners from "./utils/apply-shape-event-listeners";

Expand Down
Loading