summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-xMakefile5
-rw-r--r--debian/changelog7
-rw-r--r--debian/control2
-rw-r--r--html/index.html4
-rw-r--r--html/whiteboard.css41
-rw-r--r--html/whiteboard.js68
-rw-r--r--qrcode.cpp32
-rw-r--r--qrcode.h5
-rw-r--r--whiteboard.cpp16
9 files changed, 178 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index a387f1c..e0507bf 100755
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@ endif
LIBS=-lfcgi -lboost_filesystem
INCLUDES=-I.
-HEADERS=file.h config.h
+HEADERS=file.h config.h qrcode.h
SOURCES=$(HEADERS:.h=.cpp)
OBJECTS=$(HEADERS:.h=.o)
TARGETS=whiteboard.fcgi
@@ -59,6 +59,9 @@ LIBS+= \
-lstdc++fs
endif
+CXXFLAGS+=$(shell pkg-config --cflags qrcodegencpp Magick++ fmt)
+LIBS+=$(shell pkg-config --libs qrcodegencpp Magick++ fmt)
+
build: $(TARGETS)
all: build
diff --git a/debian/changelog b/debian/changelog
index 3db9796..1ea5c87 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+whiteboard (1.1) unstable; urgency=medium
+
+ * Compress Javascript
+ * Add QR Code
+
+ -- Roland Reichwein <mail@reichwein.it> Sat, 03 Dec 2022 16:10:06 +0100
+
whiteboard (1.0) unstable; urgency=medium
* Initial release
diff --git a/debian/control b/debian/control
index 0729cbb..ec9b915 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: whiteboard
Section: web
Priority: optional
Maintainer: Roland Reichwein <mail@reichwein.it>
-Build-Depends: debhelper (>= 12), libboost-all-dev | libboost1.71-all-dev, clang | g++-9, uglifyjs, python3-pkg-resources, htmlmin, cleancss, libfcgi-dev, libqrcodegencpp-dev, libmagick++-dev
+Build-Depends: debhelper (>= 12), libboost-all-dev | libboost1.71-all-dev, clang | g++-9, uglifyjs, python3-pkg-resources, htmlmin, cleancss, libfcgi-dev, libqrcodegencpp-dev, libmagick++-dev, pkg-config, libfmt-dev
Standards-Version: 4.5.0
Homepage: http://www.reichwein.it/whiteboard/
diff --git a/html/index.html b/html/index.html
index 04522d7..e8a07be 100644
--- a/html/index.html
+++ b/html/index.html
@@ -10,12 +10,16 @@
<script src="whiteboard.js"></script>
</head>
<body onload="init();">
+ <div class="qrwindow" id="qrwindow" hidden>
+ <img class="qrcode" id="qrcode"></img>
+ </div>
<div class="page">
<h1><img class="banner" src="banner256.png" alt="Reichwein.IT"/> Whiteboard</h1>
<textarea cols="80" rows="30" id="board" name="board"></textarea>
<br/>
<br/>
<button class="button" onclick="on_new_page();">New page</button>
+ <button class="button" onclick="on_qrcode();">QR code</button>
<br/>
<br/>
Reichwein.IT Whiteboard by <a href="https://www.reichwein.it">https://www.reichwein.it</a><br/>
diff --git a/html/whiteboard.css b/html/whiteboard.css
index 5f804c1..2af2a92 100644
--- a/html/whiteboard.css
+++ b/html/whiteboard.css
@@ -32,6 +32,37 @@ textarea {
resize: none;
}
+.qrwindow {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 400px;
+ height: 400px;
+ margin-top: -200px;
+ margin-left: -200px;
+ background-color: #FFFFFF;
+ opacity: 1;
+ z-index: 10;
+ border-width: 3px;
+ border-style: solid;
+ border-color: #FFFFFF;
+ padding: 10pt;
+ box-sizing: border-box;
+}
+
+.qrcode {
+ width: 374px;
+ height: 374px;
+
+ image-rendering: optimizeSpeed; /* */
+ image-rendering: -moz-crisp-edges; /* Firefox */
+ image-rendering: -o-crisp-edges; /* Opera */
+ image-rendering: -webkit-optimize-contrast; /* Chrome (and Safari) */
+ image-rendering: pixelated; /* Chrome as of 2019 */
+ image-rendering: optimize-contrast; /* CSS3 Proposed */
+ -ms-interpolation-mode: nearest-neighbor; /* IE8+ */
+}
+
.mobile {
width: 300px;
border-width: 80px 15px 80px 15px;
@@ -67,6 +98,16 @@ img.banner {
}
@media only screen and (min-width: 1px) and (max-width: 630px) {
+
+.qrwindow {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+}
+
}
@media only screen and (min-width: 631px) and (max-width: 950px) {
diff --git a/html/whiteboard.js b/html/whiteboard.js
index d9f0904..c6fb657 100644
--- a/html/whiteboard.js
+++ b/html/whiteboard.js
@@ -62,6 +62,16 @@ class AdjustingTimer {
var timer = new AdjustingTimer();
+function showQRWindow()
+{
+ document.getElementById("qrwindow").style.display = 'block';
+}
+
+function hideQRWindow()
+{
+ document.getElementById("qrwindow").style.display = 'none';
+}
+
function init_board() {
var xhr = new XMLHttpRequest();
@@ -124,6 +134,16 @@ function init_board() {
xhr.send(xmlDocument);
//set_status("Please wait while server prepares " + filename + " ...");
+
+ document.getElementById("qrwindow").onclick = function() {
+ hideQRWindow();
+ }
+
+ document.onkeydown = function(evt) {
+ if (evt.key == "Escape") {
+ hideQRWindow();
+ }
+ }
}
function get_id()
@@ -300,3 +320,51 @@ function checkupdate() {
//set_status("Please wait while server prepares " + filename + " ...");
}
+function on_qrcode()
+{
+ var xhr = new XMLHttpRequest();
+
+ // run on data received back
+ xhr.onreadystatechange = function() {
+ if (this.readyState == 3) {
+ //set_status("Please wait while downloading " + filename + " ...");
+ return;
+ }
+ if (this.readyState != 4) {
+ return;
+ }
+ if (this.status != 200) {
+ //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText);
+ return;
+ }
+
+ if (this.getResponseHeader("Content-Type") == "image/png") {
+ var blob = new Blob([this.response], {type: 'image/png'});
+ var url = URL.createObjectURL(blob);
+ var img = document.getElementById("qrcode");
+ img.src = url;
+ showQRWindow();
+ }
+
+ //set_status(""); // OK
+ }
+
+ var parser = new DOMParser();
+ var xmlDocument = parser.parseFromString("<request></request>", "text/xml");
+
+ var requestElement = xmlDocument.getElementsByTagName("request")[0];
+
+ var commandElement = xmlDocument.createElement("command");
+ commandElement.appendChild(document.createTextNode("qrcode"));
+ requestElement.appendChild(commandElement);
+
+ var idElement = xmlDocument.createElement("url");
+ idElement.appendChild(document.createTextNode(document.location));
+ requestElement.appendChild(idElement);
+
+ xhr.open("POST", "whiteboard.fcgi", true);
+ xhr.setRequestHeader("Content-type", "text/xml");
+ xhr.responseType = 'blob';
+ xhr.send(xmlDocument);
+}
+
diff --git a/qrcode.cpp b/qrcode.cpp
new file mode 100644
index 0000000..cd5fab8
--- /dev/null
+++ b/qrcode.cpp
@@ -0,0 +1,32 @@
+#include "qrcode.h"
+
+#include <fmt/format.h>
+
+#include <qrcodegen/qrcodegen.hpp>
+#include <ImageMagick-6/Magick++.h>
+
+using namespace qrcodegen;
+using namespace Magick;
+
+std::string getQRCode(const std::string& data)
+{
+ QrCode qrc {QrCode::encodeText(data.c_str(), QrCode::Ecc::MEDIUM)};
+
+ int size {qrc.getSize()};
+
+ Image image(fmt::format("{0}x{0}", size).c_str(), "white");
+ image.type(GrayscaleType);
+ //image.size(fmt::format("{0}x{0}", size));
+
+ for (int x = 0; x < size; x++) {
+ for (int y = 0; y < size; y++) {
+ image.pixelColor(x, y, qrc.getModule(x, y) ? "black" : "white");
+ }
+ }
+
+ image.magick("PNG");
+
+ Blob blob;
+ image.write(&blob);
+ return std::string{(char*)blob.data(), blob.length()};
+}
diff --git a/qrcode.h b/qrcode.h
new file mode 100644
index 0000000..f6546f0
--- /dev/null
+++ b/qrcode.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <string>
+
+std::string getQRCode(const std::string& data);
diff --git a/whiteboard.cpp b/whiteboard.cpp
index b3367cf..8df6d73 100644
--- a/whiteboard.cpp
+++ b/whiteboard.cpp
@@ -18,8 +18,11 @@
#include <boost/algorithm/string/trim.hpp>
#include <boost/property_tree/xml_parser.hpp>
+#include <ImageMagick-6/Magick++.h>
+
#include "config.h"
#include "file.h"
+#include "qrcode.h"
namespace pt = boost::property_tree;
using namespace std::string_literals;
@@ -95,6 +98,8 @@ int main(void)
Config config;
data_path = config.getDataPath();
+ Magick::InitializeMagick(NULL); // for qrcode.cpp
+
int result = FCGX_Init();
if (result != 0) { // error on init
fprintf(stderr, "Error: FCGX_Init()\n");
@@ -174,6 +179,17 @@ int main(void)
} else if (command == "newid") {
FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out);
FCGX_PutS(generate_id().c_str(), request.out);
+ } else if (command == "qrcode") {
+ std::string url{xml.get<std::string>("request.url")};
+
+ if (url.size() > 1000)
+ throw std::runtime_error("URL too big");
+
+ std::string pngdata {getQRCode(url)};
+
+ FCGX_PutS("Content-Type: image/png\r\n", request.out);
+ FCGX_FPrintF(request.out, "Content-Length: %d\r\n\r\n", pngdata.size());
+ FCGX_PutStr(pngdata.c_str(), pngdata.size(), request.out);
} else {
throw std::runtime_error("Bad command: "s + command);
}