Переглянути джерело

上传文件至 '专利清单转PPT'

zhuliu 3 місяців тому
батько
коміт
7b44dc95cd
1 змінених файлів з 916 додано та 0 видалено
  1. 916 0
      专利清单转PPT/excelToPPT.py

+ 916 - 0
专利清单转PPT/excelToPPT.py

@@ -0,0 +1,916 @@
+from flask import Flask, request, jsonify, Response,send_file,abort
+import os
+import json
+import re
+from datetime import datetime
+
+app = Flask(__name__)
+
+# 配置上传文件夹
+UPLOAD_FOLDER = 'uploads'
+if not os.path.exists(UPLOAD_FOLDER):
+    os.makedirs(UPLOAD_FOLDER)
+app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
+
+
+from pptx import Presentation
+from pptx.util import Inches,Pt
+from pptx.enum.text import PP_ALIGN,PP_PARAGRAPH_ALIGNMENT
+from pptx.enum.dml import MSO_THEME_COLOR
+from lxml import etree
+
+import uuid
+def generate_random_key():
+    """生成一个 UUID 密钥"""
+    return str(uuid.uuid4())
+
+# 获取当前时间
+def getCurrentDate():
+    # 获取当前日期和时间
+    current_time = datetime.now()
+    
+    # 格式化输出
+    formatted_time = current_time.strftime("%Y-%m-%d")
+    return formatted_time
+
+
+# 导出PPT
+def excelToPPT(data):
+    # 创建一个新的PPT
+    prs = Presentation()
+    slide_width = prs.slide_width  # 返回EMU单位
+    slide_height = prs.slide_height
+    font_list = ["latin", "ea", "cs"]
+    # 设置主题字体
+    for item in data:
+        order = item['order']
+        title_name = item['title']
+        patentNo = item.get('appNo','') or item.get('publicNo','')
+        appDate = item.get('appDate','')
+        publicDate = item.get('publicDate','')
+        appPerson = item.get('appPerson','')
+        files = item.get('files',[])
+        technical_problem = item.get('technical_problem','')
+        technical_means = item.get('technical_means','')
+        technical_efficacy = item.get('technical_efficacy','')
+        application_field_classification = item.get('application_field_classification','')
+
+        # 添加一个幻灯片
+        slide_layout = prs.slide_layouts[5]  # 使用一个空白布局
+        slide = prs.slides.add_slide(slide_layout)
+
+        # 添加标题
+        title = slide.shapes.title
+        title.height = Pt(40)
+        title.width = slide_width
+        title.text = f"{order}、{title_name}-{patentNo}"
+        # 设置标题字体
+        title_frame = title.text_frame
+        title_paragraph = title_frame.paragraphs[0]
+        title_paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.LEFT
+        title_run = title_paragraph.runs[0]
+        # 设置标题字体属性
+        title_run.font.name = '微软雅黑'
+        title_run.font.size = Pt(20)
+        title_run.font.bold = True 
+        r_element = title_run._r
+        rPr = r_element.find('.//a:rPr', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
+        if rPr is not None:
+            for tag in font_list:
+                # 获取或创建 ea 节点
+                node = rPr.find(f'.//a:{tag}', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
+                if node is None:
+                    # 如果 ea 节点不存在,创建一个新的 ea 节点
+                    node = etree.Element('{http://schemas.openxmlformats.org/drawingml/2006/main}'+tag)
+                    rPr.append(node)
+                node.set('typeface', '微软雅黑')  # 你可以替换为你想要的字体名称
+                # 更新 _r 属性
+                title_run._r = r_element
+
+        # 添加图片 (替换为实际图片路径)
+        if len(files)>0:
+            left = Inches(0.5)
+            top = Inches(1.3)
+            width = Inches(4.5)
+            height = Inches(3)
+            num = 1
+            for image in files:
+                if num == 1:
+                    slide.shapes.add_picture(image, left, top, width, height)
+                else:
+                    left_second_image = Inches(5 * (num - 1))
+                    slide.shapes.add_picture(image, left_second_image, top, width, height) 
+                num += 1
+
+        # 添加文本框
+        left_text = Inches(0.5)
+        top_text = Inches(4.5)
+        width_text = slide_width - Inches(0.5).emu
+        height_text = Inches(2)
+
+        text_box = slide.shapes.add_textbox(left_text, top_text, width_text, height_text)
+        text_frame = text_box.text_frame
+        # 关键配置:启用自动换行
+        text_frame.word_wrap = True 
+        text_frame.auto_size = None 
+
+        paragraph_list = [
+            {
+                'field':'申请人',
+                'content':appPerson
+            },
+            {
+                'field':'申请日',
+                'content':appDate
+            },
+            {
+                'field':'公开公告日',
+                'content':publicDate
+            },
+            {
+                'field':'解决问题',
+                'content':technical_problem
+            },
+            {
+                'field':'技术手段',
+                'content':technical_means
+            },
+            {
+                'field':'技术效果',
+                'content':technical_efficacy
+            },
+            {
+                'field':'应用领域',
+                'content':application_field_classification.replace('|','、')
+            }
+        ]
+        
+        for item in paragraph_list:
+            paragraph = text_frame.add_paragraph()
+            paragraph.text = f"{item['field']}:"
+            run = paragraph.runs[0]
+            run.font.bold = True
+            run = paragraph.add_run()
+            run.text = item['content']
+            for run_item in paragraph.runs:
+                run_item.font.size = Pt(15)
+                run_item.font.name = '微软雅黑'
+                r_element = run_item._r
+                rPr = r_element.find('.//a:rPr', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
+                if rPr is not None:
+                    for tag in font_list:
+                        # 获取或创建 ea 节点
+                        node = rPr.find(f'.//a:{tag}', namespaces={'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
+                        if node is None:
+                            # 如果 ea 节点不存在,创建一个新的 ea 节点
+                            node = etree.Element('{http://schemas.openxmlformats.org/drawingml/2006/main}'+tag)
+                            rPr.append(node)
+                        node.set('typeface', '微软雅黑')  # 你可以替换为你想要的字体名称
+                        # 更新 _r 属性
+                        run_item._r = r_element
+            paragraph.alignment = PP_PARAGRAPH_ALIGNMENT.LEFT
+
+    # 保存PPT
+    uuid = generate_random_key()
+    formatted_time = getCurrentDate()
+    file_name = f"result/{formatted_time}_{uuid}.pptx"
+    prs.save(file_name)
+    return file_name
+
+
+# 读取Excel文件
+from openpyxl import load_workbook
+def read_excel_file(file_path):
+    try:
+        # 读取Excel文件
+        # df = pd.read_excel(file_path)
+        wb = load_workbook(file_path)
+        sheet = wb[wb.sheetnames[0]]
+        df = []
+        for row in sheet.iter_rows(values_only=True):
+            df.append(row)
+        
+        # 获取所有图片
+        images = {}
+        for img in sheet._images:
+            # 图片位置信息
+            anchor = img.anchor
+            # 图片数据
+            if hasattr(img, '_data'):
+                img_data = img._data() if callable(img._data) else img._data
+            elif hasattr(img, 'image'):
+                from io import BytesIO
+                img.image.save(BytesIO(), format='png')
+                img_data = BytesIO().getvalue()
+            else:
+                continue
+            images[(anchor._from.row, anchor._from.col)] = img_data
+        # 返回数据框
+        return df,images
+    except Exception as e:
+        print(f"读取Excel文件时出错: {e}")
+        return None
+
+
+
+# 格式化excel数据
+def getExcelData(excel_data,field_obj={}):
+    fieldList = excel_data[0]
+    data = []
+    for index,item in enumerate(excel_data):
+        if index == 0:
+            continue
+        obj = {}
+        for fieldIndex,field in enumerate(fieldList):
+            if field in field_obj:
+                obj[field_obj[field]] = item[fieldIndex]
+            # else:
+            #     obj[field] = item[fieldIndex]
+        data.append(obj)
+    return data
+
+
+# 读取excel文件
+def read_excel(file_path,current_filename,field_obj):
+    #1.读取文件拿到信息
+    excel_data,images = read_excel_file(file_path)
+    
+    data = getExcelData(excel_data,field_obj)
+    uuid = generate_random_key()
+    # 打印数据
+    for i, row in enumerate(data):
+        # 检查该行是否有图片
+        for (r, c), img_data in images.items():
+            if r == i+1:
+                # 可以保存图片
+                with open(f'image/{uuid}_row_{i+1}_col_{c+1}.png', 'wb') as f:
+                    f.write(img_data)
+                    if 'files' not in row:
+                        row['files'] = []
+                    row['files'].append(f'image/{uuid}_row_{i+1}_col_{c+1}.png')
+    return data
+    
+
+# 根据excel导出PPT接口
+@app.route(f'/api/exportPPT', methods=['POST'])   
+def exportPPT(): 
+    # 检查请求中是否有文件
+    if 'file' not in request.files:
+        return jsonify({'error': 'No file part'}), 400
+    file = request.files['file']
+    filename = ''
+    # 检查是否选择了文件
+    if file.filename == '':
+        return jsonify({'error': 'No selected file'}), 400
+    if file:
+        current_filename = file.filename
+        # filename = f'uploads\{current_filename}'
+        # if os.path.exists(filename):
+        #     pass
+        # else:
+        # 安全地获取文件名
+        filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
+        # 确保不会覆盖现有文件
+        counter = 1
+        while os.path.exists(filename):
+            name, ext = os.path.splitext(file.filename)
+            filename = os.path.join(app.config['UPLOAD_FOLDER'], f"{name}_{counter}{ext}")
+            counter += 1
+        # 保存文件
+        file.save(filename)
+
+        # 文件存在时直接处理
+        file_path = ''
+        # 1. 获取文件内容
+        field_obj = {
+            '公开(公告)号':'publicNo',
+            '摘要附图':'image',
+            '序号':'order',
+            '标题':'title',
+            '申请号':'appNo',
+            '申请日':'appDate',
+            '当前申请(专利权)人':'appPerson',
+            '公开(公告)日':'publicDate',
+            '技术手段':'technical_means',
+            '技术功效':'technical_efficacy',
+            '应用领域分类':'application_field_classification',
+            '技术问题':'technical_problem',
+        }
+        excel_data = read_excel(filename,current_filename,field_obj)
+        if excel_data:
+            # 2. 生成并导出PPT 
+            file_path = excelToPPT(excel_data)
+    else:
+        return jsonify({'error': 'No file part'}), 400
+    
+    return jsonify(file_path) ,200  
+
+
+#下载文件
+@app.route(f'/api/download',methods=['GET'])
+async def download():
+    get_params = request.args
+    filename = get_params.get('filePath')
+    file_path = os.path.join(os.getcwd(), '', filename)
+
+    if not os.path.exists(file_path):
+        abort(404)  # 返回 404 错误
+ 
+    try:
+        # 返回文件给用户下载
+        return send_file(file_path, as_attachment=True)
+    except Exception as e:
+        # 处理可能的异常(例如文件权限问题)
+        return str(e), 500
+
+
+# 根据excel导出xmind接口
+@app.route(f'/api/exportXmind', methods=['POST'])   
+def exportXmind(): 
+    # 检查请求中是否有文件
+    if 'file' not in request.files:
+        return jsonify({'error': 'No file part'}), 400
+    file = request.files['file']
+    filename = ''
+    # 检查是否选择了文件
+    if file.filename == '':
+        return jsonify({'error': 'No selected file'}), 400
+    if file:
+        current_filename = file.filename
+        # filename = f'uploads\{current_filename}'
+        # if os.path.exists(filename):
+        #     pass
+        # else:
+        # 安全地获取文件名
+        filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
+        # 确保不会覆盖现有文件
+        counter = 1
+        while os.path.exists(filename):
+            name, ext = os.path.splitext(file.filename)
+            filename = os.path.join(app.config['UPLOAD_FOLDER'], f"{name}_{counter}{ext}")
+            counter += 1
+        # 保存文件
+        file.save(filename)
+
+        # 文件存在时直接处理
+        file_path = ''
+        # 分类节点
+        classify_nodes = [
+            {
+                'name':'AI智能化',
+                'path':'AI智能化',
+                'parent_name':'',
+                'order':1
+            },
+            {
+                'name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'parent_name':'AI智能化',
+                'parent_order':1,
+                'order':2
+            },
+            {
+                'name':'功能控制',
+                'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\功能控制',
+                'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'parent_order':2,
+                'order':3
+            },
+            {
+                'name':'语音控制',
+                'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\语音控制',
+                'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'parent_order':2,
+                'order':4
+            },
+            {
+                'name':'手势识别',
+                'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\手势识别',
+                'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'parent_order':2,
+                'order':5
+            },
+            {
+                'name':'佩戴检测',
+                'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\佩戴检测',
+                'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'parent_order':2,
+                'order':6
+            },
+            {
+                'name':'其它',
+                'path':'AI智能化\多模态交互、传感器处理(例如佩戴检测、语音识别)\其它',
+                'parent_name':'多模态交互、传感器处理(例如佩戴检测、语音识别)',
+                'parent_order':2,
+                'order':7
+            },
+
+            {
+                'name':'音频信号处理',
+                'path':'AI智能化\音频信号处理',
+                'parent_name':'AI智能化',
+                'parent_order':1,
+                'order':8
+            },
+            {
+                'name':'音质',
+                'path':'AI智能化\音频信号处理\音质',
+                'parent_name':'音频信号处理',
+                'parent_order':8,
+                'order':9
+            },
+            {
+                'name':'空间音频',
+                'path':'AI智能化\音频信号处理\空间音频',
+                'parent_name':'音频信号处理',
+                'parent_order':8,
+                'order':10
+            },
+            {
+                'name':'降噪',
+                'path':'AI智能化\音频信号处理\降噪',
+                'parent_name':'音频信号处理',
+                'parent_order':8,
+                'order':11
+            },
+            {
+                'name':'高清编码',
+                'path':'AI智能化\音频信号处理\高清编码',
+                'parent_name':'音频信号处理',
+                'parent_order':8,
+                'order':12
+            },
+            {
+                'name':'其它',
+                'path':'AI智能化\音频信号处理\其它',
+                'parent_name':'音频信号处理',
+                'parent_order':8,
+                'order':13
+            },
+            {
+                'name':'大模型简化',
+                'path':'AI智能化\大模型简化',
+                'parent_name':'AI智能化',
+                'parent_order':1,
+                'order':14
+            },
+            {
+                'name':'智能化应用',
+                'path':'AI智能化\智能化应用',
+                'parent_name':'AI智能化',
+                'parent_order':1,
+                'order':15
+            },
+            {
+                'name':'翻译',
+                'path':'AI智能化\智能化应用\翻译',
+                'parent_name':'智能化应用',
+                'parent_order':15,
+                'order':16
+            },
+            {
+                'name':'会议纪要',
+                'path':'AI智能化\智能化应用\会议纪要',
+                'parent_name':'智能化应用',
+                'parent_order':15,
+                'order':17
+            },
+            {
+                'name':'其它',
+                'path':'AI智能化\智能化应用\其它',
+                'parent_name':'智能化应用',
+                'parent_order':15,
+                'order':18
+            },
+            {
+                'name':'运动/健康信号处理',
+                'path':'运动/健康信号处理',
+                'parent_name':'',
+                'order':19
+            },
+            {
+                'name':'传感器元件',
+                'path':'运动/健康信号处理\传感器元件',
+                'parent_name':'运动/健康信号处理',
+                'parent_order':19,
+                'order':20
+            },
+            {
+                'name':'健康状态识别与分析',
+                'path':'运动/健康信号处理\健康状态识别与分析',
+                'parent_name':'运动/健康信号处理',
+                'parent_order':19,
+                'order':21
+            },
+            {
+                'name':'无线技术',
+                'path':'无线技术',
+                'parent_name':'',
+                'order':22
+            },
+            {
+                'name':'蓝牙/WiFi协议、连接',
+                'path':'无线技术\蓝牙/WiFi协议、连接',
+                'parent_name':'无线技术',
+                'parent_order':22,
+                'order':23
+            },
+            {
+                'name':'低延迟',
+                'path':'无线技术\蓝牙/WiFi协议、连接\低延迟',
+                'parent_name':'蓝牙/WiFi协议、连接',
+                'parent_order':23,
+                'order':24
+            },
+            {
+                'name':'其它',
+                'path':'无线技术\蓝牙/WiFi协议、连接\其它',
+                'parent_name':'蓝牙/WiFi协议、连接',
+                'parent_order':23,
+                'order':25
+            },
+            {
+                'name':'信号传输质量',
+                'path':'无线技术\信号传输质量',
+                'parent_name':'无线技术',
+                'parent_order':22,
+                'order':26
+            },
+            {
+                'name':'高可靠性',
+                'path':'无线技术\信号传输质量\高可靠性',
+                'parent_name':'信号传输质量',
+                'parent_order':26,
+                'order':27
+            },
+            {
+                'name':'其它',
+                'path':'无线技术\信号传输质量\其它',
+                'parent_name':'信号传输质量',
+                'parent_order':26,
+                'order':28
+            },
+            {
+                'name':'天线',
+                'path':'无线技术\天线',
+                'parent_name':'无线技术',
+                'parent_order':22,
+                'order':29
+            },
+            {
+                'name':'结构/材料/工艺',
+                'path':'结构/材料/工艺',
+                'parent_name':'',
+                'order':30
+            },
+            {
+                'name':'MIC/扬声器',
+                'path':'结构/材料/工艺\MIC/扬声器',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':31
+            },
+            {
+                'name':'舒适度',
+                'path':'结构/材料/工艺\舒适度',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':32
+            },
+            {
+                'name':'封装',
+                'path':'结构/材料/工艺\封装',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':33
+            },
+            {
+                'name':'电池',
+                'path':'结构/材料/工艺\电池',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':34
+            },
+            {
+                'name':'可调节结构/可拆卸结构/气囊结构',
+                'path':'结构/材料/工艺\可调节结构/可拆卸结构/气囊结构',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':35
+            },
+            {
+                'name':'传感器件与产品耦合的材料/结构',
+                'path':'结构/材料/工艺\传感器件与产品耦合的材料/结构',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':36
+            },
+            {
+                'name':'耳机特殊形态',
+                'path':'结构/材料/工艺\耳机特殊形态',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':37
+            },
+            {
+                'name':'其它',
+                'path':'结构/材料/工艺\其它',
+                'parent_name':'结构/材料/工艺',
+                'parent_order':30,
+                'order':38
+            },
+            {
+                'name':'其它',
+                'path':'其它',
+                'parent_name':'',
+                'order':39
+            }
+        ]
+        # 1. 获取文件内容
+        field_obj = {
+            '公开(公告)号':'publicNo',
+            '摘要附图':'image',
+            '序号':'order',
+            '标题':'title',
+            '申请号':'appNo',
+            '申请日':'appDate',
+            '当前申请(专利权)人':'appPerson',
+            '公开(公告)日':'publicDate',
+            '分类':'classify',
+            '法律状态/事件':'status',
+            '工作空间注释':'annotation'
+        }
+        excel_data = read_excel(filename,current_filename,field_obj)
+        if excel_data:
+            # 2. 获取待导出的数据结构
+            exportXmindData = getExportXmindData(excel_data,classify_nodes)
+            # 3. 生成并导出xmind 
+            file_path = excelToXmind(exportXmindData)
+    else:
+        return jsonify({'error': 'No file part'}), 400
+    return jsonify(file_path) ,200  
+
+# 获取待导出的数据结构
+def getExportXmindData(excel_data,classify_nodes):
+    
+    for item in excel_data:
+        if 'classify' in item and item['classify']:
+            item['classify_list'] = item['classify'].split('>')
+
+    # 将专利添加到分类节点中去
+    for node_item in classify_nodes:
+        path = node_item['path']
+        for item in excel_data:
+            if 'classify_list' in item and item['classify_list']:
+                classify_list = item['classify_list']
+                if path in classify_list:
+                    if 'patents' not in node_item:
+                        node_item['patents'] = []
+                    node_item['patents'].append(item)
+    node_dict = {node['order']: node for node in classify_nodes}
+    tree = []
+    # 生成树级结构
+    for node in classify_nodes:
+        if node['parent_name'] == '':
+            # 如果没有父节点,说明是根节点
+            tree.append(node)
+        else:
+            # 否则,找到父节点并添加当前节点为子节点
+            parent = node_dict.get(node['parent_order'])
+            if parent:
+                if 'children' not in parent:
+                    parent['children'] = []
+                parent['children'].append(node)
+    return tree
+
+import shutil
+# 复制模板文件
+def copy_file_with_relative_path(relative_source_path, destination_path):
+    try:
+        # 获取当前工作目录
+        current_dir = os.getcwd()
+ 
+        # 构建完整的源文件路径
+        full_source_path = os.path.join(current_dir, relative_source_path)
+ 
+        # 检查源文件是否存在
+        if not os.path.exists(full_source_path):
+            raise FileNotFoundError(f"源文件不存在:{full_source_path}")
+ 
+        # 使用 shutil.copy2 复制文件,同时保留元数据
+        shutil.copy2(full_source_path, destination_path)
+ 
+        print(f"文件已成功复制到:{destination_path}")
+    except Exception as e:
+        print(f"复制文件时发生错误:{e}")
+
+# 导出xmind
+def excelToXmind(tree):
+    # 1. 创建空白xmind文件
+    uuid = generate_random_key()
+    formatted_time = getCurrentDate()
+    file_name = f"result/{formatted_time}_{uuid}.xmind"
+    create_xmind_with_images(file_name,tree)
+    return file_name
+
+
+import os
+import zipfile
+import xml.etree.ElementTree as ET
+from xml.dom import minidom
+
+def create_xmind_with_images(output_file,data):
+    """创建包含三级节点和图片的 XMind 文件"""
+    # 临时工作目录
+    temp_dir = "xmind_temp"
+    
+    # 清理并创建目录结构
+    if os.path.exists(temp_dir):
+        shutil.rmtree(temp_dir)
+    os.makedirs(os.path.join(temp_dir, "content"))
+    os.makedirs(os.path.join(temp_dir, "META-INF"))
+    os.makedirs(os.path.join(temp_dir, "resources"))
+    os.makedirs(os.path.join(temp_dir, "Thumbnails"))
+    
+    create_metadata_json(temp_dir)
+
+    # 1. 创建 content.xml 文件
+    images_list = create_content_xml(temp_dir,data)
+    
+    # 2. 创建 manifest.xml 文件
+    create_manifest_xml(temp_dir,images_list)
+    
+    # 3. 添加示例图片到 resources 目录 (实际使用中替换为你的图片)
+    create_sample_images(temp_dir,images_list)
+    
+    # 4. 打包成 XMind 文件
+    with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
+        for root, _, files in os.walk(temp_dir):
+            for file in files:
+                file_path = os.path.join(root, file)
+                arcname = os.path.relpath(file_path, temp_dir)
+                zipf.write(file_path, arcname)
+    
+    # 5. 清理临时文件
+    shutil.rmtree(temp_dir)
+    print(f"XMind 文件已创建: {output_file}")
+    return output_file
+
+def create_metadata_json(temp_dir):
+    """创建 XMind 2022 的 metadata.json"""
+    metadata_content = '''{
+        "activeSheetId": "sheet1",
+        "creator": {
+            "name": "XMind Python Generator",
+            "version": "1.0"
+        },
+        "fileStructure": {
+            "markerIds": [],
+            "numberingFormat": null,
+            "theme": "Default",
+            "version": "2.0"
+        },
+        "modifiedBy": "XMind Python Generator"
+    }'''
+    
+    # 写入文件
+    metadata_path = os.path.join(temp_dir, "metadata.json")
+    with open(metadata_path, 'w', encoding='utf-8') as f:
+        f.write(metadata_content)  
+
+def add_topic_recursive(parent_element, node_data, ns,num,images_list):
+    """
+    递归添加主题节点
+    
+    参数:
+        parent_element: 父XML元素
+        node_data: 当前节点数据
+        ns: XML命名空间
+    """
+    node_name = ''
+    classify_name = node_data.get('name','')
+    if not classify_name:#不是分类节点,是专利
+        patentNo = node_data.get('appNo','') or node_data.get('publicNo','')
+        title = node_data.get('title','')
+        status = node_data.get('status','')
+        annotation = node_data.get('annotation','')
+        node_name = node_name + patentNo
+        if title:
+            node_name = node_name + f'({title})'
+        node_name = node_name + status + '\n'
+        node_name = node_name + annotation
+    else:
+        node_name = classify_name
+    # 创建当前主题节点
+    topic = ET.SubElement(parent_element, "topic", id=f"topic{num}_{generate_random_key()}")
+    ET.SubElement(topic, "title").text = node_name
+    
+    # 添加图片(如果存在)
+    if 'files' in node_data:
+        for file in node_data['files']:
+            file_index = file.rfind('/')
+            file_name = file[file_index+1:]
+            images_list.append(file_name)
+            image_elem = ET.SubElement(topic, "image", src=f"resources/{file_name}", align="center")
+            image_elem.set("width", "200")
+            image_elem.set("height", "150")
+            break
+    
+    # 如果有子节点,递归添加
+    if "children" in node_data and node_data["children"]:
+        num += 1
+        children_container = ET.SubElement(topic, "children")
+        topics_container = ET.SubElement(children_container, "topics", type="attached")
+        for child in node_data["children"]:
+            add_topic_recursive(topics_container, child, ns,num,images_list)
+    # 如果有子节点,递归添加
+    if "patents" in node_data and node_data["patents"]:
+        num += 1
+        children_container = ET.SubElement(topic, "children")
+        topics_container = ET.SubElement(children_container, "topics", type="attached")
+        for child in node_data["patents"]:
+            add_topic_recursive(topics_container, child, ns,num,images_list)
+
+def create_content_xml(temp_dir,data):
+    """创建 content.xml 文件"""
+    # XML 命名空间
+    ns = "urn:xmind:xmap:xmlns:content:2.0"
+    
+    # 创建根元素
+    xmap_content = ET.Element("xmap-content", xmlns=ns, version="2.0")
+    
+    # 创建工作表
+    sheet = ET.SubElement(xmap_content, "sheet", id="sheet1")
+    
+    # 创建主题
+    topic = ET.SubElement(sheet, "topic", id="root", timestamp="0",structure="org.xmind.ui.logic.right")
+    ET.SubElement(topic, "title").text = "**厂商**品类的专利布局"
+    structure = ET.SubElement(topic, "structure")
+    structure.set("type", "str")
+    structure.text = "org.xmind.ui.logic.right"
+    
+    # 添加子节点容器
+    children_container = ET.SubElement(topic, "children")
+    topics_container = ET.SubElement(children_container, "topics", type="attached")
+
+    images_list = []
+
+    # 递归添加子节点
+    for child in data:
+        add_topic_recursive(topics_container, child, ns,1,images_list)
+
+    # 美化XML输出
+    xml_str = ET.tostring(xmap_content, encoding='utf-8')
+    pretty_xml = minidom.parseString(xml_str).toprettyxml(indent="  ")
+    pretty_xml = pretty_xml.replace('<?xml version="1.0" ?>', '<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
+    
+    # 写入文件
+    content_path = os.path.join(temp_dir, "content.xml")
+    with open(content_path, 'w', encoding='utf-8') as f:
+        f.write(pretty_xml)
+    return images_list
+
+def create_topic(parent, title, topic_id):
+    """创建主题节点"""
+    topic = ET.SubElement(parent, "topic", id=topic_id)
+    ET.SubElement(topic, "title").text = title
+    return topic
+
+def create_manifest_xml(temp_dir,images_list):
+    """创建 manifest.xml 文件"""
+    str = ''
+    if images_list:
+        for file in images_list:
+            str = str + f'<file-entry full-path="resources/{file}" media-type="image/png"/>' + '\n'
+    manifest_content = f'''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+        <manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0">
+            <file-entry full-path="content.xml" media-type="text/xml"/>
+            <file-entry full-path="META-INF/" media-type=""/>
+            <file-entry full-path="META-INF/manifest.xml" media-type="text/xml"/>
+            <file-entry full-path="resources/" media-type=""/>
+            {str}
+        </manifest>'''
+    
+    manifest_path = os.path.join(temp_dir, "META-INF", "manifest.xml")
+    with open(manifest_path, 'w', encoding='utf-8') as f:
+        f.write(manifest_content)
+    return True
+
+def create_sample_images(temp_dir,images_list):
+    """创建示例图片(实际使用中应替换为你的图片)"""
+    # 创建两个示例图片(实际项目中应使用真实图片)
+    resources_dir = os.path.join(temp_dir, "resources")
+    
+    if images_list:
+        for file in images_list:
+            shutil.copy(f"image/{file}", os.path.join(resources_dir, file))
+    return True
+
+from xmindparser import xmind_to_xml
+def etxe1():
+    xml = xmind_to_xml('result/2025-06-26_2906a7b1-baf4-4b09-b668-b80c3a8264cb.xmind')
+    print(xml)
+
+if __name__ == '__main__':
+    # etxe1()
+    app.run(debug=True, host='0.0.0.0', port=2500)