Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b1fdc3d
adding package lock
archmoj Nov 1, 2018
1afb959
support for multi-line texts
archmoj Nov 2, 2018
336d590
some improvements for line spacing
archmoj Nov 2, 2018
9dc8842
simplified code and added the option of line-spacing in the module
archmoj Nov 2, 2018
ec81611
replace line breaks to new lines
archmoj Nov 2, 2018
e1aacb0
using an alternative for the replacement of br
archmoj Nov 2, 2018
454a9d4
support for sup
archmoj Nov 2, 2018
a29278c
revised code for minHeight
archmoj Nov 3, 2018
d3eafeb
removed superscript-text dep and added functions to parse and write t…
archmoj Nov 3, 2018
fe888a0
supporting bold and italic on webgl texts
archmoj Nov 4, 2018
f7f94ce
added sub
archmoj Nov 4, 2018
ba8fcf8
fix for start of the lines
archmoj Nov 4, 2018
ee6d7fe
parse the whole text instead of line by line
archmoj Nov 4, 2018
db8f78d
working prototype
archmoj Nov 4, 2018
4e7316b
italic bold and sub is now working well
archmoj Nov 4, 2018
c825b5b
superscript is also added to webgl
archmoj Nov 4, 2018
3a52ec1
adjust sub & sup positions and size
archmoj Nov 4, 2018
9f1e1ce
check not to assign undefined style
archmoj Nov 4, 2018
741a022
say no to backslash n
archmoj Nov 6, 2018
6cd3def
debug for multi sub or super scripts
archmoj Nov 6, 2018
79ee64a
mapping for multiple sub or super scripts without recursive calls
archmoj Nov 6, 2018
ed1cadb
support for multiple sub and super scripts
archmoj Nov 6, 2018
115c0f4
options to disable break lines
archmoj Nov 13, 2018
457db88
replace breake line tag with space when breakLines are disabled
archmoj Nov 13, 2018
8c78af8
adding styletags
archmoj Nov 16, 2018
6fe3635
enable tags
archmoj Nov 16, 2018
7e13665
revised vect-text
archmoj Nov 16, 2018
3f745e7
all styling tags are disabled by default
archmoj Nov 16, 2018
a3f30a1
updated readme
archmoj Nov 16, 2018
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ Renders a string to a 2D cell complex
+ `options.orientation` determines the orientation of any output triangles/polygon curves. Must be either `"cw"` for clockwise or `"ccw"` for counter clockwise. Default is `"cw"`.
+ `options.canvas` an optional canvas element
+ `options.context` an optional canvas 2D context
+ `options.styletags.breaklines` if set, break-line tags i.e. < br > could be used in the input to enter new lines.
+ `options.styletags.bolds` if set, parts of the input i.e. between < b > and < /b > would be presented <b>bold</b>.
+ `options.styletags.italics` if set, parts of the input i.e. between < i > and < /i > would be presented <i>italic</i>.
+ `options.styletags.superscripts` if set, parts of the input i.e. between < sup > and < /sup > would be presented in as superscript. Multiple superscipts are also allowded. For example Line 0<sup>Line 1<sup>Line 2</sup></sup>.
+ `options.styletags.subscripts` if set, parts of the input i.e. between < sub > and < /sub > would be presented in as subscript. Multiple subscipts are also allowded. For example: Line 0<sub>Line 1<sub>Line 2</sub></sub>. Note: it is also possible to combine sub and superscripts: A<sub>B<sup>C</sup></sub>.

**Returns** The returned value depends on the type of geometry

Expand Down
278 changes: 263 additions & 15 deletions lib/vtext.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,72 @@ var cleanPSLG = require('clean-pslg')
var cdt2d = require('cdt2d')
var toPolygonCrappy = require('planar-graph-to-polyline')

var TAG_bold = "b"
var CHR_bold = 'b|'

var TAG_italic = "i"
var CHR_italic = 'i|'

var TAG_super = "sup"
var CHR_super0 = '+'
var CHR_super = '+1'

var TAG_sub = "sub"
var CHR_sub0 = '-'
var CHR_sub = '-1'

