├── .Rbuildignore
├── .github
├── .gitignore
└── workflows
│ ├── R-CMD-check.yaml
│ ├── pkgdown.yaml
│ ├── pr-commands.yaml
│ ├── rhub.yaml
│ └── test-coverage.yaml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── NAMESPACE
├── NEWS.md
├── R
├── cpp11.R
├── dev_strings.R
├── emoji.R
├── font_fallback.R
├── font_feature.R
├── font_info.R
├── font_outline.R
├── font_variation.R
├── match_fonts.R
├── register_font.R
├── shape_string.R
├── sysdata.rda
├── system_fonts.R
├── systemfonts-package.R
├── web_fonts.R
└── zzz.R
├── README.Rmd
├── README.md
├── _pkgdown.yml
├── air.toml
├── cleanup
├── codecov.yml
├── configure
├── cran-comments.md
├── inst
├── include
│ ├── systemfonts-ft.h
│ └── systemfonts.h
└── unfont.ttf
├── man
├── add_fonts.Rd
├── as_font_weight.Rd
├── figures
│ ├── lifecycle-archived.svg
│ ├── lifecycle-defunct.svg
│ ├── lifecycle-deprecated.svg
│ ├── lifecycle-experimental.svg
│ ├── lifecycle-maturing.svg
│ ├── lifecycle-questioning.svg
│ ├── lifecycle-soft-deprecated.svg
│ ├── lifecycle-stable.svg
│ ├── lifecycle-superseded.svg
│ └── logo.png
├── font_fallback.Rd
├── font_feature.Rd
├── font_info.Rd
├── font_variation.Rd
├── fonts_as_import.Rd
├── get_fallback.Rd
├── glyph_info.Rd
├── glyph_outline.Rd
├── glyph_raster.Rd
├── glyph_raster_grob.Rd
├── match_fonts.Rd
├── plot_glyph_stats.Rd
├── register_font.Rd
├── register_variant.Rd
├── require_font.Rd
├── reset_font_cache.Rd
├── search_web_fonts.Rd
├── shape_string.Rd
├── str_split_emoji.Rd
├── string_metrics_dev.Rd
├── string_width.Rd
├── string_widths_dev.Rd
├── system_fonts.Rd
├── systemfonts-package.Rd
└── web-fonts.Rd
├── revdep
├── README.md
├── failures.md
└── problems.md
├── src
├── .gitignore
├── FontDescriptor.h
├── Makevars.in
├── Makevars.win
├── cache_lru.h
├── cache_store.cpp
├── cache_store.h
├── caches.cpp
├── caches.h
├── cpp11.cpp
├── dev_metrics.cpp
├── dev_metrics.h
├── emoji.cpp
├── emoji.h
├── font_fallback.cpp
├── font_fallback.h
├── font_local.cpp
├── font_local.h
├── font_matching.cpp
├── font_matching.h
├── font_metrics.cpp
├── font_metrics.h
├── font_outlines.cpp
├── font_outlines.h
├── font_registry.cpp
├── font_registry.h
├── font_variation.cpp
├── font_variation.h
├── ft_cache.cpp
├── ft_cache.h
├── init.cpp
├── mac
│ └── FontManagerMac.mm
├── string_metrics.cpp
├── string_metrics.h
├── string_shape.cpp
├── string_shape.h
├── types.h
├── unix
│ └── FontManagerLinux.cpp
├── utils.h
└── win
│ ├── DirectWriteFontManagerWindows.cpp
│ └── FontManagerWindows.cpp
├── tests
├── testthat.R
└── testthat
│ ├── test-match_font.R
│ └── test-system_fonts.R
├── tools
└── winlibs.R
├── update_emoji_codes.R
└── vignettes
├── .gitignore
├── c_interface.Rmd
├── fonts_basics.Rmd
├── systemfonts.Rmd
└── text_call_flow.svg
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^.*\.Rproj$
2 | ^\.Rproj\.user$
3 | ^LICENSE\.md$
4 | ^README\.Rmd$
5 | ^CODE_OF_CONDUCT\.md$
6 | ^\.travis\.yml$
7 | ^appveyor\.yml$
8 | ^codecov\.yml$
9 | ^src/Makevars$
10 | ^cran-comments\.md$
11 | ^CRAN-RELEASE$
12 | ^update_emoji_codes\.R$
13 | ^revdep
14 | ^configure.log$
15 | ^\.github$
16 | ^\revdep$
17 | ^O$
18 | ^CRAN-SUBMISSION$
19 | ^systemfonts\.Rproj$
20 | ^_pkgdown\.yml$
21 | ^docs$
22 | ^pkgdown$
23 | ^compile_commands\.json$
24 | ^\.cache$
25 | ^\.vscode$
26 | ^[\.]?air\.toml$
27 |
--------------------------------------------------------------------------------
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | #
4 | # NOTE: This workflow is overkill for most R packages and
5 | # check-standard.yaml is likely a better choice.
6 | # usethis::use_github_action("check-standard") will install it.
7 | on:
8 | push:
9 | branches: [main, master]
10 | pull_request:
11 |
12 | name: R-CMD-check.yaml
13 |
14 | permissions: read-all
15 |
16 | jobs:
17 | R-CMD-check:
18 | runs-on: ${{ matrix.config.os }}
19 |
20 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
21 |
22 | strategy:
23 | fail-fast: false
24 | matrix:
25 | config:
26 | - {os: macos-latest, r: 'release'}
27 |
28 | - {os: windows-latest, r: 'release'}
29 | # use 4.0 or 4.1 to check with rtools40's older compiler
30 | - {os: windows-latest, r: 'oldrel-4'}
31 |
32 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
33 | - {os: ubuntu-latest, r: 'release'}
34 | - {os: ubuntu-22.04, r: 'release'}
35 | - {os: ubuntu-latest, r: 'oldrel-1'}
36 | - {os: ubuntu-latest, r: 'oldrel-2'}
37 | - {os: ubuntu-latest, r: 'oldrel-3'}
38 | - {os: ubuntu-latest, r: 'oldrel-4'}
39 |
40 | env:
41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
42 | R_KEEP_PKG_SOURCE: yes
43 |
44 | steps:
45 | - uses: actions/checkout@v4
46 |
47 | - uses: r-lib/actions/setup-pandoc@v2
48 |
49 | - uses: r-lib/actions/setup-r@v2
50 | with:
51 | r-version: ${{ matrix.config.r }}
52 | http-user-agent: ${{ matrix.config.http-user-agent }}
53 | use-public-rspm: true
54 |
55 | - uses: r-lib/actions/setup-r-dependencies@v2
56 | with:
57 | extra-packages: any::rcmdcheck
58 | needs: check
59 |
60 | - uses: r-lib/actions/check-r-package@v2
61 | with:
62 | upload-snapshots: true
63 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
64 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | release:
8 | types: [published]
9 | workflow_dispatch:
10 |
11 | name: pkgdown.yaml
12 |
13 | permissions: read-all
14 |
15 | jobs:
16 | pkgdown:
17 | runs-on: ubuntu-latest
18 | # Only restrict concurrency for non-PR jobs
19 | concurrency:
20 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
21 | env:
22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
23 | permissions:
24 | contents: write
25 | steps:
26 | - uses: actions/checkout@v4
27 |
28 | - uses: r-lib/actions/setup-pandoc@v2
29 |
30 | - uses: r-lib/actions/setup-r@v2
31 | with:
32 | use-public-rspm: true
33 |
34 | - uses: r-lib/actions/setup-r-dependencies@v2
35 | with:
36 | extra-packages: any::pkgdown, local::.
37 | needs: website
38 |
39 | - name: Build site
40 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
41 | shell: Rscript {0}
42 |
43 | - name: Deploy to GitHub pages 🚀
44 | if: github.event_name != 'pull_request'
45 | uses: JamesIves/github-pages-deploy-action@v4.5.0
46 | with:
47 | clean: false
48 | branch: gh-pages
49 | folder: docs
50 |
--------------------------------------------------------------------------------
/.github/workflows/pr-commands.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | issue_comment:
5 | types: [created]
6 |
7 | name: pr-commands.yaml
8 |
9 | permissions: read-all
10 |
11 | jobs:
12 | document:
13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }}
14 | name: document
15 | runs-on: ubuntu-latest
16 | env:
17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
18 | permissions:
19 | contents: write
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - uses: r-lib/actions/pr-fetch@v2
24 | with:
25 | repo-token: ${{ secrets.GITHUB_TOKEN }}
26 |
27 | - uses: r-lib/actions/setup-r@v2
28 | with:
29 | use-public-rspm: true
30 |
31 | - uses: r-lib/actions/setup-r-dependencies@v2
32 | with:
33 | extra-packages: any::roxygen2
34 | needs: pr-document
35 |
36 | - name: Document
37 | run: roxygen2::roxygenise()
38 | shell: Rscript {0}
39 |
40 | - name: commit
41 | run: |
42 | git config --local user.name "$GITHUB_ACTOR"
43 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
44 | git add man/\* NAMESPACE
45 | git commit -m 'Document'
46 |
47 | - uses: r-lib/actions/pr-push@v2
48 | with:
49 | repo-token: ${{ secrets.GITHUB_TOKEN }}
50 |
51 | style:
52 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }}
53 | name: style
54 | runs-on: ubuntu-latest
55 | env:
56 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
57 | permissions:
58 | contents: write
59 | steps:
60 | - uses: actions/checkout@v4
61 |
62 | - uses: r-lib/actions/pr-fetch@v2
63 | with:
64 | repo-token: ${{ secrets.GITHUB_TOKEN }}
65 |
66 | - uses: r-lib/actions/setup-r@v2
67 |
68 | - name: Install dependencies
69 | run: install.packages("styler")
70 | shell: Rscript {0}
71 |
72 | - name: Style
73 | run: styler::style_pkg()
74 | shell: Rscript {0}
75 |
76 | - name: commit
77 | run: |
78 | git config --local user.name "$GITHUB_ACTOR"
79 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
80 | git add \*.R
81 | git commit -m 'Style'
82 |
83 | - uses: r-lib/actions/pr-push@v2
84 | with:
85 | repo-token: ${{ secrets.GITHUB_TOKEN }}
86 |
--------------------------------------------------------------------------------
/.github/workflows/rhub.yaml:
--------------------------------------------------------------------------------
1 | # R-hub's generic GitHub Actions workflow file. It's canonical location is at
2 | # https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml
3 | # You can update this file to a newer version using the rhub2 package:
4 | #
5 | # rhub::rhub_setup()
6 | #
7 | # It is unlikely that you need to modify this file manually.
8 |
9 | name: R-hub
10 | run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}"
11 |
12 | on:
13 | workflow_dispatch:
14 | inputs:
15 | config:
16 | description: 'A comma separated list of R-hub platforms to use.'
17 | type: string
18 | default: 'linux,windows,macos'
19 | name:
20 | description: 'Run name. You can leave this empty now.'
21 | type: string
22 | id:
23 | description: 'Unique ID. You can leave this empty now.'
24 | type: string
25 |
26 | jobs:
27 |
28 | setup:
29 | runs-on: ubuntu-latest
30 | outputs:
31 | containers: ${{ steps.rhub-setup.outputs.containers }}
32 | platforms: ${{ steps.rhub-setup.outputs.platforms }}
33 |
34 | steps:
35 | # NO NEED TO CHECKOUT HERE
36 | - uses: r-hub/actions/setup@v1
37 | with:
38 | config: ${{ github.event.inputs.config }}
39 | id: rhub-setup
40 |
41 | linux-containers:
42 | needs: setup
43 | if: ${{ needs.setup.outputs.containers != '[]' }}
44 | runs-on: ubuntu-latest
45 | name: ${{ matrix.config.label }}
46 | strategy:
47 | fail-fast: false
48 | matrix:
49 | config: ${{ fromJson(needs.setup.outputs.containers) }}
50 | container:
51 | image: ${{ matrix.config.container }}
52 |
53 | steps:
54 | - uses: r-hub/actions/checkout@v1
55 | - uses: r-hub/actions/platform-info@v1
56 | with:
57 | token: ${{ secrets.RHUB_TOKEN }}
58 | job-config: ${{ matrix.config.job-config }}
59 | - uses: r-hub/actions/setup-deps@v1
60 | with:
61 | token: ${{ secrets.RHUB_TOKEN }}
62 | job-config: ${{ matrix.config.job-config }}
63 | - uses: r-hub/actions/run-check@v1
64 | with:
65 | token: ${{ secrets.RHUB_TOKEN }}
66 | job-config: ${{ matrix.config.job-config }}
67 |
68 | other-platforms:
69 | needs: setup
70 | if: ${{ needs.setup.outputs.platforms != '[]' }}
71 | runs-on: ${{ matrix.config.os }}
72 | name: ${{ matrix.config.label }}
73 | strategy:
74 | fail-fast: false
75 | matrix:
76 | config: ${{ fromJson(needs.setup.outputs.platforms) }}
77 |
78 | steps:
79 | - uses: r-hub/actions/checkout@v1
80 | - uses: r-hub/actions/setup-r@v1
81 | with:
82 | job-config: ${{ matrix.config.job-config }}
83 | token: ${{ secrets.RHUB_TOKEN }}
84 | - uses: r-hub/actions/platform-info@v1
85 | with:
86 | token: ${{ secrets.RHUB_TOKEN }}
87 | job-config: ${{ matrix.config.job-config }}
88 | - uses: r-hub/actions/setup-deps@v1
89 | with:
90 | job-config: ${{ matrix.config.job-config }}
91 | token: ${{ secrets.RHUB_TOKEN }}
92 | - uses: r-hub/actions/run-check@v1
93 | with:
94 | job-config: ${{ matrix.config.job-config }}
95 | token: ${{ secrets.RHUB_TOKEN }}
96 |
--------------------------------------------------------------------------------
/.github/workflows/test-coverage.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 |
8 | name: test-coverage.yaml
9 |
10 | permissions: read-all
11 |
12 | jobs:
13 | test-coverage:
14 | runs-on: ubuntu-latest
15 | env:
16 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
17 |
18 | steps:
19 | - uses: actions/checkout@v4
20 |
21 | - uses: r-lib/actions/setup-r@v2
22 | with:
23 | use-public-rspm: true
24 |
25 | - uses: r-lib/actions/setup-r-dependencies@v2
26 | with:
27 | extra-packages: any::covr, any::xml2
28 | needs: coverage
29 |
30 | - name: Test coverage
31 | run: |
32 | cov <- covr::package_coverage(
33 | quiet = FALSE,
34 | clean = FALSE,
35 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
36 | )
37 | print(cov)
38 | covr::to_cobertura(cov)
39 | shell: Rscript {0}
40 |
41 | - uses: codecov/codecov-action@v5
42 | with:
43 | # Fail if error if not on PR, or if on PR and token is given
44 | fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }}
45 | files: ./cobertura.xml
46 | plugins: noop
47 | disable_search: true
48 | token: ${{ secrets.CODECOV_TOKEN }}
49 |
50 | - name: Show testthat output
51 | if: always()
52 | run: |
53 | ## --------------------------------------------------------------------
54 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
55 | shell: bash
56 |
57 | - name: Upload test results
58 | if: failure()
59 | uses: actions/upload-artifact@v4
60 | with:
61 | name: coverage-test-failures
62 | path: ${{ runner.temp }}/package
63 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | .Ruserdata
5 | .DS_Store
6 | systemfonts.Rproj
7 | src/*.o
8 | src/*.so
9 | src/*/*.o
10 | src/Makevars
11 | inst/doc
12 | configure.log
13 | revdep
14 | O
15 | docs
16 | compile_commands.json
17 | .cache
18 | .vscode
19 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, we pledge to respect all people who
4 | contribute through reporting issues, posting feature requests, updating documentation,
5 | submitting pull requests or patches, and other activities.
6 |
7 | We are committed to making participation in this project a harassment-free experience for
8 | everyone, regardless of level of experience, gender, gender identity and expression,
9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
10 |
11 | Examples of unacceptable behavior by participants include the use of sexual language or
12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment,
13 | insults, or other unprofessional conduct.
14 |
15 | Project maintainers have the right and responsibility to remove, edit, or reject comments,
16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this
17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
18 | from the project team.
19 |
20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
21 | opening an issue or contacting one or more of the project maintainers.
22 |
23 | This Code of Conduct is adapted from the Contributor Covenant
24 | (https://www.contributor-covenant.org), version 1.0.0, available at
25 | https://contributor-covenant.org/version/1/0/0/.
26 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Type: Package
2 | Package: systemfonts
3 | Title: System Native Font Finding
4 | Version: 1.2.3.9000
5 | Authors@R: c(
6 | person("Thomas Lin", "Pedersen", , "thomas.pedersen@posit.co", role = c("aut", "cre"),
7 | comment = c(ORCID = "0000-0002-5147-4711")),
8 | person("Jeroen", "Ooms", , "jeroen@berkeley.edu", role = "aut",
9 | comment = c(ORCID = "0000-0002-4035-0289")),
10 | person("Devon", "Govett", role = "aut",
11 | comment = "Author of font-manager"),
12 | person("Posit Software, PBC", role = c("cph", "fnd"),
13 | comment = c(ROR = "03wc8by49"))
14 | )
15 | Description: Provides system native access to the font catalogue. As font
16 | handling varies between systems it is difficult to correctly locate
17 | installed fonts across different operating systems. The 'systemfonts'
18 | package provides bindings to the native libraries on Windows, macOS
19 | and Linux for finding font files that can then be used further by e.g.
20 | graphic devices. The main use is intended to be from compiled code but
21 | 'systemfonts' also provides access from R.
22 | License: MIT + file LICENSE
23 | URL: https://github.com/r-lib/systemfonts, https://systemfonts.r-lib.org
24 | BugReports: https://github.com/r-lib/systemfonts/issues
25 | Depends:
26 | R (>= 3.2.0)
27 | Imports:
28 | base64enc,
29 | grid,
30 | jsonlite,
31 | lifecycle,
32 | tools,
33 | utils
34 | Suggests:
35 | covr,
36 | farver,
37 | ggplot2,
38 | graphics,
39 | knitr,
40 | ragg,
41 | rmarkdown,
42 | svglite,
43 | testthat (>= 2.1.0)
44 | LinkingTo:
45 | cpp11 (>= 0.2.1)
46 | VignetteBuilder:
47 | knitr
48 | Config/build/compilation-database: true
49 | Config/Needs/website: tidyverse/tidytemplate
50 | Config/usethis/last-upkeep: 2025-04-23
51 | Encoding: UTF-8
52 | Roxygen: list(markdown = TRUE)
53 | RoxygenNote: 7.3.2
54 | SystemRequirements: fontconfig, freetype2
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2025
2 | COPYRIGHT HOLDER: systemfonts authors
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2025 systemfonts authors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | S3method("$",font_variation)
4 | S3method("$<-",font_variation)
5 | S3method("[",font_variation)
6 | S3method("[<-",font_variation)
7 | S3method("[[",font_variation)
8 | S3method("[[<-",font_variation)
9 | S3method(c,font_feature)
10 | S3method(format,font_feature)
11 | S3method(format,font_variation)
12 | S3method(length,font_feature)
13 | S3method(length,font_variation)
14 | S3method(names,font_variation)
15 | S3method(print,font_feature)
16 | S3method(print,font_variation)
17 | export(add_fonts)
18 | export(as_font_weight)
19 | export(as_font_width)
20 | export(clear_local_fonts)
21 | export(clear_registry)
22 | export(font_fallback)
23 | export(font_feature)
24 | export(font_info)
25 | export(font_variation)
26 | export(fonts_as_import)
27 | export(get_fallback)
28 | export(get_from_font_squirrel)
29 | export(get_from_google_fonts)
30 | export(glyph_info)
31 | export(glyph_outline)
32 | export(glyph_raster)
33 | export(glyph_raster_grob)
34 | export(match_font)
35 | export(match_fonts)
36 | export(plot_glyph_stats)
37 | export(register_font)
38 | export(register_variant)
39 | export(registry_fonts)
40 | export(require_font)
41 | export(reset_font_cache)
42 | export(scan_local_fonts)
43 | export(search_web_fonts)
44 | export(shape_string)
45 | export(str_split_emoji)
46 | export(string_metrics_dev)
47 | export(string_width)
48 | export(string_widths_dev)
49 | export(system_fonts)
50 | importFrom(lifecycle,deprecated)
51 | useDynLib(systemfonts, .registration = TRUE)
52 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # systemfonts (development version)
2 |
3 | * Fixed a bug in the URL generation for Google Font imports
4 | * Added support for Bunny Fonts imports (#132)
5 | * Begin deprecation of `bold` argument in favour of `weight` throughout package
6 | * Improve messaging in `require_font()`
7 | * Fonts are automatically added to the session when an import is created (#131)
8 | * Fixed a bug in converting font weights as reported by macOS into ISO-style
9 | weight used by systemfonts and FreeType
10 | * `require_font()` now better handles lack of internet access
11 | * Added `plot_glyph_stats()` to provide visual explanation for various glyph
12 | measures
13 | * `font_info()` now returns the PostScript name of the font in the `name` column
14 | * Added support for variable fonts throughout the package. Other packages will
15 | need to be upgraded to take advantage of this. A new function
16 | `font_variation()` can be used to define coords for the variation axes
17 | * Fixed a bug in webfont download on Windows where fontfiles would become
18 | corrupted (#134)
19 | * Fixed an issue in textshaping where conflicting DLL names resulted in the R
20 | process inability to render text if textshaping was loaded first
21 | (textshaping#36)
22 |
23 | # systemfonts 1.2.3
24 |
25 | * Added `fonts_as_import()` to create stylesheet urls for embedding of fonts in
26 | HTML and SVG
27 | * Added two C-level functions for getting glyph outline and bitmap information
28 |
29 | # systemfonts 1.2.2
30 |
31 | * Fix compilation on macOS when the obj-c++ compiler fails to pick up the right
32 | obj-c++ version (#122)
33 | * `add_fonts()` now supports urls as well as file paths (#124)
34 |
35 | # systemfonts 1.2.1
36 |
37 | * Fix a memory issue when adding new fonts with `add_fonts()`
38 | * Default to not downloading woff2 files from Google Fonts since it is poorly
39 | supported on many systems
40 | * Fixed a bug in `get_from_font_squirrel()` where the font wasn't placed in the
41 | user specified location
42 |
43 | # systemfonts 1.2.0
44 |
45 | * Providing the font name as the family should now result in better matching
46 | * Improved the fallback options for Windows so that as many scripts are now
47 | covered
48 | * Add infrastructure to add uninstalled font files to the search path used for
49 | font matching
50 | * Add facilities to download and register fonts from web repositories such as
51 | Google Fonts and Font Squirrel
52 | * Add `require_font()` that does it's best to ensure that a given font is
53 | available after being called.
54 | * Added functions for extracting outline and raster representation of glyphs
55 |
56 | # systemfonts 1.1.0
57 |
58 | * `match_fonts()` have been added as a vectorized and generalized version of
59 | `match_font()`. In the process `match_font()` has been deprecated in favour of
60 | `match_fonts()`
61 | * Two internal functions for converting weight and width names to integers have
62 | been exported
63 | * Fix a segfault on macOS when the system encounters a corrupted font collection
64 | (#113)
65 |
66 | # systemfonts 1.0.6
67 |
68 | * Fix a bug in `shape_string()` using `vjust = 1` (#85)
69 |
70 | # systemfonts 1.0.4
71 |
72 | * Use Courier New as default mono font on macOS instead of Courier to avoid
73 | issues between FreeType and Courier (#105)
74 |
75 | # systemfonts 1.0.4
76 |
77 | * Provide a fallback solution to the setup of the CRAN windows builder so that
78 | fonts can be discovered (#87)
79 |
80 | # systemfonts 1.0.3
81 |
82 | * Avoid warning when including the systemfonts header (#77)
83 | * Fix size selection of non-scalable fonts when the requested size is bigger
84 | than the available
85 | * Fix compilation bug when systemfont is used in C packages (#76)
86 |
87 | # systemfonts 1.0.2
88 |
89 | * Ensure compitability with freetype <= 2.4.11 (#70, @jan-glx)
90 | * Prepare for UCRT compilation
91 |
92 | # systemfonts 1.0.1
93 |
94 | * Fix a bug in font matching on Windows when matching monospace fonts
95 | * Fix a bug in `reset_font_cache()` on mac that would cause a system crash if
96 | the cache was not filled in advance (#67)
97 |
98 | # systemfonts 1.0.0
99 |
100 | * Tweak size determination for non-scalable fonts
101 | * Fix bug when switching between scalable and non-scalable fonts in the cache
102 | * Add utility for querying font fallbacks at both the R and C level
103 | * Add C-level API for finding emoji embeddings in strings
104 | * Add utility for getting weight of font from C code
105 | * Add utility for getting family name of font from C code
106 | * Add font weight and width to the output of `font_info()`
107 |
108 | # systemfonts 0.3.2
109 |
110 | * Fix compiled code for old R versions
111 | * Changes to comply with next cpp11 version
112 |
113 | # systemfonts 0.3.1
114 |
115 | * Fixed warnings on CRAN LTO machine
116 |
117 | # systemfonts 0.3.0
118 |
119 | * Added `get_cached_face()` so that other packages might retrieve FT_Face
120 | objects from the cache.
121 | * Adapted cpp11
122 | * Add infrastructure for setting OpenType font features on a registered font with
123 | either `register_font()` or the new `register_variant()`, along with the
124 | `font_feature()` function.
125 |
126 | # systemfonts 0.2.3
127 |
128 | * Replace the buggy Freetype cache subsystem with own implementation
129 | * Fix indexing bug in `glyph_metrics()`
130 |
131 | # systemfonts 0.2.2
132 |
133 | * Fix remaining valgrind issues by fixing the included font-manager code
134 | * Rewrite the text shaping algorithm to make it more future proof
135 | * Work around a nasty freetype bug in their cache subsystem
136 |
137 | # systemfonts 0.2.1
138 |
139 | * Various fixes to the correctness of compiled code
140 |
141 | # systemfonts 0.2.0
142 |
143 | * Add `string_widths_dev()` and `string_metrics_dev()` to request the current
144 | graphic device for string widths and metrics.
145 | * Add system for registering non-system fonts for look-up.
146 | * systemfonts will now detect user-installed fonts on Windows
147 | (possible after the 1806 update)
148 | * Font lookup is now cached for faster performance. The caching will get flushed
149 | when new fonts are added to the registry, or manually with `reset_font_cache()`
150 | * Systemfonts now provide querying of font information with `font_info()` and
151 | `glyph_info()`
152 | * Basic string shaping is now provided with `shape_string()`
153 | * Line width calculation is now available with `string_width()` (ignores
154 | presence of newlines, use `shape_string()` for more complicated strings)
155 | * Added `str_split_emoji()` for splitting of strings into substrings of emoji
156 | and non-emoji glyphs
157 | * Provide a header file for easy use from within C in other packages
158 | * Fix memory management issues on Mac
159 | * Fix handling of erroneous font files on windows
160 |
161 | # systemfonts 0.1.1
162 |
163 | * Fix compilation on systems with a very old fontconfig version (Solaris)
164 |
165 | # systemfonts 0.1.0
166 |
167 | * First version with `match_font()` and `system_fonts()` capabilities. More to
168 | come.
169 | * Added a `NEWS.md` file to track changes to the package.
170 |
--------------------------------------------------------------------------------
/R/cpp11.R:
--------------------------------------------------------------------------------
1 | # Generated by cpp11: do not edit by hand
2 |
3 | dev_string_widths_c <- function(string, family, face, size, cex, unit) {
4 | .Call(`_systemfonts_dev_string_widths_c`, string, family, face, size, cex, unit)
5 | }
6 |
7 | dev_string_metrics_c <- function(string, family, face, size, cex, unit) {
8 | .Call(`_systemfonts_dev_string_metrics_c`, string, family, face, size, cex, unit)
9 | }
10 |
11 | load_emoji_codes_c <- function(all, default_text, base_mod) {
12 | invisible(.Call(`_systemfonts_load_emoji_codes_c`, all, default_text, base_mod))
13 | }
14 |
15 | emoji_split_c <- function(string, path, index) {
16 | .Call(`_systemfonts_emoji_split_c`, string, path, index)
17 | }
18 |
19 | get_fallback_c <- function(path, index, string, variations) {
20 | .Call(`_systemfonts_get_fallback_c`, path, index, string, variations)
21 | }
22 |
23 | add_local_fonts <- function(paths) {
24 | .Call(`_systemfonts_add_local_fonts`, paths)
25 | }
26 |
27 | clear_local_fonts_c <- function() {
28 | invisible(.Call(`_systemfonts_clear_local_fonts_c`))
29 | }
30 |
31 | match_font_c <- function(family, italic, bold) {
32 | .Call(`_systemfonts_match_font_c`, family, italic, bold)
33 | }
34 |
35 | locate_fonts_c <- function(family, italic, weight, width) {
36 | .Call(`_systemfonts_locate_fonts_c`, family, italic, weight, width)
37 | }
38 |
39 | system_fonts_c <- function() {
40 | .Call(`_systemfonts_system_fonts_c`)
41 | }
42 |
43 | reset_font_cache_c <- function() {
44 | invisible(.Call(`_systemfonts_reset_font_cache_c`))
45 | }
46 |
47 | get_font_info_c <- function(path, index, size, res, variations) {
48 | .Call(`_systemfonts_get_font_info_c`, path, index, size, res, variations)
49 | }
50 |
51 | get_glyph_info_c <- function(glyphs, path, index, size, res, variations) {
52 | .Call(`_systemfonts_get_glyph_info_c`, glyphs, path, index, size, res, variations)
53 | }
54 |
55 | get_glyph_outlines <- function(glyph, path, index, size, variations, tolerance, verbose) {
56 | .Call(`_systemfonts_get_glyph_outlines`, glyph, path, index, size, variations, tolerance, verbose)
57 | }
58 |
59 | get_glyph_bitmap <- function(glyph, path, index, size, res, variations, color, verbose) {
60 | .Call(`_systemfonts_get_glyph_bitmap`, glyph, path, index, size, res, variations, color, verbose)
61 | }
62 |
63 | register_font_c <- function(family, paths, indices, features, settings) {
64 | invisible(.Call(`_systemfonts_register_font_c`, family, paths, indices, features, settings))
65 | }
66 |
67 | clear_registry_c <- function() {
68 | invisible(.Call(`_systemfonts_clear_registry_c`))
69 | }
70 |
71 | registry_fonts_c <- function() {
72 | .Call(`_systemfonts_registry_fonts_c`)
73 | }
74 |
75 | axes_to_tags <- function(axes) {
76 | .Call(`_systemfonts_axes_to_tags`, axes)
77 | }
78 |
79 | tags_to_axes <- function(tags) {
80 | .Call(`_systemfonts_tags_to_axes`, tags)
81 | }
82 |
83 | values_to_fixed <- function(values) {
84 | .Call(`_systemfonts_values_to_fixed`, values)
85 | }
86 |
87 | fixed_to_values <- function(fixed) {
88 | .Call(`_systemfonts_fixed_to_values`, fixed)
89 | }
90 |
91 | get_string_shape_c <- function(string, id, path, index, size, res, lineheight, align, hjust, vjust, width, tracking, indent, hanging, space_before, space_after) {
92 | .Call(`_systemfonts_get_string_shape_c`, string, id, path, index, size, res, lineheight, align, hjust, vjust, width, tracking, indent, hanging, space_before, space_after)
93 | }
94 |
95 | get_line_width_c <- function(string, path, index, size, res, include_bearing) {
96 | .Call(`_systemfonts_get_line_width_c`, string, path, index, size, res, include_bearing)
97 | }
98 |
--------------------------------------------------------------------------------
/R/dev_strings.R:
--------------------------------------------------------------------------------
1 | #' Get string widths as measured by the current device
2 | #'
3 | #' For certain composition tasks it is beneficial to get the width of a string
4 | #' as interpreted by the device that is going to plot it. grid provides this
5 | #' through construction of a `textGrob` and then converting the corresponding
6 | #' grob width to e.g. cm, but this comes with a huge overhead.
7 | #' `string_widths_dev()` provides direct, vectorised, access to the graphic
8 | #' device for as high performance as possible.
9 | #'
10 | #' @param strings A character vector of strings to measure
11 | #' @param family The font families to use. Will get recycled
12 | #' @param face The font faces to use. Will get recycled
13 | #' @param size The font size to use. Will get recycled
14 | #' @param cex The cex multiplier to use. Will get recycled
15 | #' @param unit The unit to return the width in. Either `"cm"`, `"inches"`,
16 | #' `"device"`, or `"relative"`
17 | #'
18 | #' @return A numeric vector with the width of each of the strings given in
19 | #' `strings` in the unit given in `unit`
20 | #'
21 | #' @export
22 | #'
23 | #' @family device metrics
24 | #'
25 | #' @examples
26 | #' # Get the widths as measured in cm (default)
27 | #' string_widths_dev(c('a string', 'an even longer string'))
28 | #'
29 | string_widths_dev <- function(
30 | strings,
31 | family = '',
32 | face = 1,
33 | size = 12,
34 | cex = 1,
35 | unit = 'cm'
36 | ) {
37 | unit <- match.arg(unit, possible_units)
38 | unit <- match(unit, possible_units) - 1L
39 | n_total <- length(strings)
40 | if (length(family) != 1) family <- rep_len(family, n_total)
41 | if (any(c(length(face), length(size), length(cex)) != 1)) {
42 | face <- rep_len(face, n_total)
43 | size <- rep_len(size, n_total)
44 | cex <- rep_len(cex, n_total)
45 | }
46 | dev_string_widths_c(
47 | as.character(strings),
48 | as.character(family),
49 | as.integer(face),
50 | as.numeric(size),
51 | as.numeric(cex),
52 | unit
53 | )
54 | }
55 | #' Get string metrics as measured by the current device
56 | #'
57 | #' This function is much like [string_widths_dev()] but also returns the ascent
58 | #' and descent of the string making it possible to construct a tight bounding
59 | #' box around the string.
60 | #'
61 | #' @inheritParams string_widths_dev
62 | #'
63 | #' @return A data.frame with `width`, `ascent`, and `descent` columns giving the
64 | #' metrics in the requested unit.
65 | #'
66 | #' @family device metrics
67 | #'
68 | #' @export
69 | #'
70 | #' @examples
71 | #' # Get the metrics as measured in cm (default)
72 | #' string_metrics_dev(c('some text', 'a string with descenders'))
73 | #'
74 | string_metrics_dev <- function(
75 | strings,
76 | family = '',
77 | face = 1,
78 | size = 12,
79 | cex = 1,
80 | unit = 'cm'
81 | ) {
82 | unit <- match.arg(unit, possible_units)
83 | unit <- match(unit, possible_units) - 1L
84 | n_total <- length(strings)
85 | if (length(family) != 1) family <- rep_len(family, n_total)
86 | if (any(c(length(face), length(size), length(cex)) != 1)) {
87 | face <- rep_len(face, n_total)
88 | size <- rep_len(size, n_total)
89 | cex <- rep_len(cex, n_total)
90 | }
91 | dev_string_metrics_c(
92 | as.character(strings),
93 | as.character(family),
94 | as.integer(face),
95 | as.numeric(size),
96 | as.numeric(cex),
97 | unit
98 | )
99 | }
100 |
101 | # Order important. Will get converted to 0-indexed unit identity for C code
102 | possible_units <- c('cm', 'inches', 'device', 'relative')
103 |
--------------------------------------------------------------------------------
/R/emoji.R:
--------------------------------------------------------------------------------
1 | #' Split a string into emoji and non-emoji glyph runs
2 | #'
3 | #' In order to do correct text rendering, the font needed must be figured out. A
4 | #' common case is rendering of emojis within a string where the system emoji
5 | #' font is used rather than the requested font. This function will inspect the
6 | #' provided strings and split them up in runs that must be rendered with the
7 | #' emoji font, and the rest. Arguments are recycled to the length of the `string`
8 | #' vector.
9 | #'
10 | #' @param string A character vector of strings that should be splitted.
11 | #' @inheritParams match_font
12 | #' @param path,index path an index of a font file to circumvent lookup based on
13 | #' family and style
14 | #'
15 | #' @return A data.frame containing the following columns:
16 | #' \describe{
17 | #' \item{string}{The substring containing a consecutive run of glyphs}
18 | #' \item{id}{The index into the original `string` vector that the substring is part of}
19 | #' \item{emoji}{A logical vector giving if the substring is a run of emojis or not}
20 | #' }
21 | #'
22 | #' @export
23 | #'
24 | #' @examples
25 | #' emoji_string <- "This is a joke\U0001f642. It should be obvious from the smiley"
26 | #' str_split_emoji(emoji_string)
27 | #'
28 | str_split_emoji <- function(
29 | string,
30 | family = '',
31 | italic = FALSE,
32 | bold = FALSE,
33 | path = NULL,
34 | index = 0
35 | ) {
36 | n_strings <- length(string)
37 | if (is.null(path)) {
38 | fonts <- match_fonts(
39 | rep_len(family, n_strings),
40 | rep_len(italic, n_strings),
41 | ifelse(rep_len(bold, n_strings), "bold", "normal")
42 | )
43 | path <- fonts$path
44 | index <- fonts$index
45 | } else {
46 | if (!all(c(length(path), length(index)) == 1)) {
47 | path <- rep_len(path, n_strings)
48 | index <- rep_len(index, n_strings)
49 | }
50 | }
51 | if (!all(file.exists(path)))
52 | stop("path must point to a valid file", call. = FALSE)
53 | emoji_splitted <- emoji_split_c(as.character(string), path, index)
54 | groups <- diff(c(
55 | 0,
56 | which(diff(emoji_splitted[[3]]) != 0),
57 | length(emoji_splitted[[3]])
58 | ))
59 | groups <- rep(seq_along(groups), groups)
60 | groups <- paste0(emoji_splitted[[2]], '_', groups)
61 | string <- vapply(split(emoji_splitted[[1]], groups), intToUtf8, character(1))
62 | id <- vapply(split(emoji_splitted[[2]], groups), `[[`, integer(1), 1)
63 | emoji <- vapply(split(emoji_splitted[[3]], groups), `[[`, logical(1), 1)
64 | res <- data.frame(
65 | string = string,
66 | id = id + 1,
67 | emoji = emoji,
68 | stringsAsFactors = FALSE,
69 | row.names = NULL
70 | )
71 | class(res) <- c("tbl_df", "tbl", "data.frame")
72 | res
73 | }
74 |
75 | load_emoji_codes <- function() {
76 | load_emoji_codes_c(
77 | as.integer(all_emoji),
78 | as.integer(text_pres_emoji),
79 | as.integer(base_mod_emoji)
80 | )
81 | }
82 |
--------------------------------------------------------------------------------
/R/font_fallback.R:
--------------------------------------------------------------------------------
1 | #' Get the fallback font for a given string
2 | #'
3 | #' A fallback font is a font to use as a substitute if the chosen font does not
4 | #' contain the requested characters. Using font fallbacks means that the user
5 | #' doesn't have to worry about mixing characters from different scripts or
6 | #' mixing text and emojies. Fallback is calculated for the full string and the
7 | #' result is platform specific. If no font covers all the characters in the
8 | #' string an undefined "best match" is returned. The best approach is to figure
9 | #' out which characters are not covered by your chosen font and figure out
10 | #' fallbacks for these, rather than just request a fallback for the full string.
11 | #'
12 | #' @param string The strings to find fallbacks for
13 | #' @inheritParams font_info
14 | #'
15 | #' @return A data frame with a `path` and `index` column giving fallback for the
16 | #' specified string and font combinations
17 | #'
18 | #' @export
19 | #'
20 | #' @examples
21 | #' font_fallback("\U0001f604") # Smile emoji
22 | #'
23 | font_fallback <- function(
24 | string,
25 | family = '',
26 | italic = FALSE,
27 | weight = "normal",
28 | width = "undefined",
29 | path = NULL,
30 | index = 0,
31 | variation = font_variation(),
32 | bold = deprecated()
33 | ) {
34 | if (lifecycle::is_present(bold)) {
35 | lifecycle::deprecate_soft(
36 | "1.2.4",
37 | "font_fallback(bold)",
38 | "font_fallback(weight)"
39 | )
40 | weight <- ifelse(bold, "bold", "normal")
41 | }
42 | if (is_font_variation(variation)) variation <- list(variation)
43 | full_length <- length(string)
44 | if (is.null(path)) {
45 | fonts <- match_fonts(
46 | family = rep_len(family, full_length),
47 | italic = rep_len(italic, full_length),
48 | weight = rep_len(weight, full_length),
49 | width = rep_len(width, full_length)
50 | )
51 | path <- fonts$path
52 | index <- fonts$index
53 | } else {
54 | full_length <- max(length(path), length(index), full_length)
55 | if (!all(c(length(path), length(index)) == 1)) {
56 | path <- rep_len(path, full_length)
57 | index <- rep_len(index, full_length)
58 | }
59 | }
60 | if (length(string) != 1) string <- rep_len(string, full_length)
61 | variation <- rep_len(variation, full_length)
62 | if (!all(file.exists(path)))
63 | stop("path must point to a valid file", call. = FALSE)
64 | get_fallback_c(path, as.integer(index), as.character(string), variation)
65 | }
66 |
--------------------------------------------------------------------------------
/R/sysdata.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-lib/systemfonts/7e1acd4acc5cf998e854869131b12eb6f0903500/R/sysdata.rda
--------------------------------------------------------------------------------
/R/system_fonts.R:
--------------------------------------------------------------------------------
1 | #' List all fonts installed on your system
2 | #'
3 | #' @return A data frame with a row for each font and various information in each
4 | #' column
5 | #'
6 | #' @export
7 | #'
8 | #' @examples
9 | #' # See all monospace fonts
10 | #' fonts <- system_fonts()
11 | #' fonts[fonts$monospace, ]
12 | #'
13 | system_fonts <- function() {
14 | system_fonts_c()
15 | }
16 |
17 | #' Reset the system font cache
18 | #'
19 | #' Building the list of system fonts is time consuming and is therefore cached.
20 | #' This, in turn, means that changes to the system fonts (i.e. installing new
21 | #' fonts), will not propagate to systemfonts. The solution is to reset the
22 | #' cache, which will result in the next call to e.g. [match_fonts()] will
23 | #' trigger a rebuild of the cache.
24 | #'
25 | #' @export
26 | #'
27 | #' @examples
28 | #' all_fonts <- system_fonts()
29 | #'
30 | #' ##-- Install a new font on the system --##
31 | #'
32 | #' all_fonts_new <- system_fonts()
33 | #'
34 | #' ## all_fonts_new will be equal to all_fonts
35 | #'
36 | #' reset_font_cache()
37 | #'
38 | #' all_fonts_new <- system_fonts()
39 | #'
40 | #' ## all_fonts_new will now contain the new font
41 | #'
42 | reset_font_cache <- function() {
43 | reset_font_cache_c()
44 | }
45 |
--------------------------------------------------------------------------------
/R/systemfonts-package.R:
--------------------------------------------------------------------------------
1 | #' @keywords internal
2 | #' @useDynLib systemfonts, .registration = TRUE
3 | "_PACKAGE"
4 |
5 | # The following block is used by usethis to automatically manage
6 | # roxygen namespace tags. Modify with care!
7 | ## usethis namespace: start
8 | #' @importFrom lifecycle deprecated
9 | ## usethis namespace: end
10 | NULL
11 |
--------------------------------------------------------------------------------
/R/zzz.R:
--------------------------------------------------------------------------------
1 | release_bullets <- function() {
2 | c(
3 | '`rhub::check_on_solaris(env_vars = c("_R_CHECK_FORCE_SUGGESTS_" = "false"))`',
4 | '`rhub::check_with_valgrind(env_vars = c(VALGRIND_OPTS = "--leak-check=full --track-origins=yes"))`'
5 | )
6 | }
7 |
8 | .onLoad <- function(...) {
9 | load_emoji_codes()
10 | scan_local_fonts()
11 | windows_workaround()
12 | }
13 |
14 | warn_env <- new.env(parent = emptyenv())
15 | warn_env$warned <- FALSE
16 |
17 | #' Get location of the fallback font
18 | #'
19 | #' @export
20 | #' @keywords internal
21 | get_fallback <- function() {
22 | if (!warn_env$warned) {
23 | warning(
24 | "No fonts detected on your system. Using an empty font.",
25 | call. = FALSE
26 | )
27 | warn_env$warned <- TRUE
28 | }
29 | list(
30 | system.file("unfont.ttf", package = "systemfonts"),
31 | 0L
32 | )
33 | }
34 |
35 | # See https://github.com/r-lib/textshaping/issues/36
36 | windows_workaround <- function(){
37 | # This dll needs to be loaded before the textshaping pkg so we do it here.
38 | # It is harmless otherwise (it is also loaded by e.g. RGui in Windows)
39 | if(.Platform$OS.type == 'windows'){
40 | if(file.exists("C:\\Windows\\System32\\TextShaping.dll")){
41 | dyn.load("C:\\Windows\\System32\\TextShaping.dll")
42 | }
43 | }
44 | }
45 |
46 | `%||%` <- function(a, b) if (is.null(a)) b else a
47 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output: github_document
3 | ---
4 |
5 |
6 |
7 | ```{r, include = FALSE}
8 | knitr::opts_chunk$set(
9 | collapse = TRUE,
10 | comment = "#>",
11 | fig.path = "man/figures/README-",
12 | out.width = "100%"
13 | )
14 | ```
15 | # systemfonts
16 |
17 |
18 | [](https://cran.r-project.org/package=systemfonts)
19 | [](https://lifecycle.r-lib.org/articles/stages.html)
20 | [](https://github.com/r-lib/systemfonts/actions/workflows/R-CMD-check.yaml)
21 | [](https://app.codecov.io/gh/r-lib/systemfonts)
22 |
23 |
24 | systemfonts is a package that locates installed fonts. It uses the system-native libraries on Mac (CoreText) and Linux (FontConfig), and uses Freetype to parse the fonts in the registry on Windows.
25 |
26 | ## Installation
27 | systemfonts is available from CRAN using `install.packages('systemfonts')`. It
28 | is however still under development and you can install the development version
29 | using devtools.
30 |
31 | ```{r, eval=FALSE}
32 | # install.packages('pak')
33 | pak::pak('r-lib/systemfonts')
34 | ```
35 |
36 | ## Examples
37 | The main use of this package is to locate font files based on family and style:
38 |
39 | ```{r}
40 | library(systemfonts)
41 |
42 | match_fonts('Avenir', italic = TRUE)
43 | ```
44 |
45 | This function returns the path to the file holding the font, as well as the
46 | 0-based index of the font in the file.
47 |
48 | It is also possible to get a data.frame of all available fonts:
49 |
50 | ```{r, include=FALSE}
51 | library(tibble)
52 | ```
53 | ```{r}
54 | system_fonts()
55 | ```
56 |
57 | Further, you can query additional information about fonts and specific glyphs,
58 | if that is of interest using the `font_info()` and `glyph_info()` functions.
59 |
60 | ### Custom fonts
61 | While the package was created to provide transparent access to fonts installed
62 | on the system, it has grown to also provide ways to work with font files not
63 | part of the system installation. This is especially beneficial if you are
64 | running code on a system where you don't have administrator rights and need to
65 | use a custom font.
66 |
67 | systemfonts provide the `add_fonts()` function which takes a vector of file
68 | paths and add these to the lookup database without installing them. Further,
69 | systemfonts automatically looks in the `./fonts` and `~/fonts` folders and adds
70 | any font files located there during startup. This means that you can distribute
71 | a script along with a fonts directory and have those fonts automatically
72 | available during execution of the script.
73 |
74 | In addition to the above, systemfonts also provides access to online font
75 | repositories such as [Google Fonts](https://fonts.google.com) and can search and
76 | download from these, automatically adding the downloaded fonts to the lookup
77 | database.
78 |
79 | All these functionalities are condensed into a single function,
80 | `require_font()`, which allows you to state a font dependency inside a script.
81 | The function will first look for the font on the system, and failing that, will
82 | try to fetch it from an online repository. If that fails it will either throw an
83 | error or remap the font to another of the developers choosing.
84 |
85 |
86 | ## C API
87 | While getting this information in R is nice, the intended use is mostly through
88 | compiled code so that graphic devices can easily locate relevant font files etc.
89 |
90 | In order to use functions from systemfonts in C(++) code your package should
91 | list systemfonts in the `LinkingTo` field in the `DESCRIPTION` file. Once this
92 | is done you can now `#include ` in your code and use the provided
93 | functions. Look into the
94 | [`inst/include/systemfonts.h`](https://github.com/r-lib/systemfonts/blob/master/inst/include/systemfonts.h)
95 | file to familiarise yourself with the C API.
96 |
97 | ## System Defaults
98 | systemfonts will always try to find a font for you, even if none exist with the
99 | given family name or style. How it resolves this is system specific and should
100 | not be relied on, but it can be expected that a valid font file is always
101 | returned no matter the input.
102 |
103 | A few special aliases exist that behaves predictably but system dependent:
104 |
105 | - `""` and `"sans"` return *Helvetica* on Mac, *Arial* on Windows, and the
106 | default sans-serif font on Linux (*DejaVu Sans* on Ubuntu)
107 | - `"serif"` return *Times* on Mac, *Times New Roman* on Windows, and the
108 | default serif font on Linux (*DejaVu Serif* on Ubuntu)
109 | - `"mono"` return *Courier* on Mac, *Courier New* on Windows, and the
110 | default mono font on Linux (*DejaVu Mono* on Ubuntu)
111 | - `"emoji"` return *Apple Color Emoji* on Mac, *Segoe UI Emoji* on Windows, and the
112 | default emoji font on Linux (*Noto Color* on Ubuntu)
113 |
114 | ## Code of Conduct
115 | Please note that the 'systemfonts' project is released with a
116 | [Contributor Code of Conduct](https://github.com/r-lib/systemfonts/blob/master/CODE_OF_CONDUCT.md).
117 | By contributing to this project, you agree to abide by its terms.
118 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # systemfonts
5 |
6 |
7 |
8 | [](https://cran.r-project.org/package=systemfonts)
10 | [](https://lifecycle.r-lib.org/articles/stages.html)
12 | [](https://github.com/r-lib/systemfonts/actions/workflows/R-CMD-check.yaml)
13 | [](https://app.codecov.io/gh/r-lib/systemfonts)
15 |
16 |
17 | systemfonts is a package that locates installed fonts. It uses the
18 | system-native libraries on Mac (CoreText) and Linux (FontConfig), and
19 | uses Freetype to parse the fonts in the registry on Windows.
20 |
21 | ## Installation
22 |
23 | systemfonts is available from CRAN using
24 | `install.packages('systemfonts')`. It is however still under development
25 | and you can install the development version using devtools.
26 |
27 | ``` r
28 | # install.packages('pak')
29 | pak::pak('r-lib/systemfonts')
30 | ```
31 |
32 | ## Examples
33 |
34 | The main use of this package is to locate font files based on family and
35 | style:
36 |
37 | ``` r
38 | library(systemfonts)
39 |
40 | match_fonts('Avenir', italic = TRUE)
41 | #> path index features
42 | #> 1 /System/Library/Fonts/Avenir.ttc 1
43 | ```
44 |
45 | This function returns the path to the file holding the font, as well as
46 | the 0-based index of the font in the file.
47 |
48 | It is also possible to get a data.frame of all available fonts:
49 |
50 | ``` r
51 | system_fonts()
52 | #> # A tibble: 909 × 9
53 | #> path index name family style weight width italic monospace
54 | #>
55 | #> 1 /Users/thomas/fonts/B… 0 Barr… Barrio Regu… normal norm… FALSE FALSE
56 | #> 2 /System/Library/Fonts… 2 Rock… Rockw… Bold bold norm… FALSE FALSE
57 | #> 3 /Users/thomas/Library… 0 Open… Open … Ligh… light norm… TRUE FALSE
58 | #> 4 /Users/thomas/Library… 0 Open… Open … Semi… semib… semi… TRUE FALSE
59 | #> 5 /System/Library/Fonts… 0 Note… Notew… Light normal norm… FALSE FALSE
60 | #> 6 /Users/thomas/Library… 0 Taja… Tajaw… Regu… light norm… FALSE FALSE
61 | #> 7 /System/Library/Fonts… 1 Deva… Devan… Bold bold norm… FALSE FALSE
62 | #> 8 /System/Library/Fonts… 0 Kann… Kanna… Regu… normal norm… FALSE FALSE
63 | #> 9 /System/Library/Fonts… 0 Verd… Verda… Bold bold norm… FALSE FALSE
64 | #> 10 /System/Library/Fonts… 8 Aria… Arial… Light light norm… FALSE FALSE
65 | #> # ℹ 899 more rows
66 | ```
67 |
68 | Further, you can query additional information about fonts and specific
69 | glyphs, if that is of interest using the `font_info()` and
70 | `glyph_info()` functions.
71 |
72 | ### Custom fonts
73 |
74 | While the package was created to provide transparent access to fonts
75 | installed on the system, it has grown to also provide ways to work with
76 | font files not part of the system installation. This is especially
77 | beneficial if you are running code on a system where you don’t have
78 | administrator rights and need to use a custom font.
79 |
80 | systemfonts provide the `add_fonts()` function which takes a vector of
81 | file paths and add these to the lookup database without installing them.
82 | Further, systemfonts automatically looks in the `./fonts` and `~/fonts`
83 | folders and adds any font files located there during startup. This means
84 | that you can distribute a script along with a fonts directory and have
85 | those fonts automatically available during execution of the script.
86 |
87 | In addition to the above, systemfonts also provides access to online
88 | font repositories such as [Google Fonts](https://fonts.google.com) and
89 | can search and download from these, automatically adding the downloaded
90 | fonts to the lookup database.
91 |
92 | All these functionalities are condensed into a single function,
93 | `require_font()`, which allows you to state a font dependency inside a
94 | script. The function will first look for the font on the system, and
95 | failing that, will try to fetch it from an online repository. If that
96 | fails it will either throw an error or remap the font to another of the
97 | developers choosing.
98 |
99 | ## C API
100 |
101 | While getting this information in R is nice, the intended use is mostly
102 | through compiled code so that graphic devices can easily locate relevant
103 | font files etc.
104 |
105 | In order to use functions from systemfonts in C(++) code your package
106 | should list systemfonts in the `LinkingTo` field in the `DESCRIPTION`
107 | file. Once this is done you can now `#include ` in your
108 | code and use the provided functions. Look into the
109 | [`inst/include/systemfonts.h`](https://github.com/r-lib/systemfonts/blob/master/inst/include/systemfonts.h)
110 | file to familiarise yourself with the C API.
111 |
112 | ## System Defaults
113 |
114 | systemfonts will always try to find a font for you, even if none exist
115 | with the given family name or style. How it resolves this is system
116 | specific and should not be relied on, but it can be expected that a
117 | valid font file is always returned no matter the input.
118 |
119 | A few special aliases exist that behaves predictably but system
120 | dependent:
121 |
122 | - `""` and `"sans"` return *Helvetica* on Mac, *Arial* on Windows, and
123 | the default sans-serif font on Linux (*DejaVu Sans* on Ubuntu)
124 | - `"serif"` return *Times* on Mac, *Times New Roman* on Windows, and the
125 | default serif font on Linux (*DejaVu Serif* on Ubuntu)
126 | - `"mono"` return *Courier* on Mac, *Courier New* on Windows, and the
127 | default mono font on Linux (*DejaVu Mono* on Ubuntu)
128 | - `"emoji"` return *Apple Color Emoji* on Mac, *Segoe UI Emoji* on
129 | Windows, and the default emoji font on Linux (*Noto Color* on Ubuntu)
130 |
131 | ## Code of Conduct
132 |
133 | Please note that the ‘systemfonts’ project is released with a
134 | [Contributor Code of
135 | Conduct](https://github.com/r-lib/systemfonts/blob/master/CODE_OF_CONDUCT.md).
136 | By contributing to this project, you agree to abide by its terms.
137 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | url: https://systemfonts.r-lib.org
2 |
3 | template:
4 | package: tidytemplate
5 | bootstrap: 5
6 |
7 | includes:
8 | in_header: |
9 |
10 |
11 | navbar:
12 | structure:
13 | left: [intro, fonts, reference, news]
14 | components:
15 | fonts:
16 | text: Typography and R
17 | href: articles/fonts_basics.html
18 |
19 | reference:
20 | - title: Font Lookup
21 | desc: |
22 | systemfonts is primarily a package to facilitate the lookup of fonts based
23 | on typeface and style. These functions facilitate this
24 | contents:
25 | - match_fonts
26 | - font_fallback
27 | - system_fonts
28 | - reset_font_cache
29 | - title: Shaping
30 | desc: |
31 | While text shaping is better handed off to the [textshaping](https://github.com/r-lib/textshaping)
32 | package, systemfonts does provide basic shaping functionality
33 | contents:
34 | - shape_string
35 | - string_width
36 | - string_metrics_dev
37 | - string_widths_dev
38 | - str_split_emoji
39 | - title: Font file information
40 | desc: |
41 | The following functions allow you to extract various kind of information
42 | from font files
43 | contents:
44 | - font_info
45 | - glyph_info
46 | - glyph_outline
47 | - glyph_raster
48 | - glyph_raster_grob
49 | - plot_glyph_stats
50 | - title: Local fonts
51 | desc: |
52 | While systemfonts was mainly created to access fonts installed on the system
53 | it also has rich support for using non-installed (local) font files from R.
54 | Further, it provides facilities for accessing system fonts that are
55 | otherwise inaccessible from R
56 | contents:
57 | - add_fonts
58 | - register_variant
59 | - register_font
60 | - font_feature
61 | - font_variation
62 | - title: Web fonts
63 | desc: |
64 | Many fonts are now available online through font repositories. systemfonts
65 | provide access to these, both for installing and using directly, but also
66 | for embedding inside SVGs
67 | contents:
68 | - search_web_fonts
69 | - require_font
70 | - get_from_google_fonts
71 | - fonts_as_import
72 |
--------------------------------------------------------------------------------
/air.toml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-lib/systemfonts/7e1acd4acc5cf998e854869131b12eb6f0903500/air.toml
--------------------------------------------------------------------------------
/cleanup:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | rm -f src/Makevars configure.log
3 | rm -Rf .deps
4 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment: false
2 |
3 | coverage:
4 | status:
5 | project:
6 | default:
7 | target: auto
8 | threshold: 1%
9 | informational: true
10 | patch:
11 | default:
12 | target: auto
13 | threshold: 1%
14 | informational: true
15 |
--------------------------------------------------------------------------------
/configure:
--------------------------------------------------------------------------------
1 | # Anticonf (tm) script by Jeroen Ooms (2020)
2 | # This script will query 'pkg-config' for the required cflags and ldflags.
3 | # If pkg-config is unavailable or does not find the library, try setting
4 | # INCLUDE_DIR and LIB_DIR manually via e.g:
5 | # R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib'
6 |
7 | # Library settings
8 | SYS="UNIX"
9 | PKG_CONFIG_NAME="fontconfig freetype2"
10 | PKG_DEB_NAME="libfontconfig1-dev"
11 | PKG_RPM_NAME="fontconfig-devel"
12 | PKG_CSW_NAME="fontconfig_dev"
13 | PKG_BREW_NAME="freetype"
14 | PKG_TEST_HEADER=""
15 | PKG_LIBS="-lfontconfig -lfreetype"
16 | PKG_OBJCXXFLAGS=""
17 |
18 | # Alternative config on MacOS for native APIs
19 | if [ `uname` = "Darwin" ]; then
20 | SYS="DARWIN"
21 | PKG_CONFIG_NAME="--static freetype2"
22 | PKG_TEST_HEADER=""
23 | PKG_LIBS="-lfreetype"
24 | PKG_OBJCXXFLAGS="-fobjc-arc"
25 |
26 | RBIN="${R_HOME}/bin${R_ARCH_BIN}/R"
27 | OBJC=`"$RBIN" CMD config OBJC`
28 | if [ -z "$OBJC" ]; then
29 | echo "--------------------------- [SYSTEMFONTS] -----------------------------"
30 | echo "Configuration failed to find an Objective-C compiler."
31 | echo " systemfonts require the use of Objective-C code on macOS to access"
32 | echo " the system-native font matching API."
33 | echo " Please ensure that your build system is setup with an Objective-C"
34 | echo " compiler to install systemfonts on macOS"
35 | echo "-----------------------------------------------------------------------"
36 | exit 1
37 | fi
38 |
39 | fi
40 |
41 | # Use pkg-config if available
42 | if [ "`command -v pkg-config`" ]; then
43 | PKGCONFIG_CFLAGS=`pkg-config --cflags --silence-errors ${PKG_CONFIG_NAME}`
44 | PKGCONFIG_LIBS=`pkg-config --libs ${PKG_CONFIG_NAME}`
45 | fi
46 |
47 | # Note that cflags may be empty in case of success
48 | if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then
49 | echo "Found INCLUDE_DIR and/or LIB_DIR!"
50 | PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS"
51 | PKG_LIBS="-L$LIB_DIR $PKG_LIBS"
52 | elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then
53 | echo "Found pkg-config cflags and libs!"
54 | PKG_CFLAGS=${PKGCONFIG_CFLAGS}
55 | PKG_LIBS=${PKGCONFIG_LIBS}
56 | elif [ `uname` = "Darwin" ]; then
57 | test ! "$CI" && brew --version 2>/dev/null
58 | if [ $? -eq 0 ]; then
59 | BREWDIR=`brew --prefix`
60 | PKG_CFLAGS="-I$BREWDIR/include -I$BREWDIR/include/freetype2"
61 | else
62 | curl -sfL "https://autobrew.github.io/scripts/freetype" > autobrew
63 | . ./autobrew
64 | fi
65 | fi
66 |
67 | # For debugging
68 | echo "Using PKG_CFLAGS=$PKG_CFLAGS"
69 | echo "Using PKG_LIBS=$PKG_LIBS"
70 |
71 | # Find compiler
72 | CC=`${R_HOME}/bin/R CMD config CC`
73 | CFLAGS=`${R_HOME}/bin/R CMD config CFLAGS`
74 | CPPFLAGS=`${R_HOME}/bin/R CMD config CPPFLAGS`
75 |
76 | # Test configuration
77 | echo "#include $PKG_TEST_HEADER" | ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E -xc - >/dev/null 2>configure.log
78 |
79 | # Customize the error
80 | if [ $? -ne 0 ]; then
81 | echo "--------------------------- [ANTICONF] --------------------------------"
82 | echo "Configuration failed to find the $PKG_CONFIG_NAME library. Try installing:"
83 | echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)"
84 | echo " * rpm: $PKG_RPM_NAME (Fedora, EPEL)"
85 | echo " * csw: $PKG_CSW_NAME (Solaris)"
86 | echo " * brew: $PKG_BREW_NAME (OSX)"
87 | echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your"
88 | echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config"
89 | echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:"
90 | echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'"
91 | echo "-------------------------- [ERROR MESSAGE] ---------------------------"
92 | cat configure.log
93 | echo "--------------------------------------------------------------------"
94 | exit 1
95 | fi
96 |
97 | # Write to Makevars
98 | sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" -e "s|@SYS@|$SYS|g" -e "s|@objcflags@|$PKG_OBJCXXFLAGS|" src/Makevars.in > src/Makevars
99 |
100 | # Success
101 | exit 0
102 |
--------------------------------------------------------------------------------
/cran-comments.md:
--------------------------------------------------------------------------------
1 | Small update in anticipation of next svglite release
2 |
3 | ## revdepcheck results
4 |
5 | We checked 26 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package.
6 |
7 | * We saw 0 new problems
8 | * We failed to check 0 packages
9 |
--------------------------------------------------------------------------------
/inst/include/systemfonts-ft.h:
--------------------------------------------------------------------------------
1 | #ifndef SYSTEMFONTS_FT_H
2 | #define SYSTEMFONTS_FT_H
3 |
4 | #ifndef R_NO_REMAP
5 | #define R_NO_REMAP
6 | #endif
7 |
8 | #include
9 | #include FT_FREETYPE_H
10 | #include
11 | #include
12 |
13 | #include "systemfonts.h"
14 |
15 | // Retrieve an FT_Face from the cache and assigns it to the face pointer. The
16 | // retrieved face should be destroyed with FT_Done_Face once no longer needed.
17 | // Returns 0 if successful.
18 | static inline FT_Face get_cached_face(const char* fontfile, int index,
19 | double size, double res, int* error) {
20 | static FT_Face (*p_get_cached_face)(const char*, int, double, double, int*) = NULL;
21 | if (p_get_cached_face == NULL) {
22 | p_get_cached_face = (FT_Face (*)(const char*, int, double, double, int*)) R_GetCCallable("systemfonts", "get_cached_face");
23 | }
24 | return p_get_cached_face(fontfile, index, size, res, error);
25 | }
26 |
27 | namespace systemfonts {
28 | namespace ver2 {
29 | // Retrieve an FT_Face from the cache and assigns it to the face pointer. The
30 | // retrieved face should be destroyed with FT_Done_Face once no longer needed.
31 | // Returns 0 if successful.
32 | static inline FT_Face get_cached_face(const FontSettings2& font, double size, double res, int* error) {
33 | static FT_Face (*p_get_cached_face)(const FontSettings2&, double, double, int*) = NULL;
34 | if (p_get_cached_face == NULL) {
35 | p_get_cached_face = (FT_Face (*)(const FontSettings2&, double, double, int*)) R_GetCCallable("systemfonts", "get_cached_face2");
36 | }
37 | return p_get_cached_face(font, size, res, error);
38 | }
39 | }
40 | }
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/inst/unfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-lib/systemfonts/7e1acd4acc5cf998e854869131b12eb6f0903500/inst/unfont.ttf
--------------------------------------------------------------------------------
/man/add_fonts.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/register_font.R
3 | \name{add_fonts}
4 | \alias{add_fonts}
5 | \alias{scan_local_fonts}
6 | \alias{clear_local_fonts}
7 | \title{Add local font files to the search path}
8 | \usage{
9 | add_fonts(files)
10 |
11 | scan_local_fonts()
12 |
13 | clear_local_fonts()
14 | }
15 | \arguments{
16 | \item{files}{A character vector of font file paths or urls to add}
17 | }
18 | \value{
19 | This function is called for its sideeffects
20 | }
21 | \description{
22 | systemfonts is mainly about getting system native access to the fonts
23 | installed on the OS you are executing the code on. However, you may want to
24 | access fonts without doing a full installation, either because you want your
25 | project to be reproducible on all systems, because you don't have
26 | administrator privileges on the system, or for a different reason entirely.
27 | \code{add_fonts()} provide a way to side load font files so that they are found
28 | during font matching. The function differs from \code{\link[=register_font]{register_font()}} and
29 | \code{\link[=register_variant]{register_variant()}} in that they add the font file as-is using the family
30 | name etc that are provided by the font. \code{scan_local_fonts()} is run when
31 | systemfonts is loaded and will automatically add font files stored in
32 | \code{./fonts} (project local) and \verb{~/fonts} (user local).
33 | }
34 | \section{Font matching}{
35 | During font matching, systemfonts has to look in three different locations.
36 | The font registry (populated by \code{\link[=register_font]{register_font()}}/\code{\link[=register_variant]{register_variant()}}), the
37 | local fonts (populated with \code{\link[=add_fonts]{add_fonts()}}/\code{\link[=scan_local_fonts]{scan_local_fonts()}}), and the
38 | fonts installed on the system. It does so in that order: registry > local >
39 | installed.
40 |
41 | The matching performed at each step also differs. The fonts in the registry
42 | is only matched by family name. The local fonts are matched based on all the
43 | provided parameters (family, weight, italic, etc) in a way that is local to
44 | systemfonts, but try to emulate the system native matching. The installed
45 | fonts are matched using the system native matching functionality on macOS and
46 | Linux. On Windows the installed fonts are read from the system registry and
47 | matched using the same approach as for local fonts. Matching will always find
48 | a font no matter what you throw at it, defaulting to "sans" if nothing else
49 | is found.
50 | }
51 |
52 | \examples{
53 | # example code
54 | empty_font <- system.file("unfont.ttf", package = "systemfonts")
55 |
56 | add_fonts(empty_font)
57 |
58 | clear_local_fonts()
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/man/as_font_weight.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/match_fonts.R
3 | \name{as_font_weight}
4 | \alias{as_font_weight}
5 | \alias{as_font_width}
6 | \title{Convert weight and width to numerics}
7 | \usage{
8 | as_font_weight(weight)
9 |
10 | as_font_width(width)
11 | }
12 | \arguments{
13 | \item{weight, width}{character vectors with valid names for weight or width}
14 | }
15 | \value{
16 | An integer vector matching the length of the input
17 | }
18 | \description{
19 | It is often more natural to describe font weight and width with names rather
20 | than numbers (e.g. "bold" or "condensed"), but underneath these names are
21 | matched to numeric values. These two functions are used to retrieve the
22 | numeric counterparts to names
23 | }
24 | \examples{
25 | as_font_weight(
26 | c("undefined", "thin", "ultralight", "light", "normal", "medium", "semibold",
27 | "bold", "ultrabold", "heavy")
28 | )
29 |
30 | as_font_width(
31 | c("undefined", "ultracondensed", "extracondensed", "condensed", "semicondensed",
32 | "normal", "semiexpanded", "expanded", "extraexpanded", "ultraexpanded")
33 | )
34 |
35 | }
36 | \keyword{internal}
37 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-archived.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-defunct.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-deprecated.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-experimental.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-maturing.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-questioning.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-soft-deprecated.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-stable.svg:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-superseded.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-lib/systemfonts/7e1acd4acc5cf998e854869131b12eb6f0903500/man/figures/logo.png
--------------------------------------------------------------------------------
/man/font_fallback.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_fallback.R
3 | \name{font_fallback}
4 | \alias{font_fallback}
5 | \title{Get the fallback font for a given string}
6 | \usage{
7 | font_fallback(
8 | string,
9 | family = "",
10 | italic = FALSE,
11 | weight = "normal",
12 | width = "undefined",
13 | path = NULL,
14 | index = 0,
15 | variation = font_variation(),
16 | bold = deprecated()
17 | )
18 | }
19 | \arguments{
20 | \item{string}{The strings to find fallbacks for}
21 |
22 | \item{family}{The name of the font families to match}
23 |
24 | \item{italic}{logical indicating the font slant}
25 |
26 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
27 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
28 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
29 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
30 | \code{"undefined"}/\code{0}}
31 |
32 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
33 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
34 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
35 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
36 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
37 |
38 | \item{path, index}{path and index of a font file to circumvent lookup based on
39 | family and style}
40 |
41 | \item{variation}{A \code{font_variation} object or a list of them to control
42 | variable fonts}
43 |
44 | \item{bold}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{weight = "bold"} instead}
45 | }
46 | \value{
47 | A data frame with a \code{path} and \code{index} column giving fallback for the
48 | specified string and font combinations
49 | }
50 | \description{
51 | A fallback font is a font to use as a substitute if the chosen font does not
52 | contain the requested characters. Using font fallbacks means that the user
53 | doesn't have to worry about mixing characters from different scripts or
54 | mixing text and emojies. Fallback is calculated for the full string and the
55 | result is platform specific. If no font covers all the characters in the
56 | string an undefined "best match" is returned. The best approach is to figure
57 | out which characters are not covered by your chosen font and figure out
58 | fallbacks for these, rather than just request a fallback for the full string.
59 | }
60 | \examples{
61 | font_fallback("\U0001f604") # Smile emoji
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/man/font_feature.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_feature.R
3 | \name{font_feature}
4 | \alias{font_feature}
5 | \title{Define OpenType font feature settings}
6 | \usage{
7 | font_feature(ligatures = NULL, letters = NULL, numbers = NULL, ...)
8 | }
9 | \arguments{
10 | \item{ligatures}{Settings related to ligatures. One or more types of
11 | ligatures to turn on (see details).}
12 |
13 | \item{letters}{Settings related to the appearance of single
14 | letters (as opposed to ligatures that substitutes multiple letters). See
15 | details for supported values.}
16 |
17 | \item{numbers}{Settings related to the appearance of numbers. See details for
18 | supported values.}
19 |
20 | \item{...}{key-value pairs with the key being the 4-letter tag and the value
21 | being the setting (usually \code{TRUE} to turn it on).}
22 | }
23 | \value{
24 | A \code{font_feature} object
25 | }
26 | \description{
27 | This function encapsulates the specification of OpenType font features. Some
28 | specific features have named arguments, but all available features can be
29 | set by using its specific 4-letter tag For a list of the 4-letter tags
30 | available see e.g. the overview on
31 | \href{https://en.wikipedia.org/wiki/List_of_typographic_features}{Wikipedia}.
32 | }
33 | \details{
34 | OpenType features are defined by a 4-letter tag along with an integer value.
35 | Often that value is a simple \code{0} (off) or \code{1} (on), but some features support
36 | additional values, e.g. stylistic alternates (\code{salt}) where a font may
37 | provide multiple variants of a letter and the value will be used to chose
38 | which one to use.
39 |
40 | Common features related to appearance may be given with a long form name to
41 | either the \code{ligatures}, \code{letters}, or \code{numbers} argument to avoid remembering
42 | the often arbitrary 4-letter tag. Providing a long form name is the same as
43 | setting the tag to \code{1} and can thus not be used to set tags to other values.
44 |
45 | The possible long form names are given below with the tag in parenthesis:
46 |
47 | \strong{Ligatures}
48 | \itemize{
49 | \item \code{standard} (\emph{liga}): Turns on standard multiple letter substitution
50 | \item \code{historical} (\emph{hlig}): Use obsolete historical ligatures
51 | \item \code{contextual} (\emph{clig}): Apply secondary ligatures based on the character
52 | patterns surrounding the potential ligature
53 | \item \code{discretionary} (\emph{dlig}): Use ornamental ligatures
54 | }
55 |
56 | \strong{Letters}
57 | \itemize{
58 | \item \code{swash} (\emph{cswh}): Use contextual swashes (ornamental decorations)
59 | \item \code{alternates} (\emph{calt}): Use alternate letter forms based on the surrounding
60 | pattern
61 | \item \code{historical} (\emph{hist}): Use obsolete historical forms of the letters
62 | \item \code{localized} (\emph{locl}): Use alternate forms preferred by the script language
63 | \item \code{randomize} (\emph{rand}): Use random variants of the letters (e.g. to mimic
64 | handwriting)
65 | \item \code{alt_annotation} (\emph{nalt}): Use alternate annotations (e.g. circled digits)
66 | \item \code{stylistic} (\emph{salt}): Use a stylistic alternative form of the letter
67 | \item \code{subscript} (\emph{subs}): Set letter in subscript
68 | \item \code{superscript} (\emph{sups}): Set letter in superscript
69 | \item \code{titling} (\emph{titl}): Use letter forms well suited for large text and titles
70 | \item \code{small_caps} (\emph{smcp}): Use small caps variants of the letters
71 | }
72 |
73 | \strong{Numbers}
74 | \itemize{
75 | \item \code{lining} (\emph{lnum}): Use number variants that rest on the baseline
76 | \item \code{oldstyle} (\emph{onum}): Use old style numbers that use descender and ascender
77 | for various numbers
78 | \item \code{proportional} (\emph{pnum}): Let numbers take up width based on the visual
79 | width of the glyph
80 | \item \code{tabular} (\emph{tnum}): Enforce all numbers to take up the same width
81 | \item \code{fractions} (\emph{frac}): Convert numbers separated by \code{/} into a fraction
82 | glyph
83 | \item \code{fractions_alt} (\emph{afrc}): Use alternate fraction form with a horizontal
84 | divider
85 | }
86 | }
87 | \examples{
88 | font_feature(letters = "stylistic", numbers = c("lining", "tabular"))
89 |
90 | # Use the tag directly to access additional stylistic variants
91 | font_feature(numbers = c("lining", "tabular"), salt = 2)
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/man/font_info.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_info.R
3 | \name{font_info}
4 | \alias{font_info}
5 | \title{Query font-specific information}
6 | \usage{
7 | font_info(
8 | family = "",
9 | italic = FALSE,
10 | weight = "normal",
11 | width = "undefined",
12 | size = 12,
13 | res = 72,
14 | path = NULL,
15 | index = 0,
16 | variation = font_variation(),
17 | bold = deprecated()
18 | )
19 | }
20 | \arguments{
21 | \item{family}{The name of the font families to match}
22 |
23 | \item{italic}{logical indicating the font slant}
24 |
25 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
26 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
27 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
28 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
29 | \code{"undefined"}/\code{0}}
30 |
31 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
32 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
33 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
34 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
35 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
36 |
37 | \item{size}{The pointsize of the font to use for size related measures}
38 |
39 | \item{res}{The ppi of the size related measures}
40 |
41 | \item{path, index}{path and index of a font file to circumvent lookup based on
42 | family and style}
43 |
44 | \item{variation}{A \code{font_variation} object or a list of them to control
45 | variable fonts}
46 |
47 | \item{bold}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{weight = "bold"} instead}
48 | }
49 | \value{
50 | A data.frame giving info on the requested font + size combinations. The
51 | data.frame will contain the following columns:
52 |
53 | \describe{
54 | \item{path}{The path to the font file}
55 | \item{index}{The 0-based index of the font in the fontfile}
56 | \item{family}{The family name of the font}
57 | \item{style}{The style name of the font}
58 | \item{name}{The name of the font, if present, otherwise the family name}
59 | \item{italic}{A logical giving if the font is italic}
60 | \item{bold}{A logical giving if the font is bold}
61 | \item{monospace}{A logical giving if the font is monospace}
62 | \item{weight}{A factor giving the weight of the font}
63 | \item{width}{A factor giving the width of the font}
64 | \item{kerning}{A logical giving if the font supports kerning}
65 | \item{color}{A logical giving if the font has color glyphs}
66 | \item{scalable}{A logical giving if the font is scalable}
67 | \item{vertical}{A logical giving if the font is vertical}
68 | \item{n_glyphs}{The number of glyphs in the font}
69 | \item{n_sizes}{The number of predefined sizes in the font}
70 | \item{n_charmaps}{The number of character mappings in the font file}
71 | \item{bbox}{A bounding box large enough to contain any of the glyphs in the font}
72 | \item{max_ascend}{The maximum ascend of the tallest glyph in the font}
73 | \item{max_descent}{The maximum descend of the most descending glyph in the font}
74 | \item{max_advance_width}{The maximum horizontal advance a glyph can make}
75 | \item{max_advance_height}{The maximum vertical advance a glyph can make}
76 | \item{lineheight}{The height of a single line of text in the font}
77 | \item{underline_pos}{The position of a potential underlining segment}
78 | \item{underline_size}{The width the the underline}
79 | }
80 | }
81 | \description{
82 | Get general information about a font, relative to a given size. Size specific
83 | measures will be returned in pixel units. The function is vectorised to the
84 | length of the longest argument.
85 | }
86 | \examples{
87 | font_info('serif')
88 |
89 | # Avoid lookup if font file is already known
90 | sans <- match_fonts('sans')
91 | font_info(path = sans$path, index = sans$index)
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/man/font_variation.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_variation.R
3 | \name{font_variation}
4 | \alias{font_variation}
5 | \title{Define axis coordinates for variable fonts}
6 | \usage{
7 | font_variation(
8 | italic = NULL,
9 | weight = NULL,
10 | width = NULL,
11 | slant = NULL,
12 | optical_sizing = NULL,
13 | ...
14 | )
15 | }
16 | \arguments{
17 | \item{italic}{Value between 0 and 1. Usually treated as a boolean rather than
18 | a continuous axis}
19 |
20 | \item{weight}{Usually a value between 100 and 900 though fonts can limit or
21 | expand the supported range. Weight names are also allowed (see
22 | \code{\link[=as_font_weight]{as_font_weight()}})}
23 |
24 | \item{width}{Usually a value between 1 and 9 though fonts can limit or
25 | expand the supported range. Width names are also allowed (see
26 | \code{\link[=as_font_width]{as_font_width()}})}
27 |
28 | \item{slant}{The angular slant of the font, usually between -90 and 90
29 | (negative values tilt in the "standard" italic way)}
30 |
31 | \item{optical_sizing}{Stroke thickness compensation for the glyphs. During
32 | rendering the thickness of the stroke is usually increased when the font is
33 | set at small sizes to increase readability. Set this to the size of the font
34 | to get the "expected" look at that size.}
35 |
36 | \item{...}{Further axes and coordinate settings}
37 | }
38 | \value{
39 | A \code{font_variation} object with coordinates for the provided axes
40 | }
41 | \description{
42 | Variable fonts is a technology that allows font designers to encode the full,
43 | continuous font space of a typeface into a single font file and then have the
44 | user set the coordinates of the variable axes to define the font. So, rather
45 | than having a font file for bold, bold + italic, thin, and thin + italic,
46 | etc. it is all encoded in a single file with a continuous range of all axes
47 | (e.g. weight doesn't have to be one of the 9 standard weights but can be
48 | anything in between). There are 5 standard axes that fonts can use, but font
49 | designers are free to define their own completely arbitrary axes as well. You
50 | can use \code{\link[=font_info]{font_info()}} to see which axes a font defines along with their value
51 | range and default setting. Values given as \code{font_variation()} will always win
52 | over the conventional setting \emph{if} the axis is present in the font. For
53 | example, setting \code{weight = "bold"} along with
54 | \code{variation = font_variation(weight = 650)} will eventually request a weight
55 | of \code{650} (helfway between semibold and bold), assuming the weight-axis is
56 | present in the font. For clarity however, it is advised that \code{font_variation()}
57 | is only used for axes that can otherwise not be accessed by "top-level"
58 | arguments.
59 | }
60 | \note{
61 | systemfonts uses a scale of width values ranging from 1-9 while the
62 | width axis uses a different scale (0.5 - 2.0) going from half as wide to
63 | twice as wide as "normal". When using the \code{width} argument the coordinate
64 | values is automatically converted. If you set the value based on the width
65 | tag (\code{wdth}) then no conversion will happen.
66 | }
67 |
--------------------------------------------------------------------------------
/man/fonts_as_import.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/web_fonts.R
3 | \name{fonts_as_import}
4 | \alias{fonts_as_import}
5 | \title{Create import specifications for web content}
6 | \usage{
7 | fonts_as_import(
8 | family,
9 | italic = NULL,
10 | weight = NULL,
11 | width = NULL,
12 | ...,
13 | type = c("url", "import", "link"),
14 | may_embed = TRUE,
15 | repositories = c("Bunny Fonts", "Google Fonts", "Font Library")
16 | )
17 | }
18 | \arguments{
19 | \item{family}{The name of the font families to match}
20 |
21 | \item{italic}{logical indicating the font slant}
22 |
23 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
24 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
25 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
26 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
27 | \code{"undefined"}/\code{0}}
28 |
29 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
30 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
31 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
32 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
33 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
34 |
35 | \item{...}{Additional arguments passed on to the specific functions for the
36 | repositories. Currently:
37 | \itemize{
38 | \item \strong{Google Fonts and Bunny Fonts:}
39 | \itemize{
40 | \item \code{text} A piece of text containing the glyphs required. Using this can
41 | severely cut down on the size of the required download
42 | \item \code{display} One of \code{"auto"}, \code{"block"}, \code{"swap"}, \code{"fallback"}, or
43 | \code{"optional"}. Controls how the text is displayed while the font is
44 | downloading.
45 | }
46 | }}
47 |
48 | \item{type}{The type of return value. \code{"url"} returns the bare url pointing
49 | to the style sheet. \code{"import"} returns the stylesheet as an import statement
50 | (\verb{@import url()}). \code{"link"} returns the stylesheet as a link tag
51 | (\verb{})}
52 |
53 | \item{may_embed}{Logical. Should fonts that can't be found in the provided
54 | repositories be embedded as data-URLs. This is only possible if the font is
55 | available locally and in a \code{woff2}, \code{woff}, \code{otf}, or \code{ttf} file.}
56 |
57 | \item{repositories}{The repositories to try looking for the font. Currently
58 | \code{"Bunny Fonts"}, \code{"Google Fonts"}, and \code{"Font Library"} are supported. Set
59 | this to \code{NULL} together with \code{may_embed = TRUE} to force embedding of the
60 | font data.}
61 | }
62 | \value{
63 | A character vector with stylesheet specifications according to \code{type}
64 | }
65 | \description{
66 | If you create content in a text-based format such as HTML or SVG you need to
67 | make sure that the font is available on the computer where it is viewed. This
68 | can be achieved through the use of stylesheets that can either be added with
69 | a \verb{} tag or inserted with an \verb{@import} statement. This function
70 | facilitates the creation of either of these (or the bare URL to the
71 | stylesheet). It can rely on the Bunny Fonts, Google Fonts and/or Font Library
72 | repositories for serving the fonts. If the requested font is not found it can
73 | optionally hard code the data into the stylesheet.
74 | }
75 |
--------------------------------------------------------------------------------
/man/get_fallback.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/zzz.R
3 | \name{get_fallback}
4 | \alias{get_fallback}
5 | \title{Get location of the fallback font}
6 | \usage{
7 | get_fallback()
8 | }
9 | \description{
10 | Get location of the fallback font
11 | }
12 | \keyword{internal}
13 |
--------------------------------------------------------------------------------
/man/glyph_info.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_info.R
3 | \name{glyph_info}
4 | \alias{glyph_info}
5 | \title{Query glyph-specific information from fonts}
6 | \usage{
7 | glyph_info(
8 | glyphs,
9 | family = "",
10 | italic = FALSE,
11 | weight = "normal",
12 | width = "undefined",
13 | size = 12,
14 | res = 72,
15 | path = NULL,
16 | index = 0,
17 | variation = font_variation(),
18 | bold = deprecated()
19 | )
20 | }
21 | \arguments{
22 | \item{glyphs}{A vector of glyphs. Strings will be split into separate glyphs
23 | automatically}
24 |
25 | \item{family}{The name of the font families to match}
26 |
27 | \item{italic}{logical indicating the font slant}
28 |
29 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
30 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
31 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
32 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
33 | \code{"undefined"}/\code{0}}
34 |
35 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
36 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
37 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
38 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
39 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
40 |
41 | \item{size}{The pointsize of the font to use for size related measures}
42 |
43 | \item{res}{The ppi of the size related measures}
44 |
45 | \item{path, index}{path an index of a font file to circumvent lookup based on
46 | family and style}
47 |
48 | \item{variation}{A \code{font_variation} object or a list of them to control
49 | variable fonts}
50 |
51 | \item{bold}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{weight = "bold"} instead}
52 | }
53 | \value{
54 | A data.frame with information about each glyph, containing the following
55 | columns:
56 |
57 | \describe{
58 | \item{glyph}{The glyph as a character}
59 | \item{index}{The index of the glyph in the font file}
60 | \item{width}{The width of the glyph}
61 | \item{height}{The height of the glyph}
62 | \item{x_bearing}{The horizontal distance from the origin to the leftmost part of the glyph}
63 | \item{y_bearing}{The vertical distance from the origin to the top part of the glyph}
64 | \item{x_advance}{The horizontal distance to move the cursor after adding the glyph}
65 | \item{y_advance}{The vertical distance to move the cursor after adding the glyph}
66 | \item{bbox}{The tight bounding box surrounding the glyph}
67 | }
68 | }
69 | \description{
70 | This function allows you to extract information about the individual glyphs
71 | in a font, based on a specified size. All size related measures are in
72 | pixel-units. The function is vectorised to the length of the \code{glyphs} vector.
73 | }
74 |
--------------------------------------------------------------------------------
/man/glyph_outline.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_outline.R
3 | \name{glyph_outline}
4 | \alias{glyph_outline}
5 | \title{Get the outline of glyphs}
6 | \usage{
7 | glyph_outline(
8 | glyph,
9 | path,
10 | index = 0,
11 | size = 12,
12 | tolerance = 0.2,
13 | variation = font_variation(),
14 | verbose = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{glyph}{The index of the glyph in the font file}
19 |
20 | \item{path}{The path to the font file encoding the glyph}
21 |
22 | \item{index}{The index of the font in the font file}
23 |
24 | \item{size}{The size of the font in big points (1/72 inch)}
25 |
26 | \item{tolerance}{The deviation tolerance for decomposing bezier curves of the
27 | glyph. Given in the same unit as size. Smaller values give more detailed
28 | polygons}
29 |
30 | \item{variation}{A \code{font_variation} object or a list of them to control
31 | variable fonts}
32 |
33 | \item{verbose}{Should font and glyph loading errors be reported as warnings}
34 | }
35 | \value{
36 | A data frame giving the outlines of the glyphs provide in \code{glyph}. It
37 | contains the columns \code{glyph} pointing to the element in the input it relates
38 | to, \code{contour} enumerating the contours the glyph consists of, and \code{x} and \code{y}
39 | giving the coordinates in big points
40 | }
41 | \description{
42 | This function allows you to retrieve the outline of glyphs as polygon
43 | coordinates. The glyphs are given as indexes into a font file and not as
44 | characters allowing you to retrieve outlines for glyphs that doesn't have a
45 | character counterpoint. Glyphs that are given as bitmaps are ignored.
46 | }
47 | \examples{
48 | # Get the shape of s in the default font
49 | font <- font_info()
50 | glyph <- glyph_info("s", path = font$path, index = font$index)
51 |
52 | s <- glyph_outline(glyph$index, font$path, font$index, size = 150)
53 |
54 | plot(s$x, s$y, type = 'l')
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/man/glyph_raster.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_outline.R
3 | \name{glyph_raster}
4 | \alias{glyph_raster}
5 | \title{Render glyphs to raster image}
6 | \usage{
7 | glyph_raster(
8 | glyph,
9 | path,
10 | index = 0,
11 | size = 12,
12 | res = 300,
13 | variation = font_variation(),
14 | col = "black",
15 | verbose = FALSE
16 | )
17 | }
18 | \arguments{
19 | \item{glyph}{The index of the glyph in the font file}
20 |
21 | \item{path}{The path to the font file encoding the glyph}
22 |
23 | \item{index}{The index of the font in the font file}
24 |
25 | \item{size}{The size of the font in big points (1/72 inch)}
26 |
27 | \item{res}{The resolution to render the glyphs to}
28 |
29 | \item{variation}{A \code{font_variation} object or a list of them to control
30 | variable fonts}
31 |
32 | \item{col}{The color of the glyph assuming the glyph doesn't have a native
33 | coloring}
34 |
35 | \item{verbose}{Should font and glyph loading errors be reported as warnings}
36 | }
37 | \value{
38 | A list of nativeRaster objects (or \code{NULL} if it failed to render a
39 | given glyph). The nativeRasters have additional attributes attached. \code{"size"}
40 | will give the size of the glyph in big points and \code{"offset"} will give the
41 | location of the top-left corner of the raster with respect to where it should
42 | be rendered.
43 | }
44 | \description{
45 | Not all glyphs are encoded as vector outlines (emojis often not). Even for
46 | fonts that provide an outline you might be interested in a raster version.
47 | This function gives you just that. It converts a glyph into an optimized
48 | raster object that can be plotted with e.g. \code{\link[graphics:rasterImage]{graphics::rasterImage()}} or
49 | \code{\link[grid:grid.raster]{grid::grid.raster()}}. For convenience, you can also use
50 | \code{\link[=glyph_raster_grob]{glyph_raster_grob()}} for plotting the result.
51 | }
52 | \examples{
53 | font <- font_info()
54 | glyph <- glyph_info("R", path = font$path, index = font$index)
55 |
56 | R <- glyph_raster(glyph$index, font$path, font$index, size = 150)
57 |
58 | plot.new()
59 | plot.window(c(0,150), c(0, 150), asp = 1)
60 | rasterImage(R[[1]], 0, 0, attr(R[[1]], "size")[2], attr(R[[1]], "size")[1])
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/man/glyph_raster_grob.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_outline.R
3 | \name{glyph_raster_grob}
4 | \alias{glyph_raster_grob}
5 | \title{Convert an extracted glyph raster to a grob}
6 | \usage{
7 | glyph_raster_grob(glyph, x, y, ..., default.units = "bigpts")
8 | }
9 | \arguments{
10 | \item{glyph}{The nativeRaster object returned as one of the elements by
11 | \code{\link[=glyph_raster]{glyph_raster()}}}
12 |
13 | \item{x, y}{The baseline location of the glyph}
14 |
15 | \item{...}{
16 | Arguments passed on to \code{\link[grid:grid.raster]{grid::rasterGrob}}
17 | \describe{
18 | \item{\code{image}}{
19 | Any R object that can be coerced to a raster object.
20 | }
21 | \item{\code{width}}{A numeric vector or unit object specifying width.}
22 | \item{\code{height}}{A numeric vector or unit object specifying height.}
23 | \item{\code{just}}{The justification of the rectangle
24 | relative to its (x, y) location. If there are two values, the first
25 | value specifies horizontal justification and the second value specifies
26 | vertical justification. Possible string values are: \code{"left"},
27 | \code{"right"}, \code{"centre"}, \code{"center"}, \code{"bottom"},
28 | and \code{"top"}. For numeric values, 0 means left alignment
29 | and 1 means right alignment.
30 | }
31 | \item{\code{hjust}}{A numeric vector specifying horizontal justification.
32 | If specified, overrides the \code{just} setting.}
33 | \item{\code{vjust}}{A numeric vector specifying vertical justification.
34 | If specified, overrides the \code{just} setting.}
35 | \item{\code{name}}{ A character identifier. }
36 | \item{\code{gp}}{An object of class \code{"gpar"}, typically the output
37 | from a call to the function \code{\link[grid]{gpar}}. This is basically
38 | a list of graphical parameter settings.}
39 | \item{\code{vp}}{A Grid viewport object (or NULL).}
40 | \item{\code{interpolate}}{
41 | A logical value indicating whether to linearly interpolate the
42 | image (the alternative is to use nearest-neighbour interpolation,
43 | which gives a more blocky result).
44 | }
45 | }}
46 |
47 | \item{default.units}{A string indicating the default units to use
48 | if \code{x}, \code{y}, \code{width}, or \code{height}
49 | are only given as numeric vectors.}
50 | }
51 | \value{
52 | A rasterGrob object
53 | }
54 | \description{
55 | This is a convenience function that helps in creating \link{rasterGrob} with the
56 | correct settings for the glyph. It takes inot account the sizing and offset
57 | returned by \code{\link[=glyph_raster]{glyph_raster()}} and allows you to only consider the baseline
58 | position of the glyph.
59 | }
60 | \examples{
61 | font <- font_info()
62 | glyph <- glyph_info("R", path = font$path, index = font$index)
63 |
64 | R <- glyph_raster(glyph$index, font$path, font$index, size = 150)
65 |
66 | grob <- glyph_raster_grob(R[[1]], 50, 50)
67 |
68 | grid::grid.newpage()
69 | # Mark the baseline location
70 | grid::grid.points(50, 50, default.units = "bigpts")
71 | # Draw the glyph
72 | grid::grid.draw(grob)
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/man/match_fonts.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/match_fonts.R
3 | \name{match_fonts}
4 | \alias{match_fonts}
5 | \alias{match_font}
6 | \title{Find a system font by name and style}
7 | \usage{
8 | match_fonts(family, italic = FALSE, weight = "normal", width = "undefined")
9 |
10 | match_font(family, italic = FALSE, bold = FALSE)
11 | }
12 | \arguments{
13 | \item{family}{The name of the font families to match}
14 |
15 | \item{italic}{logical indicating the font slant}
16 |
17 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
18 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
19 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
20 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
21 | \code{"undefined"}/\code{0}}
22 |
23 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
24 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
25 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
26 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
27 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
28 |
29 | \item{bold}{logical indicating whether the font weight}
30 | }
31 | \value{
32 | A list containing the paths locating the font files, the 0-based
33 | index of the font in the files and the features for the font in case a
34 | registered font was located.
35 | }
36 | \description{
37 | This function locates the font file (and index) best matching a name and
38 | optional style. A font file will be returned even if a perfect match
39 | isn't found, but it is not necessarily similar to the requested family and
40 | it should not be relied on for font substitution. The aliases \code{"sans"},
41 | \code{"serif"}, \code{"mono"}, \code{"symbol"}, and \code{"emoji"} match to their respective
42 | system defaults (\code{""} is equivalent to \code{"sans"}). \code{match_font()} has been
43 | deprecated in favour of \code{match_fonts()} which provides vectorisation, as well
44 | as querying for different weights (rather than just "normal" and "bold") as
45 | well as different widths.
46 | }
47 | \section{Font matching}{
48 | During font matching, systemfonts has to look in three different locations.
49 | The font registry (populated by \code{\link[=register_font]{register_font()}}/\code{\link[=register_variant]{register_variant()}}), the
50 | local fonts (populated with \code{\link[=add_fonts]{add_fonts()}}/\code{\link[=scan_local_fonts]{scan_local_fonts()}}), and the
51 | fonts installed on the system. It does so in that order: registry > local >
52 | installed.
53 |
54 | The matching performed at each step also differs. The fonts in the registry
55 | is only matched by family name. The local fonts are matched based on all the
56 | provided parameters (family, weight, italic, etc) in a way that is local to
57 | systemfonts, but try to emulate the system native matching. The installed
58 | fonts are matched using the system native matching functionality on macOS and
59 | Linux. On Windows the installed fonts are read from the system registry and
60 | matched using the same approach as for local fonts. Matching will always find
61 | a font no matter what you throw at it, defaulting to "sans" if nothing else
62 | is found.
63 | }
64 |
65 | \examples{
66 | # Get the system default sans-serif font in italic
67 | match_fonts('sans', italic = TRUE)
68 |
69 | # Try to match it to a thin variant
70 | match_fonts(c('sans', 'serif'), weight = "thin")
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/man/plot_glyph_stats.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/font_outline.R
3 | \name{plot_glyph_stats}
4 | \alias{plot_glyph_stats}
5 | \title{Create a visual representation of what the various glyph stats mean}
6 | \usage{
7 | plot_glyph_stats(
8 | glyph,
9 | family = "",
10 | italic = FALSE,
11 | weight = "normal",
12 | width = "undefined",
13 | size = 12,
14 | res = 72,
15 | variation = font_variation(),
16 | path = NULL,
17 | index = 0
18 | )
19 | }
20 | \arguments{
21 | \item{glyph}{The character to plot}
22 |
23 | \item{family}{The name of the font families to match}
24 |
25 | \item{italic}{logical indicating the font slant}
26 |
27 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
28 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
29 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
30 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
31 | \code{"undefined"}/\code{0}}
32 |
33 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
34 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
35 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
36 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
37 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
38 |
39 | \item{size}{The pointsize of the font to use for size related measures}
40 |
41 | \item{res}{The ppi of the size related measures}
42 |
43 | \item{variation}{A \code{font_variation} object or a list of them to control
44 | variable fonts}
45 |
46 | \item{path, index}{path an index of a font file to circumvent lookup based on
47 | family and style}
48 | }
49 | \value{
50 | This function is called for its side effects
51 | }
52 | \description{
53 | This function helps you understand the concepts of width, height, bearing,
54 | and advance by annotating a glyph with the various measures
55 | }
56 | \examples{
57 | plot_glyph_stats("g")
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/man/register_font.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/register_font.R
3 | \name{register_font}
4 | \alias{register_font}
5 | \alias{registry_fonts}
6 | \alias{clear_registry}
7 | \title{Register font collections as families}
8 | \usage{
9 | register_font(
10 | name,
11 | plain,
12 | bold = plain,
13 | italic = plain,
14 | bolditalic = plain,
15 | features = font_feature()
16 | )
17 |
18 | registry_fonts()
19 |
20 | clear_registry()
21 | }
22 | \arguments{
23 | \item{name}{The name the collection will be known under (i.e. \emph{family})}
24 |
25 | \item{plain, bold, italic, bolditalic}{Fontfiles for the different faces of the
26 | collection. can either be a filepath or a list containing a filepath and an
27 | index (only for font files containing multiple fonts). If not given it will
28 | default to the \code{plain} specification.}
29 |
30 | \item{features}{A \code{\link{font_feature}} object describing the specific OpenType
31 | font features to turn on for the registered font.}
32 | }
33 | \value{
34 | \code{register_font()} and \code{clear_registry()} returns \code{NULL} invisibly.
35 | \code{registry_fonts()} returns a data table in the same style as \code{\link[=system_fonts]{system_fonts()}}
36 | though less detailed and not based on information in the font file.
37 | }
38 | \description{
39 | By design, systemfonts searches the fonts installed natively on the system.
40 | It is possible, however, to register other fonts from e.g. font packages or
41 | local font files, that will get searched before searching any installed
42 | fonts. You can always get an overview over all registered fonts with the
43 | \code{registry_fonts()} function that works as a registry focused analogue to
44 | \code{\link[=system_fonts]{system_fonts()}}. If you wish to clear out the registry, you can either
45 | restart the R session or call \code{clear_registry()}.
46 | }
47 | \details{
48 | \code{register_font} also makes it possible to use system fonts with traits that
49 | is not covered by the graphic engine in R. In plotting operations it is only
50 | possible to specify a family name and whether or not the font should be bold
51 | and/or italic. There are numerous fonts that will never get matched to this,
52 | especially because bold is only one of many weights.
53 |
54 | Apart from granting a way to use new varieties of fonts, font registration
55 | also allows you to override the default \code{sans}, \code{serif}, and \code{mono} mappings,
56 | simply by registering a collection to the relevant default name. As
57 | registered fonts are searched first it will take precedence over the default.
58 | }
59 | \section{Font matching}{
60 | During font matching, systemfonts has to look in three different locations.
61 | The font registry (populated by \code{\link[=register_font]{register_font()}}/\code{\link[=register_variant]{register_variant()}}), the
62 | local fonts (populated with \code{\link[=add_fonts]{add_fonts()}}/\code{\link[=scan_local_fonts]{scan_local_fonts()}}), and the
63 | fonts installed on the system. It does so in that order: registry > local >
64 | installed.
65 |
66 | The matching performed at each step also differs. The fonts in the registry
67 | is only matched by family name. The local fonts are matched based on all the
68 | provided parameters (family, weight, italic, etc) in a way that is local to
69 | systemfonts, but try to emulate the system native matching. The installed
70 | fonts are matched using the system native matching functionality on macOS and
71 | Linux. On Windows the installed fonts are read from the system registry and
72 | matched using the same approach as for local fonts. Matching will always find
73 | a font no matter what you throw at it, defaulting to "sans" if nothing else
74 | is found.
75 | }
76 |
77 | \examples{
78 | # Create a random font collection
79 | fonts <- system_fonts()
80 | plain <- sample(which(!fonts$italic & fonts$weight <= 'normal'), 1)
81 | bold <- sample(which(!fonts$italic & fonts$weight > 'normal'), 1)
82 | italic <- sample(which(fonts$italic & fonts$weight <= 'normal'), 1)
83 | bolditalic <- sample(which(fonts$italic & fonts$weight > 'normal'), 1)
84 | register_font(
85 | 'random',
86 | plain = list(fonts$path[plain], fonts$index[plain]),
87 | bold = list(fonts$path[bold], fonts$index[bold]),
88 | italic = list(fonts$path[italic], fonts$index[italic]),
89 | bolditalic = list(fonts$path[bolditalic], fonts$index[bolditalic])
90 | )
91 |
92 | # Look at your creation
93 | registry_fonts()
94 |
95 | # Reset
96 | clear_registry()
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/man/register_variant.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/register_font.R
3 | \name{register_variant}
4 | \alias{register_variant}
5 | \title{Register a font as a variant as an existing one}
6 | \usage{
7 | register_variant(
8 | name,
9 | family,
10 | weight = NULL,
11 | width = NULL,
12 | features = font_feature()
13 | )
14 | }
15 | \arguments{
16 | \item{name}{The new family name the variant should respond to}
17 |
18 | \item{family}{The name of an existing font family that this is a variant of}
19 |
20 | \item{weight}{One or two of \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"},
21 | \code{"medium"}, \code{"semibold"}, \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}. If one is
22 | given it sets the weight for the whole variant. If two is given the first
23 | one defines the plain weight and the second the bold weight. If \code{NULL} then
24 | the variants of the given family closest to \code{"normal"} and \code{"bold"} will be
25 | chosen.}
26 |
27 | \item{width}{One of \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"},
28 | \code{"semicondensed"}, \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"},
29 | \code{"extraexpanded"}, or \code{"ultraexpanded"} giving the width of the variant. If
30 | \code{NULL} then the width closest to \code{"normal"} will be chosen.}
31 |
32 | \item{features}{A \code{\link{font_feature}} object describing the specific OpenType
33 | font features to turn on for the registered font variant.}
34 | }
35 | \description{
36 | This function is a wrapper around \code{\link[=register_font]{register_font()}} that allows you to easily
37 | create variants of existing system fonts, e.g. to target different weights
38 | and/or widths, or for attaching OpenType features to a font.
39 | }
40 | \section{Font matching}{
41 | During font matching, systemfonts has to look in three different locations.
42 | The font registry (populated by \code{\link[=register_font]{register_font()}}/\code{\link[=register_variant]{register_variant()}}), the
43 | local fonts (populated with \code{\link[=add_fonts]{add_fonts()}}/\code{\link[=scan_local_fonts]{scan_local_fonts()}}), and the
44 | fonts installed on the system. It does so in that order: registry > local >
45 | installed.
46 |
47 | The matching performed at each step also differs. The fonts in the registry
48 | is only matched by family name. The local fonts are matched based on all the
49 | provided parameters (family, weight, italic, etc) in a way that is local to
50 | systemfonts, but try to emulate the system native matching. The installed
51 | fonts are matched using the system native matching functionality on macOS and
52 | Linux. On Windows the installed fonts are read from the system registry and
53 | matched using the same approach as for local fonts. Matching will always find
54 | a font no matter what you throw at it, defaulting to "sans" if nothing else
55 | is found.
56 | }
57 |
58 | \examples{
59 | # Get the default "sans" family
60 | sans <- match_fonts("sans")$path
61 | sans <- system_fonts()$family[system_fonts()$path == sans][1]
62 |
63 | # Register a variant of it:
64 | register_variant(
65 | "sans_ligature",
66 | sans,
67 | features = font_feature(ligatures = "discretionary")
68 | )
69 |
70 | registry_fonts()
71 |
72 | # clean up
73 | clear_registry()
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/man/require_font.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/web_fonts.R
3 | \name{require_font}
4 | \alias{require_font}
5 | \title{Ensure font availability in a script}
6 | \usage{
7 | require_font(
8 | family,
9 | fallback = NULL,
10 | dir = tempdir(),
11 | repositories = c("Google Fonts", "Font Squirrel", "Font Library"),
12 | error = TRUE,
13 | verbose = TRUE
14 | )
15 | }
16 | \arguments{
17 | \item{family}{The font family to require}
18 |
19 | \item{fallback}{An available font to fall back to if \code{family} cannot be found
20 | or downloaded}
21 |
22 | \item{dir}{The location to put the font file downloaded from repositories}
23 |
24 | \item{repositories}{The repositories to search for the font in case it is not
25 | available on the system. They will be tried in the order given. Currently
26 | \code{"Google Fonts"}, \code{"Font Squirrel"}, and \code{"Font Library"} is available.}
27 |
28 | \item{error}{Should the function throw an error if unsuccessful?}
29 |
30 | \item{verbose}{Should status messages be emitted?}
31 | }
32 | \value{
33 | Invisibly \code{TRUE} if the font is available or \code{FALSE} if not (this can
34 | only be returned if \code{error = FALSE})
35 | }
36 | \description{
37 | When running a script on a different machine you are not always in control of
38 | which fonts are installed on the system and thus how graphics created by the
39 | script ends up looking. \code{require_font()} is a way to specify your font
40 | requirements for a script. It will look at the available fonts and if the
41 | required font family is not present it will attempt to fetch it from one of
42 | the given repositories (in the order given). If that fails, it will either
43 | throw an error or, if \code{fallback} is given, provide an alias for the fallback
44 | so it maps to the required font.
45 | }
46 | \examples{
47 | # Should always work
48 | require_font("sans")
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/man/reset_font_cache.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/system_fonts.R
3 | \name{reset_font_cache}
4 | \alias{reset_font_cache}
5 | \title{Reset the system font cache}
6 | \usage{
7 | reset_font_cache()
8 | }
9 | \description{
10 | Building the list of system fonts is time consuming and is therefore cached.
11 | This, in turn, means that changes to the system fonts (i.e. installing new
12 | fonts), will not propagate to systemfonts. The solution is to reset the
13 | cache, which will result in the next call to e.g. \code{\link[=match_fonts]{match_fonts()}} will
14 | trigger a rebuild of the cache.
15 | }
16 | \examples{
17 | all_fonts <- system_fonts()
18 |
19 | ##-- Install a new font on the system --##
20 |
21 | all_fonts_new <- system_fonts()
22 |
23 | ## all_fonts_new will be equal to all_fonts
24 |
25 | reset_font_cache()
26 |
27 | all_fonts_new <- system_fonts()
28 |
29 | ## all_fonts_new will now contain the new font
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/man/search_web_fonts.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/web_fonts.R
3 | \name{search_web_fonts}
4 | \alias{search_web_fonts}
5 | \title{Search font repositories for a font based on family name}
6 | \usage{
7 | search_web_fonts(family, n_max = 10, ...)
8 | }
9 | \arguments{
10 | \item{family}{The font family name to look for}
11 |
12 | \item{n_max}{The maximum number of matches to return}
13 |
14 | \item{...}{
15 | Arguments passed on to \code{\link[utils:adist]{utils::adist}}
16 | \describe{
17 | \item{\code{costs}}{a numeric vector or list with names partially matching
18 | \samp{insertions}, \samp{deletions} and \samp{substitutions} giving
19 | the respective costs for computing the Levenshtein distance, or
20 | \code{NULL} (default) indicating using unit cost for all three
21 | possible transformations.}
22 | \item{\code{counts}}{a logical indicating whether to optionally return the
23 | transformation counts (numbers of insertions, deletions and
24 | substitutions) as the \code{"counts"} attribute of the return
25 | value.}
26 | \item{\code{fixed}}{a logical. If \code{TRUE} (default), the \code{x}
27 | elements are used as string literals. Otherwise, they are taken as
28 | regular expressions and \code{partial = TRUE} is implied
29 | (corresponding to the approximate string distance used by
30 | \code{\link{agrep}} with \code{fixed = FALSE}).}
31 | \item{\code{partial}}{a logical indicating whether the transformed \code{x}
32 | elements must exactly match the complete \code{y} elements, or only
33 | substrings of these. The latter corresponds to the approximate
34 | string distance used by \code{\link{agrep}} (by default).}
35 | \item{\code{ignore.case}}{a logical. If \code{TRUE}, case is ignored for
36 | computing the distances.}
37 | \item{\code{useBytes}}{a logical. If \code{TRUE} distance computations are
38 | done byte-by-byte rather than character-by-character.}
39 | }}
40 | }
41 | \value{
42 | A data.frame with the columns \code{family}, giving the family name of the
43 | matched font, and \code{repository} giving the repository it was found in.
44 | }
45 | \description{
46 | While it is often advisable to visit the webpage for a font repository when
47 | looking for a font, in order to see examples etc, \code{search_web_fonts()}
48 | provide a quick lookup based on family name in the repositories supported by
49 | systemfonts (currently \href{https://fonts.google.com}{Google Fonts} and
50 | \href{https://www.fontsquirrel.com}{Font Squirrel} - \href{https://fonts.bunny.net/}{Bunny Fonts}
51 | provide the same fonts as Google Fonts but doesn't have a search API). The
52 | lookup is based on fuzzy matching provided by \code{\link[utils:adist]{utils::adist()}} and the
53 | matching parameters can be controlled through \code{...}
54 | }
55 | \examples{
56 | # Requires an internet connection
57 |
58 | # search_web_fonts("Spectral")
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/man/shape_string.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shape_string.R
3 | \name{shape_string}
4 | \alias{shape_string}
5 | \title{Calculate glyph positions for strings}
6 | \usage{
7 | shape_string(
8 | strings,
9 | id = NULL,
10 | family = "",
11 | italic = FALSE,
12 | weight = "normal",
13 | width = "undefined",
14 | size = 12,
15 | res = 72,
16 | lineheight = 1,
17 | align = "left",
18 | hjust = 0,
19 | vjust = 0,
20 | max_width = NA,
21 | tracking = 0,
22 | indent = 0,
23 | hanging = 0,
24 | space_before = 0,
25 | space_after = 0,
26 | path = NULL,
27 | index = 0,
28 | bold = deprecated()
29 | )
30 | }
31 | \arguments{
32 | \item{strings}{A character vector of strings to shape}
33 |
34 | \item{id}{A vector grouping the strings together. If strings share an id the
35 | shaping will continue between strings}
36 |
37 | \item{family}{The name of the font families to match}
38 |
39 | \item{italic}{logical indicating the font slant}
40 |
41 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
42 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
43 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
44 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
45 | \code{"undefined"}/\code{0}}
46 |
47 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
48 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
49 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
50 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
51 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
52 |
53 | \item{size}{The pointsize of the font to use for size related measures}
54 |
55 | \item{res}{The ppi of the size related measures}
56 |
57 | \item{lineheight}{A multiplier for the lineheight}
58 |
59 | \item{align}{Within text box alignment, either \code{'left'}, \code{'center'}, or
60 | \code{'right'}}
61 |
62 | \item{hjust, vjust}{The justification of the textbox surrounding the text}
63 |
64 | \item{max_width}{The requested with of the string in inches. Setting this to
65 | something other than \code{NA} will turn on word wrapping.}
66 |
67 | \item{tracking}{Tracking of the glyphs (space adjustment) measured in 1/1000
68 | em.}
69 |
70 | \item{indent}{The indent of the first line in a paragraph measured in inches.}
71 |
72 | \item{hanging}{The indent of the remaining lines in a paragraph measured in
73 | inches.}
74 |
75 | \item{space_before, space_after}{The spacing above and below a paragraph,
76 | measured in points}
77 |
78 | \item{path, index}{path an index of a font file to circumvent lookup based on
79 | family and style}
80 |
81 | \item{bold}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{weight = "bold"} instead}
82 | }
83 | \value{
84 | A list with two element: \code{shape} contains the position of each glyph,
85 | relative to the origin in the enclosing textbox. \code{metrics} contain metrics
86 | about the full strings.
87 |
88 | \code{shape} is a data.frame with the following columns:
89 | \describe{
90 | \item{glyph}{The glyph as a character}
91 | \item{index}{The index of the glyph in the font file}
92 | \item{metric_id}{The index of the string the glyph is part of (referencing a row in the \code{metrics} data.frame)}
93 | \item{string_id}{The index of the string the glyph came from (referencing an element in the \code{strings} input)}
94 | \item{x_offset}{The x offset in pixels from the origin of the textbox}
95 | \item{y_offset}{The y offset in pixels from the origin of the textbox}
96 | \item{x_mid}{The x offset in pixels to the middle of the glyph, measured from the origin of the glyph}
97 | }
98 |
99 | \code{metrics} is a data.frame with the following columns:
100 | \describe{
101 | \item{string}{The text the string consist of}
102 | \item{width}{The width of the string}
103 | \item{height}{The height of the string}
104 | \item{left_bearing}{The distance from the left edge of the textbox and the leftmost glyph}
105 | \item{right_bearing}{The distance from the right edge of the textbox and the rightmost glyph}
106 | \item{top_bearing}{The distance from the top edge of the textbox and the topmost glyph}
107 | \item{bottom_bearing}{The distance from the bottom edge of the textbox and the bottommost glyph}
108 | \item{left_border}{The position of the leftmost edge of the textbox related to the origin}
109 | \item{top_border}{The position of the topmost edge of the textbox related to the origin}
110 | \item{pen_x}{The horizontal position of the next glyph after the string}
111 | \item{pen_y}{The vertical position of the next glyph after the string}
112 | }
113 | }
114 | \description{
115 | Do basic text shaping of strings. This function will use freetype to
116 | calculate advances, doing kerning if possible. It will not perform any font
117 | substitution or ligature resolving and will thus be much in line with how
118 | the standard graphic devices does text shaping. Inputs are recycled to the
119 | length of \code{strings}.
120 | }
121 | \examples{
122 | string <- "This is a long string\nLook; It spans multiple lines\nand all"
123 |
124 | # Shape with default settings
125 | shape_string(string)
126 |
127 | # Mix styles within the same string
128 | string <- c(
129 | "This string will have\na ",
130 | "very large",
131 | " text style\nin the middle"
132 | )
133 |
134 | shape_string(string, id = c(1, 1, 1), size = c(12, 24, 12))
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/man/str_split_emoji.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/emoji.R
3 | \name{str_split_emoji}
4 | \alias{str_split_emoji}
5 | \title{Split a string into emoji and non-emoji glyph runs}
6 | \usage{
7 | str_split_emoji(
8 | string,
9 | family = "",
10 | italic = FALSE,
11 | bold = FALSE,
12 | path = NULL,
13 | index = 0
14 | )
15 | }
16 | \arguments{
17 | \item{string}{A character vector of strings that should be splitted.}
18 |
19 | \item{family}{The name of the font families to match}
20 |
21 | \item{italic}{logical indicating the font slant}
22 |
23 | \item{bold}{logical indicating whether the font weight}
24 |
25 | \item{path, index}{path an index of a font file to circumvent lookup based on
26 | family and style}
27 | }
28 | \value{
29 | A data.frame containing the following columns:
30 | \describe{
31 | \item{string}{The substring containing a consecutive run of glyphs}
32 | \item{id}{The index into the original \code{string} vector that the substring is part of}
33 | \item{emoji}{A logical vector giving if the substring is a run of emojis or not}
34 | }
35 | }
36 | \description{
37 | In order to do correct text rendering, the font needed must be figured out. A
38 | common case is rendering of emojis within a string where the system emoji
39 | font is used rather than the requested font. This function will inspect the
40 | provided strings and split them up in runs that must be rendered with the
41 | emoji font, and the rest. Arguments are recycled to the length of the \code{string}
42 | vector.
43 | }
44 | \examples{
45 | emoji_string <- "This is a joke\U0001f642. It should be obvious from the smiley"
46 | str_split_emoji(emoji_string)
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/man/string_metrics_dev.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/dev_strings.R
3 | \name{string_metrics_dev}
4 | \alias{string_metrics_dev}
5 | \title{Get string metrics as measured by the current device}
6 | \usage{
7 | string_metrics_dev(
8 | strings,
9 | family = "",
10 | face = 1,
11 | size = 12,
12 | cex = 1,
13 | unit = "cm"
14 | )
15 | }
16 | \arguments{
17 | \item{strings}{A character vector of strings to measure}
18 |
19 | \item{family}{The font families to use. Will get recycled}
20 |
21 | \item{face}{The font faces to use. Will get recycled}
22 |
23 | \item{size}{The font size to use. Will get recycled}
24 |
25 | \item{cex}{The cex multiplier to use. Will get recycled}
26 |
27 | \item{unit}{The unit to return the width in. Either \code{"cm"}, \code{"inches"},
28 | \code{"device"}, or \code{"relative"}}
29 | }
30 | \value{
31 | A data.frame with \code{width}, \code{ascent}, and \code{descent} columns giving the
32 | metrics in the requested unit.
33 | }
34 | \description{
35 | This function is much like \code{\link[=string_widths_dev]{string_widths_dev()}} but also returns the ascent
36 | and descent of the string making it possible to construct a tight bounding
37 | box around the string.
38 | }
39 | \examples{
40 | # Get the metrics as measured in cm (default)
41 | string_metrics_dev(c('some text', 'a string with descenders'))
42 |
43 | }
44 | \seealso{
45 | Other device metrics:
46 | \code{\link{string_widths_dev}()}
47 | }
48 | \concept{device metrics}
49 |
--------------------------------------------------------------------------------
/man/string_width.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shape_string.R
3 | \name{string_width}
4 | \alias{string_width}
5 | \title{Calculate the width of a string, ignoring new-lines}
6 | \usage{
7 | string_width(
8 | strings,
9 | family = "",
10 | italic = FALSE,
11 | weight = "normal",
12 | width = "undefined",
13 | size = 12,
14 | res = 72,
15 | include_bearing = TRUE,
16 | path = NULL,
17 | index = 0,
18 | bold = deprecated()
19 | )
20 | }
21 | \arguments{
22 | \item{strings}{A character vector of strings}
23 |
24 | \item{family}{The name of the font families to match}
25 |
26 | \item{italic}{logical indicating the font slant}
27 |
28 | \item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200},
29 | \code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"},
30 | \code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"},
31 | \code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as
32 | \code{"undefined"}/\code{0}}
33 |
34 | \item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2},
35 | \code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"},
36 | \code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"},
37 | \code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or
38 | \code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}}
39 |
40 | \item{size}{The pointsize of the font to use for size related measures}
41 |
42 | \item{res}{The ppi of the size related measures}
43 |
44 | \item{include_bearing}{Logical, should left and right bearing be included in
45 | the string width?}
46 |
47 | \item{path, index}{path and index of a font file to circumvent lookup based on
48 | family and style}
49 |
50 | \item{bold}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{weight = "bold"} instead}
51 | }
52 | \value{
53 | A numeric vector giving the width of the strings in pixels. Use the
54 | provided \code{res} value to convert it into absolute values.
55 | }
56 | \description{
57 | This is a very simple alternative to \code{\link[=shape_string]{shape_string()}} that simply calculates
58 | the width of strings without taking any newline into account. As such it is
59 | suitable to calculate the width of words or lines that has already been
60 | splitted by \verb{\\n}. Input is recycled to the length of \code{strings}.
61 | }
62 | \examples{
63 | strings <- c('A short string', 'A very very looong string')
64 | string_width(strings)
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/man/string_widths_dev.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/dev_strings.R
3 | \name{string_widths_dev}
4 | \alias{string_widths_dev}
5 | \title{Get string widths as measured by the current device}
6 | \usage{
7 | string_widths_dev(
8 | strings,
9 | family = "",
10 | face = 1,
11 | size = 12,
12 | cex = 1,
13 | unit = "cm"
14 | )
15 | }
16 | \arguments{
17 | \item{strings}{A character vector of strings to measure}
18 |
19 | \item{family}{The font families to use. Will get recycled}
20 |
21 | \item{face}{The font faces to use. Will get recycled}
22 |
23 | \item{size}{The font size to use. Will get recycled}
24 |
25 | \item{cex}{The cex multiplier to use. Will get recycled}
26 |
27 | \item{unit}{The unit to return the width in. Either \code{"cm"}, \code{"inches"},
28 | \code{"device"}, or \code{"relative"}}
29 | }
30 | \value{
31 | A numeric vector with the width of each of the strings given in
32 | \code{strings} in the unit given in \code{unit}
33 | }
34 | \description{
35 | For certain composition tasks it is beneficial to get the width of a string
36 | as interpreted by the device that is going to plot it. grid provides this
37 | through construction of a \code{textGrob} and then converting the corresponding
38 | grob width to e.g. cm, but this comes with a huge overhead.
39 | \code{string_widths_dev()} provides direct, vectorised, access to the graphic
40 | device for as high performance as possible.
41 | }
42 | \examples{
43 | # Get the widths as measured in cm (default)
44 | string_widths_dev(c('a string', 'an even longer string'))
45 |
46 | }
47 | \seealso{
48 | Other device metrics:
49 | \code{\link{string_metrics_dev}()}
50 | }
51 | \concept{device metrics}
52 |
--------------------------------------------------------------------------------
/man/system_fonts.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/system_fonts.R
3 | \name{system_fonts}
4 | \alias{system_fonts}
5 | \title{List all fonts installed on your system}
6 | \usage{
7 | system_fonts()
8 | }
9 | \value{
10 | A data frame with a row for each font and various information in each
11 | column
12 | }
13 | \description{
14 | List all fonts installed on your system
15 | }
16 | \examples{
17 | # See all monospace fonts
18 | fonts <- system_fonts()
19 | fonts[fonts$monospace, ]
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/man/systemfonts-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/systemfonts-package.R
3 | \docType{package}
4 | \name{systemfonts-package}
5 | \alias{systemfonts}
6 | \alias{systemfonts-package}
7 | \title{systemfonts: System Native Font Finding}
8 | \description{
9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}}
10 |
11 | Provides system native access to the font catalogue. As font handling varies between systems it is difficult to correctly locate installed fonts across different operating systems. The 'systemfonts' package provides bindings to the native libraries on Windows, macOS and Linux for finding font files that can then be used further by e.g. graphic devices. The main use is intended to be from compiled code but 'systemfonts' also provides access from R.
12 | }
13 | \seealso{
14 | Useful links:
15 | \itemize{
16 | \item \url{https://github.com/r-lib/systemfonts}
17 | \item \url{https://systemfonts.r-lib.org}
18 | \item Report bugs at \url{https://github.com/r-lib/systemfonts/issues}
19 | }
20 |
21 | }
22 | \author{
23 | \strong{Maintainer}: Thomas Lin Pedersen \email{thomas.pedersen@posit.co} (\href{https://orcid.org/0000-0002-5147-4711}{ORCID})
24 |
25 | Authors:
26 | \itemize{
27 | \item Jeroen Ooms \email{jeroen@berkeley.edu} (\href{https://orcid.org/0000-0002-4035-0289}{ORCID})
28 | \item Devon Govett (Author of font-manager)
29 | }
30 |
31 | Other contributors:
32 | \itemize{
33 | \item Posit Software, PBC (03wc8by49) [copyright holder, funder]
34 | }
35 |
36 | }
37 | \keyword{internal}
38 |
--------------------------------------------------------------------------------
/man/web-fonts.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/web_fonts.R
3 | \name{web-fonts}
4 | \alias{web-fonts}
5 | \alias{get_from_google_fonts}
6 | \alias{get_from_font_squirrel}
7 | \title{Download and add web font}
8 | \usage{
9 | get_from_google_fonts(family, dir = "~/fonts", woff2 = FALSE)
10 |
11 | get_from_font_squirrel(family, dir = "~/fonts")
12 | }
13 | \arguments{
14 | \item{family}{The font family to download (case insensitive)}
15 |
16 | \item{dir}{Where to download the font to. The default places it in your user
17 | local font folder so that the font will be available automatically in new R
18 | sessions. Set to \code{tempdir()} to only keep the font for the session.}
19 |
20 | \item{woff2}{Should the font be downloaded in the woff2 format (smaller and
21 | more optimized)? Defaults to FALSE as the format is not supported on all
22 | systems}
23 | }
24 | \value{
25 | A logical invisibly indicating whether a font was found and
26 | downloaded or not
27 | }
28 | \description{
29 | In order to use a font in R it must first be made available locally. These
30 | functions facilitate the download and registration of fonts from online
31 | repositories.
32 | }
33 |
--------------------------------------------------------------------------------
/revdep/README.md:
--------------------------------------------------------------------------------
1 | # Revdeps
2 |
3 |
--------------------------------------------------------------------------------
/revdep/failures.md:
--------------------------------------------------------------------------------
1 | *Wow, no problems at all. :)*
--------------------------------------------------------------------------------
/revdep/problems.md:
--------------------------------------------------------------------------------
1 | *Wow, no problems at all. :)*
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | *.dll
4 |
--------------------------------------------------------------------------------
/src/Makevars.in:
--------------------------------------------------------------------------------
1 | PKG_CPPFLAGS=@cflags@
2 | PKG_OBJCXXFLAGS=$(CXX17STD) @objcflags@
3 |
4 | DARWIN_LIBS = -framework CoreText -framework Foundation
5 | DARWIN_OBJECTS = mac/FontManagerMac.o
6 | UNIX_OBJECTS = unix/FontManagerLinux.o
7 |
8 | PKG_LIBS = @libs@ $(@SYS@_LIBS)
9 | OBJECTS = caches.o cpp11.o dev_metrics.o font_matching.o font_local.o font_variation.o \
10 | font_registry.o ft_cache.o string_shape.o font_metrics.o font_outlines.o \
11 | font_fallback.o string_metrics.o emoji.o cache_store.o init.o $(@SYS@_OBJECTS)
12 |
13 | all: clean
14 |
15 | clean:
16 | rm -f $(SHLIB) $(OBJECTS)
17 |
--------------------------------------------------------------------------------
/src/Makevars.win:
--------------------------------------------------------------------------------
1 | PKG_CONFIG_NAME = freetype2
2 | PKG_CONFIG ?= $(BINPREF)pkg-config
3 | PKG_LIBS := $(shell $(PKG_CONFIG) --libs $(PKG_CONFIG_NAME))
4 |
5 | OBJECTS = caches.o cpp11.o dev_metrics.o font_matching.o font_local.o font_variation.o \
6 | font_registry.o ft_cache.o string_shape.o font_metrics.o font_outlines.o \
7 | font_fallback.o string_metrics.o emoji.o cache_store.o init.o win/FontManagerWindows.o
8 |
9 | ifneq ($(PKG_LIBS),)
10 | $(info using $(PKG_CONFIG_NAME) from Rtools)
11 | PKG_CPPFLAGS := $(shell $(PKG_CONFIG) --cflags $(PKG_CONFIG_NAME))
12 | else
13 | RWINLIB = ../windows/freetype2
14 | PKG_CPPFLAGS = -I$(RWINLIB)/include/freetype2
15 | PKG_LIBS = -L$(RWINLIB)/lib$(R_ARCH) -L$(RWINLIB)/lib -lfreetype -lharfbuzz -lpng -lbz2 -lz -lrpcrt4 -lgdi32 -luuid
16 | endif
17 |
18 | all: $(SHLIB)
19 |
20 | $(OBJECTS): $(RWINLIB)
21 |
22 | $(RWINLIB):
23 | "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R"
24 |
25 | clean:
26 | rm -f $(SHLIB) $(OBJECTS)
27 |
--------------------------------------------------------------------------------
/src/cache_lru.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | template
8 | class LRU_Cache {
9 |
10 | public:
11 | LRU_Cache() :
12 | _max_size(32) {
13 |
14 | }
15 | LRU_Cache(size_t max_size) :
16 | _max_size(max_size) {
17 |
18 | }
19 |
20 | ~LRU_Cache() {
21 | clear();
22 | }
23 |
24 | // Add a key-value pair, potentially passing a removed key and value back
25 | // through the removed_key and removed_value argument. Returns true if a value
26 | // was removed and false otherwise
27 | inline bool add(key_t key, value_t value, key_t& removed_key, value_t& removed_value) {
28 | cache_map_it_t it = _cache_map.find(key);
29 | _cache_list.push_front(key_value_t(key, value));
30 | if (it != _cache_map.end()) {
31 | _cache_list.erase(it->second);
32 | _cache_map.erase(it);
33 | }
34 | _cache_map[key] = _cache_list.begin();
35 |
36 | if (_cache_map.size() > _max_size) {
37 | cache_list_it_t last = _cache_list.end();
38 | last--;
39 | removed_key = last->first;
40 | removed_value = last->second;
41 | _cache_map.erase(last->first);
42 | _cache_list.pop_back();
43 | return true;
44 | }
45 | return false;
46 | }
47 |
48 | // Add a key-value pair, potentially passing a value back through the
49 | // removed_value argument. Returns true if a value was removed and false
50 | // otherwise
51 | inline bool add(key_t key, value_t value, value_t& removed_value) {
52 | key_t removed_key;
53 | return add(key, value, removed_key, removed_value);
54 | }
55 |
56 | // Add a key-value pair, potentially passing a key back through the
57 | // removed_key argument. Returns true if a value was removed and false
58 | // otherwise. Will destroy the value
59 | inline bool add(key_t key, value_t value, key_t& removed_key) {
60 | value_t removed_value;
61 | bool overflow = add(key, value, removed_key, removed_value);
62 | if (overflow) {
63 | value_dtor(removed_value);
64 | }
65 | return overflow;
66 | }
67 |
68 | // Add a key-value pair, automatically destroying any removed value
69 | inline void add(key_t key, value_t value) {
70 | value_t removed_value;
71 | key_t removed_key;
72 | if (add(key, value, removed_key, removed_value)) {
73 | value_dtor(removed_value);
74 | }
75 | }
76 |
77 | // Retrieve a value based on a key, returning true if a value was found. Will
78 | // move the key-value pair to the top of the list
79 | inline bool get(key_t key, value_t& value) {
80 | cache_map_it_t it = _cache_map.find(key);
81 | if (it == _cache_map.end()) {
82 | return false;
83 | }
84 |
85 | value = it->second->second;
86 | _cache_list.splice(_cache_list.begin(), _cache_list, it->second);
87 |
88 | return true;
89 | }
90 |
91 | // Retrieve a value based on a key without bumping the pair to the top of the
92 | // list
93 | inline bool steal(key_t key, value_t& value) {
94 | cache_map_it_t it = _cache_map.find(key);
95 | if (it == _cache_map.end()) {
96 | return false;
97 | }
98 |
99 | value = it->second->second;
100 | return true;
101 | }
102 |
103 | // Check for the existence of a key-value pair
104 | inline bool exist(key_t key) {
105 | return _cache_map.find(key) != _cache_map.end();
106 | }
107 |
108 | // Remove a key-value pair, destroying the value
109 | inline void remove(key_t key) {
110 | cache_map_it_t it = _cache_map.find(key);
111 | if (it == _cache_map.end()) {
112 | return;
113 | }
114 |
115 | value_dtor(it->second->second);
116 | _cache_list.erase(it->second);
117 | _cache_map.erase(it);
118 | }
119 |
120 | // Clear the cache, destroying all values with it
121 | inline void clear() {
122 | for (cache_list_it_t it = _cache_list.begin(); it != _cache_list.end(); ++it) {
123 | value_dtor(it->second);
124 | }
125 | _cache_list.clear();
126 | _cache_map.clear();
127 | }
128 |
129 | private:
130 | size_t _max_size;
131 |
132 | // Should be overridden for children with value types that needs special
133 | // dtor handling
134 | inline virtual void value_dtor(value_t& value) {
135 | // Allow children to destroy values properly
136 | }
137 |
138 | protected:
139 | typedef typename std::pair key_value_t;
140 | typedef typename std::list list_t;
141 | typedef typename list_t::iterator cache_list_it_t;
142 | typedef typename std::unordered_map map_t;
143 | typedef typename std::unordered_map::iterator cache_map_it_t;
144 |
145 | list_t _cache_list;
146 | map_t _cache_map;
147 | };
148 |
--------------------------------------------------------------------------------
/src/cache_store.cpp:
--------------------------------------------------------------------------------
1 | #include "cache_store.h"
2 |
3 | #include "types.h"
4 | #include "caches.h"
5 | #include "utils.h"
6 |
7 | FT_Face get_cached_face(const char* file, int index, double size, double res, int* error) {
8 | FT_Face face = nullptr;
9 | BEGIN_CPP
10 |
11 | FreetypeCache& cache = get_font_cache();
12 | if (!cache.load_font(file, index, size, res)) {
13 | *error = cache.error_code;
14 | return face;
15 | }
16 | face = cache.get_face();
17 |
18 | END_CPP
19 |
20 | *error = 0;
21 | return face;
22 | }
23 |
24 | FT_Face get_cached_face2(const FontSettings2& font, double size, double res, int* error) {
25 | FT_Face face = nullptr;
26 | BEGIN_CPP
27 |
28 | FreetypeCache& cache = get_font_cache();
29 | if (!cache.load_font(font.file, font.index, size, res)) {
30 | *error = cache.error_code;
31 | return face;
32 | }
33 | cache.set_axes(font.axes, font.coords, font.n_axes);
34 | face = cache.get_face();
35 |
36 | END_CPP
37 |
38 | *error = 0;
39 | return face;
40 | }
41 |
42 | void export_cache_store(DllInfo* dll) {
43 | R_RegisterCCallable("systemfonts", "get_cached_face", (DL_FUNC)get_cached_face);
44 | R_RegisterCCallable("systemfonts", "get_cached_face2", (DL_FUNC)get_cached_face2);
45 | }
46 |
--------------------------------------------------------------------------------
/src/cache_store.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include FT_FREETYPE_H
5 | #include
6 | #include
7 | #include "types.h"
8 |
9 | FT_Face get_cached_face(const char* file, int index, double size, double res, int* error);
10 | FT_Face get_cached_face2(const FontSettings2& font, double size, double res, int* error);
11 |
12 | [[cpp11::init]]
13 | void export_cache_store(DllInfo* dll);
14 |
--------------------------------------------------------------------------------
/src/caches.cpp:
--------------------------------------------------------------------------------
1 | #include "caches.h"
2 |
3 | static ResultSet* fonts;
4 |
5 | ResultSet& get_font_list() {
6 | return *fonts;
7 | }
8 |
9 | static ResultSet* fonts_local;
10 |
11 | ResultSet& get_local_font_list() {
12 | return *fonts_local;
13 | }
14 |
15 | static FontReg* font_registry;
16 |
17 | FontReg& get_font_registry() {
18 | return *font_registry;
19 | }
20 |
21 | static EmojiMap* emoji_map;
22 |
23 | EmojiMap& get_emoji_map() {
24 | return *emoji_map;
25 | }
26 |
27 | static FontMap* font_locations;
28 |
29 | FontMap& get_font_map() {
30 | return *font_locations;
31 | }
32 |
33 | static WinLinkMap* win_font_linking;
34 |
35 | WinLinkMap& get_win_link_map() {
36 | return *win_font_linking;
37 | }
38 |
39 | void init_caches(DllInfo* dll) {
40 | fonts = new ResultSet();
41 | fonts_local = new ResultSet();
42 | font_registry = new FontReg();
43 | emoji_map = new EmojiMap();
44 | font_locations = new FontMap();
45 | win_font_linking = new WinLinkMap();
46 | }
47 |
48 | void unload_caches(DllInfo* dll) {
49 | delete fonts;
50 | delete fonts_local;
51 | delete font_registry;
52 | delete emoji_map;
53 | delete font_locations;
54 | delete win_font_linking;
55 | }
56 |
--------------------------------------------------------------------------------
/src/caches.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "types.h"
6 | #include "FontDescriptor.h"
7 | #include "ft_cache.h"
8 |
9 | ResultSet& get_font_list();
10 |
11 | ResultSet& get_local_font_list();
12 |
13 | FontReg& get_font_registry();
14 |
15 | FreetypeCache& get_font_cache();
16 |
17 | EmojiMap& get_emoji_map();
18 |
19 | FontMap& get_font_map();
20 |
21 | WinLinkMap& get_win_link_map();
22 |
23 | [[cpp11::init]]
24 | void init_caches(DllInfo* dll);
25 |
26 | void unload_caches(DllInfo* dll);
27 |
--------------------------------------------------------------------------------
/src/dev_metrics.cpp:
--------------------------------------------------------------------------------
1 | #include "dev_metrics.h"
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | using doubles_t = cpp11::doubles;
9 | using doubles_w = cpp11::writable::doubles;
10 | using strings_t = cpp11::strings;
11 | using integers_t = cpp11::integers;
12 | using data_frame_w = cpp11::writable::data_frame;
13 |
14 | using namespace cpp11::literals;
15 |
16 | doubles_t dev_string_widths_c(strings_t string, strings_t family, integers_t face, doubles_t size, doubles_t cex, integers_t unit) {
17 | GEUnit u = GE_INCHES;
18 | switch (INTEGER(unit)[0]) {
19 | case 0:
20 | u = GE_CM;
21 | break;
22 | case 1:
23 | u = GE_INCHES;
24 | break;
25 | case 2:
26 | u = GE_DEVICE;
27 | break;
28 | case 3:
29 | u = GE_NDC;
30 | break;
31 | }
32 | pGEDevDesc dev = GEcurrentDevice();
33 | R_GE_gcontext gc = {};
34 | double width = 0;
35 | int n_total = string.size();
36 | int scalar_family = family.size() == 1;
37 | int scalar_rest = face.size() == 1;
38 | strcpy(gc.fontfamily, Rf_translateCharUTF8(family[0]));
39 | gc.fontface = face[0];
40 | gc.ps = size[0];
41 | gc.cex = cex[0];
42 | doubles_w res(n_total);
43 |
44 | for (int i = 0; i < n_total; ++i) {
45 | if (i > 0 && !scalar_family) {
46 | strcpy(gc.fontfamily, Rf_translateCharUTF8(family[i]));
47 | }
48 | if (i > 0 && !scalar_rest) {
49 | gc.fontface = face[i];
50 | gc.ps = size[i];
51 | gc.cex = cex[i];
52 | }
53 | width = GEStrWidth(
54 | CHAR(string[i]),
55 | Rf_getCharCE(string[i]),
56 | &gc,
57 | dev
58 | );
59 | res[i] = GEfromDeviceWidth(width, u, dev);
60 | }
61 | return res;
62 | }
63 |
64 | data_frame_w dev_string_metrics_c(strings_t string, strings_t family, integers_t face, doubles_t size, doubles_t cex, integers_t unit) {
65 | GEUnit u = GE_INCHES;
66 | switch (INTEGER(unit)[0]) {
67 | case 0:
68 | u = GE_CM;
69 | break;
70 | case 1:
71 | u = GE_INCHES;
72 | break;
73 | case 2:
74 | u = GE_DEVICE;
75 | break;
76 | case 3:
77 | u = GE_NDC;
78 | break;
79 | }
80 | pGEDevDesc dev = GEcurrentDevice();
81 | R_GE_gcontext gc = {};
82 | double width = 0, ascent = 0, descent = 0;
83 | int n_total = string.size();
84 | int scalar_family = family.size() == 1;
85 | int scalar_rest = face.size() == 1;
86 | strcpy(gc.fontfamily, Rf_translateCharUTF8(family[0]));
87 | gc.fontface = face[0];
88 | gc.ps = size[0];
89 | gc.cex = cex[0];
90 | doubles_w w(n_total);
91 | doubles_w a(n_total);
92 | doubles_w d(n_total);
93 |
94 | for (int i = 0; i < n_total; ++i) {
95 | if (i > 0 && !scalar_family) {
96 | strcpy(gc.fontfamily, Rf_translateCharUTF8(family[i]));
97 | }
98 | if (i > 0 && !scalar_rest) {
99 | gc.fontface = face[i];
100 | gc.ps = size[i];
101 | gc.cex = cex[i];
102 | }
103 | GEStrMetric(
104 | CHAR(string[i]),
105 | Rf_getCharCE(string[i]),
106 | &gc,
107 | &ascent, &descent, &width,
108 | dev
109 | );
110 | w[i] = GEfromDeviceWidth(width, u, dev);
111 | a[i] = GEfromDeviceWidth(ascent, u, dev);
112 | d[i] = GEfromDeviceWidth(descent, u, dev);
113 | }
114 | data_frame_w res({
115 | "width"_nm = w,
116 | "ascent"_nm = a,
117 | "descent"_nm = d
118 | });
119 | res.attr("class") = {"tbl_df", "tbl", "data.frame"};
120 |
121 | return res;
122 | }
123 |
--------------------------------------------------------------------------------
/src/dev_metrics.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | [[cpp11::register]]
9 | cpp11::doubles dev_string_widths_c(cpp11::strings string, cpp11::strings family,
10 | cpp11::integers face, cpp11::doubles size,
11 | cpp11::doubles cex, cpp11::integers unit);
12 |
13 | [[cpp11::register]]
14 | cpp11::writable::data_frame dev_string_metrics_c(cpp11::strings string,
15 | cpp11::strings family,
16 | cpp11::integers face,
17 | cpp11::doubles size,
18 | cpp11::doubles cex,
19 | cpp11::integers unit);
20 |
--------------------------------------------------------------------------------
/src/emoji.cpp:
--------------------------------------------------------------------------------
1 | #include "emoji.h"
2 | #include "types.h"
3 | #include "caches.h"
4 | #include "utils.h"
5 |
6 | #include
7 |
8 | using list_t = cpp11::list;
9 | using list_w = cpp11::writable::list;
10 | using strings_t = cpp11::strings;
11 | using strings_w = cpp11::writable::strings;
12 | using integers_t = cpp11::integers;
13 | using integers_w = cpp11::writable::integers;
14 | using logicals_t = cpp11::logicals;
15 | using logicals_w = cpp11::writable::logicals;
16 |
17 | using namespace cpp11::literals;
18 |
19 | bool has_emoji(const char* string) {
20 | UTF_UCS utf_converter;
21 | int n_glyphs = 0;
22 | uint32_t* codepoints = utf_converter.convert(string, n_glyphs);
23 | EmojiMap& emoji_map = get_emoji_map();
24 |
25 | for (int i = 0; i < n_glyphs; ++i) {
26 | EmojiMap::iterator it = emoji_map.find(codepoints[i]);
27 | if (it == emoji_map.end()) { // Not an emoji
28 | continue;
29 | }
30 | switch (it->second) {
31 | case 0: // Fully qualified emoji codepoint
32 | return true;
33 | case 1: // Emoji with text presentation default
34 | if (i != n_glyphs - 1 && codepoints[i + 1] == 0xFE0F) {
35 | return true;
36 | }
37 | break;
38 | case 2: // Emoji with text presentation default that can take modifier
39 | if (i != n_glyphs - 1 && codepoints[i + 1] >= 0x1F3FB && codepoints[i + 1] <= 0x1F3FF) {
40 | return true;
41 | }
42 | break;
43 | }
44 | }
45 |
46 | return false;
47 | }
48 |
49 | void detect_emoji_embedding(const uint32_t* codepoints, int n, int* embedding, const char* fontpath, int index) {
50 | EmojiMap& emoji_map = get_emoji_map();
51 | FreetypeCache& cache = get_font_cache();
52 | bool loaded = cache.load_font(fontpath, index, 12.0, 72.0); // We don't care about sizing
53 |
54 | for (int i = 0; i < n; ++i) {
55 | EmojiMap::iterator it = emoji_map.find(codepoints[i]);
56 | if (it == emoji_map.end()) { // Not an emoji
57 | embedding[i] = 0;
58 | continue;
59 | }
60 | switch (it->second) {
61 | case 0: // Fully qualified emoji codepoint
62 | embedding[i] = 1;
63 | break;
64 | case 1: // Emoji with text presentation default
65 | if (i == n - 1) {
66 | embedding[i] = 0;
67 | break;
68 | }
69 | if (codepoints[i + 1] == 0xFE0F) {
70 | embedding[i] = 1;
71 | embedding[i + 1] = 1;
72 | ++i;
73 | } else if (loaded && cache.has_glyph(codepoints[i])) {
74 | embedding[i] = 0;
75 | } else {
76 | embedding[i] = 1;
77 | }
78 | break;
79 | case 2: // Emoji with text presentation default that can take modifier
80 | if (i == n - 1) {
81 | embedding[i] = 0;
82 | break;
83 | }
84 | if (codepoints[i + 1] >= 0x1F3FB && codepoints[i + 1] <= 0x1F3FF) {
85 | embedding[i] = 1;
86 | embedding[i + 1] = 1;
87 | ++i;
88 | } else if (loaded && cache.has_glyph(codepoints[i])) {
89 | embedding[i] = 0;
90 | } else {
91 | embedding[i] = 1;
92 | }
93 | break;
94 | default: // should not be reached
95 | embedding[i] = 0;
96 | }
97 | }
98 | }
99 |
100 | bool is_emoji(uint32_t* codepoints, int n, logicals_w &result, const char* fontpath, int index) {
101 | EmojiMap& emoji_map = get_emoji_map();
102 | FreetypeCache& cache = get_font_cache();
103 | bool loaded = cache.load_font(fontpath, index, 12.0, 72.0); // We don't care about sizing
104 |
105 | if (!loaded) {
106 | return false;
107 | }
108 |
109 | for (int i = 0; i < n; ++i) {
110 | EmojiMap::iterator it = emoji_map.find(codepoints[i]);
111 | if (it == emoji_map.end()) { // Not an emoji
112 | result.push_back(FALSE);
113 | continue;
114 | }
115 | switch (it->second) {
116 | case 0: // Fully qualified emoji codepoint
117 | result.push_back(TRUE);
118 | break;
119 | case 1: // Emoji with text presentation default
120 | if (i == n - 1) {
121 | result.push_back(FALSE);
122 | break;
123 | }
124 | if (codepoints[i + 1] == 0xFE0F) {
125 | result.push_back(TRUE);
126 | result.push_back(TRUE);
127 | ++i;
128 | } else if (cache.has_glyph(codepoints[i])) {
129 | result.push_back(FALSE);
130 | } else {
131 | result.push_back(TRUE);
132 | }
133 | break;
134 | case 2: // Emoji with text presentation default that can take modifier
135 | if (i == n - 1) {
136 | result.push_back(FALSE);
137 | break;
138 | }
139 | if (codepoints[i + 1] >= 0x1F3FB && codepoints[i + 1] <= 0x1F3FF) {
140 | result.push_back(TRUE);
141 | result.push_back(TRUE);
142 | ++i;
143 | } else if (cache.has_glyph(codepoints[i])) {
144 | result.push_back(FALSE);
145 | } else {
146 | result.push_back(TRUE);
147 | }
148 | break;
149 | default: // should not be reached
150 | result.push_back(FALSE);
151 | }
152 | }
153 |
154 | return true;
155 | }
156 |
157 | void load_emoji_codes_c(integers_t all, integers_t default_text, integers_t base_mod) {
158 | EmojiMap& emoji_map = get_emoji_map();
159 |
160 | for (int i = 0; i < all.size(); ++i) {
161 | emoji_map[all[i]] = 0;
162 | }
163 | for (int i = 0; i < default_text.size(); ++i) {
164 | emoji_map[default_text[i]] = 1;
165 | }
166 | for (int i = 0; i < base_mod.size(); ++i) {
167 | emoji_map[base_mod[i]] = 2;
168 | }
169 | }
170 |
171 | list_t emoji_split_c(strings_t string, strings_t path, integers_t index) {
172 | int n_strings = string.size();
173 | bool one_path = path.size() == 1;
174 | const char* first_path = Rf_translateCharUTF8(path[0]);
175 | int first_index = index[0];
176 |
177 | integers_w glyph;
178 | integers_w id;
179 | logicals_w emoji;
180 |
181 | UTF_UCS utf_converter;
182 |
183 | for (int i = 0; i < n_strings; ++i) {
184 | int n_glyphs = 0;
185 |
186 | uint32_t* glyphs = utf_converter.convert(Rf_translateCharUTF8(string[i]), n_glyphs);
187 |
188 | is_emoji(glyphs, n_glyphs, emoji, one_path ? first_path : Rf_translateCharUTF8(path[i]), one_path ? first_index : index[i]);
189 |
190 | for (int j = 0; j < n_glyphs; j++) {
191 | glyph.push_back(glyphs[j]);
192 | id.push_back(i);
193 | }
194 | }
195 |
196 | return list_w({(SEXP) glyph, (SEXP) id, (SEXP) emoji});
197 | }
198 |
199 | void export_emoji_detection(DllInfo* dll){
200 | R_RegisterCCallable("systemfonts", "detect_emoji_embedding", (DL_FUNC)detect_emoji_embedding);
201 | }
202 |
--------------------------------------------------------------------------------
/src/emoji.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | typedef std::unordered_map EmojiMap;
12 |
13 | bool has_emoji(const char* string);
14 |
15 | void detect_emoji_embedding(const uint32_t* codepoints, int n, int* embedding, const char* fontpath, int index);
16 |
17 | [[cpp11::register]]
18 | void load_emoji_codes_c(cpp11::integers all, cpp11::integers default_text, cpp11::integers base_mod);
19 |
20 | [[cpp11::register]]
21 | cpp11::list emoji_split_c(cpp11::strings string, cpp11::strings path, cpp11::integers index);
22 |
23 | [[cpp11::init]]
24 | void export_emoji_detection(DllInfo* dll);
25 |
--------------------------------------------------------------------------------
/src/font_fallback.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "font_fallback.h"
6 | #include "FontDescriptor.h"
7 | #include "cpp11/list.hpp"
8 | #include "cpp11/list_of.hpp"
9 | #include "ft_cache.h"
10 | #include "caches.h"
11 |
12 | using namespace cpp11::literals;
13 |
14 | // these functions are implemented by the platform
15 | FontDescriptor *substituteFont(char *, char *);
16 |
17 | FontDescriptor *fallback_font(const char* file, int index, const char* string, const int* axes = nullptr, const int* coords = nullptr, int n_axes = 0) {
18 | FreetypeCache& cache = get_font_cache();
19 | if (!cache.load_font(file, index)) {
20 | return NULL;
21 | }
22 | cache.set_axes(axes, coords, n_axes);
23 |
24 | std::string font_name = cache.cur_name();
25 | std::vector writable_name(font_name.begin(), font_name.end());
26 | writable_name.push_back('\0');
27 | std::vector writable_string(string, string + std::strlen(string));
28 | writable_string.push_back('\0');
29 | return substituteFont(writable_name.data(), writable_string.data());
30 | }
31 |
32 | cpp11::writable::data_frame get_fallback_c(cpp11::strings path, cpp11::integers index, cpp11::strings string, cpp11::list_of variations) {
33 | bool one_path = path.size() == 1;
34 | const char* first_path = Rf_translateCharUTF8(path[0]);
35 | int first_index = index[0];
36 | bool one_string = string.size() == 1;
37 | const char* first_string = Rf_translateCharUTF8(string[0]);
38 | int full_length = 1;
39 | if (!one_path) full_length = path.size();
40 | else if (!one_string) full_length = string.size();
41 |
42 | cpp11::writable::strings paths;
43 | paths.reserve(full_length);
44 | cpp11::writable::integers indices;
45 | indices.reserve(full_length);
46 |
47 | for (int i = 0; i < full_length; ++i) {
48 | FontDescriptor* fallback = fallback_font(
49 | one_path ? first_path : Rf_translateCharUTF8(path[i]),
50 | one_path ? first_index : index[i],
51 | one_string ? first_string : Rf_translateCharUTF8(string[i]),
52 | INTEGER(variations[i]["axis"]),
53 | INTEGER(variations[i]["value"]),
54 | Rf_xlength(variations[i]["axis"])
55 | );
56 | if (fallback == NULL) {
57 | paths.push_back(R_NaString);
58 | indices.push_back(R_NaInt);
59 | } else {
60 | paths.push_back(fallback->path);
61 | indices.push_back(fallback->index);
62 | }
63 | delete fallback;
64 | }
65 | return cpp11::writable::data_frame({
66 | "path"_nm = paths,
67 | "index"_nm = indices
68 | });
69 | }
70 |
71 | FontSettings request_fallback(const char *string, const char *path, int index) {
72 | FontDescriptor *fallback = fallback_font(path, index, string);
73 | FontSettings result = {};
74 | if (fallback == NULL) {
75 | std::strncpy(result.file, path, PATH_MAX);
76 | result.index = index;
77 | } else {
78 | std::strncpy(result.file, fallback->path, PATH_MAX);
79 | result.index = fallback->index;
80 | }
81 | delete fallback;
82 | return result;
83 | }
84 |
85 | FontSettings2 request_fallback2(const char *string, const FontSettings2& font) {
86 | FontDescriptor *fallback = fallback_font(font.file, font.index, string, font.axes, font.coords, font.n_axes);
87 | FontSettings2 result = {};
88 | if (fallback == NULL) {
89 | return font;
90 | } else {
91 | std::strncpy(result.file, fallback->path, PATH_MAX);
92 | result.index = fallback->index;
93 | }
94 | delete fallback;
95 | return result;
96 | }
97 |
98 | void export_font_fallback(DllInfo* dll) {
99 | R_RegisterCCallable("systemfonts", "get_fallback", (DL_FUNC)request_fallback);
100 | R_RegisterCCallable("systemfonts", "get_fallback2", (DL_FUNC)request_fallback2);
101 | }
102 |
--------------------------------------------------------------------------------
/src/font_fallback.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "types.h"
10 |
11 | [[cpp11::register]]
12 | cpp11::writable::data_frame get_fallback_c(cpp11::strings path, cpp11::integers index, cpp11::strings string, cpp11::list_of variations);
13 |
14 | FontSettings request_fallback(const char *string, const char *path, int index);
15 | FontSettings2 request_fallback2(const char *string, const FontSettings2& font);
16 |
17 | [[cpp11::init]]
18 | void export_font_fallback(DllInfo* dll);
19 |
--------------------------------------------------------------------------------
/src/font_local.cpp:
--------------------------------------------------------------------------------
1 | #include "font_local.h"
2 | #include "FontDescriptor.h"
3 | #include "Rinternals.h"
4 | #include "caches.h"
5 | #include "ft_cache.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | FontDescriptor *find_first_match(FontDescriptor *desc, ResultSet& font_list) {
12 | for (ResultSet::iterator it = font_list.begin(); it != font_list.end(); it++) {
13 | if ((*desc) == (**it)) {
14 | FontDescriptor* font = new FontDescriptor(*it);
15 | return font;
16 | }
17 | }
18 | return NULL;
19 | }
20 |
21 | FontDescriptor *match_local_fonts(FontDescriptor *desc) {
22 | FontDescriptor *font = find_first_match(desc, get_local_font_list());
23 |
24 | // if we didn't find anything, try again with postscriptName as family
25 | if (!font) {
26 |
27 | const char* tmp_psn = desc->postscriptName;
28 | desc->postscriptName = desc->family;
29 | desc->family = NULL;
30 |
31 | font = find_first_match(desc, get_local_font_list());
32 |
33 | desc->family = desc->postscriptName;
34 | desc->postscriptName = tmp_psn;
35 | }
36 |
37 | // might be NULL but that is ok
38 | return font;
39 | }
40 |
41 | int add_local_fonts(cpp11::strings paths) {
42 | ResultSet& font_list = get_local_font_list();
43 |
44 | std::set current_files;
45 | for (size_t i = 0; i < font_list.size(); ++i) {
46 | current_files.insert(std::string(font_list[i]->get_path()));
47 | }
48 |
49 | FreetypeCache& cache = get_font_cache();
50 |
51 | for (R_xlen_t i = 0; i < paths.size(); ++i) {
52 | std::string path(paths[i]);
53 | if (current_files.find(path) != current_files.end()) {
54 | continue;
55 | }
56 | bool success = cache.load_font(path.c_str(), 0);
57 |
58 | if (!success) {
59 | continue;
60 | }
61 |
62 | font_list.push_back(new FontDescriptor(cache.get_face(), path.c_str(), 0, cache.n_axes() != 0));
63 | int n_fonts = cache.get_face()->num_faces;
64 | for (int i = 1; i < n_fonts; ++i) {
65 | success = cache.load_font(path.c_str(), i);
66 | if (!success) {
67 | continue;
68 | }
69 | font_list.push_back(new FontDescriptor(cache.get_face(), path.c_str(), i, cache.n_axes() != 0));
70 | }
71 | }
72 |
73 | FontMap& font_map = get_font_map();
74 | font_map.clear();
75 |
76 | return 0;
77 | }
78 |
79 | void clear_local_fonts_c() {
80 | ResultSet& font_list = get_local_font_list();
81 | font_list.clear();
82 | FontMap& font_map = get_font_map();
83 | font_map.clear();
84 | }
85 |
--------------------------------------------------------------------------------
/src/font_local.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "types.h"
4 | #include "caches.h"
5 | #include "utils.h"
6 |
7 | #include
8 |
9 | FontDescriptor *find_first_match(FontDescriptor *desc, ResultSet& font_list);
10 | FontDescriptor *match_local_fonts(FontDescriptor *desc);
11 |
12 | [[cpp11::register]]
13 | int add_local_fonts(cpp11::strings paths);
14 |
15 | [[cpp11::register]]
16 | void clear_local_fonts_c();
17 |
--------------------------------------------------------------------------------
/src/font_matching.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "types.h"
11 | #include "caches.h"
12 |
13 | // Default fonts based on browser behaviour
14 | #if defined _WIN32
15 | #define SANS "Arial"
16 | #define SERIF "Times New Roman"
17 | #define MONO "Courier New"
18 | #define EMOJI "Segoe UI Emoji"
19 | #define SYMBOL "Segoe UI Symbol"
20 | #elif defined __APPLE__
21 | #define SANS "Helvetica"
22 | #define SERIF "Times"
23 | #define MONO "Courier New"
24 | #define EMOJI "Apple Color Emoji"
25 | #define SYMBOL "Symbol"
26 | #else
27 | #define SANS "sans"
28 | #define SERIF "serif"
29 | #define MONO "mono"
30 | #define EMOJI "emoji"
31 | #define SYMBOL "symbol"
32 | #endif
33 |
34 | int locate_font(const char *family, int italic, int bold, char *path, int max_path_length);
35 | FontSettings locate_font_with_features(const char *family, int italic, int bold);
36 | FontSettings2 locate_font_with_features2(const char *family, double italic, double weight, double width, const int* axes, const int* coords, int n_axes);
37 |
38 | [[cpp11::register]]
39 | cpp11::list match_font_c(cpp11::strings family, cpp11::logicals italic,
40 | cpp11::logicals bold);
41 |
42 | [[cpp11::register]]
43 | cpp11::writable::data_frame locate_fonts_c(cpp11::strings family,
44 | cpp11::doubles italic,
45 | cpp11::doubles weight,
46 | cpp11::doubles width);
47 |
48 | [[cpp11::register]]
49 | cpp11::writable::data_frame system_fonts_c();
50 |
51 | [[cpp11::register]]
52 | void reset_font_cache_c();
53 |
54 | [[cpp11::init]]
55 | void export_font_matching(DllInfo* dll);
56 |
--------------------------------------------------------------------------------
/src/font_metrics.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "cpp11/list_of.hpp"
4 | #include "types.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | [[cpp11::register]]
13 | cpp11::writable::data_frame get_font_info_c(cpp11::strings path, cpp11::integers index, cpp11::doubles size, cpp11::doubles res, cpp11::list_of variations);
14 |
15 | [[cpp11::register]]
16 | cpp11::writable::data_frame get_glyph_info_c(cpp11::strings glyphs, cpp11::strings path, cpp11::integers index, cpp11::doubles size, cpp11::doubles res, cpp11::list_of variations);
17 |
18 | int glyph_metrics(uint32_t code, const char* fontfile, int index, double size,
19 | double res, double* ascent, double* descent, double* width);
20 | int glyph_metrics2(uint32_t code, const FontSettings2& font, double size,
21 | double res, double* ascent, double* descent, double* width);
22 |
23 | int font_weight(const char* fontfile, int index);
24 | int font_weight2(const FontSettings2& font);
25 | int font_family(const char* fontfile, int index, char* family, int max_length);
26 |
27 | [[cpp11::init]]
28 | void export_font_metrics(DllInfo* dll);
29 |
--------------------------------------------------------------------------------
/src/font_outlines.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | [[cpp11::register]]
10 | cpp11::writable::data_frame get_glyph_outlines(cpp11::integers glyph, cpp11::strings path, cpp11::integers index, cpp11::doubles size, cpp11::list_of variations, double tolerance, bool verbose);
11 |
12 | [[cpp11::register]]
13 | cpp11::writable::list get_glyph_bitmap(cpp11::integers glyph, cpp11::strings path, cpp11::integers index, cpp11::doubles size, cpp11::doubles res, cpp11::list_of variations, cpp11::integers color, bool verbose);
14 |
15 | [[cpp11::init]]
16 | void export_font_outline(DllInfo* dll);
17 |
--------------------------------------------------------------------------------
/src/font_registry.cpp:
--------------------------------------------------------------------------------
1 | #include "font_registry.h"
2 | #include "caches.h"
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | using list_t = cpp11::list;
13 | using list_w = cpp11::writable::list;
14 | using data_frame_w = cpp11::writable::data_frame;
15 | using strings_t = cpp11::strings;
16 | using strings_w = cpp11::writable::strings;
17 | using integers_t = cpp11::integers;
18 | using integers_w = cpp11::writable::integers;
19 | using logicals_t = cpp11::logicals;
20 | using logicals_w = cpp11::writable::logicals;
21 |
22 | using namespace cpp11::literals;
23 |
24 | void register_font_c(strings_t family, strings_t paths, integers_t indices, strings_t features, integers_t settings) {
25 | FontReg& registry = get_font_registry();
26 | std::string name(family[0]);
27 | FontCollection col = {};
28 | for (int i = 0; i < features.size(); ++i) {
29 | const char* f = Rf_translateCharUTF8(features[i]);
30 | col.features.push_back({{f[0], f[1], f[2], f[3]}, settings[i]});
31 | }
32 | for (int i = 0; i < Rf_length(paths); ++i) {
33 | if (i > 3) continue;
34 | col.fonts[i] = {paths[i], (unsigned int) indices[i]};
35 | }
36 | registry[name] = col;
37 |
38 | FontMap& font_map = get_font_map();
39 | font_map.clear();
40 | }
41 |
42 | void clear_registry_c() {
43 | FontReg& registry = get_font_registry();
44 | registry.clear();
45 | FontMap& font_map = get_font_map();
46 | font_map.clear();
47 | }
48 |
49 | data_frame_w registry_fonts_c() {
50 | FontReg& registry = get_font_registry();
51 | int n_reg = registry.size();
52 |
53 | int n = n_reg * 4;
54 |
55 | strings_w path(n);
56 | integers_w index(n);
57 | strings_w family(n);
58 | strings_w style(n);
59 | integers_w weight(n);
60 | weight.attr("class") = {"ordered", "factor"};
61 | weight.attr("levels") = {"normal", "bold"};
62 | logicals_w italic(n);
63 | list_w features(n);
64 |
65 | int i = 0;
66 | for (auto it = registry.begin(); it != registry.end(); ++it) {
67 | for (int j = 0; j < 4; j++) {
68 | path[i] = it->second.fonts[j].file;
69 | index[i] = it->second.fonts[j].index;
70 | family[i] = it->first;
71 | switch (j) {
72 | case 0:
73 | style[i] = "Regular";
74 | break;
75 | case 1:
76 | style[i] = "Bold";
77 | break;
78 | case 2:
79 | style[i] = "Italic";
80 | break;
81 | case 3:
82 | style[i] = "Bold Italic";
83 | break;
84 | }
85 | weight[i] = 1 + (int) (j == 1 || j == 3);
86 | italic[i] = (Rboolean) (j > 1);
87 | if (it->second.features.empty()) {
88 | features[i] = integers_w();
89 | } else {
90 | int n_features = it->second.features.size();
91 | integers_w feat(n_features);
92 | strings_w tag(n_features);
93 | for (int k = 0; k < n_features; ++k) {
94 | feat[k] = it->second.features[k].setting;
95 | tag[k] = cpp11::r_string({
96 | it->second.features[k].feature[0],
97 | it->second.features[k].feature[1],
98 | it->second.features[k].feature[2],
99 | it->second.features[k].feature[3]
100 | });
101 | }
102 | feat.names() = tag;
103 | features[i] = feat;
104 | }
105 | ++i;
106 | }
107 | }
108 |
109 | data_frame_w res({
110 | "path"_nm = path,
111 | "index"_nm = index,
112 | "family"_nm = family,
113 | "style"_nm = style,
114 | "weight"_nm = weight,
115 | "italic"_nm = italic,
116 | "features"_nm = features
117 | });
118 | res.attr("class") = {"tbl_df", "tbl", "data.frame"};
119 | return res;
120 | }
121 |
122 |
123 | bool locate_in_registry(const char *family, int italic, int bold, FontSettings& res) {
124 | FontReg& registry = get_font_registry();
125 | if (registry.empty()) return false;
126 | auto search = registry.find(std::string(family));
127 | if (search == registry.end()) {
128 | return false;
129 | }
130 | int index = bold ? (italic ? 3 : 1) : (italic ? 2 : 0);
131 | strncpy(res.file, search->second.fonts[index].file.c_str(), PATH_MAX);
132 | res.file[PATH_MAX] = '\0';
133 | res.index = search->second.fonts[index].index;
134 | res.features = search->second.features.data();
135 | res.n_features = search->second.features.size();
136 | return true;
137 | }
138 |
--------------------------------------------------------------------------------
/src/font_registry.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "types.h"
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | [[cpp11::register]]
10 | void register_font_c(cpp11::strings family, cpp11::strings paths, cpp11::integers indices, cpp11::strings features, cpp11::integers settings);
11 |
12 | [[cpp11::register]]
13 | void clear_registry_c();
14 |
15 | [[cpp11::register]]
16 | cpp11::writable::data_frame registry_fonts_c();
17 |
18 | bool locate_in_registry(const char *family, int italic, int bold, FontSettings& res);
19 |
--------------------------------------------------------------------------------
/src/font_variation.cpp:
--------------------------------------------------------------------------------
1 | #include "font_variation.h"
2 | #include "Rinternals.h"
3 | #include "cpp11/integers.hpp"
4 | #include "utils.h"
5 |
6 | cpp11::writable::integers axes_to_tags(cpp11::strings axes) {
7 | cpp11::writable::integers tags(axes.size());
8 | int* tags_data = INTEGER(tags.data());
9 | for (R_xlen_t i = 0; i < axes.size(); ++i) {
10 | tags_data[i] = axis_to_tag(axes[i]);
11 | }
12 | return tags;
13 | }
14 |
15 | cpp11::writable::strings tags_to_axes(cpp11::integers tags) {
16 | cpp11::writable::strings axes(tags.size());
17 | int* tags_data = INTEGER(tags.data());
18 | for (R_xlen_t i = 0; i < axes.size(); ++i) {
19 | axes[i] = tag_to_axis(tags_data[i]);
20 | }
21 | return axes;
22 | }
23 |
24 | cpp11::writable::integers values_to_fixed(cpp11::doubles values) {
25 | cpp11::writable::integers fixed(values.size());
26 | for (R_xlen_t i = 0; i < values.size(); ++i) {
27 | fixed[i] = values[i] * FIXED_MOD;
28 | }
29 | return fixed;
30 | }
31 |
32 | cpp11::writable::doubles fixed_to_values(cpp11::integers fixed) {
33 | cpp11::writable::doubles values(fixed.size());
34 | for (R_xlen_t i = 0; i < fixed.size(); ++i) {
35 | values[i] = fixed[i] / FIXED_MOD;
36 | }
37 | return values;
38 | }
39 |
--------------------------------------------------------------------------------
/src/font_variation.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | [[cpp11::register]]
10 | cpp11::writable::integers axes_to_tags(cpp11::strings axes);
11 |
12 | [[cpp11::register]]
13 | cpp11::writable::strings tags_to_axes(cpp11::integers tags);
14 |
15 | [[cpp11::register]]
16 | cpp11::writable::integers values_to_fixed(cpp11::doubles values);
17 |
18 | [[cpp11::register]]
19 | cpp11::writable::doubles fixed_to_values(cpp11::integers fixed);
20 |
--------------------------------------------------------------------------------
/src/ft_cache.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include