X Tutup
Skip to content

Commit af1afc0

Browse files
author
Steve Canny
committed
img: add ImageParts._next_image_partname
1 parent 4151eb9 commit af1afc0

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

docx/package.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from docx.opc.constants import RELATIONSHIP_TYPE as RT
1010
from docx.opc.package import OpcPackage
11+
from docx.opc.packuri import PackURI
1112
from docx.parts.image import Image, ImagePart
1213
from docx.shared import lazyproperty
1314

@@ -56,6 +57,9 @@ def __init__(self):
5657
def __contains__(self, item):
5758
return self._image_parts.__contains__(item)
5859

60+
def __iter__(self):
61+
return self._image_parts.__iter__()
62+
5963
def __len__(self):
6064
return self._image_parts.__len__()
6165

@@ -79,7 +83,7 @@ def _add_image_part(self, image):
7983
Return an |ImagePart| instance newly created from image and appended
8084
to the collection.
8185
"""
82-
partname = self._next_image_partname
86+
partname = self._next_image_partname(image.ext)
8387
image_part = ImagePart.from_image(image, partname)
8488
self.append(image_part)
8589
return image_part
@@ -94,10 +98,17 @@ def _get_by_sha1(self, sha1):
9498
return image_part
9599
return None
96100

97-
@property
98-
def _next_image_partname(self):
101+
def _next_image_partname(self, ext):
99102
"""
100103
The next available image partname, starting from
101-
``/word/media/image1.{ext}`` where unused numbers are reused.
104+
``/word/media/image1{ext}`` where unused numbers are reused. The
105+
partname is unique by number, without regard to the extension. *ext*
106+
must include the leading period.
102107
"""
103-
raise NotImplementedError
108+
def image_partname(n):
109+
return PackURI('/word/media/image%d%s' % (n, ext))
110+
used_numbers = [image_part.partname.idx for image_part in self]
111+
for n in range(1, len(self)+1):
112+
if not n in used_numbers:
113+
return image_partname(n)
114+
return image_partname(len(self)+1)

docx/parts/image.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ def content_type(self):
3636
"""
3737
raise NotImplementedError
3838

39+
@property
40+
def ext(self):
41+
"""
42+
The file extension for the image. If an actual one is available from
43+
a load filename it is used. Otherwise a canonical extension is
44+
assigned based on the content type.
45+
"""
46+
raise NotImplementedError
47+
3948
@property
4049
def filename(self):
4150
"""

tests/test_package.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from docx.parts.image import Image, ImagePart
1414

1515
from .unitutil import (
16-
docx_path, class_mock, instance_mock, method_mock, property_mock
16+
docx_path, class_mock, instance_mock, method_mock
1717
)
1818

1919

@@ -39,6 +39,13 @@ def it_can_add_a_new_image_part(self, add_image_part_fixture):
3939
image_parts._add_image_part.assert_called_once_with(image_)
4040
assert image_part is image_part_
4141

42+
def it_knows_the_next_available_image_partname(
43+
self, next_partname_fixture):
44+
image_parts, ext, expected_partname = next_partname_fixture
45+
assert image_parts._next_image_partname(ext) == expected_partname
46+
print('\n%s' % image_parts._next_image_partname(ext))
47+
print(expected_partname)
48+
4249
def it_can_really_add_a_new_image_part(
4350
self, really_add_image_part_fixture):
4451
image_parts, image_, ImagePart_, partname_, image_part_ = (
@@ -99,13 +106,33 @@ def image_part_(self, request, sha1):
99106
image_part_.sha1 = sha1
100107
return image_part_
101108

109+
def _image_part_with_partname_(self, request, n):
110+
partname = self._image_partname(n)
111+
return instance_mock(request, ImagePart, partname=partname)
112+
113+
def _image_partname(self, n):
114+
return PackURI('/word/media/image%d.png' % n)
115+
102116
@pytest.fixture
103117
def new_image_part_(self, request):
104118
return instance_mock(request, ImagePart)
105119

106120
@pytest.fixture
107121
def _next_image_partname_(self, request):
108-
return property_mock(request, ImageParts, '_next_image_partname')
122+
return method_mock(request, ImageParts, '_next_image_partname')
123+
124+
@pytest.fixture(params=[((2, 3), 1), ((1, 3), 2), ((1, 2), 3)])
125+
def next_partname_fixture(self, request):
126+
existing_partname_numbers, expected_partname_number = request.param
127+
image_parts = ImageParts()
128+
for n in existing_partname_numbers:
129+
image_part_ = self._image_part_with_partname_(request, n)
130+
image_parts.append(image_part_)
131+
ext = '.png'
132+
expected_image_partname = self._image_partname(
133+
expected_partname_number
134+
)
135+
return image_parts, ext, expected_image_partname
109136

110137
@pytest.fixture
111138
def partname_(self, request):

0 commit comments

Comments
 (0)
X Tutup