Fix everything, make stylesheets variations, make deploy script

This commit is contained in:
octospacc 2022-09-02 13:12:29 +02:00
parent 5e1087707c
commit 5c82a4e4c8
8 changed files with 565 additions and 41 deletions

31
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Build and Deploy
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
jobs:
page_build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Build
run: |
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.10 wget tar p7zip
python3.10 ./Build.py
./Tools/DeployPages.sh
- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: public

7
.gitignore vendored
View File

@ -1,3 +1,6 @@
*.pyc
twemoji.tar.gz
twemoji/
twemoji-astonishing.css
twemoji-astonishing.min.css
twemoji-astonishing/
Build/
public/

16
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,16 @@
image: alpine:latest
before_script: |
apk update
apk add python3 wget tar p7zip
pages:
stage: deploy
script: |
python3.10 ./Build.py
./Tools/DeployPages.sh
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH

View File

@ -1,11 +1,12 @@
#!/usr/bin/env python3
import argparse
import os
import unicodedata
from time import ctime
from urllib.request import urlopen
from Tools import rcssmin
Name = "twemoji-astonishing"
EmojiVer = "15.0"
cssmin = rcssmin._make_cssmin(python_only=True)
# https://stackoverflow.com/a/518232
def StripAccents(s):
@ -17,6 +18,17 @@ def ReplaceList(Text, Match, Replace):
Text = Text.replace(m, Replace)
return Text
def GetEmojiData(EmojiVer):
Response = urlopen(f"https://unicode.org/Public/emoji/{EmojiVer}/emoji-test.txt")
return Response.read().decode("utf-8")
def ParseEmojiData(Data):
Emojis = []
for Line in Data.splitlines():
if CheckEmojiLine(Line):
Emojis += [GetEmojiMeta(Line)]
return Emojis
def CheckEmojiLine(Line):
if Line.startswith("#"):
return False
@ -41,40 +53,61 @@ def GetEmojiMeta(Line):
return {"Code":Code, "Char":Char, "Name":Name}
def MinifyCSS(CSS):
return ReplaceList(CSS, ["\n","\t"," "], "")
def Main():
#print("[I] Cloning Twemoji repo")
#os.system("git clone --depth 1 https://github.com/twitter/twemoji")
print(f"[I] Getting v{EmojiVer} emoji data")
Response = urlopen(f"https://unicode.org/Public/emoji/{EmojiVer}/emoji-test.txt")
print("[I] Parsing emoji data")
Emojis = []
Data = Response.read().decode("utf-8")
for Line in Data.splitlines():
if CheckEmojiLine(Line):
Emojis += [GetEmojiMeta(Line)]
EmojiCount = str(len(Emojis))
print(f"[I] Writing CSS")
CSS = ""
def WriteCSS(Emojis, URLPrefix):
with open("Preamble.css", "r") as f:
CSS += f.read() + "\n"
Preamble = f.read() + "\n"
with open("Comment.css", "r") as f:
Comment = f.read().replace("{BuildTime}", ctime()).replace("{EmojiCount}", EmojiCount)
for Emoji in Emojis:
CSS += f"""\
.twa-{Emoji["Name"]}, .twa-{Emoji["Char"]} {{
background-image: url("{Emoji["Code"]}.svg");
Comment = f.read().format(BuildTime=ctime(), EmojiCount=str(len(Emojis)))
try:
os.mkdir("Build")
except FileExistsError:
pass
for Type in ["Chars Names", "Chars", "Names"]:
CSS = Preamble
Line = ""
if "Chars" in Type:
Line = ".twa-{EmojiChar} " + Line
if "Names" in Type:
Line = ".twa-{EmojiName} " + Line
if Type == "Chars Names":
Line = Line.replace(" .twa-", ", .twa-")
for Emoji in Emojis:
NewLine = Line.format(EmojiChar=Emoji["Char"], EmojiName=Emoji["Name"])
CSS += f"""\
{NewLine}{{
background-image: url("{URLPrefix}{Emoji["Code"]}.svg");
}}
"""
with open(f"{Name}.css", "w") as f:
f.write(CSS.replace("/*{CommentBlock}*/", Comment))
with open(f"{Name}.min.css", "w") as f:
f.write(MinifyCSS(CSS).replace("/*{CommentBlock}*/", "\n"+Comment))
FileName = "twemoji-astonishing"
if Type == "Chars":
FileName += ".chars"
elif Type == "Names":
FileName += ".names"
with open(f"Build/{FileName}.css", "w") as f:
f.write(CSS.replace("{CommentBlock}", Comment))
with open(f"Build/{FileName}.min.css", "w") as f:
f.write(cssmin(CSS).replace("{CommentBlock}", "\n"+Comment))
def Main(Args):
EmojiVer = Args.EmojiVer if Args.EmojiVer else "15.0"
URLPrefix = Args.URLPrefix if Args.URLPrefix else ""
print(f"[I] Getting v{EmojiVer} emoji data")
Data = GetEmojiData(EmojiVer)
print("[I] Parsing emoji data")
Emojis = ParseEmojiData(Data)
print(f"[I] Writing CSS")
WriteCSS(Emojis, URLPrefix)
if __name__ == "__main__":
Main()
Parser = argparse.ArgumentParser()
Parser.add_argument('--EmojiVer', type=str)
Parser.add_argument('--URLPrefix', type=str)
Main(Parser.parse_args())

4
Comment.min.css vendored
View File

@ -1,4 +0,0 @@
/*
License: MIT and CC BY 4.0.
Details and Sources: https://gitlab.com/octtspacc/twemoji-astonishing.
*/

View File

@ -1,5 +1,5 @@
@charset "UTF-8";
/*{CommentBlock}*/
{CommentBlock}
.twa {
display: inline-block;
@ -13,7 +13,7 @@
}
/* Prevent image and fallback text emoji overlap */
.twa span {
.twa > span {
font-size: 0px;
}

25
Tools/DeployPages.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
echo "[I] Downloading Twemoji files"
wget -O ./twemoji.tar.gz https://github.com/twitter/twemoji/archive/refs/heads/master.tar.gz
echo "[I] Extracting archive"
tar xvf ./twemoji.tar.gz
mv ./twemoji-master ./twemoji
echo "[I] Preparing Pages build"
rm -rf ./public
mkdir -p ./public
cp ./Build/* ./public/
cp ./twemoji/assets/svg/* ./Build/
echo "[I] Making archives"
mv ./Build ./twemoji-astonishing
cd ./public
7z a -mx9 -mmt$(nproc --all) twemoji-astonishing.zip ../twemoji-astonishing
7z a -mx9 -mmt$(nproc --all) twemoji-astonishing.7z ../twemoji-astonishing
tar cvJf twemoji-astonishing.tar.xz ../twemoji-astonishing
cd ..
echo "[I] Cleaning up"
rm -rf ./Build ./twemoji ./twemoji-astonishing ./twemoji.tar.gz

420
Tools/rcssmin.py Normal file
View File

@ -0,0 +1,420 @@
#!/usr/bin/env python
# -*- coding: ascii -*-
u"""
==============
CSS Minifier
==============
CSS Minifier.
The minifier is based on the semantics of the `YUI compressor`_\\, which
itself is based on `the rule list by Isaac Schlueter`_\\.
:Copyright:
Copyright 2011 - 2021
Andr\xe9 Malo or his licensors, as applicable
:License:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This module is a re-implementation aiming for speed instead of maximum
compression, so it can be used at runtime (rather than during a preprocessing
step). RCSSmin does syntactical compression only (removing spaces, comments
and possibly semicolons). It does not provide semantic compression (like
removing empty blocks, collapsing redundant properties etc). It does, however,
support various CSS hacks (by keeping them working as intended).
Here's a feature list:
- Strings are kept, except that escaped newlines are stripped
- Space/Comments before the very end or before various characters are
stripped: ``:{});=>],!`` (The colon (``:``) is a special case, a single
space is kept if it's outside a ruleset.)
- Space/Comments at the very beginning or after various characters are
stripped: ``{}(=:>[,!``
- Optional space after unicode escapes is kept, resp. replaced by a simple
space
- whitespaces inside ``url()`` definitions are stripped, except if it's a
quoted non-base64 data url
- Comments starting with an exclamation mark (``!``) can be kept optionally.
- All other comments and/or whitespace characters are replaced by a single
space.
- Multiple consecutive semicolons are reduced to one
- The last semicolon within a ruleset is stripped
- CSS Hacks supported:
- IE7 hack (``>/**/``)
- Mac-IE5 hack (``/*\\*/.../**/``)
- The boxmodelhack is supported naturally because it relies on valid CSS2
strings
- Between ``:first-line`` and the following comma or curly brace a space is
inserted. (apparently it's needed for IE6)
- Same for ``:first-letter``
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
factor 100 or so (depending on the input). docs/BENCHMARKS in the source
distribution contains the details.
Supported python versions are 2.7 and 3.4+.
.. _YUI compressor: https://github.com/yui/yuicompressor/
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/
"""
__author__ = u"Andr\xe9 Malo"
__license__ = "Apache License, Version 2.0"
__version__ = '1.1.0'
__all__ = ['cssmin']
import re as _re
def _make_cssmin(python_only=False):
"""
Generate CSS minifier.
Parameters:
python_only (bool):
Use only the python variant. If true, the c extension is not even
tried to be loaded.
Returns:
callable: Minifier
"""
# pylint: disable = too-many-locals
if not python_only:
try:
import _rcssmin
except ImportError:
pass
else:
# Ensure that the C version is in sync
if getattr(_rcssmin, '__version__', None) == __version__:
return _rcssmin.cssmin
nl = r'(?:[\n\f]|\r\n?)' # pylint: disable = invalid-name
spacechar = r'[\r\n\f\040\t]'
unicoded = r'[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?'
escaped = r'[^\n\r\f0-9a-fA-F]'
escape = r'(?:\\(?:%(unicoded)s|%(escaped)s))' % locals()
nmchar = r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]'
# nmstart = r'[^\000-\100\133-\136\140\173-\177]'
# ident = (r'(?:'
# r'-?(?:%(nmstart)s|%(escape)s)%(nmchar)s*(?:%(escape)s%(nmchar)s*)*'
# r')') % locals()
comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
# only for specific purposes. The bang is grouped:
_bang_comment = r'(?:/\*(!?)[^*]*\*+(?:[^/*][^*]*\*+)*/)'
string1 = \
r'(?:\047[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)'
string2 = r'(?:"[^"\\\r\n\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")'
strings = r'(?:%s|%s)' % (string1, string2)
nl_string1 = \
r'(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^\047\\\r\n\f]*)*\047)'
nl_string2 = r'(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^"\\\r\n\f]*)*")'
nl_strings = r'(?:%s|%s)' % (nl_string1, nl_string2)
uri_nl_string1 = r'(?:\047[^\047\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)'
uri_nl_string2 = r'(?:"[^"\\]*(?:\\(?:[^\r]|\r\n?)[^"\\]*)*")'
uri_nl_strings = r'(?:%s|%s)' % (uri_nl_string1, uri_nl_string2)
nl_escaped = r'(?:\\%(nl)s)' % locals()
space = r'(?:%(spacechar)s|%(comment)s)' % locals()
ie7hack = r'(?:>/\*\*/)'
uri = (
# noqa pylint: disable = bad-option-value, bad-continuation
r'(?:'
r'(?:[^\000-\040"\047()\\\177]*'
r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*)'
r'(?:'
r'(?:%(spacechar)s+|%(nl_escaped)s+)'
r'(?:'
r'(?:[^\000-\040"\047()\\\177]|%(escape)s|%(nl_escaped)s)'
r'[^\000-\040"\047()\\\177]*'
r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*'
r')+'
r')*'
r')'
) % locals()
nl_unesc_sub = _re.compile(nl_escaped).sub
uri_data_plain = _re.compile((
r'[\047"][dD][aA][tT][aA]:([^\000-\040\\"\047,]*),'
)).match
uri_space_sub = _re.compile((
r'(%(escape)s+)|%(spacechar)s+|%(nl_escaped)s+'
) % locals()).sub
uri_space_subber = lambda m: m.groups()[0] or ''
space_sub_simple = _re.compile((
r'[\r\n\f\040\t;]+|(%(comment)s+)'
) % locals()).sub
space_sub_banged = _re.compile((
r'[\r\n\f\040\t;]+|(%(_bang_comment)s+)'
) % locals()).sub
post_esc_sub = _re.compile(r'[\r\n\f\t]+').sub
main_sub = _re.compile((
# noqa pylint: disable = bad-option-value, bad-continuation
r'([^\\"\047u>@\r\n\f\040\t/;:{}+]+)' # 1
r'|(?<=[{}(=:>[,!])(%(space)s+)' # 2
r'|^(%(space)s+)' # 3
r'|(%(space)s+)(?=(([:{});=>\],!])|$)?)' # 4, 5, 6
r'|;(%(space)s*(?:;%(space)s*)*)(?=(\})?)' # 7, 8
r'|(\{)' # 9
r'|(\})' # 10
r'|(%(strings)s)' # 11
r'|(?<!%(nmchar)s)url\(%(spacechar)s*(' # 12
r'%(uri_nl_strings)s'
r'|%(uri)s'
r')%(spacechar)s*\)'
r'|(@(?:' # 13
r'[mM][eE][dD][iI][aA]'
r'|[sS][uU][pP][pP][oO][rR][tT][sS]'
r'|[dD][oO][cC][uU][mM][eE][nN][tT]'
r'|(?:-(?:'
r'[wW][eE][bB][kK][iI][tT]|[mM][oO][zZ]|[oO]|[mM][sS]'
r')-)?'
r'[kK][eE][yY][fF][rR][aA][mM][eE][sS]'
r'))(?!%(nmchar)s)'
r'|(%(ie7hack)s)(%(space)s*)' # 14, 15
r'|(:[fF][iI][rR][sS][tT]-[lL]' # 16
r'(?:[iI][nN][eE]|[eE][tT][tT][eE][rR]))'
r'(%(space)s*)(?=[{,])' # 17
r'|(%(nl_strings)s)' # 18
r'|(%(escape)s[^\\"\047u>@\r\n\f\040\t/;:{}+]*)' # 19
) % locals()).sub
# print(main_sub.__self__.pattern)
def main_subber(keep_bang_comments):
""" Make main subber """
in_macie5, in_rule, at_group = [0], [0], [0]
if keep_bang_comments:
space_sub = space_sub_banged
def space_subber(match):
""" Space|Comment subber """
if match.lastindex:
group1, group2 = match.group(1, 2)
if group2:
if group1.endswith(r'\*/'):
in_macie5[0] = 1
else:
in_macie5[0] = 0
return group1
if group1.endswith(r'\*/'):
if in_macie5[0]:
return ''
in_macie5[0] = 1
return r'/*\*/'
elif in_macie5[0]:
in_macie5[0] = 0
return '/**/'
return ''
else:
space_sub = space_sub_simple
def space_subber(match):
""" Space|Comment subber """
if match.lastindex:
if match.group(1).endswith(r'\*/'):
if in_macie5[0]:
return ''
in_macie5[0] = 1
return r'/*\*/'
elif in_macie5[0]:
in_macie5[0] = 0
return '/**/'
return ''
def fn_space_post(group):
""" space with token after """
if group(5) is None or (
group(6) == ':' and not in_rule[0] and not at_group[0]):
return ' ' + space_sub(space_subber, group(4))
return space_sub(space_subber, group(4))
def fn_semicolon(group):
""" ; handler """
return ';' + space_sub(space_subber, group(7))
def fn_semicolon2(group):
""" ; handler """
if in_rule[0]:
return space_sub(space_subber, group(7))
return ';' + space_sub(space_subber, group(7))
def fn_open(_):
""" { handler """
if at_group[0]:
at_group[0] -= 1
else:
in_rule[0] = 1
return '{'
def fn_close(_):
""" } handler """
in_rule[0] = 0
return '}'
def fn_url(group):
""" url() handler """
uri = group(12)
data = uri_data_plain(uri)
if not data or data.group(1).lower().endswith(';base64'):
uri = uri_space_sub(uri_space_subber, uri)
return 'url(%s)' % (uri,)
def fn_at_group(group):
""" @xxx group handler """
at_group[0] += 1
return group(13)
def fn_ie7hack(group):
""" IE7 Hack handler """
if not in_rule[0] and not at_group[0]:
in_macie5[0] = 0
return group(14) + space_sub(space_subber, group(15))
return '>' + space_sub(space_subber, group(15))
table = (
# noqa pylint: disable = bad-option-value, bad-continuation
None,
None,
None,
None,
fn_space_post, # space with token after
fn_space_post, # space with token after
fn_space_post, # space with token after
fn_semicolon, # semicolon
fn_semicolon2, # semicolon
fn_open, # {
fn_close, # }
lambda g: g(11), # string
fn_url, # url(...)
fn_at_group, # @xxx expecting {...}
None,
fn_ie7hack, # ie7hack
None,
lambda g: g(16) + ' ' + space_sub(space_subber, g(17)),
# :first-line|letter followed
# by [{,] (apparently space
# needed for IE6)
lambda g: nl_unesc_sub('', g(18)), # nl_string
lambda g: post_esc_sub(' ', g(19)), # escape
)
def func(match):
""" Main subber """
idx, group = match.lastindex, match.group
if idx > 3:
return table[idx](group)
# shortcuts for frequent operations below:
elif idx == 1: # not interesting
return group(1)
# else: # space with token before or at the beginning
return space_sub(space_subber, group(idx))
return func
def cssmin(style, keep_bang_comments=False):
"""
Minify CSS.
Parameters:
style (str):
CSS to minify
keep_bang_comments (bool):
Keep comments starting with an exclamation mark? (``/*!...*/``)
Returns:
str: Minified style
"""
# pylint: disable = redefined-outer-name
is_bytes, style = _as_str(style)
style = main_sub(main_subber(keep_bang_comments), style)
if is_bytes:
style = style.encode('latin-1')
if is_bytes == 2:
style = bytearray(style)
return style
return cssmin
cssmin = _make_cssmin()
def _as_str(script):
""" Make sure the style is a text string """
is_bytes = False
if str is bytes:
if not isinstance(script, basestring): # noqa pylint: disable = undefined-variable
raise TypeError("Unexpected type")
elif isinstance(script, bytes):
is_bytes = True
script = script.decode('latin-1')
elif isinstance(script, bytearray):
is_bytes = 2
script = script.decode('latin-1')
elif not isinstance(script, str):
raise TypeError("Unexpected type")
return is_bytes, script
if __name__ == '__main__':
def main():
""" Main """
import sys as _sys
keep_bang_comments = (
'-b' in _sys.argv[1:]
or '-bp' in _sys.argv[1:]
or '-pb' in _sys.argv[1:]
)
if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \
or '-pb' in _sys.argv[1:]:
xcssmin = _make_cssmin(python_only=True)
else:
xcssmin = cssmin
_sys.stdout.write(xcssmin(
_sys.stdin.read(), keep_bang_comments=keep_bang_comments
))
main()