Commit 2dde9429 authored by wanli's avatar wanli

feat: EUI字体生成工具

前端新增EUI字体生成界面,后端新增EUI字体生成接口
parent 0faf429a
...@@ -48,7 +48,7 @@ backend/*.db-journal ...@@ -48,7 +48,7 @@ backend/*.db-journal
backend/backup/* backend/backup/*
backend/upload/ backend/upload/
backend/uploads/ backend/uploads/
backend/static/ backend/temp/
backend/config.ini backend/config.ini
backend/backupData.json backend/backupData.json
*/app-store.db */app-store.db
......
...@@ -61,6 +61,15 @@ class ConvertString(BaseSchema): ...@@ -61,6 +61,15 @@ class ConvertString(BaseSchema):
class Meta: class Meta:
unknown = EXCLUDE unknown = EXCLUDE
class GenerateFont(BaseSchema):
string = fields.String(required=True) # 字典
font = fields.String(required=True) # 字体文件
sizes = fields.List(fields.Int, reuquired=True) # 字体大小列表
text = fields.Raw(required=False) # 字典文件
class Meta:
unknown = EXCLUDE
class ExportProject(BaseSchema): class ExportProject(BaseSchema):
project = fields.List(fields.String, required=True) project = fields.List(fields.String, required=True)
production = fields.List(fields.String, required=True) production = fields.List(fields.String, required=True)
......
#-*- coding: UTF-8 -*-
#!/usr/bin/python
import os
import sys
import fs
import struct
import json
from collections import OrderedDict
import zlib
import pprint
import hashlib
from ctypes import *
import platform
current_abspath = os.path.dirname(os.path.realpath(__file__))
if platform.system() == 'Windows':
pDll = CDLL(os.sep.join([current_abspath, "lib", "eheatshrink.dll"]))
elif platform.system() == 'Linux':
pDll = CDLL(os.sep.join([current_abspath, "lib", "libeheatshrink.so"]))
pDll.ecompress_size.restype = c_uint32
pDll.ecompress_size.argtypes = [c_void_p, c_uint32]
pDll.ecompress.restype = POINTER(c_uint8)
pDll.ecompress.argtypes = [c_void_p, c_uint32]
def heatshrink_compress(buf:bytes, level:int):
count = len(buf)
size = pDll.ecompress_size(buf, count)
pDll.ecompress.restype = POINTER(c_uint8)
pDll.ecompress.argtypes = [c_void_p, c_uint32]
ret = pDll.ecompress(buf, count)
arr = bytearray(size)
i = 0
while i < size:
arr[i] = ret[i]
i = i+1
return arr
def str_to_hex(s):
return ' '.join([hex(ord(c)).replace('0x', '') for c in s])
def hex_to_str(s):
return ''.join([chr(i) for i in [int(b, 16) for b in s.split(' ')]])
def str_to_bin(s):
return ' '.join([bin(ord(c)).replace('0b', '') for c in s])
def bin_to_str(s):
return ''.join([chr(i) for i in [int(b, 2) for b in s.split(' ')]])
class EpkApp(object):
def __init__(self, appName, appDir, algorithm='zlib',appVersion="1.0", output="epks"):
super(EpkApp, self).__init__()
self._appName = appName
self._appDir = os.path.abspath(appDir)
self.algorithm = algorithm
self._appVersion = appVersion
self._appCRCCode = None
self._files = []
self._infoPath = os.sep.join([self._appDir, "%s.json" % self._appName])
self._epksDir = output
if not os.path.exists(self._epksDir):
fs.open_fs(os.getcwd()).makedirs(output)
self._epkName = os.sep.join([self._epksDir, "%s.epk" % self._appName])
def compress(self):
if self.algorithm == 'h':
return heatshrink_compress
return zlib.compress
def epkInfo(self):
epkInfo = OrderedDict({
"appName": self._appName,
"appVersion": self._appVersion,
"files": self.fileinfos(self._appDir),
})
infocontent = json.dumps(epkInfo)
with open(self._infoPath, "w", encoding='utf-8') as f:
f.write(infocontent)
return epkInfo
def fileinfos(self, path):
path = os.path.abspath(path)
home_fs = fs.open_fs(path)
files = []
for jspath in home_fs.glob('*', namespaces=['details']):
fpath = "C:/%s" % jspath.info.name
fname = jspath.info.name
fsize = jspath.info.size
fbasename, fext = os.path.splitext(jspath.info.name)
if fext in ["", ".exe", ".dll", ".nv", ".conf"]:
continue
finfo = {
"path": fpath,
"name": fname,
"size": fsize,
"basename": fbasename,
"ext": fext
}
if self._infoPath == os.sep.join([path, fname]):
files.insert(0, finfo)
else:
files.append(finfo)
if fext == ".evue":
self.fileMD5(finfo)
return files
def header(self, epk_start=0xAAAA, md5_offset=0, file_count=0):
bytes_header = struct.pack("<HLH", epk_start, md5_offset, file_count)
return bytes_header
def fileMD5(self, info):
md5path = os.sep.join([self._appDir, "%s.md5" % info["basename"]])
fpath = os.sep.join([self._appDir, info["name"]])
with open(fpath, "rb") as f:
filecontent = f.read()
newmd5 = self.md5(filecontent)
with open(md5path, "wb") as f:
f.write(newmd5)
return newmd5
def sign(self, content):
ret = b""
for i in range(int(len(content) / 2 )):
ret += struct.pack("<B", int("0x%s" % (content[i*2:i*2+2]), 16))
ret = ret + b'EVM is NB ++!'
return ret
def md5(self, filecontent):
newmd5 = ''
content = filecontent
for i in range(3):
md5 = hashlib.md5() #获取一个md5加密算法对象
md5.update(content) #指定需要加密的字符串
newmd5 = md5.hexdigest() #获取加密后的16进制字符串
content = self.sign(newmd5)
ret = b""
for i in range(int(len(newmd5) / 2 )):
ret += struct.pack("<B", int("0x%s" % (newmd5[i*2:i*2+2]), 16))
return ret
def packFile(self, info, level=9):
fname = info["name"]
fpath = os.sep.join([self._appDir, fname])
fext = info["ext"]
fileBytes = b""
if fext == "md5":
fileBytes += struct.pack("<B", 1)
else:
fileBytes += struct.pack("<B", 2)
_name = fname + "\0"
fileBytes += struct.pack("<B", len(_name))
fileBytes += struct.pack("<%ds" % len(_name), fname.encode("utf-8"))
with open(fpath, "rb") as fc:
fileContentBytes = fc.read()
fileBytes += struct.pack("<L", len(fileContentBytes))
if fext == "md5":
fileCompressBytes = fileContentBytes
else:
fileCompressBytes = self.compress()(fileContentBytes, level)
fileBytes += struct.pack("<L", len(fileCompressBytes))
fileBytes += fileCompressBytes
return fileBytes
def pack(self, level=9):
for i in range(10):
infos = self.epkInfo()
# infos = self.epkInfo()
# infos = self.epkInfo()
epkFileBytes = b""
epkFileContentBytes = b""
file_count = len(infos["files"])
with open(self._epkName, "wb") as f:
for info in infos["files"]:
epkFileContentBytes += self.packFile(info)
epkFileContentLength = len(epkFileContentBytes)
epkFileBytes += self.header(md5_offset= 8 + epkFileContentLength, file_count=file_count)
epkFileBytes += epkFileContentBytes
epkmd5Bytes = self.md5(epkFileBytes)
epkFileBytes += struct.pack("<H", len(epkmd5Bytes))
epkFileBytes += epkmd5Bytes
crcBytes = zlib.crc32(epkFileBytes)
epkFileBytes += struct.pack("<L", crcBytes)
f.write(epkFileBytes)
ret = {
"epkfile": self._epkName,
"epk_filecontent_size": epkFileContentLength,
"md5_offset": 10 + epkFileContentLength,
"file_count": file_count,
"md5_length": len(epkmd5Bytes),
"md5": epkmd5Bytes,
"raw_crc": hex(crcBytes),
"compress_level": level,
"buff_length": len(epkFileBytes)
}
pprint.pprint(ret)
return ret
def main(path, appName, algorithm):
epk = EpkApp(appName, path, algorithm)
epk.pack()
if __name__ == '__main__':
main(sys.argv[1], sys.argv[2], sys.argv[3])
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
#
# FreeType high-level python API - Copyright 2011-2015 Nicolas P. Rougier
# Distributed under the terms of the new BSD license.
#
# -----------------------------------------------------------------------------
'''
Glyph bitmap monochrome rendering
'''
import json
import struct
import sys
import pprint
from pathlib import Path
import mimetypes
from freetype import Face
class EvueFontTool(object):
FONT_DPI = 100
def __init__(self, freetypefile, output_dir, sizes=[10,20,30]):
super().__init__()
self.freetypefile = freetypefile
self.sizes = sizes
self.face = Face(self.freetypefile)
self.infos = []
self.output_dir = Path(output_dir)
if not self.output_dir.exists():
self.output_dir.mkdir()
# raise Exception("{} not exists".format(output_dir))
@property
def fontInfo(self):
return {
'family': str(self.face.family_name, 'utf-8'),
'style': str(self.face.style_name, 'utf-8'),
'charmaps': [charmap.encoding_name for charmap in self.face.charmaps],
'face_number': self.face.num_faces,
'glyph_number': self.face.num_glyphs,
'available_sizes': list(map(lambda x: { 'width': x.width, 'height': x.height, 'size': x.size }, self.face.available_sizes)),
'units_per_em': self.face.units_per_EM,
'ascender': self.face.ascender,
'descender': self.face.descender,
'height': self.face.height,
'max_advance_width': self.face.max_advance_width,
'max_advance_height': self.face.max_advance_height,
'underline_position': self.face.underline_position,
'underline_thickness': self.face.underline_thickness,
'has_horizontal': self.face.has_horizontal,
'has_vertical': self.face.has_vertical,
'has_kerning': self.face.has_kerning,
'is_fixed_width': self.face.is_fixed_width,
'is_scalable': self.face.is_scalable
}
def charInfo(self, cur_char):
self.face.load_char(cur_char)
char_info = {
'glyph': ord(cur_char),
'left': self.face.glyph.bitmap_left,
'top': self.face.glyph.bitmap_top,
'advance': self.face.glyph.advance.x / 64,
'cols': self.face.glyph.bitmap.width,
'rows': self.face.glyph.bitmap.rows,
'bitmap': self.face.glyph.bitmap.buffer, # list[]
'count': len(self.face.glyph.bitmap.buffer),
}
return char_info
def dump(self, used_string):
# 遍历每一个字体
dump_json = {
'name': self.fontInfo.get("family"),
'info': self.fontInfo,
'text': used_string,
'size': self.sizes,
'bitmap': {
'total': 0
}
}
for cur_char in used_string:
# 遍历每一个字体设定的大小值
for size in self.sizes:
# 设置字体大小
self.face.set_char_size(size * 64, 0, self.FONT_DPI, 0)
char_info = self.charInfo(cur_char)
self.infos.append(char_info)
# 文件命名规则:./<output_dir>/<font-size>/<unicode-number>.bin
target_dir = self.output_dir.joinpath(str(size)).joinpath("{}.bin".format(char_info.get('glyph')))
if not target_dir.parent.exists():
target_dir.parent.mkdir()
with open(target_dir.resolve().as_posix(), 'wb+') as f:
f.write(struct.pack("%dB" % char_info['count'], *char_info['bitmap']))
dump_json['bitmap'].update({
size: dump_json.get('bitmap').get(size, 0) + target_dir.stat().st_size
})
for key in dump_json['bitmap']:
if key != 'total':
dump_json['bitmap']['total'] += dump_json['bitmap'][key]
pprint.pprint(dump_json)
# 在输出目录下,生成一个json索引文件
with open(self.output_dir.joinpath("fonts.json").resolve().as_posix(), "wb+") as f:
f.write(json.dumps(dump_json, ensure_ascii=False, indent=2).encode('utf-8'))
def load(self, file):
pass
def show(self, info):
import numpy
import matplotlib.pyplot as plt
data = info['bitmap']
rows = info['rows']
width = info['cols']
# for i in range(rows):
# data.extend(bitmap.buffer[i*pitch:i*pitch+width])
Z = numpy.array(data,dtype=numpy.ubyte).reshape(rows, width)
plt.imshow(Z, interpolation='nearest', cmap=plt.cm.gray)
plt.show()
def test(self, used_string):
for cur_char in used_string:
char_info = self.charInfo(cur_char)
self.infos.append(char_info)
self.show(char_info)
if __name__ == '__main__':
print(mimetypes.guess_type(sys.argv[1])[0])
evueFontTool = EvueFontTool(sys.argv[1], sys.argv[2])
text = '中国'
# evueFontTool.test(text)
evueFontTool.dump(text)
pprint.pprint(evueFontTool.fontInfo)
\ No newline at end of file
This diff is collapsed.
#-*- coding: UTF-8 -*-
#!/usr/bin/python
import os
import sys
import fs
import struct
import json
from collections import OrderedDict
import zlib
import pprint
import hashlib
def str_to_hex(s):
return ' '.join([hex(ord(c)).replace('0x', '') for c in s])
def hex_to_str(s):
return ''.join([chr(i) for i in [int(b, 16) for b in s.split(' ')]])
def str_to_bin(s):
return ' '.join([bin(ord(c)).replace('0b', '') for c in s])
def bin_to_str(s):
return ''.join([chr(i) for i in [int(b, 2) for b in s.split(' ')]])
class EpkApp(object):
def __init__(self, appName, appDir, appVersion="1.0", output="epks"):
super(EpkApp, self).__init__()
self._appName = appName
self._appDir = os.path.abspath(appDir)
self._appVersion = appVersion
self._appCRCCode = None
self._files = []
self._infoPath = os.sep.join([self._appDir, "%s.json" % self._appName])
self._epksDir = output
if not os.path.exists(self._epksDir):
fs.open_fs(os.getcwd()).makedirs(output)
self._epkName = os.sep.join([self._epksDir, "%s.epk" % self._appName])
def epkInfo(self):
epkInfo = OrderedDict({
"appName": self._appName,
"appVersion": self._appVersion,
"files": self.fileinfos(self._appDir),
})
infocontent = json.dumps(epkInfo)
with open(self._infoPath, "w", encoding='utf-8') as f:
f.write(infocontent)
return epkInfo
def fileinfos(self, path):
path = os.path.abspath(path)
home_fs = fs.open_fs(path)
files = []
for jspath in home_fs.glob('*', namespaces=['details']):
fpath = "C:/%s" % jspath.info.name
fname = jspath.info.name
fsize = jspath.info.size
fbasename = jspath.info.name.split(".")[0]
fext = jspath.info.name.split(".")[1]
if fext in ["exe", "dll", "nv", "conf"]:
continue
finfo = {
"path": fpath,
"name": fname,
"size": fsize,
"basename": fbasename,
"ext": fext
}
if self._infoPath == os.sep.join([path, fname]):
files.insert(0, finfo)
else:
files.append(finfo)
if fext == "evue":
self.fileMD5(finfo)
return files
def header(self, epk_start=0xAAAA, md5_offset=0, file_count=0):
bytes_header = struct.pack("<HLH", epk_start, md5_offset, file_count)
return bytes_header
def fileMD5(self, info):
md5path = os.sep.join([self._appDir, "%s.md5" % info["basename"]])
fpath = os.sep.join([self._appDir, info["name"]])
with open(fpath, "rb") as f:
filecontent = f.read()
print(fpath)
newmd5 = self.md5(filecontent)
print(md5path)
with open(md5path, "wb") as f:
f.write(newmd5)
return newmd5
def sign(self, content):
ret = b""
for i in range(int(len(content) / 2 )):
ret += struct.pack("<B", int("0x%s" % (content[i*2:i*2+2]), 16))
ret = ret + b'EVM is NB ++!'
return ret
def md5(self, filecontent):
newmd5 = ''
content = filecontent
for i in range(3):
md5 = hashlib.md5() #获取一个md5加密算法对象
md5.update(content) #指定需要加密的字符串
newmd5 = md5.hexdigest() #获取加密后的16进制字符串
print(newmd5)
content = self.sign(newmd5)
ret = b""
for i in range(int(len(newmd5) / 2 )):
ret += struct.pack("<B", int("0x%s" % (newmd5[i*2:i*2+2]), 16))
return ret
def packFile(self, info, level=9):
fname = info["name"]
fpath = os.sep.join([self._appDir, fname])
fext = info["ext"]
fileBytes = b""
if fext == "md5":
fileBytes += struct.pack("<B", 1)
else:
fileBytes += struct.pack("<B", 2)
_name = fname + "\0"
fileBytes += struct.pack("<B", len(_name))
fileBytes += struct.pack("<%ds" % len(_name), fname.encode("utf-8"))
with open(fpath, "rb") as fc:
fileContentBytes = fc.read()
print(info["name"])
print(len(fileContentBytes))
fileBytes += struct.pack("<L", len(fileContentBytes))
if fext == "md5":
fileCompressBytes = fileContentBytes
else:
fileCompressBytes = zlib.compress(fileContentBytes, level)
print(len(fileCompressBytes))
fileBytes += struct.pack("<L", len(fileCompressBytes))
fileBytes += fileCompressBytes
return fileBytes
def pack(self, level=9):
for i in range(10):
infos = self.epkInfo()
# infos = self.epkInfo()
# infos = self.epkInfo()
result = {}
epkFileBytes = b""
epkFileContentBytes = b""
file_count = len(infos["files"])
with open(self._epkName, "wb") as f:
for info in infos["files"]:
epkFileContentBytes += self.packFile(info)
epkFileContentLength = len(epkFileContentBytes)
epkFileBytes += self.header(md5_offset= 8 + epkFileContentLength, file_count=file_count)
epkFileBytes += epkFileContentBytes
epkmd5Bytes = self.md5(epkFileBytes)
epkFileBytes += struct.pack("<H", len(epkmd5Bytes))
epkFileBytes += epkmd5Bytes
crcBytes = zlib.crc32(epkFileBytes)
epkFileBytes += struct.pack("<L", crcBytes)
f.write(epkFileBytes)
ret = {
"epkfile": self._epkName,
"epk_filecontent_size": epkFileContentLength,
"md5_offset": 10 + epkFileContentLength,
"file_count": file_count,
"md5_length": len(epkmd5Bytes),
"md5": epkmd5Bytes,
"raw_crc": hex(crcBytes),
"compress_level": level,
"buff_length": len(epkFileBytes)
}
result = ret
return result
def main(path, appName):
epk = EpkApp(appName, path)
epk.pack()
if __name__ == '__main__':
main(sys.argv[1], sys.argv[2])
...@@ -8,7 +8,6 @@ import traceback ...@@ -8,7 +8,6 @@ import traceback
import uuid import uuid
import time import time
import zipfile import zipfile
import sqlite3
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
...@@ -20,10 +19,12 @@ from fullstack.log import logger ...@@ -20,10 +19,12 @@ from fullstack.log import logger
from fullstack.login import Auth from fullstack.login import Auth
from fullstack.response import ResponseCode, response_result from fullstack.response import ResponseCode, response_result
from fullstack.validation import validate_schema from fullstack.validation import validate_schema
from schema.api import UpdatePasswordSchema, ApplicationBuildSchema, ConvertString from schema.api import UpdatePasswordSchema, ApplicationBuildSchema, ConvertString, GenerateFont
import sys import sys
sys.path.append("..") sys.path.append("..")
from utils import vbuild from utils import vbuild
from utils.evuefonttool import EvueFontTool
from utils.mkromfs import mkromfs
api = Blueprint("api", __name__, url_prefix="/api/v1/%s" % config['NAME']) api = Blueprint("api", __name__, url_prefix="/api/v1/%s" % config['NAME'])
...@@ -198,7 +199,7 @@ def action_build(): ...@@ -198,7 +199,7 @@ def action_build():
with open(file, "w+") as fd: with open(file, "w+") as fd:
fd.write(text) fd.write(text)
result = subprocess.call("./executable -c {file}".format(file=file), shell=True) result = subprocess.call("./utils/executable -c {file}".format(file=file), shell=True)
new_name = "{}.{}".format(Path(file).name, "bc") new_name = "{}.{}".format(Path(file).name, "bc")
os.rename(os.sep.join([os.getcwd(), "executable.bc"]), os.sep.join([os.getcwd(), new_name])) os.rename(os.sep.join([os.getcwd(), "executable.bc"]), os.sep.join([os.getcwd(), new_name]))
dst_files.append(Path(os.getcwd()).joinpath(new_name)) dst_files.append(Path(os.getcwd()).joinpath(new_name))
...@@ -304,6 +305,65 @@ def update_db(): ...@@ -304,6 +305,65 @@ def update_db():
return response_result(ResponseCode.OK, data=result) return response_result(ResponseCode.OK, data=result)
@api.route("/system/generateFont", methods=['POST'])
def generate_font():
if not request.form:
return response_result(ResponseCode.PARAMETER_ERROR, msg="form can not be null")
sizes = request.form.get("sizes", None)
if not sizes:
return response_result(ResponseCode.PARAMETER_ERROR, msg="param sizes can not be null")
# 判断用户输入的字体大小是否合法
logger.info(sizes)
font = request.files.get("font", None)
if not font:
return response_result(ResponseCode.PARAMETER_ERROR, msg="param font can not be null")
logger.info(font.content_type)
# 保存字体文件到临时目录
random_str = uuid.uuid4().hex
temp_dir = Path(os.path.dirname(os.path.dirname(__file__))).joinpath("temp").joinpath(random_str)
if not temp_dir.exists():
temp_dir.mkdir()
fontSavePath = temp_dir.joinpath(font.filename).resolve().as_posix()
font.save(fontSavePath)
# 判断用户输入的字典是否为空
text_dict = request.form.get("text", None)
if not text_dict:
return response_result(ResponseCode.PARAMETER_ERROR, msg="param text can not be null")
# 如果通过上面校验,则进行生成文件流程
# 参数一:字体文件、参数二:输出目录
target_dir = Path(config.get("UPLOAD_PATH")).joinpath(config.get("TEMP_DIR")).joinpath(datetime.now().strftime("%Y%m%d%H%M%S%f"))
if not target_dir.exists():
target_dir.mkdir()
# 当生成字体比较大的时候,是否考虑启用线程处理文字?
sizes = sizes.split(",")
ts = []
for s in sizes:
ts.append(int(s))
evueFontTool = EvueFontTool(fontSavePath, target_dir.resolve().as_posix(), sizes=ts)
evueFontTool.dump(text_dict)
# 这里是否有必要让用户输入最终生成的文件名,可选
filename = request.form.get("filename", "fonts.bin")
mkromfs(target_dir.resolve().as_posix(), "/", filename)
# 删除字体文件
os.chdir(temp_dir.parent.resolve().as_posix())
shutil.rmtree(random_str)
return response_result(ResponseCode.OK, data={
'filename': filename,
'url': target_dir.joinpath(filename).resolve().relative_to(config.get("UPLOAD_PATH")).as_posix(),
})
@api.route("/system/convertString", methods=['POST']) @api.route("/system/convertString", methods=['POST'])
@validate_schema(ConvertString) @validate_schema(ConvertString)
def convert_string(): def convert_string():
......
...@@ -325,3 +325,11 @@ export function getDataList(params) { ...@@ -325,3 +325,11 @@ export function getDataList(params) {
params, params,
}); });
} }
export function generateFont(data) {
return request({
url: "/api/v1/evm_store/system/generateFont",
method: "post",
data
});
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
...@@ -187,14 +187,14 @@ export const constantRoutes = [ ...@@ -187,14 +187,14 @@ export const constantRoutes = [
}] }]
}, },
{ {
path: '/history', path: '/tool',
redirect: '/history/index', redirect: '/tool/font',
component: Layout, component: Layout,
children: [{ children: [{
path: 'index', path: 'font',
name: 'AppHistoryChart', name: 'FontTool',
component: () => import('@/views/system/history'), component: () => import('@/views/system/font-tool'),
meta: { title: '历史曲线', icon: 'home' } meta: { title: '字体转换工具', icon: 'home' }
}] }]
}, },
{ {
...@@ -231,14 +231,14 @@ export const constantRoutes = [ ...@@ -231,14 +231,14 @@ export const constantRoutes = [
}] }]
}, },
{ {
path: '/evue', path: '/bytecode',
redirect: '/evue/index', redirect: '/bytecode/index',
component: Layout, component: Layout,
children: [{ children: [{
path: 'index', path: 'index',
name: 'ByteCodeFile', name: 'ByteCodeFile',
component: () => import('@/views/system/evue'), component: () => import('@/views/system/bytecode-tool'),
meta: { title: 'evue', icon: 'home' } meta: { title: 'bytecode', icon: 'home' }
}] }]
}, },
{ {
......
...@@ -71,22 +71,22 @@ export default { ...@@ -71,22 +71,22 @@ export default {
// icon: "gongzuotai", // icon: "gongzuotai",
// path: "chart/index", // path: "chart/index",
// }, // },
// { {
// vue: "system/history.vue", vue: "system/font-tool.vue",
// title: "历史曲线", title: "字体转换工具",
// name: "AppHistoryChart", name: "FontTool",
// icon: "gongzuotai", icon: "gongzuotai",
// path: "history/index", path: "tool/font",
// }, },
{ {
vue: "system/tool.vue", vue: "system/tool.vue",
title: "工具", title: "字符串转C文件",
name: "AppTool", name: "AppTool",
icon: "gongzuotai", icon: "gongzuotai",
path: "tool/index", path: "tool/index",
}, },
{ {
vue: "system/evue.vue", vue: "system/bytecode-tool.vue",
title: "字节码文件转换", title: "字节码文件转换",
name: "ByteCodeFile", name: "ByteCodeFile",
icon: "gongzuotai", icon: "gongzuotai",
......
<template>
<div class="app-container">
<el-alert title="字节码文件在线转换" type="success"></el-alert>
<div style="margin: 10px 0px">
<el-form>
<el-form-item>
<el-upload
name="binfile"
drag
multiple
ref="upload"
action="null"
:auto-upload="false"
:http-request="handleUploadFile"
:on-remove="handleUploadRemove"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">请选择evue文件</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button @click="uploadFile">开始上传</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { postByteCodeFile } from "@/api/app-store";
export default {
name: "ByteCodeFile",
data() {
return {
fileList: [],
};
},
methods: {
createFile(content, filename) {
const a = document.createElement("a");
const blob = new Blob([content]);
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
},
handleUploaded(response) {
if (response.code === 200) {
this.downloadFile(response);
} else {
this.$message.warning(response.message);
}
},
handleUploadFile(file) {
this.fileList.push(file);
},
handleUploadRemove(file, fileList) {
console.log(fileList);
for (let i = 0; i < this.fileList.length; i++) {
if (this.fileList[i].uid == file.uid) {
this.fileList.splice(i, 1);
break;
}
}
},
downloadFile(result) {
const a = document.createElement("a");
a.download = result.data.filename;
a.href = result.data.url;
a.target = "";
a.click();
},
uploadFile() {
this.$refs.upload.submit();
let formData = new FormData();
this.fileList.forEach((item) => {
formData.append("binfile", item.file);
});
postByteCodeFile(formData)
.then((res) => {
if (res.code === 200) {
this.downloadFile(res);
} else {
this.$message.warning(res.message);
}
})
.catch((err) => {
this.$message.error(err.message);
});
},
},
mounted() {},
created() {},
};
</script>
<style lang="scss" scoped>
.app-container {
& > div.page-wrapper {
margin: 10px 0px;
}
}
</style>
<template>
<div class="app-container">
<h3>字体转换工具</h3>
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-input
type="textarea"
:autosize="{ minRows: 30, maxRows: 30 }"
resize="none"
placeholder="请选择要转换的文本"
v-model="post.text"
></el-input>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form label-position="right" label-width="120px" :model="post">
<el-form-item label="输出bin文件名">
<el-input v-model="post.filename"></el-input>
</el-form-item>
<el-form-item label="文字大小">
<el-input
v-model="post.sizes"
placeholder="可输入多个数值,用英文逗号隔开"
></el-input>
</el-form-item>
<el-form-item label="字库文件">
<el-upload
name="font"
ref="upload"
accept=".eot,.otf,.fon,.font,.ttf,.ttc,.woff"
:limit="1"
:multiple="false"
:data="post"
:action="uploadAction"
:on-success="handleSuccess"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
:auto-upload="false"
>
<el-button slot="trigger" size="small" type="primary"
>选取文件</el-button
>
<el-button
style="margin-left: 10px"
size="small"
type="success"
@click="submitUpload"
>开始转换字体</el-button
>
<div slot="tip" class="el-upload__tip">
请选择字库文件,目前只支持如下格式文件:eot,otf,fon,font,ttf,ttc,woff
</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button @click="loadFont('common')">加载常用字体</el-button>
<el-button @click="loadFont('all')">加载全部字体</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import allFonts from "@/assets/all-fonts.json";
import commonFonts from "@/assets/common-used-fonts.json";
import { generateFont } from "@/api/app-store";
// import { mapTrim } from '@/utils/index'
// 优化:可以取字体差异,POST只发送产生差异的字,比如删除的字、添加的字
export default {
name: "FontTool",
data() {
return {
isLoading: false,
inputString: commonFonts.join(""),
outputString: "",
post: {
sizes: null,
filename: null,
text: commonFonts.join(""),
},
uploadAction: `//${window.location.host}/api/v1/evm_store/system/generateFont`,
fileList: [],
};
},
methods: {
downloadFile(result) {
const a = document.createElement("a");
a.download = result.filename;
a.href = result.url;
a.target = "";
a.click();
},
loadFont(source) {
if (source == "all") this.post.text = allFonts.join("");
else this.post.text = commonFonts.join("");
},
handleUpload() {
let formData = new FormData();
Object.keys(this.post).forEach((k) => {
formData.append(k, this.post[k]);
});
generateFont(formData)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.error(err);
});
},
submitUpload() {
this.$refs.upload.submit();
},
handleSuccess(res) {
if (res.code == 200) {
this.downloadFile(res.data);
}
this.$refs.upload.clearFiles();
},
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
},
mounted() {},
created() {},
};
</script>
<style lang="scss" scoped>
.app-container {
& > div.page-wrapper {
margin: 10px 0px;
}
}
</style>
...@@ -87,30 +87,41 @@ ...@@ -87,30 +87,41 @@
min-width="150" min-width="150"
show-overflow-tooltip show-overflow-tooltip
></el-table-column> ></el-table-column>
<el-table-column
prop="path"
label="path"
min-width="150"
show-overflow-tooltip
></el-table-column>
<el-table-column <el-table-column
prop="timestamp" prop="timestamp"
label="timestamp" label="timestamp"
min-width="150" min-width="150"
show-overflow-tooltip show-overflow-tooltip
></el-table-column> ></el-table-column>
<el-table-column label="imei" min-width="150" show-overflow-tooltip>
<template slot-scope="scope">{{ scope.row.imei }}</template>
</el-table-column>
<el-table-column <el-table-column
prop="free_size"
label="free_size" label="free_size"
min-width="100" min-width="150"
show-overflow-tooltip show-overflow-tooltip
> >
<template slot-scope="scope" <template slot-scope="scope"
>{{ scope.row.free_size }}(KB)</template >{{ scope.row.free_size }}(KB)</template
> >
</el-table-column> </el-table-column>
<el-table-column
prop="free_space_size"
label="free_space_size"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope"
>{{ scope.row.free_space_size }}(KB)</template
>
</el-table-column>
<el-table-column
label="used_space_size"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope"
>{{ scope.row.used_space_size }}(KB)</template
>
</el-table-column>
</el-table> </el-table>
</grid-item> </grid-item>
<grid-item <grid-item
...@@ -350,7 +361,28 @@ ...@@ -350,7 +361,28 @@
<grid-item <grid-item
:x="0" :x="0"
:y="20" :y="20"
:w="12" :w="4"
:h="14"
i="7"
@resize="resizeEvent"
@move="moveEvent"
@resized="resizedEvent"
@container-resized="containerResizedEvent"
@moved="movedEvent"
>
<div>
<p class="item-title">应用大小</p>
<el-table :data="appList" size="mini" style="width: 100%">
<el-table-column prop="appName" label="应用名称" width="180"></el-table-column>
<el-table-column prop="fileSize" label="应用大小(KB)"></el-table-column>
<el-table-column prop="fileCount" label="文件个数" width="100"> </el-table-column>
</el-table>
</div>
</grid-item>
<grid-item
:x="4"
:y="20"
:w="8"
:h="7" :h="7"
i="7" i="7"
@resize="resizeEvent" @resize="resizeEvent"
...@@ -365,11 +397,11 @@ ...@@ -365,11 +397,11 @@
></SystemChart> ></SystemChart>
</grid-item> </grid-item>
<grid-item <grid-item
:x="0" :x="4"
:y="27" :y="27"
:w="12" :w="8"
:h="7" :h="7"
i="7" i="8"
@resize="resizeEvent" @resize="resizeEvent"
@move="moveEvent" @move="moveEvent"
@resized="resizedEvent" @resized="resizedEvent"
...@@ -383,7 +415,7 @@ ...@@ -383,7 +415,7 @@
:y="34" :y="34"
:w="12" :w="12"
:h="7" :h="7"
i="8" i="9"
@resize="resizeEvent" @resize="resizeEvent"
@move="moveEvent" @move="moveEvent"
@resized="resizedEvent" @resized="resizedEvent"
...@@ -544,6 +576,7 @@ export default { ...@@ -544,6 +576,7 @@ export default {
], ],
draggable: true, draggable: true,
resizable: true, resizable: true,
appList: [],
}; };
}, },
components: { components: {
...@@ -576,7 +609,7 @@ export default { ...@@ -576,7 +609,7 @@ export default {
console.error(err.msg); console.error(err.msg);
}); });
wsNotify.eventBus.$emit("exportImg"); wsNotify.eventBus.$emit("export-picture");
monitor monitor
.getAllData((params) => { .getAllData((params) => {
return params.imei && params.imei == this.device; return params.imei && params.imei == this.device;
...@@ -885,14 +918,27 @@ export default { ...@@ -885,14 +918,27 @@ export default {
}); });
} }
}); });
// 处理appList
// 一、统计每个页面有多少个资源文件;二、累加每个资源文件的size;三、以表格形式展示统计信息
if (msg.appList && msg.appList.length) {
this.appList = msg.appList.map(item => {
item.fileCount = item.files.length
item.fileSize = item.files.reduce((total, file) => {
return total + file.size;
}, 0)
item.fileSize = item.fileSize / 1024
return item
})
}
}, },
onSelectChange(res) { onSelectChange(res) {
this.device = res; this.device = res;
// 清空图表数据 // 清空图表数据
wsNotify.eventBus.$emit("clear-system-chart");
wsNotify.eventBus.$emit("clear-evm-chart"); wsNotify.eventBus.$emit("clear-evm-chart");
wsNotify.eventBus.$emit("clear-lvgl-chart"); wsNotify.eventBus.$emit("clear-lvgl-chart");
wsNotify.eventBus.$emit("clear-system-chart");
// 清空各个表格数据 // 清空各个表格数据
this.imageList = []; this.imageList = [];
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment