Skip to content

Commit

Permalink
Merge pull request #29 from omacranger/feat/fa-620-support
Browse files Browse the repository at this point in the history
FontAwesome 6.2.0+ Support
  • Loading branch information
omacranger authored Sep 3, 2022
2 parents a2d5aa9 + 97b39d8 commit a606a8f
Show file tree
Hide file tree
Showing 12 changed files with 1,883 additions and 1,156 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.3.0](https://github.com/omacranger/fontawesome-subset/compare/4.2.0...4.3.0)

- Support FontAwesome 6.2.0+ including new `sharp-solid` subset
- Add ability to locate icons based on their unicode value (for those using them in CSS), or any other aliases. Thanks to #23 for starting this effort

## [4.2.0](https://github.com/omacranger/fontawesome-subset/compare/4.1.0...4.2.0)

- Re-add support for `woff` file formats, and allow customizing exported fonts via `targetFormats` option.
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fontawesomeSubset(["check", "square", "caret-up"], "sass/webfonts");

#### fontawesomeSubset(subset, output_dir, options)

- `subset` - Array containing list of glyph names (icon names) that you want to generate for the `solid` style. This can also be an object with key->value pairs for different FA styles (solid, regular¹, brands, light¹, duotone¹). Some Icons in these **¹** subsets are only available when used with FontAwesome Pro (see [below](#using-with-fontawesome-pro)).
- `subset` - Array containing list of icon identifiers (icon or glyph names, unicode value, or a supported alias) that you want to generate for the `solid` style. This can also be an object with key->value pairs for different FA styles (solid, regular¹, brands, light¹, duotone¹, sharp-solid¹). Some Icons in these **¹** subsets are only available when used with FontAwesome Pro (see [below](#using-with-fontawesome-pro)).
- `output_dir` - Directory that you want the webfonts to be generated in. Relative to current NPM process. Ex: `sass/webfonts`
- `options` - Object of options to further customize the tool.
- `package` - `free` or `pro` . Defaults to `free` version. See [below](#using-with-fontawesome-pro) for Pro instructions.
Expand All @@ -49,7 +49,13 @@ After installation, you can supply additional information to the `subset` parame
```javascript
fontawesomeSubset(
{
regular: ["check", "square", "caret-up"],
regular: [
"check",
"square",
"caret-up",
"f007" /* fa-user unicode */,
"add" /* fa-plus alias */,
],
solid: ["plus", "minus"],
},
"sass/webfonts",
Expand All @@ -59,7 +65,7 @@ fontawesomeSubset(
);
```

You can use any of the weights / sets provided by FontAwesome Pro including `solid`, `regular`, `light`, `brands`, or `duotone`. You can mix and match and provide as many glyphs as you plan on using to trim it down.
You can use any of the weights / sets provided by FontAwesome Pro including `solid`, `regular`, `light`, `brands`, `duotone`, or `sharp-solid`. You can mix and match and provide as many glyphs as you plan on using to trim it down.

The above example would output a directory with the following structure:

Expand Down
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
// Bump test timeout since parsing large icon metadata files can take a bit.
testTimeout: 20000,
};
2,401 changes: 1,352 additions & 1,049 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fontawesome-subset",
"version": "4.2.0",
"version": "4.3.0",
"description": "Utility to create subsets for FontAwesome and FontAwesome Pro.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -45,8 +45,8 @@
"optimize"
],
"peerDependencies": {
"@fortawesome/fontawesome-free": ">5.12.0",
"@fortawesome/fontawesome-pro": ">5.12.0"
"@fortawesome/fontawesome-free": ">=5.12.0",
"@fortawesome/fontawesome-pro": ">=5.12.0"
},
"peerDependenciesMeta": {
"@fortawesome/fontawesome-free": {
Expand All @@ -57,16 +57,16 @@
}
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"@fortawesome/fontawesome-pro": "^6.1.1",
"@fortawesome/fontawesome-free": "^6.2.0",
"@types/jest": "^28.1.4",
"@types/mkdirp": "^1.0.2",
"@types/node": "^16.9.1",
"@types/opentype.js": "^1.3.3",
"@types/subset-font": "^1.4.0",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "^5.30.0",
"eslint": "^8.18.0",
"compare-versions": "^5.0.1",
"eslint": "8.22.0",
"husky": "^8.0.1",
"jest": "^28.1.2",
"opentype.js": "^1.3.4",
Expand Down
38 changes: 38 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { FAFamilyType, FAStyleType, Subset, TargetFormat } from "./types";

// Maps style to actual font name / file name.
export const STYLE_FONT_MAP: Record<Subset, string> = {
solid: "fa-solid-900",
light: "fa-light-300",
regular: "fa-regular-400",
thin: "fa-thin-100",
brands: "fa-brands-400",
duotone: "fa-duotone-900",
"sharp-solid": "fa-sharp-solid-900",
};

export const OUTPUT_FORMAT_MAP: Record<TargetFormat, string> = {
sfnt: "ttf",
woff: "woff",
woff2: "woff2",
};

export const SUBSET_FAMILY_MAP: Record<Subset, FAFamilyType> = {
"sharp-solid": "sharp",
brands: "classic",
duotone: "duotone",
light: "classic",
regular: "classic",
solid: "classic",
thin: "classic",
};

export const SUBSET_STYLE_MAP: Record<Subset, FAStyleType> = {
"sharp-solid": "solid",
brands: "brands",
duotone: "solid",
light: "light",
regular: "regular",
solid: "solid",
thin: "thin",
};
69 changes: 41 additions & 28 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
import { existsSync, readFileSync, writeFileSync } from "fs";
import { resolve } from "path";
import { sync as makeDirSync } from "mkdirp";
import { FontAwesomeOptions, IconYAML, Subset, SubsetOption, TargetFormat } from "./types";
import {
FAFamilyMetaType,
FAIconType,
FontAwesomeOptions,
IconFamilyYAML,
IconYAML,
Subset,
SubsetOption,
} from "./types";
import subsetFont from "subset-font";
import yaml from "yaml";
import { addIconError, findIconByName } from "./utils";

const OUTPUT_FORMAT_MAP: Record<TargetFormat, string> = {
sfnt: "ttf",
woff: "woff",
woff2: "woff2",
};
import { addIconError, findIconByName, iconHasStyle } from "./utils";
import { OUTPUT_FORMAT_MAP, STYLE_FONT_MAP } from "./constants";

/**
* This function will take an object of glyph names and output a subset of the standard fonts optimized in size for
Expand All @@ -31,15 +34,7 @@ function fontawesomeSubset(
) {
const { package: packageType = "free", targetFormats = ["woff2", "sfnt"] } = options;
// Maps style to actual font name / file name.
const fontMap: Record<Subset, string> = {
solid: "fa-solid-900",
light: "fa-light-300",
regular: "fa-regular-400",
thin: "fa-thin-100",
brands: "fa-brands-400",
duotone: "fa-duotone-900",
};
const fontTypes = Object.keys(fontMap);
const fontTypes = Object.keys(STYLE_FONT_MAP);
let packageLocation: string;

// Check to see if the user has either free, or pro installed.
Expand All @@ -60,14 +55,18 @@ function fontawesomeSubset(
}

const fontMeta = resolve(packageLocation, "../../metadata/icons.yml");
const fontFamilyMeta = resolve(packageLocation, "../../metadata/icon-families.yml");
const fontFiles = resolve(packageLocation, "../../webfonts");

// If 'subset' is set to array, turn into object defaulted for 'solid' use (fontawesome free)
if (Array.isArray(subset)) {
subset = { solid: subset };
}

const iconMeta: IconYAML = yaml.parse(readFileSync(fontMeta, "utf8"));
const iconMeta = yaml.parse(readFileSync(fontMeta, "utf8")) as IconYAML;
const iconFamilyMeta = existsSync(fontFamilyMeta)
? (yaml.parse(readFileSync(fontFamilyMeta, "utf8")) as IconFamilyYAML)
: null;
const entries = Object.entries(subset);

const promises: Promise<unknown>[] = [];
Expand All @@ -84,8 +83,8 @@ function fontawesomeSubset(
continue;
}

const fontFamily = key as keyof typeof fontMap;
const fontFileName = fontMap[fontFamily];
const fontFamily = key as keyof typeof STYLE_FONT_MAP;
const fontFileName = STYLE_FONT_MAP[fontFamily];
const fontFilePath = resolve(fontFiles, `./${fontFileName}.ttf`);

if (!existsSync(resolve(fontFilePath))) {
Expand All @@ -99,19 +98,33 @@ function fontawesomeSubset(
// Pull unicode characters from fontawesome yml, aggregating into array
const unicodeCharacters: string[] = [];
for (const icon of icons) {
const foundIcon = findIconByName(iconMeta, icon);
let foundIcon: FAIconType | FAFamilyMetaType | undefined;

if (iconFamilyMeta) {
foundIcon = findIconByName(iconFamilyMeta, icon);

if (!foundIcon || !foundIcon.styles.includes(fontFamily)) {
addIconError(iconErrors, fontFamily, icon);
// Skip if the icon isn't found in our icon yaml
if (!foundIcon || !iconHasStyle(foundIcon, fontFamily, packageType)) {
addIconError(iconErrors, fontFamily, icon);
continue;
}
} else {
const charCode = foundIcon.unicode;
unicodeCharacters.push(String.fromCodePoint(parseInt(charCode, 16)));
foundIcon = findIconByName(iconMeta, icon);

// Duotone secondary char codes are prefixed with a `10` for the secondary color
if (fontFamily === "duotone") {
unicodeCharacters.push(String.fromCodePoint(parseInt(`10${charCode}`, 16)));
// Skip if the icon isn't found in our icon yaml
if (!foundIcon || !foundIcon.styles.includes(fontFamily)) {
addIconError(iconErrors, fontFamily, icon);
continue;
}
}

const charCode = foundIcon.unicode;
unicodeCharacters.push(String.fromCodePoint(parseInt(charCode, 16)));

// Duotone secondary char codes are prefixed with a `10` for the secondary color
if (fontFamily === "duotone") {
unicodeCharacters.push(String.fromCodePoint(parseInt(`10${charCode}`, 16)));
}
}

makeDirSync(resolve(outputDir));
Expand Down
52 changes: 46 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ export interface FontAwesomeOptions {
/**
* The FontAwesome package type we should use. Defaults to 'free'.
*/
package?: "free" | "pro";
package?: PackageType;
/**
* Requested font output targets.
*/
targetFormats?: TargetFormat[];
}

export type Subset = "solid" | "light" | "regular" | "thin" | "brands" | "duotone";
export type PackageType = "free" | "pro";

export type Subset = "solid" | "light" | "regular" | "thin" | "brands" | "duotone" | "sharp-solid";

export type GlyphName = string;

Expand All @@ -33,14 +35,52 @@ export interface FAIconType {
* Subsets this icon is available in.
*/
styles: Partial<Subset>[];

/** Alternative names of the icon. */
/*
* Alternative means of identifying the icon.
*/
aliases?: {
names?: string[]
}
names?: string[];
unicodes?: {
composite?: string[];
primary?: string[];
secondary?: string[];
};
};
}

export type FAFamilyType = "classic" | "duotone" | "sharp";
export type FAStyleType = "solid" | "thin" | "light" | "regular" | "brands";

/**
* Type of individual result / item inside the YAML file.
*/
export interface FAFamilyMetaType {
/**
* Label / display name of the icon.
*/
label: string;
/**
* Unicode character for the icon.
*/
unicode: string;
/**
* Family styles available for each icon & family type.
*/
familyStylesByLicense: Record<
PackageType,
{
family: FAFamilyType;
style: FAStyleType;
}[]
>;
}

/**
* Type of the YAML files bundled with FontAwesome.
*/
export type IconYAML = Record<GlyphName, FAIconType | undefined>;

/**
* Type of the YAML files bundled with FontAwesome.
*/
export type IconFamilyYAML = Record<GlyphName, FAFamilyMetaType | undefined>;
Loading

0 comments on commit a606a8f

Please sign in to comment.