kdesc --typeface from the reflection klammerset; a missing font directory is a row, not an error
Assembled from klammertext-dev daad6273483a. The font-store listing is rendered from ref/ (one table per search directory, in search order, with the styles each typeface holds); the search path is described, not validated, so a KLAMMERTEXT_FONTS directory that does not exist -- the default ~/.klammertext/fonts on a fresh install -- is a row saying so and the command exits 0. A typeface an earlier directory also holds is marked shadowed, with the winning directory, in a column present only when needed. The store's layout convention has one reader, fonts_in_directory(). Six kdesc suite cases pin the listing.
This commit is contained in:
1 parent
c016b3e5f3
commit
f9af1ff869
7 files changed
+188
-103
No files matched your search
+30
-74
@@ -141,35 +141,41 @@ static Resolved_font resolve_in_directory(
|
||||
return {};
|
||||
}
|
||||
|
||||
// Every font available for the three @document role parameters: each
|
||||
// <name>.css with a matching <name>/ directory, across the installed
|
||||
// directories and the default font set. The reported name is the family name
|
||||
// from the CSS (what the writer types), falling back to the directory name.
|
||||
std::vector<Resolved_font> fonts_in_directory(const std::string& base)
|
||||
{
|
||||
std::vector<Resolved_font> result {};
|
||||
if (!fs::is_directory(base)) {
|
||||
return result;
|
||||
}
|
||||
strings_t names {};
|
||||
for (auto& entry : fs::directory_iterator(base)) {
|
||||
if (entry.path().extension() == ".css" &&
|
||||
fs::is_directory(base + "/" + entry.path().stem().string())) {
|
||||
names.push_back(entry.path().stem());
|
||||
}
|
||||
}
|
||||
std::sort(names.begin(), names.end());
|
||||
for (const std::string& dir_name : names) {
|
||||
// The directory name is the family name's fallback: classify_from_css
|
||||
// overwrites it with the name the css declares, when it declares one.
|
||||
result.push_back(resolve_in_directory(dir_name, dir_name, base));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Every font available for the three @document role parameters, across the
|
||||
// installed directories and the default font set. The reported name is the
|
||||
// family name from the CSS (what the writer types).
|
||||
strings_t available_font_families()
|
||||
{
|
||||
strings_t result {};
|
||||
static const std::regex family_rgx(R"(font-family:\s*'([^']+)')");
|
||||
auto scan = [&](const std::string& base) {
|
||||
if (!fs::exists(base)) {
|
||||
return;
|
||||
strings_t bases = installed_font_dirs();
|
||||
bases.push_back(default_font_dir());
|
||||
for (const std::string& base : bases) {
|
||||
for (const Resolved_font& font : fonts_in_directory(base)) {
|
||||
result.push_back(font.family_name);
|
||||
}
|
||||
for (auto& entry : fs::directory_iterator(base)) {
|
||||
if (entry.path().extension() == ".css" &&
|
||||
fs::is_directory(base + "/" + entry.path().stem().string())) {
|
||||
std::string css = string_from_file(entry.path());
|
||||
std::smatch match {};
|
||||
if (std::regex_search(css, match, family_rgx)) {
|
||||
result.push_back(match[1]);
|
||||
} else {
|
||||
result.push_back(entry.path().stem());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const std::string& dir : installed_font_dirs()) {
|
||||
scan(dir);
|
||||
}
|
||||
scan(default_font_dir());
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
return result;
|
||||
@@ -500,56 +506,6 @@ std::string install_fonts(const std::string& source_dir, std::string dest_dir)
|
||||
return report.str();
|
||||
}
|
||||
|
||||
std::string describe_fonts()
|
||||
{
|
||||
std::stringstream ss {};
|
||||
std::set<std::string> seen {};
|
||||
strings_t bases = installed_font_dirs();
|
||||
bases.push_back(default_font_dir());
|
||||
for (size_t i = 0; i < bases.size(); i++) {
|
||||
const std::string& base = bases[i];
|
||||
bool is_default = (i == bases.size() - 1);
|
||||
ss << base << (is_default ? " (default font set)" : "") << ":\n";
|
||||
if (!fs::exists(base)) {
|
||||
ss << " [directory does not exist]\n";
|
||||
continue;
|
||||
}
|
||||
strings_t names {};
|
||||
for (auto& entry : fs::directory_iterator(base)) {
|
||||
if (entry.path().extension() == ".css" &&
|
||||
fs::is_directory(base + "/" + entry.path().stem().string())) {
|
||||
names.push_back(entry.path().stem());
|
||||
}
|
||||
}
|
||||
std::sort(names.begin(), names.end());
|
||||
if (names.empty()) {
|
||||
ss << " [no fonts]\n";
|
||||
}
|
||||
for (const std::string& dir_name : names) {
|
||||
Resolved_font font = resolve_in_directory("?", dir_name, base);
|
||||
std::string css = string_from_file(font.css_file);
|
||||
std::smatch match {};
|
||||
std::string family = dir_name;
|
||||
if (std::regex_search(css, match,
|
||||
std::regex(R"(font-family:\s*'([^']+)')"))) {
|
||||
family = match[1];
|
||||
}
|
||||
strings_t variants {};
|
||||
if (!font.regular.empty()) variants.push_back("Regular");
|
||||
if (!font.bold.empty()) variants.push_back("Bold");
|
||||
if (!font.italic.empty()) variants.push_back("Italic");
|
||||
if (!font.bold_italic.empty()) variants.push_back("BoldItalic");
|
||||
ss << " " << family << " (" << join(variants, ", ") << ")";
|
||||
if (seen.contains(dir_name)) {
|
||||
ss << " [shadowed by an earlier directory]";
|
||||
}
|
||||
seen.insert(dir_name);
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string write_font_samples(const std::string& output_dir, const std::string& source_dir)
|
||||
{
|
||||
fs::create_directories(output_dir);
|
||||
|
||||
+7
-3
@@ -28,6 +28,13 @@ std::vector<std::string> installed_font_dirs();
|
||||
std::string default_font_dir();
|
||||
// Every installable font family found across those directories.
|
||||
std::vector<std::string> available_font_families();
|
||||
// Every font ONE base directory holds, in directory-name order: each
|
||||
// <name>.css beside a <name>/ directory, the family name read from the css
|
||||
// (the directory name when the css declares none), the styles classified.
|
||||
// Empty for a directory that does not exist or holds no fonts; the caller
|
||||
// asks the filesystem which. The store's layout convention lives here, so
|
||||
// a listing of the store (kdesc --typeface) reads it rather than restating it.
|
||||
std::vector<Resolved_font> fonts_in_directory(const std::string& base);
|
||||
// Whether the store can resolve NAME -- written as the family name or as
|
||||
// the store's directory name, since both resolve. The question a klammer
|
||||
// asks before it names a typeface that is not shipped (a CJK face), so it
|
||||
@@ -61,9 +68,6 @@ std::vector<Font_file> classify_font_files(const std::string& directory);
|
||||
// generated <family-kebab>.css with relative urls. Returns a report.
|
||||
std::string install_fonts(const std::string& source_dir, std::string dest_dir = "");
|
||||
|
||||
// Describe every font in the store with provenance and variants.
|
||||
std::string describe_fonts();
|
||||
|
||||
// Write an HTML specimen page for fonts into output_dir. With an empty
|
||||
// source_dir, samples every available font in the store; otherwise
|
||||
// classifies and samples the (possibly uninstalled) fonts in source_dir.
|
||||
|
||||
Reference in new issue
Block a user