parent
e27092843e
commit
578b58794b
@ -0,0 +1,161 @@ |
|||||||
|
// 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 <filename>", "The file name to save the background image.", "background.jpg"], |
||||||
|
["--force", "Force overwriting the background.jpg temp file."], |
||||||
|
["--query <text>", "Query to give pexels for the image", "keyboard"], |
||||||
|
["--color <color>", "General color of the image"], |
||||||
|
["--text-color <color>", "The border around text.", "#fff"], |
||||||
|
["--font-family <name>", "Font to use", "Andale Mono"], |
||||||
|
["--font-size <pts>", "Font size to use", 140], |
||||||
|
["--blend-mode <mode>", "One of: none, multiply, screen, add, diff, subtract, darken, lighten, overlay, exclusion, tint", "screen"], |
||||||
|
["--pixelate <blocksize>", "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 <string>", "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); |
||||||
|
} |
Loading…
Reference in new issue