Commit 2dde9429 authored by wanli's avatar wanli

feat: EUI字体生成工具

前端新增EUI字体生成界面,后端新增EUI字体生成接口
parent 0faf429a
......@@ -48,7 +48,7 @@ backend/*.db-journal
backend/backup/*
backend/upload/
backend/uploads/
backend/static/
backend/temp/
backend/config.ini
backend/backupData.json
*/app-store.db
......
......@@ -61,6 +61,15 @@ class ConvertString(BaseSchema):
class Meta:
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):
project = 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
import uuid
import time
import zipfile
import sqlite3
from pathlib import Path
from datetime import datetime
......@@ -20,10 +19,12 @@ from fullstack.log import logger
from fullstack.login import Auth
from fullstack.response import ResponseCode, response_result
from fullstack.validation import validate_schema
from schema.api import UpdatePasswordSchema, ApplicationBuildSchema, ConvertString
from schema.api import UpdatePasswordSchema, ApplicationBuildSchema, ConvertString, GenerateFont
import sys
sys.path.append("..")
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'])
......@@ -198,7 +199,7 @@ def action_build():
with open(file, "w+") as fd:
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")
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))
......@@ -304,6 +305,65 @@ def update_db():
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'])
@validate_schema(ConvertString)
def convert_string():
......
......@@ -325,3 +325,11 @@ export function getDataList(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 = [
}]
},
{
path: '/history',
redirect: '/history/index',
path: '/tool',
redirect: '/tool/font',
component: Layout,
children: [{
path: 'index',
name: 'AppHistoryChart',
component: () => import('@/views/system/history'),
meta: { title: '历史曲线', icon: 'home' }
path: 'font',
name: 'FontTool',
component: () => import('@/views/system/font-tool'),
meta: { title: '字体转换工具', icon: 'home' }
}]
},
{
......@@ -231,14 +231,14 @@ export const constantRoutes = [
}]
},
{
path: '/evue',
redirect: '/evue/index',
path: '/bytecode',
redirect: '/bytecode/index',
component: Layout,
children: [{
path: 'index',
name: 'ByteCodeFile',
component: () => import('@/views/system/evue'),
meta: { title: 'evue', icon: 'home' }
component: () => import('@/views/system/bytecode-tool'),
meta: { title: 'bytecode', icon: 'home' }
}]
},
{
......
......@@ -71,22 +71,22 @@ export default {
// icon: "gongzuotai",
// path: "chart/index",
// },
// {
// vue: "system/history.vue",
// title: "历史曲线",
// name: "AppHistoryChart",
// icon: "gongzuotai",
// path: "history/index",
// },
{
vue: "system/font-tool.vue",
title: "字体转换工具",
name: "FontTool",
icon: "gongzuotai",
path: "tool/font",
},
{
vue: "system/tool.vue",
title: "工具",
title: "字符串转C文件",
name: "AppTool",
icon: "gongzuotai",
path: "tool/index",
},
{
vue: "system/evue.vue",
vue: "system/bytecode-tool.vue",
title: "字节码文件转换",
name: "ByteCodeFile",
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 @@
min-width="150"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="path"
label="path"
min-width="150"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="timestamp"
label="timestamp"
min-width="150"
show-overflow-tooltip
></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
prop="free_size"
label="free_size"
min-width="100"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope"
>{{ scope.row.free_size }}(KB)</template
>
</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>
</grid-item>
<grid-item
......@@ -350,7 +361,28 @@
<grid-item
:x="0"
: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"
i="7"
@resize="resizeEvent"
......@@ -365,11 +397,11 @@
></SystemChart>
</grid-item>
<grid-item
:x="0"
:x="4"
:y="27"
:w="12"
:w="8"
:h="7"
i="7"
i="8"
@resize="resizeEvent"
@move="moveEvent"
@resized="resizedEvent"
......@@ -383,7 +415,7 @@
:y="34"
:w="12"
:h="7"
i="8"
i="9"
@resize="resizeEvent"
@move="moveEvent"
@resized="resizedEvent"
......@@ -544,6 +576,7 @@ export default {
],
draggable: true,
resizable: true,
appList: [],
};
},
components: {
......@@ -576,7 +609,7 @@ export default {
console.error(err.msg);
});
wsNotify.eventBus.$emit("exportImg");
wsNotify.eventBus.$emit("export-picture");
monitor
.getAllData((params) => {
return params.imei && params.imei == this.device;
......@@ -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) {
this.device = res;
// 清空图表数据
wsNotify.eventBus.$emit("clear-system-chart");
wsNotify.eventBus.$emit("clear-evm-chart");
wsNotify.eventBus.$emit("clear-lvgl-chart");
wsNotify.eventBus.$emit("clear-system-chart");
// 清空各个表格数据
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