手撸php框架4.2--构建BaseObject之setter,getter实现

本文介绍如何利用PHP的魔术方法__set、__get、__isset和__unset实现类属性的动态setter和getter方法,并通过示例代码展示其用法。

一般来说,类具有属性和方法,我们把属性对应于类的成员变量,其中成员变量是相对于类内部而言的,对于使用这个类的人来说,我们看到的是类的属性。所以嘞,我们就可以通过setter和getter的方式,把一个类中的方法作为类的属性供外部使用。例如:

class AAA extends BaseObject
{
    public function setPro1()
    {}

    pubic function getPro1()
    {}
}
$a = new AAA();
我们就可以使用$a->pro1 = 'aaa', var_dump($a->pro1)来设置和获取属性pro1。

1.通过重写__set魔术方法,实现setter,代码如下:

public function __set($name, $value)
{
   $setter = 'set'.ucfirst($name);
   $getter = 'get'.ucfirst($name);

   if (method_exists($this, $setter)) {
       return $this->$setter($value);
   } else if (method_exists($this, $getter)) {
       throw new Exception('read-only property!');
   } else {
       throw new Exception('unknown property!');
   }
}

2.通过重写__get魔术方法,实现getter,代码如下:

public function __get($name)
{
    $setter = 'set'.ucfirst($name);
    $getter = 'get'.ucfirst($name);

    if (method_exists($this, $getter)) {
        return $this->$getter();
    } else if (method_exists($this, $setter)) {
        throw new Exception('write-only property!');
    } else {
        throw new Exception('unknown property!');
    }
}

最后我们还要考虑到,isset()和unset()的问题,所以要重写__isset和__unset魔术方法,代码如下:

public function __isset($name)
{
    $getter = 'get'.ucfirst($name);

    if (method_exists($this, $getter)) {
        return $this->$getter() !== null;
    } else {
        return false;
    }
}

public function __unset($name)
{
    $setter = 'set'.ucfirst($name);
    $getter = 'get'.ucfirst($name);

    if (method_exists($this, $setter)) {
        return $this->$setter(null);
    } else if (method_exists($this, $getter)) {
        throw new Exception('read-only property!');
    }
}

ok,今儿个先到这,欲知下事如何,且听下回分解……
github源码:https://github.com/2lovecode/tank

