diff --git a/.gitattributes b/.gitattributes index 638e943..5fc750e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,3 +17,6 @@ gradlew text eol=lf *.png binary *.webp binary *.ico binary +# Bundled type families (res/font). `text=auto` already detects these as binary, +# but a font is too easy to corrupt silently to leave to a heuristic. +*.ttf binary diff --git a/app/licenses/EBGaramond-OFL.txt b/app/licenses/EBGaramond-OFL.txt new file mode 100644 index 0000000..c1ec5e1 --- /dev/null +++ b/app/licenses/EBGaramond-OFL.txt @@ -0,0 +1,93 @@ +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 + + +----------------------------------------------------------- +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. diff --git a/app/licenses/IMFellEnglish-OFL.txt b/app/licenses/IMFellEnglish-OFL.txt new file mode 100644 index 0000000..1bf2ab0 --- /dev/null +++ b/app/licenses/IMFellEnglish-OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010, Igino Marini (mail@iginomarini.com) + +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 + + +----------------------------------------------------------- +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. diff --git a/app/licenses/Inter-OFL.txt b/app/licenses/Inter-OFL.txt new file mode 100644 index 0000000..21f6aff --- /dev/null +++ b/app/licenses/Inter-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +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. diff --git a/app/licenses/Merriweather-OFL.txt b/app/licenses/Merriweather-OFL.txt new file mode 100644 index 0000000..17d2c51 --- /dev/null +++ b/app/licenses/Merriweather-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Merriweather Project Authors (https://github.com/EbenSorkin/Merriweather4) with Reserved Font Name "Merriweather". + +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 + + +----------------------------------------------------------- +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. diff --git a/app/licenses/PlayfairDisplay-OFL.txt b/app/licenses/PlayfairDisplay-OFL.txt new file mode 100644 index 0000000..1315389 --- /dev/null +++ b/app/licenses/PlayfairDisplay-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2017 The Playfair Display Project Authors (https://github.com/clauseggers/Playfair-Display), with Reserved Font Name "Playfair Display" + +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 + + +----------------------------------------------------------- +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. diff --git a/app/licenses/SourceSans3-OFL.txt b/app/licenses/SourceSans3-OFL.txt new file mode 100644 index 0000000..80b9a8b --- /dev/null +++ b/app/licenses/SourceSans3-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010-2020 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +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 + + +----------------------------------------------------------- +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. diff --git a/app/licenses/WorkSans-OFL.txt b/app/licenses/WorkSans-OFL.txt new file mode 100644 index 0000000..d3a046c --- /dev/null +++ b/app/licenses/WorkSans-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans) + +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 + + +----------------------------------------------------------- +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. diff --git a/app/src/main/java/com/runicgateway/app/ui/theme/Font.kt b/app/src/main/java/com/runicgateway/app/ui/theme/Font.kt index f3f783d..74f854d 100644 --- a/app/src/main/java/com/runicgateway/app/ui/theme/Font.kt +++ b/app/src/main/java/com/runicgateway/app/ui/theme/Font.kt @@ -6,35 +6,117 @@ package com.runicgateway.app.ui.theme import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontVariation import androidx.compose.ui.text.font.FontWeight import com.runicgateway.app.R /** - * Type families for the M5 shard-website design pass (docs/android/PLAN.md §M5). + * Every type family the app can draw with — the eight the admin's Appearance page + * can select between (THEMING_AND_NAV.md §5.3) plus the two system stacks its + * "shipped default" options resolve to. * - * - [Cinzel] — the engraved serif display face used for headings, screen titles, - * and the top-bar title. Shipped as a single weight-axis **variable** font - * (`res/font/cinzel_variable.ttf`, SIL OFL — see `app/licenses/Cinzel-OFL.txt`); - * the 500/600/700 instances the design uses are pinned via [FontVariation] - * (supported on API 26+, and our minSdk is 29). - * - [AppSerif] — the parchment body face. Android's platform serif is Noto Serif, - * which reads as the design's Georgia body copy without bundling another binary. - * - [AppSans] — the label/meta/button face (the design's "Helvetica Neue" runs). + * All eight webfonts are **bundled**, not downloadable: the Play Store font + * provider is the only downloadable-font source Compose ships with, so a + * de-Googled device would silently fall back and every text style would gain an + * async loading state. The binaries are taken verbatim from `google/fonts`, which + * is how [Cinzel] arrived in M5; each carries its SIL OFL licence under + * `app/licenses/`, **never** under `res/font/` (aapt rejects a `.txt` there). + * + * A family reaches a text style through [ShardTypeface], which is what maps a + * shard's `--display` / `--serif` / `--sans` stacks onto these. The three + * declared *shipped* roles are [Cinzel] for the engraved display/headline/title + * block, [AppSerif] for parchment body copy, and [AppSans] for the letter-spaced + * label/meta/button block. + * + * ## Weights + * + * The type scale asks for four: 400 (body), 500 and 700 (labels), 600 (display). + * Every family here must supply all four, because a family is **not** confined to + * the role its dropdown lives in — the `modern` preset puts Work Sans in the + * display slot and the `fantasy` preset puts EB Garamond in the sans slot, both + * bypassing the server's per-role option list (see [ShardTypeface]). The variable + * families pin the four instances through [FontVariation] (API 26+; minSdk is 29). + * + * Two families are exceptions, both upstream facts rather than choices: + * - **[IMFellEnglish] has a single weight.** Its one 400 face answers all four + * requests and Android synthesises the bold. The website's dropdown labels it + * "(no bold weight)" for the same reason. + * - **[Cinzel] is left at the 500/600/700 it shipped with in M5.** It is the only + * family the server offers in the display role alone, so nothing can ask it for + * 400; adding an instance would have edited M5's type for no reachable case. + * + * ## Italics + * + * Four families carry a true italic — the same four `client/index.html` requests + * one for. The rest are upright-only and Compose skews them, which is what the + * app already did for every family before this milestone and what the website + * does for its own upright-only faces. The app draws italic in two places. */ @OptIn(ExperimentalTextApi::class) -private fun cinzel(weight: FontWeight) = +private fun variable(resId: Int, weight: FontWeight, style: FontStyle = FontStyle.Normal) = Font( - R.font.cinzel_variable, + resId, weight = weight, + style = style, variationSettings = FontVariation.Settings(FontVariation.weight(weight.weight)), ) +// The four weights the type scale asks for, in the order Compose prefers to match. +private val ScaleWeights = listOf( + FontWeight.Normal, // 400 — body + FontWeight.Medium, // 500 — labelMedium / labelSmall + FontWeight.SemiBold, // 600 — display / headline / title + FontWeight.Bold, // 700 — labelLarge +) + +/** A variable family pinned at the four scale weights, upright only. */ +private fun variableFamily(resId: Int) = + FontFamily(ScaleWeights.map { variable(resId, it) }) + +/** A variable family pinned at the four scale weights, upright and italic. */ +private fun variableFamily(uprightResId: Int, italicResId: Int) = + FontFamily( + ScaleWeights.map { variable(uprightResId, it) } + + ScaleWeights.map { variable(italicResId, it, FontStyle.Italic) }, + ) + +private fun cinzel(weight: FontWeight) = variable(R.font.cinzel_variable, weight) + val Cinzel = FontFamily( cinzel(FontWeight.Medium), // 500 cinzel(FontWeight.SemiBold), // 600 cinzel(FontWeight.Bold), // 700 ) +val EBGaramond = variableFamily(R.font.eb_garamond_variable, R.font.eb_garamond_italic) +val Merriweather = variableFamily(R.font.merriweather_variable, R.font.merriweather_italic) +val PlayfairDisplay = + variableFamily(R.font.playfair_display_variable, R.font.playfair_display_italic) +val Inter = variableFamily(R.font.inter_variable) +val WorkSans = variableFamily(R.font.work_sans_variable) +val SourceSans3 = variableFamily(R.font.source_sans_3_variable) + +/** + * IM Fell English, whose upstream release is a single 400 face per style — there + * is no weight axis and no bold cut to pin. Declared once per style so a request + * at 500/600/700 lands on it rather than falling out of the family. + */ +val IMFellEnglish = FontFamily( + Font(R.font.im_fell_english_regular, weight = FontWeight.Normal), + Font(R.font.im_fell_english_italic, weight = FontWeight.Normal, style = FontStyle.Italic), +) + +/** + * The platform serif (Noto Serif), which is what the website's + * `Georgia, "Times New Roman", serif` stack resolves to on Android — and the + * app's shipped body face since M5. + */ val AppSerif = FontFamily.Serif + +/** + * The platform sans (Roboto), which the website's + * `"Helvetica Neue", Arial, sans-serif` stack resolves to on Android — and the + * app's shipped label face since M5. + */ val AppSans = FontFamily.SansSerif diff --git a/app/src/main/java/com/runicgateway/app/ui/theme/ShardTypeface.kt b/app/src/main/java/com/runicgateway/app/ui/theme/ShardTypeface.kt new file mode 100644 index 0000000..ad3535e --- /dev/null +++ b/app/src/main/java/com/runicgateway/app/ui/theme/ShardTypeface.kt @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.runicgateway.app.ui.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.text.font.FontFamily + +/** + * The shard's resolved type families — the `fonts` third of the admin's + * Appearance page (THEMING_AND_NAV.md §5.3), alongside [ShardPalette] and + * [ShardStructure]. + * + * The three roles map onto the M5 type scale's three groups verbatim: `--display` + * carries the Cinzel display/headline/title block, `--serif` the body block, and + * `--sans` the label/meta/button block. Sizes, weights and tracking do not move — + * only the family, which is why [shardTypography] is a one-field substitution and + * an unthemed shard is a provable no-op (§2, AC-1). + * + * Resolution is pure, so the acceptance tests need no Compose rule. + */ +@Immutable +data class ShardTypeface( + /** `--display`. */ + val display: FontFamily = Cinzel, + /** `--serif`. */ + val serif: FontFamily = AppSerif, + /** `--sans`. */ + val sans: FontFamily = AppSans, +) { + companion object { + /** The shipped app: the three M5 families. */ + val Shipped = ShardTypeface() + + /** + * Resolve a `theme` token map into three families, **field by field** (§2): + * an unreadable `--sans` must not cost the `--serif` beside it, exactly as + * in [ShardPalette.resolve] and [ShardStructure.resolve]. + */ + fun resolve(theme: Map): ShardTypeface { + if (theme.isEmpty()) return Shipped + return ShardTypeface( + display = family(theme["--display"]) ?: Shipped.display, + serif = family(theme["--serif"]) ?: Shipped.serif, + sans = family(theme["--sans"]) ?: Shipped.sans, + ) + } + } +} + +/** + * Every family name the server can publish, keyed by the lowercased first family + * of the stack. + * + * **This map is deliberately global rather than per-role**, and that is not a + * simplification. The server validates an *admin-entered* font against + * `FONT_OPTIONS[role]`, but a preset's tokens are copied verbatim by + * `resolveThemeTokens` and never pass through that list — `modern` publishes + * `--display: 'Work Sans', Arial, sans-serif`, which the display dropdown does + * not offer, and `fantasy` publishes `--sans: 'EB Garamond', Georgia, serif`, + * which the sans dropdown does not either. A per-role lookup would have missed + * the display face of one preset and the label face of the other. It is the same + * trap phase 2 hit with `--shadow-card`, in a different token group. + * + * The two system entries are the "shipped default" options: neither pulls in a + * webfont on the web, and on Android both resolve to the platform family the app + * has drawn with since M5. + */ +private val FamiliesByFirstName: Map = mapOf( + "cinzel" to Cinzel, + "eb garamond" to EBGaramond, + "merriweather" to Merriweather, + "playfair display" to PlayfairDisplay, + "im fell english" to IMFellEnglish, + "inter" to Inter, + "work sans" to WorkSans, + "source sans 3" to SourceSans3, + "georgia" to AppSerif, + "helvetica neue" to AppSans, +) + +/** + * Resolve a CSS font stack to a family by **its first name**, which is how the + * value is constructed server-side and the only part of it that carries the + * admin's choice — everything after the first comma is the web's fallback chain, + * which Android has no use for. + * + * Returns `null` for a stack this app cannot draw, so the caller falls back to + * the role's shipped family rather than to some other role's. + */ +private fun family(stack: String?): FontFamily? { + val first = stack?.substringBefore(',')?.trim()?.trim('\'', '"')?.trim() + if (first.isNullOrEmpty()) return null + return FamiliesByFirstName[first.lowercase()] +} diff --git a/app/src/main/java/com/runicgateway/app/ui/theme/Theme.kt b/app/src/main/java/com/runicgateway/app/ui/theme/Theme.kt index 8a9d726..35568bd 100644 --- a/app/src/main/java/com/runicgateway/app/ui/theme/Theme.kt +++ b/app/src/main/java/com/runicgateway/app/ui/theme/Theme.kt @@ -60,7 +60,10 @@ internal fun shardColorScheme(palette: ShardPalette): ColorScheme = darkColorSch * for the ten tokens with a Material role, and through [LocalShardPalette] for * the five without one. The radii split the same way — [MaterialTheme]'s shape * scale for everything Material draws, [LocalShardStructure] for the pill and - * the card depth, which it cannot carry. + * the card depth, which it cannot carry. The type families need no split and so + * no composition local: every text style in the app comes from + * [MaterialTheme.typography], and the two that override anything override the + * style rather than the family. */ @Composable fun RunicGatewayTheme( @@ -75,6 +78,8 @@ fun RunicGatewayTheme( } val colorScheme = remember(palette) { shardColorScheme(palette) } val structure = remember(appearance) { ShardStructure.resolve(appearance.theme) } + val typeface = remember(appearance) { ShardTypeface.resolve(appearance.theme) } + val typography = remember(typeface) { shardTypography(typeface) } CompositionLocalProvider( LocalShardPalette provides palette, @@ -82,7 +87,7 @@ fun RunicGatewayTheme( ) { MaterialTheme( colorScheme = colorScheme, - typography = Typography, + typography = typography, shapes = structure.shapes, content = content, ) diff --git a/app/src/main/java/com/runicgateway/app/ui/theme/Type.kt b/app/src/main/java/com/runicgateway/app/ui/theme/Type.kt index 8df3e97..8a49e5c 100644 --- a/app/src/main/java/com/runicgateway/app/ui/theme/Type.kt +++ b/app/src/main/java/com/runicgateway/app/ui/theme/Type.kt @@ -9,73 +9,82 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp /** - * The M5 type scale. Three families carry the design (see [ui/theme/Font.kt]): - * [Cinzel] for the engraved display/headline/title roles, [AppSerif] (Noto Serif) - * for parchment body copy, and [AppSans] for the letter-spaced label/meta/button - * roles. Sizes and tracking mirror the "Runic Gateway Screens" mockup. + * The M5 type scale, drawn in whichever three families the shard resolved to + * (THEMING_AND_NAV.md §5.3). + * + * Only the family moves. Every size, weight, line height and tracking below is + * the M5 value from the "Runic Gateway Screens" mockup, so `ShardTypeface.Shipped` + * — an unthemed instance, a backend that predates the feature, a settings call + * that failed — reproduces the pre-M12 scale exactly. [androidx.compose.material3.Typography] + * implements `equals`, so AC-1 asserts that in one comparison. + * + * The three groups map onto the three roles verbatim: [ShardTypeface.display] + * carries the engraved display/headline/title block, [ShardTypeface.serif] the + * parchment body copy, and [ShardTypeface.sans] the letter-spaced + * label/meta/button roles. */ -val Typography = Typography( - // Display / headline / title — Cinzel engraved serif +internal fun shardTypography(faces: ShardTypeface) = Typography( + // Display / headline / title — the engraved serif role displayLarge = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 40.sp, lineHeight = 46.sp, letterSpacing = 0.4.sp, ), displayMedium = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 32.sp, lineHeight = 40.sp, letterSpacing = 0.3.sp, ), displaySmall = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 28.sp, lineHeight = 36.sp, letterSpacing = 0.2.sp, ), headlineLarge = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 26.sp, lineHeight = 34.sp, letterSpacing = 0.2.sp, ), headlineMedium = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 24.sp, lineHeight = 32.sp, letterSpacing = 0.2.sp, ), headlineSmall = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = 0.2.sp, ), titleLarge = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 20.sp, lineHeight = 26.sp, letterSpacing = 0.2.sp, ), titleMedium = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 17.sp, lineHeight = 24.sp, letterSpacing = 0.15.sp, ), titleSmall = TextStyle( - fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontFamily = faces.display, fontWeight = FontWeight.SemiBold, fontSize = 15.sp, lineHeight = 22.sp, letterSpacing = 0.1.sp, ), - // Body — parchment serif + // Body — the parchment serif role bodyLarge = TextStyle( - fontFamily = AppSerif, fontWeight = FontWeight.Normal, + fontFamily = faces.serif, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 26.sp, letterSpacing = 0.15.sp, ), bodyMedium = TextStyle( - fontFamily = AppSerif, fontWeight = FontWeight.Normal, + fontFamily = faces.serif, fontWeight = FontWeight.Normal, fontSize = 15.sp, lineHeight = 24.sp, letterSpacing = 0.15.sp, ), bodySmall = TextStyle( - fontFamily = AppSerif, fontWeight = FontWeight.Normal, + fontFamily = faces.serif, fontWeight = FontWeight.Normal, fontSize = 13.sp, lineHeight = 20.sp, letterSpacing = 0.2.sp, ), - // Labels / meta / buttons — sans, letter-spaced + // Labels / meta / buttons — the sans role, letter-spaced labelLarge = TextStyle( - fontFamily = AppSans, fontWeight = FontWeight.Bold, + fontFamily = faces.sans, fontWeight = FontWeight.Bold, fontSize = 14.sp, lineHeight = 18.sp, letterSpacing = 0.45.sp, ), labelMedium = TextStyle( - fontFamily = AppSans, fontWeight = FontWeight.Medium, + fontFamily = faces.sans, fontWeight = FontWeight.Medium, fontSize = 12.sp, lineHeight = 16.sp, letterSpacing = 0.4.sp, ), labelSmall = TextStyle( - fontFamily = AppSans, fontWeight = FontWeight.Medium, + fontFamily = faces.sans, fontWeight = FontWeight.Medium, fontSize = 11.sp, lineHeight = 15.sp, letterSpacing = 0.5.sp, ), ) diff --git a/app/src/main/res/font/eb_garamond_italic.ttf b/app/src/main/res/font/eb_garamond_italic.ttf new file mode 100644 index 0000000..f9ad996 Binary files /dev/null and b/app/src/main/res/font/eb_garamond_italic.ttf differ diff --git a/app/src/main/res/font/eb_garamond_variable.ttf b/app/src/main/res/font/eb_garamond_variable.ttf new file mode 100644 index 0000000..52d34b8 Binary files /dev/null and b/app/src/main/res/font/eb_garamond_variable.ttf differ diff --git a/app/src/main/res/font/im_fell_english_italic.ttf b/app/src/main/res/font/im_fell_english_italic.ttf new file mode 100644 index 0000000..d9afa7e Binary files /dev/null and b/app/src/main/res/font/im_fell_english_italic.ttf differ diff --git a/app/src/main/res/font/im_fell_english_regular.ttf b/app/src/main/res/font/im_fell_english_regular.ttf new file mode 100644 index 0000000..275d754 Binary files /dev/null and b/app/src/main/res/font/im_fell_english_regular.ttf differ diff --git a/app/src/main/res/font/inter_variable.ttf b/app/src/main/res/font/inter_variable.ttf new file mode 100644 index 0000000..047c92f Binary files /dev/null and b/app/src/main/res/font/inter_variable.ttf differ diff --git a/app/src/main/res/font/merriweather_italic.ttf b/app/src/main/res/font/merriweather_italic.ttf new file mode 100644 index 0000000..f161207 Binary files /dev/null and b/app/src/main/res/font/merriweather_italic.ttf differ diff --git a/app/src/main/res/font/merriweather_variable.ttf b/app/src/main/res/font/merriweather_variable.ttf new file mode 100644 index 0000000..cb775b8 Binary files /dev/null and b/app/src/main/res/font/merriweather_variable.ttf differ diff --git a/app/src/main/res/font/playfair_display_italic.ttf b/app/src/main/res/font/playfair_display_italic.ttf new file mode 100644 index 0000000..94fae1f Binary files /dev/null and b/app/src/main/res/font/playfair_display_italic.ttf differ diff --git a/app/src/main/res/font/playfair_display_variable.ttf b/app/src/main/res/font/playfair_display_variable.ttf new file mode 100644 index 0000000..7a09eb7 Binary files /dev/null and b/app/src/main/res/font/playfair_display_variable.ttf differ diff --git a/app/src/main/res/font/source_sans_3_variable.ttf b/app/src/main/res/font/source_sans_3_variable.ttf new file mode 100644 index 0000000..d259aa4 Binary files /dev/null and b/app/src/main/res/font/source_sans_3_variable.ttf differ diff --git a/app/src/main/res/font/work_sans_variable.ttf b/app/src/main/res/font/work_sans_variable.ttf new file mode 100644 index 0000000..588dc07 Binary files /dev/null and b/app/src/main/res/font/work_sans_variable.ttf differ diff --git a/app/src/test/java/com/runicgateway/app/ui/theme/ShardTypefaceTest.kt b/app/src/test/java/com/runicgateway/app/ui/theme/ShardTypefaceTest.kt new file mode 100644 index 0000000..fdfa57a --- /dev/null +++ b/app/src/test/java/com/runicgateway/app/ui/theme/ShardTypefaceTest.kt @@ -0,0 +1,279 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.runicgateway.app.ui.theme + +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.sp +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertSame +import org.junit.Test +import androidx.compose.material3.Typography as MaterialTypography + +/** + * [ShardTypeface.resolve] and [shardTypography] — the font third of the theme + * (§5.3). + * + * Like the shape scale and unlike the color scheme, material3's [MaterialTypography] + * implements `equals` (checked in the 1.3.0 bytecode), so the no-op proof is one + * comparison against a **verbatim copy of the pre-M12 scale** kept in this file. + * Copying it rather than referencing `shardTypography(Shipped)` is the point: the + * assertion is against what the app used to draw, so a stray edit to a size or a + * letter-spacing in `Type.kt` fails here rather than quietly redefining "shipped". + */ +class ShardTypefaceTest { + + // ── the stacks, exactly as the server publishes them ────────────────────── + + private val runicGateway = mapOf( + "--serif" to "Georgia, \"Times New Roman\", serif", + "--display" to "Cinzel, Georgia, serif", + "--sans" to "\"Helvetica Neue\", Arial, sans-serif", + ) + + private val fantasy = mapOf( + "--serif" to "'EB Garamond', Georgia, serif", + "--display" to "Cinzel, Georgia, serif", + "--sans" to "'EB Garamond', Georgia, serif", + ) + + private val modern = mapOf( + "--serif" to "Inter, Arial, sans-serif", + "--display" to "'Work Sans', Arial, sans-serif", + "--sans" to "Inter, Arial, sans-serif", + ) + + // ── the no-op proof (AC-1) ──────────────────────────────────────────────── + + @Test + fun `no theme resolves to the shipped families`() { + assertEquals(ShardTypeface.Shipped, ShardTypeface.resolve(emptyMap())) + assertSame(Cinzel, ShardTypeface.Shipped.display) + assertSame(FontFamily.Serif, ShardTypeface.Shipped.serif) + assertSame(FontFamily.SansSerif, ShardTypeface.Shipped.sans) + } + + @Test + fun `the shipped scale is the pre-M12 scale`() { + assertEquals(preM12Typography, shardTypography(ShardTypeface.Shipped)) + } + + @Test + fun `the runic-gateway preset is a no-op`() { + assertEquals(ShardTypeface.Shipped, ShardTypeface.resolve(runicGateway)) + assertEquals(preM12Typography, shardTypography(ShardTypeface.resolve(runicGateway))) + } + + // ── the presets that bypass the per-role dropdown ───────────────────────── + + @Test + fun `the fantasy preset puts EB Garamond in the serif and sans roles`() { + val faces = ShardTypeface.resolve(fantasy) + assertSame(Cinzel, faces.display) + assertSame(EBGaramond, faces.serif) + // Not an option the sans dropdown offers — a preset's tokens are copied + // verbatim and never pass through it. + assertSame(EBGaramond, faces.sans) + } + + @Test + fun `the modern preset puts Work Sans in the display role and Inter in the serif role`() { + val faces = ShardTypeface.resolve(modern) + // Neither of these is in its role's FONT_OPTIONS list. + assertSame(WorkSans, faces.display) + assertSame(Inter, faces.serif) + assertSame(Inter, faces.sans) + } + + // ── every option on the shortlist ───────────────────────────────────────── + + @Test + fun `every serif option resolves`() { + assertSame(EBGaramond, serifFor("'EB Garamond', Georgia, serif")) + assertSame(Merriweather, serifFor("Merriweather, Georgia, serif")) + assertSame(PlayfairDisplay, serifFor("'Playfair Display', Georgia, serif")) + assertSame(IMFellEnglish, serifFor("'IM Fell English', Georgia, serif")) + assertSame(FontFamily.Serif, serifFor("Georgia, \"Times New Roman\", serif")) + } + + @Test + fun `every display option resolves`() { + assertSame(Cinzel, displayFor("Cinzel, Georgia, serif")) + assertSame(PlayfairDisplay, displayFor("'Playfair Display', Georgia, serif")) + assertSame(EBGaramond, displayFor("'EB Garamond', Georgia, serif")) + assertSame(IMFellEnglish, displayFor("'IM Fell English', Georgia, serif")) + } + + @Test + fun `every sans option resolves`() { + assertSame(Inter, sansFor("Inter, Arial, sans-serif")) + assertSame(WorkSans, sansFor("'Work Sans', Arial, sans-serif")) + assertSame(SourceSans3, sansFor("'Source Sans 3', Arial, sans-serif")) + assertSame(FontFamily.SansSerif, sansFor("\"Helvetica Neue\", Arial, sans-serif")) + } + + // ── parsing: only the first name carries the choice ─────────────────────── + + @Test + fun `the fallback chain after the first comma is ignored`() { + // Same family however the web fallbacks are written, and a serif stack in + // the sans role still resolves to what it names. + assertSame(Merriweather, sansFor("Merriweather, Georgia, serif")) + assertSame(Merriweather, sansFor("Merriweather")) + assertSame(Merriweather, sansFor("Merriweather , whatever , serif")) + } + + @Test + fun `quoting and casing do not matter`() { + assertSame(WorkSans, sansFor("'Work Sans', Arial, sans-serif")) + assertSame(WorkSans, sansFor("\"Work Sans\", Arial, sans-serif")) + assertSame(WorkSans, sansFor("Work Sans, Arial, sans-serif")) + assertSame(WorkSans, sansFor(" 'WORK SANS' , Arial ")) + } + + // ── per-field fallback (AC-2) ───────────────────────────────────────────── + + @Test + fun `an unreadable role falls back to its own shipped family, not another role's`() { + val faces = ShardTypeface.resolve( + mapOf( + "--display" to "'Playfair Display', Georgia, serif", + "--serif" to "Comic Sans MS, cursive", + "--sans" to "", + ), + ) + assertSame(PlayfairDisplay, faces.display) + assertSame(ShardTypeface.Shipped.serif, faces.serif) + assertSame(ShardTypeface.Shipped.sans, faces.sans) + } + + @Test + fun `a theme with no font tokens keeps all three shipped families`() { + // A non-empty theme that touches only colors must not disturb the type. + assertEquals( + ShardTypeface.Shipped, + ShardTypeface.resolve(mapOf("--accent" to "#c9a227")), + ) + } + + @Test + fun `a blank or comma-only stack falls back`() { + assertSame(ShardTypeface.Shipped.sans, sansFor(" ")) + assertSame(ShardTypeface.Shipped.sans, sansFor(",")) + assertSame(ShardTypeface.Shipped.sans, sansFor("'', Arial, sans-serif")) + } + + // ── the scale itself only ever changes family ───────────────────────────── + + @Test + fun `a themed scale differs from the shipped one only in its families`() { + val themed = shardTypography(ShardTypeface.resolve(fantasy)) + assertNotEquals(preM12Typography, themed) + assertEquals( + preM12Typography.bodyLarge, + themed.bodyLarge.copy(fontFamily = ShardTypeface.Shipped.serif), + ) + assertEquals( + preM12Typography.labelLarge, + themed.labelLarge.copy(fontFamily = ShardTypeface.Shipped.sans), + ) + assertEquals( + preM12Typography.displayLarge, + themed.displayLarge.copy(fontFamily = ShardTypeface.Shipped.display), + ) + } + + @Test + fun `each role reaches the styles it owns`() { + val themed = shardTypography(ShardTypeface.resolve(modern)) + // display/headline/title + assertSame(WorkSans, themed.displaySmall.fontFamily) + assertSame(WorkSans, themed.headlineMedium.fontFamily) + assertSame(WorkSans, themed.titleSmall.fontFamily) + // body + assertSame(Inter, themed.bodyLarge.fontFamily) + assertSame(Inter, themed.bodySmall.fontFamily) + // labels + assertSame(Inter, themed.labelLarge.fontFamily) + assertSame(Inter, themed.labelSmall.fontFamily) + } + + private fun serifFor(stack: String) = + ShardTypeface.resolve(mapOf("--serif" to stack)).serif + + private fun displayFor(stack: String) = + ShardTypeface.resolve(mapOf("--display" to stack)).display + + private fun sansFor(stack: String) = + ShardTypeface.resolve(mapOf("--sans" to stack)).sans + + /** + * The type scale exactly as `ui/theme/Type.kt` declared it before M12, with the + * three families it named directly. + */ + private val preM12Typography = MaterialTypography( + displayLarge = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 40.sp, lineHeight = 46.sp, letterSpacing = 0.4.sp, + ), + displayMedium = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 32.sp, lineHeight = 40.sp, letterSpacing = 0.3.sp, + ), + displaySmall = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 28.sp, lineHeight = 36.sp, letterSpacing = 0.2.sp, + ), + headlineLarge = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 26.sp, lineHeight = 34.sp, letterSpacing = 0.2.sp, + ), + headlineMedium = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 24.sp, lineHeight = 32.sp, letterSpacing = 0.2.sp, + ), + headlineSmall = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = 0.2.sp, + ), + titleLarge = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 20.sp, lineHeight = 26.sp, letterSpacing = 0.2.sp, + ), + titleMedium = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 17.sp, lineHeight = 24.sp, letterSpacing = 0.15.sp, + ), + titleSmall = TextStyle( + fontFamily = Cinzel, fontWeight = FontWeight.SemiBold, + fontSize = 15.sp, lineHeight = 22.sp, letterSpacing = 0.1.sp, + ), + bodyLarge = TextStyle( + fontFamily = FontFamily.Serif, fontWeight = FontWeight.Normal, + fontSize = 16.sp, lineHeight = 26.sp, letterSpacing = 0.15.sp, + ), + bodyMedium = TextStyle( + fontFamily = FontFamily.Serif, fontWeight = FontWeight.Normal, + fontSize = 15.sp, lineHeight = 24.sp, letterSpacing = 0.15.sp, + ), + bodySmall = TextStyle( + fontFamily = FontFamily.Serif, fontWeight = FontWeight.Normal, + fontSize = 13.sp, lineHeight = 20.sp, letterSpacing = 0.2.sp, + ), + labelLarge = TextStyle( + fontFamily = FontFamily.SansSerif, fontWeight = FontWeight.Bold, + fontSize = 14.sp, lineHeight = 18.sp, letterSpacing = 0.45.sp, + ), + labelMedium = TextStyle( + fontFamily = FontFamily.SansSerif, fontWeight = FontWeight.Medium, + fontSize = 12.sp, lineHeight = 16.sp, letterSpacing = 0.4.sp, + ), + labelSmall = TextStyle( + fontFamily = FontFamily.SansSerif, fontWeight = FontWeight.Medium, + fontSize = 11.sp, lineHeight = 15.sp, letterSpacing = 0.5.sp, + ), + ) +}