Wrap when rasterizing text

main
Isaiah Odhner 2016-11-05 19:52:44 +00:00
parent 69f459fe47
commit 401fa043b6
4 changed files with 131 additions and 22 deletions

View File

@ -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.

View File

@ -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

View File

@ -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);
});
}
};

View File

@ -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