class DocumentBlockObject(BaseObject): """文档块对象 文档块是一个文档从结构上应该视为一个整体的部分,整个文档由这样多个块构成。 在不同的文档对应的部分不同,word中指代整个word。在Excel中指代一个表单,因为每个表单承载了独立的数据。 """ def __init__(self): self._file_name = "" # 式样书的名称 self._name = '' # block名称 self._type = "block" self._header = [] # 页眉 self._footer = [] # 页脚 self._elements = [] # 所有元素的对象列表,按文档顺序装载。例:[TextObject, TableObject] self._texts: list[TextObject] = [] # 文本对象列表,按文档顺序装载。例:[TextObject] self._tables: list[TableObject] = [] # 表格对象列表,按文档顺序装载。例:[TableObject] self._pictures: list[PictureObject] = [] # 图片对象列表,按文档顺序装载rId。例:[PictureObject] self._graphics: list[GraphicObject] = [] # 图形对象列表,按文档顺序装载。例:[GraphicObject] self._timing_waves = [] # 时序图对象列表 例:[TimingWaveObject] self._timing_texts = [] # 时序图行文本对象列表 例:[TimingTextObject] self._settings = [] # block级别的属性信息 self._layouts = [] # block级别的布局信息 self._styles = [] # block级别的样式信息 self._data_id = 0 # 数据的唯一id def to_dict(self): """ 将 DocumentBlockObject 对象转换为字典 """ return { "file_name": self._file_name, "name": self._name, "type": self._type, "header": [[element.to_dict() for element in header_list] for header_list in self._header], # 假设列表中的元素为基本数据类型 "footer": [[element.to_dict() for element in footer_list] for footer_list in self._footer], "elements": [element.to_dict() for element in self._elements], # 处理嵌套对象列表 "texts": [text.to_dict() for text in self._texts], # 文本对象列表 "tables": [table.to_dict() for table in self._tables], # 表格对象列表 "pictures": [picture.to_dict() for picture in self._pictures], # 图片对象列表 "graphics": [graphic.to_dict() for graphic in self._graphics], # 图形对象列表 "timing_waves": [timing_wave.to_dict() for timing_wave in self._timing_waves], # 时序图对象列表 "timing_texts": [timing_text.to_dict() for timing_text in self._timing_texts], # 时序图行文本列表 "settings": self._settings, "layouts": [layout.to_dict() for layout in self._layouts], "styles": [style.to_dict() for style in self._styles], "data_id": self._data_id, # "position": self._position.to_dict() } @classmethod def from_dict(cls, data): """ 从字典创建 DocumentBlockObject 实例 """ obj = cls() obj._file_name = data.get("file_name", "") obj._name = data.get("name", '') obj._type = data.get("type", "block") obj._header = data.get("header", []) obj._footer = data.get("footer", []) # obj._elements = [TextObject.from_dict(e) if e.get("type") == "text" # else TableObject.from_dict(e) if e.get("type") == "table" # else PictureObject.from_dict(e) if e.get("type") == "picture" # else GraphicObject.from_dict(e) if e.get("type") == "graphic" # else e for e in data.get("elements", [])] obj._texts = [TextObject.from_dict(t) for t in data.get("texts", [])] obj._tables = [TableObject.from_dict(t) for t in data.get("tables", [])] obj._pictures = [PictureObject.from_dict(p) for p in data.get("pictures", [])] obj._graphics = [GraphicObject.from_dict(g) for g in data.get("graphics", [])] obj._timing_waves = [TimingWaveObject.from_dict(w) for w in data.get("timing_waves", [])] obj._timing_texts = [TimingTextObject.from_dict(t) for t in data.get("timing_texts", [])] obj._settings = data.get("settings", []) obj._layouts = [LayoutObject.from_dict(l) for l in data.get("layouts", [])] obj._styles = [StyleObject.from_dict(s) for s in data.get("styles", [])] obj._data_id = data.get("data_id", 0) # obj._position = Position.from_dict(data.get("position", {})) return obj def __repr__(self): return f'{self.__class__.__name__}()[NAME="{self._name}"]' def __str__(self): return self._name @property def id(self): return self._data_id @id.setter def id(self, new_value): assert type(new_value) == int self._data_id = new_value @property def name(self): return self._name @name.setter def name(self, new_value): assert type(new_value) == str self._name = new_value @property def file_name(self): return self._file_name @file_name.setter def file_name(self, new_value): assert type(new_value) is str self._file_name = new_value @property def elements(self): return self._elements @elements.setter def elements(self, new_value): assert type(new_value) == list self._elements = new_value @property def texts(self): return self._texts @texts.setter def texts(self, new_value): assert type(new_value) == list self._texts = new_value @property def header(self): return self._header @header.setter def header(self, new_value): assert type(new_value) is list self._header = new_value @property def footer(self): return self._footer @footer.setter def footer(self, new_value): assert type(new_value) is list self._footer = new_value @property def tables(self): return self._tables @tables.setter def tables(self, new_value): assert type(new_value) == list self._tables = new_value @property def pictures(self): return self._pictures @pictures.setter def pictures(self, new_value): assert type(new_value) == list self._pictures = new_value @property def graphics(self): return self._graphics @graphics.setter def graphics(self, new_value): assert type(new_value) == list self._graphics = new_value @property def timing_waves(self): return self._timing_waves @timing_waves.setter def timing_waves(self, new_value): assert type(new_value) == list self._timing_waves = new_value @property def timing_texts(self): return self._timing_texts @timing_texts.setter def timing_texts(self, new_value): assert type(new_value) == list self._timing_texts = new_value @property def settings(self): return self._settings @settings.setter def settings(self, new_value): assert type(new_value) == list self._settings = new_value @property def layouts(self): return self._layouts @layouts.setter def layouts(self, new_value): assert type(new_value) == list self._layouts = new_value @property def styles(self): return self._styles @styles.setter def styles(self, new_value): assert type(new_value) == list self._styles = new_value def add_text(self, text_object): """添加文本对象 :param text_object: 文本对象 """ assert type(text_object) in [list, TextObject] if text_object: if isinstance(text_object, list): for obj in text_object: self.id += 1 obj.data_id = self.id self._texts.extend(text_object) self._elements.extend(text_object) else: self.id += 1 text_object.data_id = self.id self._texts.append(text_object) self._elements.append(text_object) def add_table(self, table_object): """添加表格对象 :param table_object: 表格对象 """ assert type(table_object) in [list, TableObject] if table_object: if isinstance(table_object, list): for table in table_object: self.id += 1 table.data_id = self.id self.align_table_col(table) self._tables.extend(table_object) self._elements.extend(table_object) else: self.id += 1 table_object.data_id = self.id self.align_table_col(table_object) self._tables.append(table_object) self._elements.append(table_object) def add_picture(self, picture_object, is_in_table=False): """添加图片对象 :param picture_object: 图片对象 """ assert type(picture_object) in [list, PictureObject] if picture_object: if isinstance(picture_object, list): for obj in picture_object: self.id += 1 obj.data_id = self.id if not is_in_table: self._pictures.extend(picture_object) self._elements.extend(picture_object) else: self.id += 1 picture_object.data_id = self.id if not is_in_table: self._pictures.append(picture_object) self._elements.append(picture_object) def add_graphic(self, graphic_object, is_in_table=False): """添加图形对象 :param graphic_object: 图形对象 """ assert type(graphic_object) in [list, GraphicObject] if graphic_object: if isinstance(graphic_object, list): for obj in graphic_object: self.id += 1 obj.data_id = self.id if not is_in_table: self._graphics.extend(graphic_object) self._elements.extend(graphic_object) else: self.id += 1 graphic_object.data_id = self.id if not is_in_table: self._graphics.append(graphic_object) self._elements.append(graphic_object) def add_timing_wave(self, object): """ 添加时序图对象 :param object: 时序图对象 """ assert type(object) in [list, TimingWaveObject] if object: if isinstance(object, list): for obj in object: self.id += 1 obj.data_id = self.id self._timing_waves.extend(object) self._elements.extend(object) else: self.id += 1 object.data_id = self.id self._timing_waves.append(object) self._elements.append(object) def add_timing_text(self, object): """ 添加时序图对象 :param object: 时序图对象 """ assert type(object) in [list, TimingTextObject] if object: if isinstance(object, list): for obj in object: self.id += 1 obj.data_id = self.id self._timing_texts.extend(object) self._elements.extend(object) else: self.id += 1 object.data_id = self.id self._timing_texts.append(object) self._elements.append(object) def align_table_col(self, base_table): max_col_count = max([len(row.cells) for row in base_table.rows]) for base_row in base_table.rows: if len(base_row.cells) != max_col_count: # 匹配行的列数不一致,补齐缺失的cell add_col_count = abs(len(base_row.cells) - max_col_count) base_row.cells.extend([CellObject() for _ in range(add_col_count)]) def get_chapter_content(self, text_obj: TextObject) -> List[Union[str, list]]: """ 获取word文档中一个章节标题对象下的所有子内容, 返回列表。 如变更履历,获取"变更履历"章节下的文本等内容 (只考虑文本、表格) """ para_text = "" table_data = [] total_result = [] # 是章节标题 if getattr(text_obj.layout, "chapter_id", None): cur_idx = self._elements.index(text_obj) + 1 while cur_idx < len(self._elements): cur_obj = self._elements[cur_idx] if isinstance(cur_obj, TextObject): # 遇到下一个章节标题,则停止 if getattr(cur_obj.layout, "chapter_id", None): break para_text += cur_obj.text + "\n" if table_data: table_data = [] elif isinstance(cur_obj, TableObject): if para_text: total_result.append(para_text) para_text = "" for row in cur_obj.rows: row_data = [] for cell in row.cells: row_data.append(cell.text) table_data.append(row_data) total_result.append(table_data) cur_idx += 1 if para_text: total_result.append(para_text) total_result = [i.strip("\n") if isinstance(i, str) else i for i in total_result] return total_result @staticmethod def is_change_resume( obj: Union[TextObject, PictureObject, GraphicObject, TableObject, RowObject, CellObject]) -> bool: """ 判断当前对象是否为变更履历下面的内容,返回bool类型 兼容word & excel """ parent_node = None # ""/None/TextObject/DocumentBlockObject # 文本、图片、图形 if isinstance(obj, (TextObject, PictureObject, GraphicObject, TableObject)): parent_node = obj.layout.parent_ref # 表格行对象 elif isinstance(obj, RowObject): row_parent = obj.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref # 单元格对象 elif isinstance(obj, CellObject): cell_parent = obj.layout.parent_ref if cell_parent: row_parent = cell_parent.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref if isinstance(parent_node, TextObject): # word # 兼容多级别章节标题 while parent_node: if any([True if i in parent_node.text else False for i in CHANGE_RESUME]): return True parent_node = parent_node.layout.parent_ref if isinstance(parent_node, DocumentBlockObject): return False else: return False elif isinstance(parent_node, DocumentBlockObject): # excel return any([True if i in parent_node.name else False for i in CHANGE_RESUME]) return False @staticmethod def get_chapter(obj: Union[TextObject, PictureObject, GraphicObject, TableObject, RowObject, CellObject]): """ 获取通用对象的章节文本对象 """ parent_node = None # 文本、图片、图形 if isinstance(obj, (TextObject, PictureObject, GraphicObject, TableObject)): parent_node = obj.layout.parent_ref # 表格行对象 elif isinstance(obj, RowObject): row_parent = obj.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref # 单元格对象 elif isinstance(obj, CellObject): cell_parent = obj.layout.parent_ref if cell_parent: row_parent = cell_parent.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref return parent_node 绘制出类图
最新发布
10-30
""" @Author : 王定雄 @Date : 2024-10-29 @Desc : 文本对象的数据结构的定义 """ import logging from kotei_omp.data.base_object import BaseObject from kotei_omp.data.layout import LayoutObject from kotei_omp.data.position import Position from kotei_omp.data.style import StyleObject from kotei_omp.data.coordinate import CoordinateObject from kotei_i18n.api import _ class CharObject(BaseObject): def __init__(self, char, style): self.char = char self.style = style def is_same(self, char_obj) -> bool: return self.char == char_obj.char and self.style == char_obj.style class TextObject(BaseObject): """文本对象""" def __init__(self): self._type = "text" self._text = '' # 完整内容 self._style = StyleObject() # 文本样式 self._layout = LayoutObject() # 文本布局对象 self._coordinate = CoordinateObject() # 文本的坐标 self._runs = [] # 文本片段对象列表,所有内容连起来就是完整内容 self._data_id = None # 唯一标识 self._position = Position() def to_dict(self): """ 将 TextObject 对象转换为字典 """ return { "type": self._type, "text": self._text, "style": self._style.to_dict(), # 调用 StyleObject 的 to_dict 方法 "layout": self._layout.to_dict(), # 调用 LayoutObject 的 to_dict 方法 "coordinate": self._coordinate.to_dict(), # 调用 CoordinateObject 的 to_dict 方法 "runs": [run.to_dict() for run in self._runs], # 递归转换 RunObject 列表 "data_id": self._data_id, "position": self._position.to_dict() } @classmethod def from_dict(cls, data): """ 从字典创建 TextObject 实例 """ obj = cls() obj._type = data.get("type", "text") obj._text = data.get("text", '') obj._style = StyleObject.from_dict(data.get("style", {})) # 恢复 StyleObject obj._layout = LayoutObject.from_dict(data.get("layout", {})) # 恢复 LayoutObject obj._coordinate = CoordinateObject.from_dict(data.get("coordinate", {})) # 恢复 CoordinateObject obj._runs = [RunObject.from_dict(run) for run in data.get("runs", [])] # 恢复 RunObject 列表 obj._data_id = data.get("data_id", None) obj._position = Position.from_dict(data.get("position", {})) return obj def __repr__(self): return f'{self.__class__.__name__}()[TEXT="{self._text}"]' @property def data_id(self): return self._data_id @data_id.setter def data_id(self, new_value): assert type(new_value) == int self._data_id = new_value @property def text(self): return self._text @text.setter def text(self, new_value): assert type(new_value) == str self._text = new_value @property def runs(self): return self._runs @runs.setter def runs(self, new_value): assert type(new_value) == list self._runs = new_value @property def coordinate(self): return self._coordinate @coordinate.setter def coordinate(self, new_value): assert isinstance(new_value, CoordinateObject) self._coordinate = new_value @property def layout(self): return self._layout @layout.setter def layout(self, new_value): assert isinstance(new_value, LayoutObject) self._layout = new_value @property def style(self): return self._style @style.setter def style(self, new_value): assert isinstance(new_value, StyleObject) self._style = new_value @property def position(self): return self._position @position.setter def position(self, new_value): assert isinstance(new_value, Position) self._position = new_value @staticmethod def lstrip_special_char(title): """ 去除title左边的特殊字符 """ built_delims = ["、", ",", ".", " "] for sep in built_delims: title = title.lstrip(sep) return title def get_data(self): """精简化输出支持""" data = { 'type': 'text', 'content': self.text, 'parent_content': self.layout.parent_content, "chapter": "", "title": "", "desc": "", "data_id": self.data_id, "index": -1 } if hasattr(self.layout, "is_chapter_title"): if self.layout.is_chapter_title: chapter = self.layout.chapter_id if chapter not in self.text.replace("\u3000", " ").replace(".", ".").replace('-', '.'): logging.info(f'start to translate text 0: {_("章节号和标题不匹配,请检查!")}') else: title = self.text.replace("\u3000", " ").replace(".", ".").replace('-', '.').split(chapter, maxsplit=1)[1].strip(getattr(self, "customized_sep", " ")) title = self.lstrip_special_char(title) data["chapter"] = chapter data["title"] = title logging.info(f'start to translate text 1: {_("这段文本是章节标题,章节号:")}') data["desc"] = _("这段文本是章节标题,章节号:") + chapter + "," + _("标题为:") + title else: logging.info(f'start to translate text 2: {_("这段文本是纯内容,不是章节标题,没有章节号。")}') data["desc"] = _("这段文本是纯内容,不是章节标题,没有章节号。") data["index"] = self.index if hasattr(self, "index") else -1 else: logging.info(f'start to translate text 3: {_("这段文本是纯内容,不是章节标题,没有章节号。")}') # 普通文本 data["desc"] = _("这段文本是纯内容,不是章节标题,没有章节号。") if self.coordinate.desc: data["coord"] = self.coordinate.desc else: data["page_num"] = self.layout.page_id return data def get_chars(self): """ 获取文本对象中的每个字及样式 去除整行文字的前后连续空格,保留中间的空格和样式 """ # 去除整行文字的前后连续空格,保留中间的空格和样式 full_text = ''.join(str(run.text) for run in self.runs).strip() data_list = [] current_index = 0 # 用于跟踪 full_text 的匹配位置 for run in self.runs: for char in str(run.text): # 跳过前后被去掉的空格 if current_index >= len(full_text): break # 如果字符匹配 full_text 中的字符,则添加到结果列表 if char == full_text[current_index]: data_list.append(CharObject(char, run.style)) current_index += 1 return data_list def get_html_text(self): """ 获取带有样式的html文本字符串 """ html = "" for run in self.runs: # 字体样式 bg_color = run.style.background_color # 可为"" font_color = run.style.font_color # 可为"" font_family = run.style.font_family # 可为"" font_size = run.style.font_size.strip("pt") if font_size: font_size = int(float(font_size) * 1.3) # 加粗、倾斜 bold = run.style.font_style.bold italic = run.style.font_style.italic normal = run.style.font_style.normal # 装饰线 strike_out = run.style.font_style.strikeout underline = run.style.font_style.underline html += "<span style='" # 拼接样式 if not normal: if strike_out: html += "text-decoration: line-through;" elif underline: html += "text-decoration: underline;" else: html += "text-decoration: none;" if bold: html += "font-weight: bold;" if italic: html += "font-style: italic;" if font_color: html += "color: %s;" % font_color if font_family: html += "font-family: %s;" % font_family if font_size: html += "font-size: %dpx;" % font_size if bg_color: html += "background-color: %s;" % bg_color html += "'>" # 拼接文本 html += run.text + "</span>" return html def get_chapter_id(self): """ 获取文本对象章节号字符串, 返回章节id字符串 或者 空(无章节号) """ return getattr(self.layout, "chapter_id", "") class RunObject(BaseObject): """文本片段对象""" def __init__(self): self._text = '' # 完整内容 self._style = StyleObject() # 节段文本样式对象 self._layout = LayoutObject() # 文本片段布局对象 self._coordinate = CoordinateObject() # 文本的坐标 self._type = '' # 文本片段类型:text(普通文本);br(分页标识); @property def text(self): return self._text @text.setter def text(self, new_value): assert type(new_value) == str self._text = new_value @property def type(self): return self._type @type.setter def type(self, new_value): assert type(new_value) == str self._type = new_value @property def coordinate(self): return self._coordinate @coordinate.setter def coordinate(self, new_value): assert isinstance(new_value, CoordinateObject) self._coordinate = new_value @property def layout(self): return self._layout @layout.setter def layout(self, new_value): assert isinstance(new_value, LayoutObject) self._layout = new_value @property def style(self): return self._style @style.setter def style(self, new_value): assert isinstance(new_value, StyleObject) self._style = new_value def to_dict(self): return {"text": self._text, "style": self._style.to_dict()} """ @Author : 王定雄 @Date : 2024-10-29 @Desc : 图片对象的数据结构的定义 """ from kotei_omp.data.base_object import BaseObject from kotei_omp.data.layout import LayoutObject from kotei_omp.data.position import Position from kotei_omp.data.style import StyleObject from kotei_omp.data.coordinate import CoordinateObject class PictureObject(BaseObject): """图片对象""" def __init__(self): self._type = "picture" self._id = '' # 图片ID self._name = '' # 图片名称 self._width = '' # 宽 self._height = '' # 高 self._digest = '' # 图片数据的hash值 self._data = '' # 图片二进制数据的base64编码 self._layout = LayoutObject() # 文本布局对象 self._style = StyleObject() # 样式 self._coordinate = CoordinateObject() # 坐标 self._to_coordinate = CoordinateObject() self._data_id = None # 唯一标识 self._position = Position() def to_dict(self): """ 将 PictureObject 对象转换为字典 """ return { "type": self._type, "id": self._id, "name": self._name, "width": self._width, "height": self._height, "digest": self._digest, "data": self._data, "layout": self._layout.to_dict(), # 调用 LayoutObject 的 to_dict 方法 "style": self._style.to_dict(), # 调用 StyleObject 的 to_dict 方法 "coordinate": self._coordinate.to_dict(), # 调用 CoordinateObject 的 to_dict 方法 "data_id": self._data_id, "position": self._position.to_dict() } @classmethod def from_dict(cls, data): """ 从字典创建 PictureObject 实例 """ obj = cls() obj._type = data.get("type", "picture") obj._id = data.get("id", '') obj._name = data.get("name", '') obj._width = data.get("width", '') obj._height = data.get("height", '') obj._digest = data.get("digest", '') obj._data = data.get("data", '') obj._layout = LayoutObject.from_dict(data.get("layout", {})) # 恢复 LayoutObject obj._style = StyleObject.from_dict(data.get("style", {})) # 恢复 StyleObject obj._coordinate = CoordinateObject.from_dict(data.get("coordinate", {})) # 恢复 CoordinateObject obj._data_id = data.get("data_id", None) obj._position = Position.from_dict(data.get("position", {})) return obj def __repr__(self): return f'{self.__class__.__name__}()[{str(self)}]' def __str__(self): coordinate_str = f'\nCoordinate: {self._coordinate.desc}' if self._coordinate.desc else '' return f'Image [{self._name}]: Width:{self._width}, Height:{self._height}. \nType:{self._type}. \nOffset: Top:{self._coordinate.top or 0},Left:{self._coordinate.left or 0}.{coordinate_str}' @property def data_id(self): return self._data_id @data_id.setter def data_id(self, new_value): assert type(new_value) == int self._data_id = new_value @property def id(self): return self._id @id.setter def id(self, new_value): assert type(new_value) == str self._id = new_value @property def name(self): return self._name @name.setter def name(self, new_value): assert type(new_value) == str self._name = new_value @property def width(self): return self._width @width.setter def width(self, new_value): assert type(new_value) == str self._width = new_value @property def height(self): return self._height @height.setter def height(self, new_value): assert type(new_value) == str self._height = new_value @property def digest(self): return self._digest @digest.setter def digest(self, new_value): assert type(new_value) == str self._digest = new_value @property def data(self): return self._data @data.setter def data(self, new_value): assert type(new_value) == str self._data = new_value @property def layout(self): return self._layout @layout.setter def layout(self, new_value): assert isinstance(new_value, LayoutObject) self._layout = new_value @property def style(self): return self._style @style.setter def style(self, new_value): assert isinstance(new_value, StyleObject) self._style = new_value @property def coordinate(self): return self._coordinate @coordinate.setter def coordinate(self, new_value): assert isinstance(new_value, CoordinateObject) self._coordinate = new_value @property def to_coordinate(self): return self._to_coordinate @to_coordinate.setter def to_coordinate(self, new_value): assert isinstance(new_value, CoordinateObject) self.to_coordinate = new_value @property def position(self): return self._position @position.setter def position(self, new_value): assert isinstance(new_value, Position) self._position = new_value def get_data(self): """精简化输出支持""" data = { "data_id": self.data_id, 'type': 'picture', "parent_content": self.layout.parent_content, 'content': { 'id': self.id, 'name': self.name, 'width': self.width, 'height': self.height, 'data': self.data, 'digest': self.digest, }, "index": self.index if hasattr(self, "index") else 0 } if self.coordinate.desc: data["coord"] = self.coordinate.desc else: data["page_num"] = self.layout.page_id return data """ @Author : 王定雄 @Date : 2024-10-29 @Desc : 图形对象的数据结构的定义 """ from kotei_omp.data.base_object import BaseObject from kotei_omp.data.layout import LayoutObject from kotei_omp.data.position import Position from kotei_omp.data.style import StyleObject from kotei_omp.data.coordinate import CoordinateObject class GraphicObject(BaseObject): """图形对象""" def __init__(self): self._type = "graphic" self._id = '' # ID self._name = '' # 名称 self._width = '' # 宽 self._height = '' # 高 self._digest = '' # 图片数据的hash值 self._data = '' # 图片二进制数据的base64编码 self._text = '' # 图形文本框内容 self._graphic_type = '' # 类型,如矩形rect、线条line、嵌入对象等 self._layout = LayoutObject() # 文本布局对象 self._style = StyleObject() # 样式 self._coordinate = CoordinateObject() # 坐标位置 self._to_coordinate = CoordinateObject() self._data_id = None # 唯一标识 self._position = Position() def to_dict(self): """ 将 GraphicObject 对象转换为字典 """ return { "type": self._type, "id": self._id, "name": self._name, "width": self._width, "height": self._height, "digest": self._digest, "data": self._data, "text": self._text, "graphic_type": self._graphic_type, "layout": self._layout.to_dict(), # 调用 LayoutObject 的 to_dict 方法 "style": self._style.to_dict(), # 调用 StyleObject 的 to_dict 方法 "coordinate": self._coordinate.to_dict(), # 调用 CoordinateObject 的 to_dict 方法 "data_id": self._data_id, "position": self._position.to_dict() } @classmethod def from_dict(cls, data): """ 从字典创建 GraphicObject 实例 """ obj = cls() obj._type = data.get("type", "graphic") obj._id = data.get("id", '') obj._name = data.get("name", '') obj._width = data.get("width", '') obj._height = data.get("height", '') obj._digest = data.get("digest", '') obj._data = data.get("data", '') obj._text = data.get("text", '') obj._graphic_type = data.get("graphic_type", '') obj._layout = LayoutObject.from_dict(data.get("layout", {})) # 恢复 LayoutObject obj._style = StyleObject.from_dict(data.get("style", {})) # 恢复 StyleObject obj._coordinate = CoordinateObject.from_dict(data.get("coordinate", {})) # 恢复 CoordinateObject obj._data_id = data.get("data_id", None) obj._position = Position.from_dict(data.get("position", {})) return obj def __repr__(self): return f'{self.__class__.__name__}()[{str(self) }]' def __str__(self): coordinate_str = f'\nCoordinate: {self._coordinate.desc}' if self._coordinate.desc else '' return f'Image [{self._name}]: Width:{self._width}, Height:{self._height}. \nType:{self._type}. \nOffset: Top:{self._coordinate.top or 0},Left:{self._coordinate.left or 0}.{coordinate_str}' @property def position(self): return self._position @position.setter def position(self, new_value): assert isinstance(new_value, Position) self._position = new_value @property def data_id(self): return self._data_id @data_id.setter def data_id(self, new_value): assert type(new_value) == int self._data_id = new_value @property def id(self): return self._id @id.setter def id(self, new_value): assert type(new_value) == str self._id = new_value @property def name(self): return self._name @name.setter def name(self, new_value): assert type(new_value) == str self._name = new_value @property def width(self): return self._width @width.setter def width(self, new_value): assert type(new_value) == str self._width = new_value @property def height(self): return self._height @height.setter def height(self, new_value): assert type(new_value) == str self._height = new_value @property def digest(self): return self._digest @digest.setter def digest(self, new_value): assert type(new_value) == str self._digest = new_value @property def data(self): return self._data @data.setter def data(self, new_value): assert type(new_value) == str self._data = new_value @property def text(self): return self._text @text.setter def text(self, new_value): assert type(new_value) == str self._text = new_value @property def graphic_type(self): return self._graphic_type @graphic_type.setter def graphic_type(self, new_value): assert type(new_value) == str self._graphic_type = new_value @property def layout(self): return self._layout @layout.setter def layout(self, new_value): assert isinstance(new_value, LayoutObject) self._layout = new_value @property def style(self): return self._style @style.setter def style(self, new_value): assert isinstance(new_value, StyleObject) self._style = new_value @property def coordinate(self): return self._coordinate @coordinate.setter def coordinate(self, new_value): assert isinstance(new_value, CoordinateObject) self._coordinate = new_value @property def to_coordinate(self): return self._to_coordinate @to_coordinate.setter def to_coordinate(self, new_value): assert isinstance(new_value, CoordinateObject) self.to_coordinate = new_value def get_data(self): """精简化输出支持""" data = { "data_id": self.data_id, 'type': 'graphic', "parent_content": self.layout.parent_content, 'content': { 'id': self.id, 'name': self.name, 'width': self.width, 'height': self.height, 'data': self.data, 'digest': self.digest, 'text': self.text, }, "index": self.index if hasattr(self, "index") else 0 } if self.coordinate.desc: data["coord"] = self.coordinate.desc else: data["page_num"] = self.layout.page_id if hasattr(self, 'text_obj'): runs_style_obj = [] from kotei_omp.data.table import get_text_obj_runs_style get_text_obj_runs_style(self.text_obj, runs_style_obj) if runs_style_obj: data['runs_style'] = runs_style_obj return data 完善这三个类的类图 ┌──────────────────────────────┐ │ TextObject │ ├──────────────────────────────┤ │ - text: str │ ← 文本内容 │ - runs: List[RunObject] │ ← 文本片段列表 │ - style: StyleObject │ ← 文本样式 ├──────────────────────────────┤ │ + to_dict(): dict │ ← 转换为字典 │ + from_dict(data: dict): cls │ ← 从字典创建实例 └──────────────────────────────┘
10-29
class DocumentBlockObject(BaseObject): “”“文档块对象 文档块是一个文档从结构上应该视为一个整体的部分,整个文档由这样多个块构成。 在不同的文档对应的部分不同,word中指代整个word。在Excel中指代一个表单,因为每个表单承载了独立的数据。 “”” def __init__(self): self._file_name = "" # 式样书的名称 self._name = '' # block名称 self._type = "block" self._header = [] # 页眉 self._footer = [] # 页脚 self._elements = [] # 所有元素的对象列表,按文档顺序装载。例:[TextObject, TableObject] self._texts = [] # 文本对象列表,按文档顺序装载。例:[TextObject] self._tables = [] # 表格对象列表,按文档顺序装载。例:[TableObject] self._pictures = [] # 图片对象列表,按文档顺序装载rId。例:[PictureObject] self._graphics = [] # 图形对象列表,按文档顺序装载。例:[GraphicObject] self._timing_waves = [] # 时序图对象列表 例:[TimingWaveObject] self._timing_texts = [] # 时序图行文本对象列表 例:[TimingTextObject] self._settings = [] # block级别的属性信息 self._layouts = [] # block级别的布局信息 self._styles = [] # block级别的样式信息 def __repr__(self): return f'{self.__class__.__name__}()[NAME="{self._name}"]' def __str__(self): return self._name @property def name(self): return self._name @name.setter def name(self, new_value): assert type(new_value) == str self._name = new_value @property def file_name(self): return self._file_name @file_name.setter def file_name(self, new_value): assert type(new_value) is str self._file_name = new_value @property def elements(self): return self._elements @elements.setter def elements(self, new_value): assert type(new_value) == list self._elements = new_value @property def texts(self): return self._texts @texts.setter def texts(self, new_value): assert type(new_value) == list self._texts = new_value @property def header(self): return self._header @header.setter def header(self, new_value): assert type(new_value) is list self._header = new_value @property def footer(self): return self._footer @footer.setter def footer(self, new_value): assert type(new_value) is list self._footer = new_value @property def tables(self): return self._tables @tables.setter def tables(self, new_value): assert type(new_value) == list self._tables = new_value @property def pictures(self): return self._pictures @pictures.setter def pictures(self, new_value): assert type(new_value) == list self._pictures = new_value @property def graphics(self): return self._graphics @graphics.setter def graphics(self, new_value): assert type(new_value) == list self._graphics = new_value @property def timing_waves(self): return self._timing_waves @timing_waves.setter def timing_waves(self, new_value): assert type(new_value) == list self._timing_waves = new_value @property def timing_texts(self): return self._timing_texts @timing_texts.setter def timing_texts(self, new_value): assert type(new_value) == list self._timing_texts = new_value @property def settings(self): return self._settings @settings.setter def settings(self, new_value): assert type(new_value) == list self._settings = new_value @property def layouts(self): return self._layouts @layouts.setter def layouts(self, new_value): assert type(new_value) == list self._layouts = new_value @property def styles(self): return self._styles @styles.setter def styles(self, new_value): assert type(new_value) == list self._styles = new_value def add_text(self, text_object): """添加文本对象 :param text_object: 文本对象 """ assert type(text_object) in [list, TextObject] if text_object: if isinstance(text_object, list): self._texts.extend(text_object) self._elements.extend(text_object) else: self._texts.append(text_object) self._elements.append(text_object) def add_table(self, table_object): """添加表格对象 :param table_object: 表格对象 """ assert type(table_object) in [list, TableObject] if table_object: if isinstance(table_object, list): for table in table_object: self.align_table_col(table) self._tables.extend(table_object) self._elements.extend(table_object) else: self.align_table_col(table_object) self._tables.append(table_object) self._elements.append(table_object) def add_picture(self, picture_object): """添加图片对象 :param picture_object: 图片对象 """ assert type(picture_object) in [list, PictureObject] if picture_object: if isinstance(picture_object, list): self._pictures.extend(picture_object) self._elements.extend(picture_object) else: self._pictures.append(picture_object) self._elements.append(picture_object) def add_graphic(self, graphic_object): """添加图形对象 :param graphic_object: 图形对象 """ assert type(graphic_object) in [list, GraphicObject] if graphic_object: if isinstance(graphic_object, list): self._graphics.extend(graphic_object) self._elements.extend(graphic_object) else: self._graphics.append(graphic_object) self._elements.append(graphic_object) def add_timing_wave(self, object): """ 添加时序图对象 :param object: 时序图对象 """ assert type(object) in [list, TimingWaveObject] if object: if isinstance(object, list): self._timing_waves.extend(object) self._elements.extend(object) else: self._timing_waves.append(object) self._elements.append(object) def add_timing_text(self, object): """ 添加时序图对象 :param object: 时序图对象 """ assert type(object) in [list, TimingTextObject] if object: if isinstance(object, list): self._timing_texts.extend(object) self._elements.extend(object) else: self._timing_texts.append(object) self._elements.append(object) def align_table_col(self, base_table): max_col_count = max([len(row.cells) for row in base_table.rows]) for base_row in base_table.rows: if len(base_row.cells) != max_col_count: # 匹配行的列数不一致,补齐缺失的cell add_col_count = abs(len(base_row.cells) - max_col_count) base_row.cells.extend([CellObject() for _ in range(add_col_count)]) def get_chapter_content(self, text_obj: TextObject) -> List[Union[str, list]]: """ 获取word文档中一个章节标题对象下的所有子内容, 返回列表。 如变更履历,获取"变更履历"章节下的文本等内容 (只考虑文本、表格) """ para_text = "" table_data = [] total_result = [] # 是章节标题 if getattr(text_obj.layout, "chapter_id", None): cur_idx = self._elements.index(text_obj) + 1 while cur_idx < len(self._elements): cur_obj = self._elements[cur_idx] if isinstance(cur_obj, TextObject): # 遇到下一个章节标题,则停止 if getattr(cur_obj.layout, "chapter_id", None): break para_text += cur_obj.text + "\n" if table_data: table_data = [] elif isinstance(cur_obj, TableObject): if para_text: total_result.append(para_text) para_text = "" for row in cur_obj.rows: row_data = [] for cell in row.cells: row_data.append(cell.text) table_data.append(row_data) total_result.append(table_data) cur_idx += 1 if para_text: total_result.append(para_text) total_result = [i.strip("\n") if isinstance(i, str) else i for i in total_result] return total_result @staticmethod def is_change_resume( obj: Union[TextObject, PictureObject, GraphicObject, TableObject, RowObject, CellObject]) -> bool: """ 判断当前对象是否为变更履历下面的内容,返回bool类型 兼容word & excel """ parent_node = None # ""/None/TextObject/DocumentBlockObject # 文本、图片、图形 if isinstance(obj, (TextObject, PictureObject, GraphicObject, TableObject)): parent_node = obj.layout.parent_ref # 表格行对象 elif isinstance(obj, RowObject): row_parent = obj.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref # 单元格对象 elif isinstance(obj, CellObject): cell_parent = obj.layout.parent_ref if cell_parent: row_parent = cell_parent.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref if isinstance(parent_node, TextObject): # word # 兼容多级别章节标题 while parent_node: if any([True if i in parent_node.text else False for i in CHANGE_RESUME]): return True parent_node = parent_node.layout.parent_ref else: return False elif isinstance(parent_node, DocumentBlockObject): # excel return any([True if i in parent_node.name else False for i in CHANGE_RESUME]) return False @staticmethod def get_chapter(obj: Union[TextObject, PictureObject, GraphicObject, TableObject, RowObject, CellObject]): """ 获取通用对象的章节文本对象 """ parent_node = None # 文本、图片、图形 if isinstance(obj, (TextObject, PictureObject, GraphicObject, TableObject)): parent_node = obj.layout.parent_ref # 表格行对象 elif isinstance(obj, RowObject): row_parent = obj.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref # 单元格对象 elif isinstance(obj, CellObject): cell_parent = obj.layout.parent_ref if cell_parent: row_parent = cell_parent.layout.parent_ref if row_parent: parent_node = row_parent.layout.parent_ref return parent_node 一行行解释
09-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值