Add module for generating quote images

This commit is contained in:
John Factotum
2024-09-11 14:12:27 +08:00
parent ffceb8a4aa
commit 0b9fac8ff3
2 changed files with 95 additions and 0 deletions
+12
View File
@@ -340,6 +340,18 @@ These two functions return an object that implements the following interface:
- `value`: a string; the default value of the parameter - `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. - `.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 ### 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. 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.
+83
View File
@@ -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 = `<style>
:host {
position: absolute;
visibility: hidden;
}
</style>
<main style="width: ${width}px; height: ${height}px; overflow: hidden; display: flex; flex-direction: column; justify-content: center; text-align: center; background: #fff; color: #000; font: 16pt serif">
<style>
.ellipsis {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
}
</style>
<div style="font-size: min(4em, 8rem); line-height: 1; margin-bottom: -.5em">“</div>
<div id="text" style="margin: 1em; text-wrap: balance"></div>
<div style="margin: 0 1em">
<div style="font-size: min(.8em, 1.25rem); font-family: sans-serif">
<div style="display: block; font-weight: bold; margin-bottom: .25em">
<span id="author" class="ellipsis"></span>
</div>
<div style="display: block; text-wrap: balance">
<cite id="title" class="ellipsis"></cite>
</div>
</div>
</div>
<div style="height: 1em">&nbsp;</div>
</main>`
// 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)
})
}
})