From ff5c543147b5333bf37372911267e2242299a00a Mon Sep 17 00:00:00 2001 From: Dylan Pizzo Date: Tue, 7 Jan 2025 20:02:50 -0800 Subject: [PATCH] Allow big symbols --- src/dominiontext.ts | 91 +++++++++++++++++++++++++++++++++++++++------ src/draw.ts | 3 +- src/sampleData.ts | 2 +- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/dominiontext.ts b/src/dominiontext.ts index 40b4f4a..d1d4488 100644 --- a/src/dominiontext.ts +++ b/src/dominiontext.ts @@ -9,6 +9,8 @@ export type Piece = | { type: "symbol"; symbol: "coin" | "debt" | "potion" | "vp" | "vp-token"; + isBig?: boolean; + prefix?: string; text: string; textColor: string; }; @@ -157,13 +159,21 @@ const symbolPiece = pieceDef({ const metrics = context.measureText(" "); const height = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; + const prefixMetrics = context.measureText(piece.prefix ?? ""); const coinImage = getImage(piece.symbol); context.restore(); + const { isBig } = piece; + const scale = isBig ? 2.5 : 1; return { type: "content", - width: coinImage.width * (height / coinImage.height), - ascent: metrics.fontBoundingBoxAscent, - descent: metrics.fontBoundingBoxDescent, + width: + scale * + (prefixMetrics.width + + coinImage.width * (height / coinImage.height)), + ascent: scale * metrics.fontBoundingBoxAscent, + descent: scale * metrics.fontBoundingBoxDescent, + prefixWidth: scale * prefixMetrics.width, + scale, }; }, render(context, piece, x, y, measure) { @@ -173,20 +183,34 @@ const symbolPiece = pieceDef({ // context.fillRect(x, y - measure.ascent, measure.width, height); context.drawImage( getImage(piece.symbol), - x, + x + measure.prefixWidth, y - measure.ascent, - measure.width, + measure.width - measure.prefixWidth, height ); + + const prefixFontInfo = parseFont(context.font); + prefixFontInfo.weight = "bold"; + prefixFontInfo.size = + parseInt(prefixFontInfo.size.toString()) * measure.scale; + const prefixFont = stringifyFont(prefixFontInfo); + context.font = prefixFont; + context.fillText(piece.prefix ?? "", x, y); + const fontInfo = parseFont(context.font); fontInfo.family = ["DominionSpecial"]; fontInfo.weight = "bold"; - fontInfo.size = parseInt(fontInfo.size.toString()) * 1.2; + fontInfo.size = + parseInt(fontInfo.size.toString()) * 1.2 * measure.scale; const font = stringifyFont(fontInfo); context.font = font; context.fillStyle = piece.textColor; context.textAlign = "center"; - context.fillText(piece.text, x + measure.width / 2, y); + context.fillText( + piece.text, + x + measure.prefixWidth + (measure.width - measure.prefixWidth) / 2, + y + ); context.restore(); }, }); @@ -341,7 +365,11 @@ export const renderDominionText = async ( } }; -export const parse = (text: string): Piece[] => { +export const parse = ( + text: string, + options?: { isDescription: boolean } +): Piece[] => { + const { isDescription = false } = options ?? {}; const pieces: Piece[] = []; const symbolMap = { "$": { symbol: "coin", textColor: "black" }, @@ -351,23 +379,28 @@ export const parse = (text: string): Piece[] => { "#": { symbol: "vp-token", textColor: "black" }, } as const; for (let i = 0; i < text.length; i++) { - const char = text[i]; + const char = text[i]!; if (char === " ") { pieces.push({ type: "space" }); } else if (char === "\n") { pieces.push({ type: "break" }); - } else if (char && char in symbolMap) { + } else if (char in symbolMap) { const c = char as keyof typeof symbolMap; const end = text.slice(i).match(new RegExp(`\\${c}\\w*`))![0] .length; + const isBig = + isDescription && + ["\n", undefined].includes(text[i - 1]) && + ["\n", undefined].includes(text[i + end]); pieces.push({ type: "symbol", ...symbolMap[c], text: text.slice(i + 1, i + end), + isBig, }); i += end - 1; } else if (char === "+") { - const match = text.slice(i).match(/\+\d* \w+/); + const match = text.slice(i).match(/\+\d*( \w+)?/); if (match) { const end = match[0].length; pieces.push({ @@ -389,8 +422,42 @@ export const parse = (text: string): Piece[] => { text[i + 1] === "\n" ) { pieces.push({ type: "hr" }); + } else if (/\d/.test(char)) { + const match = text.slice(i).match( + new RegExp( + `\\d+(${Object.keys(symbolMap) + .map((s) => `\\${s}`) + .join("|")})` + ) + ); + if (match) { + const end = match[0].length; + const symbolChar = match[1] as keyof typeof symbolMap; + const isBig = + isDescription && + ["\n", undefined].includes(text[i - 1]) && + ["\n", undefined].includes(text[i + end]); + pieces.push({ + type: "symbol", + ...symbolMap[symbolChar], + prefix: text.slice(i, i + end - 1), + text: "", + isBig, + }); + i += end - 1; + } else { + const end = text.slice(i).match(/\d+/)![0].length; + pieces.push({ type: "text", text: text.slice(i, i + end) }); + i += end - 1; + } } else { - const end = text.slice(i).match(/[^$ \n]+/)![0].length; + const end = text.slice(i).match( + new RegExp( + `[^${Object.keys(symbolMap) + .map((s) => `\\${s}`) + .join("")} \n]+` + ) + )![0].length; pieces.push({ type: "text", text: text.slice(i, i + end) }); i += end - 1; } diff --git a/src/draw.ts b/src/draw.ts index d337d9b..e231829 100644 --- a/src/draw.ts +++ b/src/draw.ts @@ -181,11 +181,12 @@ const drawStandardCard = async ( context.font = "60pt DominionText"; await renderDominionText( context, - parse(card.description), + parse(card.description, { isDescription: true }), w / 2, 1490, 1000 ); + console.log(card.title, parse(card.description)); // Draw the types size = 65; context.font = `${size}pt DominionTitle`; diff --git a/src/sampleData.ts b/src/sampleData.ts index 3bb2e43..8c455b2 100644 --- a/src/sampleData.ts +++ b/src/sampleData.ts @@ -48,7 +48,7 @@ export const sampleCards: DominionCard[] = [ { orientation: "card", title: "VP Card", - description: "+1 #\n\n-\n\n2 %", + description: "+1#\n\n\n-\n\n\n2%", types: [TYPE_VICTORY], image: "", artist: "",