Skip to content

Commit 81ab148

Browse files
author
Steve Canny
committed
add _ContentTypesItem.xml_for()
1 parent 7064640 commit 81ab148

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

opc/pkgwriter.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
Convention (OPC) package, essentially an implementation of OpcPackage.save()
1313
"""
1414

15+
from opc.constants import CONTENT_TYPE as CT
16+
from opc.oxml import CT_Types, oxml_tostring
1517
from opc.packuri import CONTENT_TYPES_URI
1618
from opc.phys_pkg import PhysPkgWriter
19+
from opc.spec import default_content_types
1720

1821

1922
class PackageWriter(object):
@@ -74,3 +77,38 @@ def xml_for(parts):
7477
appropriate content type and suitable for storage as
7578
``[Content_Types].xml`` in an OPC package.
7679
"""
80+
defaults = dict((('.rels', CT.OPC_RELATIONSHIPS), ('.xml', CT.XML)))
81+
overrides = dict()
82+
for part in parts:
83+
_ContentTypesItem._add_content_type(
84+
defaults, overrides, part.partname, part.content_type
85+
)
86+
return _ContentTypesItem._xml(defaults, overrides)
87+
88+
@staticmethod
89+
def _add_content_type(defaults, overrides, partname, content_type):
90+
"""
91+
Add a content type for the part with *partname* and *content_type*,
92+
using a default or override as appropriate.
93+
"""
94+
ext = partname.ext
95+
if (ext, content_type) in default_content_types:
96+
defaults[ext] = content_type
97+
else:
98+
overrides[partname] = content_type
99+
100+
@staticmethod
101+
def _xml(defaults, overrides):
102+
"""
103+
XML form of this content types item, suitable for storage as
104+
``[Content_Types].xml`` in an OPC package. Although the sequence of
105+
elements is not strictly significant, as an aid to testing and
106+
readability Default elements are sorted by extension and Override
107+
elements are sorted by partname.
108+
"""
109+
_types_elm = CT_Types.new()
110+
for ext in sorted(defaults.keys()):
111+
_types_elm.add_default(ext, defaults[ext])
112+
for partname in sorted(overrides.keys()):
113+
_types_elm.add_override(partname, overrides[partname])
114+
return oxml_tostring(_types_elm, encoding='UTF-8', standalone=True)

opc/spec.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# spec.py
4+
#
5+
# Copyright (C) 2013 Steve Canny scanny@cisco.com
6+
#
7+
# This module is part of python-opc and is released under the MIT License:
8+
# http://www.opensource.org/licenses/mit-license.php
9+
10+
"""
11+
Provides mappings that embody aspects of the Open XML spec ISO/IEC 29500.
12+
"""
13+
14+
from opc.constants import CONTENT_TYPE as CT
15+
16+
17+
default_content_types = (
18+
('.bin', CT.PML_PRINTER_SETTINGS),
19+
('.bin', CT.SML_PRINTER_SETTINGS),
20+
('.bin', CT.WML_PRINTER_SETTINGS),
21+
('.bmp', CT.BMP),
22+
('.emf', CT.X_EMF),
23+
('.fntdata', CT.X_FONTDATA),
24+
('.gif', CT.GIF),
25+
('.jpe', CT.JPEG),
26+
('.jpeg', CT.JPEG),
27+
('.jpg', CT.JPEG),
28+
('.png', CT.PNG),
29+
('.rels', CT.OPC_RELATIONSHIPS),
30+
('.tif', CT.TIFF),
31+
('.tiff', CT.TIFF),
32+
('.wdp', CT.MS_PHOTO),
33+
('.wmf', CT.X_WMF),
34+
('.xlsx', CT.SML_SHEET),
35+
('.xml', CT.XML),
36+
)

tests/test_pkgwriter.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313

1414
from mock import call, Mock, patch
1515

16+
from opc.constants import CONTENT_TYPE as CT
17+
from opc.packuri import PackURI
1618
from opc.pkgwriter import _ContentTypesItem, PackageWriter
1719

18-
from .unitutil import method_mock
20+
from .unitutil import function_mock, method_mock
1921

2022

2123
class DescribePackageWriter(object):
@@ -77,3 +79,53 @@ def it_can_write_a_content_types_stream(self, xml_for):
7779
xml_for.assert_called_once_with(parts)
7880
phys_writer.write.assert_called_once_with('/[Content_Types].xml',
7981
xml_for.return_value)
82+
83+
84+
class Describe_ContentTypesItem(object):
85+
86+
@pytest.fixture
87+
def oxml_tostring(self, request):
88+
return function_mock('opc.pkgwriter.oxml_tostring', request)
89+
90+
@pytest.fixture
91+
def parts(self):
92+
"""list of parts that will exercise _ContentTypesItem.xml_for()"""
93+
return [
94+
Mock(name='part_1', partname=PackURI('/docProps/core.xml'),
95+
content_type='app/vnd.core'),
96+
Mock(name='part_2', partname=PackURI('/docProps/thumbnail.jpeg'),
97+
content_type=CT.JPEG),
98+
Mock(name='part_3', partname=PackURI('/ppt/slides/slide2.xml'),
99+
content_type='app/vnd.ct_sld'),
100+
Mock(name='part_4', partname=PackURI('/ppt/slides/slide1.xml'),
101+
content_type='app/vnd.ct_sld'),
102+
Mock(name='part_5', partname=PackURI('/zebra/foo.bar'),
103+
content_type='app/vnd.foobar'),
104+
]
105+
106+
@pytest.fixture
107+
def types(self, request):
108+
"""Mock returned by CT_Types.new() call"""
109+
types = Mock(name='types')
110+
_patch = patch('opc.pkgwriter.CT_Types')
111+
CT_Types = _patch.start()
112+
CT_Types.new.return_value = types
113+
request.addfinalizer(_patch.stop)
114+
return types
115+
116+
def it_can_compose_content_types_xml(self, parts, types, oxml_tostring):
117+
# # exercise ---------------------
118+
_ContentTypesItem.xml_for(parts)
119+
# verify -----------------------
120+
expected_types_calls = [
121+
call.add_default('.jpeg', CT.JPEG),
122+
call.add_default('.rels', CT.OPC_RELATIONSHIPS),
123+
call.add_default('.xml', CT.XML),
124+
call.add_override('/docProps/core.xml', 'app/vnd.core'),
125+
call.add_override('/ppt/slides/slide1.xml', 'app/vnd.ct_sld'),
126+
call.add_override('/ppt/slides/slide2.xml', 'app/vnd.ct_sld'),
127+
call.add_override('/zebra/foo.bar', 'app/vnd.foobar'),
128+
]
129+
assert types.mock_calls == expected_types_calls
130+
oxml_tostring.assert_called_once_with(types, encoding='UTF-8',
131+
standalone=True),

0 commit comments

Comments
 (0)