diff --git a/Dockerfile b/Dockerfile index ad9cd49..2e53068 100644 --- a/Dockerfile +++ b/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"] diff --git a/assets/caption.otf b/assets/fonts/caption.otf similarity index 100% rename from assets/caption.otf rename to assets/fonts/caption.otf diff --git a/assets/caption2.ttf b/assets/fonts/caption2.ttf similarity index 100% rename from assets/caption2.ttf rename to assets/fonts/caption2.ttf diff --git a/assets/hbc.ttf b/assets/fonts/hbc.ttf similarity index 100% rename from assets/hbc.ttf rename to assets/fonts/hbc.ttf diff --git a/assets/reddit.ttf b/assets/fonts/reddit.ttf similarity index 100% rename from assets/reddit.ttf rename to assets/fonts/reddit.ttf diff --git a/assets/fonts/twemoji.otf b/assets/fonts/twemoji.otf new file mode 100644 index 0000000..321f355 Binary files /dev/null and b/assets/fonts/twemoji.otf differ diff --git a/assets/whisper.otf b/assets/fonts/whisper.otf similarity index 100% rename from assets/whisper.otf rename to assets/fonts/whisper.otf diff --git a/classes/imageCommand.js b/classes/imageCommand.js index 4f48714..2a887a7 100644 --- a/classes/imageCommand.js +++ b/classes/imageCommand.js @@ -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) { diff --git a/commands/image-editing/uncanny.js b/commands/image-editing/uncanny.js index b941c9f..1c20263 100644 --- a/commands/image-editing/uncanny.js +++ b/commands/image-editing/uncanny.js @@ -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" }; } diff --git a/docs/setup.md b/docs/setup.md index c037b6b..5853b74 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -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. diff --git a/natives/caption.cc b/natives/caption.cc index 34ac990..8741253 100644 --- a/natives/caption.cc +++ b/natives/caption.cc @@ -1,3 +1,4 @@ +#include "common.h" #include #include @@ -15,6 +16,7 @@ Napi::Value Caption(const Napi::CallbackInfo &info) { string caption = obj.Get("caption").As().Utf8Value(); string font = obj.Get("font").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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 = "" + caption + ""; - 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){0, 0, 0, 0}).bandand()) diff --git a/natives/caption2.cc b/natives/caption2.cc index b9d1cd0..554cf0c 100644 --- a/natives/caption2.cc +++ b/natives/caption2.cc @@ -1,3 +1,4 @@ +#include "common.h" #include #include @@ -16,6 +17,7 @@ Napi::Value CaptionTwo(const Napi::CallbackInfo &info) { bool top = obj.Get("top").As().Value(); string font = obj.Get("font").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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 = "" + caption + ""; - 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){0, 0, 0, 0}).bandand()) .ifthenelse(255, text) diff --git a/natives/common.h b/natives/common.h new file mode 100644 index 0000000..4510065 --- /dev/null +++ b/natives/common.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +const std::unordered_map fontPaths { + {"futura", "assets/fonts/caption.otf"}, + {"helvetica", "assets/fonts/caption2.ttf"}, + {"roboto", "assets/fonts/reddit.ttf"} +}; \ No newline at end of file diff --git a/natives/homebrew.cc b/natives/homebrew.cc index a34ca63..5ac9dd8 100644 --- a/natives/homebrew.cc +++ b/natives/homebrew.cc @@ -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(("" + - caption + "") - .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( + ("" + caption + + "") + .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() diff --git a/natives/meme.cc b/natives/meme.cc index 848fff1..bd14c9a 100644 --- a/natives/meme.cc +++ b/natives/meme.cc @@ -1,3 +1,4 @@ +#include "common.h" #include #include @@ -16,6 +17,7 @@ Napi::Value Meme(const Napi::CallbackInfo &info) { string bottom = obj.Get("bottom").As().Utf8Value(); string font = obj.Get("font").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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 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::Copy(env, (char *)buf, length)); result.Set("type", type); diff --git a/natives/motivate.cc b/natives/motivate.cc index 6027694..c95c142 100644 --- a/natives/motivate.cc +++ b/natives/motivate.cc @@ -1,3 +1,4 @@ +#include "common.h" #include #include @@ -16,6 +17,7 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) { string bottom_text = obj.Get("bottom").As().Utf8Value(); string font = obj.Get("font").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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), diff --git a/natives/reddit.cc b/natives/reddit.cc index 45aaafb..26551eb 100644 --- a/natives/reddit.cc +++ b/natives/reddit.cc @@ -8,7 +8,7 @@ using namespace vips; Napi::Value Reddit(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); Napi::Object result = Napi::Object::New(env); - + try { Napi::Object obj = info[0].As(); Napi::Buffer data = obj.Get("data").As>(); @@ -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 = "" + text + ""; - 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::Copy(env, (char *)buf, length)); result.Set("type", type); diff --git a/natives/snapchat.cc b/natives/snapchat.cc index fda09ca..dbf3429 100644 --- a/natives/snapchat.cc +++ b/natives/snapchat.cc @@ -16,6 +16,7 @@ Napi::Value Snapchat(const Napi::CallbackInfo &info) { float pos = obj.Has("pos") ? obj.Get("pos").As().FloatValue() : 0.5; string type = obj.Get("type").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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(("" + - caption + "") - .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( + ("" + caption + + "") + .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){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::Copy(env, (char *)buf, length)); result.Set("type", type); diff --git a/natives/sonic.cc b/natives/sonic.cc index b432ce2..ed8d319 100644 --- a/natives/sonic.cc +++ b/natives/sonic.cc @@ -18,13 +18,16 @@ Napi::Value Sonic(const Napi::CallbackInfo &info) { VImage bg = VImage::new_from_file(assetPath.c_str()); VImage textImage = - VImage::text(("" + text + "").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( + ("" + text + "").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, diff --git a/natives/uncanny.cc b/natives/uncanny.cc index 8b46ef6..5c4388f 100644 --- a/natives/uncanny.cc +++ b/natives/uncanny.cc @@ -1,3 +1,4 @@ +#include "common.h" #include #include @@ -17,6 +18,7 @@ Napi::Value Uncanny(const Napi::CallbackInfo &info) { string font = obj.Get("font").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); string path = obj.Get("path").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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 = "" + @@ -36,25 +39,36 @@ Napi::Value Uncanny(const Napi::CallbackInfo &info) { string caption2Text = "" + caption2 + ""; - 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); diff --git a/natives/whisper.cc b/natives/whisper.cc index 5c24626..ecf0b05 100644 --- a/natives/whisper.cc +++ b/natives/whisper.cc @@ -14,6 +14,7 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) { Napi::Buffer data = obj.Get("data").As>(); string caption = obj.Get("caption").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); + string basePath = obj.Get("basePath").As().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( ("" + caption + "").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::Copy(env, (char *)buf, length)); result.Set("type", type); diff --git a/utils/imageConnection.js b/utils/imageConnection.js index 49afc73..1488359 100644 --- a/utils/imageConnection.js +++ b/utils/imageConnection.js @@ -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() {