diff --git a/src/dominiontext.ts b/src/dominiontext.ts index a4d8f2b..5a98f9a 100644 --- a/src/dominiontext.ts +++ b/src/dominiontext.ts @@ -1,4 +1,5 @@ import { getImage } from "./draw.ts"; +import { parseFont, stringifyFont } from "./fonthelper.ts"; export type Piece = | { type: "text"; text: string; isBold?: boolean; isItalic?: boolean } @@ -18,6 +19,7 @@ type PieceMeasure = { type Line = { pieces: { piece: Piece; + measure: PieceMeasure; xOffset: number; }[]; width: number; @@ -108,10 +110,12 @@ const breakPiece = pieceDef({ const coinPiece = pieceDef({ type: "coin", measure(context, _piece) { + context.save(); const metrics = context.measureText(" "); const height = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; const coinImage = getImage("coin"); + context.restore(); return { type: "content", width: coinImage.width * (height / coinImage.height), @@ -131,6 +135,12 @@ const coinPiece = pieceDef({ measure.width, height ); + const fontInfo = parseFont(context.font); + fontInfo.family = ["DominionSpecial"]; + fontInfo.weight = "bold"; + fontInfo.size = parseInt(fontInfo.size.toString()) * 1.2; + const font = stringifyFont(fontInfo); + context.font = font; context.fillStyle = "black"; context.textAlign = "center"; context.fillText(piece.text, x + measure.width / 2, y); @@ -140,7 +150,7 @@ const coinPiece = pieceDef({ const pieceDefs = [textPiece, spacePiece, breakPiece, coinPiece]; -let tools: PieceTools = {} as any; +const tools: PieceTools = {} as any; const measurePiece = (context: CanvasRenderingContext2D, piece: Piece) => { const def = pieceDefs.find((def) => def.type === piece.type)!; @@ -188,7 +198,7 @@ export const measureDominionText = async ( for (const pieceInfo of data) { const line = lines[lines.length - 1]!; if (pieceInfo.measure.type === "break") { - line.pieces.push({ piece: pieceInfo.piece, xOffset: line.width }); + line.pieces.push({ ...pieceInfo, xOffset: line.width }); line.width += pieceInfo.measure.width; line.ascent = Math.max(line.ascent, pieceInfo.measure.ascent); line.descent = Math.max(line.descent, pieceInfo.measure.descent); @@ -196,14 +206,14 @@ export const measureDominionText = async ( } else { if (line.width + pieceInfo.measure.width > maxWidth) { lines.push({ - pieces: [{ piece: pieceInfo.piece, xOffset: 0 }], + pieces: [{ ...pieceInfo, xOffset: 0 }], width: pieceInfo.measure.width, ascent: pieceInfo.measure.ascent, descent: pieceInfo.measure.descent, }); } else { line.pieces.push({ - piece: pieceInfo.piece, + ...pieceInfo, xOffset: line.width, }); line.width += pieceInfo.measure.width; @@ -216,7 +226,15 @@ export const measureDominionText = async ( } } return { - lines, + lines: lines.map((line) => { + while ( + line.pieces[line.pieces.length - 1] && + line.pieces[line.pieces.length - 1]!.measure.type === "space" + ) { + line.pieces = line.pieces.slice(0, -1); + } + return line; + }), width: Math.max(...lines.map((line) => line.width)), height: lines .map((line) => line.ascent + line.descent) @@ -224,6 +242,8 @@ export const measureDominionText = async ( }; }; +const debug = false; + export const renderDominionText = async ( context: CanvasRenderingContext2D, pieces: Piece[], @@ -246,6 +266,30 @@ export const renderDominionText = async ( x - line.width / 2 + xOffset, y - height / 2 + yOffset ); + if (debug) { + context.save(); + context.strokeStyle = "blue"; + context.lineWidth = 5; + const pieceMeasure = await measurePiece(context, piece); + context.strokeRect( + x - line.width / 2 + xOffset, + y - height / 2 - line.ascent + yOffset, + pieceMeasure.width, + pieceMeasure.ascent + pieceMeasure.descent + ); + context.strokeStyle = "red"; + context.beginPath(); + context.moveTo( + x - line.width / 2 + xOffset - 5, + y - height / 2 + yOffset + ); + context.lineTo( + x - line.width / 2 + xOffset + 5, + y - height / 2 + yOffset + ); + context.stroke(); + context.restore(); + } } yOffset += line.descent; } diff --git a/src/draw.ts b/src/draw.ts index 7fac90c..47b822e 100644 --- a/src/draw.ts +++ b/src/draw.ts @@ -107,24 +107,20 @@ const drawStandardCard = async ( context.drawImage(getImage("card-description-focus"), 44, 1094); // Draw the name context.font = "75pt DominionTitle"; - await renderDominionText( - context, - parse("Moonlit Scheme"), - w / 2, - 220, - 1100 - ); + await renderDominionText(context, parse(card.title), w / 2, 220, 1100); // Draw the description context.font = "60pt DominionText"; await renderDominionText( context, - parse("You may play an Action card from your hand costing up to $4."), + parse(card.description), w / 2, 1520, 1100 ); // Draw the types // Draw the cost + context.font = "90pt DominionText"; + await renderDominionText(context, parse(card.price), 210, 1940, 200); // Draw the preview // Draw the icon // Draw the credit diff --git a/src/fonthelper.ts b/src/fonthelper.ts new file mode 100644 index 0000000..ab7842d --- /dev/null +++ b/src/fonthelper.ts @@ -0,0 +1,41 @@ +import font from "npm:css-font"; + +export type FontInfo = { + style: "normal" | "italic" | "oblique"; + variant: "normal" | "small-caps"; + weight: + | "normal" + | "bold" + | "lighter" + | "bolder" + | "100" + | "200" + | "300" + | "400" + | "500" + | "600" + | "700" + | "800" + | "900"; + stretch: + | "normal" + | "condensed" + | "semi-condensed" + | "extra-condensed" + | "ultra-condensed" + | "expanded" + | "semi-expanded" + | "extra-expanded" + | "ultra-expanded"; + lineHeight: "normal" | number | string; + size: number | string; + family: string[]; +}; + +export const parseFont = (fontString: string): FontInfo => { + return { ...font.parse(fontString) }; +}; + +export const stringifyFont = (fontInfo: FontInfo): string => { + return font.stringify(fontInfo); +}; diff --git a/src/sampleData.ts b/src/sampleData.ts index 774a00f..46c02cf 100644 --- a/src/sampleData.ts +++ b/src/sampleData.ts @@ -9,6 +9,6 @@ export const sampleCard: DominionCard = { artist: "", author: "", version: "", - price: "", + price: "$", preview: "", }; diff --git a/static/fonts.css b/static/fonts.css index 8717be9..3f828d1 100644 --- a/static/fonts.css +++ b/static/fonts.css @@ -17,7 +17,7 @@ } @font-face { - font-family: 'DominionSpecials'; + font-family: 'DominionSpecial'; font-display: auto; src: local("Minion Std Black"), local("MinionStd-Black"), local("Minion Std"), local('Minion Pro'), url('https://fonts.cdnfonts.com/s/13260/MinionPro-Regular.woff') format('woff'),