X Tutup
Skip to content

Commit 8102bd4

Browse files
author
Steve Canny
committed
shp: add InlineShape.type
Along the way: * enhanced BaseBuilder (XML test data builder) to accept namespace prefixed attribute names
1 parent 3420abd commit 8102bd4

File tree

12 files changed

+272
-41
lines changed

12 files changed

+272
-41
lines changed

docx/enum/shape.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ class WD_INLINE_SHAPE_TYPE(object):
1212
Corresponds to WdInlineShapeType enumeration
1313
http://msdn.microsoft.com/en-us/library/office/ff192587.aspx
1414
"""
15-
PICTURE = 3
15+
CHART = 12
1616
LINKED_PICTURE = 4
17+
PICTURE = 3
18+
SMART_ART = 15
1719
NOT_IMPLEMENTED = -6
1820

1921
WD_INLINE_SHAPE = WD_INLINE_SHAPE_TYPE

docx/oxml/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ class ValidationError(Exception):
2121

2222
from docx.oxml.shared import CT_String
2323

24+
from docx.oxml.shape import (
25+
CT_Blip, CT_BlipFillProperties, CT_GraphicalObject,
26+
CT_GraphicalObjectData, CT_Inline, CT_Picture
27+
)
28+
register_custom_element_class('a:blip', CT_Blip)
29+
register_custom_element_class('a:graphic', CT_GraphicalObject)
30+
register_custom_element_class('a:graphicData', CT_GraphicalObjectData)
31+
register_custom_element_class('pic:blipFill', CT_BlipFillProperties)
32+
register_custom_element_class('pic:pic', CT_Picture)
33+
register_custom_element_class('wp:inline', CT_Inline)
34+
2435
from docx.oxml.parts import CT_Body, CT_Document
2536
register_custom_element_class('w:body', CT_Body)
2637
register_custom_element_class('w:document', CT_Document)

docx/oxml/shape.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# encoding: utf-8
2+
3+
"""
4+
Custom element classes for shape-related elements like ``<w:inline>``
5+
"""
6+
7+
from docx.oxml.shared import OxmlBaseElement, qn
8+
9+
10+
class CT_Blip(OxmlBaseElement):
11+
"""
12+
``<a:blip>`` element, specifies image source and adjustments such as
13+
alpha and tint.
14+
"""
15+
@property
16+
def link(self):
17+
return self.get(qn('r:link'))
18+
19+
20+
class CT_BlipFillProperties(OxmlBaseElement):
21+
"""
22+
``<pic:blipFill>`` element, specifies picture properties
23+
"""
24+
@property
25+
def blip(self):
26+
return self.find(qn('a:blip'))
27+
28+
29+
class CT_GraphicalObject(OxmlBaseElement):
30+
"""
31+
``<a:graphic>`` element, container for a DrawingML object
32+
"""
33+
@property
34+
def graphicData(self):
35+
return self.find(qn('a:graphicData'))
36+
37+
38+
class CT_GraphicalObjectData(OxmlBaseElement):
39+
"""
40+
``<a:graphicData>`` element, container for the XML of a DrawingML object
41+
"""
42+
@property
43+
def pic(self):
44+
return self.find(qn('pic:pic'))
45+
46+
@property
47+
def uri(self):
48+
return self.get('uri')
49+
50+
51+
class CT_Inline(OxmlBaseElement):
52+
"""
53+
``<w:inline>`` element, container for an inline shape.
54+
"""
55+
@property
56+
def graphic(self):
57+
return self.find(qn('a:graphic'))
58+
59+
60+
class CT_Picture(OxmlBaseElement):
61+
"""
62+
``<pic:pic>`` element, a DrawingML picture
63+
"""
64+
@property
65+
def blipFill(self):
66+
return self.find(qn('pic:blipFill'))

docx/oxml/shared.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88

99

1010
nsmap = {
11-
'w': ('http://schemas.openxmlformats.org/wordprocessingml/2006/main'),
12-
'wp': ('http://schemas.openxmlformats.org/drawingml/2006/wordprocessingD'
13-
'rawing'),
11+
'a': ('http://schemas.openxmlformats.org/drawingml/2006/main'),
12+
'c': ('http://schemas.openxmlformats.org/drawingml/2006/chart'),
13+
'dgm': ('http://schemas.openxmlformats.org/drawingml/2006/diagram'),
14+
'pic': ('http://schemas.openxmlformats.org/drawingml/2006/picture'),
15+
'r': ('http://schemas.openxmlformats.org/officeDocument/2006/relations'
16+
'hips'),
17+
'w': ('http://schemas.openxmlformats.org/wordprocessingml/2006/main'),
18+
'wp': ('http://schemas.openxmlformats.org/drawingml/2006/wordprocessing'
19+
'Drawing'),
1420
}
1521

1622
# configure XML parser
@@ -23,10 +29,6 @@
2329
# utility functions
2430
# ===========================================================================
2531

26-
# def _Element(tag, nsmap=None):
27-
# return oxml_parser.makeelement(qn(tag), nsmap=nsmap)
28-
29-
3032
class NamespacePrefixedTag(str):
3133
"""
3234
Value object that knows the semantics of an XML tag having a namespace
@@ -138,6 +140,10 @@ def _SubElement(parent, tag):
138140
return etree.SubElement(parent, qn(tag), nsmap=nsmap)
139141

