Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added document tool to upload images #42

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
1 change: 1 addition & 0 deletions client-data/board.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<script src="../tools/text/text.js"></script>
<script src="../tools/eraser/eraser.js"></script>
<script src="../tools/hand/hand.js"></script>
<script src="../tools/document/document.js"></script>
<script src="../tools/zoom/zoom.js"></script>
<script src="../js/canvascolor/canvascolor.min.js"></script>
</body>
Expand Down
102 changes: 102 additions & 0 deletions client-data/tools/document/document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
(function documents() { //Code isolation


var xlinkNS = "http://www.w3.org/1999/xlink";
var imgCount = 1;

function assert_count() {
if (Tools.svg.querySelectorAll("image").length > Tools.server_config.MAX_DOCUMENT_COUNT) {
alert("Too many documents exist already");
throw new Error("Too many documents exist already");
}
}

function onstart() {
var fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "image/*";
fileInput.click();
fileInput.addEventListener("change", () => {
finnboeger marked this conversation as resolved.
Show resolved Hide resolved
assert_count();

var reader = new FileReader();
reader.readAsDataURL(fileInput.files[0]);

reader.onload = function (e) {
// use canvas to compress image
var image = new Image();
image.src = e.target.result;
image.onload = function () {

assert_count();

var uid = Tools.generateUID("doc"); // doc for document

var ctx, size;
var scale = 1;

do {
ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = image.width * scale;
ctx.canvas.height = image.height * scale;
ctx.drawImage(image, 0, 0, image.width * scale, image.height * scale);
var dataURL = ctx.canvas.toDataURL("image/webp", 0.7);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably use jpeg by default here... We can make it configurable, though.

https://caniuse.com/#feat=webp

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jpeg does not support transparency, which would require us to check what kind of image was uploaded and if it made use of the alpha channel.
If it is a png with transparency we would not be able to compress it other than by shrinking the dimensions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest using a webp polyfill, e.g. https://github.com/chase-moskal/webp-hero

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's maybe start with JPEG, and then add support for webp, with a polyfill, in a second pull request ? webp-hero won't work as-is with data urls...


// Compressed file size as data url, approximately 1/3 larger than as bytestream
size = dataURL.length;

console.log(size, scale);
// attempt again with an image that is at least 10% smaller
scale = scale * Math.sqrt(Math.min(
0.9,
Tools.server_config.MAX_DOCUMENT_SIZE / size
));
} while (size > Tools.server_config.MAX_DOCUMENT_SIZE);

var msg = {
id: uid,
type: "doc",
data: dataURL,
size: size,
w: this.width * scale || 300,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This uses the newly reduced value for scale. I think it was meant to use the previous value, which is the value used with drawImage()

h: this.height * scale || 300,
x: (100 + document.documentElement.scrollLeft) / Tools.scale + 10 * imgCount,
y: (100 + document.documentElement.scrollTop) / Tools.scale + 10 * imgCount
//fileType: fileInput.files[0].type
};

assert_count();

draw(msg);
Tools.send(msg,"Document");
imgCount++;
};
};
// Tools.change(Tools.prevToolName);
});
}

function draw(msg) {
var aspect = msg.w/msg.h;
var img = Tools.createSVGElement("image");
img.id=msg.id;
img.setAttribute("class", "layer-"+Tools.layer);
img.setAttributeNS(xlinkNS, "href", msg.data);
img.x.baseVal.value = msg['x'];
img.y.baseVal.value = msg['y'];
img.setAttribute("width", 400*aspect);
img.setAttribute("height", 400);
Comment on lines +88 to +89
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably have a maximum and a minimum image size, and scale only images that do not fit.

Tools.drawingArea.appendChild(img);

}

Tools.add({
"name": "Document",
//"shortcut": "",
"draw": draw,
"onstart": onstart,
"oneTouch":true,
"icon": "/tools/document/icon.svg",
});

})(); //End of code isolation
17 changes: 17 additions & 0 deletions client-data/tools/document/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions server/client_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const config = require("./configuration");

/** Settings that should be handed through to the clients */
module.exports = {
"MAX_DOCUMENT_SIZE": config.MAX_DOCUMENT_SIZE,
"MAX_DOCUMENT_COUNT": config.MAX_DOCUMENT_COUNT,
"MAX_BOARD_SIZE": config.MAX_BOARD_SIZE,
"MAX_EMIT_COUNT": config.MAX_EMIT_COUNT,
"MAX_EMIT_COUNT_PERIOD": config.MAX_EMIT_COUNT_PERIOD,
Expand Down
6 changes: 6 additions & 0 deletions server/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ module.exports = {
/** Maximum value for any x or y on the board */
MAX_BOARD_SIZE: parseInt(process.env['WBO_MAX_BOARD_SIZE']) || 65536,

/** Maximum size of uploaded documents default 1MB */
MAX_DOCUMENT_SIZE: parseInt(process.env['WBO_MAX_DOCUMENT_SIZE']) || 1048576,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large images failed for me, causing a transport error. The socket.io default maxHttpBufferSize is 1e6, so I changed this to 1e6 - 500.


/** Maximum number of documents allowed */
MAX_DOCUMENT_COUNT: parseInt(process.env['WBO_MAX_DOCUMENT_COUNT']) || 5,

/** Maximum messages per user over the given time period before banning them */
MAX_EMIT_COUNT: parseInt(process.env['WBO_MAX_EMIT_COUNT']) || 128,

Expand Down
20 changes: 20 additions & 0 deletions server/sockets.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function socketConnection(socket) {
var board = await getBoard(name);
board.users.add(socket.id);
log('board joined', { 'board': board.name, 'users': board.users.size });

return board;
}

Expand Down Expand Up @@ -96,6 +97,25 @@ function socketConnection(socket) {
return;
}

if (message.data.type === "doc") {
getBoard(boardName).then(boardData => {
let existingDocuments = 0;
for (key in boardData.board) {
if (boardData.board[key].type === "doc") {
existingDocuments += 1;
}
if (existingDocuments >= config.MAX_DOCUMENTS) {
console.warn("Received too many documents");
return;
}
}
finnboeger marked this conversation as resolved.
Show resolved Hide resolved
});

if (!message.data.size || message.data.size > config.MAX_DOCUMENT_SIZE) {
finnboeger marked this conversation as resolved.
Show resolved Hide resolved
console.warn("Received too large file");
return;
}
}

// Save the message in the board
handleMessage(boardName, data, socket);
Expand Down