* Creates an SVG from the hat SVG that pads, offsets and scales it to end up * in the right size / place relative to the character it will be placed over. * [This image](../images/svg-calculations.png) may or may not be helpful. * * @param fontMeasurements Info about the user's font *
(
fontMeasurements: FontMeasurements,
shape: HatShape,
scaleFactor: number,
hatVerticalOffsetEm: number
)
| 249 | * @returns An object with the new SVG and its measurements |
| 250 | */ |
| 251 | private processSvg( |
| 252 | fontMeasurements: FontMeasurements, |
| 253 | shape: HatShape, |
| 254 | scaleFactor: number, |
| 255 | hatVerticalOffsetEm: number |
| 256 | ) { |
| 257 | const iconPath = join( |
| 258 | this.graph.extensionContext.extensionPath, |
| 259 | "images", |
| 260 | "hats", |
| 261 | `${shape}.svg` |
| 262 | ); |
| 263 | const rawSvg = readFileSync(iconPath, "utf8"); |
| 264 | const { characterWidth, characterHeight, fontSize } = fontMeasurements; |
| 265 | |
| 266 | const { originalViewBoxHeight, originalViewBoxWidth } = |
| 267 | this.getViewBoxDimensions(rawSvg); |
| 268 | |
| 269 | const defaultHatHeightPx = DEFAULT_HAT_HEIGHT_EM * fontSize; |
| 270 | const defaultHatWidthPx = |
| 271 | (originalViewBoxWidth / originalViewBoxHeight) * defaultHatHeightPx; |
| 272 | |
| 273 | const hatHeightPx = defaultHatHeightPx * scaleFactor; |
| 274 | const hatWidthPx = defaultHatWidthPx * scaleFactor; |
| 275 | |
| 276 | const hatVerticalOffsetPx = |
| 277 | (DEFAULT_VERTICAL_OFFSET_EM + hatVerticalOffsetEm) * fontSize - |
| 278 | hatHeightPx / 2; |
| 279 | |
| 280 | const svgWidthPx = Math.ceil(characterWidth); |
| 281 | const svgHeightPx = characterHeight + hatHeightPx + hatVerticalOffsetPx; |
| 282 | |
| 283 | const newViewBoxWidth = originalViewBoxWidth * (svgWidthPx / hatWidthPx); |
| 284 | const newViewBoxHeight = newViewBoxWidth * (svgHeightPx / svgWidthPx); |
| 285 | const newViewBoxX = |
| 286 | (-(characterWidth - hatWidthPx) * (newViewBoxWidth / svgWidthPx)) / 2; |
| 287 | const newViewBoxY = 0; |
| 288 | |
| 289 | const newViewBoxString = `${newViewBoxX} ${newViewBoxY} ${newViewBoxWidth} ${newViewBoxHeight}`; |
| 290 | |
| 291 | if ( |
| 292 | rawSvg.match(/width="[^"]+"/) == null || |
| 293 | rawSvg.match(/height="[^"]+"/) == null |
| 294 | ) { |
| 295 | throw Error("Raw svg doesn't have height or width"); |
| 296 | } |
| 297 | |
| 298 | const svg = rawSvg |
| 299 | .replace(/width="[^"]+"/, `width="${svgWidthPx}px"`) |
| 300 | .replace(/height="[^"]+"/, `height="${svgHeightPx}px"`) |
| 301 | .replace(/viewBox="([^"]+)"/, `viewBox="${newViewBoxString}"`); |
| 302 | |
| 303 | return { |
| 304 | svg, |
| 305 | svgHeightPx, |
| 306 | svgWidthPx, |
| 307 | }; |
| 308 | } |
no test coverage detected