// you may not need all of these but they come up a lot import fs from "fs"; import assert from "assert"; import logging from '../lib/logging.js'; import glob from "fast-glob"; import path from "path"; import { fabric } from "fabric"; import { defer } from "../lib/api.js"; import { createClient } from 'pexels'; import { exec_i } from "../lib/builderator.js"; import { pexels } from "../lib/config.js"; import fetch from "node-fetch"; import { playstart, playstop } from "../lib/testing.js"; const log = logging.create(import.meta.url); export const description = "Gets a random image from Pexels and places the text on it." // your command uses the npm package commander's options format export const options = [ ["--background ", "The file name to save the background image.", "background.jpg"], ["--force", "Force overwriting the background.jpg temp file."], ["--query ", "Query to give pexels for the image", "keyboard"], ["--color ", "General color of the image"], ["--text-color ", "The border around text.", "#fff"], ["--font-family ", "Font to use", "Andale Mono"], ["--font-size ", "Font size to use", 140], ["--blend-mode ", "One of: none, multiply, screen, add, diff, subtract, darken, lighten, overlay, exclusion, tint", "screen"], ["--pixelate ", "Pixelate the image with blocksize (try 8)"], ] // example of a positional argument, it's the 1st argument to main export const argument = ["text", "The message to put on the image."]; // put required options in the required variable export const required = [ ["--output ", "Image file to save."], ] // handy function for checking things are good and aborting const check = (test, fail_message) => { if(!test) { log.error(fail_message); process.exit(1); } } export const main = async (title, opts) => { const per_page = 50; const ext_check = path.parse(opts.output).ext; check(ext_check === "", `Do not add an extension to the output. You have ${ext_check}.`); title = title.replaceAll("\\n", "\n"); if(opts.force || !fs.existsSync(opts.background)) { const client = createClient(pexels.key); const result = await client.photos.search({ query: opts.query, per_page, orientation: opts.orientation, color: opts.color }); const randi = Math.floor(Math.random() * per_page); const image = await client.photos.show({id: result.photos[randi].id}); const img_fetch = await fetch(image.src.large); const img_data = await img_fetch.arrayBuffer(); await fs.writeFileSync(opts.background, new Uint8Array(img_data)); } const background = path.join(process.cwd(), opts.background); let canvas = new fabric.Canvas('c', { width: 1600, height: 900 }) let gradient = new fabric.Gradient({ type: 'linear', gradientUnits: 'pixels', // or 'percentage' coords: { x1: 0, y1: 0, x2: canvas.height, y2: canvas.width }, colorStops:[ { offset: 0, color: '#000' }, { offset: 1, color: '#fff'} ] }) let image_def = defer(); fabric.Image.fromURL(`file:///${background}`, img => { let blur = new fabric.Image.filters.Blur({ blur: 0.05 }); img.set('scaleY', canvas.height / img.height); img.set('scaleX', canvas.width / img.width); if(opts.pixelate) { let pixelate = new fabric.Image.filters.Pixelate({ blocksize: parseInt(opts.pixelate, 10) }); img.filters.push(pixelate); } let blend = new fabric.Image.filters.BlendColor({ color: opts.color, mode: opts.blendMode, }); img.filters.push(blend); img.filters.push(blur); img.applyFilters(); image_def.resolve(img); }); let bg_img = await image_def; let rect = new fabric.Rect({ left: 0, top: 0, fill: opts.color, width: canvas.width, height: canvas.height, opacity: 0.3 }); let text = new fabric.Text(title, { fontFamily: opts.fontFamily, fontSize: opts.fontSize, strokeWidth: 5, originX: "center", originY: "center", left: canvas.width / 2, top: canvas.height / 2, stroke: opts.textColor, shadow: 'rgba(0, 0, 0, 1) 10px 10px 10px', }); canvas.add(bg_img); // canvas.add(rect); canvas.add(text); const output_svg = `${opts.output}.svg`; const output_jpg = `${opts.output}.jpg`; const output_png = `${opts.output}.png`; fs.writeFileSync(output_svg, canvas.toSVG()); const {browser, context, p} = await playstart(`file:///${path.resolve(output_svg)}`); await p.setViewportSize({ width: canvas.width, height: canvas.height }); await p.screenshot({ path: output_png, type: "png", omitBackground: true}); await playstop(browser, p); exec_i(`convert ${output_png} -sampling-factor 4:2:0 -strip -quality 85 -interlace JPEG -colorspace RGB ${output_jpg}`); // due to how async/await works it's just easier to manually exit with exit codes process.exit(0); }