Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PicturePlaceholder.insert_picture() #757

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pptx/oxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ def oxml_tostring(elm, encoding=None, pretty_print=False, standalone=None):
# if xsi parameter is not set to False, PowerPoint won't load without a
# repair step; deannotate removes some original xsi:type tags in core.xml
# if this parameter is left out (or set to True)
objectify.deannotate(elm, xsi=False, cleanup_namespaces=True)
# objectify.deannotate(elm, xsi=False, cleanup_namespaces=True)
objectify.deannotate(elm, xsi=False, cleanup_namespaces=False)
return etree.tostring(elm, encoding=encoding, pretty_print=pretty_print,
standalone=standalone)

Expand Down Expand Up @@ -250,6 +251,8 @@ def __set_str_prop(self, name, value):
raise ValueError(tmpl % (name, value))
tag = qn(CT_CoreProperties._str_tags[name])
setattr(self, tag, value)
elm = getattr(self, tag)
objectify.deannotate(elm, cleanup_namespaces=True)

def __set_date_prop(self, name, value):
"""Set datetime value of *name* property to *value*"""
Expand All @@ -261,6 +264,8 @@ def __set_date_prop(self, name, value):
tag = qn(tagname)
dt_str = value.strftime('%Y-%m-%dT%H:%M:%SZ')
setattr(self, tag, dt_str)
elm = getattr(self, tag)
objectify.deannotate(elm, cleanup_namespaces=True)
if name in ('created', 'modified'):
# these two require an explicit 'xsi:type' attribute
# first and last line are a hack required to add the xsi
Expand All @@ -277,6 +282,8 @@ def __set_revision(self, value):
raise ValueError(tmpl % value)
tag = qn('cp:revision')
setattr(self, tag, str(value))
elm = getattr(self, tag)
objectify.deannotate(elm, cleanup_namespaces=True)

_offset_pattern = re.compile('([+-])(\d\d):(\d\d)')

Expand Down
16 changes: 8 additions & 8 deletions pptx/presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1094,14 +1094,14 @@ def __minimal_element(self):
Return element containing the minimal XML for a slide, based on what
is required by the XMLSchema.
"""
sld = _Element('p:sld', _nsmap)
_SubElement(sld, 'p:cSld', _nsmap)
_SubElement(sld.cSld, 'p:spTree', _nsmap)
_SubElement(sld.cSld.spTree, 'p:nvGrpSpPr', _nsmap)
_SubElement(sld.cSld.spTree.nvGrpSpPr, 'p:cNvPr', _nsmap)
_SubElement(sld.cSld.spTree.nvGrpSpPr, 'p:cNvGrpSpPr', _nsmap)
_SubElement(sld.cSld.spTree.nvGrpSpPr, 'p:nvPr', _nsmap)
_SubElement(sld.cSld.spTree, 'p:grpSpPr', _nsmap)
sld = _Element('p:sld', namespaces('p'))
_SubElement(sld, 'p:cSld', namespaces('p'))
_SubElement(sld.cSld, 'p:spTree', namespaces('p'))
_SubElement(sld.cSld.spTree, 'p:nvGrpSpPr', namespaces('p'))
_SubElement(sld.cSld.spTree.nvGrpSpPr, 'p:cNvPr', namespaces('p'))
_SubElement(sld.cSld.spTree.nvGrpSpPr, 'p:cNvGrpSpPr', namespaces('p'))
_SubElement(sld.cSld.spTree.nvGrpSpPr, 'p:nvPr', namespaces('p'))
_SubElement(sld.cSld.spTree, 'p:grpSpPr', namespaces('p'))
sld.cSld.spTree.nvGrpSpPr.cNvPr.set('id', '1')
sld.cSld.spTree.nvGrpSpPr.cNvPr.set('name', '')
return sld
Expand Down
19 changes: 11 additions & 8 deletions pptx/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def title(self):
return shape
return None

def add_picture(self, file, left, top, width=None, height=None):
def add_picture(self, file, left, top, width=None, height=None, z_idx=-1):
"""
Add picture shape displaying image in *file*, where *file* can be
either a path to a file (a string) or a file-like object.
Expand All @@ -494,9 +494,12 @@ def add_picture(self, file, left, top, width=None, height=None):

pic = CT_Picture.new_pic(id, name, desc, rId, left, top, width, height)

self.__spTree.append(pic)
idx = len(self.__shapes) if z_idx == -1 else z_idx
# self.__spTree.append(pic)
self.__spTree.insert(idx+2, pic)
picture = _Picture(pic)
self.__shapes.append(picture)
# self.__shapes.append(picture)
self.__shapes.insert(idx, picture)
return picture

