mirror of
https://github.com/enso-org/enso.git
synced 2024-11-21 16:36:59 +03:00
Remove download font scripts (#11317)
The fonts were added (by mistake) anyway, but they are rarely changed, and the scripts add unnecessary complexity. This also fixes some post- #11287 problems.
This commit is contained in:
parent
244effde0c
commit
d1ee7fadce
@ -1,5 +1,6 @@
|
|||||||
# Build Artefacts
|
# Build Artefacts
|
||||||
dist
|
dist
|
||||||
|
mockDist
|
||||||
target
|
target
|
||||||
|
|
||||||
# Release body template should not be auto-formatted, as the word wrap does not really looks good in the GitHub UI.
|
# Release body template should not be auto-formatted, as the word wrap does not really looks good in the GitHub UI.
|
||||||
|
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -1211,8 +1211,6 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"enso-build-base",
|
"enso-build-base",
|
||||||
"enso-build-macros-lib",
|
"enso-build-macros-lib",
|
||||||
"enso-enso-font",
|
|
||||||
"enso-font",
|
|
||||||
"enso-install-config",
|
"enso-install-config",
|
||||||
"futures",
|
"futures",
|
||||||
"glob",
|
"glob",
|
||||||
@ -1313,23 +1311,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enso-enso-font"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"enso-font",
|
|
||||||
"ide-ci",
|
|
||||||
"owned_ttf_parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enso-font"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"derive_more",
|
|
||||||
"owned_ttf_parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enso-formatter"
|
name = "enso-formatter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -2858,15 +2839,6 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "owned_ttf_parser"
|
|
||||||
version = "0.15.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb"
|
|
||||||
dependencies = [
|
|
||||||
"ttf-parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@ -4349,12 +4321,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ttf-parser"
|
|
||||||
version = "0.15.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tuikit"
|
name = "tuikit"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
6
app/gui/.gitignore
vendored
6
app/gui/.gitignore
vendored
@ -26,11 +26,5 @@ mockDist
|
|||||||
test-results/
|
test-results/
|
||||||
playwright-report/
|
playwright-report/
|
||||||
|
|
||||||
public/font-dejavu/
|
|
||||||
public/font-enso/
|
|
||||||
public/font-mplus1/
|
|
||||||
src/project-view/assets/font-dejavu.css
|
|
||||||
src/project-view/assets/font-enso.css
|
|
||||||
src/project-view/assets/font-mplus1.css
|
|
||||||
src/project-view/util/iconList.json
|
src/project-view/util/iconList.json
|
||||||
src/project-view/util/iconName.ts
|
src/project-view/util/iconName.ts
|
||||||
|
@ -29,11 +29,10 @@
|
|||||||
"test-dev:unit": "vitest",
|
"test-dev:unit": "vitest",
|
||||||
"test:e2e": "cross-env NODE_ENV=production playwright test",
|
"test:e2e": "cross-env NODE_ENV=production playwright test",
|
||||||
"test-dev:e2e": "cross-env NODE_ENV=production playwright test --ui",
|
"test-dev:e2e": "cross-env NODE_ENV=production playwright test --ui",
|
||||||
"preinstall": "corepack pnpm run generate-metadata && corepack pnpm run download-fonts",
|
"preinstall": "corepack pnpm run generate-metadata",
|
||||||
"postinstall": "playwright install",
|
"postinstall": "playwright install",
|
||||||
"build-rust-ffi": "wasm-pack build ./rust-ffi --release --target web && wasm-pack build ./rust-ffi --out-dir node-pkg --target nodejs",
|
"build-rust-ffi": "wasm-pack build ./rust-ffi --release --target web && wasm-pack build ./rust-ffi --out-dir node-pkg --target nodejs",
|
||||||
"generate-metadata": "node scripts/generateIconMetadata.js",
|
"generate-metadata": "node scripts/generateIconMetadata.js"
|
||||||
"download-fonts": "node scripts/downloadFonts.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-amplify/auth": "5.6.5",
|
"@aws-amplify/auth": "5.6.5",
|
||||||
@ -166,7 +165,6 @@
|
|||||||
"@types/mapbox-gl": "^2.7.13",
|
"@types/mapbox-gl": "^2.7.13",
|
||||||
"@types/shuffle-seed": "^1.1.0",
|
"@types/shuffle-seed": "^1.1.0",
|
||||||
"@types/tar": "^6.1.4",
|
"@types/tar": "^6.1.4",
|
||||||
"@types/unbzip2-stream": "^1.4.3",
|
|
||||||
"@types/wicg-file-system-access": "^2023.10.2",
|
"@types/wicg-file-system-access": "^2023.10.2",
|
||||||
"@types/ws": "^8.5.5",
|
"@types/ws": "^8.5.5",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
@ -188,7 +186,6 @@
|
|||||||
"sql-formatter": "^13.0.0",
|
"sql-formatter": "^13.0.0",
|
||||||
"tar": "^6.2.1",
|
"tar": "^6.2.1",
|
||||||
"tsx": "^4.7.1",
|
"tsx": "^4.7.1",
|
||||||
"unbzip2-stream": "^1.4.3",
|
|
||||||
"vite-plugin-vue-devtools": "7.3.7",
|
"vite-plugin-vue-devtools": "7.3.7",
|
||||||
"vite-plugin-wasm": "^3.3.0",
|
"vite-plugin-wasm": "^3.3.0",
|
||||||
"vue-react-wrapper": "^0.3.1",
|
"vue-react-wrapper": "^0.3.1",
|
||||||
|
Binary file not shown.
@ -1,217 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file ⚠️⚠️⚠️ THIS SCRIPT IS PROVIDED ONLY FOR CONVENIENCE. ⚠️⚠️⚠️
|
|
||||||
* The sources of truth are at `build/build/src/project/gui.rs` and
|
|
||||||
* `build/build/src/ide/web/fonts.rs`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fsSync from 'node:fs'
|
|
||||||
import * as fs from 'node:fs/promises'
|
|
||||||
import * as http from 'node:http'
|
|
||||||
import * as https from 'node:https'
|
|
||||||
import * as process from 'node:process'
|
|
||||||
import tar from 'tar'
|
|
||||||
import bz2 from 'unbzip2-stream'
|
|
||||||
|
|
||||||
if (process.env.CI === '1') process.exit(0)
|
|
||||||
|
|
||||||
const WARNING_MESSAGE =
|
|
||||||
'⚠️⚠️⚠️ Please use the buildscript (`./run`) to download fonts instead. ⚠️⚠️⚠️'
|
|
||||||
let warningMessageAlreadyShown = false
|
|
||||||
let exitCode = 0
|
|
||||||
|
|
||||||
const ENSO_FONT_URL = 'https://github.com/enso-org/font/releases/download/1.0/enso-font-1.0.tar.gz'
|
|
||||||
const MPLUS1_FONT_URL =
|
|
||||||
'https://github.com/coz-m/MPLUS_FONTS/raw/71d438c798d063cc6fdae8d2864bc48f2d3d06ad/fonts/ttf/MPLUS1%5Bwght%5D.ttf'
|
|
||||||
const DEJAVU_SANS_MONO_FONT_URL =
|
|
||||||
'https://sourceforge.net/projects/dejavu/files/dejavu/2.37/dejavu-fonts-ttf-2.37.tar.bz2'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string | https.RequestOptions | URL} options
|
|
||||||
* @param {((res: import('node:http').IncomingMessage) => void) | undefined} [callback]
|
|
||||||
*/
|
|
||||||
function get(options, callback) {
|
|
||||||
const protocol =
|
|
||||||
typeof options === 'string' ? new URL(options).protocol : options.protocol ?? 'https:'
|
|
||||||
/** @type {{ get: typeof http['get'] }} */
|
|
||||||
let httpModule = https
|
|
||||||
switch (protocol) {
|
|
||||||
case 'http:': {
|
|
||||||
httpModule = http
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return httpModule.get(options, (response) => {
|
|
||||||
const location = response.headers.location
|
|
||||||
if (location) {
|
|
||||||
get(
|
|
||||||
typeof options === 'string' || options instanceof URL ?
|
|
||||||
location
|
|
||||||
: { ...options, ...new URL(location) },
|
|
||||||
callback,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
callback?.(response)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {unknown} error */
|
|
||||||
function errorCode(error) {
|
|
||||||
return (
|
|
||||||
typeof error === 'object' &&
|
|
||||||
error != null &&
|
|
||||||
'code' in error &&
|
|
||||||
typeof error.code === 'string'
|
|
||||||
) ?
|
|
||||||
error.code
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {unknown} error */
|
|
||||||
function isFileNotFoundError(error) {
|
|
||||||
return errorCode(error) === 'ENOENT'
|
|
||||||
}
|
|
||||||
|
|
||||||
const ENSO_FONT_VARIANTS = [
|
|
||||||
{ variant: 'Thin', weight: 100 },
|
|
||||||
{ variant: 'ExtraLight', weight: 200 },
|
|
||||||
{ variant: 'Light', weight: 300 },
|
|
||||||
{ variant: 'Regular', weight: 400 },
|
|
||||||
{ variant: 'Medium', weight: 500 },
|
|
||||||
{ variant: 'SemiBold', weight: 600 },
|
|
||||||
{ variant: 'Bold', weight: 700 },
|
|
||||||
{ variant: 'ExtraBold', weight: 800 },
|
|
||||||
{ variant: 'Black', weight: 900 },
|
|
||||||
].map((variant) => ({ font: 'Enso', ...variant }))
|
|
||||||
|
|
||||||
const DEJAVU_FONT_VARIANTS = [
|
|
||||||
{ variant: 'DejaVuSansMono', weight: 400 },
|
|
||||||
{ variant: 'DejaVuSansMono-Bold', weight: 700 },
|
|
||||||
].map((variant) => ({ font: 'DejaVu Sans Mono', ...variant }))
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.access(`./src/project-view/assets/font-enso.css`)
|
|
||||||
for (const { variant } of ENSO_FONT_VARIANTS) {
|
|
||||||
await fs.access(`./public/font-enso/Enso-${variant}.ttf`)
|
|
||||||
}
|
|
||||||
console.info('Enso font already downloaded, skipping...')
|
|
||||||
} catch (error) {
|
|
||||||
if (!isFileNotFoundError(error)) {
|
|
||||||
console.error('Unexpected error occurred when checking for Enso font:')
|
|
||||||
console.error(error)
|
|
||||||
exitCode = 1
|
|
||||||
} else {
|
|
||||||
if (!warningMessageAlreadyShown) console.warn(WARNING_MESSAGE)
|
|
||||||
warningMessageAlreadyShown = true
|
|
||||||
console.info('Downloading Enso font...')
|
|
||||||
await fs.rm('./public/font-enso/', { recursive: true, force: true })
|
|
||||||
await fs.mkdir('./public/font-enso/', { recursive: true })
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
get(ENSO_FONT_URL, (response) => {
|
|
||||||
response.pipe(
|
|
||||||
tar.extract({
|
|
||||||
cwd: './public/font-enso/',
|
|
||||||
strip: 1,
|
|
||||||
filter(path) {
|
|
||||||
// Reject files starting with `.`.
|
|
||||||
return !/[\\/][.]/.test(path)
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
response.on('end', resolve)
|
|
||||||
response.on('error', reject)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
/** @type {string[]} */
|
|
||||||
let css = []
|
|
||||||
for (const { font, variant, weight } of ENSO_FONT_VARIANTS) {
|
|
||||||
css.push(`\
|
|
||||||
@font-face {
|
|
||||||
font-family: '${font}';
|
|
||||||
src: url('/font-enso/Enso-${variant}.ttf');
|
|
||||||
font-weight: ${weight};
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
await fs.writeFile('./src/project-view/assets/font-enso.css', css.join('\n'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await fs.access(`./src/project-view/assets/font-mplus1.css`)
|
|
||||||
await fs.access(`./public/font-mplus1/MPLUS1[wght].ttf`)
|
|
||||||
console.info('M PLUS 1 font already downloaded, skipping...')
|
|
||||||
} catch (error) {
|
|
||||||
if (!isFileNotFoundError(error)) {
|
|
||||||
console.error('Unexpected error occurred when checking for M PLUS 1 font:')
|
|
||||||
console.error(error)
|
|
||||||
exitCode = 1
|
|
||||||
} else {
|
|
||||||
if (!warningMessageAlreadyShown) console.warn(WARNING_MESSAGE)
|
|
||||||
warningMessageAlreadyShown = true
|
|
||||||
console.info('Downloading M PLUS 1 font...')
|
|
||||||
await fs.rm('./public/font-mplus1/', { recursive: true, force: true })
|
|
||||||
await fs.mkdir('./public/font-mplus1/', { recursive: true })
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
get(MPLUS1_FONT_URL, (response) => {
|
|
||||||
response.pipe(fsSync.createWriteStream('./public/font-mplus1/MPLUS1[wght].ttf'))
|
|
||||||
response.on('end', resolve)
|
|
||||||
response.on('error', reject)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const css = `\
|
|
||||||
@font-face {
|
|
||||||
font-family: 'M PLUS 1';
|
|
||||||
src: url('/font-mplus1/MPLUS1[wght].ttf');
|
|
||||||
}
|
|
||||||
`
|
|
||||||
await fs.writeFile('./src/project-view/assets/font-mplus1.css', css)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await fs.access(`./src/project-view/assets/font-dejavu.css`)
|
|
||||||
for (const variant of ['', '-Bold']) {
|
|
||||||
await fs.access(`./public/font-dejavu/DejaVuSansMono${variant}.ttf`)
|
|
||||||
}
|
|
||||||
console.info('DejaVu Sans Mono font already downloaded, skipping...')
|
|
||||||
} catch (error) {
|
|
||||||
if (!isFileNotFoundError(error)) {
|
|
||||||
console.error('Unexpected error occurred when checking for DejaVu Sans Mono font:')
|
|
||||||
console.error(error)
|
|
||||||
exitCode = 1
|
|
||||||
} else {
|
|
||||||
if (!warningMessageAlreadyShown) console.warn(WARNING_MESSAGE)
|
|
||||||
warningMessageAlreadyShown = true
|
|
||||||
console.info('Downloading DejaVu Sans Mono font...')
|
|
||||||
await fs.rm('./public/font-dejavu/', { recursive: true, force: true })
|
|
||||||
await fs.mkdir('./public/font-dejavu/', { recursive: true })
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
get(DEJAVU_SANS_MONO_FONT_URL, (response) => {
|
|
||||||
response.pipe(bz2()).pipe(
|
|
||||||
tar.extract({
|
|
||||||
cwd: './public/font-dejavu/',
|
|
||||||
strip: 2,
|
|
||||||
filter(path) {
|
|
||||||
return /[\\/]DejaVuSansMono/.test(path) && !/Oblique[.]ttf$/.test(path)
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
response.on('end', resolve)
|
|
||||||
response.on('error', reject)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
/** @type {string[]} */
|
|
||||||
let css = []
|
|
||||||
for (const { font, variant, weight } of DEJAVU_FONT_VARIANTS) {
|
|
||||||
css.push(`\
|
|
||||||
@font-face {
|
|
||||||
font-family: '${font}';
|
|
||||||
src: url('/font-dejavu/${variant}.ttf');
|
|
||||||
font-weight: ${weight};
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
await fs.writeFile('./src/project-view/assets/font-dejavu.css', css.join('\n'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.info('Done.')
|
|
||||||
process.exit(exitCode)
|
|
12
app/gui/src/project-view/assets/font-dejavu.css
Normal file
12
app/gui/src/project-view/assets/font-dejavu.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'DejaVu Sans Mono';
|
||||||
|
src: url('/font-dejavu/DejaVuSansMono.ttf');
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DejaVu Sans Mono';
|
||||||
|
src: url('/font-dejavu/DejaVuSansMono-Bold.ttf');
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
54
app/gui/src/project-view/assets/font-enso.css
Normal file
54
app/gui/src/project-view/assets/font-enso.css
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-Thin.ttf');
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-ExtraLight.ttf');
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-Light.ttf');
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-Regular.ttf');
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-Medium.ttf');
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-SemiBold.ttf');
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-Bold.ttf');
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-ExtraBold.ttf');
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Enso';
|
||||||
|
src: url('/font-enso/Enso-Black.ttf');
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
5
app/gui/src/project-view/assets/font-mplus1.css
Normal file
5
app/gui/src/project-view/assets/font-mplus1.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'M PLUS 1';
|
||||||
|
src: url('/font-mplus1/MPLUS1[wght].ttf');
|
||||||
|
}
|
||||||
|
|
@ -1,213 +0,0 @@
|
|||||||
[
|
|
||||||
"3_dot_menu",
|
|
||||||
"accessed_by_projects",
|
|
||||||
"accessed_data",
|
|
||||||
"add",
|
|
||||||
"add_to_graph_editor",
|
|
||||||
"array_new",
|
|
||||||
"array_new2",
|
|
||||||
"arrow_left",
|
|
||||||
"arrow_right",
|
|
||||||
"arrow_right_head_only",
|
|
||||||
"auto_replay",
|
|
||||||
"bell",
|
|
||||||
"bold",
|
|
||||||
"bookmark",
|
|
||||||
"bottom_panel",
|
|
||||||
"braces",
|
|
||||||
"brush",
|
|
||||||
"bullet-list",
|
|
||||||
"calendar",
|
|
||||||
"camera",
|
|
||||||
"chat",
|
|
||||||
"chat_gpt",
|
|
||||||
"chat_gpt2",
|
|
||||||
"chat_gpt_mod",
|
|
||||||
"chat_gpt_mod2",
|
|
||||||
"check",
|
|
||||||
"check-list",
|
|
||||||
"clone",
|
|
||||||
"close",
|
|
||||||
"cloud",
|
|
||||||
"cloud_from",
|
|
||||||
"code",
|
|
||||||
"collection",
|
|
||||||
"column_add",
|
|
||||||
"columns_increasing",
|
|
||||||
"command",
|
|
||||||
"command2",
|
|
||||||
"command3",
|
|
||||||
"command4",
|
|
||||||
"command5",
|
|
||||||
"comment",
|
|
||||||
"compass",
|
|
||||||
"compliance",
|
|
||||||
"compliance2",
|
|
||||||
"connector",
|
|
||||||
"connector_add",
|
|
||||||
"convert",
|
|
||||||
"copy",
|
|
||||||
"copy2",
|
|
||||||
"credit_card",
|
|
||||||
"data_download",
|
|
||||||
"data_input",
|
|
||||||
"data_output",
|
|
||||||
"data_upload",
|
|
||||||
"datetime",
|
|
||||||
"docs",
|
|
||||||
"drive",
|
|
||||||
"duplicate",
|
|
||||||
"edit",
|
|
||||||
"enso_logo",
|
|
||||||
"enter_node",
|
|
||||||
"error",
|
|
||||||
"exclamation",
|
|
||||||
"exit_fullscreen",
|
|
||||||
"expanded_node",
|
|
||||||
"eye",
|
|
||||||
"find",
|
|
||||||
"folder",
|
|
||||||
"folder_add",
|
|
||||||
"folder_closed",
|
|
||||||
"folder_opened",
|
|
||||||
"fullscreen",
|
|
||||||
"fullscreen2",
|
|
||||||
"fullscreen3",
|
|
||||||
"geo_map_distance",
|
|
||||||
"geo_map_pin",
|
|
||||||
"ghost",
|
|
||||||
"google",
|
|
||||||
"google_color",
|
|
||||||
"graph",
|
|
||||||
"graph2",
|
|
||||||
"graph_editor",
|
|
||||||
"graph_editor",
|
|
||||||
"group",
|
|
||||||
"header1",
|
|
||||||
"header2",
|
|
||||||
"header3",
|
|
||||||
"heart",
|
|
||||||
"heatmap",
|
|
||||||
"help",
|
|
||||||
"home",
|
|
||||||
"home2",
|
|
||||||
"icon/lock",
|
|
||||||
"icon/skip",
|
|
||||||
"image",
|
|
||||||
"in_out",
|
|
||||||
"in_out2",
|
|
||||||
"input_number",
|
|
||||||
"italic",
|
|
||||||
"join",
|
|
||||||
"join-1",
|
|
||||||
"join2",
|
|
||||||
"join2-1",
|
|
||||||
"key",
|
|
||||||
"key_cmd",
|
|
||||||
"key_ctrl",
|
|
||||||
"key_option",
|
|
||||||
"keyboard_shortcuts",
|
|
||||||
"lab",
|
|
||||||
"lab2",
|
|
||||||
"lab3",
|
|
||||||
"libraries",
|
|
||||||
"local_scope",
|
|
||||||
"local_scope2",
|
|
||||||
"local_scope3",
|
|
||||||
"local_scope4",
|
|
||||||
"location",
|
|
||||||
"location2",
|
|
||||||
"log",
|
|
||||||
"log_cloud",
|
|
||||||
"make_node_def_local_copy",
|
|
||||||
"map_row",
|
|
||||||
"marketplace",
|
|
||||||
"math",
|
|
||||||
"metadata",
|
|
||||||
"microphone",
|
|
||||||
"minus",
|
|
||||||
"mixed",
|
|
||||||
"no_auto_replay",
|
|
||||||
"not_cloud",
|
|
||||||
"not_exclamation",
|
|
||||||
"not_paragraph",
|
|
||||||
"numbered-list",
|
|
||||||
"open",
|
|
||||||
"open_count",
|
|
||||||
"operators",
|
|
||||||
"order",
|
|
||||||
"paint",
|
|
||||||
"paint_palette",
|
|
||||||
"panic",
|
|
||||||
"paragraph",
|
|
||||||
"parse",
|
|
||||||
"parse2",
|
|
||||||
"parse3",
|
|
||||||
"parse4",
|
|
||||||
"path2",
|
|
||||||
"people",
|
|
||||||
"people_settings",
|
|
||||||
"points",
|
|
||||||
"predict",
|
|
||||||
"preparation",
|
|
||||||
"quote",
|
|
||||||
"random",
|
|
||||||
"recent",
|
|
||||||
"record",
|
|
||||||
"record_once",
|
|
||||||
"redo",
|
|
||||||
"refresh",
|
|
||||||
"remove-textstyle",
|
|
||||||
"restore",
|
|
||||||
"right_panel",
|
|
||||||
"right_side_panel",
|
|
||||||
"robot",
|
|
||||||
"root",
|
|
||||||
"row_add",
|
|
||||||
"scissors",
|
|
||||||
"select",
|
|
||||||
"select_column",
|
|
||||||
"select_row",
|
|
||||||
"settings",
|
|
||||||
"settings1",
|
|
||||||
"settings2",
|
|
||||||
"shift_key",
|
|
||||||
"shortcut",
|
|
||||||
"shortcut2",
|
|
||||||
"shortcut3",
|
|
||||||
"show_all",
|
|
||||||
"sort",
|
|
||||||
"sort_ascending",
|
|
||||||
"sort_descending",
|
|
||||||
"split",
|
|
||||||
"star",
|
|
||||||
"strike-through",
|
|
||||||
"system",
|
|
||||||
"table",
|
|
||||||
"table_clean",
|
|
||||||
"table_edit",
|
|
||||||
"table_from_columns",
|
|
||||||
"table_from_rows",
|
|
||||||
"tag",
|
|
||||||
"temp",
|
|
||||||
"text",
|
|
||||||
"text2",
|
|
||||||
"text3",
|
|
||||||
"text_input",
|
|
||||||
"time",
|
|
||||||
"time2",
|
|
||||||
"transform",
|
|
||||||
"transform2",
|
|
||||||
"transform3",
|
|
||||||
"transform4",
|
|
||||||
"trash",
|
|
||||||
"trash2",
|
|
||||||
"undo",
|
|
||||||
"union",
|
|
||||||
"unstable",
|
|
||||||
"unstable2",
|
|
||||||
"vector_add",
|
|
||||||
"warning",
|
|
||||||
"workflow_play",
|
|
||||||
"zoom"
|
|
||||||
]
|
|
@ -21,8 +21,6 @@ futures = { workspace = true }
|
|||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
handlebars = "4.3.5"
|
handlebars = "4.3.5"
|
||||||
enso-build-base = { path = "../base" }
|
enso-build-base = { path = "../base" }
|
||||||
enso-enso-font = { path = "../../lib/rust/enso-font" }
|
|
||||||
enso-font = { path = "../../lib/rust/font" }
|
|
||||||
enso-install-config = { path = "../install/config" }
|
enso-install-config = { path = "../install/config" }
|
||||||
ide-ci = { path = "../ci_utils" }
|
ide-ci = { path = "../ci_utils" }
|
||||||
mime = { workspace = true }
|
mime = { workspace = true }
|
||||||
|
@ -21,13 +21,6 @@ use tempfile::TempDir;
|
|||||||
// === Export ===
|
// === Export ===
|
||||||
// ==============
|
// ==============
|
||||||
|
|
||||||
pub mod dejavu_font;
|
|
||||||
pub mod enso_font;
|
|
||||||
pub mod fonts;
|
|
||||||
pub mod google_font;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Path to the file with build information that is consumed by the JS part of the IDE.
|
/// Path to the file with build information that is consumed by the JS part of the IDE.
|
||||||
///
|
///
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
//! Definitions for DejaVu fonts, and functions for downloading and installing them.
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use enso_font as font;
|
|
||||||
use enso_font::NonVariableDefinition;
|
|
||||||
use enso_font::NonVariableFaceHeader;
|
|
||||||
use ide_ci::cache::Cache;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Constants ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
pub const PACKAGE_URL: &str = "https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-fonts-ttf-2.37.zip";
|
|
||||||
|
|
||||||
const FONT_FAMILY: &str = "DejaVu Sans Mono";
|
|
||||||
|
|
||||||
const FILE_PREFIX: &str = "DejaVu";
|
|
||||||
|
|
||||||
const FILE_SANS_MONO_PREFIX: &str = "SansMono";
|
|
||||||
|
|
||||||
const SANS_MONO_FONT_FAMILY_FONTS: &[(&str, font::Weight)] =
|
|
||||||
&[("-Bold", font::Weight::Bold), ("", font::Weight::Normal)];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===================
|
|
||||||
// === DejaVu Font ===
|
|
||||||
// ===================
|
|
||||||
|
|
||||||
/// Internal helper function to download the DejaVu Sans Mono font. Exposed via thin wrapper
|
|
||||||
/// functions.
|
|
||||||
async fn install_sans_mono_internal(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
css_output_info: Option<(&str, impl AsRef<Path>)>,
|
|
||||||
) -> Result {
|
|
||||||
let output_path = output_path.as_ref();
|
|
||||||
let font = font();
|
|
||||||
let faces = faces();
|
|
||||||
let font = crate::ide::web::fonts::filter_font(&font, &faces);
|
|
||||||
let package = download(cache, octocrab).await?;
|
|
||||||
let get_font_files = extract_fonts(&font, package, output_path);
|
|
||||||
let make_css_file = crate::ide::web::fonts::write_css_file_if_required(
|
|
||||||
FONT_FAMILY,
|
|
||||||
&font,
|
|
||||||
&faces,
|
|
||||||
css_output_info,
|
|
||||||
);
|
|
||||||
try_join!(get_font_files, make_css_file)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Install DejaVu Sans Mono, without an auto-generated CSS file.
|
|
||||||
pub async fn install_sans_mono(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
install_sans_mono_internal(cache, octocrab, output_path, None::<(&str, &str)>).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Install DejaVu Sans Mono, including an auto-generated CSS file.
|
|
||||||
pub async fn install_sans_mono_with_css(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
css_basepath: &str,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
css_output_path: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
install_sans_mono_internal(cache, octocrab, output_path, Some((css_basepath, css_output_path)))
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The DejaVu Sans Mono Font.
|
|
||||||
pub fn font() -> NonVariableDefinition {
|
|
||||||
SANS_MONO_FONT_FAMILY_FONTS
|
|
||||||
.iter()
|
|
||||||
.map(|(name, weight)| {
|
|
||||||
let file = format!("{FILE_PREFIX}{FILE_SANS_MONO_PREFIX}{name}.ttf");
|
|
||||||
let header = NonVariableFaceHeader {
|
|
||||||
weight: *weight,
|
|
||||||
width: font::Width::Normal,
|
|
||||||
style: font::Style::Normal,
|
|
||||||
};
|
|
||||||
(header, file)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All font faces contained in this font.
|
|
||||||
pub fn faces() -> [NonVariableFaceHeader; 2] {
|
|
||||||
[NonVariableFaceHeader { weight: font::Weight::Normal, ..default() }, NonVariableFaceHeader {
|
|
||||||
weight: font::Weight::Bold,
|
|
||||||
..default()
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the fonts from the given archive file, and write them in the given directory.
|
|
||||||
pub async fn extract_fonts(
|
|
||||||
fonts: &NonVariableDefinition,
|
|
||||||
package: impl AsRef<Path>,
|
|
||||||
out_dir: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
let mut archive = ide_ci::archive::zip::open(&package)?;
|
|
||||||
crate::ide::web::fonts::extract_fonts(&mut archive, fonts, package, out_dir, &mut |path| {
|
|
||||||
let mut iter = path.iter();
|
|
||||||
for _ in iter.by_ref().take(2) {}
|
|
||||||
Box::from(iter.as_str())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Download the DejaVu Font package, with caching and GitHub authentication.
|
|
||||||
pub async fn download(cache: &Cache, octocrab: &Octocrab) -> Result<Box<Path>> {
|
|
||||||
Ok(cache
|
|
||||||
.get(ide_ci::cache::download::DownloadFile {
|
|
||||||
client: octocrab.client.clone(),
|
|
||||||
key: ide_ci::cache::download::Key {
|
|
||||||
url: PACKAGE_URL.parse().unwrap(),
|
|
||||||
additional_headers: reqwest::header::HeaderMap::from_iter([(
|
|
||||||
reqwest::header::ACCEPT,
|
|
||||||
reqwest::header::HeaderValue::from_static(
|
|
||||||
mime::APPLICATION_OCTET_STREAM.as_ref(),
|
|
||||||
),
|
|
||||||
)]),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.await?
|
|
||||||
.into_boxed_path())
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
//! Definitions for Enso Font, and functions for downloading and installing them.
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use enso_enso_font::ttf;
|
|
||||||
use enso_font::NonVariableFaceHeader;
|
|
||||||
use ide_ci::cache::Cache;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Constants ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
const FONT_FAMILY: &str = "Enso";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Enso Font ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
pub async fn install_for_html(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
let output_path = output_path.as_ref();
|
|
||||||
let html_fonts: HashMap<_, _> = [
|
|
||||||
(NonVariableFaceHeader { weight: ttf::Weight::Normal, ..default() }, "Regular"),
|
|
||||||
(NonVariableFaceHeader { weight: ttf::Weight::ExtraBold, ..default() }, "Bold"),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
let html_font_definitions = enso_enso_font::font()
|
|
||||||
.variations()
|
|
||||||
.filter(|v| html_fonts.contains_key(&v.header))
|
|
||||||
.collect();
|
|
||||||
let get_font_files = async {
|
|
||||||
let package = download(cache, octocrab).await?;
|
|
||||||
enso_enso_font::extract_fonts(&html_font_definitions, package, output_path).await
|
|
||||||
};
|
|
||||||
let make_css_file = async {
|
|
||||||
let mut css = String::new();
|
|
||||||
let url = ".";
|
|
||||||
for (header, variant) in html_fonts {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let def = html_font_definitions.get(header);
|
|
||||||
let def = def.ok_or_else(|| {
|
|
||||||
anyhow!(
|
|
||||||
"Required font not found in Enso Font package. \
|
|
||||||
Expected a font matching: {header:?}."
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let file = &def.file;
|
|
||||||
// Note that this cannot use `generate_css_file`, as it specifies a different font
|
|
||||||
// family for each variant.
|
|
||||||
writeln!(&mut css, "@font-face {{")?;
|
|
||||||
writeln!(&mut css, " font-family: '{FONT_FAMILY}{variant}';")?;
|
|
||||||
writeln!(&mut css, " src: url('{url}/{file}');")?;
|
|
||||||
writeln!(&mut css, " font-weight: normal;")?;
|
|
||||||
writeln!(&mut css, " font-style: normal;")?;
|
|
||||||
writeln!(&mut css, "}}")?;
|
|
||||||
}
|
|
||||||
let css_path = output_path.join("ensoFont.css");
|
|
||||||
ide_ci::fs::tokio::write(css_path, css).await?;
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
try_join!(get_font_files, make_css_file)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn install_with_css(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
css_basepath: &str,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
css_output_path: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
let output_path = output_path.as_ref();
|
|
||||||
let font = enso_enso_font::font();
|
|
||||||
let faces = enso_enso_font::faces();
|
|
||||||
let font = crate::ide::web::fonts::filter_font(&font, &faces);
|
|
||||||
let package = download(cache, octocrab).await?;
|
|
||||||
let get_font_files = enso_enso_font::extract_fonts(&font, package, output_path);
|
|
||||||
let css_output_info = Some((css_basepath, css_output_path));
|
|
||||||
let make_css_file = crate::ide::web::fonts::write_css_file_if_required(
|
|
||||||
FONT_FAMILY,
|
|
||||||
&font,
|
|
||||||
&faces,
|
|
||||||
css_output_info,
|
|
||||||
);
|
|
||||||
try_join!(get_font_files, make_css_file)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Download the Enso Font package, with caching and GitHub authentication.
|
|
||||||
pub async fn download(cache: &Cache, octocrab: &Octocrab) -> Result<Box<Path>> {
|
|
||||||
Ok(cache
|
|
||||||
.get(ide_ci::cache::download::DownloadFile {
|
|
||||||
client: octocrab.client.clone(),
|
|
||||||
key: ide_ci::cache::download::Key {
|
|
||||||
url: enso_enso_font::PACKAGE_URL.parse().unwrap(),
|
|
||||||
additional_headers: reqwest::header::HeaderMap::from_iter([(
|
|
||||||
reqwest::header::ACCEPT,
|
|
||||||
reqwest::header::HeaderValue::from_static(
|
|
||||||
mime::APPLICATION_OCTET_STREAM.as_ref(),
|
|
||||||
),
|
|
||||||
)]),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.await?
|
|
||||||
.into_boxed_path())
|
|
||||||
}
|
|
@ -1,192 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use enso_font::NonVariableDefinition;
|
|
||||||
use enso_font::NonVariableFaceHeader;
|
|
||||||
use ide_ci::archive::extract_files::ExtractFiles;
|
|
||||||
use ide_ci::cache::Cache;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// === HTML Font Support ===
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
pub async fn install_html_fonts(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
let output_path = output_path.as_ref();
|
|
||||||
crate::ide::web::google_font::install(cache, octocrab, "mplus1", output_path).await?;
|
|
||||||
crate::ide::web::enso_font::install_for_html(cache, octocrab, output_path).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A CSS font style that is displayed as a CSS [`@font-face`] [`font-style`] value.
|
|
||||||
///
|
|
||||||
/// [`@font-face`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
|
||||||
/// [`font-style`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-style
|
|
||||||
#[derive(Debug, Display, Copy, Clone)]
|
|
||||||
pub enum FontStyle {
|
|
||||||
#[display("normal")]
|
|
||||||
Normal,
|
|
||||||
#[display("italic")]
|
|
||||||
Italic,
|
|
||||||
#[display("oblique")]
|
|
||||||
Oblique,
|
|
||||||
/// Angle is in degrees, between -90 and 90.
|
|
||||||
#[display("oblique {_0}deg")]
|
|
||||||
ObliqueWithAngle(f64),
|
|
||||||
/// Angles are in degrees, between -90 and 90.
|
|
||||||
#[display("oblique {_0}deg {_1}deg")]
|
|
||||||
ObliqueWithAngleRange(f64, f64),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A CSS font face that is displayed as a CSS [`@font-face`] declaration.
|
|
||||||
///
|
|
||||||
/// [`@font-face`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FontFace<'a> {
|
|
||||||
family: Cow<'a, str>,
|
|
||||||
path: Cow<'a, str>,
|
|
||||||
weight: Option<u16>,
|
|
||||||
style: Option<FontStyle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FontFace<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let family = &self.family;
|
|
||||||
let path = &self.path;
|
|
||||||
writeln!(f, "@font-face {{")?;
|
|
||||||
writeln!(f, " font-family: '{family}';")?;
|
|
||||||
writeln!(f, " src: url('{path}');")?;
|
|
||||||
if let Some(weight) = self.weight {
|
|
||||||
writeln!(f, " font-weight: {weight};")?;
|
|
||||||
}
|
|
||||||
if let Some(style) = self.style {
|
|
||||||
writeln!(f, " font-style: {style};")?;
|
|
||||||
}
|
|
||||||
writeln!(f, "}}")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a CSS file containing the given font files. Does not include font-weight, so use this
|
|
||||||
/// only if the font weights are not known - prefer [`generate_css_file`] in all other cases.
|
|
||||||
pub fn generate_css_file_from_paths<AsRefStr>(
|
|
||||||
basepath: &str,
|
|
||||||
family: &str,
|
|
||||||
paths: impl Iterator<Item = AsRefStr>,
|
|
||||||
) -> Result<String>
|
|
||||||
where
|
|
||||||
AsRefStr: AsRef<str>,
|
|
||||||
{
|
|
||||||
let mut css = String::new();
|
|
||||||
for path in paths {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let path = format!("{basepath}/{}", path.as_ref());
|
|
||||||
let font_face = FontFace {
|
|
||||||
family: Cow::Borrowed(family),
|
|
||||||
path: Cow::Borrowed(path.as_str()),
|
|
||||||
weight: None,
|
|
||||||
style: None,
|
|
||||||
};
|
|
||||||
writeln!(&mut css, "{font_face}")?;
|
|
||||||
}
|
|
||||||
Ok(css)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a CSS file containing the given font family, including only the given font variations.
|
|
||||||
pub fn generate_css_file<'a>(
|
|
||||||
basepath: &str,
|
|
||||||
family: &str,
|
|
||||||
definitions: &NonVariableDefinition,
|
|
||||||
fonts: impl Iterator<Item = &'a NonVariableFaceHeader>,
|
|
||||||
) -> Result<String> {
|
|
||||||
let mut css = String::new();
|
|
||||||
for header in fonts {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let def = definitions.get(*header);
|
|
||||||
let def = def.ok_or_else(|| {
|
|
||||||
anyhow!(
|
|
||||||
"Required font not found in {family} Font package. \
|
|
||||||
Expected a font matching: {header:?}."
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let path = format!("{basepath}/{}", def.file);
|
|
||||||
let weight = def.header.weight.to_number();
|
|
||||||
let font_face = FontFace {
|
|
||||||
family: Cow::Borrowed(family),
|
|
||||||
path: Cow::Borrowed(path.as_str()),
|
|
||||||
weight: Some(weight),
|
|
||||||
style: None,
|
|
||||||
};
|
|
||||||
writeln!(&mut css, "{font_face}")?;
|
|
||||||
}
|
|
||||||
Ok(css)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===================
|
|
||||||
// === Filter Font ===
|
|
||||||
// ===================
|
|
||||||
|
|
||||||
pub fn filter_font(
|
|
||||||
font: &NonVariableDefinition,
|
|
||||||
faces: &[NonVariableFaceHeader],
|
|
||||||
) -> NonVariableDefinition {
|
|
||||||
font.variations().filter(|v| faces.contains(&v.header)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =====================
|
|
||||||
// === Make CSS File ===
|
|
||||||
// =====================
|
|
||||||
|
|
||||||
pub async fn write_css_file_if_required(
|
|
||||||
font_family: &str,
|
|
||||||
font: &NonVariableDefinition,
|
|
||||||
faces: &[NonVariableFaceHeader],
|
|
||||||
css_output_info: Option<(&str, impl AsRef<Path>)>,
|
|
||||||
) -> Result {
|
|
||||||
if let Some((css_basepath, css_output_path)) = css_output_info {
|
|
||||||
let contents = generate_css_file(css_basepath, font_family, font, faces.iter())?;
|
|
||||||
ide_ci::fs::tokio::write(css_output_path, contents).await?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =====================
|
|
||||||
// === Extract Fonts ===
|
|
||||||
// =====================
|
|
||||||
|
|
||||||
/// Extract the fonts from the given archive file, and write them in the given directory.
|
|
||||||
#[context("Failed to extract fonts from archive {}", package.as_ref().display())]
|
|
||||||
pub async fn extract_fonts(
|
|
||||||
archive: impl ExtractFiles,
|
|
||||||
fonts: &NonVariableDefinition,
|
|
||||||
package: impl AsRef<Path>,
|
|
||||||
out_dir: impl AsRef<Path>,
|
|
||||||
normalize_path: &mut impl FnMut(&Path) -> Box<str>,
|
|
||||||
) -> Result {
|
|
||||||
ide_ci::fs::tokio::create_dir_if_missing(out_dir.as_ref()).await?;
|
|
||||||
let mut files_expected: HashSet<_> = fonts.files().collect();
|
|
||||||
archive
|
|
||||||
.extract_files(|path_in_archive| {
|
|
||||||
let stripped_path = normalize_path(path_in_archive);
|
|
||||||
if files_expected.remove(stripped_path.as_ref()) {
|
|
||||||
Some(out_dir.as_ref().join(stripped_path.as_ref()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
ensure!(files_expected.is_empty(), "Required fonts not found in archive: {files_expected:?}.");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,196 +0,0 @@
|
|||||||
//! Functions for downloading and installing Google fonts.
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use ide_ci::cache::Cache;
|
|
||||||
use ide_ci::cache::Storable;
|
|
||||||
use ide_ci::github;
|
|
||||||
use ide_ci::github::RepoRef;
|
|
||||||
use octocrab::models::repos;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Constants ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
/// Google Fonts repository.
|
|
||||||
pub const REPOSITORY: RepoRef = RepoRef { owner: "google", name: "fonts" };
|
|
||||||
|
|
||||||
/// Path to the directory on the Google Fonts repository where we get the fonts from.
|
|
||||||
///
|
|
||||||
/// The directory name denotes the license of the fonts. In our case this is SIL OPEN FONT LICENSE
|
|
||||||
/// Version 1.1, commonly known as OFL.
|
|
||||||
pub const DIRECTORY: &str = "ofl";
|
|
||||||
|
|
||||||
/// We keep dependency to a fixed commit, so we can safely cache it.
|
|
||||||
///
|
|
||||||
/// There are no known reasons not to bump this.
|
|
||||||
pub const COMMIT_SHA1: &str = "ea893a43af7c5ab5ccee189fc2720788d99887ed";
|
|
||||||
|
|
||||||
|
|
||||||
// ==============
|
|
||||||
// === Family ===
|
|
||||||
// ==============
|
|
||||||
|
|
||||||
/// Identifies uniquely a source of font family download.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct Family {
|
|
||||||
/// Remote repository with fonts.
|
|
||||||
pub repo: github::Repo,
|
|
||||||
/// Which commit we want to be downloaded.
|
|
||||||
pub r#ref: String,
|
|
||||||
/// Font family. It corresponds to the subdirectories names (under the top-level
|
|
||||||
/// license-denoting directories).
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Family {
|
|
||||||
/// List content items in the repository that contain TTF files for the given font family.
|
|
||||||
pub async fn list_ttf(
|
|
||||||
&self,
|
|
||||||
handle: github::repo::Handle<impl IsRepo>,
|
|
||||||
) -> Result<Vec<repos::Content>> {
|
|
||||||
let path = format!("{DIRECTORY}/{}", self.name);
|
|
||||||
let files = handle.repos().get_content().r#ref(&self.r#ref).path(path).send().await?;
|
|
||||||
Ok(files.items.into_iter().filter(|file| file.name.ends_with(".ttf")).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ====================
|
|
||||||
// === DownloadFont ===
|
|
||||||
// ====================
|
|
||||||
|
|
||||||
/// Description of the job to download the fonts.
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[derive_where(Debug)]
|
|
||||||
pub struct DownloadFont {
|
|
||||||
pub family: Family,
|
|
||||||
/// Possible authentication to GitHub (to get bigger rate limit).
|
|
||||||
#[derive_where(skip)]
|
|
||||||
pub octocrab: Octocrab,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DownloadFont {
|
|
||||||
/// Get a handle to the remote repository with the fonts.
|
|
||||||
pub fn handle(&self) -> github::repo::Handle<impl IsRepo> {
|
|
||||||
self.family.repo.handle(&self.octocrab)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Download the font family to the given directory. They will be placed in the output
|
|
||||||
/// directory. The function returns relative paths to the downloaded files.
|
|
||||||
pub async fn download(&self, output_path: impl AsRef<Path>) -> Result<Vec<PathBuf>> {
|
|
||||||
let files = self.family.list_ttf(self.handle()).await?;
|
|
||||||
let mut ret = Vec::new();
|
|
||||||
for file in &files {
|
|
||||||
let destination_file = output_path.as_ref().join(&file.name);
|
|
||||||
let url = file.download_url.as_ref().context("Missing 'download_url' in the reply.")?;
|
|
||||||
let reply = ide_ci::io::web::client::download(&self.octocrab.client, url).await?;
|
|
||||||
ide_ci::io::web::stream_to_file(reply, &destination_file).await?;
|
|
||||||
ret.push(file.name.as_str().into());
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Storable for DownloadFont {
|
|
||||||
/// In metadata form we just store paths relative to the store.
|
|
||||||
type Metadata = Vec<PathBuf>;
|
|
||||||
/// Here paths are absolute.
|
|
||||||
type Output = Vec<PathBuf>;
|
|
||||||
type Key = Family;
|
|
||||||
|
|
||||||
fn generate(
|
|
||||||
&self,
|
|
||||||
_cache: Cache,
|
|
||||||
store: PathBuf,
|
|
||||||
) -> BoxFuture<'static, Result<Self::Metadata>> {
|
|
||||||
let this = self.clone();
|
|
||||||
async move {
|
|
||||||
let fonts = this.download(&store).await?;
|
|
||||||
Ok(fonts)
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn adapt(
|
|
||||||
&self,
|
|
||||||
cache: PathBuf,
|
|
||||||
mut metadata: Self::Metadata,
|
|
||||||
) -> BoxFuture<'static, Result<Self::Output>> {
|
|
||||||
async move {
|
|
||||||
for font in &mut metadata {
|
|
||||||
*font = cache.join(&*font);
|
|
||||||
}
|
|
||||||
Ok(metadata)
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key(&self) -> Self::Key {
|
|
||||||
self.family.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================
|
|
||||||
// === Entry Point ===
|
|
||||||
// ===================
|
|
||||||
|
|
||||||
/// Install a Google font, without an auto-generated CSS file.
|
|
||||||
pub async fn install(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
family: &str,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
) -> Result<Vec<PathBuf>> {
|
|
||||||
let family =
|
|
||||||
Family { repo: REPOSITORY.into(), r#ref: COMMIT_SHA1.into(), name: family.into() };
|
|
||||||
let font = DownloadFont { family, octocrab: octocrab.clone() };
|
|
||||||
let cached_fonts = cache.get(font).await?;
|
|
||||||
let copy_futures =
|
|
||||||
cached_fonts.into_iter().map(|font| ide_ci::fs::tokio::copy_to(font, &output_path));
|
|
||||||
let result = futures::future::join_all(copy_futures).await.into_iter().try_collect()?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Install a Google font, including an auto-generated CSS file.
|
|
||||||
pub async fn install_with_css(
|
|
||||||
cache: &Cache,
|
|
||||||
octocrab: &Octocrab,
|
|
||||||
family: &str,
|
|
||||||
css_family: &str,
|
|
||||||
css_basepath: &str,
|
|
||||||
output_path: impl AsRef<Path>,
|
|
||||||
css_output_path: impl AsRef<Path>,
|
|
||||||
) -> Result<Vec<PathBuf>> {
|
|
||||||
let paths = install(cache, octocrab, family, output_path).await?;
|
|
||||||
let css = crate::ide::web::fonts::generate_css_file_from_paths(
|
|
||||||
css_basepath,
|
|
||||||
css_family,
|
|
||||||
paths.iter().flat_map(|path| path.try_file_name().map(|name| name.as_str())),
|
|
||||||
)?;
|
|
||||||
ide_ci::fs::tokio::write(css_output_path, css).await?;
|
|
||||||
Ok(paths)
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============
|
|
||||||
// === Tests ===
|
|
||||||
// =============
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[ignore]
|
|
||||||
async fn new_download() -> Result {
|
|
||||||
setup_logging().ok();
|
|
||||||
let path = r"C:\temp\google_fonts2";
|
|
||||||
let octocrab = github::setup_octocrab().await?;
|
|
||||||
let cache = Cache::new_default().await?;
|
|
||||||
let aaa = install(&cache, &octocrab, "mplus1", path).await?;
|
|
||||||
dbg!(aaa);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -64,32 +64,6 @@ impl IsTarget for Gui {
|
|||||||
let WithDestination { inner: _, destination } = job;
|
let WithDestination { inner: _, destination } = job;
|
||||||
async move {
|
async move {
|
||||||
let repo_root = &context.repo_root;
|
let repo_root = &context.repo_root;
|
||||||
crate::ide::web::google_font::install_with_css(
|
|
||||||
&context.cache,
|
|
||||||
&context.octocrab,
|
|
||||||
"mplus1",
|
|
||||||
"M PLUS 1",
|
|
||||||
"/font-mplus1",
|
|
||||||
&repo_root.app.gui.public.font_mplus_1,
|
|
||||||
&repo_root.app.gui.src.project_view.assets.font_mplus_1_css,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
crate::ide::web::dejavu_font::install_sans_mono_with_css(
|
|
||||||
&context.cache,
|
|
||||||
&context.octocrab,
|
|
||||||
"/font-dejavu",
|
|
||||||
&repo_root.app.gui.public.font_dejavu,
|
|
||||||
&repo_root.app.gui.src.project_view.assets.font_dejavu_css,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
crate::ide::web::enso_font::install_with_css(
|
|
||||||
&context.cache,
|
|
||||||
&context.octocrab,
|
|
||||||
"/font-enso",
|
|
||||||
&repo_root.app.gui.public.font_enso,
|
|
||||||
&repo_root.app.gui.src.project_view.assets.font_enso_css,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
crate::web::install(repo_root).await?;
|
crate::web::install(repo_root).await?;
|
||||||
crate::web::run_script(repo_root, crate::web::Script::Build).await?;
|
crate::web::run_script(repo_root, crate::web::Script::Build).await?;
|
||||||
ide_ci::fs::mirror_directory(
|
ide_ci::fs::mirror_directory(
|
||||||
|
@ -191,6 +191,7 @@ export default [
|
|||||||
'**/.cache/**',
|
'**/.cache/**',
|
||||||
'**/playwright-report',
|
'**/playwright-report',
|
||||||
'**/dist',
|
'**/dist',
|
||||||
|
'**/mockDist',
|
||||||
'**/build.mjs',
|
'**/build.mjs',
|
||||||
'**/*.timestamp-*.mjs',
|
'**/*.timestamp-*.mjs',
|
||||||
'**/node_modules',
|
'**/node_modules',
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "enso-enso-font"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Enso Team <contact@enso.org>"]
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
enso-font = { path = "../font" }
|
|
||||||
ide-ci = { path = "../../../build/ci_utils" }
|
|
||||||
owned_ttf_parser = { workspace = true }
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
@ -1,112 +0,0 @@
|
|||||||
//! The Enso Font. This crate supports downloading and unpacking the font family, as well as
|
|
||||||
//! constructing a reduced font family from a subset of the fonts.
|
|
||||||
|
|
||||||
// === Non-Standard Linter Configuration ===
|
|
||||||
#![deny(unconditional_recursion)]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(trivial_casts)]
|
|
||||||
|
|
||||||
use ide_ci::prelude::*;
|
|
||||||
|
|
||||||
use enso_font::NonVariableDefinition;
|
|
||||||
use enso_font::NonVariableFaceHeader;
|
|
||||||
|
|
||||||
|
|
||||||
// ==============
|
|
||||||
// === Export ===
|
|
||||||
// ==============
|
|
||||||
|
|
||||||
pub use owned_ttf_parser as ttf;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Constants ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
/// The name of the Enso font family.
|
|
||||||
pub const FONT_FAMILY: &str = "enso";
|
|
||||||
|
|
||||||
const FONTS: &[(&str, ttf::Weight)] = &[
|
|
||||||
("Black", ttf::Weight::Black),
|
|
||||||
("Bold", ttf::Weight::Bold),
|
|
||||||
("ExtraBold", ttf::Weight::ExtraBold),
|
|
||||||
("ExtraLight", ttf::Weight::ExtraLight),
|
|
||||||
("Light", ttf::Weight::Light),
|
|
||||||
("Medium", ttf::Weight::Medium),
|
|
||||||
("Regular", ttf::Weight::Normal),
|
|
||||||
("SemiBold", ttf::Weight::SemiBold),
|
|
||||||
("Thin", ttf::Weight::Thin),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// The URL for the Enso Font package.
|
|
||||||
pub const PACKAGE_URL: &str =
|
|
||||||
"https://github.com/enso-org/font/releases/download/1.0/enso-font-1.0.tar.gz";
|
|
||||||
const PACKAGE_FONTS_PREFIX: &str = "ttf";
|
|
||||||
|
|
||||||
/// Font features.
|
|
||||||
pub mod feature {
|
|
||||||
/// The flag identifying the ligature feature in this font.
|
|
||||||
pub const LIGATURES: &str = "liga";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Enso Font ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
/// The Enso Font.
|
|
||||||
pub fn font() -> NonVariableDefinition {
|
|
||||||
FONTS
|
|
||||||
.iter()
|
|
||||||
.map(|(name, weight)| {
|
|
||||||
let file = format!("Enso-{name}.ttf");
|
|
||||||
let header = NonVariableFaceHeader {
|
|
||||||
weight: *weight,
|
|
||||||
width: ttf::Width::Normal,
|
|
||||||
style: ttf::Style::Normal,
|
|
||||||
};
|
|
||||||
(header, file)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All font faces contained in this font.
|
|
||||||
pub fn faces() -> [NonVariableFaceHeader; 9] {
|
|
||||||
[
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::Thin, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::ExtraLight, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::Light, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::Normal, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::Medium, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::SemiBold, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::Bold, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::ExtraBold, ..default() },
|
|
||||||
NonVariableFaceHeader { weight: ttf::Weight::Black, ..default() },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the fonts from the given archive file, and write them in the given directory.
|
|
||||||
#[context("Failed to extract fonts from archive {}", package.as_ref().display())]
|
|
||||||
pub async fn extract_fonts(
|
|
||||||
fonts: &NonVariableDefinition,
|
|
||||||
package: impl AsRef<Path>,
|
|
||||||
out_dir: impl AsRef<Path>,
|
|
||||||
) -> Result {
|
|
||||||
use ide_ci::archive::extract_files::ExtractFiles;
|
|
||||||
ide_ci::fs::tokio::create_dir_if_missing(out_dir.as_ref()).await?;
|
|
||||||
let mut files_expected: HashSet<_> = fonts.files().collect();
|
|
||||||
ide_ci::archive::tar::Archive::open_tar_gz(&package)
|
|
||||||
.await?
|
|
||||||
.extract_files(|path_in_archive| {
|
|
||||||
path_in_archive
|
|
||||||
.strip_prefix(PACKAGE_FONTS_PREFIX)
|
|
||||||
.ok()
|
|
||||||
.filter(|path| path.to_str().map_or(false, |path| files_expected.remove(path)))
|
|
||||||
.map(|path| out_dir.as_ref().join(path))
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
ensure!(files_expected.is_empty(), "Required fonts not found in archive: {files_expected:?}.");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "enso-font"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Enso Team <contact@enso.org>"]
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
owned_ttf_parser = { workspace = true }
|
|
||||||
derive_more = { workspace = true }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib", "rlib"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
@ -1,203 +0,0 @@
|
|||||||
//! Definition of a font family, a set of related font faces.
|
|
||||||
//!
|
|
||||||
//! # One font face per file
|
|
||||||
//! The implementation of this library has an important limitation that should not cause any
|
|
||||||
//! problems, however, it is important to be aware of it. In case of non-variable fonts, only one
|
|
||||||
//! font face is supported per file. A font face is identified by (width, weight, style) triple (see
|
|
||||||
//! [`NonVariableFaceHeader`]) to learn more. If you want to use font faces defined in the same
|
|
||||||
//! file (e.g. ".ttf" file), you have to split them into multiple files first. All major browsers
|
|
||||||
//! have the same limitation. For example, you are unable to define in CSS a new font family by
|
|
||||||
//! loading different faces from the same file with the `@font-face` rule
|
|
||||||
//! (https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face).
|
|
||||||
|
|
||||||
// === Non-Standard Linter Configuration ===
|
|
||||||
#![deny(unconditional_recursion)]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(trivial_casts)]
|
|
||||||
#![warn(unused_qualifications)]
|
|
||||||
|
|
||||||
use derive_more::Deref;
|
|
||||||
use derive_more::Display;
|
|
||||||
|
|
||||||
|
|
||||||
// ==============
|
|
||||||
// === Export ===
|
|
||||||
// ==============
|
|
||||||
|
|
||||||
pub use owned_ttf_parser::Style;
|
|
||||||
pub use owned_ttf_parser::Weight;
|
|
||||||
pub use owned_ttf_parser::Width;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============
|
|
||||||
// === Name ===
|
|
||||||
// ============
|
|
||||||
|
|
||||||
/// A name of a font. The name is normalized to case-insensitive form during construction to
|
|
||||||
/// eliminate accidental mistakes, the same way as it's done in CSS:
|
|
||||||
/// https://stackoverflow.com/questions/17967371/are-property-values-in-css-case-sensitive
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Debug, Deref, Display, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Name {
|
|
||||||
pub normalized: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Name> for Name {
|
|
||||||
fn from(name: &Name) -> Self {
|
|
||||||
name.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Name {
|
|
||||||
fn from(name: &str) -> Self {
|
|
||||||
let normalized = name.to_lowercase();
|
|
||||||
Name { normalized }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&String> for Name {
|
|
||||||
fn from(name: &String) -> Self {
|
|
||||||
name.as_str().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Name {
|
|
||||||
fn from(name: String) -> Self {
|
|
||||||
(&name).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===================
|
|
||||||
// === Font Family ===
|
|
||||||
// ===================
|
|
||||||
|
|
||||||
/// Definition of a font family. Font family consist of one font face in case of variable fonts or
|
|
||||||
/// multiple font faces in case of non-variable ones.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum FontFamily {
|
|
||||||
Variable(VariableDefinition),
|
|
||||||
NonVariable(NonVariableDefinition),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==========================
|
|
||||||
// === VariableDefinition ===
|
|
||||||
// ==========================
|
|
||||||
|
|
||||||
/// Definition of a variable font family. See the following link to learn more about variable fonts:
|
|
||||||
/// https://docs.microsoft.com/en-us/windows/win32/directwrite/opentype-variable-fonts
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct VariableDefinition {
|
|
||||||
/// Name of the file that the font data was read from. It contains the file extension, for
|
|
||||||
/// example `MPLUS1[wght].ttf`.
|
|
||||||
pub file_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VariableDefinition {
|
|
||||||
/// Constructor.
|
|
||||||
pub fn new(file_name: impl Into<String>) -> Self {
|
|
||||||
let file_name = file_name.into();
|
|
||||||
Self { file_name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =============================
|
|
||||||
// === NonVariableDefinition ===
|
|
||||||
// =============================
|
|
||||||
|
|
||||||
/// Definition of a non-variable font family. Contains mapping between (width, weight, style) triple
|
|
||||||
/// (see [`NonVariableFaceHeader`]) to learn more) and file names.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct NonVariableDefinition {
|
|
||||||
variations: std::collections::HashMap<NonVariableFaceHeader, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NonVariableDefinition {
|
|
||||||
/// All weights defined in this font family.
|
|
||||||
pub fn possible_weights(&self) -> Vec<Weight> {
|
|
||||||
let mut weights: Vec<_> = self.variations().map(|var| var.header.weight).collect();
|
|
||||||
weights.sort_unstable_by_key(|w| w.to_number());
|
|
||||||
weights.dedup();
|
|
||||||
weights
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an iterator over the filenames associated with fonts in this family.
|
|
||||||
pub fn files(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.variations().map(|v| v.file)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an iterator over the fonts in this family.
|
|
||||||
pub fn variations(&self) -> impl Iterator<Item = NonVariableVariation> {
|
|
||||||
self.variations
|
|
||||||
.iter()
|
|
||||||
.map(|(header, file)| NonVariableVariation { header: *header, file: file.as_str() })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the font in this family that exactly matches the given parameters, if any.
|
|
||||||
pub fn get(&self, header: NonVariableFaceHeader) -> Option<NonVariableVariation> {
|
|
||||||
self.variations.get(&header).map(|file| NonVariableVariation { header, file })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<(NonVariableFaceHeader, String)> for NonVariableDefinition {
|
|
||||||
fn from_iter<T>(iter: T) -> Self
|
|
||||||
where T: IntoIterator<Item = (NonVariableFaceHeader, String)> {
|
|
||||||
let map = iter.into_iter().collect();
|
|
||||||
Self { variations: map }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromIterator<NonVariableVariation<'a>> for NonVariableDefinition {
|
|
||||||
fn from_iter<T>(iter: T) -> Self
|
|
||||||
where T: IntoIterator<Item = NonVariableVariation<'a>> {
|
|
||||||
let map = iter.into_iter().map(|v| (v.header, v.file.to_owned())).collect();
|
|
||||||
Self { variations: map }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============================
|
|
||||||
// === NonVariableVariation ===
|
|
||||||
// ============================
|
|
||||||
|
|
||||||
/// The parameters of a font, and the filename where it should be found.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct NonVariableVariation<'a> {
|
|
||||||
/// The parameters of the font.
|
|
||||||
pub header: NonVariableFaceHeader,
|
|
||||||
/// The filename for the font.
|
|
||||||
pub file: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =============================
|
|
||||||
// === NonVariableFaceHeader ===
|
|
||||||
// =============================
|
|
||||||
|
|
||||||
/// Combination of all information allowing mapping the font face to a font file for non-variable
|
|
||||||
/// fonts. For variable fonts, there is just one definition for any combination of the parameters.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct NonVariableFaceHeader {
|
|
||||||
pub width: Width,
|
|
||||||
pub weight: Weight,
|
|
||||||
pub style: Style,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NonVariableFaceHeader {
|
|
||||||
/// Constructor.
|
|
||||||
pub const fn new(width: Width, weight: Weight, style: Style) -> Self {
|
|
||||||
Self { width, weight, style }
|
|
||||||
}
|
|
||||||
}
|
|
@ -428,9 +428,6 @@ importers:
|
|||||||
'@types/tar':
|
'@types/tar':
|
||||||
specifier: ^6.1.4
|
specifier: ^6.1.4
|
||||||
version: 6.1.13
|
version: 6.1.13
|
||||||
'@types/unbzip2-stream':
|
|
||||||
specifier: ^1.4.3
|
|
||||||
version: 1.4.3
|
|
||||||
'@types/validator':
|
'@types/validator':
|
||||||
specifier: ^13.11.7
|
specifier: ^13.11.7
|
||||||
version: 13.12.0
|
version: 13.12.0
|
||||||
@ -524,9 +521,6 @@ importers:
|
|||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.5.3
|
specifier: ^5.5.3
|
||||||
version: 5.5.3
|
version: 5.5.3
|
||||||
unbzip2-stream:
|
|
||||||
specifier: ^1.4.3
|
|
||||||
version: 1.4.3
|
|
||||||
vite:
|
vite:
|
||||||
specifier: ^5.3.5
|
specifier: ^5.3.5
|
||||||
version: 5.3.5(@types/node@20.11.21)(lightningcss@1.25.1)
|
version: 5.3.5(@types/node@20.11.21)(lightningcss@1.25.1)
|
||||||
@ -3065,18 +3059,12 @@ packages:
|
|||||||
'@types/tar@6.1.13':
|
'@types/tar@6.1.13':
|
||||||
resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==}
|
resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==}
|
||||||
|
|
||||||
'@types/through@0.0.33':
|
|
||||||
resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==}
|
|
||||||
|
|
||||||
'@types/to-ico@1.1.3':
|
'@types/to-ico@1.1.3':
|
||||||
resolution: {integrity: sha512-3Ew8Hsz/qiDGzwvz75pjRU+6Ocfvrit6hHfirauEdJXxdro73MTe5XdcANh4GZ2wdJqWw9BIuHwWgKAfjUXoGw==}
|
resolution: {integrity: sha512-3Ew8Hsz/qiDGzwvz75pjRU+6Ocfvrit6hHfirauEdJXxdro73MTe5XdcANh4GZ2wdJqWw9BIuHwWgKAfjUXoGw==}
|
||||||
|
|
||||||
'@types/tough-cookie@4.0.5':
|
'@types/tough-cookie@4.0.5':
|
||||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||||
|
|
||||||
'@types/unbzip2-stream@1.4.3':
|
|
||||||
resolution: {integrity: sha512-D8X5uuJRISqc8YtwL8jNW2FpPdUOCYXbfD6zNROCTbVXK9nawucxh10tVXE3MPjnHdRA1LvB0zDxVya/lBsnYw==}
|
|
||||||
|
|
||||||
'@types/validator@13.12.0':
|
'@types/validator@13.12.0':
|
||||||
resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
|
resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
|
||||||
|
|
||||||
@ -6960,9 +6948,6 @@ packages:
|
|||||||
thenify@3.3.1:
|
thenify@3.3.1:
|
||||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||||
|
|
||||||
through@2.3.8:
|
|
||||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
|
||||||
|
|
||||||
tiny-invariant@1.3.3:
|
tiny-invariant@1.3.3:
|
||||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
|
||||||
@ -7121,9 +7106,6 @@ packages:
|
|||||||
unbox-primitive@1.0.2:
|
unbox-primitive@1.0.2:
|
||||||
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||||
|
|
||||||
unbzip2-stream@1.4.3:
|
|
||||||
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
|
|
||||||
|
|
||||||
undici-types@5.26.5:
|
undici-types@5.26.5:
|
||||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||||
|
|
||||||
@ -10504,20 +10486,12 @@ snapshots:
|
|||||||
'@types/node': 20.11.21
|
'@types/node': 20.11.21
|
||||||
minipass: 4.2.8
|
minipass: 4.2.8
|
||||||
|
|
||||||
'@types/through@0.0.33':
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 20.11.21
|
|
||||||
|
|
||||||
'@types/to-ico@1.1.3':
|
'@types/to-ico@1.1.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.11.21
|
'@types/node': 20.11.21
|
||||||
|
|
||||||
'@types/tough-cookie@4.0.5': {}
|
'@types/tough-cookie@4.0.5': {}
|
||||||
|
|
||||||
'@types/unbzip2-stream@1.4.3':
|
|
||||||
dependencies:
|
|
||||||
'@types/through': 0.0.33
|
|
||||||
|
|
||||||
'@types/validator@13.12.0': {}
|
'@types/validator@13.12.0': {}
|
||||||
|
|
||||||
'@types/verror@1.10.10':
|
'@types/verror@1.10.10':
|
||||||
@ -15079,8 +15053,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
|
|
||||||
through@2.3.8: {}
|
|
||||||
|
|
||||||
tiny-invariant@1.3.3: {}
|
tiny-invariant@1.3.3: {}
|
||||||
|
|
||||||
tinybench@2.8.0: {}
|
tinybench@2.8.0: {}
|
||||||
@ -15238,11 +15210,6 @@ snapshots:
|
|||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
which-boxed-primitive: 1.0.2
|
which-boxed-primitive: 1.0.2
|
||||||
|
|
||||||
unbzip2-stream@1.4.3:
|
|
||||||
dependencies:
|
|
||||||
buffer: 5.7.1
|
|
||||||
through: 2.3.8
|
|
||||||
|
|
||||||
undici-types@5.26.5: {}
|
undici-types@5.26.5: {}
|
||||||
|
|
||||||
undici@6.13.0: {}
|
undici@6.13.0: {}
|
||||||
|
Loading…
Reference in New Issue
Block a user