ref: backend

This commit is contained in:
Andrey
2023-12-07 22:56:11 +03:00
parent 07d23f6bec
commit 2d414c86b4
100 changed files with 459 additions and 95 deletions

View File

@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeSVGToFile = exports.generateUniqueSVGPath = exports.createCharacterDirectory = exports.validateName = exports.numberOfFiles = exports.svgLinePathNames = exports.generateSvg = exports.printSVG = void 0;
exports.parseSVG = exports.fetchSVGContent = exports.warpSvg = exports.getRandomEntityPath = exports.writeSVGToFile = exports.generateUniqueSVGPath = exports.createCharacterDirectory = exports.validateName = exports.numberOfFiles = exports.svgLinePathNames = exports.generateSvg = exports.printSVG = void 0;
const fs_1 = __importDefault(require("fs"));
const paths_1 = require("../paths");
const jsdom_1 = __importDefault(require("jsdom"));
@@ -103,9 +103,9 @@ const isUpperCase = (char) => {
* @param charName - The title of the character.
* @returns A promise that resolves to a string representing the path to the random entity or empty.
*/
const getRandomEntityPath = (userName, charName) => __awaiter(void 0, void 0, void 0, function* () {
const getRandomEntityPath = (username, charName) => __awaiter(void 0, void 0, void 0, function* () {
try {
const basePath = `${paths_1.PROFILES_PATH}/${userName}/${charName}`;
const basePath = `${paths_1.PROFILES_PATH}/${username}/${charName}`;
const characters = yield fs_1.default.promises.readdir(basePath);
const randomIndex = Math.floor(Math.random() * characters.length);
return `${basePath}/${characters[randomIndex]}`;
@@ -115,6 +115,7 @@ const getRandomEntityPath = (userName, charName) => __awaiter(void 0, void 0, vo
return "";
}
});
exports.getRandomEntityPath = getRandomEntityPath;
/**
* Checks if a given character path contains any special characters.
* Special characters include: questionMark, asterisk, slash, backslash, colon, pipe, lessThan, greaterThan,
@@ -149,7 +150,16 @@ const isSpecialChar = (charPath) => {
"exclamationMark",
"tilde",
];
const specialLocatedBottom = ["underscore", "comma", "period"];
const specialLocatedBottom = [
"underscore",
"questionMark",
"slash",
"backslash",
"pipe",
"exclamationMark",
"comma",
"period",
];
let isSpecial = false;
let position = "bottom";
for (const special of specialLocatedTop) {
@@ -179,6 +189,193 @@ const isSpecialChar = (charPath) => {
}
return { isSpecial, position };
};
const specialWidthGlyphs = (glyphPath) => {
const symbols = {
colon: {
width: 5,
maxWidth: 7,
height: 15,
maxHeight: 20,
},
lessThan: {
width: 15,
height: 20,
maxWidth: 25,
maxHeight: 30,
},
greaterThan: {
width: 15,
height: 20,
maxWidth: 25,
maxHeight: 30,
},
leftParenthesis: {
width: 8,
height: 20,
maxWidth: 16,
maxHeight: 40,
},
rightParenthesis: {
width: 8,
height: 20,
maxWidth: 16,
maxHeight: 40,
},
hyphen: {
width: 10,
height: 2,
maxWidth: 20,
maxHeight: 4,
},
equal: {
width: 10,
height: 5,
maxWidth: 20,
maxHeight: 10,
},
plus: {
width: 10,
height: 10,
maxWidth: 20,
maxHeight: 20,
},
leftCurlyBrace: {
width: 12,
height: 25,
maxWidth: 24,
maxHeight: 50,
},
rightCurlyBrace: {
width: 12,
height: 25,
maxWidth: 24,
maxHeight: 50,
},
leftSquareBracket: {
width: 8,
height: 20,
maxWidth: 16,
maxHeight: 40,
},
rightSquareBracket: {
width: 8,
height: 20,
maxWidth: 16,
maxHeight: 40,
},
exclamationMark: {
width: 6,
height: 30,
maxWidth: 9,
maxHeight: 30,
},
tilde: {
width: 20,
height: 8,
maxWidth: 30,
maxHeight: 10,
},
comma: {
width: 5,
height: 10,
maxWidth: 10,
maxHeight: 20,
},
dot: {
width: 5,
height: 5,
maxWidth: 10,
maxHeight: 10,
},
questionMark: {
width: 10,
height: 30,
maxWidth: 20,
maxHeight: 40,
},
underscore: {
width: 10,
height: 2,
maxWidth: 20,
maxHeight: 4,
},
pipe: {
width: 5,
height: 30,
maxWidth: 10,
maxHeight: 40,
},
one: {
width: 5,
height: 30,
maxWidth: 10,
maxHeight: 40,
},
"lower-m": {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
},
"lower-q": {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
},
"lower-y": {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
},
"lower-g": {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
},
"lower-p": {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
},
"lower-j": {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
},
"lower-i": {
width: 5,
height: 30,
maxWidth: 10,
maxHeight: 40,
},
"lower-l": {
width: 10,
height: 40,
maxWidth: 10,
maxHeight: 40,
},
};
let isSpecialGlyph = false;
let values = {
width: 20,
height: 30,
maxWidth: 30,
maxHeight: 40,
};
for (const symbol in symbols) {
if (glyphPath.includes(symbol)) {
isSpecialGlyph = true;
values = symbols[symbol];
break;
}
}
return { isSpecialGlyph, values };
};
/**
* Determines if the given character path should extend below the baseline.
* @param charPath The character path to check.
@@ -238,14 +435,13 @@ exports.svgLinePathNames = svgLinePathNames;
* @returns An object containing the SVG element and its paths.
*/
const parseSVG = (svg) => {
const dom = new jsdom_1.default.JSDOM(svg);
const svgElement = dom.window.document.querySelector("svg");
const svgPaths = svgElement === null || svgElement === void 0 ? void 0 : svgElement.querySelectorAll("path");
const dom = new jsdom_1.default.JSDOM(svg, { contentType: "image/svg+xml" });
const svgElement = dom.window.document.querySelector("g");
return {
parent: svgElement,
paths: svgPaths,
};
};
exports.parseSVG = parseSVG;
/**
* Returns a random number between the given minimum and maximum values, with an optional percentage range.
* @param min The minimum value for the random number.
@@ -256,7 +452,6 @@ const parseSVG = (svg) => {
const getRandomNumber = (min, max, percentage = 25, scale = 1) => {
const howRandom = Math.round((max - min) * (percentage / 100));
const randomNumber = Math.floor(Math.random() * (howRandom + 1));
// const randomSign = Math.random() < 0.5 ? -1 : 1;
return Math.round(min + randomNumber) * scale;
};
// Get standard values for the characters
@@ -264,18 +459,19 @@ const getStandardValues = (isSpecial, position) => {
// Standard values for the characters
const standard = {
char_width: 20,
char_height: 30,
space_width: 10,
special_char_located_top_width: 5,
special_char_located_middle_width: 15,
special_char_located_top_max_width: 10,
special_char_located_middle_max_width: 20,
special_char_located_bottom_width: 5,
special_char_located_bottom_max_width: 15,
special_char_located_bottom_max_width: 10,
special_char_height_top: 10,
special_char_height_middle: 20,
special_char_height_bottom: 30,
max_char_width: 30,
max_char_height: 30,
max_char_height: 40,
};
const standerdWidth = isSpecial
? position === "top"
@@ -297,7 +493,7 @@ const getStandardValues = (isSpecial, position) => {
: position === "middle"
? standard.special_char_height_middle
: standard.special_char_height_bottom
: standard.max_char_height;
: standard.char_height;
const standerdMaxHeight = isSpecial
? position === "top"
? standard.special_char_height_top
@@ -310,11 +506,25 @@ const getStandardValues = (isSpecial, position) => {
// Get Random Defects
const getRandomDefects = (defects, scaleFactor, charPath = "") => {
const { baseline, kerning, letterSize, lineSpacing, indent } = defects;
const { isSpecialGlyph, values } = specialWidthGlyphs(charPath);
if (isSpecialGlyph) {
const { width, height, maxWidth, maxHeight } = values;
const letterSizeWidthRandom = getRandomNumber(width, maxWidth, letterSize, scaleFactor);
const letterSizeRandomHeight = getRandomNumber(height, maxHeight, letterSize, scaleFactor);
return {
indentRandom: 0,
lineSpacingRandom: 0,
kerningDeffects: 0,
baselineOffset: 0,
letterSizeWidthRandom,
letterSizeRandomHeight,
};
}
const { isSpecial, position } = isSpecialChar(charPath);
const { standerdWidth, standerdMaxWidth, standerdHeight, standerdMaxHeight } = getStandardValues(isSpecial, position);
const indentRandom = getRandomNumber(0, 80, indent, scaleFactor);
const lineSpacingRandom = getRandomNumber(0, 30, lineSpacing, scaleFactor);
const kerningDeffects = getRandomNumber(0, 10, kerning, scaleFactor);
const kerningDeffects = getRandomNumber(5, -5, kerning, scaleFactor);
const baselineOffset = getRandomNumber(0, 10, baseline, scaleFactor);
const letterSizeWidthRandom = getRandomNumber(standerdWidth, standerdMaxWidth, letterSize, scaleFactor);
const letterSizeRandomHeight = getRandomNumber(standerdHeight, standerdMaxHeight, letterSize, scaleFactor);
@@ -327,6 +537,10 @@ const getRandomDefects = (defects, scaleFactor, charPath = "") => {
letterSizeRandomHeight,
};
};
const fetchSVGContent = (path) => {
return fs_1.default.readFileSync(path, "utf-8");
};
exports.fetchSVGContent = fetchSVGContent;
/**
* Assembles a word by processing each character and generating SVG elements.
*
@@ -341,7 +555,7 @@ const getRandomDefects = (defects, scaleFactor, charPath = "") => {
* @returns {Object} - The assembled word elements, the height of the word, and the updated X offset.
*/
const assembleWord = ({ word, offsetX, offsetY, scaleFactor, indentRandom, defects, }) => {
const space_width = 10 * scaleFactor;
const space_width = 30;
let wordElements = [];
let wordHeight = 0;
if (word.length === 0) {
@@ -351,30 +565,31 @@ const assembleWord = ({ word, offsetX, offsetY, scaleFactor, indentRandom, defec
offsetX += indentRandom;
for (let j = 0; j < word.length; j++) {
const char = word[j];
const { kerningDeffects, baselineOffset } = getRandomDefects(defects, scaleFactor);
const { kerningDeffects } = getRandomDefects(defects, scaleFactor);
const { isSpecial, position } = isSpecialChar(char);
const { letterSizeWidthRandom, letterSizeRandomHeight } = getRandomDefects(defects, scaleFactor, char);
// You need to load the SVG content from the file
const svgFileContent = fs_1.default.readFileSync(char, "utf-8");
const svgFileContent = fetchSVGContent(char);
// Get the width and height of the SVG and its paths children
const { parent } = parseSVG(svgFileContent);
const width = parent === null || parent === void 0 ? void 0 : parent.getAttribute("width");
const height = parent === null || parent === void 0 ? void 0 : parent.getAttribute("height");
// Scale down the width to the standerd width while keeping the aspect ratio
const widthScale = letterSizeWidthRandom / Number(width);
const heightScale = letterSizeRandomHeight / Number(height);
const scale = Math.min(widthScale, heightScale);
// const widthScale = letterSizeWidthRandom / Number(width);
// const heightScale = letterSizeRandomHeight / Number(height);
// Calculate the scaled width and height
const scaledHeight = Number(height) * scale * scaleFactor;
const scaledWidth = Number(width) * scale * scaleFactor;
// const scale = Math.pow(
// Math.min(widthScale, heightScale),
// 1 / scaleFactor
// );
const scale = scaleFactor;
const scaledWidth = Number(width) * scale;
const scaledHeight = Number(height) * scale;
// Change the width and height of the SVG
parent === null || parent === void 0 ? void 0 : parent.setAttribute("width", String(scaledWidth));
parent === null || parent === void 0 ? void 0 : parent.setAttribute("height", String(scaledHeight));
// Add viewBox attribute to scale the paths inside the SVG
parent === null || parent === void 0 ? void 0 : parent.setAttribute("viewBox", `0 0 ${width} ${height}`);
// Change the position of the SVG
parent === null || parent === void 0 ? void 0 : parent.setAttribute("x", offsetX.toString());
parent === null || parent === void 0 ? void 0 : parent.setAttribute("y", String(offsetY + baselineOffset));
// Add translation and scale to the SVG content
parent === null || parent === void 0 ? void 0 : parent.setAttribute("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
// Add the SVG content to the SVG content variable
offsetX += scaledWidth + kerningDeffects;
wordElements.push({
@@ -383,38 +598,8 @@ const assembleWord = ({ word, offsetX, offsetY, scaleFactor, indentRandom, defec
position,
extendBelowBaseline: extendBelowBaseline(char),
});
wordHeight = Math.max(wordHeight, scaledHeight + baselineOffset);
wordHeight = Math.max(wordHeight, scaledHeight);
}
// Align the line elements to the bottom of the line
let extended = false;
wordElements.forEach((e) => {
const { element, isSpecial, position, extendBelowBaseline } = e;
const elementHeight = parseInt(element.getAttribute("height"));
const lineYOffset = wordHeight - elementHeight;
if (isSpecial) {
if (position === "top") {
element.setAttribute("y", String(offsetY));
}
else if (position === "middle") {
element.setAttribute("y", String(offsetY + lineYOffset / 2));
}
else {
element.setAttribute("y", String(offsetY + lineYOffset));
}
}
else {
if (extendBelowBaseline) {
element.setAttribute("y", String(offsetY + lineYOffset + wordHeight / 2));
extended = true;
}
else {
element.setAttribute("y", String(offsetY + lineYOffset));
}
}
});
// Fix the line height
if (extended)
wordHeight += wordHeight / 2;
// Add a space between words
offsetX += space_width * scaleFactor;
}
@@ -437,6 +622,12 @@ const assembleLine = ({ defects, scaleFactor, line, offsetY, }) => {
let lineHeight = 0;
let offsetX = indentRandom;
let lineElements = [];
// Detect the empty line
const lineArray = line.flat();
if (lineArray.length === 0) {
offsetY += 50 * scaleFactor + lineSpacingRandom;
return { lineContent, offsetY, offsetX };
}
for (let i = 0; i < line.length; i++) {
const word = line[i];
const { wordElements, wordHeight, offsetX: newOffsetX, } = assembleWord({
@@ -452,14 +643,70 @@ const assembleLine = ({ defects, scaleFactor, line, offsetY, }) => {
lineHeight = Math.max(lineHeight, wordHeight);
lineElements = lineElements.concat(wordElements);
}
// Update the offset
offsetY += lineHeight + lineSpacingRandom;
// Align the line elements to the bottom of the line
const aligned = verticalAlign(lineElements, lineHeight, offsetY, scaleFactor, defects);
[lineElements, lineHeight] = [aligned.lineElements, aligned.lineHeight];
offsetY += lineHeight || 10 * scaleFactor + lineSpacingRandom;
// Append the line elements to the SVG content
lineElements.forEach((e) => {
lineContent += e.element.outerHTML;
});
return { lineContent, offsetY, offsetX };
};
const verticalAlign = (lineElements, lineHeight, offsetY, scaleFactor, defects) => {
// Align the line elements to the bottom of the line
let extended = false;
const extendValue = 10 * scaleFactor;
lineElements.forEach((e) => {
const { element, isSpecial, position, extendBelowBaseline } = e;
// Match all numbers in style attribute
const style = element.getAttribute("transform");
const regex = /[-+]?[0-9]*\.?[0-9]+/g;
const { baselineOffset } = getRandomDefects(defects, scaleFactor);
const [x, y, s] = style.match(regex);
const elementHeight = parseInt(element.getAttribute("height"));
const lineYOffset = Math.abs(lineHeight - elementHeight);
const elementOffset = Math.abs(lineHeight - elementHeight);
if (isSpecial) {
if (position === "top") {
element.setAttribute("transform", `translate(${x}, ${offsetY}) scale(${s})`);
}
else if (position === "middle") {
element.setAttribute("transform", `translate(${x}, ${Number(y) + lineYOffset / 2}) scale(${s})`);
}
else {
element.setAttribute("transform", `translate(${x}, ${Number(y) + lineYOffset}) scale(${s})`);
}
}
else {
if (extendBelowBaseline) {
element.setAttribute("transform", `translate(${x}, ${Number(y) + lineYOffset + extendValue}) scale(${s})`);
extended = true;
}
else {
element.setAttribute("transform", `translate(${x}, ${Number(y) + elementOffset + baselineOffset}) scale(${s})`);
}
}
});
// Fix the line height
if (extended)
lineHeight += extendValue;
// Add line margin
lineHeight += 3 * scaleFactor;
return { lineElements, lineHeight };
};
const warpSvg = (svg, width, height) => {
// Create empty SVG document
const dom = new jsdom_1.default.JSDOM();
const svgWrapper = dom.window.document.createElementNS("http://www.w3.org/2000/svg", "svg");
// Set the SVG width and height
svgWrapper.setAttribute("width", String(width));
svgWrapper.setAttribute("height", String(height));
// Add the SVG content to the SVG document
svgWrapper.innerHTML = svg;
return svgWrapper === null || svgWrapper === void 0 ? void 0 : svgWrapper.outerHTML;
};
exports.warpSvg = warpSvg;
/**
* Writes the SVG content to a file and returns the server file path.
* @param svgContent - The SVG content to be written to the file.
@@ -468,6 +715,7 @@ const assembleLine = ({ defects, scaleFactor, line, offsetY, }) => {
const writeSVG = (svgContent, totalHeight, totalWidth) => __awaiter(void 0, void 0, void 0, function* () {
// wrap the SVG content in an SVG document
const outputFile = `<svg width="${totalWidth}" height="${totalHeight}" xmlns="http://www.w3.org/2000/svg">${svgContent}</svg>`; // Change this to your desired SVG content
// const outputFile = svgFlatten(svgContent).pathify().value();
// Write the SVG content to a file
const svgFilePath = `${paths_1.STATIC_PATH}/generated.svg`; // Change this to your desired file path
fs_1.default.writeFileSync(svgFilePath, outputFile);
@@ -478,14 +726,8 @@ const writeSVG = (svgContent, totalHeight, totalWidth) => __awaiter(void 0, void
const serverFilePath = `${basePath}:${port}/static/generated.svg?v=${Date.now()}`;
return serverFilePath;
});
/**
* Generates an SVG file based on the provided paths, scale factor, and defects.
* @param paths - A 3D array of file paths representing the characters to be included in the SVG.
* @param scaleFactor - The scale factor to apply to the SVG. Default is 1.
* @param defects - An object containing defect values for line spacing, kerning, letter size, and baseline offset.
* @returns A Promise that resolves to the file path of the generated SVG.
*/
const generateSvg = (paths, scaleFactor = 1, defects) => __awaiter(void 0, void 0, void 0, function* () {
const generateSvg = (options) => __awaiter(void 0, void 0, void 0, function* () {
const { paths, scaleFactor = 1, defects } = options;
let svgContent = "";
let offsetY = 0;
let totalHeight = 0;
@@ -505,7 +747,7 @@ const generateSvg = (paths, scaleFactor = 1, defects) => __awaiter(void 0, void
});
// Write the SVG content to a file
const serverFilePath = yield writeSVG(svgContent, totalHeight, totalWidth);
return serverFilePath;
return { serverFilePath, totalWidth, totalHeight };
});
exports.generateSvg = generateSvg;
/**
@@ -518,7 +760,7 @@ const printSVG = (inputPath) => __awaiter(void 0, void 0, void 0, function* () {
// Execute the following command : axicli inputPath usin os.system
const { execSync } = require("child_process");
try {
const command = `axicli ${inputPath}`;
const command = `axicli "${inputPath}"`;
const result = execSync(command, { encoding: "utf-8" });
// Process the result and return an object
return {
@@ -546,7 +788,7 @@ exports.createCharacterDirectory = createCharacterDirectory;
// Function to generate a unique SVG filename
const generateUniqueSVGPath = (characterDir) => __awaiter(void 0, void 0, void 0, function* () {
const characterLength = yield numberOfFiles(characterDir);
return `${characterDir}/${characterLength + 1}.svg`;
return `${characterDir}/${characterLength}.svg`;
});
exports.generateUniqueSVGPath = generateUniqueSVGPath;
// Function to write SVG content to a file