Skip to content

Commit 9ebec03

Browse files
committed
Implemented selection for showText()
1 parent 46a48a5 commit 9ebec03

File tree

2 files changed

+98
-63
lines changed

2 files changed

+98
-63
lines changed

src/canvas.js

Lines changed: 93 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,58 @@ var CanvasGraphics = (function canvasGraphics() {
453453
nextLine: function canvasGraphicsNextLine() {
454454
this.moveText(0, this.current.leading);
455455
},
456-
showText: function canvasGraphicsShowText(text) {
456+
applyTextTransforms: function canvasApplyTransforms() {
457+
var ctx = this.ctx;
458+
var current = this.current;
459+
var textHScale = current.textHScale;
460+
var font = current.font;
461+
462+
ctx.transform.apply(ctx, current.textMatrix);
463+
ctx.scale(1, -1);
464+
ctx.translate(current.x, -1 * current.y);
465+
ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX);
466+
ctx.scale(1 / textHScale, 1);
467+
},
468+
getTextGeometry: function canvasGetTextGeometry() {
469+
var geom = {};
470+
var ctx = this.ctx;
471+
var font = this.current.font;
472+
var ctxMatrix = ctx.mozCurrentTransform;
473+
if (ctxMatrix) {
474+
var bl = Util.applyTransform([0, 0], ctxMatrix);
475+
var tr = Util.applyTransform([1, 1], ctxMatrix);
476+
geom.x = bl[0];
477+
geom.y = bl[1];
478+
geom.hScale = tr[0] - bl[0];
479+
geom.vScale = tr[1] - bl[1];
480+
}
481+
var spaceGlyph = font.charsToGlyphs(' ', true);
482+
// Hack (sometimes space is not encoded)
483+
if (spaceGlyph.length === 0 || spaceGlyph[0].width === 0)
484+
spaceGlyph = font.charsToGlyphs('i', true);
485+
// Fallback
486+
if (spaceGlyph.length === 0 || spaceGlyph[0].width === 0)
487+
spaceGlyph = [ {width:0} ];
488+
geom.spaceWidth = spaceGlyph[0].width;
489+
return geom;
490+
},
491+
pushTextDivs: function canvasGraphicsPushTextDivs(text) {
492+
var div = document.createElement('div');
493+
var fontSize = this.current.fontSize;
494+
var fontHeight = text.geom.vScale * fontSize;
495+
496+
div.style.fontSize = fontHeight + 'px';
497+
// TODO: family should be '= font.loadedName', but some fonts don't
498+
// have spacing info (cf. fonts.js > Font > fields > htmx)
499+
div.style.fontFamily = 'serif';
500+
div.style.left = text.geom.x + 'px';
501+
div.style.top = (text.geom.y - fontHeight) + 'px';
502+
div.innerHTML = text.str;
503+
div.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
504+
div.dataset.textLength = text.length;
505+
this.textDivs.push(div);
506+
},
507+
showText: function canvasGraphicsShowText(str, skipTextSelection) {
457508
function unicodeToChar(unicode) {
458509
return (unicode >= 0x10000) ?
459510
String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10),
@@ -463,14 +514,24 @@ var CanvasGraphics = (function canvasGraphics() {
463514
var ctx = this.ctx;
464515
var current = this.current;
465516
var font = current.font;
466-
var glyphs = font.charsToGlyphs(text);
517+
var glyphs = font.charsToGlyphs(str);
467518
var fontSize = current.fontSize;
468519
var charSpacing = current.charSpacing;
469520
var wordSpacing = current.wordSpacing;
470521
var textHScale = current.textHScale;
471522
var glyphsLength = glyphs.length;
472-
var text = { chars:'', width:0 };
523+
var textLayer = this.textLayer;
524+
var text = { str:'', length:0, canvasWidth:0, geom:{}};
525+
var textSelection = textLayer && !skipTextSelection ? true : false;
526+
527+
if (textSelection) {
528+
ctx.save();
529+
this.applyTextTransforms();
530+
text.geom = this.getTextGeometry();
531+
ctx.restore();
532+
}
473533

534+
// Type3 fonts - each glyph is a "mini-PDF"
474535
if (font.coded) {
475536
ctx.save();
476537
ctx.transform.apply(ctx, current.textMatrix);
@@ -498,17 +559,14 @@ var CanvasGraphics = (function canvasGraphics() {
498559
ctx.translate(charWidth, 0);
499560
current.x += charWidth;
500561

501-
text.chars += unicodeToChar(glyph.unicode);
502-
text.width += charWidth;
562+
text.str += unicodeToChar(glyph.unicode);
563+
text.canvasWidth += charWidth;
564+
text.length++;
503565
}
504566
ctx.restore();
505567
} else {
506568
ctx.save();
507-
ctx.transform.apply(ctx, current.textMatrix);
508-
ctx.scale(1, -1);
509-
ctx.translate(current.x, -1 * current.y);
510-
ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX);
511-
ctx.scale(1 / textHScale, 1);
569+
this.applyTextTransforms();
512570

513571
var width = 0;
514572
for (var i = 0; i < glyphsLength; ++i) {
@@ -524,12 +582,18 @@ var CanvasGraphics = (function canvasGraphics() {
524582
ctx.fillText(char, width, 0);
525583
width += charWidth;
526584

527-
text.chars += char;
528-
text.width += charWidth;
585+
text.str += char;
586+
text.canvasWidth += charWidth;
587+
text.length++;
529588
}
589+
530590
current.x += width;
531591
ctx.restore();
532592
}
593+
594+
if (textSelection)
595+
this.pushTextDivs(text);
596+
533597
return text;
534598
},
535599
showSpacedText: function canvasGraphicsShowSpacedText(arr) {
@@ -540,32 +604,13 @@ var CanvasGraphics = (function canvasGraphics() {
540604
var arrLength = arr.length;
541605
var textLayer = this.textLayer;
542606
var font = current.font;
543-
var text = {str:'', length:0, canvasWidth:0, spaceWidth:0, geom:{}};
544-
545-
if (textLayer) {
546-
text.spaceWidth = this.current.font.charsToGlyphs(' ')[0].width;
547-
if (!text.spaceWidth>0) {
548-
// Hack (space is sometimes not encoded)
549-
text.spaceWidth = this.current.font.charsToGlyphs('i')[0].width;
550-
}
607+
var text = {str:'', length:0, canvasWidth:0, geom:{}};
608+
var textSelection = textLayer ? true : false;
551609

552-
// Compute text.geom
553-
// TODO: refactor the series of transformations below, and share it with showText()
610+
if (textSelection) {
554611
ctx.save();
555-
ctx.transform.apply(ctx, current.textMatrix);
556-
ctx.scale(1, -1);
557-
ctx.translate(current.x, -1 * current.y);
558-
ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX);
559-
ctx.scale(1 / textHScale, 1);
560-
var ctxMatrix = ctx.mozCurrentTransform;
561-
if (ctxMatrix) {
562-
var bl = Util.applyTransform([0, 0], ctxMatrix);
563-
var tr = Util.applyTransform([1, 1], ctxMatrix);
564-
text.geom.x = bl[0];
565-
text.geom.y = bl[1];
566-
text.geom.xFactor = tr[0] - bl[0];
567-
text.geom.yFactor = tr[1] - bl[1];
568-
}
612+
this.applyTextTransforms();
613+
text.geom = this.getTextGeometry();
569614
ctx.restore();
570615
}
571616

@@ -575,47 +620,35 @@ var CanvasGraphics = (function canvasGraphics() {
575620
var spacingLength = -e * 0.001 * fontSize * textHScale;
576621
current.x += spacingLength;
577622

578-
if (textLayer) {
623+
if (textSelection) {
579624
// Emulate precise spacing via HTML spaces
580625
text.canvasWidth += spacingLength;
581-
if (e<0 && text.spaceWidth>0) { // avoid div by zero
582-
var numFakeSpaces = Math.round(-e / text.spaceWidth);
626+
if (e<0 && text.geom.spaceWidth>0) { // avoid div by zero
627+
var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
583628
for (var j = 0; j < numFakeSpaces; ++j)
584629
text.str += '&nbsp;';
585630
text.length += numFakeSpaces>0 ? 1 : 0;
586631
}
587632
}
588633
} else if (isString(e)) {
589-
var shownText = this.showText(e);
634+
var shownText = this.showText(e, true);
590635

591-
if (textLayer) {
592-
if (shownText.chars === ' ') {
593-
text.str += '&nbsp;';
636+
if (textSelection) {
637+
if (shownText.str === ' ') {
638+
text.str += '&nbsp;';
594639
} else {
595-
text.str += shownText.chars;
640+
text.str += shownText.str;
596641
}
597-
text.canvasWidth += shownText.width;
642+
text.canvasWidth += shownText.canvasWidth;
598643
text.length += e.length;
599644
}
600645
} else {
601646
malformed('TJ array element ' + e + ' is not string or num');
602647
}
603648
}
604-
605-
if (textLayer) {
606-
var div = document.createElement('div');
607-
var fontHeight = text.geom.yFactor * fontSize;
608-
div.style.fontSize = fontHeight + 'px';
609-
// TODO: family should be '= font.loadedName', but some fonts don't
610-
// have spacing info (cf. fonts.js > Font > fields > htmx)
611-
div.style.fontFamily = 'serif';
612-
div.style.left = text.geom.x + 'px';
613-
div.style.top = (text.geom.y - fontHeight) + 'px';
614-
div.innerHTML = text.str;
615-
div.dataset.canvasWidth = text.canvasWidth * text.geom.xFactor;
616-
div.dataset.textLength = text.length;
617-
this.textDivs.push(div);
618-
}
649+
650+
if (textSelection)
651+
this.pushTextDivs(text);
619652
},
620653
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
621654
this.nextLine();

src/fonts.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ var Font = (function Font() {
17971797
return rule;
17981798
},
17991799

1800-
charsToGlyphs: function fonts_chars2Glyphs(chars) {
1800+
charsToGlyphs: function fonts_chars2Glyphs(chars, suppressWarnings) {
18011801
var charsCache = this.charsCache;
18021802
var glyphs;
18031803

@@ -1830,7 +1830,8 @@ var Font = (function Font() {
18301830
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
18311831
var glyph = encoding[charcode];
18321832
if ('undefined' == typeof(glyph)) {
1833-
warn('Unencoded charcode ' + charcode);
1833+
if (!suppressWarnings)
1834+
warn('Unencoded charcode ' + charcode);
18341835
glyph = {
18351836
unicode: charcode,
18361837
width: this.defaultWidth
@@ -1847,7 +1848,8 @@ var Font = (function Font() {
18471848
var charcode = chars.charCodeAt(i);
18481849
var glyph = encoding[charcode];
18491850
if ('undefined' == typeof(glyph)) {
1850-
warn('Unencoded charcode ' + charcode);
1851+
if (!suppressWarnings)
1852+
warn('Unencoded charcode ' + charcode);
18511853
glyph = {
18521854
unicode: charcode,
18531855
width: this.defaultWidth

0 commit comments

Comments
 (0)