140142

143+
# ===========================================================================
144+
# shared custom element classes
145+
# ===========================================================================
146+
141147
class OxmlBaseElement(etree.ElementBase):
142148
"""
143149
Base class for all custom element classes, to add standardized behavior
@@ -153,10 +159,6 @@ def xml(self):
153159
return serialize_for_reading(self)
154160

155161

156-
# ===========================================================================
157-
# shared custom element classes
158-
# ===========================================================================
159-
160162
class CT_String(OxmlBaseElement):
161163
"""
162164
Used for ``<w:pStyle>`` and ``<w:tblStyle>`` elements and others,

docx/parts.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Document parts such as _Document, and closely related classes.
55
"""
66

7+
from docx.enum.shape import WD_INLINE_SHAPE
78
from docx.opc.oxml import serialize_part_xml
89
from docx.opc.package import Part
910
from docx.oxml.shared import nsmap, oxml_fromstring
@@ -108,6 +109,21 @@ def __init__(self, inline):
108109
super(InlineShape, self).__init__()
109110
self._inline = inline
110111

112+
@property
113+
def type(self):
114+
graphicData = self._inline.graphic.graphicData
115+
uri = graphicData.uri
116+
if uri == nsmap['pic']:
117+
blip = graphicData.pic.blipFill.blip
118+
if blip.link is not None:
119+
return WD_INLINE_SHAPE.LINKED_PICTURE
120+
return WD_INLINE_SHAPE.PICTURE
121+
if uri == nsmap['c']:
122+
return WD_INLINE_SHAPE.CHART
123+
if uri == nsmap['dgm']:
124+
return WD_INLINE_SHAPE.SMART_ART
125+
return WD_INLINE_SHAPE.NOT_IMPLEMENTED
126+
111127

112128
class InlineShapes(object):
113129
"""

features/shp-inline-shape-access.feature

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,23 @@ Feature: Access inline shapes in document
44
I need the ability to access the inline shapes in a document
55

66
Scenario: Access inline shapes collection of document
7-
Given a document containing two inline shapes
7+
Given a document containing five inline shapes
88
Then I can access the inline shape collection of the document
9-
And the length of the inline shape collection is 2
9+
And the length of the inline shape collection is 5
1010

1111
Scenario: Access shape in inline shape collection
12-
Given an inline shape collection containing two shapes
12+
Given an inline shape collection containing five shapes
1313
Then I can iterate over the inline shape collection
14-
And I can access an inline shape by index
14+
And I can access each inline shape by index
1515

16-
@wip
1716
Scenario Outline: Identify type of inline shape
1817
Given an inline shape known to be <shape of type>
1918
Then its inline shape type is <shape type>
2019

2120
Examples: Inline shapes of recognized types
22-
| shape of type | shape type |
23-
| an embedded picture | WD_INLINE_SHAPE.PICTURE |
24-
| a linked picture | WD_INLINE_SHAPE.LINKED_PICTURE |
21+
| shape of type | shape type |
22+
| an embedded picture | WD_INLINE_SHAPE.PICTURE |
23+
| a linked picture | WD_INLINE_SHAPE.LINKED_PICTURE |
24+
| a link+embed picture | WD_INLINE_SHAPE.LINKED_PICTURE |
25+
| a smart art diagram | WD_INLINE_SHAPE.SMART_ART |
26+
| a chart | WD_INLINE_SHAPE.CHART |

features/steps/shape.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717

1818
# given ===================================================
1919

20-
@given('a document containing two inline shapes')
20+
@given('a document containing five inline shapes')
2121
def given_a_document_containing_two_inline_shapes(context):
2222
docx_path = test_docx('shp-inline-shape-access')
2323
context.document = Document(docx_path)
2424

2525

26-
@given('an inline shape collection containing two shapes')
26+
@given('an inline shape collection containing five shapes')
2727
def given_inline_shape_collection_containing_two_shapes(context):
2828
docx_path = test_docx('shp-inline-shape-access')
2929
document = Document(docx_path)
@@ -33,8 +33,11 @@ def given_inline_shape_collection_containing_two_shapes(context):
3333
@given('an inline shape known to be {shp_of_type}')
3434
def given_inline_shape_known_to_be_shape_of_type(context, shp_of_type):
3535
inline_shape_idx = {
36-
'an embedded picture': 0,
37-
'a linked picture': 1,
36+
'an embedded picture': 0,
37+
'a linked picture': 1,
38+
'a link+embed picture': 2,
39+
'a smart art diagram': 3,
40+
'a chart': 4,
3841
}[shp_of_type]
3942
docx_path = test_docx('shp-inline-shape-access')
4043
document = Document(docx_path)
@@ -43,8 +46,8 @@ def given_inline_shape_known_to_be_shape_of_type(context, shp_of_type):
4346

