From e36ddbe9adc443db9e6d892c4f1acd057d141b26 Mon Sep 17 00:00:00 2001 From: b0txec Date: Thu, 20 Aug 2026 09:52:58 +0300 Subject: [PATCH] Fix broadcast overlay scaling --- docs/PRODUCT_WORKFLOWS.md | 3 +++ docs/THIRD_PARTY_NOTICES.md | 4 ++++ docs/UPDATE_ROADMAP.md | 1 + web/src/components/map/canvasDraw.ts | 4 ++-- .../water-temperature/WaterTemperature.tsx | 24 +++++++++++-------- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/PRODUCT_WORKFLOWS.md b/docs/PRODUCT_WORKFLOWS.md index 8b82a69..152da31 100644 --- a/docs/PRODUCT_WORKFLOWS.md +++ b/docs/PRODUCT_WORKFLOWS.md @@ -104,6 +104,8 @@ Required behavior: The background, title, source, field set, and value positions remain fixed. Only the water-temperature values are routinely edited. Both output sizes are production requirements and must be validated independently; implementation must not assume that browser preview dimensions define export geometry. +When switching output size, the background may use the target canvas aspect ratio, but boxes, typography, and nameplate elements must scale uniformly. They must never be independently stretched on the horizontal and vertical axes. + Implementation status: first working version implemented. The page provides six Latvian-named manual min/max controls, automatic fixed placement, Monda rendering, explicit resolution selection, completeness validation, and Latvian PNG filenames. Pixel-level comparison and independent calibration of both output sizes remain required. ## Latvia overview @@ -136,4 +138,5 @@ Development limitation: representative forecast CSV fixtures are not currently i - Whether Stende and Zīlāni remain the production feeds after users test real provider data. - Confirm how Valmiera is named and sourced when authorized provider data is available. - Final pixel-level Monda export comparison against the production masters. +- Replace Daira font-glyph rendering with a normalized SVG weather-symbol set after the design assets are supplied. - Required authentication and role separation for analytical, production, and administrative workspaces. diff --git a/docs/THIRD_PARTY_NOTICES.md b/docs/THIRD_PARTY_NOTICES.md index b793024..dca4d54 100644 --- a/docs/THIRD_PARTY_NOTICES.md +++ b/docs/THIRD_PARTY_NOTICES.md @@ -17,3 +17,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ## Monda The locally bundled Monda Regular and Monda Bold font files are distributed under the SIL Open Font License 1.1. The complete license text is stored at `Monda/OFL.txt`. + +## Planned weather-symbol SVG handoff + +The production designer will export the authorized Daira weather symbols as outlined SVG artwork for use in the web renderer. Each file should use the same transparent 256×256 artboard, contain no embedded font or raster image, use a tight but optically consistent visible scale, preserve the production stroke appearance, and have a descriptive stable filename. Licensing/authorization for those workplace graphical assets must be confirmed before external distribution. diff --git a/docs/UPDATE_ROADMAP.md b/docs/UPDATE_ROADMAP.md index 0579445..3ee154b 100644 --- a/docs/UPDATE_ROADMAP.md +++ b/docs/UPDATE_ROADMAP.md @@ -120,6 +120,7 @@ Status: pending - [x] Add an explicit PNG download workflow for broadcast map assets. - [x] Implement the fixed Faktiskā station set, latest-temperature loading, manual overrides, and locked 3840×1440 export. - [x] Make Faktiskā weather-symbol selection manual while keeping placement automatic and badge-relative. +- [ ] Replace Daira font glyphs in the web renderer with normalized SVG assets supplied from the production design workflow. - [x] Add a separate Latvian-named Ūdens temperatūra workspace with fixed manual fields. - [ ] Validate independent Ūdens temperatūra exports at 1920×1080 and 3840×1440 against the production specification. - [x] Bundle the confirmed Monda Regular/Bold files and replace temporary Rubik rendering. diff --git a/web/src/components/map/canvasDraw.ts b/web/src/components/map/canvasDraw.ts index 24993c9..9c92f90 100644 --- a/web/src/components/map/canvasDraw.ts +++ b/web/src/components/map/canvasDraw.ts @@ -128,7 +128,7 @@ function drawTitleOverlay(ctx: CanvasRenderingContext2D, title: string, source: ctx.textBaseline = "middle"; ctx.font = `bold ${30 * scale}px Monda`; const titleText = title.trim().toUpperCase(); - const titleWidth = Math.max(360 * scale, ctx.measureText(titleText).width + horizontalPadding * 2); + const titleWidth = ctx.measureText(titleText).width + horizontalPadding * 2; const titleX = canvasWidth - right - titleWidth; ctx.fillStyle = "#9b0008"; @@ -139,7 +139,7 @@ function drawTitleOverlay(ctx: CanvasRenderingContext2D, title: string, source: if (source.trim()) { ctx.font = `bold ${19 * scale}px Monda`; const sourceText = source.trim().toUpperCase(); - const sourceWidth = Math.max(130 * scale, ctx.measureText(sourceText).width + horizontalPadding * 2); + const sourceWidth = ctx.measureText(sourceText).width + horizontalPadding * 2; const sourceX = canvasWidth - right - sourceWidth; ctx.fillStyle = "#FFFFFF"; ctx.fillRect(sourceX, top + titleHeight, sourceWidth, sourceHeight); diff --git a/web/src/pages/water-temperature/WaterTemperature.tsx b/web/src/pages/water-temperature/WaterTemperature.tsx index 8af1833..bbd59b4 100644 --- a/web/src/pages/water-temperature/WaterTemperature.tsx +++ b/web/src/pages/water-temperature/WaterTemperature.tsx @@ -107,9 +107,12 @@ export function WaterTemperature() { function drawRanges(ctx: CanvasRenderingContext2D, width: number, height: number, values: Record) { const scaleX = width / 3840; const scaleY = height / 1440; - const fontSize = 88 * Math.min(scaleX, scaleY); - const badgeHeight = 132 * scaleY; - const padding = 56 * scaleX; + // Backgrounds use their target aspect ratio, but production graphics scale + // uniformly so boxes and type are never stretched vertically or horizontally. + const graphicScale = scaleX; + const fontSize = 88 * graphicScale; + const badgeHeight = 132 * graphicScale; + const padding = 56 * graphicScale; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.font = `700 ${fontSize}px Monda`; @@ -131,26 +134,27 @@ function drawRanges(ctx: CanvasRenderingContext2D, width: number, height: number function drawNameplate(ctx: CanvasRenderingContext2D, width: number, height: number) { const scaleX = width / 3840; const scaleY = height / 1440; + const graphicScale = scaleX; const x = 2698 * scaleX; const y = 145 * scaleY; - const titleWidth = 1062 * scaleX; - const titleHeight = 124 * scaleY; - const sourceWidth = 290 * scaleX; - const sourceHeight = 60 * scaleY; + const titleWidth = 1062 * graphicScale; + const titleHeight = 124 * graphicScale; + const sourceWidth = 290 * graphicScale; + const sourceHeight = 60 * graphicScale; ctx.fillStyle = '#8f090b'; ctx.fillRect(x, y, titleWidth, titleHeight); ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; - ctx.font = `700 ${56 * Math.min(scaleX, scaleY)}px Monda`; + ctx.font = `700 ${56 * graphicScale}px Monda`; ctx.fillStyle = '#fff'; - ctx.fillText('ŪDENS TEMPERATŪRA', x + 66 * scaleX, y + titleHeight / 2); + ctx.fillText('ŪDENS TEMPERATŪRA', x + 66 * graphicScale, y + titleHeight / 2); const sourceX = x + titleWidth - sourceWidth; ctx.fillStyle = '#f4f4f4'; ctx.fillRect(sourceX, y + titleHeight, sourceWidth, sourceHeight); ctx.fillStyle = '#000'; - ctx.font = `700 ${39 * Math.min(scaleX, scaleY)}px Monda`; + ctx.font = `700 ${39 * graphicScale}px Monda`; ctx.textAlign = 'center'; ctx.fillText('LVĢMC', sourceX + sourceWidth / 2, y + titleHeight + sourceHeight / 2); }