├── .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 systemfonts website 16 | 17 | 18 | [![CRAN status](https://www.r-pkg.org/badges/version/systemfonts)](https://cran.r-project.org/package=systemfonts) 19 | [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html) 20 | [![R-CMD-check](https://github.com/r-lib/systemfonts/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/systemfonts/actions/workflows/R-CMD-check.yaml) 21 | [![Codecov test coverage](https://codecov.io/gh/r-lib/systemfonts/graph/badge.svg)](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 systemfonts website 5 | 6 | 7 | 8 | [![CRAN 9 | status](https://www.r-pkg.org/badges/version/systemfonts)](https://cran.r-project.org/package=systemfonts) 10 | [![Lifecycle: 11 | stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html) 12 | [![R-CMD-check](https://github.com/r-lib/systemfonts/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/systemfonts/actions/workflows/R-CMD-check.yaml) 13 | [![Codecov test 14 | coverage](https://codecov.io/gh/r-lib/systemfonts/graph/badge.svg)](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 | 2 | lifecycle: archived 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | archived 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: defunct 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | defunct 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: experimental 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | experimental 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: maturing 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | maturing 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: questioning 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | questioning 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: soft-deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | soft-deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: stable 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | lifecycle 21 | 22 | 25 | 26 | stable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: superseded 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | superseded 20 | 21 | 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 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include FT_FREETYPE_H 14 | #include FT_TYPES_H 15 | #include FT_SIZES_H 16 | #include FT_TRUETYPE_TABLES_H 17 | #include FT_MULTIPLE_MASTERS_H 18 | 19 | #ifdef __EMSCRIPTEN__ 20 | #undef TYPEOF 21 | #endif 22 | 23 | #include "cache_lru.h" 24 | 25 | 26 | struct FaceID { 27 | std::string file; 28 | unsigned int index; 29 | 30 | inline FaceID() : file(""), index(0) {} 31 | inline FaceID(std::string f) : file(f), index(0) {} 32 | inline FaceID(std::string f, unsigned int i) : file(f), index(i) {} 33 | inline FaceID(const FaceID& face) : file(face.file), index(face.index) {} 34 | 35 | inline bool operator==(const FaceID &other) const { 36 | return (index == other.index && file == other.file); 37 | } 38 | }; 39 | struct SizeID { 40 | FaceID face; 41 | double size; 42 | double res; 43 | 44 | inline SizeID() : face(), size(-1.0), res(-1.0) {} 45 | inline SizeID(FaceID f, double s, double r) : face(f), size(s), res(r) {} 46 | inline SizeID(const SizeID& s) : face(s.face), size(s.size), res(s.res) {} 47 | 48 | inline bool operator==(const SizeID &other) const { 49 | return (size == other.size && res == other.res && face == other.face); 50 | } 51 | }; 52 | 53 | namespace std { 54 | template <> 55 | struct hash { 56 | size_t operator()(const FaceID & x) const { 57 | return std::hash()(x.file) ^ std::hash()(x.index); 58 | } 59 | }; 60 | template<> 61 | struct hash { 62 | size_t operator()(const SizeID & x) const { 63 | return std::hash()(x.face) ^ std::hash()(x.size) ^ std::hash()(x.res); 64 | } 65 | }; 66 | } 67 | 68 | struct FaceStore { 69 | FT_Face face; 70 | std::unordered_set sizes; 71 | 72 | FaceStore() : sizes() {}; 73 | FaceStore(FT_Face f) : face(f), sizes() {} 74 | }; 75 | 76 | struct FontInfo { 77 | std::string family; 78 | std::string style; 79 | std::string name; 80 | bool is_italic; 81 | bool is_bold; 82 | bool is_monospace; 83 | int weight; 84 | int width; 85 | bool is_vertical; 86 | bool has_kerning; 87 | bool has_color; 88 | bool is_scalable; 89 | int n_glyphs; 90 | int n_sizes; 91 | int n_charmaps; 92 | std::vector bbox; 93 | long max_ascend; 94 | long max_descend; 95 | long max_advance_h; 96 | long max_advance_w; 97 | long lineheight; 98 | long underline_pos; 99 | long underline_size; 100 | }; 101 | 102 | struct GlyphInfo { 103 | unsigned index; 104 | long x_bearing; 105 | long y_bearing; 106 | long width; 107 | long height; 108 | long x_advance; 109 | long y_advance; 110 | std::vector bbox; 111 | }; 112 | 113 | struct VariationInfo { 114 | std::string name; 115 | double min; 116 | double max; 117 | double def; 118 | double set; 119 | }; 120 | 121 | class FaceCache : public LRU_Cache { 122 | using typename LRU_Cache::key_value_t; 123 | using typename LRU_Cache::list_t; 124 | using typename LRU_Cache::cache_list_it_t; 125 | using typename LRU_Cache::map_t; 126 | using typename LRU_Cache::cache_map_it_t; 127 | 128 | public: 129 | FaceCache() : 130 | LRU_Cache() { 131 | 132 | } 133 | FaceCache(size_t max_size) : 134 | LRU_Cache(max_size) { 135 | 136 | } 137 | 138 | void add_size_id(FaceID fid, SizeID sid) { 139 | cache_map_it_t it = _cache_map.find(fid); 140 | if (it == _cache_map.end()) { 141 | return; 142 | } 143 | it->second->second.sizes.insert(sid); 144 | } 145 | private: 146 | inline virtual void value_dtor(FaceStore& value) { 147 | FT_Done_Face(value.face); 148 | } 149 | }; 150 | 151 | class SizeCache : public LRU_Cache { 152 | public: 153 | SizeCache() : 154 | LRU_Cache() { 155 | 156 | } 157 | SizeCache(size_t max_size) : 158 | LRU_Cache(max_size) { 159 | 160 | } 161 | private: 162 | inline virtual void value_dtor(FT_Size& value) { 163 | FT_Done_Size(value); 164 | } 165 | }; 166 | 167 | class FreetypeCache { 168 | public: 169 | FreetypeCache(); 170 | ~FreetypeCache(); 171 | 172 | bool load_font(const char* file, int index, double size, double res); 173 | bool load_font(const char* file, int index); 174 | FontInfo font_info(); 175 | bool has_glyph(uint32_t index); 176 | bool load_unicode(uint32_t index); 177 | bool load_glyph(FT_UInt index, int flags = FT_LOAD_DEFAULT); 178 | GlyphInfo glyph_info(); 179 | GlyphInfo cached_glyph_info(uint32_t index, int& error); 180 | double string_width(uint32_t* string, int length, bool add_kern); 181 | long cur_lineheight(); 182 | long cur_ascender(); 183 | long cur_descender(); 184 | bool cur_is_variable(); 185 | bool get_kerning(uint32_t left, uint32_t right, long &x, long &y); 186 | bool apply_kerning(uint32_t left, uint32_t right, long &x, long &y); 187 | double tracking_diff(double tracking); 188 | FT_Face get_face(); 189 | int get_weight(); 190 | int get_width(); 191 | void get_family_name(char* family, int max_length); 192 | std::string cur_name(); 193 | std::vector cur_axes(); 194 | void has_axes(bool& weight, bool& width, bool& italic); 195 | int n_axes(); 196 | void set_axes(const int* axes, const int* vals, size_t n); 197 | int error_code; 198 | 199 | private: 200 | FT_Library library; 201 | std::map glyphstore; 202 | FaceCache face_cache; 203 | SizeCache size_cache; 204 | 205 | FaceID cur_id; 206 | int cur_var; 207 | double cur_size; 208 | double cur_res; 209 | bool cur_can_kern; 210 | unsigned int cur_glyph; 211 | bool cur_is_scalable; 212 | bool cur_has_variations; 213 | double unscaled_scaling; 214 | 215 | FT_Face face; 216 | FT_Size size; 217 | 218 | bool load_face(FaceID face); 219 | bool load_size(FaceID face, double size, double res); 220 | 221 | inline bool current_face(FaceID id, double size, double res) { 222 | return size == cur_size && res == cur_res && id == cur_id; 223 | }; 224 | 225 | bool is_variable(); 226 | 227 | }; 228 | 229 | FreetypeCache& get_font_cache(); 230 | 231 | [[cpp11::init]] 232 | void init_ft_caches(DllInfo* dll); 233 | 234 | void unload_ft_caches(DllInfo* dll); 235 | -------------------------------------------------------------------------------- /src/init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "caches.h" 3 | 4 | extern "C" void R_unload_systemfonts(DllInfo *dll) { 5 | unload_caches(dll); 6 | unload_ft_caches(dll); 7 | } 8 | -------------------------------------------------------------------------------- /src/string_metrics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | [[cpp11::register]] 11 | cpp11::list get_string_shape_c(cpp11::strings string, cpp11::integers id, 12 | cpp11::strings path, cpp11::integers index, 13 | cpp11::doubles size, cpp11::doubles res, 14 | cpp11::doubles lineheight, cpp11::integers align, 15 | cpp11::doubles hjust, cpp11::doubles vjust, 16 | cpp11::doubles width, cpp11::doubles tracking, 17 | cpp11::doubles indent, cpp11::doubles hanging, 18 | cpp11::doubles space_before, cpp11::doubles space_after); 19 | 20 | [[cpp11::register]] 21 | cpp11::doubles get_line_width_c(cpp11::strings string, cpp11::strings path, 22 | cpp11::integers index, cpp11::doubles size, 23 | cpp11::doubles res, cpp11::logicals include_bearing); 24 | 25 | int string_width(const char* string, const char* fontfile, int index, 26 | double size, double res, int include_bearing, double* width); 27 | 28 | int string_shape(const char* string, const char* fontfile, int index, 29 | double size, double res, double* x, double* y, unsigned int max_length); 30 | 31 | [[cpp11::init]] 32 | void export_string_metrics(DllInfo* dll); 33 | -------------------------------------------------------------------------------- /src/string_shape.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include FT_FREETYPE_H 8 | #include FT_TYPES_H 9 | #include FT_CACHE_H 10 | 11 | #ifdef __EMSCRIPTEN__ 12 | #undef TYPEOF 13 | #endif 14 | 15 | #include "utils.h" 16 | #include "ft_cache.h" 17 | 18 | class FreetypeShaper { 19 | public: 20 | FreetypeShaper() : 21 | width(0), 22 | height(0), 23 | left_bearing(0), 24 | right_bearing(0), 25 | top_bearing(0), 26 | bottom_bearing(0), 27 | top_border(0), 28 | left_border(0), 29 | pen_x(0), 30 | pen_y(0), 31 | error_code(0), 32 | cur_lineheight(0.0), 33 | cur_align(0), 34 | cur_string(0), 35 | cur_hjust(0.0), 36 | cur_vjust(0.0), 37 | cur_res(0.0), 38 | line_left_bear(), 39 | line_right_bear(), 40 | line_width(), 41 | line_id(), 42 | top(0), 43 | bottom(0), 44 | ascend(0), 45 | descend(0), 46 | max_width(0), 47 | indent(0), 48 | hanging(0), 49 | space_before(0), 50 | space_after(0) 51 | {}; 52 | ~FreetypeShaper() {}; 53 | 54 | static std::vector glyph_uc; 55 | static std::vector glyph_id; 56 | static std::vector string_id; 57 | static std::vector x_pos; 58 | static std::vector y_pos; 59 | static std::vector x_mid; 60 | long width; 61 | long height; 62 | long left_bearing; 63 | long right_bearing; 64 | long top_bearing; 65 | long bottom_bearing; 66 | long top_border; 67 | long left_border; 68 | long pen_x; 69 | long pen_y; 70 | 71 | int error_code; 72 | 73 | bool shape_string(const char* string, const char* fontfile, int index, 74 | double size, double res, double lineheight, 75 | int align, double hjust, double vjust, double width, 76 | double tracking, double ind, double hang, double before, 77 | double after); 78 | bool add_string(const char* string, const char* fontfile, int index, 79 | double size, double tracking); 80 | bool finish_string(); 81 | 82 | bool single_line_width(const char* string, const char* fontfile, int index, 83 | double size, double res, bool include_bearing, long& width); 84 | 85 | private: 86 | static UTF_UCS utf_converter; 87 | double cur_lineheight; 88 | int cur_align; 89 | unsigned int cur_string; 90 | double cur_hjust; 91 | double cur_vjust; 92 | double cur_res; 93 | static std::vector x_advance; 94 | static std::vector x_offset; 95 | static std::vector left_bear; 96 | static std::vector right_bear; 97 | static std::vector top_extend; 98 | static std::vector bottom_extend; 99 | static std::vector ascenders; 100 | static std::vector descenders; 101 | std::vector line_left_bear; 102 | std::vector line_right_bear; 103 | std::vector line_width; 104 | std::vector line_id; 105 | 106 | long top; 107 | long bottom; 108 | long ascend; 109 | long descend; 110 | long max_width; 111 | long indent; 112 | long hanging; 113 | long space_before; 114 | long space_after; 115 | 116 | void reset(); 117 | bool shape_glyphs(uint32_t* glyphs, int n_glyphs, FreetypeCache& cache, double tracking); 118 | 119 | inline bool glyph_is_linebreak(int id) { 120 | switch (id) { 121 | case 10: return true; 122 | case 11: return true; 123 | case 12: return true; 124 | case 13: return true; 125 | case 133: return true; 126 | case 8232: return true; 127 | case 8233: return true; 128 | } 129 | return false; 130 | } 131 | 132 | inline bool glyph_is_breaker(int id) { 133 | switch (id) { 134 | case 9: return true; 135 | case 32: return true; 136 | case 5760: return true; 137 | case 6158: return true; 138 | case 8192: return true; 139 | case 8193: return true; 140 | case 8194: return true; 141 | case 8195: return true; 142 | case 8196: return true; 143 | case 8197: return true; 144 | case 8198: return true; 145 | case 8200: return true; 146 | case 8201: return true; 147 | case 8202: return true; 148 | case 8203: return true; 149 | case 8204: return true; 150 | case 8205: return true; 151 | case 8287: return true; 152 | case 12288: return true; 153 | } 154 | return false; 155 | } 156 | }; 157 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // The exact location of a single file 11 | struct FontLoc { 12 | std::string file; 13 | unsigned int index; 14 | std::vector axes; 15 | std::vector coords; 16 | }; 17 | // Settings related to a single OpenType feature (all feature ids are 4 char long) 18 | struct FontFeature { 19 | char feature[4]; 20 | int setting; 21 | }; 22 | // A collection of four fonts (plain, bold, italic, bold-italic) along with optional features 23 | struct FontCollection { 24 | FontLoc fonts[4]; 25 | std::vector features; 26 | }; 27 | // A structure to pass around a single font with features (used by the C interface) 28 | struct FontSettings { 29 | char file[PATH_MAX + 1]; 30 | unsigned int index; 31 | const FontFeature* features; 32 | int n_features; 33 | 34 | FontSettings() : index(0), features(nullptr), n_features(0) { 35 | file[0] = '\0'; 36 | } 37 | }; 38 | // A structure to pass around a single font with features and variable axes (used by the C interface) 39 | struct FontSettings2 : public FontSettings { 40 | const int* axes; 41 | const int* coords; 42 | int n_axes; 43 | 44 | FontSettings2() : axes(nullptr), coords(nullptr), n_axes(0) { 45 | 46 | } 47 | FontSettings2(FontSettings x) : axes(nullptr), coords(nullptr), n_axes(0) { 48 | strncpy(file, x.file, PATH_MAX + 1); 49 | index = x.index; 50 | features = x.features; 51 | n_features = x.n_features; 52 | } 53 | }; 54 | // A collection of registered fonts 55 | typedef std::unordered_map FontReg; 56 | // A map of Emoji unicode points 57 | typedef std::unordered_map EmojiMap; 58 | // A map for keeping font linking on Windows 59 | typedef std::unordered_map > WinLinkMap; 60 | 61 | // Key for looking up cached font locations 62 | struct FontKey { 63 | std::string family; 64 | int weight; 65 | int width; 66 | int italic; 67 | 68 | FontKey() : family(""), weight(400), width(5), italic(0) {} 69 | FontKey(std::string _family) : family(_family), weight(400), width(5), italic(0) {} 70 | FontKey(std::string _family, int _weight, int _width, int _italic) : family(_family), weight(_weight), width(_width), italic(_italic) {} 71 | 72 | inline bool operator==(const FontKey &other) const { 73 | return (weight == other.weight && width == other.width && italic == other.italic && family == other.family); 74 | } 75 | }; 76 | namespace std { 77 | template <> 78 | struct hash { 79 | size_t operator()(const FontKey & x) const { 80 | return std::hash()(x.family) ^ std::hash()(x.weight) ^ std::hash()(x.width) ^ std::hash()(x.italic); 81 | } 82 | }; 83 | } 84 | // Map for keeping already resolved font locations 85 | typedef std::unordered_map FontMap; 86 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #define BEGIN_CPP \ 13 | SEXP err = R_NilValue; \ 14 | const size_t ERROR_SIZE = 8192; \ 15 | char buf[ERROR_SIZE] = ""; \ 16 | try { 17 | #define END_CPP \ 18 | } \ 19 | catch (cpp11::unwind_exception & e) { \ 20 | err = e.token; \ 21 | } \ 22 | catch (std::exception & e) { \ 23 | strncpy(buf, e.what(), ERROR_SIZE - 1); \ 24 | } \ 25 | catch (...) { \ 26 | strncpy(buf, "C++ error (unknown cause)", ERROR_SIZE - 1); \ 27 | } \ 28 | if (buf[0] != '\0') { \ 29 | Rf_error("%s", buf); \ 30 | } else if (err != R_NilValue) { \ 31 | R_ContinueUnwind(err); \ 32 | } 33 | 34 | const static long WGHT_TAG = 2003265652; 35 | const static long WDTH_TAG = 2003072104; 36 | const static long ITAL_TAG = 1769234796; 37 | const static double FIXED_MOD = 65536.0; 38 | 39 | inline bool strcmp_no_case(const char * A, const char * B) { 40 | if (A == NULL && B == NULL) return true; 41 | if (A == NULL || B == NULL) return false; 42 | unsigned int a_len = strlen(A); 43 | if (strlen(B) != a_len) 44 | return false; 45 | for (unsigned int i = 0; i < a_len; ++i) 46 | if (tolower(A[i]) != tolower(B[i])) 47 | return false; 48 | return true; 49 | } 50 | 51 | 52 | 53 | /* 54 | Basic UTF-8 manipulation routines 55 | by Jeff Bezanson 56 | placed in the public domain Fall 2005 57 | 58 | This code is designed to provide the utilities you need to manipulate 59 | UTF-8 as an internal string encoding. These functions do not perform the 60 | error checking normally needed when handling UTF-8 data, so if you happen 61 | to be from the Unicode Consortium you will want to flay me alive. 62 | I do this because error checking can be performed at the boundaries (I/O), 63 | with these routines reserved for higher performance on data known to be 64 | valid. 65 | 66 | Source: https://www.cprogramming.com/tutorial/utf8.c 67 | 68 | Modified 2019 by Thomas Lin Pedersen to work with const char* 69 | */ 70 | 71 | static const uint32_t offsetsFromUTF8[6] = { 72 | 0x00000000UL, 0x00003080UL, 0x000E2080UL, 73 | 0x03C82080UL, 0xFA082080UL, 0x82082080UL 74 | }; 75 | 76 | static const char trailingBytesForUTF8[256] = { 77 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 78 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 79 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 80 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 81 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 82 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 83 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 84 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 85 | }; 86 | 87 | /* conversions without error checking 88 | only works for valid UTF-8, i.e. no 5- or 6-byte sequences 89 | srcsz = source size in bytes, or -1 if 0-terminated 90 | sz = dest size in # of wide characters 91 | 92 | returns # characters converted 93 | dest will always be L'\0'-terminated, even if there isn't enough room 94 | for all the characters. 95 | if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space. 96 | */ 97 | static int u8_toucs(uint32_t *dest, int sz, const char *src, int srcsz) 98 | { 99 | uint32_t ch; 100 | const char *src_end = src + srcsz; 101 | int nb; 102 | int i=0; 103 | 104 | while (i < sz-1) { 105 | nb = trailingBytesForUTF8[(unsigned char)*src]; 106 | if (srcsz == -1) { 107 | if (*src == 0) 108 | goto done_toucs; 109 | } 110 | else { 111 | if (src + nb >= src_end) 112 | goto done_toucs; 113 | } 114 | ch = 0; 115 | switch (nb) { 116 | /* these fall through deliberately */ 117 | case 5: ch += (unsigned char)*src++; ch <<= 6; 118 | case 4: ch += (unsigned char)*src++; ch <<= 6; 119 | case 3: ch += (unsigned char)*src++; ch <<= 6; 120 | case 2: ch += (unsigned char)*src++; ch <<= 6; 121 | case 1: ch += (unsigned char)*src++; ch <<= 6; 122 | case 0: ch += (unsigned char)*src++; 123 | } 124 | ch -= offsetsFromUTF8[nb]; 125 | dest[i++] = ch; 126 | } 127 | done_toucs: 128 | dest[i] = 0; 129 | return i; 130 | } 131 | 132 | /* 133 | End of Basic UTF-8 manipulation routines 134 | by Jeff Bezanson 135 | */ 136 | 137 | class UTF_UCS { 138 | std::vector buffer; 139 | 140 | public: 141 | UTF_UCS() { 142 | // Allocate space in buffer 143 | buffer.resize(1024); 144 | } 145 | ~UTF_UCS() { 146 | } 147 | uint32_t * convert(const char * string, int &n_conv) { 148 | if (string == NULL) { 149 | n_conv = 0; 150 | return buffer.data(); 151 | } 152 | int n_bytes = strlen(string) + 1; 153 | unsigned int max_size = n_bytes * 4; 154 | if (buffer.size() < max_size) { 155 | buffer.resize(max_size); 156 | } 157 | 158 | n_conv = u8_toucs(buffer.data(), max_size, string, -1); 159 | 160 | return buffer.data(); 161 | } 162 | }; 163 | 164 | inline int axis_to_tag(std::string axis) { 165 | std::reverse(axis.begin(), axis.end()); 166 | const int *tag = reinterpret_cast(axis.c_str()); 167 | return *tag; 168 | } 169 | 170 | inline std::string tag_to_axis(int tag) { 171 | const char *axis = reinterpret_cast(&tag); 172 | std::string axis2(axis, 4); 173 | std::reverse(axis2.begin(), axis2.end()); 174 | return axis2; 175 | } 176 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(systemfonts) 3 | 4 | test_check("systemfonts") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-match_font.R: -------------------------------------------------------------------------------- 1 | context("Font Matching") 2 | 3 | sysname <- tolower(Sys.info()[["sysname"]]) 4 | font <- switch( 5 | sysname, 6 | darwin = "Helvetica", 7 | linux = "DejaVuSans", 8 | freebsd = "DejaVuSans", 9 | windows = "arial" 10 | ) 11 | 12 | test_that("Font files can be found", { 13 | font_path <- match_fonts("sans")$path 14 | 15 | expect_true(file.exists(font_path)) 16 | 17 | skip_on_os("linux") # Different fonts for different distros 18 | skip_on_os("solaris") # Have no idea what it is supposed to give 19 | expect_equal(tools::file_path_sans_ext(basename(font_path)), font) 20 | }) 21 | 22 | test_that("Default font is correct", { 23 | font_path <- match_font("sans")$path 24 | 25 | expect_true(file.exists(font_path)) 26 | 27 | skip_on_os("linux") 28 | skip_on_os("solaris") 29 | expect_equal(tools::file_path_sans_ext(basename(font_path)), font) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/testthat/test-system_fonts.R: -------------------------------------------------------------------------------- 1 | context("Font listing") 2 | 3 | test_that("System fonts can be listed", { 4 | fonts <- system_fonts() 5 | expect_is(fonts, 'data.frame') 6 | expect_gt(nrow(fonts), 0) 7 | expect_named( 8 | fonts, 9 | c( 10 | "path", 11 | "index", 12 | "name", 13 | "family", 14 | "style", 15 | "weight", 16 | "width", 17 | "italic", 18 | "monospace", 19 | "variable" 20 | ) 21 | ) 22 | }) 23 | -------------------------------------------------------------------------------- /tools/winlibs.R: -------------------------------------------------------------------------------- 1 | if (!file.exists("../windows/harfbuzz/include/harfbuzz/hb.h")) { 2 | unlink("../windows", recursive = TRUE) 3 | url <- if (grepl("aarch", R.version$platform)) { 4 | "https://github.com/r-windows/bundles/releases/download/harfbuzz-8.2.1/harfbuzz-8.2.1-clang-aarch64.tar.xz" 5 | } else if (grepl("clang", Sys.getenv('R_COMPILED_BY'))) { 6 | "https://github.com/r-windows/bundles/releases/download/harfbuzz-8.2.1/harfbuzz-8.2.1-clang-x86_64.tar.xz" 7 | } else if (getRversion() >= "4.2") { 8 | "https://github.com/r-windows/bundles/releases/download/harfbuzz-8.2.1/harfbuzz-8.2.1-ucrt-x86_64.tar.xz" 9 | } else { 10 | "https://github.com/rwinlib/harfbuzz/archive/v2.7.4.tar.gz" 11 | } 12 | download.file(url, basename(url), quiet = TRUE) 13 | dir.create("../windows", showWarnings = FALSE) 14 | untar(basename(url), exdir = "../windows", tar = 'internal') 15 | unlink(basename(url)) 16 | setwd("../windows") 17 | file.rename(list.files(), 'freetype2') 18 | } 19 | -------------------------------------------------------------------------------- /update_emoji_codes.R: -------------------------------------------------------------------------------- 1 | uc_data <- 'https://unicode.org/Public/13.0.0/ucd/emoji/emoji-data.txt' 2 | cp <- read.table(uc_data, header = FALSE, sep = ';', stringsAsFactors = FALSE) 3 | cp <- split(strsplit(trimws(cp[[1]]), '..', fixed = TRUE), trimws(cp[[2]])) 4 | emoji <- unlist(lapply(cp$Emoji, function(x) { 5 | x <- strtoi(paste0('0x', x)) 6 | if (length(x) == 1) return(x) 7 | seq.int(x[1], x[2]) 8 | })) 9 | pres <- unlist(lapply(cp$Emoji_Presentation, function(x) { 10 | x <- strtoi(paste0('0x', x)) 11 | if (length(x) == 1) return(x) 12 | seq.int(x[1], x[2]) 13 | })) 14 | text_pres_emoji <- setdiff(emoji, pres) 15 | base_mod <- unlist(lapply(cp$Emoji_Modifier_Base, function(x) { 16 | x <- strtoi(paste0('0x', x)) 17 | if (length(x) == 1) return(x) 18 | seq.int(x[1], x[2]) 19 | })) 20 | base_mod_emoji <- intersect(text_pres_emoji, base_mod) 21 | all_emoji <- unique(unlist(lapply( 22 | unlist(cp, recursive = FALSE, use.names = FALSE), 23 | function(x) { 24 | x <- strtoi(paste0('0x', x)) 25 | if (length(x) == 1) return(x) 26 | seq.int(x[1], x[2]) 27 | } 28 | ))) 29 | all_emoji <- as.integer(all_emoji) 30 | text_pres_emoji <- as.integer(text_pres_emoji) 31 | base_mod_emoji <- as.integer(base_mod_emoji) 32 | 33 | save( 34 | all_emoji, 35 | text_pres_emoji, 36 | base_mod_emoji, 37 | file = 'R/sysdata.rda', 38 | version = 2L 39 | ) 40 | 41 | rm( 42 | uc_data, 43 | cp, 44 | emoji, 45 | pres, 46 | text_pres_emoji, 47 | base_mod, 48 | base_mod_emoji, 49 | all_emoji 50 | ) 51 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | 4 | /.quarto/ 5 | -------------------------------------------------------------------------------- /vignettes/c_interface.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "systemfonts C interface" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{systemfonts C interface} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r} 11 | #| include: false 12 | knitr::opts_chunk$set( 13 | collapse = TRUE, 14 | comment = "#>" 15 | ) 16 | ``` 17 | 18 | ```{r} 19 | #| label: setup 20 | library(systemfonts) 21 | ``` 22 | 23 | Most of the functionality in systemfonts is intended to be used from compiled 24 | code to help e.g. graphic devices to resolve font specifications to a font file 25 | prior to rendering. systemfonts provide key functionality to get called at the 26 | C level by putting systemfonts in the `LinkingTo` field in the description and 27 | adding `#include ` to your C code. Make sure systemfonts is 28 | loaded before using it, e.g. by having `match_fonts()` imported into your package 29 | namespace. All functions are provided in the `systemfonts::ver2` namespace. 30 | Legacy API is not namespaced. The different functionality will be discussed 31 | below: 32 | 33 | ## Font matching 34 | The C equivalent of the `match_fonts()` R function is `locate_font()` with the 35 | following signature: 36 | 37 | ```C 38 | FontSettings2 locate_font( 39 | const char *family, 40 | double italic, 41 | double weight, 42 | double width, 43 | const int* axes, 44 | const int* coords, 45 | int n_axes 46 | ) 47 | ``` 48 | 49 | It takes a UTF-8 encoded string with the font family name, a double giving 50 | italic (usually 0.0 == "upright" and 1.0 == "italic"), a double giving weight 51 | (usually ranging between 100.0 and 1000.0 — 0.0 means "undefined") and a double 52 | giving "width" (usually ranging from 1.0 to 10.0 — 0.0 means undefined). Lastly 53 | you can provide variable axis coords with the `axes` and `coords` array pointers 54 | with `n_axes` giving the number in the arrays (which are assumed to be of the 55 | same length). The values of each array are not immediately understandable to the 56 | human eye and will usually come from a user through a call to 57 | `font_variation()`. If the axes array contain "ital", "wght", and/or "wdth" 58 | *and* the font has these variable axes then the values for these axes will 59 | overwrite the values provide in `italic`, `weight`, and `width`. 60 | 61 | The returned `FontSettings2` struct will contain both the font location and 62 | index along with any OpenType feature settings and the axes settings in the case 63 | of a variable font. The struct (along with its `FontFeature` struct dependency) 64 | is shown below and is pretty self-documenting. 65 | 66 | Do not cache the `FontSettings2` struct as the `features`, `axes`, and `coords` 67 | arrays may be cleared at any time after the call has ended. systemfonts itself 68 | takes care of caching so this is not something you should be concerned with in 69 | your code. 70 | 71 | ```C 72 | struct FontFeature { 73 | char feature[4]; 74 | int setting; 75 | }; 76 | struct FontSettings { 77 | char file[PATH_MAX + 1]; 78 | unsigned int index; 79 | const FontFeature* features; 80 | int n_features; 81 | const int* axes; 82 | const int* coords; 83 | int n_axes; 84 | }; 85 | ``` 86 | 87 | ## Glyph metrics 88 | The C equivalent of `glyph_info()` is `glyph_metrics()` with the following 89 | signature: 90 | 91 | ```C 92 | int glyph_metrics( 93 | uint32_t code, 94 | const FontSettings2& font, 95 | double size, 96 | double res, 97 | double* ascent, 98 | double* descent, 99 | double* width 100 | ) 101 | ``` 102 | 103 | It takes the glyph to measure as an int giving the UTF code of the glyph, with a 104 | `FontSettings2` object describing the font. Further it takes a size in pt and a 105 | resolution in ppi. It will write the ascent, descent, and width in pts to the 106 | pointers passed in, and return `0` if the operation was successful. 107 | 108 | ## Retrieving cached freetype face 109 | A heavy part of text layouting is reading and parsing font files. systemfonts 110 | contains its own cache to make sure that parsing is kept at a minimum. If you 111 | want to use this cache to load and cache freetype face object (FT_Face) you can 112 | use `get_cached_face()`. This resides in a separate header (`systemfonts-ft.h`) 113 | because it requires FreeType to be linked in your package, which the rest of the 114 | C api does not. It will look in the cache for a face and size that 115 | matches your request and return that if found. If not, it will load it for you 116 | and add it to the cache, before returning it to you. `get_cached_face()` sets 117 | the passed int error pointer to 0 if successful. 118 | 119 | ```C 120 | get_cached_face( 121 | const FontSettings2& font, 122 | double size, 123 | double res, 124 | int * error 125 | ) 126 | ``` 127 | 128 | Freetype uses reference counting to keep track of objects and the count is 129 | increased by a call to `get_cached_face()`. It is the responsibility of the 130 | caller to decrease it once the face is no longer needed using `FT_Done_Face()`. 131 | 132 | ## Font fallback 133 | When rendering text it is not given that all the requested characters have a 134 | glyph in the given font. While one can elect to render a "missing glyph" glyph 135 | (often either an empty square or a questionmark in a tilted square) a better 136 | approach is often to find a font substitute that does contain the character and 137 | use that for rendering it. This function allows you to find a fallback font for 138 | a given string and font. The string should be stripped of characters that you 139 | already know how to render. The fallback font is returned as a `FontSettings2` 140 | object, though features are always empty. 141 | 142 | ```C 143 | FontSettings2 get_fallback( 144 | const char* string, 145 | const FontSettings2& font 146 | ) 147 | ``` 148 | 149 | ## Font Weight 150 | When encoding text with CSS it may be necessary to know the exact weight of the 151 | font given by a file so that it may be reflected in the style sheet. This 152 | function takes a `FontSettings2` object and returns the weight (100-900 or 0 if 153 | it is undefined by the font) respecting the variable axes settings if given. 154 | 155 | ```C 156 | int get_font_weight( 157 | const FontSettings2& font 158 | ) 159 | ``` 160 | 161 | ## Family name 162 | It may be beneficial to know the family name from a given font. This can be 163 | obtained with `get_font_family()` which will write the name to the provided 164 | `char*` argument. It will return 0 if it was somehow unsuccessful. 165 | 166 | ```C 167 | int get_font_family( 168 | const FontSettings2& font, 169 | char* family, 170 | int max_length 171 | ) 172 | ``` 173 | 174 | ## Emoji location 175 | Figuring out which character in a string should be treated as an emoji is 176 | non-trivial due to the existence of emojis with text representation default etc. 177 | systemfonts allow you to get the embedding of emojis in a string based on the 178 | correct rules. 179 | 180 | ```C 181 | void detect_emoji_embedding( 182 | const uint32_t* string, 183 | int n, 184 | int* embedding, 185 | const FontSettings2& font 186 | ) 187 | ``` 188 | --------------------------------------------------------------------------------