Skip to content
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