function parseTag(tag, TAG_CHR, str, map) {

var opnTag = "<" + tag + ">"
var clsTag = "</" + tag + ">"

var nOPN = opnTag.length
var nCLS = clsTag.length

var isRecursive = (TAG_CHR[0] === CHR_super0) ||
(TAG_CHR[0] === CHR_sub0);

var a = 0
var b = -nCLS
while (a > -1) {
a = str.indexOf(opnTag, a)
if(a === -1) break

b = str.indexOf(clsTag, a + nOPN)
if(b === -1) break

if(b <= a) break

for(var i = a; i < b + nCLS; ++i){
if((i < a + nOPN) || (i >= b)) {
map[i] = null
str = str.substr(0, i) + " " + str.substr(i + 1)
} else {
if(map[i] !== null) {
var pos = map[i].indexOf(TAG_CHR[0])
if(pos === -1) {
map[i] += TAG_CHR
} else { // i.e. to handle multiple sub/super-scripts
if(isRecursive) {
// i.e to increase the sub/sup number
map[i] = map[i].substr(0, pos + 1) + (1 + parseInt(map[i][pos + 1])) + map[i].substr(pos + 2)
}
}
}
}
}

var start = a + nOPN
var remainingStr = str.substr(start, b - start)

var c = remainingStr.indexOf(opnTag)
if(c !== -1) a = c
else a = b + nCLS
}

return map
}

function transformPositions(positions, options, size) {
var align = options.textAlign || "start"
var baseline = options.textBaseline || "alphabetic"
Expand Down Expand Up @@ -83,27 +149,169 @@ function transformPositions(positions, options, size) {
})
}

