Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metrics, unpack: Calculate and expose the xWidthAvg metric #122

Merged
merged 11 commits into from
Jan 30, 2023
5 changes: 5 additions & 0 deletions .changeset/curvy-drinks-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@capsizecss/metrics': minor
---

Update Google Fonts
7 changes: 7 additions & 0 deletions .changeset/kind-impalas-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@capsizecss/metrics': minor
'@capsizecss/unpack': minor
---

Calculate and expose `xWidthAvg`, the average width of lowercase characters.

7 changes: 7 additions & 0 deletions .changeset/loud-rules-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@capsizecss/metrics': minor
---

Add `category` field to describe the style of the font, e.g. “serif”, “sans-serif” etc.

Exposes the `category` field captured by Google Fonts, manually populating it for system fonts.
11 changes: 11 additions & 0 deletions .changeset/pretty-beds-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@capsizecss/core': patch
---

Add additional properties to `FontMetrics` type definition.

Previously the `FontMetrics` type captured only the metrics required by the options for the `createStyleObject` and `createStyleString` APIs.
With additional APIs coming that require a different subset of metrics, the type now reflects the structure of the data from the `metrics` package.

The additional properties are: `familyName`, `category`, `xHeight` and `xWidthAvg`.
See documentation for more details.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ npm install @capsizecss/core
- [Core](#core)
- [precomputeValues](#precomputevalues)
- [getCapHeight](#getcapheight)
- [Metrics](#metrics)
- [Unpack](#unpack)
- [Integrations](#integrations)
- [vanilla-extract](packages/vanilla-extract/README.md)

Expand Down Expand Up @@ -204,6 +206,26 @@ const actualCapHeight = getCapHeight({
// => number
```

## Metrics

To make the retrieval of font metrics easy, Capsize provides the `@capsizecss/metrics` package containing all the required data for both system and Google fonts.

```bash
npm install @capsizecss/metrics
```

See the [package](packages/metrics/README.md) for documentation.

## Unpack

If you are using a custom font or one not included in the `@capsizecss/metrics` package, Capsize provides the `@capsizecss/unpack` package to extract the required data either via a URL or from a local file.

```bash
npm install @capsizecss/unpack
```

See the [package](packages/unpack/README.md) for documentation.

## Integrations

- [vanilla-extract](https://vanilla-extract.style) integration via [@capsizecss/vanilla-extract](packages/vanilla-extract/README.md)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"site:serve": "manypkg run site serve",
"site:deploy": "manypkg run site deploy",
"site:deploy-preview": "pnpm metrics:build && manypkg run site deploy-preview",
"metrics:build-system": "manypkg run @capsizecss/metrics extract-system-metrics",
"metrics:build": "manypkg run @capsizecss/metrics build",
"metrics:clean": "manypkg run @capsizecss/metrics clean",
"metrics:download": "manypkg run @capsizecss/metrics download",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/getCapHeight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export const getCapHeight = ({
fontMetrics,
}: {
fontSize: number;
fontMetrics: FontMetrics;
fontMetrics: Pick<FontMetrics, 'capHeight' | 'unitsPerEm'>;
}) => round((fontSize * fontMetrics.capHeight) / fontMetrics.unitsPerEm);
30 changes: 26 additions & 4 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
export interface FontMetrics {
/** The font family name as authored by font creator */
familyName: string;
/**
* The style of the font: serif, sans-serif, monospace, display, or handwriting.
*
* (Optional as only availble for metrics from the `@capsizecss/metrics` package. Value not extractable from font data tables.)
*/
category?: string;
/** The height of the ascenders above baseline */
ascent: number;
/** The descent of the descenders below baseline */
descent: number;
/** The amount of space included between lines */
lineGap: number;
/** The size of the font’s internal coordinate grid */
unitsPerEm: number;
/** The height of capital letters above the baseline */
capHeight: number;
/** The height of the main body of lower case letters above baseline */
xHeight: number;
/** The average width of lowercase characters (currently derived from latin character frequencies in English language) */
xWidthAvg: number;
}

export type ComputedValues = {
Expand All @@ -17,28 +34,33 @@ type NotComputedValues = {
[V in keyof ComputedValues]?: never;
};

type FontMetricsForTrim = Pick<
FontMetrics,
'ascent' | 'descent' | 'capHeight' | 'lineGap' | 'unitsPerEm'
>;

type CapHeightWithLeading = {
capHeight: number;
leading?: number;
fontMetrics: FontMetrics;
fontMetrics: FontMetricsForTrim;
} & NotComputedValues;

type CapHeightWithLineGap = {
capHeight: number;
lineGap: number;
fontMetrics: FontMetrics;
fontMetrics: FontMetricsForTrim;
} & NotComputedValues;

type FontSizeWithLeading = {
fontSize: number;
leading?: number;
fontMetrics: FontMetrics;
fontMetrics: FontMetricsForTrim;
} & Omit<NotComputedValues, 'fontSize'>;

type FontSizeWithLineGap = {
fontSize: number;
lineGap: number;
fontMetrics: FontMetrics;
fontMetrics: FontMetricsForTrim;
} & Omit<NotComputedValues, 'fontSize'>;

export type CapsizeOptions =
Expand Down
23 changes: 14 additions & 9 deletions packages/metrics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ const capsizeStyles = createStyleObject({

The font metrics object returned contains the following properties if available:

| Property | Type | Description |
| ---------- | ------ | --------------------------------------------------- |
| familyName | string | Font family name as authored by font creator |
| capHeight | number | The height of capital letters above the baseline |
| ascent | number | The height of the ascenders above baseline |
| descent | number | The descent of the descenders below baseline |
| lineGap | number | The amount of space included between lines |
| unitsPerEm | number | The size of the font’s internal coordinate grid |
| xHeight | number | The height of lower case letters above the baseline |
| Property | Type | Description |
| ---------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| familyName | string | The font family name as authored by font creator |
| category | string | The style of the font: serif, sans-serif, monospace, display, or handwriting. |
| capHeight | number | The height of capital letters above the baseline |
| ascent | number | The height of the ascenders above baseline |
| descent | number | The descent of the descenders below baseline |
| lineGap | number | The amount of space included between lines |
| unitsPerEm | number | The size of the font’s internal coordinate grid |
| xHeight | number | The height of the main body of lower case letters above baseline |
| xWidthAvg | number | The average width of lowercase characters.<br/><br/>Currently derived from latin [character frequencies] in English language, falling back to the built in [xAvgCharWidth] from the OS/2 table. |

[character frequencies]: https://en.wikipedia.org/wiki/Letter_frequency#Relative_frequencies_of_letters_in_other_languages
[xavgcharwidth]: https://learn.microsoft.com/en-us/typography/opentype/spec/os2#xavgcharwidth

## Thanks

Expand Down
45 changes: 38 additions & 7 deletions packages/metrics/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,39 @@ const toCamelCase = (str: string) =>
.join('');

const writeFile = async (fileName: string, content: string) =>
await fs.writeFile(path.join(__dirname, '..', fileName), content, 'utf-8');
await fs.writeFile(path.join(__dirname, fileName), content, 'utf-8');

interface MetricsFont extends Font {
category: string;
}

const buildFiles = async ({
familyName,
category,
capHeight,
ascent,
descent,
lineGap,
unitsPerEm,
xHeight,
}: Font) => {
xWidthAvg,
}: MetricsFont) => {
const fileName = toCamelCase(familyName);

await writeFile(
`${fileName}.js`,
path.join('..', `${fileName}.js`),
`module.exports = ${JSON.stringify(
{ familyName, capHeight, ascent, descent, lineGap, unitsPerEm, xHeight },
{
familyName,
category,
capHeight,
ascent,
descent,
lineGap,
unitsPerEm,
xHeight,
xWidthAvg,
},
null,
2,
)
Expand All @@ -48,11 +64,12 @@ const buildFiles = async ({
)}Metrics`;

await writeFile(
`${fileName}.d.ts`,
path.join('..', `${fileName}.d.ts`),
dedent`
declare module '@capsizecss/metrics/${fileName}' {
interface ${typeName} {
familyName: string;${
familyName: string;
category: string;${
typeof capHeight === 'number' && capHeight > 0
? `
capHeight: number;`
Expand Down Expand Up @@ -82,6 +99,11 @@ const buildFiles = async ({
? `
xHeight: number;`
: ''
}${
typeof xWidthAvg === 'number' && xWidthAvg > 0
? `
xWidthAvg: number;`
: ''
}
}
export const fontMetrics: ${typeName};
Expand Down Expand Up @@ -109,6 +131,8 @@ const buildFiles = async ({
progress.increment();
});

const metricsForAnalysis: MetricsFont[] = [];

await queue.addAll(systemMetrics.map((m) => async () => await buildFiles(m)));
await queue.addAll(
googleFonts.items.map((font: typeof googleFonts.items[number]) => {
Expand All @@ -119,11 +143,18 @@ const buildFiles = async ({

return async () => {
const m = await fromUrl(fontUrl as string);
await buildFiles(m);
const categorisedMetrics = { ...m, category: font.category };
metricsForAnalysis.push(categorisedMetrics);
await buildFiles(categorisedMetrics);
};
}),
);

await writeFile(
'googleFonts.json',
`${JSON.stringify(metricsForAnalysis, null, 2)}\n`,
);

progress.stop();

console.log('✅ Complete');
Expand Down
28 changes: 19 additions & 9 deletions packages/metrics/scripts/extractSystemFontMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,25 @@ import { fromFile } from '@capsizecss/unpack';

const content = JSON.stringify(
[
arial,
{ ...sfPro, familyName: '-apple-system', descent: -420 },
{ ...sfPro, familyName: 'BlinkMacSystemFont', descent: -420 },
roboto,
segoeui,
{ ...oxygen, capHeight: 1468, xHeight: 1085 },
helvetica,
helveticaNeue,
timesNewRoman,
{ ...arial, category: 'sans-serif' },
{
...sfPro,
familyName: '-apple-system',
descent: -420,
category: 'sans-serif',
},
{
...sfPro,
familyName: 'BlinkMacSystemFont',
descent: -420,
category: 'sans-serif',
},
{ ...roboto, category: 'sans-serif' },
{ ...segoeui, category: 'sans-serif' },
{ ...oxygen, capHeight: 1468, xHeight: 1085, category: 'sans-serif' },
{ ...helvetica, category: 'sans-serif' },
{ ...helveticaNeue, category: 'sans-serif' },
{ ...timesNewRoman, category: 'serif' },
],
null,
2,
Expand Down