You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Visio的VDX文件提取形状文本?现有代码无法正常显示

问题

目标是提取Visio VDX文件中每个形状的形状信息、形状类型、文本内容以及所在泳道。目前已成功提取所有形状及其类型,但代码能识别形状内存在文本,却无法正确显示(反而显示异常值或空值)。尝试过使用.text属性或转为字符串,但均无效。

用户原代码:

import xml.etree.ElementTree as ET

# Load the .VDX file
tree = ET.parse('Test VDX.vdx')
root = tree.getroot()

# Define the namespace used in the .VDX file
ns = {'visio': 'http://schemas.microsoft.com/visio/2003/core'}

# Find all shape elements in the .VDX file
pages = root.findall('.//visio:Page', ns)
shapes = root.findall('.//visio:Shape', ns)

# Iterate over the shapes and extract information
for page in pages:
    page_id = page.get('ID')
    page_name = page.get('NameU')
    print(f"Page ID: {page_id}, Name: {page_name}")

for shape in shapes:
    shape_id = shape.get('ID')
    shape_name = shape.get('Name')
    shape_type = shape.get('Type')
    shape_text_element = shape.find('.//visio:Text', ns)
    if shape_text_element is not None:
        shape_text = shape_text_element.text
    else:
        shape_text = 'TExt'
    print(f"Shape ID: {shape_id}, Name: {shape_name}, Type: {shape_type}, Text: {shape_text}")

VDX文件中的Shape节点示例:

<Shape ID="21" Type="Shape" Name="Rectangle Fill:Marble.21">
      <XForm>
        <Angle>-0</Angle>
        <PinX>5.413385833333333</PinX>
        <PinY>6.181102430555556</PinY>
        <Width>1.377952777777778</Width>
        <Height>0.59055125</Height>
        <LocPinX>0.6889763888888889</LocPinX>
        <LocPinY>0.295275625</LocPinY>
      </XForm>
      <TextXForm>
        <TxtPinX F="Width*0.500000">1.322397232055664</TxtPinX>
        <TxtLocPinX F="Width*0.500000">1.322397232055664</TxtLocPinX>
        <TxtPinY F="Height*0.500000">0.4719645182291667</TxtPinY>
        <TxtLocPinY F="Height*0.500000">0.4719645182291667</TxtLocPinY>
        <TxtWidth F="Width*1">1.322397232055664</TxtWidth>
        <TxtHeight F="Height*1">0.4719645182291667</TxtHeight>
        <TxtAngle>-0</TxtAngle>
      </TextXForm>
      <Prop ID="0" NameU="Row_0">
        <Type>0</Type>
        <Value Unit="STR">Purchaser</Value>
        <Label/>
      </Prop>
      <Prop ID="1" NameU="Row_1">
        <Type>0</Type>
        <Value Unit="STR">0</Value>
        <Label>Cost</Label>
      </Prop>
      <Prop ID="2" NameU="Row_2">
        <Type>0</Type>
        <Value Unit="STR">0</Value>
        <Label>Duration</Label>
      </Prop>
      <Prop ID="3" NameU="Row_3">
        <Type>0</Type>
        <Value Unit="STR">0</Value>
        <Label>Resources</Label>
      </Prop>
      <Misc>
        <ObjType>1</ObjType>
      </Misc>
      <Line>
        <LinePattern>1</LinePattern>
        <LineWeight>0.00333333</LineWeight>
        <LineColor>0</LineColor>
        <LineColorTrans>0</LineColorTrans>
        <Rounding>0</Rounding>
        <LineCap>0</LineCap>
      </Line>
      <Fill>
        <FillPattern>1</FillPattern>
        <FillForegnd>#e8eef7</FillForegnd>
        <FillForegndTrans>0</FillForegndTrans>
        <ShdwPattern>0</ShdwPattern>
        <ShdwForegnd>#ffffff</ShdwForegnd>
        <ShdwForegndTrans>0</ShdwForegndTrans>
        <ShapeShdwType>1</ShapeShdwType>
        <ShapeShdwOffsetX>0.11811</ShapeShdwOffsetX>
        <ShapeShdwOffsetY>-0.11811</ShapeShdwOffsetY>
      </Fill>
      <Geom IX="0">
        <NoFill>0</NoFill>
        <NoLine>0</NoLine>
        <MoveTo IX="1">
          <X F="Width*0.000000">0</X>
          <Y F="Height*1.000000">0.5905512499999995</Y>
        </MoveTo>
        <LineTo IX="2">
          <X F="Width*1.000000">1.377952777777777</X>
          <Y F="Height*1.000000">0.5905512499999995</Y>
        </LineTo>
        <LineTo IX="3">
          <X F="Width*1.000000">1.377952777777777</X>
          <Y F="Height*0.000000">0</Y>
        </LineTo>
        <LineTo IX="4">
          <X F="Width*0.000000">0</X>
          <Y F="Height*0.000000">0</Y>
        </LineTo>
        <LineTo IX="5">
          <X F="Width*0.000000">0</X>
          <Y F="Height*1.000000">0.5905512499999995</Y>
        </LineTo>
      </Geom>
      <LayerMem>
        <LayerMember>0</LayerMember>
      </LayerMem>
      <Connection ID="0">
        <X F="Width*0.000000">0</X>
        <Y F="Width*0.214286">0.2952755555555563</Y>
        <Type>0</Type>
      </Connection>
      <Connection ID="1">
        <X F="Width*1.000000">1.377952777777777</X>
        <Y F="Width*0.214286">0.2952755555555563</Y>
        <Type>0</Type>
      </Connection>
      <Connection ID="2">
        <X F="Width*0.500000">0.6889763888888886</X>
        <Y F="Width*0.000000">0</Y>
        <Type>0</Type>
      </Connection>
      <Connection ID="3">
        <X F="Width*0.500000">0.6889763888888886</X>
        <Y F="Width*0.428571">0.5905512499999995</Y>
        <Type>0</Type>
      </Connection>
      <TextBlock>
        <LeftMargin>0.0277778</LeftMargin>
        <RightMargin>0.0277778</RightMargin>
        <TopMargin>0.0277778</TopMargin>
        <BottomMargin>0.0277778</BottomMargin>
        <VerticalAlign>1</VerticalAlign>
        <DefaultTabStop>0</DefaultTabStop>
      </TextBlock>
      <Char IX="0">
        <Font>0</Font>
        <Color>0</Color>
        <Style>0</Style>
        <Size>0.138889</Size>
        <ColorTrans>0</ColorTrans>
      </Char>
      <Para IX="0">
        <IndFirst>0</IndFirst>
        <IndLeft>0</IndLeft>
        <IndRight>0</IndRight>
        <SpLine>-1.2</SpLine>
        <SpBefore>0</SpBefore>
        <HorzAlign>1</HorzAlign>
      </Para>
      <Text><cp IX="0"/><pp IX="0"/>Attach Pos to invoice and complete coding form</Text>
    </Shape>

