SVGHMI: Implemented "Add Font" and "Remove Font", add font embedding in CSS at build time, tested ok with some OTF for now.
--- a/svghmi/fonts.py Tue Mar 30 10:05:55 2021 +0200
+++ b/svghmi/fonts.py Tue Mar 30 14:54:43 2021 +0200
@@ -17,14 +17,17 @@
"""
familyname = None
+ uniquename = None
formatname = None
mimetype = None
font = ttLib.TTFont(filename)
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
for name in font["name"].names:
- if name.nameID==1 and name.platformID in [0,3]:
+ if name.nameID in [1,16] and name.platformID==3 and name.langID==1033:
familyname = name.toUnicode()
+ if name.nameID==4 and name.platformID==3 and name.langID==1033:
+ uniquename = name.toUnicode()
if font.flavor :
# woff and woff2
@@ -32,13 +35,13 @@
mimetype = "font/" + formatname
# conditions on sfntVersion was deduced from fontTools.ttLib.sfnt
elif font.sfntVersion in ("\x00\x01\x00\x00", "true"):
- formatname = "truetype"
+ formatname = "truetype"
mimetype = "font/ttf"
elif font.sfntVersion == "OTTO":
formatname = "opentype"
mimetype = "font/otf"
- return familyname,formatname,mimetype
+ return familyname,uniquename,formatname,mimetype
def DataURIFromFile(filename, mimetype):
with open(filename, "rb") as fp:
@@ -50,7 +53,7 @@
b64encode(data).strip()])
def GetCSSFontFaceFromFontFile(filename):
- familyname, formatname, mimetype = GetFontTypeAndFamilyName(filename)
+ familyname, uniquename, formatname, mimetype = GetFontTypeAndFamilyName(filename)
data_uri = DataURIFromFile(filename, mimetype)
css_font_face = \
"""
--- a/svghmi/gen_index_xhtml.xslt Tue Mar 30 10:05:55 2021 +0200
+++ b/svghmi/gen_index_xhtml.xslt Tue Mar 30 14:54:43 2021 +0200
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:debug="debug" xmlns:preamble="preamble" xmlns:declarations="declarations" xmlns:definitions="definitions" xmlns:epilogue="epilogue" xmlns:ns="beremiz" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions">
- <xsl:output cdata-section-elements="xhtml:script" method="xml"/>
+<xsl:stylesheet xmlns:ns="beremiz" xmlns:definitions="definitions" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:func="http://exslt.org/functions" xmlns:epilogue="epilogue" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:str="http://exslt.org/strings" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:exsl="http://exslt.org/common" xmlns:declarations="declarations" xmlns:debug="debug" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0">
+ <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
<xsl:variable name="svg" select="/svg:svg"/>
<xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
<xsl:variable name="hmitree" select="ns:GetHMITree()"/>
@@ -1713,7 +1713,7 @@
</xsl:text>
</xsl:template>
<xsl:variable name="excluded_types" select="str:split('Page VarInit VarInitPersistent')"/>
- <xsl:key name="TypesKey" match="widget" use="@type"/>
+ <xsl:key use="@type" name="TypesKey" match="widget"/>
<declarations:hmi-classes/>
<xsl:template match="declarations:hmi-classes">
<xsl:text>
--- a/svghmi/gen_index_xhtml.ysl2 Tue Mar 30 10:05:55 2021 +0200
+++ b/svghmi/gen_index_xhtml.ysl2 Tue Mar 30 14:54:43 2021 +0200
@@ -69,7 +69,11 @@
html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" {
- head;
+ head {
+ style type="text/css" media="screen" {
+ value "ns:GetFonts()";
+ }
+ }
// prevents user selection by mouse click / touch and drag
// prevents pinch zoom and other accidental panning panning with touch devices
body style="margin:0;overflow:hidden;user-select:none;touch-action:none;" {
--- a/svghmi/svghmi.py Tue Mar 30 10:05:55 2021 +0200
+++ b/svghmi/svghmi.py Tue Mar 30 14:54:43 2021 +0200
@@ -313,7 +313,7 @@
{
"bitmap": "AddFont",
"name": _("Add Font"),
- "tooltip": _("Add TTF, OTH or WOFF font to be embedded in HMI"),
+ "tooltip": _("Add TTF, OTF or WOFF font to be embedded in HMI"),
"method": "_AddFont"
},
{
@@ -400,6 +400,18 @@
return ret
+ def GetFonts(self, _context):
+ project_path = self.CTNPath()
+ fontdir = os.path.join(project_path, "fonts")
+ css_parts = []
+
+ for f in sorted(os.listdir(fontdir)):
+ fontfile = os.path.join(fontdir,f)
+ if os.path.isfile(fontfile):
+ css_parts.append(GetCSSFontFaceFromFontFile(fontfile))
+
+ return "".join(css_parts)
+
times_msgs = {}
indent = 1
def ProgressStart(self, k, m):
@@ -455,6 +467,7 @@
[("GetSVGGeometry", lambda *_ignored:self.GetSVGGeometry()),
("GetHMITree", lambda *_ignored:self.GetHMITree()),
("GetTranslations", self.GetTranslations),
+ ("GetFonts", self.GetFonts),
("ProgressStart", lambda _ign,k,m:self.ProgressStart(str(k),str(m))),
("ProgressEnd", lambda _ign,k:self.ProgressEnd(str(k)))])
@@ -618,10 +631,62 @@
self.GetCTRoot().logger.write_error(_("POT file does not exist, add translatable text (label starting with '_') in Inkscape first\n"))
def _AddFont(self):
- pass
+ dialog = wx.FileDialog(
+ self.GetCTRoot().AppFrame,
+ _("Choose a font"),
+ os.path.expanduser("~"),
+ "",
+ _("Font files (*.ttf;*.otf;*.woff;*.woff2)|*.ttf;*.otf;*.woff;*.woff2"), wx.OPEN)
+
+ if dialog.ShowModal() == wx.ID_OK:
+ fontfile = dialog.GetPath()
+ if os.path.isfile(fontfile):
+ familyname, uniquename, formatname, mimetype = GetFontTypeAndFamilyName(fontfile)
+ else:
+ self.GetCTRoot().logger.write_error(
+ _('Selected font %s is not a readable file\n')%fontfile)
+ return
+ if familyname is None or uniquename is None or formatname is None or mimetype is None:
+ self.GetCTRoot().logger.write_error(
+ _('Selected font file %s is invalid or incompatible\n')%fontfile)
+ return
+
+ project_path = self.CTNPath()
+
+ fontfname = uniquename + "." + mimetype.split('/')[1]
+ fontdir = os.path.join(project_path, "fonts")
+ newfontfile = os.path.join(fontdir, fontfname)
+
+ if not os.path.exists(fontdir):
+ os.mkdir(fontdir)
+
+ shutil.copyfile(fontfile, newfontfile)
+
+ self.GetCTRoot().logger.write(
+ _('Added font %s as %s\n')%(fontfile,newfontfile))
def _DelFont(self):
- pass
+ project_path = self.CTNPath()
+ fontdir = os.path.join(project_path, "fonts")
+ dialog = wx.FileDialog(
+ self.GetCTRoot().AppFrame,
+ _("Choose a font to remove"),
+ fontdir,
+ "",
+ _("Font files (*.ttf;*.otf;*.woff;*.woff2)|*.ttf;*.otf;*.woff;*.woff2"), wx.OPEN)
+ if dialog.ShowModal() == wx.ID_OK:
+ fontfile = dialog.GetPath()
+ if os.path.isfile(fontfile):
+ if os.path.relpath(fontfile, fontdir) == os.path.basename(fontfile):
+ os.remove(fontfile)
+ self.GetCTRoot().logger.write(
+ _('Removed font %s\n')%fontfile)
+ else:
+ self.GetCTRoot().logger.write_error(
+ _("Font to remove %s is not in %s\n") % (fontfile,fontdir))
+ else:
+ self.GetCTRoot().logger.write_error(
+ _("Font file does not exist: %s\n") % fontfile)
def CTNGlobalInstances(self):
# view_name = self.BaseParams.getName()