Load fonts directly from assets directory, fix image buffer issues
This commit is contained in:
parent
273e5b94d7
commit
4f66519aa7
22 changed files with 188 additions and 111 deletions
24
Dockerfile
24
Dockerfile
|
@ -12,15 +12,6 @@ RUN apk add --no-cache git cmake msttcorefonts-installer python3 alpine-sdk ffmp
|
|||
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
|
||||
npm install -g pnpm@6.27.1
|
||||
|
||||
# gets latest version of twemoji
|
||||
RUN mkdir /tmp/twemoji \
|
||||
&& cd /tmp/twemoji \
|
||||
&& package=$(wget --quiet -O - https://fedora.mirror.liteserver.nl/linux/development/rawhide/Everything/aarch64/os/Packages/t/ | grep -Po '(?<=href=")twitter-twemoji-fonts-[^"]*' | tail -1) \
|
||||
&& wget https://fedora.mirror.liteserver.nl/linux/development/rawhide/Everything/aarch64/os/Packages/t/$package \
|
||||
&& rpm2cpio $package | cpio -ivd \
|
||||
&& cp ./usr/share/fonts/twemoji/Twemoji.ttf /usr/share/fonts/Twemoji.ttf \
|
||||
&& rm -r /tmp/twemoji
|
||||
|
||||
# liblqr needs to be built manually for magick to work
|
||||
# and because alpine doesn't have it in their repos
|
||||
RUN git clone https://github.com/carlobaldassi/liblqr \
|
||||
|
@ -53,17 +44,12 @@ RUN git clone https://github.com/ImageMagick/ImageMagick.git ImageMagick \
|
|||
RUN update-ms-fonts && fc-cache -f
|
||||
|
||||
RUN adduser esmBot -s /bin/sh -D
|
||||
USER esmBot
|
||||
|
||||
WORKDIR /home/esmBot/.internal
|
||||
|
||||
COPY ./assets/caption.otf /usr/share/fonts/caption.otf
|
||||
COPY ./assets/caption2.ttf /usr/share/fonts/caption2.ttf
|
||||
COPY ./assets/hbc.ttf /usr/share/fonts/hbc.ttf
|
||||
COPY ./assets/reddit.ttf /usr/share/fonts/reddit.ttf
|
||||
COPY ./assets/whisper.otf /usr/share/fonts/whisper.otf
|
||||
RUN fc-cache -fv
|
||||
|
||||
COPY --chown=node:node ./package.json package.json
|
||||
COPY --chown=node:node ./pnpm-lock.yaml pnpm-lock.yaml
|
||||
COPY --chown=esmBot:esmBot ./package.json package.json
|
||||
COPY --chown=esmBot:esmBot ./pnpm-lock.yaml pnpm-lock.yaml
|
||||
RUN pnpm install
|
||||
COPY . .
|
||||
RUN rm .env
|
||||
|
@ -81,6 +67,4 @@ RUN mkdir /home/esmBot/.internal/logs \
|
|||
&& chown esmBot:esmBot /home/esmBot/.internal/logs \
|
||||
&& chmod 777 /home/esmBot/.internal/logs
|
||||
|
||||
USER esmBot
|
||||
|
||||
ENTRYPOINT ["node", "app.js"]
|
||||
|
|
BIN
assets/fonts/twemoji.otf
Normal file
BIN
assets/fonts/twemoji.otf
Normal file
Binary file not shown.
|
@ -77,13 +77,13 @@ class ImageCommand extends Command {
|
|||
}
|
||||
|
||||
try {
|
||||
const { arrayBuffer, type } = await runImageJob(imageParams);
|
||||
const { buffer, type } = await runImageJob(imageParams);
|
||||
if (type === "nogif" && this.constructor.requiresGIF) {
|
||||
return "That isn't a GIF!";
|
||||
}
|
||||
this.success = true;
|
||||
return {
|
||||
file: Buffer.from(arrayBuffer),
|
||||
file: buffer,
|
||||
name: `${this.constructor.command}.${type}`
|
||||
};
|
||||
} catch (e) {
|
||||
|
|
|
@ -17,7 +17,7 @@ class UncannyCommand extends ImageCommand {
|
|||
return {
|
||||
caption: text1?.trim() ? textEncode(text1) : random(prompts),
|
||||
caption2: textEncode(text2),
|
||||
path: `./assets/images/uncanny/${typeof this.options.phase === "string" && names.includes(this.options.phase.toLowerCase()) ? this.options.phase.toLowerCase() : random(names.filter((val) => val !== "goated"))}.png`,
|
||||
path: `assets/images/uncanny/${typeof this.options.phase === "string" && names.includes(this.options.phase.toLowerCase()) ? this.options.phase.toLowerCase() : random(names.filter((val) => val !== "goated"))}.png`,
|
||||
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "helvetica"
|
||||
};
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ Choose the distro you're using below for insallation instructions.
|
|||
|
||||
### 2. Install libvips.
|
||||
|
||||
[libvips](https://github.com/libvips/libvips) is the core of esmBot's image processing commands. The latest version (8.13.0) is recommended because it contains fixes to GIF handling and support for the freeze command; however, this version isn't packaged for most distros yet. To fix this, you'll need to build libvips from source.
|
||||
[libvips](https://github.com/libvips/libvips) is the core of esmBot's image processing commands. Version 8.13.0 or higher is recommended because it contains fixes to GIF handling and support for the freeze command; however, this version isn't packaged for most distros yet. To fix this, you'll need to build libvips from source.
|
||||
|
||||
!!! note
|
||||
Alpine, Arch, and RHEL **(not Fedora!)** users can skip this step, since these distros now have 8.13.0 packaged.
|
||||
|
@ -120,11 +120,6 @@ pnpm i -g node-gyp
|
|||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
You'll also need to copy over some fonts for the image commands:
|
||||
```sh
|
||||
sudo cp assets/*.ttf assets/*.otf /usr/local/share/fonts
|
||||
fc-cache -fv
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
|
@ -225,13 +220,6 @@ pm2 start app.js
|
|||
??? faq "Gifs from Tenor result in a "no decode delegate for this image format" or "improper image header" error"
|
||||
Tenor GIFs are actually stored as MP4s, which libvips can't decode most of the time. You'll need to get a Tenor API key from [here](https://developers.google.com/tenor/guides/quickstart) and put it in the `TENOR` variable in .env.
|
||||
|
||||
??? faq "Emojis are missing in some commands"
|
||||
Your system doesn't have an emoji font installed. You can install Google's emoji set with `sudo apt-get install fonts-noto-color-emoji` on Debian/Ubuntu systems, `doas apk add font-noto-emoji` on Alpine, and `sudo pacman -S noto-fonts-emoji` on Arch/Manjaro.
|
||||
|
||||
If you want to use the same set that Discord and the main bot uses (Twemoji) on Fedora, then you can run `sudo dnf remove google-noto-emoji-color-fonts && sudo dnf install twitter-twemoji-fonts`.
|
||||
|
||||
If you want to install Twemoji on another distro then it's slightly more difficult. Go [here](https://koji.fedoraproject.org/koji/packageinfo?packageID=26306) and choose the latest build, then download the `noarch` RPM file. You'll then have to extract this file; most graphical tools (e.g. 7-Zip, Ark, The Unarchiver) should be able to extract this just fine, but on the command line you'll have to use the `rpm2cpio` tool. The font file should be inside the archive at `usr/share/fonts/Twemoji/Twemoji.ttf`; copy this to `/usr/share/fonts/Twemoji.ttf` (note the / at the beginning). After this, run `fc-cache -fv` and you should be good to go!
|
||||
|
||||
??? faq "Sound/music commands do nothing"
|
||||
Make sure Lavalink is running and started up completely. The bot skips loading sound commands if Lavalink is not present, so make sure it's running when the bot starts as well.
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "common.h"
|
||||
#include <napi.h>
|
||||
|
||||
#include <vips/vips8>
|
||||
|
@ -15,6 +16,7 @@ Napi::Value Caption(const Napi::CallbackInfo &info) {
|
|||
string caption = obj.Get("caption").As<Napi::String>().Utf8Value();
|
||||
string font = obj.Get("font").As<Napi::String>().Utf8Value();
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -31,17 +33,27 @@ Napi::Value Caption(const Napi::CallbackInfo &info) {
|
|||
int nPages = vips_image_get_n_pages(in.get_image());
|
||||
int textWidth = width - ((width / 25) * 2);
|
||||
|
||||
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
string font_string = "Twemoji Color Emoji, " +
|
||||
(font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
(font != "impact" ? "bold" : "normal") + " " +
|
||||
to_string(size);
|
||||
|
||||
string captionText = "<span background=\"white\">" + caption + "</span>";
|
||||
|
||||
VImage text =
|
||||
VImage::text(captionText.c_str(), VImage::option()
|
||||
VImage text;
|
||||
auto findResult = fontPaths.find(font);
|
||||
if (findResult != fontPaths.end()) {
|
||||
text = VImage::text(
|
||||
".", VImage::option()->set("fontfile",
|
||||
(basePath + findResult->second).c_str()));
|
||||
}
|
||||
text = VImage::text(
|
||||
captionText.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", textWidth));
|
||||
VImage captionImage =
|
||||
((text == (vector<double>){0, 0, 0, 0}).bandand())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "common.h"
|
||||
#include <napi.h>
|
||||
|
||||
#include <vips/vips8>
|
||||
|
@ -16,6 +17,7 @@ Napi::Value CaptionTwo(const Napi::CallbackInfo &info) {
|
|||
bool top = obj.Get("top").As<Napi::Boolean>().Value();
|
||||
string font = obj.Get("font").As<Napi::String>().Utf8Value();
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -32,17 +34,27 @@ Napi::Value CaptionTwo(const Napi::CallbackInfo &info) {
|
|||
int nPages = vips_image_get_n_pages(in.get_image());
|
||||
int textWidth = width - ((width / 25) * 2);
|
||||
|
||||
string font_string =
|
||||
(font == "roboto" ? "Roboto Condensed" : font) + " " + to_string(size);
|
||||
string font_string = "Twemoji Color Emoji, " +
|
||||
(font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
to_string(size);
|
||||
|
||||
string captionText = "<span background=\"white\">" + caption + "</span>";
|
||||
|
||||
VImage text =
|
||||
VImage::text(captionText.c_str(), VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("font", font_string.c_str())
|
||||
->set("align", VIPS_ALIGN_LOW)
|
||||
->set("width", textWidth));
|
||||
VImage text;
|
||||
auto findResult = fontPaths.find(font);
|
||||
if (findResult != fontPaths.end()) {
|
||||
text = VImage::text(
|
||||
".", VImage::option()->set("fontfile",
|
||||
(basePath + findResult->second).c_str()));
|
||||
}
|
||||
text = VImage::text(
|
||||
captionText.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("align", VIPS_ALIGN_LOW)
|
||||
->set("width", textWidth));
|
||||
VImage captionImage =
|
||||
((text == (vector<double>){0, 0, 0, 0}).bandand())
|
||||
.ifthenelse(255, text)
|
||||
|
|
10
natives/common.h
Normal file
10
natives/common.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
const std::unordered_map<std::string, std::string> fontPaths {
|
||||
{"futura", "assets/fonts/caption.otf"},
|
||||
{"helvetica", "assets/fonts/caption2.ttf"},
|
||||
{"roboto", "assets/fonts/reddit.ttf"}
|
||||
};
|
|
@ -17,14 +17,18 @@ Napi::Value Homebrew(const Napi::CallbackInfo &info) {
|
|||
string assetPath = basePath + "assets/images/hbc.png";
|
||||
VImage bg = VImage::new_from_file(assetPath.c_str());
|
||||
|
||||
VImage text =
|
||||
VImage::text(("<span letter_spacing=\"-5120\" color=\"white\">" +
|
||||
caption + "</span>")
|
||||
.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", "PF Square Sans Pro 96"));
|
||||
VImage text = VImage::text(
|
||||
".", VImage::option()->set(
|
||||
"fontfile", (basePath + "assets/fonts/hbc.ttf").c_str()));
|
||||
text = VImage::text(
|
||||
("<span letter_spacing=\"-5120\" color=\"white\">" + caption +
|
||||
"</span>")
|
||||
.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", "Twemoji Color Font, PF Square Sans Pro 96")
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()));
|
||||
|
||||
VImage out = bg.composite2(text, VIPS_BLEND_MODE_OVER,
|
||||
VImage::option()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "common.h"
|
||||
#include <napi.h>
|
||||
|
||||
#include <vips/vips8>
|
||||
|
@ -16,6 +17,7 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
string bottom = obj.Get("bottom").As<Napi::String>().Utf8Value();
|
||||
string font = obj.Get("font").As<Napi::String>().Utf8Value();
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -23,7 +25,8 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
VImage::new_from_buffer(data.Data(), data.Length(), "",
|
||||
type == "gif" ? options->set("n", -1) : options)
|
||||
.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||
if (!in.has_alpha()) in = in.bandjoin(255);
|
||||
if (!in.has_alpha())
|
||||
in = in.bandjoin(255);
|
||||
|
||||
int width = in.width();
|
||||
int pageHeight = vips_image_get_page_height(in.get_image());
|
||||
|
@ -33,7 +36,8 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
int rad = 1;
|
||||
vector<double> zeroVec = {0, 0, 0, 0};
|
||||
|
||||
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
string font_string = "Twemoji Color Font, " +
|
||||
(font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
(font != "impact" ? "bold" : "normal") + " " +
|
||||
to_string(size);
|
||||
|
||||
|
@ -48,6 +52,13 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
VImage::option()->set("fill", true));
|
||||
}
|
||||
|
||||
auto findResult = fontPaths.find(font);
|
||||
if (findResult != fontPaths.end()) {
|
||||
VImage::text(
|
||||
".", VImage::option()->set("fontfile",
|
||||
(basePath + findResult->second).c_str()));
|
||||
}
|
||||
|
||||
VImage topText;
|
||||
if (top != "") {
|
||||
VImage topIn = VImage::text(
|
||||
|
@ -56,6 +67,7 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", width));
|
||||
|
||||
topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20,
|
||||
|
@ -70,10 +82,11 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
}
|
||||
topOutline = (topOutline == zeroVec);
|
||||
VImage topInvert = topOutline.extract_band(3).invert();
|
||||
topOutline = topOutline
|
||||
.extract_band(0, VImage::option()->set(
|
||||
"n", topOutline.bands() - 1))
|
||||
.bandjoin(topInvert);
|
||||
topOutline =
|
||||
topOutline
|
||||
.extract_band(0,
|
||||
VImage::option()->set("n", topOutline.bands() - 1))
|
||||
.bandjoin(topInvert);
|
||||
topText = topOutline.composite2(topIn, VIPS_BLEND_MODE_OVER);
|
||||
}
|
||||
|
||||
|
@ -85,6 +98,7 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", width));
|
||||
bottomIn =
|
||||
bottomIn.embed(rad + 10, rad + 10, (bottomIn.width() + 2 * rad) + 20,
|
||||
|
@ -130,7 +144,8 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
|
|||
size_t length;
|
||||
final.write_to_buffer(
|
||||
("." + type).c_str(), &buf, &length,
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1) : 0);
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
|
||||
: 0);
|
||||
|
||||
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
|
||||
result.Set("type", type);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "common.h"
|
||||
#include <napi.h>
|
||||
|
||||
#include <vips/vips8>
|
||||
|
@ -16,6 +17,7 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) {
|
|||
string bottom_text = obj.Get("bottom").As<Napi::String>().Utf8Value();
|
||||
string font = obj.Get("font").As<Napi::String>().Utf8Value();
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -32,7 +34,15 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) {
|
|||
int nPages = vips_image_get_n_pages(in.get_image());
|
||||
int textWidth = width - ((width / 25) * 2);
|
||||
|
||||
string font_string = font == "roboto" ? "Roboto Condensed" : font;
|
||||
string font_string =
|
||||
"Twemoji Color Font, " + (font == "roboto" ? "Roboto Condensed" : font);
|
||||
|
||||
auto findResult = fontPaths.find(font);
|
||||
if (findResult != fontPaths.end()) {
|
||||
VImage::text(
|
||||
".", VImage::option()->set("fontfile",
|
||||
(basePath + findResult->second).c_str()));
|
||||
}
|
||||
|
||||
VImage topImage;
|
||||
if (top_text != "") {
|
||||
|
@ -45,6 +55,7 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) {
|
|||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", (font_string + " " + to_string(size)).c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", textWidth));
|
||||
}
|
||||
|
||||
|
@ -59,6 +70,7 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) {
|
|||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", (font_string + " " + to_string(size * 0.4)).c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", textWidth));
|
||||
}
|
||||
|
||||
|
@ -96,7 +108,8 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) {
|
|||
VImage::option()->set("background", 0x000000)->set("expand", true));
|
||||
}
|
||||
if (bottom_text != "") {
|
||||
if (top_text == "") frame = bordered3;
|
||||
if (top_text == "")
|
||||
frame = bordered3;
|
||||
frame = frame.join(
|
||||
bottomImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(),
|
||||
bottomImage.height() + (size / 4),
|
||||
|
|
|
@ -22,7 +22,8 @@ Napi::Value Reddit(const Napi::CallbackInfo &info) {
|
|||
VImage::new_from_buffer(data.Data(), data.Length(), "",
|
||||
type == "gif" ? options->set("n", -1) : options)
|
||||
.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||
if (!in.has_alpha()) in = in.bandjoin(255);
|
||||
if (!in.has_alpha())
|
||||
in = in.bandjoin(255);
|
||||
|
||||
string assetPath = basePath + "assets/images/reddit.png";
|
||||
VImage tmpl = VImage::new_from_file(assetPath.c_str());
|
||||
|
@ -33,11 +34,16 @@ Napi::Value Reddit(const Napi::CallbackInfo &info) {
|
|||
|
||||
string captionText = "<span foreground=\"white\">" + text + "</span>";
|
||||
|
||||
VImage textImage =
|
||||
VImage::text(captionText.c_str(), VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("font", "Roboto 62")
|
||||
->set("align", VIPS_ALIGN_LOW));
|
||||
VImage textImage = VImage::text(
|
||||
".", VImage::option()->set(
|
||||
"fontfile", (basePath + "assets/fonts/reddit.ttf").c_str()));
|
||||
textImage = VImage::text(
|
||||
captionText.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("font", "Twemoji Color Font, Roboto 62")
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("align", VIPS_ALIGN_LOW));
|
||||
|
||||
VImage composited =
|
||||
tmpl.composite2(textImage, VIPS_BLEND_MODE_OVER,
|
||||
|
@ -61,7 +67,8 @@ Napi::Value Reddit(const Napi::CallbackInfo &info) {
|
|||
size_t length;
|
||||
final.write_to_buffer(
|
||||
("." + type).c_str(), &buf, &length,
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1) : 0);
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
|
||||
: 0);
|
||||
|
||||
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
|
||||
result.Set("type", type);
|
||||
|
|
|
@ -16,6 +16,7 @@ Napi::Value Snapchat(const Napi::CallbackInfo &info) {
|
|||
float pos =
|
||||
obj.Has("pos") ? obj.Get("pos").As<Napi::Number>().FloatValue() : 0.5;
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -23,7 +24,8 @@ Napi::Value Snapchat(const Napi::CallbackInfo &info) {
|
|||
VImage::new_from_buffer(data.Data(), data.Length(), "",
|
||||
type == "gif" ? options->set("n", -1) : options)
|
||||
.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||
if (!in.has_alpha()) in = in.bandjoin(255);
|
||||
if (!in.has_alpha())
|
||||
in = in.bandjoin(255);
|
||||
|
||||
int width = in.width();
|
||||
int pageHeight = vips_image_get_page_height(in.get_image());
|
||||
|
@ -31,17 +33,22 @@ Napi::Value Snapchat(const Napi::CallbackInfo &info) {
|
|||
int size = width / 20;
|
||||
int textWidth = width - ((width / 25) * 2);
|
||||
|
||||
string font_string = "Helvetica Neue " + to_string(size);
|
||||
string font_string =
|
||||
"Twemoji Color Font, Helvetica Neue " + to_string(size);
|
||||
|
||||
VImage textIn =
|
||||
VImage::text(("<span foreground=\"white\" background=\"#000000B2\">" +
|
||||
caption + "</span>")
|
||||
.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("width", textWidth));
|
||||
VImage textIn = VImage::text(
|
||||
".", VImage::option()->set(
|
||||
"fontfile", (basePath + "assets/fonts/caption2.ttf").c_str()));
|
||||
textIn = VImage::text(
|
||||
("<span foreground=\"white\" background=\"#000000B2\">" + caption +
|
||||
"</span>")
|
||||
.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", textWidth));
|
||||
int bgHeight = textIn.height() + (width / 25);
|
||||
textIn =
|
||||
((textIn == (vector<double>){0, 0, 0, 0}).bandand())
|
||||
|
@ -68,7 +75,8 @@ Napi::Value Snapchat(const Napi::CallbackInfo &info) {
|
|||
size_t length;
|
||||
final.write_to_buffer(
|
||||
("." + type).c_str(), &buf, &length,
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1) : 0);
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
|
||||
: 0);
|
||||
|
||||
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
|
||||
result.Set("type", type);
|
||||
|
|
|
@ -18,13 +18,16 @@ Napi::Value Sonic(const Napi::CallbackInfo &info) {
|
|||
VImage bg = VImage::new_from_file(assetPath.c_str());
|
||||
|
||||
VImage textImage =
|
||||
VImage::text(("<span foreground=\"white\">" + text + "</span>").c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", "Bitstream Vera Sans")
|
||||
->set("width", 542)
|
||||
->set("height", 390))
|
||||
VImage::text(
|
||||
("<span foreground=\"white\">" + text + "</span>").c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", "Twemoji Color Font, Bitstream Vera Sans")
|
||||
->set("fontfile",
|
||||
(basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", 542)
|
||||
->set("height", 390))
|
||||
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 542, 390);
|
||||
|
||||
VImage out = bg.composite2(textImage, VIPS_BLEND_MODE_OVER,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "common.h"
|
||||
#include <napi.h>
|
||||
|
||||
#include <vips/vips8>
|
||||
|
@ -17,6 +18,7 @@ Napi::Value Uncanny(const Napi::CallbackInfo &info) {
|
|||
string font = obj.Get("font").As<Napi::String>().Utf8Value();
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string path = obj.Get("path").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -28,7 +30,8 @@ Napi::Value Uncanny(const Napi::CallbackInfo &info) {
|
|||
|
||||
VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3));
|
||||
|
||||
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
string font_string = "Twemoji Color Font, " +
|
||||
(font == "roboto" ? "Roboto Condensed" : font) + " " +
|
||||
(font != "impact" ? "bold" : "normal") + " 72";
|
||||
|
||||
string captionText = "<span background=\"black\" foreground=\"white\">" +
|
||||
|
@ -36,25 +39,36 @@ Napi::Value Uncanny(const Napi::CallbackInfo &info) {
|
|||
string caption2Text =
|
||||
"<span background=\"black\" foreground=\"red\">" + caption2 + "</span>";
|
||||
|
||||
VImage text =
|
||||
VImage::text(captionText.c_str(), VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("width", 588)
|
||||
->set("height", 90));
|
||||
auto findResult = fontPaths.find(font);
|
||||
if (findResult != fontPaths.end()) {
|
||||
VImage::text(
|
||||
".", VImage::option()->set("fontfile",
|
||||
(basePath + findResult->second).c_str()));
|
||||
}
|
||||
|
||||
VImage text = VImage::text(
|
||||
captionText.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", 588)
|
||||
->set("height", 90));
|
||||
VImage captionImage =
|
||||
text.extract_band(0, VImage::option()->set("n", 3))
|
||||
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40,
|
||||
VImage::option()->set("extend", "black"));
|
||||
|
||||
VImage text2 = VImage::text(caption2Text.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("width", 588)
|
||||
->set("height", 90));
|
||||
VImage text2 = VImage::text(
|
||||
caption2Text.c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", 588)
|
||||
->set("height", 90));
|
||||
VImage caption2Image =
|
||||
text2.extract_band(0, VImage::option()->set("n", 3))
|
||||
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40,
|
||||
|
@ -66,7 +80,7 @@ Napi::Value Uncanny(const Napi::CallbackInfo &info) {
|
|||
int pageHeight = vips_image_get_page_height(in.get_image());
|
||||
int nPages = vips_image_get_n_pages(in.get_image());
|
||||
|
||||
VImage uncanny = VImage::new_from_file(path.c_str());
|
||||
VImage uncanny = VImage::new_from_file((basePath + path).c_str());
|
||||
|
||||
base = base.insert(uncanny, 0, 130);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) {
|
|||
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
|
||||
string caption = obj.Get("caption").As<Napi::String>().Utf8Value();
|
||||
string type = obj.Get("type").As<Napi::String>().Utf8Value();
|
||||
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
|
||||
|
||||
VOption *options = VImage::option()->set("access", "sequential");
|
||||
|
||||
|
@ -21,7 +22,8 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) {
|
|||
VImage::new_from_buffer(data.Data(), data.Length(), "",
|
||||
type == "gif" ? options->set("n", -1) : options)
|
||||
.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||
if (!in.has_alpha()) in = in.bandjoin(255);
|
||||
if (!in.has_alpha())
|
||||
in = in.bandjoin(255);
|
||||
|
||||
int width = in.width();
|
||||
int pageHeight = vips_image_get_page_height(in.get_image());
|
||||
|
@ -30,7 +32,7 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) {
|
|||
int dividedWidth = width / 175;
|
||||
int rad = 1;
|
||||
|
||||
string font_string = "Upright " + to_string(size);
|
||||
string font_string = "Twemoji Color Font, Upright " + to_string(size);
|
||||
|
||||
VImage mask;
|
||||
if (dividedWidth >= 1) {
|
||||
|
@ -44,11 +46,15 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) {
|
|||
}
|
||||
|
||||
VImage textIn = VImage::text(
|
||||
".", VImage::option()->set(
|
||||
"fontfile", (basePath + "assets/fonts/whisper.otf").c_str()));
|
||||
textIn = VImage::text(
|
||||
("<span foreground=\"white\">" + caption + "</span>").c_str(),
|
||||
VImage::option()
|
||||
->set("rgba", true)
|
||||
->set("align", VIPS_ALIGN_CENTRE)
|
||||
->set("font", font_string.c_str())
|
||||
->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())
|
||||
->set("width", width));
|
||||
|
||||
textIn = textIn.embed(rad + 10, rad + 10, (textIn.width() + 2 * rad) + 20,
|
||||
|
@ -82,7 +88,8 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) {
|
|||
size_t length;
|
||||
final.write_to_buffer(
|
||||
("." + type).c_str(), &buf, &length,
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1) : 0);
|
||||
type == "gif" ? VImage::option()->set("dither", 0)->set("reoptimise", 1)
|
||||
: 0);
|
||||
|
||||
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
|
||||
result.Set("type", type);
|
||||
|
|
|
@ -140,7 +140,7 @@ class ImageConnection {
|
|||
type = contentType;
|
||||
break;
|
||||
}
|
||||
return { arrayBuffer: await req.body.arrayBuffer(), type };
|
||||
return { buffer: Buffer.from(await req.body.arrayBuffer()), type };
|
||||
}
|
||||
|
||||
async getCount() {
|
||||
|
|
Loading…
Reference in a new issue