function getPixels(canvas, context, str, size) {
var width = Math.ceil(context.measureText(str).width + 2*size)|0
if(width > 8192) {
throw new Error("vectorize-text: String too long (sorry, this will get fixed later)")
function getPixels(canvas, context, rawString, fontSize, lineSpacing, styletags) {

rawString = rawString.replace(/\n/g, '') // don't accept \n in the input

if(styletags.breaklines === true) {
rawString = rawString.replace(/\<br\>/g, '\n') // replace <br> tags with \n in the string
} else {
rawString = rawString.replace(/\<br\>/g, ' ') // don't accept <br> tags in the input and replace with space in this case
}

var activeStyle = ""
var map = []
for(j = 0; j < rawString.length; ++j) {
map[j] = activeStyle
}
var height = 3 * size
if(canvas.height < height) {
canvas.height = height

if(styletags.bolds === true) map = parseTag(TAG_bold, CHR_bold, rawString, map)
if(styletags.italics === true) map = parseTag(TAG_italic, CHR_italic, rawString, map)
if(styletags.superscripts === true) map = parseTag(TAG_super, CHR_super, rawString, map)
if(styletags.subscripts === true) map = parseTag(TAG_sub, CHR_sub, rawString, map)

var allStyles = []
var plainText = ""
for(j = 0; j < rawString.length; ++j) {
if(map[j] !== null) {
plainText += rawString[j]
allStyles.push(map[j])
}
}

var allTexts = plainText.split('\n')

var numberOfLines = allTexts.length
var lineHeight = Math.round(lineSpacing * fontSize)
var offsetX = fontSize
var offsetY = fontSize * 2
var maxWidth = 0
var minHeight = numberOfLines * lineHeight + offsetY

if(canvas.height < minHeight) {
canvas.height = minHeight
}

context.fillStyle = "#000"
context.fillRect(0, 0, canvas.width, canvas.height)

context.fillStyle = "#fff"
context.fillText(str, size, 2*size)
var i, j, xPos, yPos, zPos
var nDone = 0

//Cut pixels from image
var pixelData = context.getImageData(0, 0, width, height)
var pixels = ndarray(pixelData.data, [height, width, 4])
for(i = 0; i < numberOfLines; ++i) {

var txt = allTexts[i] + '\n'
xPos = 0
yPos = i * lineHeight
zPos = fontSize

var buffer = ""
function writeBuffer() {
if(buffer !== "") {
var delta = context.measureText(buffer).width

context.fillText(buffer, offsetX + xPos, offsetY + yPos)
xPos += delta
}
}

function changeStyle(oldStyle, newStyle) {

function getTextFontSize() {
return "" + Math.round(zPos) + "px ";
}

var ctxFont = "" + context.font;

if(styletags.subscripts === true) {
var oldIndex_Sub = oldStyle.indexOf(CHR_sub0);
var newIndex_Sub = newStyle.indexOf(CHR_sub0);

return pixels.pick(-1,-1,0).transpose(1,0)
var oldSub = (oldIndex_Sub > -1) ? parseInt(oldStyle[1 + oldIndex_Sub]) : 0;
var newSub = (newIndex_Sub > -1) ? parseInt(newStyle[1 + newIndex_Sub]) : 0;

if(oldSub !== newSub) {
ctxFont = ctxFont.replace(getTextFontSize(), "?px ")
zPos *= Math.pow(0.75, (newSub - oldSub))
ctxFont = ctxFont.replace("?px ", getTextFontSize())
}
yPos += 0.25 * lineHeight * (newSub - oldSub);
}

if(styletags.superscripts === true) {
var oldIndex_Super = oldStyle.indexOf(CHR_super0);
var newIndex_Super = newStyle.indexOf(CHR_super0);

var oldSuper = (oldIndex_Super > -1) ? parseInt(oldStyle[1 + oldIndex_Super]) : 0;
var newSuper = (newIndex_Super > -1) ? parseInt(newStyle[1 + newIndex_Super]) : 0;

if(oldSuper !== newSuper) {
ctxFont = ctxFont.replace(getTextFontSize(), "?px ")
zPos *= Math.pow(0.75, (newSuper - oldSuper))
ctxFont = ctxFont.replace("?px ", getTextFontSize())
}
yPos -= 0.25 * lineHeight * (newSuper - oldSuper);
}

if(styletags.bolds === true) {
var wasBold = (oldStyle.indexOf(CHR_bold) > -1)
var is_Bold = (newStyle.indexOf(CHR_bold) > -1)

if(!wasBold && is_Bold) {
if(wasItalic) {
ctxFont = ctxFont.replace("italic ", "italic bold ")
} else {
ctxFont = "bold " + ctxFont
}
}
if(wasBold && !is_Bold) {
ctxFont = ctxFont.replace("bold ", '')
}
}

if(styletags.italics === true) {
var wasItalic = (oldStyle.indexOf(CHR_italic) > -1)
var is_Italic = (newStyle.indexOf(CHR_italic) > -1)

if(!wasItalic && is_Italic) {
ctxFont = "italic " + ctxFont
}
if(wasItalic && !is_Italic) {
ctxFont = ctxFont.replace("italic ", '')
}
}

context.font = ctxFont
}

for(j = 0; j < txt.length; ++j) {
var style = (j + nDone < allStyles.length) ? allStyles[j + nDone] : allStyles[allStyles.length - 1]
if(activeStyle === style) {
buffer += txt[j]
} else {
writeBuffer()
buffer = txt[j]

if(style !== undefined) {
changeStyle(activeStyle, style)
activeStyle = style
}
}
}
writeBuffer()

nDone += txt.length

var width = Math.round(xPos + 2 * offsetX) | 0
if(maxWidth < width) maxWidth = width
}

//Cut pixels from image
var xCut = maxWidth
var yCut = offsetY + lineHeight * numberOfLines
var pixels = ndarray(context.getImageData(0, 0, xCut, yCut).data, [yCut, xCut, 4])
return pixels.pick(-1, -1, 0).transpose(1, 0)
}

function getContour(pixels, doSimplify) {
Expand Down Expand Up @@ -190,8 +398,48 @@ function processPixels(pixels, options, size) {
}

function vectorizeText(str, canvas, context, options) {
var size = options.size || 64


var size = 64
var lineSpacing = 1.25
var styletags = {
breaklines: false,
bolds: false,
italics: false,
subscripts: false,
superscripts: false
}

if(options) {

if(options.size &&
options.size > 0) size =
options.size

if(options.lineSpacing &&
options.lineSpacing > 0) lineSpacing =
options.lineSpacing

if(options.styletags &&
options.styletags.breaklines) styletags.breaklines =
options.styletags.breaklines ? true : false

if(options.styletags &&
options.styletags.bolds) styletags.bolds =
options.styletags.bolds ? true : false

if(options.styletags &&
options.styletags.italics) styletags.italics =
options.styletags.italics ? true : false

if(options.styletags &&
options.styletags.subscripts) styletags.subscripts =
options.styletags.subscripts ? true : false

if(options.styletags &&
options.styletags.superscripts) styletags.superscripts =
options.styletags.superscripts ? true : false
}

context.font = [
options.fontStyle,
options.fontVariant,
Expand All @@ -203,7 +451,7 @@ function vectorizeText(str, canvas, context, options) {
context.textBaseline = "alphabetic"
context.direction = "ltr"

var pixels = getPixels(canvas, context, str, size)
var pixels = getPixels(canvas, context, str, size, lineSpacing, styletags)

return processPixels(pixels, options, size)
}
Loading