From 0b9fac8ff3dd8e0a9ba8b2b8f7e63b2c80fa8b62 Mon Sep 17 00:00:00 2001 From: John Factotum <50942278+johnfactotum@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:10:37 +0800 Subject: [PATCH] Add module for generating quote images --- README.md | 12 ++++++++ quote-image.js | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 quote-image.js diff --git a/README.md b/README.md index 473ad01..99d6002 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,18 @@ These two functions return an object that implements the following interface: - `value`: a string; the default value of the parameter - `.search(map)`: a function, whose argument is a `Map` whose values are `Map`s (i.e. a two-dimensional map). The first key is the namespace of the search parameter. For non-namespaced parameters, the first key must be `null`. The second key is the parameter's name. Returns a string representing the URL of the search results. +### Generating Images for Quotes + +With `quote-image.js`, one can generate shareable images for quotes: + +```js +document.querySelector('foliate-quoteimage').getBlob({ + title: 'The Time Machine', + author: 'H. G. Wells', + text: 'Can an instantaneous cube exist?', +}) +``` + ### Supported Browsers The main use of the library is for use in [Foliate](https://github.com/johnfactotum/foliate), which uses WebKitGTK. As such it's the only engine that has been tested extensively. But it should also work in Chromium and Firefox. diff --git a/quote-image.js b/quote-image.js new file mode 100644 index 0000000..29eadf5 --- /dev/null +++ b/quote-image.js @@ -0,0 +1,83 @@ +const SVG_NS = 'http://www.w3.org/2000/svg' + +// bisect +const fit = (el, a = 1, b = 50) => { + const c = Math.floor(a + (b - a) / 2) + el.style.fontSize = `${c}px` + if (b - a === 1) return + if (el.scrollHeight > el.clientHeight + || el.scrollWidth > el.clientWidth) fit(el, a, c) + else fit(el, c, b) +} + +const width = 540 +const height = 540 +const pixelRatio = 2 + +const html = ` +
+ +
+
+
+
+
+ +
+
+ +
+
+
+
 
+
` + +// TODO: lang, vertical writing +customElements.define('foliate-quoteimage', class extends HTMLElement { + #root = this.attachShadow({ mode: 'closed' }) + constructor() { + super() + this.#root.innerHTML = html + } + async getBlob({ title, author, text }) { + this.#root.querySelector('#title').textContent = title + this.#root.querySelector('#author').textContent = author + this.#root.querySelector('#text').innerText = text + + fit(this.#root.querySelector('main')) + + const img = document.createElement('img') + return new Promise(resolve => { + img.onload = () => { + const canvas = document.createElement('canvas') + canvas.width = pixelRatio * width + canvas.height = pixelRatio * height + const ctx = canvas.getContext('2d') + ctx.drawImage(img, 0, 0, canvas.width, canvas.height) + canvas.toBlob(resolve) + } + const doc = document.implementation.createDocument(SVG_NS, 'svg') + doc.documentElement.setAttribute('viewBox', `0 0 ${width} ${height}`) + const obj = doc.createElementNS(SVG_NS, 'foreignObject') + obj.setAttribute('width', width) + obj.setAttribute('height', height) + obj.append(doc.importNode(this.#root.querySelector('main'), true)) + doc.documentElement.append(obj) + img.src = 'data:image/svg+xml;charset=utf-8,' + + new XMLSerializer().serializeToString(doc) + }) + } +})