如何从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
相关产品推荐
相关产品推荐