问题原因分析

从VDX的Shape节点可以看到,<Text>节点内并非直接包含文本内容,而是先有<cp IX="0"/>和<pp IX="0"/>两个子元素,实际文本是这两个子元素的尾部文本(tail),而非<Text>节点自身的.text属性。

在XML ElementTree中,.text获取的是当前元素起始标签后的第一个文本段;如果元素内部有子元素,后续的文本会被视为子元素的.tail属性。原代码只读取了<Text>节点的.text,自然获取不到实际内容。

解决方案(修改后的代码)

import xml.etree.ElementTree as ET

# 加载VDX文件
tree = ET.parse('Test VDX.vdx')
root = tree.getroot()

# 定义VDX文件使用的命名空间
ns = {'visio': 'http://schemas.microsoft.com/visio/2003/core'}

# 遍历页面
pages = root.findall('.//visio:Page', ns)
for page in pages:
    page_id = page.get('ID')
    page_name = page.get('NameU')
    print(f"页面ID: {page_id}, 名称: {page_name}")

# 遍历所有形状
shapes = root.findall('.//visio:Shape', ns)
for shape in shapes:
    shape_id = shape.get('ID')
    shape_name = shape.get('Name')
    shape_type = shape.get('Type')
    
    shape_text_element = shape.find('.//visio:Text', ns)
    shape_text = ''
    if shape_text_element is not None:
        # 先获取Text节点自身的文本(如果存在)
        if shape_text_element.text:
            shape_text += shape_text_element.text
        # 遍历所有子元素,收集它们的tail文本
        for child in shape_text_element:
            if child.tail:
                shape_text += child.tail
    else:
        shape_text = '无文本'
    
    print(f"形状ID: {shape_id}, 名称: {shape_name}, 类型: {shape_type}, 文本: {shape_text}")

关键修改点

  • 不再仅读取<Text>节点的.text,而是遍历<Text>节点的所有子元素,收集子元素的.tail属性内容,这才是形状的实际文本。
  • 增加文本拼接逻辑,确保所有分散的文本段都被整合。

内容的提问来源于stack exchange,提问作者HaggisBonBon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:07:00