def add_shape(self, autoshape_type_id, left, top, width, height):
Expand Down Expand Up @@ -1191,7 +1194,7 @@ def add_paragraph(self):
paragraphs contained in this text frame.
"""
# <a:p> elements are last in txBody, so can simply append new one
p = _Element('a:p', _nsmap)
p = _Element('a:p', namespaces('a'))
self.__txBody.append(p)
return _Paragraph(p)

Expand Down Expand Up @@ -1297,7 +1300,7 @@ def font(self):
# included in the XML if the _Font element is referred to but not
# populated with values.
if not hasattr(self.__p, 'pPr'):
pPr = _Element('a:pPr', _nsmap)
pPr = _Element('a:pPr', namespaces('a'))
self.__p.insert(0, pPr)
if not hasattr(self.__p.pPr, 'defRPr'):
_SubElement(self.__p.pPr, 'a:defRPr')
Expand All @@ -1320,7 +1323,7 @@ def _set_level(self, level):
msg = "paragraph level must be integer between 0 and 8 inclusive"
raise ValueError(msg)
if not hasattr(self.__p, 'pPr'):
pPr = _Element('a:pPr', _nsmap)
pPr = _Element('a:pPr', namespaces('a'))
self.__p.insert(0, pPr)
self.__p.pPr.set('lvl', str(level))

Expand Down Expand Up @@ -1359,7 +1362,7 @@ def _set_text(self, text):

def add_run(self):
"""Return a new run appended to the runs in this paragraph."""
r = _Element('a:r', _nsmap)
r = _Element('a:r', namespaces('a'))
_SubElement(r, 'a:t')
# work out where to insert it, ahead of a:endParaRPr if there is one
endParaRPr = _child(self.__p, 'a:endParaRPr')
Expand Down Expand Up @@ -1396,7 +1399,7 @@ def font(self):
level are contained in the |_Font| object.
"""
if not hasattr(self.__r, 'rPr'):
self.__r.insert(0, _Element('a:rPr', _nsmap))
self.__r.insert(0, _Element('a:rPr', namespaces('a')))
return _Font(self.__r.rPr)

@property
Expand Down
1 change: 0 additions & 1 deletion test/test_files/minimal_slide.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<p:sld xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
<p:cSld>
<p:spTree>
Expand Down
19 changes: 3 additions & 16 deletions test/test_presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from pptx.oxml import (
CT_CoreProperties, oxml_fromstring, oxml_parse, oxml_tostring
)
from pptx.packaging import prettify_nsdecls
from pptx.presentation import (
_BasePart, _BaseSlide, _CoreProperties, _Image, _Package, _Part,
_PartCollection, Presentation, _Relationship, _RelationshipCollection,
Expand Down Expand Up @@ -983,12 +982,8 @@ def test__element_minimal_sld_on_construction(self):
elm = self.sld._element
# verify -----------------------
with open(path, 'r') as f:
expected = f.read()
actual = prettify_nsdecls(
oxml_tostring(elm, encoding='UTF-8', pretty_print=True,
standalone=True))
msg = "\nexpected:\n\n'%s'\n\nbut got:\n\n'%s'" % (expected, actual)
self.assertEqual(expected, actual, msg)
expected_xml = f.read()
self.assertEqualLineByLine(expected_xml, elm)

def test_slidelayout_property_none_on_construction(self):
"""_Slide.slidelayout property None on construction"""
Expand Down Expand Up @@ -1028,15 +1023,7 @@ def test___minimal_element_xml(self):
# verify -----------------------
with open(path, 'r') as f:
expected_xml = f.read()
sld_xml = prettify_nsdecls(
oxml_tostring(sld, encoding='UTF-8', pretty_print=True,
standalone=True))
sld_xml_lines = sld_xml.split('\n')
expected_xml_lines = expected_xml.split('\n')
for idx, line in enumerate(sld_xml_lines):
# msg = '\n\n%s' % sld_xml
msg = "expected:\n%s\n, got\n%s" % (expected_xml, sld_xml)
self.assertEqual(line, expected_xml_lines[idx], msg)
self.assertEqualLineByLine(expected_xml, sld)


class Test_SlideCollection(TestCase):
Expand Down
6 changes: 3 additions & 3 deletions test/test_shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,7 @@ def test_add_picture_collaboration(self, next_shape_id, CT_Picture,
slide = Mock(name='slide')
slide._add_image.return_value = image, rel
__spTree = Mock(name='__spTree')
__shapes = Mock(name='__shapes')
__shapes = MagicMock(name='__shapes')
shapes = _ShapeCollection(test_shape_elements.empty_spTree, slide)
shapes._ShapeCollection__spTree = __spTree
shapes._ShapeCollection__shapes = __shapes
Expand All @@ -1249,9 +1249,9 @@ def test_add_picture_collaboration(self, next_shape_id, CT_Picture,
image._scale.assert_called_once_with(width, height)
CT_Picture.new_pic.assert_called_once_with(
id_, name, desc, rId, left, top, width, height)
__spTree.append.assert_called_once_with(pic)
__spTree.insert.assert_called_once_with(2, pic)
_Picture.assert_called_once_with(pic)
__shapes.append.assert_called_once_with(picture)
__shapes.insert.assert_called_once_with(0, picture)
assert_that(retval, is_(equal_to(picture)))

@patch('pptx.shapes._Table')
Expand Down
2 changes: 1 addition & 1 deletion test/testdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(self):

@property
def _ns_prefixes(self):
ns_prefixes = ['cp']
ns_prefixes = ['cp', 'dc', 'dcterms']
for propname, tag in self.properties:
value = getattr(self, '_%s' % propname)
if value is None:
Expand Down
3 changes: 3 additions & 0 deletions test/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import unittest2

# from lxml import objectify

from pptx.oxml import oxml_tostring


Expand All @@ -21,6 +23,7 @@ def assertEqualLineByLine(self, expected_xml, element):
Apply assertEqual() to each line of *expected_xml* and corresponding
line of XML derived from *element*.
"""
# objectify.deannotate(element, xsi=False, cleanup_namespaces=True)
actual_xml = oxml_tostring(element, pretty_print=True)
actual_xml_lines = actual_xml.split('\n')
expected_xml_lines = expected_xml.split('\n')
Expand Down