Unicode sequences, @typeface, and the font/typeface distinction
A run of Unicode code points now needs one pair of carets, not one pair
each: ^9053 53EF 9053^ renders the characters adjacently, and a single
character inside such a run stands for itself. An invalid code point is
an error naming the position that wrote it, rather than an apology
rendered into the finished document; noncharacters are refused alongside
surrogates; and a code point that lands on a Klammertext special is safe
through the re-reads that assemble a document. The character tables now
produce the precomposed form, so a diacritic written as ^a" and one typed
directly are the same text to a search or a copy-paste.
"kdesc -u" describes how to write a Unicode character, with tables of
glyphs beside the codes that produce them. It is new and not yet
complete: it predates the sequence form and the typeface klammer below.
A typeface and a style are no longer both called "font". A typeface is a
family resolved by name; a style code is the one letter naming a type
style. So @document takes :serif_typeface and its siblings, a table takes
:style, captions take :caption_style and :caption_size, the uniform scale
is :text_scale, and the command is "kdesc --typeface". This renames
arguments a document may already use; an old name is a located error
rather than a silent omission.
@typeface sets a passage in another typeface for the length of that
passage:
@typeface Noto Sans Egyptian Hieroglyphs | ^132AA 133CF^ :scale 1.5 @
Writing the klammer is the whole declaration -- the document resolves the
typeface, embeds it in what it produces, and carries its license along.
Two typefaces for scripts no text font covers now ship with Klammertext:
Noto Sans Egyptian Hieroglyphs and Noto Sans Cuneiform. Every typeface in
the distribution now carries its license file.
(from dev 61e598698891)
This commit is contained in:
1 parent
37b6ba1c4f
commit
af29bbf7ac
47 files changed
+1921
-135
No files matched your search
+2
-2
@@ -63,11 +63,11 @@ public:
|
||||
// next -/-- token (zero or more). get(name) returns the words
|
||||
// space-joined; given(name) distinguishes "--name with no words"
|
||||
// from an absent --name. Used for subcommand-style interfaces
|
||||
// (kdesc --font install <dir>).
|
||||
// (kdesc --typeface install <dir>).
|
||||
// A variadic option: the words after the flag, up to the next one.
|
||||
// `parameter` is shown in the usage line as "[<parameter>]" -- an option
|
||||
// whose words are a free argument (a search phrase) should name it, so
|
||||
// the usage says what may follow; a subcommand-style option (--font,
|
||||
// the usage says what may follow; a subcommand-style option (--typeface,
|
||||
// --coverage) leaves it empty and documents its words in its own help.
|
||||
void var(const std::string& name, const std::string& desc,
|
||||
const std::string& parameter = "");
|
||||
|
||||
+97
-10
@@ -2,9 +2,26 @@
|
||||
|
||||
#include "util.h"
|
||||
#include "character.h"
|
||||
#include "precomposed.h"
|
||||
#include "error.h"
|
||||
#include "target.h"
|
||||
#include "log.h"
|
||||
#include "show.h"
|
||||
|
||||
// A base letter composed with a combining mark. The PRECOMPOSED (NFC)
|
||||
// code point is emitted where one exists -- that is the byte sequence
|
||||
// editors and input methods produce for direct entry, so ^a" and a typed
|
||||
// ä compare equal (search, copy-paste); the tables formerly emitted
|
||||
// base + combining mark, which renders identically but is a different
|
||||
// string. Pairs with no precomposed form keep the combining fallback.
|
||||
// The table is GENERATED: doc/make_precomposed_table.py -> precomposed.h.
|
||||
static std::string compose(const std::string& base, const std::string& mark_cp)
|
||||
{
|
||||
auto it = precomposed.find(base + mark_cp);
|
||||
if (it != precomposed.end()) return it->second;
|
||||
return base + utf8char(std::stoi(mark_cp, nullptr, 16));
|
||||
}
|
||||
|
||||
inline
|
||||
std::string klammertext_special_characters { "@|*^#" };
|
||||
|
||||
@@ -44,6 +61,24 @@ std::string utf8char(int cp)
|
||||
}
|
||||
|
||||
|
||||
bool valid_codepoint(int cp)
|
||||
{
|
||||
if (cp < 0 || cp > 0x10FFFF) return false;
|
||||
if (0xD800 <= cp && cp <= 0xDFFF) return false; // surrogates
|
||||
if (0xFDD0 <= cp && cp <= 0xFDEF) return false; // noncharacters
|
||||
if ((cp & 0xFFFE) == 0xFFFE) return false; // U+xFFFE / U+xFFFF
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string invalid_codepoint_reason(int cp)
|
||||
{
|
||||
if (cp < 0 || cp > 0x10FFFF)
|
||||
return "beyond U+10FFFF, the top of the Unicode range";
|
||||
if (0xD800 <= cp && cp <= 0xDFFF)
|
||||
return "a surrogate (U+D800-U+DFFF), not a character";
|
||||
return "a noncharacter, reserved by Unicode for internal use";
|
||||
}
|
||||
|
||||
std::string unicode_hex_to_char(std::string s, int width=4) //, std::string marker)
|
||||
{
|
||||
(void)K::log(4, s);
|
||||
@@ -82,7 +117,7 @@ std::string process_diacritics(std::string s)
|
||||
std::regex hit { regex_escape((*p)[0].str()) };
|
||||
std::string ch = (*p)[1].str();
|
||||
std::string d = (*p)[2].str();
|
||||
result = std::regex_replace(result, hit, ch + unicode_hex_to_char(diacritics[d].first));
|
||||
result = std::regex_replace(result, hit, compose(ch, diacritics[d].first));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -124,15 +159,14 @@ std::string process_pinyin(std::string s)
|
||||
std::string vowel = (*p)[1].str();
|
||||
std::string tone = (*p)[2].str();
|
||||
result = std::regex_replace(result, hit,
|
||||
vowel + unicode_hex_to_char(pinyin_tones[tone].first));
|
||||
compose(vowel, pinyin_tones[tone].first));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string process_unicode_codepoint(std::string s)
|
||||
std::string process_unicode_codepoint(std::string s, const Locator& loc = Locator::none())
|
||||
{
|
||||
(void)K::log(4);
|
||||
//return std::regex_replace(s, unicode_re, hidehat + "$1" + hidehat);
|
||||
std::string result {s};
|
||||
std::sregex_iterator end {};
|
||||
for (std::sregex_iterator p { s.begin(), s.end(), unicode_re }; p!= end; ++p) {
|
||||
@@ -141,8 +175,22 @@ std::string process_unicode_codepoint(std::string s)
|
||||
// its argument at a fixed width, and its 4-digit default truncated a
|
||||
// 5-digit code point to its first four digits (^13000^ rendered as
|
||||
// U+1300 followed by a literal "0").
|
||||
int cp = std::stoi((*p)[1].str(), nullptr, 16);
|
||||
// An invalid code point is a located ERROR, not text in the document
|
||||
// (the no-warn-and-continue policy): a generator's typo must not
|
||||
// produce a finished-looking document with an apology embedded
|
||||
// where a character should be.
|
||||
if (!valid_codepoint(cp)) {
|
||||
throw Parsing_error("Invalid Unicode code point \"" + (*p)[0].str()
|
||||
+ "\": " + invalid_codepoint_reason(cp), loc);
|
||||
}
|
||||
// A code point that lands on a Klammertext structural character
|
||||
// (@ | # ^ : *) is hidden as its KTESC marker, exactly as the
|
||||
// quoted form of that character would be -- a bare "@" from
|
||||
// ^0040^ would otherwise be re-interpreted when the text is
|
||||
// re-katomized (@document :text, the @eval read-back).
|
||||
result = std::regex_replace(result, hit_re,
|
||||
utf8char(std::stoi((*p)[1].str(), nullptr, 16)));
|
||||
hide_structural_characters(utf8char(cp)));
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -154,7 +202,7 @@ std::string process_unicode_codepoint(std::string s)
|
||||
|
||||
|
||||
|
||||
std::string encode(const std::string& s)
|
||||
std::string encode(const std::string& s, const Locator& loc)
|
||||
{
|
||||
if (s.find("^") == std::string::npos)
|
||||
return s;
|
||||
@@ -177,7 +225,7 @@ std::string encode(const std::string& s)
|
||||
result = process_extended_latin(result);
|
||||
if (dbg) std::cout << "extended_latin: " << result << "\n";
|
||||
|
||||
result = process_unicode_codepoint(result);
|
||||
result = process_unicode_codepoint(result, loc);
|
||||
if (dbg) std::cout << "unicode: " << result << "\n";
|
||||
|
||||
result = process_diacritics(result);
|
||||
@@ -225,7 +273,7 @@ void diacritics_examples(const std::string& kt_filename)
|
||||
<< " @i-Displayed | @i-Written | @i-Name\n";
|
||||
else
|
||||
std::cout << boldblack
|
||||
<< "\nDiacritics (with typical base characters)\n" << black;
|
||||
<< "\nDiacritics (with typical base characters)\n\n" << black;
|
||||
|
||||
std::string line_sep = "|| @line@ ";
|
||||
for (char symbol : diacritic_symbols_order) {
|
||||
@@ -260,7 +308,7 @@ void extended_latin_examples(const std::string& kt_filename)
|
||||
kt << "@table :caption " << title << " |\n"
|
||||
<< " @i-Displayed | @i-Written | @i-Name\n";
|
||||
else
|
||||
std::cout << "\n" << boldblack << title << black << "\n";
|
||||
std::cout << "\n" << boldblack << title << black << "\n\n";
|
||||
|
||||
std::string line_sep = "|| @line@ ";
|
||||
for (const std::string& symbol : extended_latin_symbols) {
|
||||
@@ -291,7 +339,7 @@ void pinyin_examples(const std::string& kt_filename)
|
||||
<< " @i-Displayed | @i-Written | @i-Name\n";
|
||||
else
|
||||
std::cout << boldblack
|
||||
<< "\nMandarin pinyin tones (using vowel \"a\")\n" << black;
|
||||
<< "\nMandarin pinyin tones (using vowel \"a\")\n\n" << black;
|
||||
|
||||
std::string line_sep = "|| @line@ ";
|
||||
for (auto [symbol, codename] : pinyin_tones) {
|
||||
@@ -309,6 +357,45 @@ void pinyin_examples(const std::string& kt_filename)
|
||||
write_kt_example_file(kt, kt_filename);
|
||||
}
|
||||
|
||||
|
||||
void unicode_examples(const std::string& kt_filename)
|
||||
{
|
||||
std::stringstream kt {};
|
||||
bool write_kt_file = kt_filename.size() != 0;
|
||||
|
||||
std::cout << boldblack << "\nIncluding Unicode in Klammertext files\n\n" << black
|
||||
<< "To specify a wide variety of different letter forms, the Unicode Standard\n"
|
||||
<< "defines a large set of \"code points\" organized by groups. To include a\n"
|
||||
<< "Unicode character in Klammertext, you surround its hexadecimal number with\n"
|
||||
<< "the \"^\" character. For example, the Cyrillic captial letter \"I\" is \"И\",\n"
|
||||
<< "included in Klammertext as ^0418^.\n\n"
|
||||
<< "To search for Unicode characters by their description, see:\n"
|
||||
<< " https://www.fileformat.info/info/unicode/char/search.htm\n";
|
||||
|
||||
std::cout << "\n⟐ Examples of Chinese characters\n\n"
|
||||
<< " Pinyin\n";
|
||||
for (auto chr : dao) {
|
||||
std::string code = "^" + chr[1] + "^";
|
||||
std::cout << " " << process_unicode_codepoint(code) << " "
|
||||
<< code << " "
|
||||
<< chr[0] << "\n";
|
||||
}
|
||||
|
||||
std::cout << "\n⟐ Examples of Egyptian hieroglyphs\n\n";
|
||||
std::cout << std::setw(16) << " " << "Sound Gardiner\n";
|
||||
for (auto glyph : ptolemy) {
|
||||
std::cout << " " << std::setw(4) << process_unicode_codepoint("^" + glyph[2] + "^") << " "
|
||||
<< "^" + glyph[2] + "^ "
|
||||
<< std::setw(4)
|
||||
<< glyph[1] << " "
|
||||
<< std::setw(4)
|
||||
<< glyph[0] << "\n";
|
||||
}
|
||||
std::cout << "\nSee: https://en.wikipedia.org/wiki/List_of_Egyptian_hieroglyphs\n"
|
||||
<< "The Unicode codepoint in the second column of the \"List of hieroglyphs\"\n"
|
||||
<< "table is the number in the ^...^ code used by Klammertext.\n\n";
|
||||
}
|
||||
|
||||
void show_special_characters()
|
||||
{
|
||||
std::cout << std::setfill(' ');
|
||||
|
||||
+45
-3
@@ -11,6 +11,8 @@
|
||||
#include <regex>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "locator.h"
|
||||
|
||||
// ^UUUU^ — a Unicode code point in hex, 4 to 6 digits (^263A^ is the BMP,
|
||||
// ^13000^ EGYPTIAN HIEROGLYPH A001, ^10FFFD^ the top of the range), or a
|
||||
// single digit. Lengths 2-3 are NOT accepted: two- and three-letter
|
||||
@@ -89,21 +91,61 @@ std::map<std::string, std::pair<std::string, std::string>> pinyin_tones {
|
||||
{"3", {"030C", "falling-rising"}},
|
||||
{"4", {"0300", "falling"}}};
|
||||
|
||||
inline
|
||||
std::vector<std::vector<std::string>> ptolemy {
|
||||
{"Q3", "P", "132AA"},
|
||||
{"X1", "t", "133CF"},
|
||||
{"V4", "o/wa", "13371"},
|
||||
{"E23", "l/r", "130ED"},
|
||||
{"Aa15", "m", "1340D"},
|
||||
{"M17A", "e/y", "131CC"},
|
||||
{"S29", "s", "132F4"}};
|
||||
|
||||
/*
|
||||
道 (dào): U+9053
|
||||
可 (kě): U+53EF
|
||||
道 (dào): U+9053
|
||||
非 (fēi): U+975E
|
||||
常 (cháng): U+5E38
|
||||
道 (dào): U+9053
|
||||
*/
|
||||
|
||||
inline
|
||||
std::vector<std::vector<std::string>> dao {
|
||||
{"dào", "9053"},
|
||||
{"kě", "53EF"},
|
||||
{"dào", "9053"},
|
||||
{"fēi", "975E"},
|
||||
{"cháng", "5E38"},
|
||||
{"dào", "9053"}};
|
||||
|
||||
inline
|
||||
bool is_tty() { return isatty(fileno(stdout)); }
|
||||
//const char* italic_on() { return tty() ? "\033[3m" : ""; }
|
||||
//const char* italic_off() { return tty() ? "\033[0m" : ""; }
|
||||
inline
|
||||
const std::string italic_on() { return is_tty() ? "\033[3m" : ""; }
|
||||
inline
|
||||
const std::string italic_off() { return is_tty() ? "\033[0m" : ""; }
|
||||
|
||||
std::string encode(const std::string& s, const Locator& loc);
|
||||
|
||||
std::string encode(const std::string& s);
|
||||
// UTF-8 encoding of one code point (to U+10FFFF; surrogates and values
|
||||
// above the range yield an "Invalid Unicode: N" text). Used by the
|
||||
// character tables here and by the ^UUUU UUUU ...^ sequence pass
|
||||
// (process_unicode_sequences in katom_list.cpp).
|
||||
std::string utf8char(int cp);
|
||||
|
||||
// Whether cp may be written in a ^...^ form: within the Unicode range,
|
||||
// not a surrogate, and not a NONCHARACTER (U+FDD0-U+FDEF and the last two
|
||||
// code points of every plane) -- the standard reserves those for internal
|
||||
// use, never for interchange. An invalid code point in a document is a
|
||||
// located Parsing_error (the no-warn-and-continue policy; the in-band
|
||||
// "Invalid Unicode" text is no longer reachable from input).
|
||||
bool valid_codepoint(int cp);
|
||||
std::string invalid_codepoint_reason(int cp);
|
||||
|
||||
void diacritics_examples(const std::string& kt_filename = "");
|
||||
void extended_latin_examples(const std::string& kt_filename = "");
|
||||
void pinyin_examples(const std::string& kt_filename = "");
|
||||
void unicode_examples(const std::string& kt_filename = "");
|
||||
|
||||
void show_special_characters();
|
||||
+8
-8
@@ -206,7 +206,7 @@ static void extract_font_metrics(Resolved_font& font)
|
||||
}
|
||||
}
|
||||
|
||||
Resolved_font resolve_font(const std::string& family_name)
|
||||
Resolved_font resolve_font(const std::string& family_name, const Locator& loc)
|
||||
{
|
||||
if (family_name.empty())
|
||||
return {};
|
||||
@@ -222,26 +222,26 @@ Resolved_font resolve_font(const std::string& family_name)
|
||||
if (!font.family_name.empty()) {
|
||||
extract_font_metrics(font);
|
||||
// A DERIVED value: an installed font may shadow a default one, and
|
||||
// which directory won is not readable from ":serif_font Whatever".
|
||||
// which directory won is not readable from ":serif_typeface Whatever".
|
||||
// The commonest font question -- "why does it not look like the
|
||||
// one I installed?" -- is answered by this line.
|
||||
(void)K::log(1, "Font \"" + family_name + "\": " + base + "/" + dir_name);
|
||||
(void)K::log(1, "Typeface \"" + family_name + "\": " + base + "/" + dir_name);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss {};
|
||||
ss << "The font \"" << family_name << "\" is not installed.\n\n"
|
||||
<< "Available fonts:\n";
|
||||
ss << "The typeface \"" << family_name << "\" is not installed.\n\n"
|
||||
<< "Available typefaces:\n";
|
||||
for (const std::string& name : available_font_families()) {
|
||||
ss << " " << name << "\n";
|
||||
}
|
||||
ss << "\nFonts are searched in the directories of the KLAMMERTEXT_FONTS\n"
|
||||
ss << "\nTypefaces are searched in the directories of the KLAMMERTEXT_FONTS\n"
|
||||
<< "environment variable (colon-separated; default $HOME/.klammertext/fonts)\n"
|
||||
<< "and then in the default font set. To install a font,\n"
|
||||
<< "and then in the default set. To install a typeface,\n"
|
||||
<< "place its files as <fonts-dir>/" << dir_name << "/*.ttf with a\n"
|
||||
<< "<fonts-dir>/" << dir_name << ".css declaring its @font-face variants.";
|
||||
throw Argument_error(ss.str());
|
||||
throw Argument_error(ss.str(), loc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-1
@@ -3,6 +3,7 @@
|
||||
// The Klammertext font store (infrastructure; see font_store.cpp).
|
||||
|
||||
#include <string>
|
||||
#include "locator.h"
|
||||
#include <vector>
|
||||
|
||||
struct Resolved_font {
|
||||
@@ -27,7 +28,11 @@ 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();
|
||||
Resolved_font resolve_font(const std::string& family_name);
|
||||
// The Locator is the DOCUMENT location to blame when the typeface is not
|
||||
// installed: without it the "not installed" error cites font_store.cpp,
|
||||
// a source location no user can act on (the no-.cpp-location rule).
|
||||
Resolved_font resolve_font(const std::string& family_name,
|
||||
const Locator& loc = Locator::none());
|
||||
void install_resolved_font(const Resolved_font& font, std::string output_dir);
|
||||
|
||||
// One font file classified by its internal metadata (name table, OS/2).
|
||||
|
||||
+154
-1
@@ -281,11 +281,164 @@ void encode_nonascii_characters(katom_list& katoms)
|
||||
{
|
||||
for (Katom& k : katoms) {
|
||||
if (is_nonascii(k)) {
|
||||
k.m_text = encode(k.m_text);
|
||||
k.m_text = encode(k.m_text, k.m_loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unicode sequences
|
||||
|
||||
// ^UUUU UUUU ...^ -- a whitespace-delimited sequence of Unicode code
|
||||
// points between ONE pair of carets: ^9053 53EF 9053^ renders the three
|
||||
// characters adjacently. The single-token form ^UUUU^ is the katomizer's
|
||||
// nonascii katom, resolved by encode(); a sequence cannot be a katom at
|
||||
// all, because whitespace is preserved as separate katoms and no katom
|
||||
// spans it. So the tokens arrive as word katoms -- the opener "^"+hex,
|
||||
// the closer hex+"^", bare hex between, separated by SPACE katoms only (a
|
||||
// newline aborts: a sequence is one line; so does any other katom, which
|
||||
// is also what enforces "no space inside the delimiters' elbows": a bare
|
||||
// "^" is not an opener) -- and are assembled here. Each token is 4-6 hex
|
||||
// digits, the same rule as the single form. The rendered characters
|
||||
// replace the opening katom's text; the other members are marked
|
||||
// replaced. Skipped inside @@/@@@ definition spans and
|
||||
// @eval/@read/@cond argument spans (code keeps its carets -- the rule the
|
||||
// string-level single-form pass cannot honor) and for literal katoms
|
||||
// (verbatim content; mark_literal_* run before this).
|
||||
namespace {
|
||||
|
||||
bool hex_token(const std::string& s)
|
||||
{
|
||||
if (s.size() < 4 || s.size() > 6) return false;
|
||||
for (char c : s) {
|
||||
bool hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F');
|
||||
if (!hex) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// A sequence token is a 4-6-digit hex code point or a SINGLE character
|
||||
// standing for itself ("^" excluded -- a lone caret cannot say which
|
||||
// caret closes). The single-character reading exists only inside a
|
||||
// sequence; ^s^ alone stays the mnemonic.
|
||||
bool shape_token(const std::string& s)
|
||||
{
|
||||
return hex_token(s) || (s.size() == 1 && s[0] != '^');
|
||||
}
|
||||
|
||||
// Render one scanned token. A hex token is validated -- an invalid code
|
||||
// point is a located error, not text in the document -- and a structural
|
||||
// character in the result is hidden as its KTESC marker. A single
|
||||
// character behaves exactly as if quoted with "^": alphanumerics stand
|
||||
// bare, anything else becomes its marker.
|
||||
std::string render_token(const std::string& s, const Locator& loc)
|
||||
{
|
||||
if (hex_token(s)) {
|
||||
int cp = std::stoi(s, nullptr, 16);
|
||||
if (!valid_codepoint(cp)) {
|
||||
throw Parsing_error(
|
||||
"Invalid Unicode code point \"" + s + "\" in a ^...^ sequence: "
|
||||
+ invalid_codepoint_reason(cp), loc);
|
||||
}
|
||||
return hide_structural_characters(utf8char(cp));
|
||||
}
|
||||
char c = s[0];
|
||||
bool alnum = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z');
|
||||
return alnum ? s : Target::escape_marker(s);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void process_unicode_sequences(katom_list& katoms)
|
||||
{
|
||||
(void)K::log(4);
|
||||
int definition_depth = 0;
|
||||
int code_depth = 0; // inside an @eval/@read/@cond span
|
||||
std::vector<bool> apply_is_code; // one entry per open application
|
||||
for (size_t i = 0; i < katoms.size(); ++i) {
|
||||
Katom& k = katoms[i];
|
||||
switch (k.m_type) {
|
||||
case katom_t::define_begin:
|
||||
case katom_t::machine_begin:
|
||||
++definition_depth;
|
||||
continue;
|
||||
case katom_t::define_end:
|
||||
case katom_t::machine_end:
|
||||
if (definition_depth > 0) --definition_depth;
|
||||
continue;
|
||||
case katom_t::eval_begin:
|
||||
case katom_t::read_begin:
|
||||
case katom_t::cond_begin:
|
||||
apply_is_code.push_back(true);
|
||||
++code_depth;
|
||||
continue;
|
||||
case katom_t::apply_begin:
|
||||
apply_is_code.push_back(false);
|
||||
continue;
|
||||
case katom_t::apply_end:
|
||||
if (!apply_is_code.empty()) {
|
||||
if (apply_is_code.back()) --code_depth;
|
||||
apply_is_code.pop_back();
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (definition_depth > 0 || code_depth > 0) continue;
|
||||
if (k.m_type != katom_t::word && k.m_type != katom_t::text) continue;
|
||||
// The opener: "^" + one token, nothing else.
|
||||
if (k.m_text.size() < 2 || k.m_text[0] != '^') continue;
|
||||
if (!shape_token(k.m_text.substr(1))) continue;
|
||||
// Phase 1 -- SCAN by shape only, no validation: an unclosed run
|
||||
// must fall through untouched (it may be innocent prose about hex),
|
||||
// so an invalid code point may only error once the sequence has
|
||||
// actually closed. Tokens are word katoms separated by SPACE
|
||||
// katoms; any other katom (a newline included: one line only)
|
||||
// aborts.
|
||||
std::vector<std::pair<std::string, const Locator*>> tokens {
|
||||
{ k.m_text.substr(1), &k.m_loc } };
|
||||
std::vector<size_t> members {};
|
||||
bool closed = false;
|
||||
size_t j = i + 1;
|
||||
for (; j < katoms.size(); ++j) {
|
||||
Katom& t = katoms[j];
|
||||
if (t.m_type == katom_t::space) {
|
||||
members.push_back(j);
|
||||
continue;
|
||||
}
|
||||
if (t.m_type != katom_t::word && t.m_type != katom_t::text) break;
|
||||
const std::string& s = t.m_text;
|
||||
if (s.size() >= 2 && s.back() == '^'
|
||||
&& shape_token(s.substr(0, s.size() - 1))) {
|
||||
tokens.push_back({ s.substr(0, s.size() - 1), &t.m_loc });
|
||||
members.push_back(j);
|
||||
closed = true;
|
||||
++j;
|
||||
break;
|
||||
}
|
||||
if (shape_token(s)) {
|
||||
tokens.push_back({ s, &t.m_loc });
|
||||
members.push_back(j);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!closed) continue;
|
||||
// Phase 2 -- render, validating each code point at its own katom.
|
||||
std::string rendered {};
|
||||
for (const auto& [text, loc] : tokens) {
|
||||
rendered += render_token(text, *loc);
|
||||
}
|
||||
k.m_text = rendered;
|
||||
k.m_unparsed = false;
|
||||
for (size_t m : members) {
|
||||
mark_as_replaced(katoms[m]);
|
||||
}
|
||||
i = j - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Literal
|
||||
|
||||
void mark_literal_katoms(katom_list& katoms)
|
||||
|
||||
@@ -46,6 +46,7 @@ find_span_katoms(
|
||||
std::vector<Katom>::iterator kbegin, std::vector<Katom>::iterator kend, const Katom& begin, const Katom& end);
|
||||
|
||||
void encode_nonascii_characters(std::vector<Katom>& katoms);
|
||||
void process_unicode_sequences(std::vector<Katom>& katoms);
|
||||
void mark_literal_katoms(std::vector<Katom>& katoms);
|
||||
void hide_special_katoms(std::vector<Katom>& katoms);
|
||||
void mark_ignored_katoms(std::vector<Katom>& katoms);
|
||||
|
||||
@@ -135,3 +135,25 @@ std::string locator_summary(std::vector<Locator> locators)
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Parse "[<file>:<line>.<column>]" -- see the declaration in locator.h.
|
||||
// Scanned from the RIGHT, because a pathname may itself contain ":" and
|
||||
// "." while the line and column never do.
|
||||
Locator Locator::from_string(const std::string& s)
|
||||
{
|
||||
if (s.size() < 6 || s.front() != '[' || s.back() != ']')
|
||||
return Locator::none();
|
||||
std::string body = s.substr(1, s.size() - 2);
|
||||
size_t dot = body.rfind('.');
|
||||
if (dot == std::string::npos) return Locator::none();
|
||||
size_t colon = body.rfind(':', dot);
|
||||
if (colon == std::string::npos) return Locator::none();
|
||||
try {
|
||||
int line = std::stoi(body.substr(colon + 1, dot - colon - 1));
|
||||
int chr = std::stoi(body.substr(dot + 1));
|
||||
// str() prints m_chr + 1; undo that so a round trip is exact.
|
||||
return Locator(fs::path(body.substr(0, colon)), line, chr - 1);
|
||||
} catch (const std::exception&) {
|
||||
return Locator::none();
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,13 @@ public:
|
||||
// errors (found by the error gallery, 2026-08-22). Locator() is for
|
||||
// logging; errors either carry a real document location or this.
|
||||
static Locator none() { return Locator(fs::path{}, -1, -1); }
|
||||
// The inverse of str(): "[<file>:<line>.<column>]" back to a Locator.
|
||||
// A klammer implementation receives its position as the STATE value
|
||||
// K_loc, which is that string; without this it can only pass a
|
||||
// default-constructed Locator, which captures the C++ source location
|
||||
// and prints a file no user can act on. Anything unparseable gives
|
||||
// none() -- a missing location beats a wrong one.
|
||||
static Locator from_string(const std::string& s);
|
||||
std::string str(bool relative = false) const;
|
||||
std::string desc(bool relative = false) const;
|
||||
std::string abbrev(bool include_chr=true) const;
|
||||
|
||||
@@ -435,6 +435,11 @@ void Machine::process_katoms(
|
||||
{
|
||||
mark_literal_klammer_content(katoms);
|
||||
if (literal) mark_literal_katoms(katoms);
|
||||
// Sequences BEFORE the quoted-punctuation hiding: a single-character
|
||||
// token such as the "-" in ^9053 - 53EF^ must reach the sequence pass
|
||||
// as written; an unmatched run falls through untouched and the hiding
|
||||
// then treats its ^P pairs normally.
|
||||
if (nonascii) process_unicode_sequences(katoms);
|
||||
hide_special_katoms(katoms);
|
||||
if (nonascii) encode_nonascii_characters(katoms);
|
||||
if (ignore) mark_ignored_katoms(katoms);
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
// GENERATED by doc/make_precomposed_table.py -- do not edit.
|
||||
// (base letter + combining code point) -> precomposed UTF-8.
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
inline
|
||||
const std::map<std::string, std::string> precomposed {
|
||||
{"a0300", "\xc3\xa0"}, // LATIN SMALL LETTER A WITH GRAVE
|
||||
{"a0301", "\xc3\xa1"}, // LATIN SMALL LETTER A WITH ACUTE
|
||||
{"a0302", "\xc3\xa2"}, // LATIN SMALL LETTER A WITH CIRCUMFLEX
|
||||
{"a0303", "\xc3\xa3"}, // LATIN SMALL LETTER A WITH TILDE
|
||||
{"a0304", "\xc4\x81"}, // LATIN SMALL LETTER A WITH MACRON
|
||||
{"a0308", "\xc3\xa4"}, // LATIN SMALL LETTER A WITH DIAERESIS
|
||||
{"a0306", "\xc4\x83"}, // LATIN SMALL LETTER A WITH BREVE
|
||||
{"a0307", "\xc8\xa7"}, // LATIN SMALL LETTER A WITH DOT ABOVE
|
||||
{"a030A", "\xc3\xa5"}, // LATIN SMALL LETTER A WITH RING ABOVE
|
||||
{"a030C", "\xc7\x8e"}, // LATIN SMALL LETTER A WITH CARON
|
||||
{"b0307", "\xe1\xb8\x83"}, // LATIN SMALL LETTER B WITH DOT ABOVE
|
||||
{"c0301", "\xc4\x87"}, // LATIN SMALL LETTER C WITH ACUTE
|
||||
{"c0302", "\xc4\x89"}, // LATIN SMALL LETTER C WITH CIRCUMFLEX
|
||||
{"c0307", "\xc4\x8b"}, // LATIN SMALL LETTER C WITH DOT ABOVE
|
||||
{"c030C", "\xc4\x8d"}, // LATIN SMALL LETTER C WITH CARON
|
||||
{"c0327", "\xc3\xa7"}, // LATIN SMALL LETTER C WITH CEDILLA
|
||||
{"d0307", "\xe1\xb8\x8b"}, // LATIN SMALL LETTER D WITH DOT ABOVE
|
||||
{"d030C", "\xc4\x8f"}, // LATIN SMALL LETTER D WITH CARON
|
||||
{"d0327", "\xe1\xb8\x91"}, // LATIN SMALL LETTER D WITH CEDILLA
|
||||
{"e0300", "\xc3\xa8"}, // LATIN SMALL LETTER E WITH GRAVE
|
||||
{"e0301", "\xc3\xa9"}, // LATIN SMALL LETTER E WITH ACUTE
|
||||
{"e0302", "\xc3\xaa"}, // LATIN SMALL LETTER E WITH CIRCUMFLEX
|
||||
{"e0303", "\xe1\xba\xbd"}, // LATIN SMALL LETTER E WITH TILDE
|
||||
{"e0304", "\xc4\x93"}, // LATIN SMALL LETTER E WITH MACRON
|
||||
{"e0308", "\xc3\xab"}, // LATIN SMALL LETTER E WITH DIAERESIS
|
||||
{"e0306", "\xc4\x95"}, // LATIN SMALL LETTER E WITH BREVE
|
||||
{"e0307", "\xc4\x97"}, // LATIN SMALL LETTER E WITH DOT ABOVE
|
||||
{"e030C", "\xc4\x9b"}, // LATIN SMALL LETTER E WITH CARON
|
||||
{"e0327", "\xc8\xa9"}, // LATIN SMALL LETTER E WITH CEDILLA
|
||||
{"f0307", "\xe1\xb8\x9f"}, // LATIN SMALL LETTER F WITH DOT ABOVE
|
||||
{"g0301", "\xc7\xb5"}, // LATIN SMALL LETTER G WITH ACUTE
|
||||
{"g0302", "\xc4\x9d"}, // LATIN SMALL LETTER G WITH CIRCUMFLEX
|
||||
{"g0304", "\xe1\xb8\xa1"}, // LATIN SMALL LETTER G WITH MACRON
|
||||
{"g0306", "\xc4\x9f"}, // LATIN SMALL LETTER G WITH BREVE
|
||||
{"g0307", "\xc4\xa1"}, // LATIN SMALL LETTER G WITH DOT ABOVE
|
||||
{"g030C", "\xc7\xa7"}, // LATIN SMALL LETTER G WITH CARON
|
||||
{"g0327", "\xc4\xa3"}, // LATIN SMALL LETTER G WITH CEDILLA
|
||||
{"h0302", "\xc4\xa5"}, // LATIN SMALL LETTER H WITH CIRCUMFLEX
|
||||
{"h0308", "\xe1\xb8\xa7"}, // LATIN SMALL LETTER H WITH DIAERESIS
|
||||
{"h0307", "\xe1\xb8\xa3"}, // LATIN SMALL LETTER H WITH DOT ABOVE
|
||||
{"h030C", "\xc8\x9f"}, // LATIN SMALL LETTER H WITH CARON
|
||||
{"h0327", "\xe1\xb8\xa9"}, // LATIN SMALL LETTER H WITH CEDILLA
|
||||
{"i0300", "\xc3\xac"}, // LATIN SMALL LETTER I WITH GRAVE
|
||||
{"i0301", "\xc3\xad"}, // LATIN SMALL LETTER I WITH ACUTE
|
||||
{"i0302", "\xc3\xae"}, // LATIN SMALL LETTER I WITH CIRCUMFLEX
|
||||
{"i0303", "\xc4\xa9"}, // LATIN SMALL LETTER I WITH TILDE
|
||||
{"i0304", "\xc4\xab"}, // LATIN SMALL LETTER I WITH MACRON
|
||||
{"i0308", "\xc3\xaf"}, // LATIN SMALL LETTER I WITH DIAERESIS
|
||||
{"i0306", "\xc4\xad"}, // LATIN SMALL LETTER I WITH BREVE
|
||||
{"i030C", "\xc7\x90"}, // LATIN SMALL LETTER I WITH CARON
|
||||
{"j0302", "\xc4\xb5"}, // LATIN SMALL LETTER J WITH CIRCUMFLEX
|
||||
{"j030C", "\xc7\xb0"}, // LATIN SMALL LETTER J WITH CARON
|
||||
{"k0301", "\xe1\xb8\xb1"}, // LATIN SMALL LETTER K WITH ACUTE
|
||||
{"k030C", "\xc7\xa9"}, // LATIN SMALL LETTER K WITH CARON
|
||||
{"k0327", "\xc4\xb7"}, // LATIN SMALL LETTER K WITH CEDILLA
|
||||
{"l0301", "\xc4\xba"}, // LATIN SMALL LETTER L WITH ACUTE
|
||||
{"l030C", "\xc4\xbe"}, // LATIN SMALL LETTER L WITH CARON
|
||||
{"l0327", "\xc4\xbc"}, // LATIN SMALL LETTER L WITH CEDILLA
|
||||
{"m0301", "\xe1\xb8\xbf"}, // LATIN SMALL LETTER M WITH ACUTE
|
||||
{"m0307", "\xe1\xb9\x81"}, // LATIN SMALL LETTER M WITH DOT ABOVE
|
||||
{"n0300", "\xc7\xb9"}, // LATIN SMALL LETTER N WITH GRAVE
|
||||
{"n0301", "\xc5\x84"}, // LATIN SMALL LETTER N WITH ACUTE
|
||||
{"n0303", "\xc3\xb1"}, // LATIN SMALL LETTER N WITH TILDE
|
||||
{"n0307", "\xe1\xb9\x85"}, // LATIN SMALL LETTER N WITH DOT ABOVE
|
||||
{"n030C", "\xc5\x88"}, // LATIN SMALL LETTER N WITH CARON
|
||||
{"n0327", "\xc5\x86"}, // LATIN SMALL LETTER N WITH CEDILLA
|
||||
{"o0300", "\xc3\xb2"}, // LATIN SMALL LETTER O WITH GRAVE
|
||||
{"o0301", "\xc3\xb3"}, // LATIN SMALL LETTER O WITH ACUTE
|
||||
{"o0302", "\xc3\xb4"}, // LATIN SMALL LETTER O WITH CIRCUMFLEX
|
||||
{"o0303", "\xc3\xb5"}, // LATIN SMALL LETTER O WITH TILDE
|
||||
{"o0304", "\xc5\x8d"}, // LATIN SMALL LETTER O WITH MACRON
|
||||
{"o0308", "\xc3\xb6"}, // LATIN SMALL LETTER O WITH DIAERESIS
|
||||
{"o0306", "\xc5\x8f"}, // LATIN SMALL LETTER O WITH BREVE
|
||||
{"o0307", "\xc8\xaf"}, // LATIN SMALL LETTER O WITH DOT ABOVE
|
||||
{"o030C", "\xc7\x92"}, // LATIN SMALL LETTER O WITH CARON
|
||||
{"o030B", "\xc5\x91"}, // LATIN SMALL LETTER O WITH DOUBLE ACUTE
|
||||
{"p0301", "\xe1\xb9\x95"}, // LATIN SMALL LETTER P WITH ACUTE
|
||||
{"p0307", "\xe1\xb9\x97"}, // LATIN SMALL LETTER P WITH DOT ABOVE
|
||||
{"r0301", "\xc5\x95"}, // LATIN SMALL LETTER R WITH ACUTE
|
||||
{"r0307", "\xe1\xb9\x99"}, // LATIN SMALL LETTER R WITH DOT ABOVE
|
||||
{"r030C", "\xc5\x99"}, // LATIN SMALL LETTER R WITH CARON
|
||||
{"r0327", "\xc5\x97"}, // LATIN SMALL LETTER R WITH CEDILLA
|
||||
{"s0301", "\xc5\x9b"}, // LATIN SMALL LETTER S WITH ACUTE
|
||||
{"s0302", "\xc5\x9d"}, // LATIN SMALL LETTER S WITH CIRCUMFLEX
|
||||
{"s0307", "\xe1\xb9\xa1"}, // LATIN SMALL LETTER S WITH DOT ABOVE
|
||||
{"s030C", "\xc5\xa1"}, // LATIN SMALL LETTER S WITH CARON
|
||||
{"s0327", "\xc5\x9f"}, // LATIN SMALL LETTER S WITH CEDILLA
|
||||
{"t0308", "\xe1\xba\x97"}, // LATIN SMALL LETTER T WITH DIAERESIS
|
||||
{"t0307", "\xe1\xb9\xab"}, // LATIN SMALL LETTER T WITH DOT ABOVE
|
||||
{"t030C", "\xc5\xa5"}, // LATIN SMALL LETTER T WITH CARON
|
||||
{"t0327", "\xc5\xa3"}, // LATIN SMALL LETTER T WITH CEDILLA
|
||||
{"u0300", "\xc3\xb9"}, // LATIN SMALL LETTER U WITH GRAVE
|
||||
{"u0301", "\xc3\xba"}, // LATIN SMALL LETTER U WITH ACUTE
|
||||
{"u0302", "\xc3\xbb"}, // LATIN SMALL LETTER U WITH CIRCUMFLEX
|
||||
{"u0303", "\xc5\xa9"}, // LATIN SMALL LETTER U WITH TILDE
|
||||
{"u0304", "\xc5\xab"}, // LATIN SMALL LETTER U WITH MACRON
|
||||
{"u0308", "\xc3\xbc"}, // LATIN SMALL LETTER U WITH DIAERESIS
|
||||
{"u0306", "\xc5\xad"}, // LATIN SMALL LETTER U WITH BREVE
|
||||
{"u030A", "\xc5\xaf"}, // LATIN SMALL LETTER U WITH RING ABOVE
|
||||
{"u030C", "\xc7\x94"}, // LATIN SMALL LETTER U WITH CARON
|
||||
{"u030B", "\xc5\xb1"}, // LATIN SMALL LETTER U WITH DOUBLE ACUTE
|
||||
{"v0303", "\xe1\xb9\xbd"}, // LATIN SMALL LETTER V WITH TILDE
|
||||
{"w0300", "\xe1\xba\x81"}, // LATIN SMALL LETTER W WITH GRAVE
|
||||
{"w0301", "\xe1\xba\x83"}, // LATIN SMALL LETTER W WITH ACUTE
|
||||
{"w0302", "\xc5\xb5"}, // LATIN SMALL LETTER W WITH CIRCUMFLEX
|
||||
{"w0308", "\xe1\xba\x85"}, // LATIN SMALL LETTER W WITH DIAERESIS
|
||||
{"w0307", "\xe1\xba\x87"}, // LATIN SMALL LETTER W WITH DOT ABOVE
|
||||
{"w030A", "\xe1\xba\x98"}, // LATIN SMALL LETTER W WITH RING ABOVE
|
||||
{"x0308", "\xe1\xba\x8d"}, // LATIN SMALL LETTER X WITH DIAERESIS
|
||||
{"x0307", "\xe1\xba\x8b"}, // LATIN SMALL LETTER X WITH DOT ABOVE
|
||||
{"y0300", "\xe1\xbb\xb3"}, // LATIN SMALL LETTER Y WITH GRAVE
|
||||
{"y0301", "\xc3\xbd"}, // LATIN SMALL LETTER Y WITH ACUTE
|
||||
{"y0302", "\xc5\xb7"}, // LATIN SMALL LETTER Y WITH CIRCUMFLEX
|
||||
{"y0303", "\xe1\xbb\xb9"}, // LATIN SMALL LETTER Y WITH TILDE
|
||||
{"y0304", "\xc8\xb3"}, // LATIN SMALL LETTER Y WITH MACRON
|
||||
{"y0308", "\xc3\xbf"}, // LATIN SMALL LETTER Y WITH DIAERESIS
|
||||
{"y0307", "\xe1\xba\x8f"}, // LATIN SMALL LETTER Y WITH DOT ABOVE
|
||||
{"y030A", "\xe1\xba\x99"}, // LATIN SMALL LETTER Y WITH RING ABOVE
|
||||
{"z0301", "\xc5\xba"}, // LATIN SMALL LETTER Z WITH ACUTE
|
||||
{"z0302", "\xe1\xba\x91"}, // LATIN SMALL LETTER Z WITH CIRCUMFLEX
|
||||
{"z0307", "\xc5\xbc"}, // LATIN SMALL LETTER Z WITH DOT ABOVE
|
||||
{"z030C", "\xc5\xbe"}, // LATIN SMALL LETTER Z WITH CARON
|
||||
{"A0300", "\xc3\x80"}, // LATIN CAPITAL LETTER A WITH GRAVE
|
||||
{"A0301", "\xc3\x81"}, // LATIN CAPITAL LETTER A WITH ACUTE
|
||||
{"A0302", "\xc3\x82"}, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
|
||||
{"A0303", "\xc3\x83"}, // LATIN CAPITAL LETTER A WITH TILDE
|
||||
{"A0304", "\xc4\x80"}, // LATIN CAPITAL LETTER A WITH MACRON
|
||||
{"A0308", "\xc3\x84"}, // LATIN CAPITAL LETTER A WITH DIAERESIS
|
||||
{"A0306", "\xc4\x82"}, // LATIN CAPITAL LETTER A WITH BREVE
|
||||
{"A0307", "\xc8\xa6"}, // LATIN CAPITAL LETTER A WITH DOT ABOVE
|
||||
{"A030A", "\xc3\x85"}, // LATIN CAPITAL LETTER A WITH RING ABOVE
|
||||
{"A030C", "\xc7\x8d"}, // LATIN CAPITAL LETTER A WITH CARON
|
||||
{"B0307", "\xe1\xb8\x82"}, // LATIN CAPITAL LETTER B WITH DOT ABOVE
|
||||
{"C0301", "\xc4\x86"}, // LATIN CAPITAL LETTER C WITH ACUTE
|
||||
{"C0302", "\xc4\x88"}, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
|
||||
{"C0307", "\xc4\x8a"}, // LATIN CAPITAL LETTER C WITH DOT ABOVE
|
||||
{"C030C", "\xc4\x8c"}, // LATIN CAPITAL LETTER C WITH CARON
|
||||
{"C0327", "\xc3\x87"}, // LATIN CAPITAL LETTER C WITH CEDILLA
|
||||
{"D0307", "\xe1\xb8\x8a"}, // LATIN CAPITAL LETTER D WITH DOT ABOVE
|
||||
{"D030C", "\xc4\x8e"}, // LATIN CAPITAL LETTER D WITH CARON
|
||||
{"D0327", "\xe1\xb8\x90"}, // LATIN CAPITAL LETTER D WITH CEDILLA
|
||||
{"E0300", "\xc3\x88"}, // LATIN CAPITAL LETTER E WITH GRAVE
|
||||
{"E0301", "\xc3\x89"}, // LATIN CAPITAL LETTER E WITH ACUTE
|
||||
{"E0302", "\xc3\x8a"}, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
|
||||
{"E0303", "\xe1\xba\xbc"}, // LATIN CAPITAL LETTER E WITH TILDE
|
||||
{"E0304", "\xc4\x92"}, // LATIN CAPITAL LETTER E WITH MACRON
|
||||
{"E0308", "\xc3\x8b"}, // LATIN CAPITAL LETTER E WITH DIAERESIS
|
||||
{"E0306", "\xc4\x94"}, // LATIN CAPITAL LETTER E WITH BREVE
|
||||
{"E0307", "\xc4\x96"}, // LATIN CAPITAL LETTER E WITH DOT ABOVE
|
||||
{"E030C", "\xc4\x9a"}, // LATIN CAPITAL LETTER E WITH CARON
|
||||
{"E0327", "\xc8\xa8"}, // LATIN CAPITAL LETTER E WITH CEDILLA
|
||||
{"F0307", "\xe1\xb8\x9e"}, // LATIN CAPITAL LETTER F WITH DOT ABOVE
|
||||
{"G0301", "\xc7\xb4"}, // LATIN CAPITAL LETTER G WITH ACUTE
|
||||
{"G0302", "\xc4\x9c"}, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
|
||||
{"G0304", "\xe1\xb8\xa0"}, // LATIN CAPITAL LETTER G WITH MACRON
|
||||
{"G0306", "\xc4\x9e"}, // LATIN CAPITAL LETTER G WITH BREVE
|
||||
{"G0307", "\xc4\xa0"}, // LATIN CAPITAL LETTER G WITH DOT ABOVE
|
||||
{"G030C", "\xc7\xa6"}, // LATIN CAPITAL LETTER G WITH CARON
|
||||
{"G0327", "\xc4\xa2"}, // LATIN CAPITAL LETTER G WITH CEDILLA
|
||||
{"H0302", "\xc4\xa4"}, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
|
||||
{"H0308", "\xe1\xb8\xa6"}, // LATIN CAPITAL LETTER H WITH DIAERESIS
|
||||
{"H0307", "\xe1\xb8\xa2"}, // LATIN CAPITAL LETTER H WITH DOT ABOVE
|
||||
{"H030C", "\xc8\x9e"}, // LATIN CAPITAL LETTER H WITH CARON
|
||||
{"H0327", "\xe1\xb8\xa8"}, // LATIN CAPITAL LETTER H WITH CEDILLA
|
||||
{"I0300", "\xc3\x8c"}, // LATIN CAPITAL LETTER I WITH GRAVE
|
||||
{"I0301", "\xc3\x8d"}, // LATIN CAPITAL LETTER I WITH ACUTE
|
||||
{"I0302", "\xc3\x8e"}, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
|
||||
{"I0303", "\xc4\xa8"}, // LATIN CAPITAL LETTER I WITH TILDE
|
||||
{"I0304", "\xc4\xaa"}, // LATIN CAPITAL LETTER I WITH MACRON
|
||||
{"I0308", "\xc3\x8f"}, // LATIN CAPITAL LETTER I WITH DIAERESIS
|
||||
{"I0306", "\xc4\xac"}, // LATIN CAPITAL LETTER I WITH BREVE
|
||||
{"I0307", "\xc4\xb0"}, // LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||
{"I030C", "\xc7\x8f"}, // LATIN CAPITAL LETTER I WITH CARON
|
||||
{"J0302", "\xc4\xb4"}, // LATIN CAPITAL LETTER J WITH CIRCUMFLEX
|
||||
{"K0301", "\xe1\xb8\xb0"}, // LATIN CAPITAL LETTER K WITH ACUTE
|
||||
{"K030C", "\xc7\xa8"}, // LATIN CAPITAL LETTER K WITH CARON
|
||||
{"K0327", "\xc4\xb6"}, // LATIN CAPITAL LETTER K WITH CEDILLA
|
||||
{"L0301", "\xc4\xb9"}, // LATIN CAPITAL LETTER L WITH ACUTE
|
||||
{"L030C", "\xc4\xbd"}, // LATIN CAPITAL LETTER L WITH CARON
|
||||
{"L0327", "\xc4\xbb"}, // LATIN CAPITAL LETTER L WITH CEDILLA
|
||||
{"M0301", "\xe1\xb8\xbe"}, // LATIN CAPITAL LETTER M WITH ACUTE
|
||||
{"M0307", "\xe1\xb9\x80"}, // LATIN CAPITAL LETTER M WITH DOT ABOVE
|
||||
{"N0300", "\xc7\xb8"}, // LATIN CAPITAL LETTER N WITH GRAVE
|
||||
{"N0301", "\xc5\x83"}, // LATIN CAPITAL LETTER N WITH ACUTE
|
||||
{"N0303", "\xc3\x91"}, // LATIN CAPITAL LETTER N WITH TILDE
|
||||
{"N0307", "\xe1\xb9\x84"}, // LATIN CAPITAL LETTER N WITH DOT ABOVE
|
||||
{"N030C", "\xc5\x87"}, // LATIN CAPITAL LETTER N WITH CARON
|
||||
{"N0327", "\xc5\x85"}, // LATIN CAPITAL LETTER N WITH CEDILLA
|
||||
{"O0300", "\xc3\x92"}, // LATIN CAPITAL LETTER O WITH GRAVE
|
||||
{"O0301", "\xc3\x93"}, // LATIN CAPITAL LETTER O WITH ACUTE
|
||||
{"O0302", "\xc3\x94"}, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
|
||||
{"O0303", "\xc3\x95"}, // LATIN CAPITAL LETTER O WITH TILDE
|
||||
{"O0304", "\xc5\x8c"}, // LATIN CAPITAL LETTER O WITH MACRON
|
||||
{"O0308", "\xc3\x96"}, // LATIN CAPITAL LETTER O WITH DIAERESIS
|
||||
{"O0306", "\xc5\x8e"}, // LATIN CAPITAL LETTER O WITH BREVE
|
||||
{"O0307", "\xc8\xae"}, // LATIN CAPITAL LETTER O WITH DOT ABOVE
|
||||
{"O030C", "\xc7\x91"}, // LATIN CAPITAL LETTER O WITH CARON
|
||||
{"O030B", "\xc5\x90"}, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
|
||||
{"P0301", "\xe1\xb9\x94"}, // LATIN CAPITAL LETTER P WITH ACUTE
|
||||
{"P0307", "\xe1\xb9\x96"}, // LATIN CAPITAL LETTER P WITH DOT ABOVE
|
||||
{"R0301", "\xc5\x94"}, // LATIN CAPITAL LETTER R WITH ACUTE
|
||||
{"R0307", "\xe1\xb9\x98"}, // LATIN CAPITAL LETTER R WITH DOT ABOVE
|
||||
{"R030C", "\xc5\x98"}, // LATIN CAPITAL LETTER R WITH CARON
|
||||
{"R0327", "\xc5\x96"}, // LATIN CAPITAL LETTER R WITH CEDILLA
|
||||
{"S0301", "\xc5\x9a"}, // LATIN CAPITAL LETTER S WITH ACUTE
|
||||
{"S0302", "\xc5\x9c"}, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
|
||||
{"S0307", "\xe1\xb9\xa0"}, // LATIN CAPITAL LETTER S WITH DOT ABOVE
|
||||
{"S030C", "\xc5\xa0"}, // LATIN CAPITAL LETTER S WITH CARON
|
||||
{"S0327", "\xc5\x9e"}, // LATIN CAPITAL LETTER S WITH CEDILLA
|
||||
{"T0307", "\xe1\xb9\xaa"}, // LATIN CAPITAL LETTER T WITH DOT ABOVE
|
||||
{"T030C", "\xc5\xa4"}, // LATIN CAPITAL LETTER T WITH CARON
|
||||
{"T0327", "\xc5\xa2"}, // LATIN CAPITAL LETTER T WITH CEDILLA
|
||||
{"U0300", "\xc3\x99"}, // LATIN CAPITAL LETTER U WITH GRAVE
|
||||
{"U0301", "\xc3\x9a"}, // LATIN CAPITAL LETTER U WITH ACUTE
|
||||
{"U0302", "\xc3\x9b"}, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
|
||||
{"U0303", "\xc5\xa8"}, // LATIN CAPITAL LETTER U WITH TILDE
|
||||
{"U0304", "\xc5\xaa"}, // LATIN CAPITAL LETTER U WITH MACRON
|
||||
{"U0308", "\xc3\x9c"}, // LATIN CAPITAL LETTER U WITH DIAERESIS
|
||||
{"U0306", "\xc5\xac"}, // LATIN CAPITAL LETTER U WITH BREVE
|
||||
{"U030A", "\xc5\xae"}, // LATIN CAPITAL LETTER U WITH RING ABOVE
|
||||
{"U030C", "\xc7\x93"}, // LATIN CAPITAL LETTER U WITH CARON
|
||||
{"U030B", "\xc5\xb0"}, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
|
||||
{"V0303", "\xe1\xb9\xbc"}, // LATIN CAPITAL LETTER V WITH TILDE
|
||||
{"W0300", "\xe1\xba\x80"}, // LATIN CAPITAL LETTER W WITH GRAVE
|
||||
{"W0301", "\xe1\xba\x82"}, // LATIN CAPITAL LETTER W WITH ACUTE
|
||||
{"W0302", "\xc5\xb4"}, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
|
||||
{"W0308", "\xe1\xba\x84"}, // LATIN CAPITAL LETTER W WITH DIAERESIS
|
||||
{"W0307", "\xe1\xba\x86"}, // LATIN CAPITAL LETTER W WITH DOT ABOVE
|
||||
{"X0308", "\xe1\xba\x8c"}, // LATIN CAPITAL LETTER X WITH DIAERESIS
|
||||
{"X0307", "\xe1\xba\x8a"}, // LATIN CAPITAL LETTER X WITH DOT ABOVE
|
||||
{"Y0300", "\xe1\xbb\xb2"}, // LATIN CAPITAL LETTER Y WITH GRAVE
|
||||
{"Y0301", "\xc3\x9d"}, // LATIN CAPITAL LETTER Y WITH ACUTE
|
||||
{"Y0302", "\xc5\xb6"}, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
|
||||
{"Y0303", "\xe1\xbb\xb8"}, // LATIN CAPITAL LETTER Y WITH TILDE
|
||||
{"Y0304", "\xc8\xb2"}, // LATIN CAPITAL LETTER Y WITH MACRON
|
||||
{"Y0308", "\xc5\xb8"}, // LATIN CAPITAL LETTER Y WITH DIAERESIS
|
||||
{"Y0307", "\xe1\xba\x8e"}, // LATIN CAPITAL LETTER Y WITH DOT ABOVE
|
||||
{"Z0301", "\xc5\xb9"}, // LATIN CAPITAL LETTER Z WITH ACUTE
|
||||
{"Z0302", "\xe1\xba\x90"}, // LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
|
||||
{"Z0307", "\xc5\xbb"}, // LATIN CAPITAL LETTER Z WITH DOT ABOVE
|
||||
{"Z030C", "\xc5\xbd"}, // LATIN CAPITAL LETTER Z WITH CARON
|
||||
};
|
||||
Reference in new issue
Block a user