Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get most correct y placement for all edge cases #13

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ dist
/lib
test_handle.html
test_svg.jpg
*_svg_*.jpg
test__handle_name_svg_0.png
[email protected]
test__handle_name_svg_01.png
Expand Down
106 changes: 45 additions & 61 deletions src/HandleSvg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { getImageDetails } from './utils/imageHelpers';

const supportedChars =
' 1234567890-!@#$%^&*()_=+qwertyuiop[]\\asdfghjkl;\'zxcvbnm,./QWWERTYUIOP{}}|ASDFGHJKL:"ZXCVBNM<>?ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïð';

// Characters without ascenders or descenders (sitting between x-height and baseline)
export const noAscenderDescenderChars = 'aceimnorsuvwxz0123456789-._';

export default class HandleSvg {
private _options: IHandleSvgOptions;
private _params: { size: number; handle: string; disableDollarSymbol: boolean };
Expand Down Expand Up @@ -616,23 +620,20 @@ export default class HandleSvg {
// Build the handle with only "$" logo and handle text (ex: "$bigirishlion")
async buildLogoAndHandleName(decompress: any, opentype: any) {
const { size, handle } = this._params;

// Since this will be used for just the text, the height can be reduced
const sizeY = size / 3;

// ****** GENERAL FONT SETTINGS *******
// font color must be slighly off white due to Sharp's unflatten
// https://sharp.pixelplumbing.com/api-operation#unflatten
const font_color = '0xfdfdfd';
const font = 'Inter';
const baseFontSize = 200;
const fontSize = size * (baseFontSize / this._baseSize);
const minimumFontHeight = size * (getMinimumFontSize(handle) / this._baseSize);
const fontMarginX = size * (200 / this._baseSize);
const fontMarginY = size * (94 / this._baseSize);
const ribbonHeight = size * (314 / this._baseSize);
const maxFontWidth = size - fontMarginX;
const maxFontHeight = ribbonHeight - fontMarginY;
const minimumFontHeight = size * (getMinimumFontSize(handle) / this._baseSize);


// ****** LOAD AND PARSE THE FONT *******
const { parsedFont, boundingBox: bb } = await this.loadParsedFont(font, decompress, handle, fontSize, opentype);
Expand All @@ -641,87 +642,70 @@ export default class HandleSvg {

const svg = path.toSVG(2);

// ******* PLACEMENT AND ZOOM MATH *******
// Get reference height using zeros
const referenceText = '0'.repeat(handle.length);
const { boundingBox: refBB } = await this.loadParsedFont(font, decompress, referenceText, fontSize, opentype);
const referenceHeight = refBB.y2 - refBB.y1;

// ******* TEXT DIMENSIONS *******
let realFontHeight = bb.y2 - bb.y1;
const originalFontHeight = realFontHeight;
const realFontWidth = bb.x2 - bb.x1;

// For ex) ".", "..", "_", "-"
if (realFontHeight < minimumFontHeight) {
// This is to keep really small characters from becoming ginormous
realFontHeight = minimumFontHeight;
}

// First calculate zoom for text only to determine final text size
let textOnlyZoomPercent = (maxFontWidth - realFontWidth) / realFontWidth;
let zoomPercent = (maxFontWidth - realFontWidth) / realFontWidth;
if (realFontHeight / maxFontHeight > realFontWidth / maxFontWidth) {
textOnlyZoomPercent = (maxFontHeight - realFontHeight) / realFontHeight;
zoomPercent = (maxFontHeight - realFontHeight) / realFontHeight;
}
textOnlyZoomPercent = 1 + textOnlyZoomPercent;

// Begin calculating dollar sign size and placement
zoomPercent = 1 + zoomPercent;

// The dollar sign SVG path coordinates
// ******* DOLLAR SIGN DIMENSIONS *******
const dollarSignBounds = {
minY: 0,
maxY: 31.7,
maxY: 15.38,
minX: 0,
maxX: 14
maxX: 10.25
};

const dollarPathHeight = dollarSignBounds.maxY - dollarSignBounds.minY;
const dollarAscenderSize = 1.4;
const dollarDescenderSize = 1.4;
const dollarBodyMinY = dollarAscenderSize;
const dollarBodyMaxY = dollarSignBounds.maxY - dollarDescenderSize;
const dollarBodyHeight = dollarBodyMaxY - dollarBodyMinY;
const dollarPathWidth = dollarSignBounds.maxX - dollarSignBounds.minX;

// For 1-2 character handles, make dollar sign same height as text
const isShortHandle = handle.length <= 2;
const dollarSignScale = isShortHandle
? ((originalFontHeight * 2.45) / dollarPathHeight) * textOnlyZoomPercent
: ((originalFontHeight * 2) / dollarPathHeight) * textOnlyZoomPercent;
const dollarSignScaleWidth = originalFontHeight / dollarPathHeight / textOnlyZoomPercent;

// Calculate width while maintaining aspect ratio
const dollarSignWidth = dollarSignScaleWidth * dollarPathWidth;
const spacing = size * ((45 * textOnlyZoomPercent) / this._baseSize);
// Scale dollar sign to match text body height
const dollarScale = referenceHeight / dollarBodyHeight;
const dollarWidth = dollarPathWidth * dollarScale;

// Calculate total width including dollar sign and spacing
const totalWidth = realFontWidth + dollarSignWidth + spacing;

// Now calculate final zoom including dollar sign
let zoomPercent = (maxFontWidth - totalWidth) / totalWidth;
if (realFontHeight / maxFontHeight > totalWidth / maxFontWidth) {
zoomPercent = (maxFontHeight - realFontHeight) / realFontHeight;
}
zoomPercent = 1 + zoomPercent;

const viewBoxWidth = size / zoomPercent;
const viewBoxHeight = sizeY / zoomPercent;
// ******* LAYOUT CALCULATIONS *******
const spacing = size * (20 / this._baseSize);
const totalWidth = realFontWidth + dollarWidth + spacing;

// Center the combined elements
const combinedX = size / 2 - (totalWidth / 2 + bb.x1) * zoomPercent;
const y = sizeY / 2 - (realFontHeight / 2 + bb.y2) * zoomPercent;
const centerX = size / 2 - (totalWidth * zoomPercent) / 2;
const centerY = sizeY / 2;

const viewBoxX = 0;
const viewBoxY = (realFontHeight - (realFontHeight - (bb.y2 - bb.y1)) / 2 / zoomPercent) * -1;

const viewBox = `viewBox="${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}"`;
// Position elements relative to center
const dollarX = centerX;
const textX = dollarX + dollarWidth * zoomPercent + spacing - bb.x1;
const textY = centerY - (referenceHeight * zoomPercent) / 2;

// Position the dollar sign to the left of the text with proper spacing
const dollarSignX = combinedX;
const handleX = dollarSignX + (dollarSignWidth + spacing) * zoomPercent;
// Adjust vertical position of dollar sign to align with text
const dollarSignY = y + originalFontHeight * (isShortHandle ? -1.2 : zoomPercent) * 0.12; // Small adjustment to visually center the dollar sign

const dollarFill = '#0cd15b'; // default green color

// Create the dollar sign SVG with the same scale as the text
const dollarSignSvg = `
<path transform="translate(${-dollarSignBounds.minX}, ${-dollarSignBounds.minY}) scale(${dollarSignScale})" id="S" fill="${dollarFill}" d="M3.48,1.16c0-.28,.21-.54,.64-.78,.43-.25,.99-.38,1.7-.38,.54,0,.89,.12,1.05,.36,.11,.17,.17,.33,.17,.47v.54c1.28,.14,2.16,.4,2.66,.76,.2,.14,.31,.28,.31,.42,0,.63-.14,1.3-.42,2.02-.27,.72-.54,1.08-.8,1.08-.05,0-.19-.05-.44-.16-.75-.35-1.37-.52-1.86-.52s-.82,.05-1,.14c-.17,.1-.25,.25-.25,.45,0,.19,.12,.34,.37,.45,.25,.11,.55,.2,.92,.29,.37,.07,.77,.2,1.2,.38,.44,.18,.85,.4,1.22,.65,.37,.25,.68,.63,.93,1.14,.25,.49,.37,1,.37,1.52s-.05,.95-.14,1.28c-.09,.34-.25,.69-.47,1.05-.21,.36-.55,.69-1,.98-.44,.28-.97,.48-1.59,.61v.29c0,.28-.21,.54-.64,.78-.43,.25-.99,.38-1.7,.38-.54,0-.89-.12-1.05-.36-.11-.17-.17-.33-.17-.47v-.54c-.73-.07-1.37-.19-1.92-.34-1.04-.3-1.56-.63-1.56-.98,0-.67,.09-1.37,.27-2.1,.18-.73,.4-1.1,.64-1.1,.05,0,.51,.16,1.41,.49s1.57,.49,2.02,.49,.75-.05,.88-.14c.14-.11,.2-.26,.2-.45s-.12-.36-.37-.49c-.25-.14-.56-.26-.93-.34-.37-.08-.78-.22-1.22-.4-.43-.18-.83-.39-1.2-.63-.37-.24-.68-.6-.93-1.07-.25-.47-.37-1.02-.37-1.64C.39,3.06,1.42,1.76,3.48,1.39v-.23Z"/>
`;
const dollarFill = '#0cd15b';
const dollarSignSvg = `<path id="S" fill="${dollarFill}" d="M3.48,1.16c0-.28,.21-.54,.64-.78,.43-.25,.99-.38,1.7-.38,.54,0,.89,.12,1.05,.36,.11,.17,.17,.33,.17,.47v.54c1.28,.14,2.16,.4,2.66,.76,.2,.14,.31,.28,.31,.42,0,.63-.14,1.3-.42,2.02-.27,.72-.54,1.08-.8,1.08-.05,0-.19-.05-.44-.16-.75-.35-1.37-.52-1.86-.52s-.82,.05-1,.14c-.17,.1-.25,.25-.25,.45,0,.19,.12,.34,.37,.45,.25,.11,.55,.2,.92,.29,.37,.07,.77,.2,1.2,.38,.44,.18,.85,.4,1.22,.65,.37,.25,.68,.63,.93,1.14,.25,.49,.37,1,.37,1.52s-.05,.95-.14,1.28c-.09,.34-.25,.69-.47,1.05-.21,.36-.55,.69-1,.98-.44,.28-.97,.48-1.59,.61v.29c0,.28-.21,.54-.64,.78-.43,.25-.99,.38-1.7,.38-.54,0-.89-.12-1.05-.36-.11-.17-.17-.33-.17-.47v-.54c-.73-.07-1.37-.19-1.92-.34-1.04-.3-1.56-.63-1.56-.98,0-.67,.09-1.37,.27-2.1,.18-.73,.4-1.1,.64-1.1,.05,0,.51,.16,1.41,.49s1.57,.49,2.02,.49,.75-.05,.88-.14c.14-.11,.2-.26,.2-.45s-.12-.36-.37-.49c-.25-.14-.56-.26-.93-.34-.37-.08-.78-.22-1.22-.4-.43-.18-.83-.39-1.2-.63-.37-.24-.68-.6-.93-1.07-.25-.47-.37-1.02-.37-1.64C.39,3.06,1.42,1.76,3.48,1.39v-.23Z"/>`;

return `
<svg id="dollar_sign" x="${dollarSignX}" y="${dollarSignY}" xmlns="http://www.w3.org/2000/svg">
${dollarSignSvg}
<svg id="handle_name_${handle}" xmlns="http://www.w3.org/2000/svg" width="${size}" height="${sizeY}">
<g transform="translate(${dollarX},${textY - ((dollarAscenderSize * dollarScale))}) scale(${dollarScale * zoomPercent})">
${dollarSignSvg}
</g>
<g transform="translate(${textX},${textY + (dollarBodyHeight * dollarScale * zoomPercent)}) scale(${zoomPercent})">
${svg}
</g>
</svg>
<svg id="handle_name_${handle}" x="${handleX}" y="${y}" xmlns="http://www.w3.org/2000/svg" ${viewBox}>${svg}</svg>
`;
}

Expand Down
139 changes: 75 additions & 64 deletions src/scripts/renderHandleNameLocally.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,27 @@ const buildTestHandle = async (handle: string) => {
console.log('STARTED!');
try {
const handles = [
// '0ctopus',
// '1nternet lnternet',
// 'mmw5j7h0gqklwmm',
// '1lnternetz',
// 'w00di',
// '0o1lijt2z5s8b',
// 'b_.-mj',
'0ctopus',
'jlg',
'0o1lijt2z5g8@0o1lijt2z5s8',
'0',
'01'
'bigirishlion',
// '0o1lijt2z5g8@0o1lijt2z5s8', // bad
// '0o1lijt2z5g8@0o1', // looks good
'0o1lijt2z5g8@0o1lijd',
'jl5',
'h5',
'jh'
// '0',
// '05',
// '055',
// '0555',
// 'h',
// 'j',
// ".",
// "..",
// "_",
// "-",
// "---",
// "____",
];

// ****** SHORT HANDLE ******
Expand All @@ -138,60 +148,61 @@ const buildTestHandle = async (handle: string) => {
svg = await buildTestHandle(handle);

// --------- HTML-------

const html = `
<html>
<head>
<title>${handle} SVG</title>
<script type="text/javascript" src="https://unpkg.com/[email protected]/build/decompress_binding.js"></script>
</head>
<body style="margin: 0; padding: 0;">
<div style="
position: absolute;
width: 435px;
height: 2px;
background: red;
top: 1075px;
left: 811px;
"></div>
<div style="
position: absolute;
width: 435px;
height: 1px;
background: red;
top: 1024px;
left: 811px;
"></div>
<div style="
position: absolute;
width: 435px;
height: 1px;
background: red;
top: 973px;
left: 811px;
"></div>
<div style="
position: absolute;
width: 1px;
height: 130px;
background: red;
position: absolute;
top: 960;
left: 1024px;
"></div>
${svg}
</body>
</html>
`;

// write html file
fs.writeFile(`test_handle_${handle}.html`, html, (err: any) => {
// throws an error, you could also catch it here
if (err) throw err;

// success case, the file was saved
console.log('HTML written!');
});
if (false) {
const html = `
<html>
<head>
<title>${handle} SVG</title>
<script type="text/javascript" src="https://unpkg.com/[email protected]/build/decompress_binding.js"></script>
</head>
<body style="margin: 0; padding: 0;">
<div style="
position: absolute;
width: 435px;
height: 2px;
background: red;
top: 1075px;
left: 811px;
"></div>
<div style="
position: absolute;
width: 435px;
height: 1px;
background: red;
top: 1024px;
left: 811px;
"></div>
<div style="
position: absolute;
width: 435px;
height: 1px;
background: red;
top: 973px;
left: 811px;
"></div>
<div style="
position: absolute;
width: 1px;
height: 130px;
background: red;
position: absolute;
top: 960;
left: 1024px;
"></div>
${svg}
</body>
</html>
`;

// write html file
fs.writeFile(`test_handle_${handle}.html`, html, (err: any) => {
// throws an error, you could also catch it here
if (err) throw err;

// success case, the file was saved
console.log('HTML written!');
});
}
}
} catch (error) {
console.error('ERROR:', error);
Expand Down
11 changes: 11 additions & 0 deletions src/scripts/renderLocally.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@ const options: IHandleSvgOptions = {
const handle = '0o1lijt2z5s8@0o1lijt2z5s8';
// 0ctopus, 1nternet lnternet

const options: IHandleSvgOptions = {
font_shadow_color: '0x353eabff',
font_shadow_size: [8, 8, 5],
pfp_zoom: 100,
pfp_offset: [0, 0],
bg_image: 'ipfs://bafybeig5xnhvexbkq7i462vmp6ycjtnt262eggusqpa3gpftk7ndcm7ycy',
qr_bg_color: '0xffffff00',
creator_defaults_enabled: 1,
bg_asset: '0x788765a51e4add65ac37e686b937e6698964ad68910d3503674aef97001bc2804e6967687420416c70'
};

const input: IHandleSvg = {
handle,
disableDollarSymbol: false,
Expand Down