systemfonts/ 0000755 0001762 0000144 00000000000 13506400572 012652 5 ustar ligges users systemfonts/inst/ 0000755 0001762 0000144 00000000000 13506366517 013641 5 ustar ligges users systemfonts/inst/doc/ 0000755 0001762 0000144 00000000000 13506366517 014406 5 ustar ligges users systemfonts/inst/doc/c_interface.R 0000644 0001762 0000144 00000000354 13506366516 016774 0 ustar ligges users ## ---- include = FALSE----------------------------------------------------
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
## ----setup---------------------------------------------------------------
library(systemfonts)
systemfonts/inst/doc/c_interface.Rmd 0000644 0001762 0000144 00000003670 13502750205 017305 0 ustar ligges users ---
title: "systemfonts C interface"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{systemfonts C interface}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
```{r setup}
library(systemfonts)
```
Most of the functionality in systemfonts is intended to be used from compiled
code to help e.g. graphic devices to resolve font specifications to a font file
prior to rendering. systemfonts provide key functionality to get called at the
C level using the `R_getCCallable()` function. The systemfonts namespace must
be loaded for this to work, so be sure to import e.g. `match_font()` in the
package namespace. For more information about this interface see
[*Registering native routines*](https://cran.r-project.org/doc/manuals/R-exts.html#Registering-native-routines)
in *Writing R Extensions*.
## Font matching
The C equivalent of the `match_font()` R function is `locate_font()` with the
following signature:
```C
int locate_font(const char *family, int italic, int bold, char *path, int max_path_length)
```
It takes a UTF-8 encoded string with the font family name, an int setting both
italic and bold styles along with a char pointer to be filled with the located
path and the maximum length it can hold. The return value is an int giving the
index of the font in the font file.
The best way to use the function in a package is to wrap it in a function to
avoid fetching it multiple times, e.g.
```C
static int locate_font(const char *family, int italic, int bold, char *path, int max_path_length) {
static int (*p_locate_font)(const char *family, int italic, int bold, char *path, int max_path_length) = NULL;
if (p_locate_font == NULL) {
p_locate_font = (int(*)(const char *, int, int, char *, int)) R_GetCCallable("systemfonts", "locate_font");
}
return p_locate_font(family, italic, bold, path, max_path_length);
}
```
systemfonts/inst/doc/c_interface.html 0000644 0001762 0000144 00000025715 13506366517 017550 0 ustar ligges users
systemfonts C interface
systemfonts C interface
Most of the functionality in systemfonts is intended to be used from compiled code to help e.g. graphic devices to resolve font specifications to a font file prior to rendering. systemfonts provide key functionality to get called at the C level using the R_getCCallable()
function. The systemfonts namespace must be loaded for this to work, so be sure to import e.g. match_font()
in the package namespace. For more information about this interface see Registering native routines in Writing R Extensions.
Font matching
The C equivalent of the match_font()
R function is locate_font()
with the following signature:
It takes a UTF-8 encoded string with the font family name, an int setting both italic and bold styles along with a char pointer to be filled with the located path and the maximum length it can hold. The return value is an int giving the index of the font in the font file.
The best way to use the function in a package is to wrap it in a function to avoid fetching it multiple times, e.g.
static int locate_font(const char *family, int italic, int bold, char *path, int max_path_length) {
static int (*p_locate_font)(const char *family, int italic, int bold, char *path, int max_path_length) = NULL;
if (p_locate_font == NULL) {
p_locate_font = (int(*)(const char *, int, int, char *, int)) R_GetCCallable("systemfonts", "locate_font");
}
return p_locate_font(family, italic, bold, path, max_path_length);
}
systemfonts/tests/ 0000755 0001762 0000144 00000000000 13502411462 014010 5 ustar ligges users systemfonts/tests/testthat.R 0000644 0001762 0000144 00000000102 13502411462 015764 0 ustar ligges users library(testthat)
library(systemfonts)
test_check("systemfonts")
systemfonts/tests/testthat/ 0000755 0001762 0000144 00000000000 13506400572 015654 5 ustar ligges users systemfonts/tests/testthat/test-system_fonts.R 0000644 0001762 0000144 00000000444 13502666007 021516 0 ustar ligges users context("Font listing")
test_that("System fonts can be listed", {
fonts <- system_fonts()
expect_is(fonts, 'data.frame')
expect_gt(nrow(fonts), 0)
expect_named(fonts, c("path", "index", "name", "family", "style", "weight",
"width", "italic", "monospace"))
})
systemfonts/tests/testthat/test-match_font.R 0000644 0001762 0000144 00000001324 13506366416 021106 0 ustar ligges users context("Font Matching")
sysname <- tolower(Sys.info()[["sysname"]])
font <- switch(
sysname,
darwin = "Helvetica",
linux = "DejaVuSans",
windows = "arial"
)
test_that("Font files can be found", {
font_path <- match_font("sans")$path
expect_true(file.exists(font_path))
skip_on_os("linux") # Different fonts for different distros
skip_on_os("solaris") # Have no idea what it is supposed to give
expect_equal(tools::file_path_sans_ext(basename(font_path)), font)
})
test_that("Default font is correct", {
font_path <- match_font("sans")$path
expect_true(file.exists(font_path))
skip_on_os("linux")
skip_on_os("solaris")
expect_equal(tools::file_path_sans_ext(basename(font_path)), font)
})
systemfonts/src/ 0000755 0001762 0000144 00000000000 13506366517 013453 5 ustar ligges users systemfonts/src/FontDescriptor.h 0000755 0001762 0000144 00000007051 13502433634 016566 0 ustar ligges users #ifndef FONT_DESCRIPTOR_H
#define FONT_DESCRIPTOR_H
#include
#include
enum FontWeight {
FontWeightUndefined = 0,
FontWeightThin = 100,
FontWeightUltraLight = 200,
FontWeightLight = 300,
FontWeightNormal = 400,
FontWeightMedium = 500,
FontWeightSemiBold = 600,
FontWeightBold = 700,
FontWeightUltraBold = 800,
FontWeightHeavy = 900
};
enum FontWidth {
FontWidthUndefined = 0,
FontWidthUltraCondensed = 1,
FontWidthExtraCondensed = 2,
FontWidthCondensed = 3,
FontWidthSemiCondensed = 4,
FontWidthNormal = 5,
FontWidthSemiExpanded = 6,
FontWidthExpanded = 7,
FontWidthExtraExpanded = 8,
FontWidthUltraExpanded = 9
};
struct FontDescriptor {
public:
const char *path;
int index;
const char *postscriptName;
const char *family;
const char *style;
FontWeight weight;
FontWidth width;
bool italic;
bool monospace;
FontDescriptor() {
path = NULL;
index = -1;
postscriptName = NULL;
family = NULL;
style = NULL;
weight = FontWeightUndefined;
width = FontWidthUndefined;
italic = false;
monospace = false;
}
// Constructor added by Thomas Lin Pedersen
FontDescriptor(const char *family, bool italic, bool bold) {
this->path = NULL;
this->index = -1;
this->postscriptName = NULL;
this->family = copyString(family);
this->style = NULL;
this->weight = bold ? FontWeightBold : FontWeightNormal;
this->width = FontWidthUndefined;
this->italic = italic;
this->monospace = false;
}
FontDescriptor(const char *path, const char *postscriptName, const char *family, const char *style,
FontWeight weight, FontWidth width, bool italic, bool monospace) {
this->path = copyString(path);
this->index = 0;
this->postscriptName = copyString(postscriptName);
this->family = copyString(family);
this->style = copyString(style);
this->weight = weight;
this->width = width;
this->italic = italic;
this->monospace = monospace;
}
FontDescriptor(const char *path, int index, const char *postscriptName, const char *family, const char *style,
FontWeight weight, FontWidth width, bool italic, bool monospace) {
this->path = copyString(path);
this->index = index;
this->postscriptName = copyString(postscriptName);
this->family = copyString(family);
this->style = copyString(style);
this->weight = weight;
this->width = width;
this->italic = italic;
this->monospace = monospace;
}
FontDescriptor(FontDescriptor *desc) {
path = copyString(desc->path);
index = desc->index;
postscriptName = copyString(desc->postscriptName);
family = copyString(desc->family);
style = copyString(desc->style);
weight = desc->weight;
width = desc->width;
italic = desc->italic;
monospace = desc->monospace;
}
~FontDescriptor() {
if (path)
delete[] path;
if (postscriptName)
delete[] postscriptName;
if (family)
delete[] family;
if (style)
delete[] style;
postscriptName = NULL;
family = NULL;
style = NULL;
}
private:
char *copyString(const char *input) {
if (!input)
return NULL;
char *str = new char[strlen(input) + 1];
strcpy(str, input);
return str;
}
};
class ResultSet : public std::vector {
public:
~ResultSet() {
for (ResultSet::iterator it = this->begin(); it != this->end(); it++) {
delete *it;
}
}
int n_fonts() {
return size();
}
};
#endif
systemfonts/src/systemfonts.cpp 0000755 0001762 0000144 00000013552 13502710243 016547 0 ustar ligges users #include
#include
#include "systemfonts.h"
#include "utils.h"
#include "FontDescriptor.h"
// these functions are implemented by the platform
ResultSet *getAvailableFonts();
ResultSet *findFonts(FontDescriptor *);
FontDescriptor *findFont(FontDescriptor *);
FontDescriptor *substituteFont(char *, char *);
// Default fonts based on browser behaviour
#if defined _WIN32
#define SANS "Arial"
#define SERIF "Times New Roman"
#define MONO "Courier New"
#elif defined __APPLE__
#define SANS "Helvetica"
#define SERIF "Times"
#define MONO "Courier"
#else
#define SANS "sans"
#define SERIF "serif"
#define MONO "mono"
#endif
int locate_font(const char *family, int italic, int bold, char *path, int max_path_length) {
const char* resolved_family = family;
if (strcmp_no_case(family, "") || strcmp_no_case(family, "sans")) {
resolved_family = SANS;
} else if (strcmp_no_case(family, "serif")) {
resolved_family = SERIF;
} else if (strcmp_no_case(family, "mono")) {
resolved_family = MONO;
}
FontDescriptor font_desc(resolved_family, italic, bold);
FontDescriptor* font_loc = findFont(&font_desc);
strncpy(path, font_loc->path, max_path_length);
int index = font_loc->index;
delete font_loc;
return index;
}
SEXP match_font(SEXP family, SEXP italic, SEXP bold) {
char *path = new char[PATH_MAX+1];
path[PATH_MAX] = '\0';
int index = locate_font(Rf_translateCharUTF8(STRING_ELT(family, 0)),
LOGICAL(italic)[0], LOGICAL(bold)[0], path, PATH_MAX);
SEXP font = PROTECT(Rf_allocVector(VECSXP, 2));
SET_VECTOR_ELT(font, 0, Rf_mkString(path));
SET_VECTOR_ELT(font, 1, Rf_ScalarInteger(index));
SEXP names = PROTECT(Rf_allocVector(STRSXP, 2));
SET_STRING_ELT(names, 0, Rf_mkChar("path"));
SET_STRING_ELT(names, 1, Rf_mkChar("index"));
Rf_setAttrib(font, Rf_install("names"), names);
delete[] path;
UNPROTECT(2);
return font;
}
SEXP system_fonts() {
SEXP res = PROTECT(Rf_allocVector(VECSXP, 9));
SEXP cl = PROTECT(Rf_allocVector(STRSXP, 3));
SET_STRING_ELT(cl, 0, Rf_mkChar("tbl_df"));
SET_STRING_ELT(cl, 1, Rf_mkChar("tbl"));
SET_STRING_ELT(cl, 2, Rf_mkChar("data.frame"));
Rf_classgets(res, cl);
SEXP names = PROTECT(Rf_allocVector(STRSXP, 9));
SET_STRING_ELT(names, 0, Rf_mkChar("path"));
SET_STRING_ELT(names, 1, Rf_mkChar("index"));
SET_STRING_ELT(names, 2, Rf_mkChar("name"));
SET_STRING_ELT(names, 3, Rf_mkChar("family"));
SET_STRING_ELT(names, 4, Rf_mkChar("style"));
SET_STRING_ELT(names, 5, Rf_mkChar("weight"));
SET_STRING_ELT(names, 6, Rf_mkChar("width"));
SET_STRING_ELT(names, 7, Rf_mkChar("italic"));
SET_STRING_ELT(names, 8, Rf_mkChar("monospace"));
setAttrib(res, Rf_install("names"), names);
ResultSet* all_fonts = getAvailableFonts();
int n = all_fonts->n_fonts();
SEXP path = PROTECT(Rf_allocVector(STRSXP, n));
SEXP index = PROTECT(Rf_allocVector(INTSXP, n));
SEXP name = PROTECT(Rf_allocVector(STRSXP, n));
SEXP family = PROTECT(Rf_allocVector(STRSXP, n));
SEXP style = PROTECT(Rf_allocVector(STRSXP, n));
SEXP fct_cl = PROTECT(Rf_allocVector(STRSXP, 2));
SET_STRING_ELT(fct_cl, 0, Rf_mkChar("ordered"));
SET_STRING_ELT(fct_cl, 0, Rf_mkChar("factor"));
SEXP weight = PROTECT(Rf_allocVector(INTSXP, n));
SEXP weight_lvl = PROTECT(Rf_allocVector(STRSXP, 9));
SET_STRING_ELT(weight_lvl, 0, Rf_mkChar("thin"));
SET_STRING_ELT(weight_lvl, 1, Rf_mkChar("ultralight"));
SET_STRING_ELT(weight_lvl, 2, Rf_mkChar("light"));
SET_STRING_ELT(weight_lvl, 3, Rf_mkChar("normal"));
SET_STRING_ELT(weight_lvl, 4, Rf_mkChar("medium"));
SET_STRING_ELT(weight_lvl, 5, Rf_mkChar("semibold"));
SET_STRING_ELT(weight_lvl, 6, Rf_mkChar("bold"));
SET_STRING_ELT(weight_lvl, 7, Rf_mkChar("ultrabold"));
SET_STRING_ELT(weight_lvl, 8, Rf_mkChar("heavy"));
Rf_classgets(weight, fct_cl);
Rf_setAttrib(weight, Rf_install("levels"), weight_lvl);
SEXP width = PROTECT(Rf_allocVector(INTSXP, n));
SEXP width_lvl = PROTECT(Rf_allocVector(STRSXP, 9));
SET_STRING_ELT(width_lvl, 0, Rf_mkChar("ultracondensed"));
SET_STRING_ELT(width_lvl, 1, Rf_mkChar("extracondensed"));
SET_STRING_ELT(width_lvl, 2, Rf_mkChar("condensed"));
SET_STRING_ELT(width_lvl, 3, Rf_mkChar("semicondensed"));
SET_STRING_ELT(width_lvl, 4, Rf_mkChar("normal"));
SET_STRING_ELT(width_lvl, 5, Rf_mkChar("semiexpanded"));
SET_STRING_ELT(width_lvl, 6, Rf_mkChar("expanded"));
SET_STRING_ELT(width_lvl, 7, Rf_mkChar("extraexpanded"));
SET_STRING_ELT(width_lvl, 8, Rf_mkChar("ultraexpanded"));
Rf_classgets(width, fct_cl);
Rf_setAttrib(width, Rf_install("levels"), width_lvl);
SEXP italic = PROTECT(Rf_allocVector(LGLSXP, n));
SEXP monospace = PROTECT(Rf_allocVector(LGLSXP, n));
SET_VECTOR_ELT(res, 0, path);
SET_VECTOR_ELT(res, 1, index);
SET_VECTOR_ELT(res, 2, name);
SET_VECTOR_ELT(res, 3, family);
SET_VECTOR_ELT(res, 4, style);
SET_VECTOR_ELT(res, 5, weight);
SET_VECTOR_ELT(res, 6, width);
SET_VECTOR_ELT(res, 7, italic);
SET_VECTOR_ELT(res, 8, monospace);
int i = 0;
for (ResultSet::iterator it = all_fonts->begin(); it != all_fonts->end(); it++) {
SET_STRING_ELT(path, i, Rf_mkChar((*it)->path));
INTEGER(index)[i] = (*it)->index;
SET_STRING_ELT(name, i, Rf_mkChar((*it)->postscriptName));
SET_STRING_ELT(family, i, Rf_mkChar((*it)->family));
SET_STRING_ELT(style, i, Rf_mkChar((*it)->style));
if ((*it)->weight == 0) {
INTEGER(weight)[i] = NA_INTEGER;
} else {
INTEGER(weight)[i] = (*it)->weight / 100;
}
if ((*it)->width == 0) {
INTEGER(width)[i] = NA_INTEGER;
} else {
INTEGER(width)[i] = (int) (*it)->width;
}
LOGICAL(italic)[i] = (int) (*it)->italic;
LOGICAL(monospace)[i] = (int) (*it)->monospace;
i++;
}
SEXP row_names = PROTECT(Rf_allocVector(REALSXP, 2));
REAL(row_names)[0] = NA_REAL;
REAL(row_names)[1] = -n;
setAttrib(res, Rf_install("row.names"), row_names);
UNPROTECT(16);
return res;
}
systemfonts/src/systemfonts.h 0000644 0001762 0000144 00000000317 13502434122 016203 0 ustar ligges users #include
#include
int locate_font(const char *family, int italic, int bold, char *path, int max_path_length);
SEXP match_font(SEXP family, SEXP italic, SEXP bold);
SEXP system_fonts();
systemfonts/src/win/ 0000755 0001762 0000144 00000000000 13502424665 014243 5 ustar ligges users systemfonts/src/win/DirectWriteFontManagerWindows.cpp 0000755 0001762 0000144 00000030434 13476460513 022702 0 ustar ligges users #define WINVER 0x0600
#include "../FontDescriptor.h"
#include
#include
#include
// throws a JS error when there is some exception in DirectWrite
#define HR(hr) \
if (FAILED(hr)) throw "Font loading error";
WCHAR *utf8ToUtf16(const char *input) {
unsigned int len = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
WCHAR *output = new WCHAR[len];
MultiByteToWideChar(CP_UTF8, 0, input, -1, output, len);
return output;
}
char *utf16ToUtf8(const WCHAR *input) {
unsigned int len = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
char *output = new char[len];
WideCharToMultiByte(CP_UTF8, 0, input, -1, output, len, NULL, NULL);
return output;
}
// returns the index of the user's locale in the set of localized strings
unsigned int getLocaleIndex(IDWriteLocalizedStrings *strings) {
unsigned int index = 0;
BOOL exists = false;
wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
// Get the default locale for this user.
int success = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH);
// If the default locale is returned, find that locale name, otherwise use "en-us".
if (success) {
HR(strings->FindLocaleName(localeName, &index, &exists));
}
// if the above find did not find a match, retry with US English
if (!exists) {
HR(strings->FindLocaleName(L"en-us", &index, &exists));
}
if (!exists)
index = 0;
return index;
}
// gets a localized string for a font
char *getString(IDWriteFont *font, DWRITE_INFORMATIONAL_STRING_ID string_id) {
char *res = NULL;
IDWriteLocalizedStrings *strings = NULL;
BOOL exists = false;
HR(font->GetInformationalStrings(
string_id,
&strings,
&exists
));
if (exists) {
unsigned int index = getLocaleIndex(strings);
unsigned int len = 0;
WCHAR *str = NULL;
HR(strings->GetStringLength(index, &len));
str = new WCHAR[len + 1];
HR(strings->GetString(index, str, len + 1));
// convert to utf8
res = utf16ToUtf8(str);
delete str;
strings->Release();
}
if (!res) {
res = new char[1];
res[0] = '\0';
}
return res;
}
FontDescriptor *resultFromFont(IDWriteFont *font) {
FontDescriptor *res = NULL;
IDWriteFontFace *face = NULL;
unsigned int numFiles = 0;
HR(font->CreateFontFace(&face));
// get the font files from this font face
IDWriteFontFile *files = NULL;
HR(face->GetFiles(&numFiles, NULL));
HR(face->GetFiles(&numFiles, &files));
// return the first one
if (numFiles > 0) {
IDWriteFontFileLoader *loader = NULL;
IDWriteLocalFontFileLoader *fileLoader = NULL;
unsigned int nameLength = 0;
const void *referenceKey = NULL;
unsigned int referenceKeySize = 0;
WCHAR *name = NULL;
HR(files[0].GetLoader(&loader));
// check if this is a local font file
HRESULT hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void **)&fileLoader);
if (SUCCEEDED(hr)) {
// get the file path
HR(files[0].GetReferenceKey(&referenceKey, &referenceKeySize));
HR(fileLoader->GetFilePathLengthFromKey(referenceKey, referenceKeySize, &nameLength));
name = new WCHAR[nameLength + 1];
HR(fileLoader->GetFilePathFromKey(referenceKey, referenceKeySize, name, nameLength + 1));
char *psName = utf16ToUtf8(name);
char *postscriptName = getString(font, DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME);
char *family = getString(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES);
char *style = getString(font, DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES);
// this method requires windows 7, so we need to cast to an IDWriteFontFace1
IDWriteFontFace1 *face1 = static_cast(face);
bool monospace = face1->IsMonospacedFont() == TRUE;
res = new FontDescriptor(
psName,
postscriptName,
family,
style,
(FontWeight) font->GetWeight(),
(FontWidth) font->GetStretch(),
font->GetStyle() == DWRITE_FONT_STYLE_ITALIC,
monospace
);
delete psName;
delete name;
delete postscriptName;
delete family;
delete style;
fileLoader->Release();
}
loader->Release();
}
face->Release();
files->Release();
return res;
}
ResultSet *getAvailableFonts() {
ResultSet *res = new ResultSet();
int count = 0;
IDWriteFactory *factory = NULL;
HR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast(&factory)
));
// Get the system font collection.
IDWriteFontCollection *collection = NULL;
HR(factory->GetSystemFontCollection(&collection));
// Get the number of font families in the collection.
int familyCount = collection->GetFontFamilyCount();
// track postscript names we've already added
// using a set so we don't get any duplicates.
std::unordered_set psNames;
for (int i = 0; i < familyCount; i++) {
IDWriteFontFamily *family = NULL;
// Get the font family.
HR(collection->GetFontFamily(i, &family));
int fontCount = family->GetFontCount();
for (int j = 0; j < fontCount; j++) {
IDWriteFont *font = NULL;
HR(family->GetFont(j, &font));
FontDescriptor *result = resultFromFont(font);
if (psNames.count(result->postscriptName) == 0) {
res->push_back(resultFromFont(font));
psNames.insert(result->postscriptName);
}
}
family->Release();
}
collection->Release();
factory->Release();
return res;
}
bool resultMatches(FontDescriptor *result, FontDescriptor *desc) {
if (desc->postscriptName && strcmp(desc->postscriptName, result->postscriptName) != 0)
return false;
if (desc->family && strcmp(desc->family, result->family) != 0)
return false;
if (desc->style && strcmp(desc->style, result->style) != 0)
return false;
if (desc->weight && desc->weight != result->weight)
return false;
if (desc->width && desc->width != result->width)
return false;
if (desc->italic != result->italic)
return false;
if (desc->monospace != result->monospace)
return false;
return true;
}
ResultSet *findFonts(FontDescriptor *desc) {
ResultSet *fonts = getAvailableFonts();
for (ResultSet::iterator it = fonts->begin(); it != fonts->end();) {
if (!resultMatches(*it, desc)) {
delete *it;
it = fonts->erase(it);
} else {
it++;
}
}
return fonts;
}
FontDescriptor *findFont(FontDescriptor *desc) {
ResultSet *fonts = findFonts(desc);
// if we didn't find anything, try again with only the font traits, no string names
if (fonts->size() == 0) {
delete fonts;
FontDescriptor *fallback = new FontDescriptor(
NULL, NULL, NULL, NULL,
desc->weight, desc->width, desc->italic, false
);
fonts = findFonts(fallback);
}
// ok, nothing. shouldn't happen often.
// just return the first available font
if (fonts->size() == 0) {
delete fonts;
fonts = getAvailableFonts();
}
// hopefully we found something now.
// copy and return the first result
if (fonts->size() > 0) {
FontDescriptor *res = new FontDescriptor(fonts->front());
delete fonts;
return res;
}
// whoa, weird. no fonts installed or something went wrong.
delete fonts;
return NULL;
}
// custom text renderer used to determine the fallback font for a given char
class FontFallbackRenderer : public IDWriteTextRenderer {
public:
IDWriteFontCollection *systemFonts;
IDWriteFont *font;
unsigned long refCount;
FontFallbackRenderer(IDWriteFontCollection *collection) {
refCount = 0;
collection->AddRef();
systemFonts = collection;
font = NULL;
}
~FontFallbackRenderer() {
if (systemFonts)
systemFonts->Release();
if (font)
font->Release();
}
// IDWriteTextRenderer methods
IFACEMETHOD(DrawGlyphRun)(
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const *glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription,
IUnknown *clientDrawingEffect) {
// save the font that was actually rendered
return systemFonts->GetFontFromFontFace(glyphRun->fontFace, &font);
}
IFACEMETHOD(DrawUnderline)(
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_UNDERLINE const *underline,
IUnknown *clientDrawingEffect) {
return E_NOTIMPL;
}
IFACEMETHOD(DrawStrikethrough)(
void *clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const *strikethrough,
IUnknown *clientDrawingEffect) {
return E_NOTIMPL;
}
IFACEMETHOD(DrawInlineObject)(
void *clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject *inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown *clientDrawingEffect) {
return E_NOTIMPL;
}
// IDWritePixelSnapping methods
IFACEMETHOD(IsPixelSnappingDisabled)(void *clientDrawingContext, BOOL *isDisabled) {
*isDisabled = FALSE;
return S_OK;
}
IFACEMETHOD(GetCurrentTransform)(void *clientDrawingContext, DWRITE_MATRIX *transform) {
const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
*transform = ident;
return S_OK;
}
IFACEMETHOD(GetPixelsPerDip)(void *clientDrawingContext, FLOAT *pixelsPerDip) {
*pixelsPerDip = 1.0f;
return S_OK;
}
// IUnknown methods
IFACEMETHOD_(unsigned long, AddRef)() {
return InterlockedIncrement(&refCount);
}
IFACEMETHOD_(unsigned long, Release)() {
unsigned long newCount = InterlockedDecrement(&refCount);
if (newCount == 0) {
delete this;
return 0;
}
return newCount;
}
IFACEMETHOD(QueryInterface)(IID const& riid, void **ppvObject) {
if (__uuidof(IDWriteTextRenderer) == riid) {
*ppvObject = this;
} else if (__uuidof(IDWritePixelSnapping) == riid) {
*ppvObject = this;
} else if (__uuidof(IUnknown) == riid) {
*ppvObject = this;
} else {
*ppvObject = nullptr;
return E_FAIL;
}
this->AddRef();
return S_OK;
}
};
FontDescriptor *substituteFont(char *postscriptName, char *string) {
FontDescriptor *res = NULL;
IDWriteFactory *factory = NULL;
HR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast(&factory)
));
// Get the system font collection.
IDWriteFontCollection *collection = NULL;
HR(factory->GetSystemFontCollection(&collection));
// find the font for the given postscript name
FontDescriptor *desc = new FontDescriptor();
desc->postscriptName = postscriptName;
FontDescriptor *font = findFont(desc);
// create a text format object for this font
IDWriteTextFormat *format = NULL;
if (font) {
WCHAR *familyName = utf8ToUtf16(font->family);
// create a text format
HR(factory->CreateTextFormat(
familyName,
collection,
(DWRITE_FONT_WEIGHT) font->weight,
font->italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
(DWRITE_FONT_STRETCH) font->width,
12.0,
L"en-us",
&format
));
delete familyName;
delete font;
} else {
// this should never happen, but just in case, let the system
// decide the default font in case findFont returned nothing.
HR(factory->CreateTextFormat(
L"",
collection,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
12.0,
L"en-us",
&format
));
}
// convert utf8 string for substitution to utf16
WCHAR *str = utf8ToUtf16(string);
// create a text layout for the substitution string
IDWriteTextLayout *layout = NULL;
HR(factory->CreateTextLayout(
str,
wcslen(str),
format,
100.0,
100.0,
&layout
));
// render it using a custom renderer that saves the physical font being used
FontFallbackRenderer *renderer = new FontFallbackRenderer(collection);
HR(layout->Draw(NULL, renderer, 100.0, 100.0));
// if we found something, create a result object
if (renderer->font) {
res = resultFromFont(renderer->font);
}
// free all the things
delete renderer;
layout->Release();
format->Release();
desc->postscriptName = NULL;
delete desc;
delete str;
collection->Release();
factory->Release();
return res;
}
systemfonts/src/win/FontManagerWindows.cpp 0000644 0001762 0000144 00000017326 13502707172 020531 0 ustar ligges users #include
#include
#include
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include "../FontDescriptor.h"
#include "../utils.h"
ResultSet* get_font_list();
WCHAR *utf8ToUtf16(const char *input) {
unsigned int len = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
WCHAR *output = new WCHAR[len];
MultiByteToWideChar(CP_UTF8, 0, input, -1, output, len);
return output;
}
char *utf16ToUtf8(const WCHAR *input) {
unsigned int len = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
char *output = new char[len];
WideCharToMultiByte(CP_UTF8, 0, input, -1, output, len, NULL, NULL);
return output;
}
FontWeight get_font_weight(FT_Face face) {
void* table = FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
if (table == NULL) {
return FontWeightUndefined;
}
TT_OS2* os2_table = (TT_OS2*) table;
return (FontWeight) os2_table->usWeightClass;
}
FontWidth get_font_width(FT_Face face) {
void* table = FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
if (table == NULL) {
return FontWidthUndefined;
}
TT_OS2* os2_table = (TT_OS2*) table;
return (FontWidth) os2_table->usWidthClass;
}
FontDescriptor* descriptor_from_face(FT_Face &face, const char* path, int index) {
FontDescriptor* res = NULL;
res = new FontDescriptor(
path,
index,
FT_Get_Postscript_Name(face) == NULL ? "" : FT_Get_Postscript_Name(face),
face->family_name,
face->style_name,
get_font_weight(face),
get_font_width(face),
face->style_flags & FT_STYLE_FLAG_ITALIC,
FT_IS_FIXED_WIDTH(face)
);
return res;
}
int scan_font_dir() {
char win_dir[MAX_PATH];
GetWindowsDirectoryA(win_dir, MAX_PATH);
std::string font_dir;
font_dir += win_dir;
font_dir += "\\Fonts\\";
static const LPCSTR font_registry_path = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
HKEY h_key;
LONG result;
result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, font_registry_path, 0, KEY_READ, &h_key);
if (result != ERROR_SUCCESS) {
return 1;
}
DWORD max_value_name_size, max_value_data_size;
result = RegQueryInfoKey(h_key, 0, 0, 0, 0, 0, 0, 0, &max_value_name_size, &max_value_data_size, 0, 0);
if (result != ERROR_SUCCESS) {
return 1;
}
DWORD value_index = 0;
LPSTR value_name = new CHAR[max_value_name_size];
LPBYTE value_data = new BYTE[max_value_data_size];
DWORD value_name_size, value_data_size, value_type;
std::string font_path;
ResultSet* font_list = get_font_list();
FT_Library library;
FT_Face face;
FT_Error error;
error = FT_Init_FreeType(&library);
if (error) {
return 1;
}
do {
// Loop over font registry, construct file path and parse with freetype
value_data_size = max_value_data_size;
value_name_size = max_value_name_size;
result = RegEnumValueA(h_key, value_index, value_name, &value_name_size, 0, &value_type, value_data, &value_data_size);
value_index++;
if (result != ERROR_SUCCESS || value_type != REG_SZ) {
continue;
}
font_path.clear();
font_path += font_dir;
font_path.append((LPSTR) value_data, value_data_size);
error = FT_New_Face(library,
font_path.c_str(),
0,
&face);
if (error) {
continue;
}
font_list->push_back(descriptor_from_face(face, font_path.c_str(), 0));
int n_fonts = face->num_faces;
FT_Done_Face(face);
for (int i = 1; i < n_fonts; i++) {
error = FT_New_Face(library,
font_path.c_str(),
i,
&face);
if (error) {
continue;
}
font_list->push_back(descriptor_from_face(face, font_path.c_str(), i));
FT_Done_Face(face);
}
} while (result != ERROR_NO_MORE_ITEMS);
// Cleanup
delete[] value_name;
delete[] value_data;
FT_Done_FreeType(library);
// Move Arial Regular to front
for (ResultSet::iterator it = font_list->begin(); it != font_list->end(); it++) {
if (strcmp((*it)->family, "Arial") == 0 && strcmp((*it)->style, "Regular") == 0) {
FontDescriptor* arial = *it;
font_list->erase(it);
font_list->insert(font_list->begin(), arial);
break;
}
}
return 0;
}
ResultSet *getAvailableFonts() {
ResultSet *res = new ResultSet();
ResultSet* font_list = get_font_list();
if (font_list->size() == 0) scan_font_dir();
for (ResultSet::iterator it = font_list->begin(); it != font_list->end(); it++) {
FontDescriptor* font = new FontDescriptor(*it);
res->push_back(font);
}
return res;
}
bool resultMatches(FontDescriptor *result, FontDescriptor *desc) {
if (desc->postscriptName && !strcmp_no_case(desc->postscriptName, result->postscriptName))
return false;
if (desc->family && !strcmp_no_case(desc->family, result->family))
return false;
if (desc->style && !strcmp_no_case(desc->style, result->style))
return false;
if (desc->weight && desc->weight != result->weight)
return false;
if (desc->width && desc->width != result->width)
return false;
if (desc->italic != result->italic)
return false;
if (desc->monospace != result->monospace)
return false;
return true;
}
ResultSet *findFonts(FontDescriptor *desc) {
ResultSet *res = new ResultSet();
ResultSet* font_list = get_font_list();
if (font_list->size() == 0) scan_font_dir();
for (ResultSet::iterator it = font_list->begin(); it != font_list->end(); it++) {
if (!resultMatches(*it, desc)) {
continue;
}
FontDescriptor* font = new FontDescriptor(*it);
res->push_back(font);
}
return res;
}
FontDescriptor *findFont(FontDescriptor *desc) {
ResultSet *fonts = findFonts(desc);
// if we didn't find anything, try again with only the font traits, no string names
if (fonts->size() == 0) {
delete fonts;
FontDescriptor *fallback = new FontDescriptor(
NULL, NULL, NULL, NULL,
desc->weight, desc->width, desc->italic, false
);
fonts = findFonts(fallback);
}
// ok, nothing. shouldn't happen often.
// just return the first available font
if (fonts->size() == 0) {
delete fonts;
fonts = getAvailableFonts();
}
// hopefully we found something now.
// copy and return the first result
if (fonts->size() > 0) {
FontDescriptor *res = new FontDescriptor(fonts->front());
delete fonts;
return res;
}
// whoa, weird. no fonts installed or something went wrong.
delete fonts;
return NULL;
}
bool font_has_glyphs(const char * font_path, FT_Library library, WCHAR * str) {
FT_Face face;
FT_Error error;
error = FT_New_Face(library,
font_path,
0,
&face);
if (error) {
return false;
}
int i = 0;
while (str[i]) {
if (FT_Get_Char_Index( face, str[i])) {
return false;
}
i++;
}
FT_Done_Face(face);
return true;
}
FontDescriptor *substituteFont(char *postscriptName, char *string) {
FontDescriptor *res = NULL;
// find the font for the given postscript name
FontDescriptor *desc = new FontDescriptor();
desc->postscriptName = postscriptName;
FontDescriptor *font = findFont(desc);
desc->postscriptName = NULL;
desc->weight = font->weight;
desc->width = font->width;
desc->italic = font->italic;
desc->monospace = font->monospace;
ResultSet* style_matches = findFonts(desc);
WCHAR *str = utf8ToUtf16(string);
FT_Library library;
FT_Error error;
error = FT_Init_FreeType( &library );
if (error) {
return res;
}
for (ResultSet::iterator it = style_matches->begin(); it != style_matches->end(); it++) {
if (font_has_glyphs((*it)->path, library, str)) {
res = new FontDescriptor(*it);
break;
}
}
FT_Done_FreeType(library);
delete str;
delete desc;
delete font;
return res;
}
systemfonts/src/utils.h 0000644 0001762 0000144 00000000530 13502707115 014747 0 ustar ligges users #ifndef SFUTILS_H
#define SFUTILS_H
#include
#include
static bool strcmp_no_case(const char * A, const char * B) {
unsigned int a_len = strlen(A);
if (strlen(B) != a_len)
return false;
for (unsigned int i = 0; i < a_len; ++i)
if (tolower(A[i]) != tolower(B[i]))
return false;
return true;
}
#endif
systemfonts/src/Makevars.in 0000644 0001762 0000144 00000000457 13502666265 015561 0 ustar ligges users PKG_CPPFLAGS=@cflags@ -DSTRICT_R_HEADERS
DARWIN_LIBS = -framework CoreText -framework Foundation
DARWIN_OBJECTS = mac/FontManagerMac.o
UNIX_OBJECTS = unix/FontManagerLinux.o
PKG_LIBS = @libs@ $(@SYS@_LIBS)
OBJECTS = systemfonts.o init.o $(@SYS@_OBJECTS)
all: clean
clean:
rm -f $(SHLIB) $(OBJECTS)
systemfonts/src/unix/ 0000755 0001762 0000144 00000000000 13502432513 014420 5 ustar ligges users systemfonts/src/unix/FontManagerLinux.cpp 0000755 0001762 0000144 00000016035 13506334560 020364 0 ustar ligges users #include
#include "../FontDescriptor.h"
int convertWeight(FontWeight weight) {
switch (weight) {
case FontWeightThin:
return FC_WEIGHT_THIN;
case FontWeightUltraLight:
return FC_WEIGHT_ULTRALIGHT;
case FontWeightLight:
return FC_WEIGHT_LIGHT;
case FontWeightNormal:
return FC_WEIGHT_REGULAR;
case FontWeightMedium:
return FC_WEIGHT_MEDIUM;
case FontWeightSemiBold:
return FC_WEIGHT_SEMIBOLD;
case FontWeightBold:
return FC_WEIGHT_BOLD;
case FontWeightUltraBold:
return FC_WEIGHT_EXTRABOLD;
case FontWeightHeavy:
return FC_WEIGHT_ULTRABLACK;
default:
return FC_WEIGHT_REGULAR;
}
}
FontWeight convertWeight(int weight) {
switch (weight) {
case FC_WEIGHT_THIN:
return FontWeightThin;
case FC_WEIGHT_ULTRALIGHT:
return FontWeightUltraLight;
case FC_WEIGHT_LIGHT:
return FontWeightLight;
case FC_WEIGHT_REGULAR:
return FontWeightNormal;
case FC_WEIGHT_MEDIUM:
return FontWeightMedium;
case FC_WEIGHT_SEMIBOLD:
return FontWeightSemiBold;
case FC_WEIGHT_BOLD:
return FontWeightBold;
case FC_WEIGHT_EXTRABOLD:
return FontWeightUltraBold;
case FC_WEIGHT_ULTRABLACK:
return FontWeightHeavy;
default:
return FontWeightNormal;
}
}
int convertWidth(FontWidth width) {
switch (width) {
case FontWidthUltraCondensed:
return FC_WIDTH_ULTRACONDENSED;
case FontWidthExtraCondensed:
return FC_WIDTH_EXTRACONDENSED;
case FontWidthCondensed:
return FC_WIDTH_CONDENSED;
case FontWidthSemiCondensed:
return FC_WIDTH_SEMICONDENSED;
case FontWidthNormal:
return FC_WIDTH_NORMAL;
case FontWidthSemiExpanded:
return FC_WIDTH_SEMIEXPANDED;
case FontWidthExpanded:
return FC_WIDTH_EXPANDED;
case FontWidthExtraExpanded:
return FC_WIDTH_EXTRAEXPANDED;
case FontWidthUltraExpanded:
return FC_WIDTH_ULTRAEXPANDED;
default:
return FC_WIDTH_NORMAL;
}
}
FontWidth convertWidth(int width) {
switch (width) {
case FC_WIDTH_ULTRACONDENSED:
return FontWidthUltraCondensed;
case FC_WIDTH_EXTRACONDENSED:
return FontWidthExtraCondensed;
case FC_WIDTH_CONDENSED:
return FontWidthCondensed;
case FC_WIDTH_SEMICONDENSED:
return FontWidthSemiCondensed;
case FC_WIDTH_NORMAL:
return FontWidthNormal;
case FC_WIDTH_SEMIEXPANDED:
return FontWidthSemiExpanded;
case FC_WIDTH_EXPANDED:
return FontWidthExpanded;
case FC_WIDTH_EXTRAEXPANDED:
return FontWidthExtraExpanded;
case FC_WIDTH_ULTRAEXPANDED:
return FontWidthUltraExpanded;
default:
return FontWidthNormal;
}
}
FontDescriptor *createFontDescriptor(FcPattern *pattern) {
FcChar8 *path, *psName, *family, *style;
int index, weight, width, slant, spacing;
FcPatternGetString(pattern, FC_FILE, 0, &path);
#ifdef FC_POSTSCRIPT_NAME
FcPatternGetString(pattern, FC_POSTSCRIPT_NAME, 0, &psName);
#else
psName = (FcChar8*) "";
#endif
FcPatternGetString(pattern, FC_FAMILY, 0, &family);
FcPatternGetString(pattern, FC_STYLE, 0, &style);
FcPatternGetInteger(pattern, FC_INDEX, 0, &index);
FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight);
FcPatternGetInteger(pattern, FC_WIDTH, 0, &width);
FcPatternGetInteger(pattern, FC_SLANT, 0, &slant);
FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing);
return new FontDescriptor(
(char *) path,
index,
(char *) psName,
(char *) family,
(char *) style,
convertWeight(weight),
convertWidth(width),
slant == FC_SLANT_ITALIC,
spacing == FC_MONO
);
}
ResultSet *getResultSet(FcFontSet *fs) {
ResultSet *res = new ResultSet();
if (!fs)
return res;
for (int i = 0; i < fs->nfont; i++) {
res->push_back(createFontDescriptor(fs->fonts[i]));
}
return res;
}
ResultSet *getAvailableFonts() {
FcInit();
FcPattern *pattern = FcPatternCreate();
#ifdef FC_POSTSCRIPT_NAME
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_SPACING, NULL);
#else
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_SPACING, NULL);
#endif
FcFontSet *fs = FcFontList(NULL, pattern, os);
ResultSet *res = getResultSet(fs);
FcPatternDestroy(pattern);
FcObjectSetDestroy(os);
FcFontSetDestroy(fs);
return res;
}
FcPattern *createPattern(FontDescriptor *desc) {
FcInit();
FcPattern *pattern = FcPatternCreate();
#ifdef FC_POSTSCRIPT_NAME
if (desc->postscriptName)
FcPatternAddString(pattern, FC_POSTSCRIPT_NAME, (FcChar8 *) desc->postscriptName);
#endif
if (desc->family)
FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *) desc->family);
if (desc->style)
FcPatternAddString(pattern, FC_STYLE, (FcChar8 *) desc->style);
if (desc->italic)
FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
if (desc->weight)
FcPatternAddInteger(pattern, FC_WEIGHT, convertWeight(desc->weight));
if (desc->width)
FcPatternAddInteger(pattern, FC_WIDTH, convertWidth(desc->width));
if (desc->monospace)
FcPatternAddInteger(pattern, FC_SPACING, FC_MONO);
return pattern;
}
ResultSet *findFonts(FontDescriptor *desc) {
FcPattern *pattern = createPattern(desc);
#ifdef FC_POSTSCRIPT_NAME
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_SPACING, NULL);
#else
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_SPACING, NULL);
#endif
FcFontSet *fs = FcFontList(NULL, pattern, os);
ResultSet *res = getResultSet(fs);
FcFontSetDestroy(fs);
FcPatternDestroy(pattern);
FcObjectSetDestroy(os);
return res;
}
FontDescriptor *findFont(FontDescriptor *desc) {
FcPattern *pattern = createPattern(desc);
FcConfigSubstitute(NULL, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern *font = FcFontMatch(NULL, pattern, &result);
FontDescriptor *res = createFontDescriptor(font);
FcPatternDestroy(pattern);
FcPatternDestroy(font);
return res;
}
FontDescriptor *substituteFont(char *postscriptName, char *string) {
FcInit();
// create a pattern with the postscript name
FcPattern* pattern = FcPatternCreate();
#ifdef FC_POSTSCRIPT_NAME
FcPatternAddString(pattern, FC_POSTSCRIPT_NAME, (FcChar8 *) postscriptName);
#endif
// create a charset with each character in the string
FcCharSet* charset = FcCharSetCreate();
int len = strlen(string);
for (int i = 0; i < len;) {
FcChar32 c;
i += FcUtf8ToUcs4((FcChar8 *)string + i, &c, len - i);
FcCharSetAddChar(charset, c);
}
FcPatternAddCharSet(pattern, FC_CHARSET, charset);
FcCharSetDestroy(charset);
FcConfigSubstitute(0, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
// find the best match font
FcResult result;
FcPattern *font = FcFontMatch(NULL, pattern, &result);
FontDescriptor *res = createFontDescriptor(font);
FcPatternDestroy(pattern);
FcPatternDestroy(font);
return res;
}
systemfonts/src/mac/ 0000755 0001762 0000144 00000000000 13506366517 014213 5 ustar ligges users systemfonts/src/mac/FontManagerMac.mm 0000755 0001762 0000144 00000022154 13502665646 017400 0 ustar ligges users #include
#include
#include
#include