4447
# then =====================================================
4548

46-
@then('I can access an inline shape by index')
47-
def then_can_access_inline_shape_by_index(context):
49+
@then('I can access each inline shape by index')
50+
def then_can_access_each_inline_shape_by_index(context):
4851
inline_shapes = context.inline_shapes
4952
for idx in range(2):
5053
inline_shape = inline_shapes[idx]
@@ -61,24 +64,30 @@ def then_can_access_inline_shape_collection_of_document(context):
6164
@then('I can iterate over the inline shape collection')
6265
def then_can_iterate_over_inline_shape_collection(context):
6366
inline_shapes = context.inline_shapes
64-
actual_count = 0
67+
shape_count = 0
6568
for inline_shape in inline_shapes:
66-
actual_count += 1
69+
shape_count += 1
6770
assert isinstance(inline_shape, InlineShape)
68-
assert actual_count == 2
71+
expected_count = 5
72+
assert shape_count == expected_count, (
73+
'expected %d, got %d' % (expected_count, shape_count)
74+
)
6975

7076

7177
@then('its inline shape type is {shape_type}')
7278
def then_inline_shape_type_is_shape_type(context, shape_type):
7379
expected_value = {
74-
'WD_INLINE_SHAPE.PICTURE': WD_INLINE_SHAPE.PICTURE,
80+
'WD_INLINE_SHAPE.CHART': WD_INLINE_SHAPE.CHART,
7581
'WD_INLINE_SHAPE.LINKED_PICTURE': WD_INLINE_SHAPE.LINKED_PICTURE,
82+
'WD_INLINE_SHAPE.PICTURE': WD_INLINE_SHAPE.PICTURE,
83+
'WD_INLINE_SHAPE.SMART_ART': WD_INLINE_SHAPE.SMART_ART,
7684
}[shape_type]
7785
inline_shape = context.inline_shape
7886
assert inline_shape.type == expected_value
7987

8088

81-
@then('the length of the inline shape collection is 2')
82-
def then_len_of_inline_shape_collection_is_2(context):
89+
@then('the length of the inline shape collection is 5')
90+
def then_len_of_inline_shape_collection_is_5(context):
8391
inline_shapes = context.document.inline_shapes
84-
assert len(inline_shapes) == 2
92+
shape_count = len(inline_shapes)
93+
assert shape_count == 5, 'got %s' % shape_count
3.2 KB
Loading
60.3 KB
Binary file not shown.

tests/oxml/unitdata/dml.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,40 @@
11
# encoding: utf-8
22

33
"""
4-
Test data builders for text XML elements
4+
Test data builders for DrawingML XML elements
55
"""
66

77
from ...unitdata import BaseBuilder
88

99

10+
class CT_BlipBuilder(BaseBuilder):
11+
__tag__ = 'a:blip'
12+
__nspfxs__ = ('a',)
13+
__attrs__ = ('r:embed', 'r:link', 'cstate')
14+
15+
16+
class CT_BlipFillPropertiesBuilder(BaseBuilder):
17+
__tag__ = 'pic:blipFill'
18+
__nspfxs__ = ('pic',)
19+
__attrs__ = ()
20+
21+
1022
class CT_DrawingBuilder(BaseBuilder):
1123
__tag__ = 'w:drawing'
1224
__nspfxs__ = ('w',)
1325
__attrs__ = ()
1426

1527

16-
def a_drawing():
17-
return CT_DrawingBuilder()
28+
class CT_GraphicalObjectBuilder(BaseBuilder):
29+
__tag__ = 'a:graphic'
30+
__nspfxs__ = ('a',)
31+
__attrs__ = ()
32+
33+
34+
class CT_GraphicalObjectDataBuilder(BaseBuilder):
35+
__tag__ = 'a:graphicData'
36+
__nspfxs__ = ('a',)
37+
__attrs__ = ('uri',)
1838

1939

2040
class CT_InlineBuilder(BaseBuilder):
@@ -23,5 +43,35 @@ class CT_InlineBuilder(BaseBuilder):
2343
__attrs__ = ('distT', 'distB', 'distL', 'distR')
2444

2545

46+
class CT_PictureBuilder(BaseBuilder):
47+
__tag__ = 'pic:pic'
48+
__nspfxs__ = ('pic',)
49+
__attrs__ = ()
50+
51+
52+
def a_blip():
53+
return CT_BlipBuilder()
54+
55+
56+
def a_blipFill():
57+
return CT_BlipFillPropertiesBuilder()
58+
59+
60+
def a_drawing():
61+
return CT_DrawingBuilder()
62+
63+
64+
def a_graphic():
65+
return CT_GraphicalObjectBuilder()
66+
67+
68+
def a_graphicData():
69+
return CT_GraphicalObjectDataBuilder()
70+
71+
72+
def a_pic():
73+
return CT_PictureBuilder()
74+
75+
2676
def an_inline():
2777
return CT_InlineBuilder()

0 commit comments

Comments
 (0)
X Tutup