diff --git a/shared/src/publication/html/Locations.ts b/shared/src/publication/html/Locations.ts index 25f27aa5..5c438c06 100644 --- a/shared/src/publication/html/Locations.ts +++ b/shared/src/publication/html/Locations.ts @@ -49,11 +49,11 @@ declare module '../Locator' { time(): number | undefined; /** - * Spatial Dimension media fragment, used for example in audiobooks. + * Spatial Dimension media fragment, used for example in video/images. * - * https://www.w3.org/TR/media-frags/ + * https://www.w3.org/TR/media-frags/#naming-space */ - space(): [number, number, number, number] | undefined; + space(): { unit: "pixel" | "percent"; x: number; y: number; w: number; h: number } | undefined; } } @@ -118,12 +118,26 @@ LocatorLocations.prototype.time = function(): number | undefined { return parseNptTime(raw); } -LocatorLocations.prototype.space = function(): [number, number, number, number] | undefined { +LocatorLocations.prototype.space = function(): { unit: "pixel" | "percent"; x: number; y: number; w: number; h: number } | undefined { const fp = this.fragmentParameters(); - if(!fp.has("xywh")) return; - // TODO more sophiticated parsing to handle the format - const xywh = fp.get("xywh")!.split(",").map(s => parseInt(s)); - if(xywh.length !== 4) return; // Must have four parts - if(xywh.some(isNaN)) return; // All parts must be numbers - return xywh as [number, number, number, number]; + if (!fp.has("xywh")) return; + + const raw = fp.get("xywh")!; + let unit: "pixel" | "percent" = "pixel"; + let coords = raw; + + const normalizedRaw = raw.toLowerCase(); + if (normalizedRaw.startsWith("pixel:")) { + coords = raw.slice(6); + } else if (normalizedRaw.startsWith("percent:")) { + unit = "percent"; + coords = raw.slice(8); + } + + const parse = unit === "percent" ? parseFloat : (s: string) => parseInt(s, 10); + const parts = coords.split(",").map(parse); + if (parts.length !== 4 || parts.some(isNaN)) return; + + const [x, y, w, h] = parts; + return { unit, x, y, w, h }; }