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:
ack committed 2026-08-25 23:30:55 +02:00
1 parent 37b6ba1c4f
commit af29bbf7ac
47 files changed
+1921 -135

No files matched your search

+1 -1
View File
@@ -51,7 +51,7 @@ are regenerated on each release — patches cannot be merged directly.
Report problems (or send patches) to the author; accepted changes are Report problems (or send patches) to the author; accepted changes are
applied to the development tree and appear in a following snapshot. applied to the development tree and appear in a following snapshot.
This snapshot was assembled from development commit `07ce5ea86a0a`. This snapshot was assembled from development commit `61e598698891`.
## License ## License
+8 -3
View File
@@ -161,10 +161,11 @@ int main(int argc, char* argv[])
// rather than verbosity levels. // rather than verbosity levels.
args.var("k", "Show klammers. With <text>, only those whose name or description contains <text>.", "text"); args.var("k", "Show klammers. With <text>, only those whose name or description contains <text>.", "text");
args.flag("c", "Show the codes for accented vowels and other special characters"); args.flag("c", "Show the codes for accented vowels and other special characters");
args.flag("u", "Describe including Unicode characters in KLammertext");
args.flag("t", "Show the targets for Klammertext output"); args.flag("t", "Show the targets for Klammertext output");
args.opt("i", "Input filename to analyze containing klammer and other definitions", "filename", "", "'text'"); args.opt("i", "Input filename to analyze containing klammer and other definitions", "filename", "", "'text'");
args.var("klammersets", "List the available klammersets, or load one or more for analysis. Enter \"--klammersets help\" for details.", "symbols"); args.var("klammersets", "List the available klammersets, or load one or more for analysis. Enter \"--klammersets help\" for details.", "symbols");
args.var("font", "List installed fonts. Enter \"--font help\" for font maintenance commands."); args.var("typeface", "List installed typefaces. Enter \"--typeface help\" for typeface maintenance commands.");
args.flag("argtypes", "Show the klammer argument types"); args.flag("argtypes", "Show the klammer argument types");
args.var("katoms", "Show the katom types. Enter \"--katoms help\" for details."); args.var("katoms", "Show the katom types. Enter \"--katoms help\" for details.");
args.flag("rewrite", "Show the katom rewrite patterns"); args.flag("rewrite", "Show the katom rewrite patterns");
@@ -192,6 +193,10 @@ int main(int argc, char* argv[])
std::cout << "\n"; std::cout << "\n";
} }
if (p("u")) {
unicode_examples();
}
if (p("argtypes")) { if (p("argtypes")) {
Argtype_registry argtypes; Argtype_registry argtypes;
std::cout << boldblack << "\nStandard klammer argument types\n" << black; std::cout << boldblack << "\nStandard klammer argument types\n" << black;
@@ -219,8 +224,8 @@ int main(int argc, char* argv[])
// The --font subcommands operate on the Klammertext font store // The --font subcommands operate on the Klammertext font store
// (infrastructure) and load no klammer set. // (infrastructure) and load no klammer set.
if (args.given("font")) { if (args.given("typeface")) {
font_command(option_words(args, "font")); font_command(option_words(args, "typeface"));
return 0; return 0;
} }
+1 -1
View File
@@ -128,7 +128,7 @@ That's it — you're running Klammertext.
others) are built in, so PDFs work with no internet connection. Font others) are built in, so PDFs work with no internet connection. Font
resolution is entirely offline: asking for a font that isn't installed resolution is entirely offline: asking for a font that isn't installed
lists the available fonts, and additional fonts are installed from font lists the available fonts, and additional fonts are installed from font
files you already have with `kdesc --font install <folder>` (`kdesc --font files you already have with `kdesc --typeface install <folder>` (`kdesc --typeface
help` explains). help` explains).
- **Updating later.** When a new version is announced, run `klammertext-update` - **Updating later.** When a new version is announced, run `klammertext-update`
in Terminal. in Terminal.
+95
View File
@@ -0,0 +1,95 @@
Copyright 2018 The Crimson Pro Project Authors (https://github.com/Fonthausen/CrimsonPro)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
(The copyright line above and the license URL are the font's own, read
from its name table; the license text below is the canonical OFL 1.1.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+95
View File
@@ -0,0 +1,95 @@
Copyright 2017 The EB Garamond Project Authors (https://github.com/octaviopardo/EBGaramond12)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
(The copyright line above and the license URL are the font's own, read
from its name table; the license text below is the canonical OFL 1.1.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+95
View File
@@ -0,0 +1,95 @@
Copyright 2006 The Inconsolata Project Authors (https://github.com/cyrealtype/Inconsolata)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
(The copyright line above and the license URL are the font's own, read
from its name table; the license text below is the canonical OFL 1.1.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+95
View File
@@ -0,0 +1,95 @@
Copyright (c) 2012, Pablo Impallari (www.impallari.com|impallari@gmail.com), Copyright (c) 2012, Rodrigo Fuenzalida (www.rfuenzalida.com|hello@rfuenzalida.com), with Reserved Font Name Libre Baskerville.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
(The copyright line above and the license URL are the font's own, read
from its name table; the license text below is the canonical OFL 1.1.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
@font-face {
font-family: 'Noto Sans Cuneiform';
font-style: normal;
font-weight: 400;
src: url('noto-sans-cuneiform/Regular.ttf') format('truetype');
}
+93
View File
@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/cuneiform)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
@font-face {
font-family: 'Noto Sans Egyptian Hieroglyphs';
font-style: normal;
font-weight: 400;
src: url('noto-sans-egyptian-hieroglyphs/Regular.ttf') format('truetype');
}
@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/egyptian-hieroglyphs)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+95
View File
@@ -0,0 +1,95 @@
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
(The copyright line above and the license URL are the font's own, read
from its name table; the license text below is the canonical OFL 1.1.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+99
View File
@@ -0,0 +1,99 @@
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
with Reserved Font Name 'Source'.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
(The copyright line above is the font's own, read from its name table.
This binary carries no license record of its own -- it predates the
practice -- so the license is stated from the upstream release it comes
from: https://github.com/adobe-fonts/source-code-pro, whose LICENSE.md
is the OFL 1.1 text below.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+95
View File
@@ -0,0 +1,95 @@
© 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
(The copyright line above and the license URL are the font's own, read
from its name table; the license text below is the canonical OFL 1.1.)
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+2 -2
View File
@@ -63,11 +63,11 @@ public:
// next -/-- token (zero or more). get(name) returns the words // next -/-- token (zero or more). get(name) returns the words
// space-joined; given(name) distinguishes "--name with no words" // space-joined; given(name) distinguishes "--name with no words"
// from an absent --name. Used for subcommand-style interfaces // 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. // A variadic option: the words after the flag, up to the next one.
// `parameter` is shown in the usage line as "[<parameter>]" -- an option // `parameter` is shown in the usage line as "[<parameter>]" -- an option
// whose words are a free argument (a search phrase) should name it, so // 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. // --coverage) leaves it empty and documents its words in its own help.
void var(const std::string& name, const std::string& desc, void var(const std::string& name, const std::string& desc,
const std::string& parameter = ""); const std::string& parameter = "");
+97 -10
View File
@@ -2,9 +2,26 @@
#include "util.h" #include "util.h"
#include "character.h" #include "character.h"
#include "precomposed.h"
#include "error.h"
#include "target.h"
#include "log.h" #include "log.h"
#include "show.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 inline
std::string klammertext_special_characters { "@|*^#" }; 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) std::string unicode_hex_to_char(std::string s, int width=4) //, std::string marker)
{ {
(void)K::log(4, s); (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::regex hit { regex_escape((*p)[0].str()) };
std::string ch = (*p)[1].str(); std::string ch = (*p)[1].str();
std::string d = (*p)[2].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; return result;
} }
@@ -124,15 +159,14 @@ std::string process_pinyin(std::string s)
std::string vowel = (*p)[1].str(); std::string vowel = (*p)[1].str();
std::string tone = (*p)[2].str(); std::string tone = (*p)[2].str();
result = std::regex_replace(result, hit, result = std::regex_replace(result, hit,
vowel + unicode_hex_to_char(pinyin_tones[tone].first)); compose(vowel, pinyin_tones[tone].first));
} }
return result; 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); (void)K::log(4);
//return std::regex_replace(s, unicode_re, hidehat + "$1" + hidehat);
std::string result {s}; std::string result {s};
std::sregex_iterator end {}; std::sregex_iterator end {};
for (std::sregex_iterator p { s.begin(), s.end(), unicode_re }; p!= end; ++p) { 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 // 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 // 5-digit code point to its first four digits (^13000^ rendered as
// U+1300 followed by a literal "0"). // 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, result = std::regex_replace(result, hit_re,
utf8char(std::stoi((*p)[1].str(), nullptr, 16))); hide_structural_characters(utf8char(cp)));
} }
return result; 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) if (s.find("^") == std::string::npos)
return s; return s;
@@ -177,7 +225,7 @@ std::string encode(const std::string& s)
result = process_extended_latin(result); result = process_extended_latin(result);
if (dbg) std::cout << "extended_latin: " << result << "\n"; 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"; if (dbg) std::cout << "unicode: " << result << "\n";
result = process_diacritics(result); result = process_diacritics(result);
@@ -225,7 +273,7 @@ void diacritics_examples(const std::string& kt_filename)
<< " @i-Displayed | @i-Written | @i-Name\n"; << " @i-Displayed | @i-Written | @i-Name\n";
else else
std::cout << boldblack std::cout << boldblack
<< "\nDiacritics (with typical base characters)\n" << black; << "\nDiacritics (with typical base characters)\n\n" << black;
std::string line_sep = "|| @line@ "; std::string line_sep = "|| @line@ ";
for (char symbol : diacritic_symbols_order) { for (char symbol : diacritic_symbols_order) {
@@ -260,7 +308,7 @@ void extended_latin_examples(const std::string& kt_filename)
kt << "@table :caption " << title << " |\n" kt << "@table :caption " << title << " |\n"
<< " @i-Displayed | @i-Written | @i-Name\n"; << " @i-Displayed | @i-Written | @i-Name\n";
else else
std::cout << "\n" << boldblack << title << black << "\n"; std::cout << "\n" << boldblack << title << black << "\n\n";
std::string line_sep = "|| @line@ "; std::string line_sep = "|| @line@ ";
for (const std::string& symbol : extended_latin_symbols) { 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"; << " @i-Displayed | @i-Written | @i-Name\n";
else else
std::cout << boldblack 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@ "; std::string line_sep = "|| @line@ ";
for (auto [symbol, codename] : pinyin_tones) { 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); 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() void show_special_characters()
{ {
std::cout << std::setfill(' '); std::cout << std::setfill(' ');
+45 -3
View File
@@ -11,6 +11,8 @@
#include <regex> #include <regex>
#include <unistd.h> #include <unistd.h>
#include "locator.h"
// ^UUUU^ — a Unicode code point in hex, 4 to 6 digits (^263A^ is the BMP, // ^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 // ^13000^ EGYPTIAN HIEROGLYPH A001, ^10FFFD^ the top of the range), or a
// single digit. Lengths 2-3 are NOT accepted: two- and three-letter // 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"}}, {"3", {"030C", "falling-rising"}},
{"4", {"0300", "falling"}}}; {"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 inline
bool is_tty() { return isatty(fileno(stdout)); } bool is_tty() { return isatty(fileno(stdout)); }
//const char* italic_on() { return tty() ? "\033[3m" : ""; }
//const char* italic_off() { return tty() ? "\033[0m" : ""; }
inline inline
const std::string italic_on() { return is_tty() ? "\033[3m" : ""; } const std::string italic_on() { return is_tty() ? "\033[3m" : ""; }
inline inline
const std::string italic_off() { return is_tty() ? "\033[0m" : ""; } 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 diacritics_examples(const std::string& kt_filename = "");
void extended_latin_examples(const std::string& kt_filename = ""); void extended_latin_examples(const std::string& kt_filename = "");
void pinyin_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(); void show_special_characters();
+8 -8
View File
@@ -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()) if (family_name.empty())
return {}; return {};
@@ -222,26 +222,26 @@ Resolved_font resolve_font(const std::string& family_name)
if (!font.family_name.empty()) { if (!font.family_name.empty()) {
extract_font_metrics(font); extract_font_metrics(font);
// A DERIVED value: an installed font may shadow a default one, and // 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 // The commonest font question -- "why does it not look like the
// one I installed?" -- is answered by this line. // 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; return font;
} }
} }
std::stringstream ss {}; std::stringstream ss {};
ss << "The font \"" << family_name << "\" is not installed.\n\n" ss << "The typeface \"" << family_name << "\" is not installed.\n\n"
<< "Available fonts:\n"; << "Available typefaces:\n";
for (const std::string& name : available_font_families()) { for (const std::string& name : available_font_families()) {
ss << " " << name << "\n"; 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" << "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" << "place its files as <fonts-dir>/" << dir_name << "/*.ttf with a\n"
<< "<fonts-dir>/" << dir_name << ".css declaring its @font-face variants."; << "<fonts-dir>/" << dir_name << ".css declaring its @font-face variants.";
throw Argument_error(ss.str()); throw Argument_error(ss.str(), loc);
} }
+6 -1
View File
@@ -3,6 +3,7 @@
// The Klammertext font store (infrastructure; see font_store.cpp). // The Klammertext font store (infrastructure; see font_store.cpp).
#include <string> #include <string>
#include "locator.h"
#include <vector> #include <vector>
struct Resolved_font { struct Resolved_font {
@@ -27,7 +28,11 @@ std::vector<std::string> installed_font_dirs();
std::string default_font_dir(); std::string default_font_dir();
// Every installable font family found across those directories. // Every installable font family found across those directories.
std::vector<std::string> available_font_families(); 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); void install_resolved_font(const Resolved_font& font, std::string output_dir);
// One font file classified by its internal metadata (name table, OS/2). // One font file classified by its internal metadata (name table, OS/2).
+154 -1
View File
@@ -281,11 +281,164 @@ void encode_nonascii_characters(katom_list& katoms)
{ {
for (Katom& k : katoms) { for (Katom& k : katoms) {
if (is_nonascii(k)) { 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 // Literal
void mark_literal_katoms(katom_list& katoms) void mark_literal_katoms(katom_list& katoms)
+1
View File
@@ -46,6 +46,7 @@ find_span_katoms(
std::vector<Katom>::iterator kbegin, std::vector<Katom>::iterator kend, const Katom& begin, const Katom& end); 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 encode_nonascii_characters(std::vector<Katom>& katoms);
void process_unicode_sequences(std::vector<Katom>& katoms);
void mark_literal_katoms(std::vector<Katom>& katoms); void mark_literal_katoms(std::vector<Katom>& katoms);
void hide_special_katoms(std::vector<Katom>& katoms); void hide_special_katoms(std::vector<Katom>& katoms);
void mark_ignored_katoms(std::vector<Katom>& katoms); void mark_ignored_katoms(std::vector<Katom>& katoms);
+22
View File
@@ -135,3 +135,25 @@ std::string locator_summary(std::vector<Locator> locators)
} }
return ""; 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();
}
}
+7
View File
@@ -32,6 +32,13 @@ public:
// errors (found by the error gallery, 2026-08-22). Locator() is for // errors (found by the error gallery, 2026-08-22). Locator() is for
// logging; errors either carry a real document location or this. // logging; errors either carry a real document location or this.
static Locator none() { return Locator(fs::path{}, -1, -1); } 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 str(bool relative = false) const;
std::string desc(bool relative = false) const; std::string desc(bool relative = false) const;
std::string abbrev(bool include_chr=true) const; std::string abbrev(bool include_chr=true) const;
+5
View File
@@ -435,6 +435,11 @@ void Machine::process_katoms(
{ {
mark_literal_klammer_content(katoms); mark_literal_klammer_content(katoms);
if (literal) mark_literal_katoms(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); hide_special_katoms(katoms);
if (nonascii) encode_nonascii_characters(katoms); if (nonascii) encode_nonascii_characters(katoms);
if (ignore) mark_ignored_katoms(katoms); if (ignore) mark_ignored_katoms(katoms);
+250
View File
@@ -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
};
+5 -5
View File
@@ -278,8 +278,8 @@ class Code(klammer_base.Klammer_base):
return measure + L.add_caption( return measure + L.add_caption(
boxed, "Listing", self.number, self.caption, width, boxed, "Listing", self.number, self.caption, width,
hpos=self.hpos, side=self.caption_side, hpos=self.hpos, side=self.caption_side,
font_symbol=self.caption_font, font_symbol=self.caption_style,
font_size=self.caption_font_size, offset=self.offset) font_size=self.caption_size, offset=self.offset)
return measure + L.caption_wrapper(boxed, self.hpos, return measure + L.caption_wrapper(boxed, self.hpos,
offset=self.offset) offset=self.offset)
@@ -298,7 +298,7 @@ class Code(klammer_base.Klammer_base):
if captioned: if captioned:
caption = L.make_caption_text( caption = L.make_caption_text(
self.number, "Listing", self.caption, self.number, "Listing", self.caption,
self.caption_font, self.caption_font_size) self.caption_style, self.caption_size)
# \nobreak: a caption must not be separated from its listing by # \nobreak: a caption must not be separated from its listing by
# a page break, even though the listing itself may break. # a page break, even though the listing itself may break.
block = (f"\\noindent\\hspace*{{{shift}}}" block = (f"\\noindent\\hspace*{{{shift}}}"
@@ -327,8 +327,8 @@ class Code(klammer_base.Klammer_base):
if self.number or self.caption: if self.number or self.caption:
return html_util.add_caption( return html_util.add_caption(
listing, "Listing", self.number, self.caption, listing, "Listing", self.number, self.caption,
self.caption_font, self.hpos, self.caption_side, True, self.caption_style, self.hpos, self.caption_side, True,
self.caption_font_size, offset=self.offset) self.caption_size, offset=self.offset)
return html_util.hpos_container(listing, self.hpos, self.offset).str() return html_util.hpos_container(listing, self.hpos, self.offset).str()
+4 -4
View File
@@ -40,10 +40,10 @@
:include_sks_js.bool true :include_sks_js.bool true
:include_fonts.bool true :include_fonts.bool true
:serif_font Crimson Pro :serif_typeface Crimson Pro
:sans_font Open Sans :sans_typeface Open Sans
:mono_font Inconsolata :mono_typeface Inconsolata
:font_scale 1.0 :text_scale 1.0
:landscape.bool.tex false :landscape.bool.tex false
:paper_size..tex a4paper :paper_size..tex a4paper
+143 -10
View File
@@ -31,6 +31,10 @@ Document_class::Document_class(Machine& machine) : Klammer_base(machine)
// show("document in main SKS"); // show("document in main SKS");
m_machine = machine; m_machine = machine;
sloc = get("K_loc"); sloc = get("K_loc");
// The klammer's own position, so an argument error points at the
// @document application rather than at document_class.cpp: a
// default-constructed Locator captures the C++ source location.
loc = Locator::from_string(sloc);
m_title = get("title"); m_title = get("title");
m_subtitle = get("subtitle"); m_subtitle = get("subtitle");
m_page_title = get("page_title"); m_page_title = get("page_title");
@@ -77,17 +81,17 @@ Document_class::Document_class(Machine& machine) : Klammer_base(machine)
s = s.substr(1, s.size() - 2); s = s.substr(1, s.size() - 2);
return s; return s;
}; };
m_serif_font = strip_quotes(get("serif_font")); m_serif_typeface = strip_quotes(get("serif_typeface"));
m_sans_font = strip_quotes(get("sans_font")); m_sans_typeface = strip_quotes(get("sans_typeface"));
m_mono_font = strip_quotes(get("mono_font")); m_mono_typeface = strip_quotes(get("mono_typeface"));
m_font_scale = stof(get("font_scale")); m_text_scale = stof(get("text_scale"));
if (!m_serif_font.empty()) if (!m_serif_typeface.empty())
m_resolved_serif = resolve_font(m_serif_font); m_resolved_serif = resolve_font(m_serif_typeface, loc);
if (!m_sans_font.empty()) if (!m_sans_typeface.empty())
m_resolved_sans = resolve_font(m_sans_font); m_resolved_sans = resolve_font(m_sans_typeface, loc);
if (!m_mono_font.empty()) if (!m_mono_typeface.empty())
m_resolved_mono = resolve_font(m_mono_font); m_resolved_mono = resolve_font(m_mono_typeface, loc);
paper_size = get("paper_size"); paper_size = get("paper_size");
landscape = strbool(get("landscape"), loc); landscape = strbool(get("landscape"), loc);
leading = stof(get("leading")); leading = stof(get("leading"));
@@ -207,6 +211,135 @@ void write_file(const std::string& filename, const std::string& contents, bool w
} }
// ---------------------------------------------------------------------
// Typeface markers (@typeface; see the marker note in sks/font/font.py)
//
// Hand-rolled scanning, no std::regex: these run over the whole assembled
// document and libstdc++'s regex executor recurses per character (the
// documented SIGSEGV hazard).
// ---------------------------------------------------------------------
namespace {
const std::string typeface_tag { "__TYPEFACE__" };
// The next marker at or after `from`. Returns false when there is none.
// A marker is __TYPEFACE__<name>__<scale>__ : the two fields are taken up
// to the next "__", so neither may contain one (a typeface name with a
// double underscore is not a thing, and the scale is a number).
bool next_typeface_marker(const std::string& text, size_t from,
size_t& start, size_t& end,
std::string& name, std::string& scale)
{
start = text.find(typeface_tag, from);
if (start == std::string::npos) return false;
size_t name_at = start + typeface_tag.size();
size_t name_end = text.find("__", name_at);
if (name_end == std::string::npos) return false;
size_t scale_at = name_end + 2;
size_t scale_end = text.find("__", scale_at);
if (scale_end == std::string::npos) return false;
name = text.substr(name_at, name_end - name_at);
scale = text.substr(scale_at, scale_end - scale_at);
end = scale_end + 2;
return true;
}
// The fontspec command name for the nth distinct (typeface, scale) pair.
// A TeX command name may hold only letters, and a typeface name may hold
// anything -- so the pairs are NUMBERED rather than transliterated, and
// the number is turned into letters. Nothing outside this file needs to
// know the rule: the klammer leaves a marker and this replaces it, so
// there is no second implementation to keep in step.
std::string typeface_command(size_t index)
{
std::string suffix {};
do {
suffix = char('a' + (index % 26)) + suffix;
index /= 26;
} while (index > 0);
return "\\ktface" + suffix;
}
} // namespace
void Document_class::collect_typefaces(const std::string& text)
{
size_t pos = 0, start = 0, end = 0;
std::string name {}, scale {};
while (next_typeface_marker(text, pos, start, end, name, scale)) {
std::pair<std::string, std::string> entry { name, scale };
if (std::find(m_extra_typefaces.begin(), m_extra_typefaces.end(), entry)
== m_extra_typefaces.end()) {
m_extra_typefaces.push_back(entry);
// Resolve ONCE per distinct typeface: resolve_font() throws a
// located error naming the available typefaces if the store
// does not have it, which is the whole error path for a
// misspelled or uninstalled typeface.
bool seen = false;
for (const auto& rf : m_resolved_extra)
if (rf.family_name == name) seen = true;
if (!seen) {
// What a @typeface klammer asked for, and what the store
// resolved it to: a decision the user cannot read off
// their own input, so "-v 1".
Resolved_font rf = resolve_font(name, loc);
(void)K::log(1, "Typeface \"" + name + "\" resolved to:",
rf.font_dir);
m_resolved_extra.push_back(rf);
}
}
pos = end;
}
}
std::string Document_class::strip_typeface_markers(const std::string& text)
{
std::string result {};
size_t pos = 0, start = 0, end = 0;
std::string name {}, scale {};
while (next_typeface_marker(text, pos, start, end, name, scale)) {
result += text.substr(pos, start - pos);
pos = end;
}
result += text.substr(pos);
return result;
}
std::string Document_class::resolve_typeface_markers(const std::string& text)
{
std::string result {};
size_t pos = 0, start = 0, end = 0;
std::string name {}, scale {};
while (next_typeface_marker(text, pos, start, end, name, scale)) {
result += text.substr(pos, start - pos);
auto entry = std::make_pair(name, scale);
auto it = std::find(m_extra_typefaces.begin(), m_extra_typefaces.end(), entry);
if (it != m_extra_typefaces.end())
result += typeface_command(it - m_extra_typefaces.begin());
pos = end;
}
result += text.substr(pos);
return result;
}
std::string Document_class::typeface_preamble()
{
std::stringstream ss {};
for (size_t i = 0; i < m_extra_typefaces.size(); i++) {
const auto& [name, scale] = m_extra_typefaces[i];
const Resolved_font* rf = nullptr;
for (const auto& f : m_resolved_extra)
if (f.family_name == name) rf = &f;
if (!rf) continue;
// \newfontfamily declares a switch command, unlike \setmainfont,
// which changes the document's font permanently.
ss << latex::font_command("newfontfamily" + typeface_command(i),
*rf, stof(scale));
}
return ss.str();
}
/* /*
void output_msg(const std::string& msg) void output_msg(const std::string& msg)
{ {
+25 -4
View File
@@ -121,14 +121,35 @@ public:
strings_t m_font_dirs {}; strings_t m_font_dirs {};
std::string m_serif_font {}; std::string m_serif_typeface {};
std::string m_sans_font {}; std::string m_sans_typeface {};
std::string m_mono_font {}; std::string m_mono_typeface {};
float m_font_scale = 1.0f; float m_text_scale = 1.0f;
Resolved_font m_resolved_serif {}; Resolved_font m_resolved_serif {};
Resolved_font m_resolved_sans {}; Resolved_font m_resolved_sans {};
Resolved_font m_resolved_mono {}; Resolved_font m_resolved_mono {};
// Typefaces requested by @typeface klammers inside the document, in
// first-appearance order, each with the scale it was asked for. A
// klammer cannot declare a typeface itself (see font.py's
// typeface_marker), so it leaves a marker and @document collects them
// here, resolves and localizes each, declares it, and replaces every
// marker with the target's inline switch.
// SYNC: typeface_marker() in sks/font/font.py builds this marker.
std::vector<std::pair<std::string, std::string>> m_extra_typefaces {};
std::vector<Resolved_font> m_resolved_extra {};
// Collect the (typeface, scale) pairs of `text` into
// m_extra_typefaces, resolving each typeface once. Returns nothing;
// the markers are replaced by the per-target functions below.
void collect_typefaces(const std::string& text);
// html: the marker carries no inline switch (the span holds the family
// name), so each is replaced by nothing.
std::string strip_typeface_markers(const std::string& text);
// tex: each distinct (typeface, scale) gets a \newfontfamily command in
// the preamble; the marker is replaced by that command.
std::string resolve_typeface_markers(const std::string& text);
std::string typeface_preamble();
std::string prolog {}; std::string prolog {};
std::string paper_size {}; std::string paper_size {};
bool m_two_column = false; bool m_two_column = false;
+30 -10
View File
@@ -93,24 +93,28 @@ strings_t Document_class::resolved_font_names()
add(m_resolved_serif); add(m_resolved_serif);
add(m_resolved_sans); add(m_resolved_sans);
add(m_resolved_mono); add(m_resolved_mono);
// Typefaces a @typeface klammer asked for: each needs its own <link>
// to the localized css, which is what carries the @font-face.
for (const auto& rf : m_resolved_extra)
add(rf);
return result; return result;
} }
std::string Document_class::font_definitions() std::string Document_class::font_definitions()
{ {
std::stringstream ss {}; std::stringstream ss {};
if (!m_serif_font.empty() || !m_sans_font.empty() || !m_mono_font.empty()) { if (!m_serif_typeface.empty() || !m_sans_typeface.empty() || !m_mono_typeface.empty()) {
// Family names are quoted: an unquoted name with a digit-initial // Family names are quoted: an unquoted name with a digit-initial
// word ("Source Sans 3") is invalid CSS, and a font-family using // word ("Source Sans 3") is invalid CSS, and a font-family using
// var() with such a value computes to inherit, silently losing // var() with such a value computes to inherit, silently losing
// the font. // the font.
ss << ":root {\n"; ss << ":root {\n";
if (!m_serif_font.empty()) if (!m_serif_typeface.empty())
ss << " --serif: \"" << m_serif_font << "\", serif;\n"; ss << " --serif: \"" << m_serif_typeface << "\", serif;\n";
if (!m_sans_font.empty()) if (!m_sans_typeface.empty())
ss << " --sans-serif: \"" << m_sans_font << "\", sans-serif;\n"; ss << " --sans-serif: \"" << m_sans_typeface << "\", sans-serif;\n";
if (!m_mono_font.empty()) if (!m_mono_typeface.empty())
ss << " --monospace: \"" << m_mono_font << "\", monospace;\n"; ss << " --monospace: \"" << m_mono_typeface << "\", monospace;\n";
ss << "}\n"; ss << "}\n";
} }
// Font-size normalization across families is no longer emitted from // Font-size normalization across families is no longer emitted from
@@ -120,11 +124,11 @@ std::string Document_class::font_definitions()
// factors did from build-time OS/2 metrics, but applied to EVERY family // factors did from build-time OS/2 metrics, but applied to EVERY family
// switch instead of the four CSS sites that remembered to multiply. // switch instead of the four CSS sites that remembered to multiply.
// The metric extraction in the font store remains (the tex path and // The metric extraction in the font store remains (the tex path and
// kdesc --font still use it). // kdesc --typeface still use it).
// Global font scale: applied to body font-size // Global font scale: applied to body font-size
if (m_font_scale != 1.0f) { if (m_text_scale != 1.0f) {
char buf[16]; char buf[16];
std::snprintf(buf, sizeof(buf), "%.4f", m_font_scale); std::snprintf(buf, sizeof(buf), "%.4f", m_text_scale);
ss << "body { font-size: " << buf << "rem; }\n"; ss << "body { font-size: " << buf << "rem; }\n";
} }
return ss.str(); return ss.str();
@@ -743,6 +747,22 @@ std::string Document_class::html()
std::string output_directory = create_html_output_directories(); std::string output_directory = create_html_output_directories();
save_string_input_as_file(); save_string_input_as_file();
process_input_files(); process_input_files();
// @typeface markers are collected AFTER processing (a klammer cannot
// declare a typeface itself) but BEFORE the page is assembled, so the
// localized files and the <link> to each typeface's css are in place
// when make_*_html_page() runs. The markers survive into the page
// CACHE deliberately: a cached file must still declare its typefaces
// on a later run, so the stripping happens here, on the assembled
// components, not before write_to_cache().
for (auto& [basename, text] : m_file_components) {
collect_typefaces(text);
}
for (const auto& rf : m_resolved_extra) {
install_resolved_font(rf, output_directory);
}
for (auto& [basename, text] : m_file_components) {
text = strip_typeface_markers(text);
}
auto headings = insert_section_numbers(); auto headings = insert_section_numbers();
insert_section_numbers("___NUM2___", false); insert_section_numbers("___NUM2___", false);
insert_html_caption_numbers(); insert_html_caption_numbers();
+9 -1
View File
@@ -72,6 +72,14 @@ std::string Document_class::tex()
} }
latex_text = join(lines, "\n"); latex_text = join(lines, "\n");
latex_text = string_replace(latex_text, "___AT___", "^@"); latex_text = string_replace(latex_text, "___AT___", "^@");
// @typeface: collect what the document asked for, declare each as a
// \newfontfamily switch command in the preamble, and replace every
// marker with its command. The declarations are appended to the
// prolog, which latex::page emits AFTER \setmainfont -- so fontspec is
// loaded and \newfontfamily is valid there.
collect_typefaces(latex_text);
latex_text = resolve_typeface_markers(latex_text);
prolog += typeface_preamble();
std::string structure_name = std::string structure_name =
m_structure == docstruct_t::book ? "book" : m_structure == docstruct_t::book ? "book" :
m_structure == docstruct_t::article ? "article" : "plain"; m_structure == docstruct_t::article ? "article" : "plain";
@@ -81,5 +89,5 @@ std::string Document_class::tex()
point_size, ragged_right, point_size, ragged_right,
cover, landscape, latex_text, cover, landscape, latex_text,
m_resolved_serif, m_resolved_sans, m_resolved_mono, m_resolved_serif, m_resolved_sans, m_resolved_mono,
m_font_scale); m_text_scale);
} }
+43 -16
View File
@@ -2,28 +2,44 @@
@@@state font_dirs :desc Directories containing local fonts @@@ @@@state font_dirs :desc Directories containing local fonts @@@
@@@argtype font | @@@argtype style_code |
an abbreivation for a font a style code: one letter naming a type style
:pattern r^|i^|b^|s^|t^|c :pattern r^|i^|b^|s^|t^|c
@@@ @@@
@@@argtype font_list | @@@argtype style_code_list |
a list of font symbols. One or more of 'r' (roman), 'i' (italic), a list of style codes. One or more of 'r' (roman), 'i' (italic),
'b' (bold), 's' (sans-serif), 't' (typewriter or monospace) or 'c' 'b' (bold), 's' (sans-serif), 't' (typewriter or monospace) or 'c'
(code) for each cell in a row. If there are fewer font symbols than (code) for each cell in a row. If there are fewer style codes than
cells in a row, the last symbol is repeated. Extra symbols are cells in a row, the last code is repeated. Extra codes are
ignored. ignored.
:pattern ('font'^|\s)+ :pattern ('style_code'^|\s)+
:python_cast (lambda s : s.split()) :python_cast (lambda s : s.split())
:default r :default r
@@@ @@@
@@font.k fontname : Switch to an available font for PDF output. @@ #[
@font was retired 2026-08-25 and replaced by @typeface. It switched the
main font PERMANENTLY (a bare \setmainfont, no group) for tex, and for
html and txt its body was "*fontname*" -- so it printed the NAME of the
font as document text instead of setting anything. @typeface is the
temporary, all-target replacement.
]#
@@font.html :: *fontname* @@ @@typeface.k name | text :scale.float 1.0 :
@@font.tex :: \setmainfont[Ligatures=TeX]{*fontname*} @@ A temporary change of typeface for *text*: the typeface *name* is
# @@font.tex : \setmainfont[Ligatures=TeX,Numbers=Lining]{*fontname*} @@ resolved in the Klammertext font store (the directories of
@@font.txt :: *fontname* @@ KLAMMERTEXT_FONTS, then the distribution's own), and the document
declares and localizes it automatically -- writing this klammer is the
only declaration needed. *scale* multiplies the surrounding text size,
which a script whose glyphs are drawn small usually wants.
The change ends with *text*: nothing after it is affected. Under the
txt target a typeface cannot be expressed at all, so *text* is emitted
unchanged.
@@
@@typeface.html,tex,txt :: @eval font.Typeface(K) @ @@
@@r.k text : Use the current Roman font for *text* @@ @@r.k text : Use the current Roman font for *text* @@
@@r.html :: <span class="plain">*text*</span> @@ @@r.html :: <span class="plain">*text*</span> @@
@@ -74,10 +90,21 @@
\setmainfont{Garamond Libre} \setmainfont{Garamond Libre}
@@ @@
@@cjk.k text : Switch to font that supports CJK for *text* @@ #[
@@cjk.html :: *cjk* @@ Before 2026-08-25 every one of these bodies substituted "*cjk*" while the
@@cjk.tex :: { #- @font Noto Sans CJK SC @ *cjk* #- } @@ declaration named the parameter "text", so @cjk raised "Variable "cjk" not
@@cjk.txt :: *cjk* @@ defined" under EVERY target -- it had never worked. Rewritten on
@typeface, which is what it was reaching for.
The typeface it names is NOT part of the distribution (a usable Noto Sans
CJK is some 16-20 MB per weight, and Chinese line breaking needs more
than a typeface -- xeCJK for the tex path), so this klammer errors with
the list of available typefaces until one is installed with
"kdesc --typeface install". That is the honest behaviour: the klammer
states what it needs and the store reports what it has.
]#
@@cjk.k text : Set *text* in a typeface that supports CJK @@
@@cjk.html,tex,txt :: @typeface Noto Sans CJK SC | *text* @ @@
@@fraktur.k s : Set text *s* in Fraktur @@ @@fraktur.k s : Set text *s* in Fraktur @@
@@fraktur.tex :: \textfrak{*s*} @@ @@fraktur.tex :: \textfrak{*s*} @@
+57
View File
@@ -23,6 +23,63 @@ class Subsuper(klammer_base.Klammer_base):
else: else:
return f"${self.base}^{self.script}$" return f"${self.base}^{self.script}$"
# The typeface-declaration marker. A klammer cannot declare a typeface
# itself: in html the @font-face must be in the page's css and the files
# localized into <output>/fonts/, and in tex the \newfontfamily must be in
# the preamble -- both are @document's, and @document runs AFTER the
# klammers it contains (application is post-order). So @typeface leaves
# this marker where the typeface is needed, and @document collects the
# markers, resolves and localizes each typeface, declares it, and replaces
# the marker with whatever inline switch the target needs (a font-switch
# command for tex, nothing for html, which carries the family name in the
# span's own style). Same shape as the __CAPTION__ markers.
#
# SYNC: typeface_marker_rgx in sks/document/document_class.h -- a C++/Python
# pair, so the constant cannot be single-sourced the way a .k/.py pair can
# (TODO #47); change both or neither.
def typeface_marker(name, scale):
return f"__TYPEFACE__{name}__{scale}__"
class Typeface(klammer_base.Klammer_base):
"""A temporary typeface change (@typeface). See sks/font/font.k."""
def __init__(self, K):
super().__init__(K)
def html(self):
# font-size-adjust is switched OFF for the span: the document
# normalizes families to a common x-height (font.css), which is
# meaningless for a script whose glyphs have no x-height at all --
# hieroglyphs, cuneiform -- and would resize them unpredictably.
# The family name is quoted -- an unquoted digit-initial word
# ("Source Sans 3") is invalid CSS -- with SINGLE quotes, because
# this sits inside a double-quoted html attribute.
style = (f"font-family: '{self.name}', serif; "
f"font-size-adjust: none")
if float(self.scale) != 1.0:
style += f"; font-size: {float(self.scale)}em"
# The STYLE is markup, so its typographically active characters are
# hidden: the html target's transforms turn a single quote into a
# curly one, which broke the css font-family value. The TEXT is
# writer content and is deliberately left alone -- its quotes and
# dashes should be transformed.
return (typeface_marker(self.name, self.scale)
+ f'<span style="{klammer_base.hide_typographic(style)}">'
+ f'{self.text}</span>')
def tex(self):
# The marker becomes the font-switch command; the group is what
# makes the change temporary (the retired @font emitted a bare
# \setmainfont, which changed the font for the rest of the
# document).
return "{" + typeface_marker(self.name, self.scale) + " " + self.text + "}"
def txt(self):
# A typeface cannot be expressed in plain text. Declared in the
# .k description, so this is documented behaviour rather than a
# silent per-target divergence.
return self.text
def html_fontify(text, font_symbol, font_size): def html_fontify(text, font_symbol, font_size):
cls = {"r" : "", cls = {"r" : "",
"i" :"ritalic", "i" :"ritalic",
+2 -2
View File
@@ -102,8 +102,8 @@ class Image(klammer_base.Klammer_base):
self.caption += self.file_error_message self.caption += self.file_error_message
result = html_util.add_caption( result = html_util.add_caption(
result, "Figure", self.number, self.caption, self.caption_font, result, "Figure", self.number, self.caption, self.caption_style,
self.hpos, self.caption_side, False, self.caption_font_size, self.hpos, self.caption_side, False, self.caption_size,
offset=self.offset) offset=self.offset)
result = E("div").body(result).cls("image_margin") result = E("div").body(result).cls("image_margin")
+2 -2
View File
@@ -15,7 +15,7 @@ class Kargs:
def __init__(self, target, K, **kwargs): def __init__(self, target, K, **kwargs):
self.__dict__ = kwargs self.__dict__ = kwargs
self.center = False self.center = False
self.caption_font = "i" self.caption_style = "i"
self.caption_side = "bottom" self.caption_side = "bottom"
self.caption_side_center = False self.caption_side_center = False
self.Image_search_path = K.Image_search_path self.Image_search_path = K.Image_search_path
@@ -40,7 +40,7 @@ class Kargs:
self.border = False self.border = False
self.vmargin = False self.vmargin = False
self.abswidth = 0.0 self.abswidth = 0.0
self.caption_font_size = .9 self.caption_size = .9
def img(K, target, basename, caption, width): def img(K, target, basename, caption, width):
result = image.Image( result = image.Image(
+2 -2
View File
@@ -163,8 +163,8 @@ other structures. The ^:offset is used only if the ^:hpos value is "left" or
:caption :caption
:number.bool true :number.bool true
:caption_side.side :caption_side.side
:caption_font.font i :caption_style.style_code i
:caption_font_size.float .9 :caption_size.float .9
: Arguments that define a caption for a block element. : Arguments that define a caption for a block element.
@@ @@
+1 -1
View File
@@ -9,7 +9,7 @@
@@ol.k list_items.rest :cmp.bool false :initial.int 1 : Ordered list, with numbered list elements. @@ @@ol.k list_items.rest :cmp.bool false :initial.int 1 : Ordered list, with numbered list elements. @@
@@ol.html,tex,txt :: @eval list.List(K, "ol") eval@ @@ @@ol.html,tex,txt :: @eval list.List(K, "ol") eval@ @@
@@define.k descriptions.rest(2) :font i : A list of words or phrases and their definitions or descriptions @@ @@define.k descriptions.rest(2) :style.style_code i : A list of words or phrases and their definitions or descriptions @@
@@define.html,tex :: @eval list.Define(K) eval@ @@ @@define.html,tex :: @eval list.Define(K) eval@ @@
@@columns.k items :n.int 2 : Create two or more columns from the text @@ @@columns.k items :n.int 2 : Create two or more columns from the text @@
+2 -2
View File
@@ -66,7 +66,7 @@ class Define(klammer_base.Klammer_base):
"b" : f"<b>{s}</b>", "b" : f"<b>{s}</b>",
"t" : f'<span class="monospace">{s}</span>', "t" : f'<span class="monospace">{s}</span>',
"c" : f'<span class="monospace">{s}</span>', "c" : f'<span class="monospace">{s}</span>',
"s" : f'<span class="sansserif">{s}</span>'}[self.font] "s" : f'<span class="sansserif">{s}</span>'}[self.style]
result = "\n\n".join([f"<dt>{fontify(term)}</dt>\n<dd>{definition}</dd>" result = "\n\n".join([f"<dt>{fontify(term)}</dt>\n<dd>{definition}</dd>"
for term,definition in self.items]) for term,definition in self.items])
result = f"<dl>\n{result}\n</dl>" result = f"<dl>\n{result}\n</dl>"
@@ -79,7 +79,7 @@ class Define(klammer_base.Klammer_base):
'b' : r'\normalfont\bfseries', 'b' : r'\normalfont\bfseries',
't' : r'\normalfont\ttfamily', 't' : r'\normalfont\ttfamily',
'c' : r'\normalfont\ttfamily', 'c' : r'\normalfont\ttfamily',
's' : r'\normalfont\sffamily'}[self.font] 's' : r'\normalfont\sffamily'}[self.style]
return f"{font} {s}" return f"{font} {s}"
# The "\hfill\newline" was to force the description to the next line even if the item was small: # The "\hfill\newline" was to force the description to the next line even if the item was small:
# result = "\n\n".join([f"\\item[{{{fontify(term)}}}]\\hfill\n\\newline {definition}" # result = "\n\n".join([f"\\item[{{{fontify(term)}}}]\\hfill\n\\newline {definition}"
+2 -2
View File
@@ -206,8 +206,8 @@
:grid.bool false :grid.bool false
:cell_hpos.cell_hpos :cell_hpos.cell_hpos
:justify.table_justify :justify.table_justify
:header_font.font i :header_style.style_code i
:font.font_list :style.style_code_list
:colspan.table_span :colspan.table_span
:rowspan.table_span :rowspan.table_span
:calc.table_calc :calc.table_calc
+14 -14
View File
@@ -84,7 +84,7 @@ class Table(klammer_base.Klammer_base):
self.apply_formats() self.apply_formats()
self.cell_hpos = extend(parse_hpos(self.K_target, self.cell_hpos), self.row_size) self.cell_hpos = extend(parse_hpos(self.K_target, self.cell_hpos), self.row_size)
#self.cell_hpos = self.cell_hpos.split(";") #self.cell_hpos = self.cell_hpos.split(";")
self.font = extend(self.font, self.row_size) self.style = extend(self.style, self.row_size)
# In html, a 'fit' column mixed with sized columns must be clamped # In html, a 'fit' column mixed with sized columns must be clamped
# to its widest entry (tex's \widthof semantics) or it absorbs the # to its widest entry (tex's \widthof semantics) or it absorbs the
# window width: its cells get nowrap (class Wfit; see table.css). # window width: its cells get nowrap (class Wfit; see table.css).
@@ -445,7 +445,7 @@ class Table(klammer_base.Klammer_base):
self.column_widths = [] self.column_widths = []
for col_i in range(len(self.cells[0])): for col_i in range(len(self.cells[0])):
longest = "" longest = ""
longest_font = "r" longest_style = "r"
for row_i in range(len(self.cells)): for row_i in range(len(self.cells)):
cell = self.cells[row_i][col_i] cell = self.cells[row_i][col_i]
# A colspan anchor's text spans several columns and must # A colspan anchor's text spans several columns and must
@@ -456,9 +456,9 @@ class Table(klammer_base.Klammer_base):
longest_in_line = lines[-1] longest_in_line = lines[-1]
if len(longest_in_line) > len(longest): if len(longest_in_line) > len(longest):
longest = longest_in_line longest = longest_in_line
longest_font = cell.font longest_style = cell.font
if longest_font != "r": if longest_style != "r":
longest = font.tex_fontify(longest, longest_font, 1.0) longest = font.tex_fontify(longest, longest_style, 1.0)
self.column_widths.append(longest) self.column_widths.append(longest)
# ---- :hpos ------------------------------------------------------------- # ---- :hpos -------------------------------------------------------------
@@ -500,9 +500,9 @@ class Table(klammer_base.Klammer_base):
for cell_i, cell in enumerate(row): for cell_i, cell in enumerate(row):
rspan = self.span_count(self.s_rowspan, cell_i, row_i) rspan = self.span_count(self.s_rowspan, cell_i, row_i)
cspan = self.span_count(self.s_colspan, row_i, cell_i) cspan = self.span_count(self.s_colspan, row_i, cell_i)
font = self.font[cell_i] font = self.style[cell_i]
if row_i == 0 and self.header: if row_i == 0 and self.header:
font = self.header_font font = self.header_style
# A span anchor's right and bottom borders come from the # A span anchor's right and bottom borders come from the
# boundary at the END of the merged region. # boundary at the END of the merged region.
right_i = cell_i + max(cspan, 1) right_i = cell_i + max(cspan, 1)
@@ -589,9 +589,9 @@ class Table(klammer_base.Klammer_base):
if self.number or self.caption: if self.number or self.caption:
result = html_util.add_caption( result = html_util.add_caption(
result, "Table", self.number, self.caption, result, "Table", self.number, self.caption,
self.caption_font, hpos=self.hpos, self.caption_style, hpos=self.hpos,
side=self.caption_side, side=self.caption_side,
font_size=self.caption_font_size, max_width="100%", font_size=self.caption_size, max_width="100%",
offset=self.offset) offset=self.offset)
else: else:
result.attr("style", "max-width: 100%") result.attr("style", "max-width: 100%")
@@ -606,9 +606,9 @@ class Table(klammer_base.Klammer_base):
if table_width: if table_width:
result.attr("style", f"{layout}width: 100%") result.attr("style", f"{layout}width: 100%")
result = html_util.add_caption( result = html_util.add_caption(
result, "Table", self.number, self.caption, self.caption_font, result, "Table", self.number, self.caption, self.caption_style,
hpos=self.hpos, side=self.caption_side, hpos=self.hpos, side=self.caption_side,
font_size=self.caption_font_size, width=table_width, font_size=self.caption_size, width=table_width,
offset=self.offset) offset=self.offset)
else: else:
# An uncaptioned table still gets the position container, so # An uncaptioned table still gets the position container, so
@@ -757,7 +757,7 @@ class Table(klammer_base.Klammer_base):
def make_caption(self): def make_caption(self):
column_count = str(len(self.rows[0])) column_count = str(len(self.rows[0]))
caption = latex_util.make_caption_text( caption = latex_util.make_caption_text(
self.number, "Table", self.caption, self.caption_font, self.caption_font_size) self.number, "Table", self.caption, self.caption_style, self.caption_size)
result = "\\multicolumn{" + column_count + "}{c}{\\parbox{\\tableboxwidth}{" result = "\\multicolumn{" + column_count + "}{c}{\\parbox{\\tableboxwidth}{"
result += "\\vspace*{8pt}\\centering\\small\\em " result += "\\vspace*{8pt}\\centering\\small\\em "
result += caption result += caption
@@ -836,8 +836,8 @@ class Table(klammer_base.Klammer_base):
result = latex_util.add_caption( result = latex_util.add_caption(
result, "Table", self.number, self.caption, "\\tableboxwidth", result, "Table", self.number, self.caption, "\\tableboxwidth",
hpos=self.hpos, side=self.caption_side, hpos=self.hpos, side=self.caption_side,
font_symbol=self.caption_font, font_symbol=self.caption_style,
font_size=self.caption_font_size, offset=self.offset) font_size=self.caption_size, offset=self.offset)
else: else:
result = latex_util.caption_wrapper(result, self.hpos, offset=self.offset) result = latex_util.caption_wrapper(result, self.hpos, offset=self.offset)
result = measure + result result = measure + result
+18 -18
View File
@@ -163,10 +163,10 @@ namespace latex {
const std::string& cover, const std::string& cover,
bool landscape, bool landscape,
const std::string& body, const std::string& body,
Resolved_font serif_font, Resolved_font serif_typeface,
Resolved_font sans_font, Resolved_font sans_typeface,
Resolved_font mono_font, Resolved_font mono_typeface,
float font_scale) float text_scale)
{ {
(void)K::log(3); (void)K::log(3);
bool book_format = (structure == "book"); bool book_format = (structure == "book");
@@ -202,10 +202,10 @@ namespace latex {
// x-height: serif_xh / other_xh (matches lowercase) // x-height: serif_xh / other_xh (matches lowercase)
// cap-height: serif_ch / other_ch (matches capitals) // cap-height: serif_ch / other_ch (matches capitals)
// average: mean(serif_xh,serif_ch) / mean(other_xh,other_ch) // average: mean(serif_xh,serif_ch) / mean(other_xh,other_ch)
float serif_xh = serif_font.xheight_ratio; float serif_xh = serif_typeface.xheight_ratio;
float serif_ch = serif_font.capheight_ratio; float serif_ch = serif_typeface.capheight_ratio;
float serif_avg = (serif_xh + serif_ch) / 2.0f; float serif_avg = (serif_xh + serif_ch) / 2.0f;
auto font_scale_fn = [&](const Resolved_font& other) -> float { auto text_scale_fn = [&](const Resolved_font& other) -> float {
float other_avg = (other.xheight_ratio + other.capheight_ratio) / 2.0f; float other_avg = (other.xheight_ratio + other.capheight_ratio) / 2.0f;
if (serif_avg > 0.0f && other_avg > 0.0f) if (serif_avg > 0.0f && other_avg > 0.0f)
// return serif_xh / other.xheight_ratio; // x-height // return serif_xh / other.xheight_ratio; // x-height
@@ -213,21 +213,21 @@ namespace latex {
return serif_avg / other_avg; // average return serif_avg / other_avg; // average
return 0.0f; return 0.0f;
}; };
// Global font_scale multiplies all font sizes uniformly // Global text_scale multiplies all font sizes uniformly
float gs = (font_scale != 1.0f) ? font_scale : 0.0f; float gs = (text_scale != 1.0f) ? text_scale : 0.0f;
float sans_s = font_scale_fn(sans_font); float sans_s = text_scale_fn(sans_typeface);
float mono_s = font_scale_fn(mono_font); float mono_s = text_scale_fn(mono_typeface);
// Apply global scale: multiply into per-font scale, or use alone // Apply global scale: multiply into per-font scale, or use alone
if (gs > 0.0f) { if (gs > 0.0f) {
sans_s = (sans_s > 0.0f) ? sans_s * font_scale : font_scale; sans_s = (sans_s > 0.0f) ? sans_s * text_scale : text_scale;
mono_s = (mono_s > 0.0f) ? mono_s * font_scale : font_scale; mono_s = (mono_s > 0.0f) ? mono_s * text_scale : text_scale;
} }
ss << font_command("setmainfont", serif_font, gs); ss << font_command("setmainfont", serif_typeface, gs);
ss << font_command("setsansfont", sans_font, sans_s); ss << font_command("setsansfont", sans_typeface, sans_s);
ss << font_command("setmonofont", mono_font, mono_s); ss << font_command("setmonofont", mono_typeface, mono_s);
// Scale leading proportionally with font_scale // Scale leading proportionally with text_scale
float effective_leading = leading * ((font_scale != 1.0f) ? font_scale : 1.0f); float effective_leading = leading * ((text_scale != 1.0f) ? text_scale : 1.0f);
ss << "\n\\setstretch{" << effective_leading << "}\n"; ss << "\n\\setstretch{" << effective_leading << "}\n";
if (ragged_right) if (ragged_right)
ss << "\\raggedright\n"; ss << "\\raggedright\n";
+12 -4
View File
@@ -6,6 +6,14 @@
namespace latex { namespace latex {
// A fontspec declaration: "\<command>[Path=...,Scale=...]{<regular>}".
// The command is "setmainfont"/"setsansfont"/"setmonofont" for the
// document's three role typefaces, and "newfontfamily\ktface<n>" for a
// typeface a @typeface klammer asked for (a switch command rather than a
// permanent change).
std::string font_command(const std::string& command, const Resolved_font& font,
float scale);
std::string page(const std::string& structure, std::string page(const std::string& structure,
const std::string& title, const std::string& title,
const std::string& subtitle, const std::string& subtitle,
@@ -23,10 +31,10 @@ std::string page(const std::string& structure,
const std::string& cover, const std::string& cover,
bool landscape, bool landscape,
const std::string& body, const std::string& body,
Resolved_font serif_font = {}, Resolved_font serif_typeface = {},
Resolved_font sans_font = {}, Resolved_font sans_typeface = {},
Resolved_font mono_font = {}, Resolved_font mono_typeface = {},
float font_scale = 1.0f); float text_scale = 1.0f);
} }
void check_for_xelatex(); void check_for_xelatex();
+59 -7
View File
@@ -2,8 +2,12 @@
# #
# character_test.sh — Regression tests for the ^-character forms the engine # character_test.sh — Regression tests for the ^-character forms the engine
# resolves at read time: the ^UUUU^ Unicode code point (4-6 hex digits, # resolves at read time: the ^UUUU^ Unicode code point (4-6 hex digits,
# since 2026-08-23 covering the full range to U+10FFFF), and the character # since 2026-08-23 covering the full range to U+10FFFF), the
# tables (mnemonics, diacritics) it must not disturb. # ^UUUU UUUU ...^ SEQUENCE form (added 2026-08-25: one caret pair around
# whitespace-delimited tokens, assembled at katom level by
# process_unicode_sequences — so, unlike the string-level single form, it
# is skipped inside @eval/@read/@cond spans and for literal content), and
# the character tables (mnemonics, diacritics) it must not disturb.
# #
# SKS-independent: character handling is katomizer/engine machinery # SKS-independent: character handling is katomizer/engine machinery
# (mac/ktype.h nonascii katom, mac/character.cpp), so everything runs with # (mac/ktype.h nonascii katom, mac/character.cpp), so everything runs with
@@ -39,6 +43,26 @@ reset=$'\033[0m'
trim() { awk '{ sub(/[ \t\r]+$/, "") } { line[NR]=$0 } END { f=1; while (f<=NR && line[f]=="") f++; l=NR; while (l>=1 && line[l]=="") l--; for (i=f;i<=l;i++) print line[i] }'; } trim() { awk '{ sub(/[ \t\r]+$/, "") } { line[NR]=$0 } END { f=1; while (f<=NR && line[f]=="") f++; l=NR; while (l>=1 && line[l]=="") l--; for (i=f;i<=l;i++) print line[i] }'; }
# check_err NAME PHRASE INPUT — the render must FAIL (nonzero exit) with
# PHRASE on stderr. Matched wrap-insensitively: error messages are
# justified to 80 columns and may wrap anywhere.
check_err() {
local name="$1" phrase="$2" input="$3"
local err status
err=$("$KTEXT" --klammersets none -s "$input" -d 2>&1 >/dev/null); status=$?
err=$(printf '%s' "$err" | tr '\n' ' ' | tr -s ' ')
if [ $status -eq 0 ]; then
echo "${red}FAIL${reset} $name — expected an error, got exit 0"
FAIL=$((FAIL+1)); return
fi
case "$err" in
*"$phrase"*) echo "${green}PASS${reset} $name"; PASS=$((PASS+1));;
*) echo "${red}FAIL${reset} $name"
echo " wanted phrase: [$phrase]"
echo " stderr: $(echo "$err" | head -c 200)"; FAIL=$((FAIL+1));;
esac
}
# check NAME EXPECTED INPUT — render INPUT (no klammer set, default # check NAME EXPECTED INPUT — render INPUT (no klammer set, default
# target); exit 0 and stdout == EXPECTED. # target); exit 0 and stdout == EXPECTED.
check() { check() {
@@ -66,14 +90,42 @@ check " 1. 4-digit BMP code point" $'A ☺ B' 'A ^263A^ B'
check " 2. 5-digit code point, whole" $'A \xf0\x93\x80\x80 B' 'A ^13000^ B' check " 2. 5-digit code point, whole" $'A \xf0\x93\x80\x80 B' 'A ^13000^ B'
check " 3. 5-digit emoji" $'\xf0\x9f\x98\x80' '^1F600^' check " 3. 5-digit emoji" $'\xf0\x9f\x98\x80' '^1F600^'
check " 4. 6-digit top of the range" $'\xf4\x8f\xbf\xbd' '^10FFFD^' check " 4. 6-digit top of the range" $'\xf4\x8f\xbf\xbd' '^10FFFD^'
check " 5. above U+10FFFF is invalid" 'Invalid Unicode: 16777215' '^FFFFFF^' # An invalid code point is a located ERROR (2026-08-25, the
check " 6. surrogate is invalid" 'Invalid Unicode: 55296' '^D800^' # no-warn-and-continue policy) -- it was in-band "Invalid Unicode" text in
# the document before, which is the wrong channel for a generator's typo.
check_err " 5. above U+10FFFF is an error" 'beyond U+10FFFF' '^FFFFFF^'
check_err " 6. surrogate is an error" 'a surrogate' '^D800^'
check_err " 6a. noncharacter is an error" 'a noncharacter' '^FDD0^'
check " 7. mnemonic undisturbed" 'ß' '^s^' check " 7. mnemonic undisturbed" 'ß' '^s^'
# The diacritic tables compose base + COMBINING mark (a U+0308), not the # The tables emit the PRECOMPOSED (NFC) character where one exists (since
# precomposed letter -- the expected string is built the same way. # 2026-08-25; they formerly emitted base + combining mark) -- the same
check " 8. diacritic undisturbed" $'Ma\xcc\x88dchen' 'M^a"dchen' # bytes an editor's direct entry produces, so ^a" and a typed ä compare
# equal in search and copy-paste. The literal ä below is precomposed.
check " 8. diacritic is the precomposed character" 'Mädchen' 'M^a"dchen'
check " 9. code point beside quoted punctuation" $'☺ --' '^263A^ ^-^-' check " 9. code point beside quoted punctuation" $'☺ --' '^263A^ ^-^-'
echo
echo "-- sequences: ^UUUU UUUU ...^ (one caret pair, tokens 4-6 hex digits,"
echo " no space at the elbows, one line; process_unicode_sequences)"
check "10. CJK sequence" $'X \xe9\x81\x93\xe5\x8f\xaf\xe9\x81\x93\xe9\x9d\x9e\xe5\xb8\xb8\xe9\x81\x93 Y' 'X ^9053 53EF 9053 975E 5E38 9053^ Y'
check "11. mixed widths" $'\xe2\x98\xba\xf0\x93\x80\x80\xf0\x9f\x98\x80' '^263A 13000 1F600^'
check "12. space after opening caret: not a sequence" 'A ^ 263A 263A^ B' 'A ^ 263A 263A^ B'
check "13. space before closing caret: not a sequence" 'A ^263A 263A ^ B' 'A ^263A 263A ^ B'
check "14. newline aborts (one line only)" $'A ^263A\n263A^ B' $'A ^263A\n263A^ B'
check "15. short token aborts whole sequence" 'A ^263A 263^ B' 'A ^263A 263^ B'
check "16. @eval code keeps its carets" '11' '@eval len("^263A 263A^") @'
check "17. literal span: sequence stays verbatim" 'x ^263A 263A^ y' "^'x ^263A 263A^ y'^"
check "17a. single-character token stands for itself" $'\xe9\x81\x93x\xe5\x8f\xaf' '^9053 x 53EF^'
check "17b. non-alnum single token behaves as quoted" '☺-☺' '^263A - 263A^'
check "17c. structural code points are re-read-safe" '@@' '^0040 0040^'
check_err "17d. invalid code point in a CLOSED sequence" 'in a ^...^ sequence' '^263A FFFFFF^'
check "17e. unclosed hex-like prose is untouched" 'about ^263A FFFFFF prose' 'about ^263A FFFFFF prose'
echo
echo "-- direct entry and NFC convergence"
check "18. pinyin tone is precomposed" 'ā' '^a1'
check "19. typed UTF-8 passes through byte-identical" 'Mädchen naïveté ü — done' 'Mädchen naïveté ü — done'
echo echo
echo "=================================================================" echo "================================================================="
echo "${bold}Results: $PASS passed, $FAIL failed${reset}" echo "${bold}Results: $PASS passed, $FAIL failed${reset}"
+12
View File
@@ -83,6 +83,18 @@ shows " 1. -k lists klammers" '@table' "$KDESC" -k
shows " 2. -t lists targets" 'html' "$KDESC" -t shows " 2. -t lists targets" 'html' "$KDESC" -t
shows " 3. -c lists character codes" '.' "$KDESC" -c shows " 3. -c lists character codes" '.' "$KDESC" -c
shows " 4. -i reads the named input" '@x' "$KDESC" -i "$DIR/split.k" -k shows " 4. -i reads the named input" '@x' "$KDESC" -i "$DIR/split.k" -k
# -u describes how to write a Unicode character. Asserted by OUTCOME, on
# the two things the section must carry however its prose is rewritten:
# the ^...^ notation it teaches, and a rendered character beside its code
# point -- the whole point of the section is that a reader sees the glyph
# they are about to write. It does NOT pin the prose, which is meant to
# grow (as of 2026-08-25 the section predates the sequence form and the
# typeface klammer, and does not yet mention either).
shows " 4a. -u describes the Unicode syntax" '\^0418\^' "$KDESC" -u
# The rendered glyph, as UTF-8 BYTE escapes: macOS ships bash 3.2, which
# expands $'\xHH' but not $'\U...' (that difference failed this suite's
# sibling on Olion, 2026-08-23).
shows " 4b. ... and shows the characters" $'\xf0\x93\x8a\xaa' "$KDESC" -u
echo echo
echo "-- the multi-letter topics --" echo "-- the multi-letter topics --"
+3 -1
View File
@@ -281,7 +281,9 @@ shows "35. --ws applies whitespace operators" 'ab' --ws 'a#- b'
# DIAERESIS, not the precomposed U+00FC. The diacritic FOLLOWS the base # DIAERESIS, not the precomposed U+00FC. The diacritic FOLLOWS the base
# letter -- that is the design of the ^ codes -- so the two spellings look # letter -- that is the design of the ^ codes -- so the two spellings look
# identical in an editor and only one of them matches. # identical in an editor and only one of them matches.
shows "36. --nonascii decodes to a combining sequence" 'ü' --nonascii '^u"' # Precomposed since 2026-08-25: the tables emit NFC, the same bytes as
# direct entry (formerly base + combining mark).
shows "36. --nonascii decodes to the precomposed character" 'ü' --nonascii '^u"'
shows "37. --literal marks a literal span" 'x' --literal "^'x'^" shows "37. --literal marks a literal span" 'x' --literal "^'x'^"
# Evaluating needs a target: Eval::eval re-reads its result under K_target, and # Evaluating needs a target: Eval::eval re-reads its result under K_target, and
# kdiag has no target argument, so with the variable unset EVERY @eval died # kdiag has no target argument, so with the variable unset EVERY @eval died