Skip to content

Commit 4008ab2

Browse files
author
Steve Canny
committed
img: add Image.scaled_dimensions()
1 parent 66fedd4 commit 4008ab2

File tree

4 files changed

+47
-11
lines changed

4 files changed

+47
-11
lines changed

docx/image/image.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from ..compat import BytesIO, is_string
1414
from .exceptions import UnrecognizedImageError
15-
from ..shared import Inches, lazyproperty
15+
from ..shared import Emu, Inches, lazyproperty
1616

1717

1818
class Image(object):
@@ -133,7 +133,7 @@ def height(self):
133133
"""
134134
return Inches(self.px_height / self.vert_dpi)
135135

136-
def scaled_dimensions(self, width, height):
136+
def scaled_dimensions(self, width=None, height=None):
137137
"""
138138
Return a (cx, cy) 2-tuple representing the native dimensions of this
139139
image scaled by applying the following rules to *width* and *height*.
@@ -147,7 +147,18 @@ def scaled_dimensions(self, width, height):
147147
dpi if no value is specified, as is often the case. The returned
148148
values are both |Length| objects.
149149
"""
150-
raise NotImplementedError
150+
if width is None and height is None:
151+
return self.width, self.height
152+
153+
if width is None:
154+
scaling_factor = float(height) / float(self.height)
155+
width = round(self.width * scaling_factor)
156+
157+
if height is None:
158+
scaling_factor = float(width) / float(self.width)
159+
height = round(self.height * scaling_factor)
160+
161+
return Emu(width), Emu(height)
151162

152163
@lazyproperty
153164
def sha1(self):

features/doc-add-picture.feature

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,26 @@ Feature: Append an inline picture in its own paragraph
44
I need a way to add a picture in its own paragraph
55

66

7-
@wip
87
Scenario: Add a picture at native size
98
Given a blank document
109
When I add a picture specifying only the image file
1110
Then the document contains the inline picture
1211
And the picture has its native width and height
1312

1413

15-
@wip
1614
Scenario: Add a picture specifying both width and height
1715
Given a blank document
1816
When I add a picture specifying 1.75" width and 2.5" height
1917
Then picture.width is 1.75 inches
2018
And picture.height is 2.5 inches
2119

2220

23-
@wip
2421
Scenario: Add a picture specifying only width
2522
Given a blank document
2623
When I add a picture specifying a width of 1.5 inches
2724
Then picture.height is 2.14 inches
2825

2926

30-
@wip
3127
Scenario: Add a picture specifying only height
3228
Given a blank document
3329
When I add a picture specifying a height of 1.5 inches

features/run-add-picture.feature

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ Feature: Add picture to a run
44
I need a way to add a picture to a run
55

66

7-
@wip
87
Scenario: Add a picture to a body paragraph run
98
Given a run
109
When I add a picture to the run
1110
Then the picture appears at the end of the run
1211
And the document contains the inline picture
1312

1413

15-
@wip
1614
Scenario Outline: Add a picture to a run in a table cell
1715
Given a run inside a table cell retrieved from <cell-source>
1816
When I add a picture to the run

tests/image/test_image.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
from docx.image.png import Png
1818
from docx.image.tiff import Tiff
1919
from docx.opc.constants import CONTENT_TYPE as CT
20-
from docx.shared import Length
20+
from docx.shared import Emu, Length
2121

2222
from ..unitutil.file import test_file
2323
from ..unitutil.mock import (
24-
function_mock, class_mock, initializer_mock, instance_mock, method_mock
24+
function_mock, class_mock, initializer_mock, instance_mock, method_mock,
25+
property_mock
2526
)
2627

2728

@@ -90,6 +91,15 @@ def it_knows_the_image_native_size(self, size_fixture):
9091
assert isinstance(image.width, Length)
9192
assert isinstance(image.height, Length)
9293

94+
def it_can_scale_its_dimensions(self, scale_fixture):
95+
image, width, height, expected_value = scale_fixture
96+
97+
scaled_width, scaled_height = image.scaled_dimensions(width, height)
98+
99+
assert (scaled_width, scaled_height) == expected_value
100+
assert isinstance(scaled_width, Length)
101+
assert isinstance(scaled_height, Length)
102+
93103
def it_knows_the_image_filename(self):
94104
filename = 'foobar.png'
95105
image = Image(None, filename, None)
@@ -189,6 +199,19 @@ def known_image_fixture(self, request):
189199
image_filename, characteristics = cases[request.param]
190200
return image_filename, characteristics
191201

202+
@pytest.fixture(params=[
203+
(None, None, 1000, 2000),
204+
(100, None, 100, 200),
205+
(None, 500, 250, 500),
206+
(1500, 1500, 1500, 1500),
207+
])
208+
def scale_fixture(self, request, width_prop_, height_prop_):
209+
width, height, scaled_width, scaled_height = request.param
210+
width_prop_.return_value = Emu(1000)
211+
height_prop_.return_value = Emu(2000)
212+
image = Image(None, None, None)
213+
return image, width, height, (scaled_width, scaled_height)
214+
192215
@pytest.fixture
193216
def size_fixture(self, image_header_):
194217
image_header_.px_width, image_header_.px_height = 150, 75
@@ -218,6 +241,10 @@ def _from_stream_(self, request, image_):
218241
request, Image, '_from_stream', return_value=image_
219242
)
220243

244+
@pytest.fixture
245+
def height_prop_(self, request):
246+
return property_mock(request, Image, 'height')
247+
221248
@pytest.fixture
222249
def image_(self, request):
223250
return instance_mock(request, Image)
@@ -241,6 +268,10 @@ def Image__init_(self, request):
241268
def stream_(self, request):
242269
return instance_mock(request, BytesIO)
243270

271+
@pytest.fixture
272+
def width_prop_(self, request):
273+
return property_mock(request, Image, 'width')
274+
244275

245276
class Describe_ImageHeaderFactory(object):
246277

0 commit comments

Comments
 (0)