Wrap when rasterizing text
parent
69f459fe47
commit
401fa043b6
|
@ -6,8 +6,8 @@ A nice web-based MS Paint remake and more... [Try it out!][jspaint web app]
|
|||
<!-- It's not published yet...
|
||||
You can also install it [on the Chrome Web Store][jspaint chrome app].
|
||||
-->
|
||||
You can also install it as a Chrome app.
|
||||
<!-- Coming Soon to Web Stores near you! -->
|
||||
<!-- You can also install it as a Chrome app. -->
|
||||
|
||||
|
||||
The goal is to remake MS Paint
|
||||
(including its [little-known features](#did-you-know)),
|
||||
|
@ -76,7 +76,7 @@ I want to bring good old paint into the modern era.
|
|||
* [Extended Editing](#extended-editing)
|
||||
* Proportionally resize the selection or canvas by holding <kbd>Shift</kbd>
|
||||
* After adding text, save as SVG or HTML with selectable text
|
||||
(invisible text positioned over an embeded bitmap image)
|
||||
(invisible text positioned over an embedded bitmap image)
|
||||
* <kbd>Alt</kbd> as a shortcut for the eyedropper, as long as it doesn't conflict with keyboard navigation of menus
|
||||
* Alternate themes (You can already theoretically style it with browser extensions like Stylebot or Stylish)
|
||||
* Noncontiguous fill (Probably by holding <kbd>Shift</kbd> when using the fill tool)
|
||||
|
@ -89,7 +89,6 @@ I want to bring good old paint into the modern era.
|
|||
* The Magnifier's viewport preview
|
||||
* Shape styles on most of the shape tools
|
||||
* The polygon tool needs some work
|
||||
* Handles on selections and text boxes
|
||||
* [This entire document full of things to do](TODO.md)
|
||||
|
||||
Clipboard support is somewhat limited.
|
||||
|
|
8
TODO.md
8
TODO.md
|
@ -33,15 +33,10 @@
|
|||
* `*.htm` to `*.html`
|
||||
|
||||
|
||||
* Make storage quota dialogue clearer
|
||||
* Also add a message like "All cleaned up!" (probably with an additional Close button)
|
||||
|
||||
|
||||
* Visual
|
||||
* Warning sign for "Save changes to X?" dialogue
|
||||
* Error symbol for error message dialogues
|
||||
* The window close button uses text; font rendering is not consistent
|
||||
* The menus use text; the arrow character is converted to an icon on some mobile devices
|
||||
* The progress bar (Rendering GIF) is left native
|
||||
* Use win98 default scrollbar size
|
||||
* Menu separator spacing
|
||||
|
@ -183,9 +178,8 @@
|
|||
|
||||
|
||||
* Text
|
||||
* Wrapping!
|
||||
* Underline
|
||||
* Expanding to new lines
|
||||
* Expand box to make room for new lines
|
||||
* Minimum size of 3em x 1em
|
||||
* Store position of FontBox
|
||||
* Keep an old TextBox while drawing a new one
|
||||
|
|
130
src/TextBox.js
130
src/TextBox.js
|
@ -29,7 +29,7 @@ function TextBox(x, y, width, height){
|
|||
webkitWritingMode: font.vertical ? "vertical-lr" : "",
|
||||
linesupWritingMode: font.vertical ? "vertical-lr" : "",
|
||||
nonsenseWritingMode: font.vertical ? "vertical-lr" : "",
|
||||
totesfakeWritingMode: font.vertical ? "vertical-lr" : "",
|
||||
quitefakeWritingMode: font.vertical ? "vertical-lr" : "",
|
||||
lineHeight: font.size * font.line_scale * magnification + "px",
|
||||
color: font.color,
|
||||
background: font.background,
|
||||
|
@ -134,6 +134,117 @@ TextBox.prototype.instantiate = function(){
|
|||
}
|
||||
};
|
||||
|
||||
// var wrap = function(ctx, text, x, y, maxWidth, lineHeight) {
|
||||
// var original_lines = text.split(/(\r\n|[\n\v\f\r\x85\u2028\u2029])/);
|
||||
// var lines = [];
|
||||
// for(var i = 0; i < original_lines.length; i++){
|
||||
// var original_line = original_lines[i];
|
||||
// var words = original_line.split(' ');
|
||||
// var line = '';
|
||||
// for(var n = 0, len = words.length; n < len; n++){
|
||||
// var word = words[n];
|
||||
// var testLine = line + word + ' ';
|
||||
// var metrics = ctx.measureText(testLine);
|
||||
// var testWidth = metrics.width;
|
||||
// if (testWidth > maxWidth) {
|
||||
// // we need to break this,
|
||||
// // but let's see if we can start the next line with this word,
|
||||
// // or if we need to break the word
|
||||
// testLine = word + ' ';
|
||||
// metrics = ctx.measureText(testLine);
|
||||
// testWidth = metrics.width;
|
||||
// if (testWidth > maxWidth) {
|
||||
// // break this word
|
||||
// console.log("breaking word", word);
|
||||
// // if(word.indexOf('-') > -1){
|
||||
|
||||
// // }
|
||||
// var remaining_word = word;
|
||||
// var include_start_of_line = true;
|
||||
// while(remaining_word.length){
|
||||
// for(var slice_index = remaining_word.length; slice_index >= 0; slice_index--){
|
||||
// testLine = remaining_word.slice(0, slice_index);
|
||||
// if (include_start_of_line) {
|
||||
// testLine = line + testLine;
|
||||
// }
|
||||
// console.log(include_start_of_line, slice_index, line, testLine);
|
||||
// metrics = ctx.measureText(testLine);
|
||||
// testWidth = metrics.width;
|
||||
// if (testWidth <= maxWidth) {
|
||||
// line = testLine;
|
||||
// lines.push({ text: line, x: x, y: y });
|
||||
// remaining_word = remaining_word.slice(slice_index);
|
||||
// y += lineHeight;
|
||||
// break;
|
||||
// }
|
||||
// if (slice_index === 0) {
|
||||
// console.log("discarding", remaining_word);
|
||||
// remaining_word = "";
|
||||
// }
|
||||
// }
|
||||
// include_start_of_line = false;
|
||||
// }
|
||||
// line = remaining_word + ' ';
|
||||
// // lines.push({ text: line, x: x, y: y });
|
||||
// // line = word + ' ';
|
||||
// // y += lineHeight;
|
||||
// } else {
|
||||
// // start the next line with this word
|
||||
// lines.push({ text: line, x: x, y: y });
|
||||
// line = testLine;
|
||||
// y += lineHeight;
|
||||
// }
|
||||
// } else {
|
||||
// line = testLine;
|
||||
// }
|
||||
// }
|
||||
|
||||
// lines.push({ text: line, x: x, y: y });
|
||||
// y += lineHeight;
|
||||
// }
|
||||
// return lines;
|
||||
// };
|
||||
|
||||
function draw_text_wrapped(ctx, text, x, y, maxWidth, lineHeight) {
|
||||
var original_lines = text.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/);
|
||||
// console.log("original_lines", original_lines);
|
||||
for(var j = 0; j < original_lines.length; j++){
|
||||
var original_line = original_lines[j];
|
||||
var words = original_line.split(' ');
|
||||
var line = '';
|
||||
var test;
|
||||
var metrics;
|
||||
// ctx.fillRect(x-5, y, 5, 5);
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
test = words[i];
|
||||
metrics = ctx.measureText(test);
|
||||
// TODO: break words on hyphens and perhaps other characters
|
||||
while (metrics.width > maxWidth) {
|
||||
// Determine how much of the word will fit
|
||||
test = test.substring(0, test.length - 1);
|
||||
metrics = ctx.measureText(test);
|
||||
}
|
||||
if (words[i] != test) {
|
||||
words.splice(i + 1, 0, words[i].substr(test.length));
|
||||
words[i] = test;
|
||||
}
|
||||
|
||||
test = line + words[i] + ' ';
|
||||
metrics = ctx.measureText(test);
|
||||
|
||||
if (metrics.width > maxWidth && i > 0) {
|
||||
ctx.fillText(line, x, y);
|
||||
line = words[i] + ' ';
|
||||
y += lineHeight;
|
||||
} else {
|
||||
line = test;
|
||||
}
|
||||
}
|
||||
ctx.fillText(line, x, y);
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
TextBox.prototype.draw = function(){
|
||||
var tb = this;
|
||||
var text = tb.$editor.val();
|
||||
|
@ -145,11 +256,18 @@ TextBox.prototype.draw = function(){
|
|||
ctx.fillStyle = font.color;
|
||||
var style_ = (font.bold ? (font.italic ? "italic bold " : "bold ") : (font.italic ? "italic " : ""));
|
||||
ctx.font = style_ + font.size + "px " + font.family;
|
||||
ctx.textBaseline = "middle";
|
||||
var lines = text.split("\n")
|
||||
for(var i=0; i<lines.length; i++){
|
||||
ctx.fillText(lines[i], tb.x+1, tb.y+1 + (i+0.5)*(font.size * font.line_scale), tb.width);
|
||||
}
|
||||
ctx.textBaseline = "top";
|
||||
// var lines = text.split("\n");
|
||||
// for(var i=0; i<lines.length; i++){
|
||||
// ctx.fillText(lines[i], tb.x+1, tb.y+1 + (i+0.5)*(font.size * font.line_scale), tb.width);
|
||||
// }
|
||||
// var lines = wrap(ctx, text, tb.x+1, tb.y+1, tb.width, font.size * font.line_scale);
|
||||
// for (var i = 0; i < lines.length; i++){
|
||||
// var line = lines[i];
|
||||
// ctx.fillText(line.text, line.x, line.y);
|
||||
// }
|
||||
var max_width = Math.max(tb.width, font.size);
|
||||
draw_text_wrapped(ctx, text, tb.x+1, tb.y+1, max_width, font.size * font.line_scale);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ function render_brush(ctx, shape, size){
|
|||
}else if(shape === "vertical"){
|
||||
draw_line(ctx, mid_x, top, mid_x, size);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function draw_ellipse(ctx, x, y, w, h, stroke, fill){
|
||||
|
||||
|
@ -175,7 +175,7 @@ function bresenham_line(x1, y1, x2, y2, callback){
|
|||
}
|
||||
|
||||
function brosandham_line(x1, y1, x2, y2, callback){
|
||||
// Bresenham's line algorithm modified to callback in-between going horizontal and vertical
|
||||
// Bresenham's line argorithm with a callback between going horizontal and vertical
|
||||
x1=~~x1, x2=~~x2, y1=~~y1, y2=~~y2;
|
||||
|
||||
var dx = Math.abs(x2 - x1);
|
||||
|
@ -279,9 +279,7 @@ function draw_fill(ctx, x, y, fill_r, fill_g, fill_b, fill_a){
|
|||
function apply_image_transformation(fn){
|
||||
// Apply an image transformation function to either the selection or the entire canvas
|
||||
var new_canvas = new Canvas();
|
||||
var original_canvas =
|
||||
selection? selection. // Selection!? Selection!
|
||||
canvas: canvas; // Canvas, canvas, canvas.
|
||||
var original_canvas = selection ? selection.canvas: canvas;
|
||||
|
||||
// Sometimes selection.canvas is an Image
|
||||
// Maybe that should be changed instead having of this here
|
||||
|
|
Loading…
Reference in New Issue