diff --git a/core/constants.py b/core/constants.py index 5e5d18b..d413c03 100644 --- a/core/constants.py +++ b/core/constants.py @@ -1,8 +1,8 @@ UNITS = { - 'mm': 1 / 25.4, - 'cm': 1 / 2.54, - 'in': 1, - 'pt': 1 / 72., - 'pc': 1 / 6., - 'px': None, -} \ No newline at end of file + "mm": 1 / 25.4, + "cm": 1 / 2.54, + "in": 1, + "pt": 1 / 72.0, + "pc": 1 / 6.0, + "px": None, +} diff --git a/core/document.py b/core/document.py index 0e33a1a..4ba81f6 100644 --- a/core/document.py +++ b/core/document.py @@ -1,13 +1,23 @@ import io import os +import shutil +import tempfile import traceback -from zipfile import PyZipFile, BadZipFile +from pathlib import Path +from typing import Optional +from zipfile import BadZipFile, PyZipFile import cssselect2 from defusedxml import ElementTree from .constants import UNITS -from .resources import res_add_font, res_add_multimedia, res_add_drawparams, MultiMedias, Images +from .resources import ( + Images, + MultiMedias, + res_add_drawparams, + res_add_font, + res_add_multimedia, +) from .surface import * @@ -15,22 +25,23 @@ class OFDFile(object): """ OFD Ref:GBT_33190-2016_电子文件存储与交换格式版式文档.pdf """ + #: contains OFD file header data header = None #: references to document's resources resources = None - zf:PyZipFile + zf: PyZipFile def __init__(self, fobj): self.zf = fobj if isinstance(fobj, PyZipFile) else PyZipFile(fobj) - if getattr(fobj, 'filename', None): - self.zf.filename = getattr(fobj, 'filename') + if getattr(fobj, "filename", None): + self.zf.filename = getattr(fobj, "filename") # for info in self._zf.infolist(): # print(info) - self.node_tree = self.read_node('OFD.xml') + self.node_tree = self.read_node("OFD.xml") # parse node - self.document_node = self.read_node(self.node_tree['DocBody']['DocRoot'].text) + self.document_node = self.read_node(self.node_tree["DocBody"]["DocRoot"].text) self.document = OFDDocument(self.zf, self.document_node) # print_node_recursive(self.document_node) @@ -40,12 +51,15 @@ def read_node(self, location): root = cssselect2.ElementWrapper.from_xml_root(tree) return Node(root) - def draw_document(self, doc_num=0): + def draw_document(self, doc_num=0, destination: Optional[str] = None): document = self.document + destination = destination or "." + destination = Path(destination) + destination.mkdir(exist_ok=True, parents=True) paths = [] - for page in document.pages: - surface = Surface(page, os.path.split(self.zf.filename)[-1].strip('.ofd')) - paths.append(surface.draw(page)) + for i, page in enumerate(document.pages): + surface = Surface(page, os.path.split(self.zf.filename)[-1].strip(".ofd")) + paths.append(surface.draw(page, destination / Path(f"{surface.filename}_{i}.png"))) return paths @@ -53,62 +67,88 @@ class OFDDocument(object): def __init__(self, _zf, node, n=0): self.pages = [] self._zf = _zf - self.name = f'Doc_{n}' + self.name = f"Doc_{n}" self.node = node try: - self.physical_box = [float(i) for i in node['CommonData']['PageArea']['PhysicalBox'].text.split(' ')] + self.physical_box = [ + float(i) for i in node["CommonData"]["PageArea"]["PhysicalBox"].text.split(" ") + ] except: self.physical_box = [0.0, 0.0, 210.0, 140.0] self._parse_res() # print('Resources:', Fonts, Images) # assert len(node['CommonData']['TemplatePage']) == len(node['Pages']['Page']) - if isinstance(node['Pages']['Page'], list): - sorted_pages = sorted(node['Pages']['Page'], key=lambda x: int(x.attr['ID'])) + if isinstance(node["Pages"]["Page"], list): + sorted_pages = sorted(node["Pages"]["Page"], key=lambda x: int(x.attr["ID"])) else: - sorted_pages = [node['Pages']['Page']] + sorted_pages = [node["Pages"]["Page"]] sorted_tpls = [] - if 'TemplatePage' in node['CommonData']: - if isinstance(node['CommonData']['TemplatePage'], list): - sorted_tpls = sorted(node['CommonData']['TemplatePage'], key=lambda x: int(x.attr['ID'])) + if "TemplatePage" in node["CommonData"]: + if isinstance(node["CommonData"]["TemplatePage"], list): + sorted_tpls = sorted( + node["CommonData"]["TemplatePage"], key=lambda x: int(x.attr["ID"]) + ) else: - sorted_tpls = [node['CommonData']['TemplatePage']] + sorted_tpls = [node["CommonData"]["TemplatePage"]] seal_node = None - if f'{self.name}/Signs/Sign_0/SignedValue.dat' in _zf.namelist(): + if f"{self.name}/Signs/Sign_0/SignedValue.dat" in _zf.namelist(): try: - seal_file = OFDFile(io.BytesIO(_zf.read(f'{self.name}/Signs/Sign_0/SignedValue.dat'))) + seal_file = OFDFile( + io.BytesIO(_zf.read(f"{self.name}/Signs/Sign_0/SignedValue.dat")) + ) seal_node = seal_file.document.pages[0].page_node except BadZipFile as _: - print(f'BadZipFile: {self.name}/Signs/Sign_0/SignedValue.dat') + print(f"BadZipFile: {self.name}/Signs/Sign_0/SignedValue.dat") annots = None - if 'Annotations' in self.node: - annots = self.get_node_tree(self.name + '/' + self.node['Annotations'].text) + if "Annotations" in self.node: + annots = self.get_node_tree(self.name + "/" + self.node["Annotations"].text) for i, p in enumerate(sorted_pages): - page_id = p.attr['ID'] - page_node = self.get_node_tree(self.name + '/' + sorted_pages[i].attr['BaseLoc']) + page_id = p.attr["ID"] + page_node = self.get_node_tree(self.name + "/" + sorted_pages[i].attr["BaseLoc"]) annot_node = None if annots: - if isinstance(annots['Page'], list): - annot_page = next(iter([page for page in annots['Page'] if page.attr['PageID'] == page_id]), None) + if isinstance(annots["Page"], list): + annot_page = next( + iter([page for page in annots["Page"] if page.attr["PageID"] == page_id]), + None, + ) if annot_page: - annot_node = self.get_node_tree(self.name + '/Annots/' + annot_page['FileLoc'].text) - elif isinstance(annots['Page'], Node) and annots['Page'].attr['PageID'] == page_id: - annot_node = self.get_node_tree(self.name + '/Annots/' + annots['Page']['FileLoc'].text) + annot_node = self.get_node_tree( + self.name + "/Annots/" + annot_page["FileLoc"].text + ) + elif isinstance(annots["Page"], Node) and annots["Page"].attr["PageID"] == page_id: + annot_node = self.get_node_tree( + self.name + "/Annots/" + annots["Page"]["FileLoc"].text + ) tpl_node = None try: # get tpl_node from ID - tpl = [tpl for tpl in sorted_tpls if page_node['Template'].attr['TemplateID'] == tpl.attr['ID']][0] - tpl_node = self.get_node_tree(self.name + '/' + tpl.attr['BaseLoc']) + tpl = [ + tpl + for tpl in sorted_tpls + if page_node["Template"].attr["TemplateID"] == tpl.attr["ID"] + ][0] + tpl_node = self.get_node_tree(self.name + "/" + tpl.attr["BaseLoc"]) except: pass # fallback using sorted one. if tpl_node is None and i < len(sorted_tpls): - tpl_node = self.get_node_tree(self.name + '/' + sorted_tpls[i].attr['BaseLoc']) - - self.pages.append(OFDPage(self, f'Page_{i}', page_id, page_node, tpl_node, seal_node if i == 0 else None, - annot_node=annot_node)) + tpl_node = self.get_node_tree(self.name + "/" + sorted_tpls[i].attr["BaseLoc"]) + + self.pages.append( + OFDPage( + self, + f"Page_{i}", + page_id, + page_node, + tpl_node, + seal_node if i == 0 else None, + annot_node=annot_node, + ) + ) def get_node_tree(self, location): if location not in self._zf.namelist(): @@ -119,12 +159,16 @@ def get_node_tree(self, location): return Node(root) def _parse_res(self): - if 'DocumentRes' in self.node['CommonData']: - node = Node.from_zp_location(self._zf, f"{self.name}/{self.node['CommonData']['DocumentRes'].text}") + if "DocumentRes" in self.node["CommonData"]: + node = Node.from_zp_location( + self._zf, f"{self.name}/{self.node['CommonData']['DocumentRes'].text}" + ) self._parse_res_node(node) - if 'PublicRes' in self.node['CommonData']: - node = Node.from_zp_location(self._zf, f"{self.name}/{self.node['CommonData']['PublicRes'].text}") + if "PublicRes" in self.node["CommonData"]: + node = Node.from_zp_location( + self._zf, f"{self.name}/{self.node['CommonData']['PublicRes'].text}" + ) self._parse_res_node(node) def _parse_res_node(self, node): @@ -144,13 +188,22 @@ def _parse_res_node(self, node): class OFDPage(object): - def __init__(self, parent: OFDDocument, name, page_id, page_node, tpl_node, seal_node=None, annot_node=None): + def __init__( + self, + parent: OFDDocument, + name, + page_id, + page_node, + tpl_node, + seal_node=None, + annot_node=None, + ): self.parent = parent self.page_id = page_id - self.name = f'{parent.name}_{name}' + self.name = f"{parent.name}_{name}" self.physical_box = self.parent.physical_box - if 'Area' in page_node and 'PhysicalBox' in page_node['Area']: - self.physical_box = [float(i) for i in page_node['Area']['PhysicalBox'].text.split(' ')] + if "Area" in page_node and "PhysicalBox" in page_node["Area"]: + self.physical_box = [float(i) for i in page_node["Area"]["PhysicalBox"].text.split(" ")] self.tpl_node = tpl_node self.page_node = page_node self.seal_node = seal_node @@ -158,7 +211,6 @@ def __init__(self, parent: OFDDocument, name, page_id, page_node, tpl_node, seal class Surface(object): - def __init__(self, page, name, dpi=192): self.page = page self.dpi = dpi @@ -166,7 +218,7 @@ def __init__(self, page, name, dpi=192): @property def pixels_per_mm(self): - return self.dpi * UNITS['mm'] + return self.dpi * UNITS["mm"] def cairo_draw(self, cr, node): # Only draw known tags @@ -179,8 +231,12 @@ def cairo_draw(self, cr, node): print(traceback.format_exc()) pass return # no need to go deeper - if node.tag == 'Appearance': - boundary = [float(i) for i in node.attr['Boundary'].split(' ')] if 'Boundary' in node.attr else [0, 0, 0, 0] + if node.tag == "Appearance": + boundary = ( + [float(i) for i in node.attr["Boundary"].split(" ")] + if "Boundary" in node.attr + else [0, 0, 0, 0] + ) cr.save() cr.translate(boundary[0], boundary[1]) for child in node.children: @@ -188,7 +244,7 @@ def cairo_draw(self, cr, node): self.cairo_draw(cr, child) cr.restore() return - elif node.tag == 'Layer': + elif node.tag == "Layer": try: cairo_layer(node) except Exception as e: @@ -200,7 +256,7 @@ def cairo_draw(self, cr, node): # Only draw known tags self.cairo_draw(cr, child) - def draw(self, page): + def draw(self, page, path: Optional[str] = None) -> str: # 计算A4 210mm 192dpi 下得到的宽高 physical_width = self.page.physical_box[2] physical_height = self.page.physical_box[3] @@ -230,22 +286,22 @@ def draw(self, page): self.cr.translate(90, 8) self.cairo_draw(self.cr, self.page.seal_node) - path = f'{self.filename}_{page.name}.png' + path = path or f"{self.filename}_{page.name}.png" cairo_surface.write_to_png(path) cairo_surface.finish() return path CAIRO_TAGS = { - 'PathObject': cairo_path, - 'TextObject': cairo_text, - 'ImageObject': cairo_image, + "PathObject": cairo_path, + "TextObject": cairo_text, + "ImageObject": cairo_image, } RESOURCE_TAGS = { - 'Font': res_add_font, - 'MultiMedia': res_add_multimedia, - 'DrawParams': res_add_drawparams, + "Font": res_add_font, + "MultiMedia": res_add_multimedia, + "DrawParams": res_add_drawparams, } @@ -257,9 +313,11 @@ def __init__(self, element): self.children = [] self.text = node.text - self.tag = (element.local_name - if element.namespace_url in ('', 'http://www.ofdspec.org/2016') else - '{%s}%s' % (element.namespace_url, element.local_name)) + self.tag = ( + element.local_name + if element.namespace_url in ("", "http://www.ofdspec.org/2016") + else "{%s}%s" % (element.namespace_url, element.local_name) + ) self.attr = node.attrib for child in element.iter_children(): child_node = Node(child) @@ -282,10 +340,10 @@ def from_zp_location(zf, location): return Node(root) def __repr__(self): - return f'Tag: {self.tag}, Attr: {self.attr}, Text: {self.text}' + return f"Tag: {self.tag}, Attr: {self.attr}, Text: {self.text}" def print_node_recursive(node, depth=0): - print(' ' * depth, node) + print(" " * depth, node) for child in node.children: print_node_recursive(child, depth=depth + 1) diff --git a/core/resources.py b/core/resources.py index a86d58d..3dad335 100644 --- a/core/resources.py +++ b/core/resources.py @@ -1,13 +1,14 @@ +import os import platform + import gi -import os gi.require_version("Gtk", "3.0") -gi.require_version('PangoCairo', '1.0') -from gi.repository import PangoCairo -import cairo -from subprocess import Popen, PIPE +gi.require_version("PangoCairo", "1.0") +from subprocess import PIPE, Popen +import cairo +from gi.repository import PangoCairo Fonts = {} MultiMedias = {} @@ -16,14 +17,21 @@ font_map = PangoCairo.font_map_get_default() Cairo_Font_Family_Names = [f.get_name() for f in font_map.list_families()] # print(Cairo_Font_Family_Names) -print([f.get_name() for f in font_map.list_families() if - 'sun' in f.get_name().lower() or 'cour' in f.get_name().lower() or 'kai' in f.get_name().lower()]) +# print( +# [ +# f.get_name() +# for f in font_map.list_families() +# if "sun" in f.get_name().lower() +# or "cour" in f.get_name().lower() +# or "kai" in f.get_name().lower() +# ] +# ) OFD_FONT_MAP = { - '楷体': ['KaiTi', 'Kai'], - 'KaiTi': ['KaiTi', 'Kai'], - '宋体': ['SimSun', 'FangSong', 'STSong'], - 'Courier New': ['Courier New', 'Courier'], + "楷体": ["KaiTi", "Kai"], + "KaiTi": ["KaiTi", "Kai"], + "宋体": ["SimSun", "FangSong", "STSong"], + "Courier New": ["Courier New", "Courier"], } @@ -31,18 +39,19 @@ class ResNotFoundException(Exception): """ 资源文件找不到 """ + pass class Font(object): - ID = '' - FontName = '' - FamilyName = '' + ID = "" + FontName = "" + FamilyName = "" def __init__(self, attr): - self.ID = attr['ID'] if 'ID' in attr else '' - self.FontName = attr['FontName'] if 'FontName' in attr else '' - self.FamilyName = attr['FamilyName'] if 'FamilyName' in attr else '' + self.ID = attr["ID"] if "ID" in attr else "" + self.FontName = attr["FontName"] if "FontName" in attr else "" + self.FamilyName = attr["FamilyName"] if "FamilyName" in attr else "" def get_font_family(self): # fixme: 印章的Font只有FontName, 沒有FamilyName @@ -51,18 +60,18 @@ def get_font_family(self): for c in candidates: if c in Cairo_Font_Family_Names: return c - raise ResNotFoundException(f'OFD字体文件[{self.FontName}] 找不到') + # raise ResNotFoundException(f"Can't find '{self.FontName}' font file") return self.FontName def __repr__(self): - return f'ID:{self.ID}, FontName:{self.FontName} FamilyName:{self.FamilyName}, System:{self.get_font_family()}' + return f"ID:{self.ID}, FontName:{self.FontName} FamilyName:{self.FamilyName}, System:{self.get_font_family()}" class MultiMedia(object): def __init__(self, node): - self.ID = node.attr['ID'] - self.Type = node.attr['Type'] - self.location = node['MediaFile'].text + self.ID = node.attr["ID"] + self.Type = node.attr["Type"] + self.location = node["MediaFile"].text @staticmethod def parse_from_node(node): @@ -73,25 +82,23 @@ class Image(MultiMedia): def __init__(self, node, _zf): super().__init__(node) self.png_location = None - self.Format = node.attr['Format'] if 'Format' in node.attr else '' - suffix = self.location.split('.')[-1] - if suffix == 'jb2': - # print('tempdir', tempfile.gettempdir()) + self.Format = node.attr["Format"] if "Format" in node.attr else "" + suffix = self.location.split(".")[-1] + if suffix == "jb2": jb2_path = [loc for loc in _zf.namelist() if self.location in loc][0] - tmp_folder = os.path.basename(_zf.filename).replace('.ofd', '') + tmp_folder = os.path.basename(_zf.filename).replace(".ofd", "") x_path = _zf.extract(jb2_path, tmp_folder) - png_path = x_path.replace('.jb2', '.png') - if platform.system() == 'Windows': - Popen(['./bin/jbig2dec', '-o', png_path, x_path], stdout=PIPE) + png_path = x_path.replace(".jb2", ".png") + if platform.system() == "Windows": + Popen(["./bin/jbig2dec", "-o", png_path, x_path], stdout=PIPE) else: - Popen(['jbig2dec', '-o', png_path, x_path], stdout=PIPE) + Popen(["jbig2dec", "-o", png_path, x_path], stdout=PIPE) - # print(f'jbig2dec {png_path}', output.stdout.read()) self.png_location = png_path - elif suffix == 'png': + elif suffix == "png": png_path = [loc for loc in _zf.namelist() if self.location in loc][0] - tmp_folder = os.path.basename(_zf.filename).replace('.ofd', '') + tmp_folder = os.path.basename(_zf.filename).replace(".ofd", "") x_path = _zf.extract(png_path, tmp_folder) self.png_location = x_path @@ -101,37 +108,58 @@ def get_cairo_surface(self): return None def __repr__(self): - return f'Image ID:{self.ID}, Format:{self.Format}' + return f"Image ID:{self.ID}, Format:{self.Format}" class DrawParam(object): def __init__(self, node=None): - self.ID = node.attr.get('ID', None) if node else None - self.line_width = node.attr.get('LineWidth', 0.25) if node else 0.25 - - self.stroke_color = next(iter( - [[float(i) / 256. for i in child.attr['Value'].split(' ')] - for child in node.children if child.tag == 'StrokeColor' and 'Value' in child.attr] - ), [0, 0, 0]) if node else [0, 0, 0] - self.fill_color = next(iter( - [[float(i) / 256. for i in child.attr['Value'].split(' ')] - for child in node.children if child.tag == 'FillColor' and 'Value' in child.attr] - ), [0, 0, 0]) if node else [0, 0, 0] + self.ID = node.attr.get("ID", None) if node else None + self.line_width = node.attr.get("LineWidth", 0.25) if node else 0.25 + + self.stroke_color = ( + next( + iter( + [ + [float(i) / 256.0 for i in child.attr["Value"].split(" ")] + for child in node.children + if child.tag == "StrokeColor" and "Value" in child.attr + ] + ), + [0, 0, 0], + ) + if node + else [0, 0, 0] + ) + self.fill_color = ( + next( + iter( + [ + [float(i) / 256.0 for i in child.attr["Value"].split(" ")] + for child in node.children + if child.tag == "FillColor" and "Value" in child.attr + ] + ), + [0, 0, 0], + ) + if node + else [0, 0, 0] + ) # print(self) def __repr__(self): - return f'ID[{self.ID}], line_width: {self.line_width}, stroke{self.stroke_color}, fill{self.fill_color}' + return f"ID[{self.ID}], line_width: {self.line_width}, stroke{self.stroke_color}, fill{self.fill_color}" + def res_add_font(node, _zf): - Fonts[node.attr['ID']] = Font(node.attr) + Fonts[node.attr["ID"]] = Font(node.attr) def res_add_multimedia(node, _zf): - if node.attr['Type'] == 'Image': + if node.attr["Type"] == "Image": image = Image(node, _zf) - Images[node.attr['ID']] = image + Images[node.attr["ID"]] = image def res_add_drawparams(node, _zf): for draw_param in node.children: - DrawParams[draw_param.attr['ID']] = DrawParam(draw_param) \ No newline at end of file + DrawParams[draw_param.attr["ID"]] = DrawParam(draw_param) diff --git a/core/surface.py b/core/surface.py index e227575..49fbcf0 100644 --- a/core/surface.py +++ b/core/surface.py @@ -1,23 +1,31 @@ -from math import pi, sin, cos, hypot, atan2, radians import re +from math import atan2, cos, hypot, pi, radians, sin + import gi -from .resources import Fonts, Images, DrawParams, DrawParam +from .resources import DrawParam, DrawParams, Fonts, Images gi.require_version("Gtk", "3.0") -gi.require_version('PangoCairo', '1.0') -from gi.repository import Pango, PangoCairo +gi.require_version("PangoCairo", "1.0") import cairo +from gi.repository import Pango, PangoCairo SCALE_192 = 7.559 SCALE_128 = 5.039 -COMMANDS = set('SMLQBAC') +COMMANDS = set("SMLQBAC") COMMAND_RE = re.compile(r"([SMLQBAC])") FLOAT_RE = re.compile(r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?") font_map = PangoCairo.font_map_get_default() -print([f.get_name() for f in font_map.list_families() if - 'song' in f.get_name().lower() or 'cour' in f.get_name().lower() or 'kai' in f.get_name().lower()]) +# print( +# [ +# f.get_name() +# for f in font_map.list_families() +# if "song" in f.get_name().lower() +# or "cour" in f.get_name().lower() +# or "kai" in f.get_name().lower() +# ] +# ) # https://github.com/Kozea/CairoSVG/blob/main/cairosvg/helpers.py#L95 @@ -39,7 +47,9 @@ def _tokenize_path(pathdef): yield token -def _draw_AbbreviatedData(draw, boundary, path, fillColor=(128, 128, 128), lineWidth=2, scale=SCALE_192): +def _draw_AbbreviatedData( + draw, boundary, path, fillColor=(128, 128, 128), lineWidth=2, scale=SCALE_192 +): x_start = boundary[0] y_start = boundary[1] current_pos = (x_start, y_start) @@ -51,21 +61,21 @@ def _draw_AbbreviatedData(draw, boundary, path, fillColor=(128, 128, 128), lineW if elements[-1] in COMMANDS: command = elements.pop() else: - raise Exception('操作符违法') + raise Exception("操作符违法") - if command == 'M': + if command == "M": x = scale * float(elements.pop()) y = scale * float(elements.pop()) pos = (x_start + x, y_start + y) current_pos = pos - elif command == 'L': + elif command == "L": x = scale * float(elements.pop()) y = scale * float(elements.pop()) pos = (x_start + x, y_start + y) draw.line(current_pos + pos, fill=fillColor, width=lineWidth) - elif command == 'B': + elif command == "B": pass @@ -82,14 +92,14 @@ def _cairo_draw_path(cr, boundary, path): if elements[-1] in COMMANDS: command = elements.pop() else: - raise Exception(f'操作符 {elements[-1]} 违法') + raise Exception(f"操作符 {elements[-1]} 违法") - if command == 'M': + if command == "M": x = float(elements.pop()) y = float(elements.pop()) cr.move_to(x, y) current_pos = (x, y) - elif command == 'L': + elif command == "L": x = float(elements.pop()) y = float(elements.pop()) # pos = (x_start + x, y_start + y) @@ -97,7 +107,7 @@ def _cairo_draw_path(cr, boundary, path): current_pos = (x, y) # draw.line(current_pos + pos, fill=fillColor, width=lineWidth) - elif command == 'B': + elif command == "B": x1 = float(elements.pop()) y1 = float(elements.pop()) x2 = float(elements.pop()) @@ -106,12 +116,14 @@ def _cairo_draw_path(cr, boundary, path): y3 = float(elements.pop()) cr.curve_to(x1, y1, x2, y2, x3, y3) current_pos = (x3, y3) - elif command == 'A': + elif command == "A": # rx ry x-axis-rotation large-arc-flag sweep-flag x y # A 1.875 1.875 90 0 1 0.125 2 # GBT_33190-2016_电子文件存储与交换格式版式文档.pdf #9.3.5 # https://github.com/Kozea/CairoSVG/blob/main/cairosvg/path.py#L209 - ellipse_x, ellipse_y, rotation_angle, large, sweep, x3, y3 = [elements.pop() for _ in range(7)] + ellipse_x, ellipse_y, rotation_angle, large, sweep, x3, y3 = [ + elements.pop() for _ in range(7) + ] rx, ry = float(ellipse_x), float(ellipse_y) rotation = radians(float(rotation_angle)) large, sweep = int(large), int(sweep) @@ -134,7 +146,7 @@ def _cairo_draw_path(cr, boundary, path): # Find one circle centre xc = xe / 2 - yc = (rx ** 2 - xc ** 2) ** .5 + yc = (rx**2 - xc**2) ** 0.5 # Choose between the two circles according to flags if not (large ^ sweep): @@ -158,14 +170,14 @@ def _cairo_draw_path(cr, boundary, path): arc(xc, yc, rx, angle1, angle2) cr.restore() current_pos = (current_pos[0] + x3, current_pos[1] + y3) - elif command == 'Q': + elif command == "Q": x1 = float(elements.pop()) y1 = float(elements.pop()) x2 = float(elements.pop()) y2 = float(elements.pop()) cr.curve_to(x1, y1, x1, y1, x2, y2) current_pos = (x2, y2) - elif command == 'C': + elif command == "C": pass @@ -174,7 +186,7 @@ def _trans_Delta(elements, scale=SCALE_192): elements.reverse() while elements: e = elements.pop() - if e == 'g': + if e == "g": c = int(elements.pop()) v = float(elements.pop()) parsed += c * [v * scale] @@ -189,7 +201,7 @@ def _trans_Delta(elements, scale=SCALE_192): def cairo_layer(node): global layer_draw - layer_drawparam = node.attr.get('DrawParam', None) + layer_drawparam = node.attr.get("DrawParam", None) if layer_drawparam in DrawParams: layer_draw = DrawParams.get(layer_drawparam) print(layer_draw) @@ -199,18 +211,18 @@ def cairo_layer(node): def cairo_path(cr: cairo.Context, node): lineWidth = layer_draw.line_width if layer_draw.line_width else 0.5 - lineWidth = float(node.attr['LineWidth']) if 'LineWidth' in node.attr else lineWidth - boundary = [float(i) for i in node.attr['Boundary'].split(' ')] + lineWidth = float(node.attr["LineWidth"]) if "LineWidth" in node.attr else lineWidth + boundary = [float(i) for i in node.attr["Boundary"].split(" ")] ctm = None - if 'CTM' in node.attr: - ctm = [float(i) for i in node.attr['CTM'].split(' ')] + if "CTM" in node.attr: + ctm = [float(i) for i in node.attr["CTM"].split(" ")] fillColor = layer_draw.fill_color - if 'FillColor' in node and 'Value' in node.attr: - fillColor = [float(i) / 256. for i in node['FillColor'].attr['Value'].split(' ')] + if "FillColor" in node and "Value" in node.attr: + fillColor = [float(i) / 256.0 for i in node["FillColor"].attr["Value"].split(" ")] using_fill_color = sum(fillColor) > 0.0 strokeColor = layer_draw.stroke_color - if 'StrokeColor' in node and 'Value' in node.attr: - strokeColor = [float(i) / 256. for i in node['StrokeColor'].attr['Value'].split(' ')] + if "StrokeColor" in node and "Value" in node.attr: + strokeColor = [float(i) / 256.0 for i in node["StrokeColor"].attr["Value"].split(" ")] # print('draw path', boundary, fillColor, strokeColor) cr.save() if ctm: @@ -224,7 +236,7 @@ def cairo_path(cr: cairo.Context, node): else: cr.translate(boundary[0], boundary[1]) - AbbreviatedData = node['AbbreviatedData'].text + AbbreviatedData = node["AbbreviatedData"].text if using_fill_color: cr.set_source_rgba(*fillColor) else: @@ -236,45 +248,45 @@ def cairo_path(cr: cairo.Context, node): def cairo_text(cr: cairo.Context, node): - boundary = [float(i) for i in node.attr['Boundary'].split(' ')] + boundary = [float(i) for i in node.attr["Boundary"].split(" ")] ctm = None - if 'CTM' in node.attr: - ctm = [float(i) for i in node.attr['CTM'].split(' ')] - font_id = node.attr['Font'] + if "CTM" in node.attr: + ctm = [float(i) for i in node.attr["CTM"].split(" ")] + font_id = node.attr["Font"] font_family = get_font_from_id(font_id).get_font_family() - font_size = float(node.attr['Size']) / 1.3 + font_size = float(node.attr["Size"]) / 1.3 fillColor = layer_draw.fill_color - if 'FillColor' in node and 'Value' in node['FillColor'].attr: - fillColor = [float(i) / 255. for i in node['FillColor'].attr['Value'].split(' ')] + if "FillColor" in node and "Value" in node["FillColor"].attr: + fillColor = [float(i) / 255.0 for i in node["FillColor"].attr["Value"].split(" ")] strokeColor = layer_draw.stroke_color - if 'StrokeColor' in node and 'Value' in node['StrokeColor'].attr: - strokeColor = [float(i) / 255. for i in node['StrokeColor'].attr['Value'].split(' ')] + if "StrokeColor" in node and "Value" in node["StrokeColor"].attr: + strokeColor = [float(i) / 255.0 for i in node["StrokeColor"].attr["Value"].split(" ")] - TextCode = node['TextCode'] + TextCode = node["TextCode"] text = TextCode.text # print(f'cario text {text}, {font_id}') deltaX = None deltaY = None - if 'DeltaX' in TextCode.attr: - deltaX = _trans_Delta(TextCode.attr['DeltaX'].split(' '), scale=1) + if "DeltaX" in TextCode.attr: + deltaX = _trans_Delta(TextCode.attr["DeltaX"].split(" "), scale=1) if deltaX and len(deltaX) + 1 != len(text): # raise Exception(f'{text} TextCode DeltaX 与字符个数不符') - deltaX = deltaX[:len(text)-1] + deltaX = deltaX[: len(text) - 1] if deltaX and len(deltaX) < len(text) - 1: deltaX.extend([deltaX[-1]] * (len(text) - 1 - len(deltaX))) - if 'DeltaY' in TextCode.attr: - deltaY = _trans_Delta(TextCode.attr['DeltaY'].split(' '), scale=1) + if "DeltaY" in TextCode.attr: + deltaY = _trans_Delta(TextCode.attr["DeltaY"].split(" "), scale=1) if deltaY and len(deltaY) + 1 != len(text): # raise Exception(f'{text} TextCode DeltaY 与字符个数不符') - deltaY = deltaY[:len(text)-1] + deltaY = deltaY[: len(text) - 1] if deltaY and len(deltaY) < len(text) - 1: deltaY.extend([deltaY[-1]] * (len(text) - 1 - len(deltaY))) - X = float(TextCode.attr['X']) - Y = float(TextCode.attr['Y']) + X = float(TextCode.attr["X"]) + Y = float(TextCode.attr["Y"]) for idx, rune in enumerate(text): cr.save() # cr.identity_matrix() @@ -307,11 +319,11 @@ def cairo_text(cr: cairo.Context, node): def cairo_image(cr: cairo.Context, node): - resource_id = node.attr['ResourceID'] - boundary = [float(i) for i in node.attr['Boundary'].split(' ')] + resource_id = node.attr["ResourceID"] + boundary = [float(i) for i in node.attr["Boundary"].split(" ")] ctm = None - if 'CTM' in node.attr: - ctm = [float(i) for i in node.attr['CTM'].split(' ')] + if "CTM" in node.attr: + ctm = [float(i) for i in node.attr["CTM"].split(" ")] img_surface = get_res_image(resource_id).get_cairo_surface() cr.save() @@ -322,7 +334,9 @@ def cairo_image(cr: cairo.Context, node): x, y = cr.get_matrix().transform_point(x, y) # 画图片是fillparent,自己重新计算缩放matrix, 同时缩放基础点x,y - matrix = cairo.Matrix(width / img_surface.get_width(), 0, 0, height / img_surface.get_height(), 0, 0) + matrix = cairo.Matrix( + width / img_surface.get_width(), 0, 0, height / img_surface.get_height(), 0, 0 + ) cr.identity_matrix() cr.set_matrix(matrix) matrix.invert() diff --git a/ofd_test.py b/ofd_test.py index 708f82a..6359a32 100644 --- a/ofd_test.py +++ b/ofd_test.py @@ -1,11 +1,11 @@ from core.document import OFDFile import os -folder = 'ofds' +folder = "ofds" for path in os.listdir(folder): - if not path.endswith('.ofd'): + if not path.endswith(".ofd"): continue - print('read file', path) + print("read file", path) file_path = os.path.join(folder, path) doc = OFDFile(file_path) doc.draw_document() diff --git a/requirements.txt b/requirements.txt index 7ff4dd2..34e4e81 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -defusedxml -cssselect2 -pillow +cssselect2~=0.4 +defusedxml~=0.7