diff --git a/.gitignore b/.gitignore index 9e9dbcf6..f9edf881 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ dist *.egg *.egg-info .*.swp +.cache .coverage .DS_Store diff --git a/.travis.yml b/.travis.yml index 08f8e2f0..0f6deb6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: - secure: "EhG2uSD2m1eGxuL2HeQewJJx7MVL4WpjrxyfiUBEgsApemD1yKJPjUnLwAyd\nbPi5HJx5ySm1TTRSvj6/yP85YLYTaJHA8OabKk7p0wFW294qcrYIDGovU7NL\n3YOqZmqN+S3XOBGFCOnByxE+pcxxWW/3/I09EgeW7D6tBPh67G0=" install: - echo $TRAVIS_PYTHON_VERSION -- pip install pytest pytest-xdist pytest-capturelog memory_profiler psutil coveralls +- pip install pytest pytest-xdist pytest-capturelog memory_profiler psutil coveralls flake8 - > if [[ "$TRAVIS_PYTHON_VERSION" != "3.2" && "$TRAVIS_PYTHON_VERSION" != "pypy" && "$TRAVIS_PYTHON_VERSION" != "pypy3" ]]; then pip install 'pytest-cov>=1.8.0'; @@ -26,5 +26,6 @@ script: else py.test --cov wand --boxed --durations=20; fi +- flake8 . after_success: - coveralls diff --git a/docs/conf.py b/docs/conf.py index 157e3604..7a8cddf9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ # on ReadTheDocs builder. if os.environ.get('READTHEDOCS', 0): try: - import wand.api + import wand.api # noqa except ImportError: pass @@ -92,8 +92,8 @@ def __reduce__(self): master_doc = 'index' # General information about the project. -project = u'Wand' -copyright = str(datetime.date.today().year) + u', Hong Minhee' +project = 'Wand' +copyright = str(datetime.date.today().year) + ', Hong Minhee' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -235,7 +235,7 @@ def __reduce__(self): # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ - ('index', 'Wand.tex', u'Wand Documentation', u'Hong Minhee', 'manual'), + ('index', 'Wand.tex', 'Wand Documentation', 'Hong Minhee', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -267,8 +267,8 @@ def __reduce__(self): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'wand', u'Wand Documentation', - [u'Hong Minhee'], 1) + ('index', 'wand', 'Wand Documentation', + ['Hong Minhee'], 1) ] diff --git a/tests/drawing_test.py b/tests/drawing_test.py index eb31ae96..233cc68e 100644 --- a/tests/drawing_test.py +++ b/tests/drawing_test.py @@ -20,52 +20,64 @@ def fx_wand(request): def test_is_drawing_wand(fx_wand): assert library.IsDrawingWand(fx_wand.resource) + def test_set_get_border_color(fx_wand): with Color("#0F0") as green: fx_wand.border_color = green assert green == fx_wand.border_color + def test_set_get_clip_path(fx_wand): fx_wand.clip_path = 'path_id' assert fx_wand.clip_path == 'path_id' + def test_set_get_clip_rule(fx_wand): fx_wand.clip_rule = 'evenodd' assert fx_wand.clip_rule == 'evenodd' + def test_set_get_clip_units(fx_wand): fx_wand.clip_units = 'object_bounding_box' assert fx_wand.clip_units == 'object_bounding_box' + def test_set_get_font(fx_wand, fx_asset): fx_wand.font = str(fx_asset.join('League_Gothic.otf')) assert fx_wand.font == str(fx_asset.join('League_Gothic.otf')) + def test_set_get_font_family(fx_wand): assert fx_wand.font_family is None fx_wand.font_family = 'sans-serif' assert fx_wand.font_family == 'sans-serif' + def test_set_get_font_resolution(fx_wand): fx_wand.font_resolution = (78.0, 78.0) assert fx_wand.font_resolution == (78.0, 78.0) + def test_set_get_font_size(fx_wand): fx_wand.font_size = 22.2 assert fx_wand.font_size == 22.2 + def test_set_get_font_stretch(fx_wand): fx_wand.font_stretch = 'condensed' assert fx_wand.font_stretch == 'condensed' + def test_set_get_font_style(fx_wand): fx_wand.font_style = 'italic' assert fx_wand.font_style == 'italic' + def test_set_get_font_weight(fx_wand): - fx_wand.font_weight = 400 # Normal + fx_wand.font_weight = 400 # Normal assert fx_wand.font_weight == 400 + def test_set_get_fill_color(fx_wand): with Color('#333333') as black: fx_wand.fill_color = black @@ -97,6 +109,7 @@ def test_set_get_text_decoration(fx_wand): fx_wand.text_decoration = 'underline' assert fx_wand.text_decoration == 'underline' + def test_set_get_text_direction(fx_wand): try: fx_wand.text_direction = 'right_to_left' @@ -104,6 +117,7 @@ def test_set_get_text_direction(fx_wand): except WandLibraryVersionError: skip("DrawGetTextDirection not supported by installed drawing library") + def test_set_get_text_encoding(fx_wand): fx_wand.text_encoding = 'UTF-8' assert fx_wand.text_encoding == 'UTF-8' @@ -129,6 +143,7 @@ def test_set_get_text_under_color(fx_wand): fx_wand.text_under_color = black assert fx_wand.text_under_color == Color('#333333') + def test_set_get_vector_graphics(fx_wand): fx_wand.stroke_width = 7 xml = fx_wand.vector_graphics @@ -160,6 +175,7 @@ def test_clear_drawing_wand(fx_wand): fx_wand.clear() assert fx_wand.text_kerning == 0 + def test_composite(fx_wand): with nested(Color('#fff'), Color('#000')) as (white, black): @@ -170,8 +186,9 @@ def test_composite(fx_wand): fx_wand.draw(img) fx_wand.composite("replace", 0, 0, 25, 25, img) fx_wand.draw(img) - assert img[45,45] == img[20,20] == black - assert img[45,20] == img[20,45] == white + assert img[45, 45] == img[20, 20] == black + assert img[45, 20] == img[20, 45] == white + def test_draw_arc(fx_asset): with nested(Color('#fff'), @@ -181,13 +198,14 @@ def test_draw_arc(fx_asset): with Drawing() as draw: draw.fill_color = red draw.stroke_color = black - draw.arc((10, 10), # Start - (40, 40), # End - (-90, 90)) # Degree + draw.arc((10, 10), # Start + (40, 40), # End + (-90, 90)) # Degree draw.draw(img) - assert img[20,25] == white - assert img[30,25] == red - assert img[40,25] == black + assert img[20, 25] == white + assert img[30, 25] == red + assert img[40, 25] == black + def test_draw_circle(fx_asset): with nested(Color('#fff'), @@ -195,11 +213,12 @@ def test_draw_circle(fx_asset): with Image(width=50, height=50, background=white) as img: with Drawing() as draw: draw.fill_color = black - draw.circle((25, 25), # Origin - (40, 40)) # Perimeter + draw.circle((25, 25), # Origin + (40, 40)) # Perimeter draw.draw(img) - assert img[5,5] == img[45,45] == white - assert img[25,25] == black + assert img[5, 5] == img[45, 45] == white + assert img[25, 25] == black + def test_draw_comment(): comment = 'pikachu\'s ghost' @@ -210,15 +229,17 @@ def test_draw_comment(): blob = img.make_blob(format="mvg") assert expected == text(blob) + def test_draw_color(): with nested(Color('#fff'), Color('#000')) as (white, black): with Image(width=50, height=50, background=white) as img: with Drawing() as draw: draw.fill_color = black - draw.color(25,25,'floodfill') + draw.color(25, 25, 'floodfill') draw.draw(img) - assert img[25,25] == black + assert img[25, 25] == black + def test_draw_color_user_error(): with Drawing() as draw: @@ -229,16 +250,18 @@ def test_draw_color_user_error(): with raises(ValueError): draw.color(1, 2, 'apples') + def test_draw_ellipse(fx_wand): gray, red = Color('#ccc'), Color('#f00') with Image(width=50, height=50, background=gray) as img: with Drawing() as draw: draw.fill_color = red - draw.ellipse((25,25), # origin - (20,10)) # radius + draw.ellipse((25, 25), # origin + (20, 10)) # radius draw.draw(img) - assert img[25,10] == gray - assert img[45,25] == red + assert img[25, 10] == gray + assert img[45, 25] == red + def test_draw_line(fx_wand): gray = Color('#ccc') @@ -253,15 +276,17 @@ def test_draw_line(fx_wand): assert img[7, 5] == Color('#333333') assert img[8, 5] == Color('#ccc') + def test_draw_matte(): with nested(Color('#fff'), Color('transparent')) as (white, transparent): with Image(width=50, height=50, background=white) as img: with Drawing() as draw: draw.fill_opacity = 0 - draw.matte(25,25,'floodfill') + draw.matte(25, 25, 'floodfill') draw.draw(img) - assert img[25,25] == transparent + assert img[25, 25] == transparent + def test_draw_matte_user_error(): with Drawing() as draw: @@ -272,14 +297,16 @@ def test_draw_matte_user_error(): with raises(ValueError): draw.matte(1, 2, 'apples') + def test_draw_point(): with nested(Color('#fff'), Color('#000')) as (white, black): with Image(width=5, height=5, background=white) as img: with Drawing() as draw: draw.stroke_color = black - draw.point(2,2) + draw.point(2, 2) draw.draw(img) - assert img[2,2] == black + assert img[2, 2] == black + def test_draw_polygon(fx_wand): with nested(Color('#fff'), @@ -289,13 +316,14 @@ def test_draw_polygon(fx_wand): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw.polygon([(10,10), - (40,25), - (10,40)]) + draw.polygon([(10, 10), + (40, 25), + (10, 40)]) draw.draw(img) - assert img[10,25] == red - assert img[25,25] == blue - assert img[35,15] == img[35,35] == white + assert img[10, 25] == red + assert img[25, 25] == blue + assert img[35, 15] == img[35, 35] == white + def test_draw_polyline(fx_wand): with nested(Color('#fff'), @@ -305,12 +333,11 @@ def test_draw_polyline(fx_wand): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw.polyline([(10,10), - (40,25), - (10,40)]) + draw.polyline([(10, 10), (40, 25), (10, 40)]) draw.draw(img) - assert img[10,25] == img[25,25] == blue - assert img[35,15] == img[35,35] == white + assert img[10, 25] == img[25, 25] == blue + assert img[35, 15] == img[35, 35] == white + def test_draw_push_pop(): with Drawing() as draw: @@ -321,6 +348,7 @@ def test_draw_push_pop(): draw.pop() assert 2 == draw.stroke_width + def test_draw_bezier(fx_wand): with nested(Color('#fff'), Color('#f00'), @@ -329,14 +357,15 @@ def test_draw_bezier(fx_wand): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw.bezier([(10,10), - (10,40), - (40,10), - (40,40)]) + draw.bezier([(10, 10), + (10, 40), + (40, 10), + (40, 40)]) draw.draw(img) - assert img[10,10] == img[25,25] == img[40,40] == red - assert img[34,32] == img[15,18] == blue - assert img[34,38] == img[15,12] == white + assert img[10, 10] == img[25, 25] == img[40, 40] == red + assert img[34, 32] == img[15, 18] == blue + assert img[34, 38] == img[15, 12] == white + def test_path_curve(): with nested(Color('#fff'), @@ -346,22 +375,27 @@ def test_path_curve(): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw = (draw.path_start() - .path_move(to=(0,25), relative=True) - .path_curve(to=(25,25), controls=((0, 0), (25, 0))) - .path_curve(to=(25,0), controls=((0, 25), (25, 25)), relative=True) - .path_finish()) + draw = draw.path_start() \ + .path_move(to=(0, 25), relative=True) \ + .path_curve(to=(25, 25), + controls=((0, 0), (25, 0))) \ + .path_curve(to=(25, 0), + controls=((0, 25), (25, 25)), + relative=True) \ + .path_finish() draw.draw(img) - assert img[25,25] == red - assert img[35,35] == img[35,35] == blue - assert img[35,15] == img[15,35] == white + assert img[25, 25] == red + assert img[35, 35] == img[35, 35] == blue + assert img[35, 15] == img[15, 35] == white + def test_path_curve_user_error(): with Drawing() as draw: with raises(TypeError): - draw.path_curve(to=(5,7)) + draw.path_curve(to=(5, 7)) with raises(TypeError): - draw.path_curve(controls=(5,7)) + draw.path_curve(controls=(5, 7)) + def test_path_curve_to_quadratic_bezier(): with nested(Color('#fff'), @@ -371,13 +405,17 @@ def test_path_curve_to_quadratic_bezier(): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw = (draw.path_start() - .path_move(to=(0, 25), relative=True) - .path_curve_to_quadratic_bezier(to=(50, 25), control=(25, 50)) - .path_curve_to_quadratic_bezier(to=(-20, -20), control=(-25, 0), relative=True) - .path_finish()) + draw = draw.path_start() \ + .path_move(to=(0, 25), relative=True) \ + .path_curve_to_quadratic_bezier(to=(50, 25), + control=(25, 50)) \ + .path_curve_to_quadratic_bezier(to=(-20, -20), + control=(-25, 0), + relative=True) \ + .path_finish() draw.draw(img) - assert img[30,5] == red + assert img[30, 5] == red + def test_path_curve_to_quadratic_bezier_smooth(): with nested(Color('#fff'), @@ -387,22 +425,31 @@ def test_path_curve_to_quadratic_bezier_smooth(): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw = (draw.path_start() - .path_curve_to_quadratic_bezier(to=(25, 25), control=(25, 25)) - .path_curve_to_quadratic_bezier(to=( 10, -10), smooth=True, relative=True) - .path_curve_to_quadratic_bezier(to=( 35, 35), smooth=True, relative=False) - .path_curve_to_quadratic_bezier(to=(-10, -10), smooth=True, relative=True) - .path_finish()) + draw = draw.path_start() \ + .path_curve_to_quadratic_bezier(to=(25, 25), + control=(25, 25)) \ + .path_curve_to_quadratic_bezier(to=(10, -10), + smooth=True, + relative=True) \ + .path_curve_to_quadratic_bezier(to=(35, 35), + smooth=True, + relative=False) \ + .path_curve_to_quadratic_bezier(to=(-10, -10), + smooth=True, + relative=True) \ + .path_finish() draw.draw(img) - assert img[25,25] == red - assert img[30,30] == blue + assert img[25, 25] == red + assert img[30, 30] == blue + def test_path_curve_quadratic_bezier_user_error(): with Drawing() as draw: with raises(TypeError): draw.path_curve_to_quadratic_bezier() with raises(TypeError): - draw.path_curve_to_quadratic_bezier(to=(5,6)) + draw.path_curve_to_quadratic_bezier(to=(5, 6)) + def test_draw_path_elliptic_arc(): with nested(Color('#fff'), @@ -412,23 +459,25 @@ def test_draw_path_elliptic_arc(): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw = (draw.path_start() - .path_move(to=(25,0)) - .path_elliptic_arc(to=(25, 50), radius=(15, 25)) - .path_elliptic_arc(to=(0,-15), radius=(5, 5), clockwise=False, relative=True) - .path_close() - .path_finish()) + draw = draw.path_start() \ + .path_move(to=(25, 0)) \ + .path_elliptic_arc(to=(25, 50), radius=(15, 25)) \ + .path_elliptic_arc(to=(0, -15), radius=(5, 5), + clockwise=False, relative=True) \ + .path_close() \ + .path_finish() draw.draw(img) - assert img[25,35] == img[25,20] == red - assert img[15,25] == img[30,45] == blue + assert img[25, 35] == img[25, 20] == red + assert img[15, 25] == img[30, 45] == blue def test_draw_path_elliptic_arc_user_error(): with Drawing() as draw: with raises(TypeError): - draw.path_elliptic_arc(to=(5,7)) + draw.path_elliptic_arc(to=(5, 7)) with raises(TypeError): - draw.path_elliptic_arc(radius=(5,7)) + draw.path_elliptic_arc(radius=(5, 7)) + def test_draw_path_line(): with nested(Color('#fff'), @@ -438,19 +487,20 @@ def test_draw_path_line(): with Drawing() as draw: draw.fill_color = blue draw.stroke_color = red - draw = (draw.path_start() - .path_move(to=( 5, 5)) - .path_move(to=( 5, 5), relative=True) - .path_line(to=(40, 40)) - .path_line(to=( 0,-10), relative=True) - .path_horizontal_line(x=45) - .path_vertical_line(y=25) - .path_horizontal_line(x=-5, relative=True) - .path_vertical_line(y=-5, relative=True) - .path_finish()) + draw = draw.path_start() \ + .path_move(to=(5, 5)) \ + .path_move(to=(5, 5), relative=True) \ + .path_line(to=(40, 40)) \ + .path_line(to=(0, -10), relative=True) \ + .path_horizontal_line(x=45) \ + .path_vertical_line(y=25) \ + .path_horizontal_line(x=-5, relative=True) \ + .path_vertical_line(y=-5, relative=True) \ + .path_finish() draw.draw(img) - assert img[40,40] == img[40,30] == red - assert img[45,25] == img[40,20] == red + assert img[40, 40] == img[40, 30] == red + assert img[45, 25] == img[40, 20] == red + def test_draw_path_line_user_error(): with Drawing() as draw: @@ -462,12 +512,14 @@ def test_draw_path_line_user_error(): with raises(TypeError): draw.path_vertical_line() + def test_draw_move_user_error(): with Drawing() as draw: # Test missing value with raises(TypeError): draw.path_move() + @mark.parametrize('kwargs', itertools.product( [('right', 40), ('width', 30)], [('bottom', 40), ('height', 30)] @@ -488,6 +540,7 @@ def test_draw_rectangle(kwargs, display, fx_wand): assert (img[12, 12] == img[12, 38] == img[38, 12] == img[38, 38] == black) + @mark.parametrize('kwargs', itertools.product( [('xradius', 10), ('yradius', 10)], [('xradius', 20)], @@ -509,6 +562,7 @@ def test_draw_rectangle_with_radius(kwargs, display, fx_wand): assert img[10, 10] == img[40, 40] == white assert img[26, 12] == img[26, 36] == black + def test_draw_rotate(): with nested(Color('#fff'), Color('#000')) as (white, black): @@ -518,7 +572,8 @@ def test_draw_rotate(): draw.rotate(45) draw.line((3, 3), (35, 35)) draw.draw(img) - assert img[0,49] == black + assert img[0, 49] == black + def test_draw_scale(display, fx_wand): with nested(Color("#fff"), @@ -530,16 +585,17 @@ def test_draw_scale(display, fx_wand): fx_wand.draw(img) display(img) # if width was scaled up by 200% - assert img[45,10] == black + assert img[45, 10] == black # if height was scaled down by 50% - assert img[20,20] == white + assert img[20, 20] == white + def test_set_fill_pattern_url(display, fx_wand): with nested(Color("#fff"), Color("#0f0"), Color("#000")) as (white, green, black): with Image(width=50, height=50, background=white) as img: - fx_wand.push_pattern('green_circle',0 , 0, 10, 10) + fx_wand.push_pattern('green_circle', 0, 0, 10, 10) fx_wand.fill_color = green fx_wand.stroke_color = black fx_wand.circle(origin=(5, 5), perimeter=(5, 0)) @@ -548,14 +604,15 @@ def test_set_fill_pattern_url(display, fx_wand): fx_wand.rectangle(top=5, left=5, width=40, height=40) fx_wand.draw(img) display(img) - assert img[25,25] == green + assert img[25, 25] == green + def test_set_stroke_pattern_url(display, fx_wand): with nested(Color("#fff"), Color("#0f0"), Color("#000")) as (white, green, black): with Image(width=50, height=50, background=white) as img: - fx_wand.push_pattern('green_ring',0 , 0, 6, 6) + fx_wand.push_pattern('green_ring', 0, 0, 6, 6) fx_wand.fill_color = green fx_wand.stroke_color = white fx_wand.circle(origin=(3, 3), perimeter=(3, 0)) @@ -574,10 +631,11 @@ def test_draw_skew(): with Image(width=50, height=50, background=white) as img: with Drawing() as draw: draw.stroke_color = black - draw.skew(x=11,y=-24) + draw.skew(x=11, y=-24) draw.line((3, 3), (35, 35)) draw.draw(img) - assert img[43,42] == black + assert img[43, 42] == black + def test_draw_translate(): with nested(Color('#fff'), @@ -588,7 +646,8 @@ def test_draw_translate(): draw.translate(x=5, y=5) draw.line((3, 3), (35, 35)) draw.draw(img) - assert img[40,40] == black + assert img[40, 40] == black + def test_draw_text(fx_asset): with Color('#fff') as white: @@ -628,17 +687,19 @@ def test_get_font_metrics_test(fx_asset): assert m2.text_width > m3.text_width assert m2.text_height < m3.text_height + def test_viewbox(fx_asset): - with Drawing() as draw: - with raises(TypeError): - draw.viewbox(None, None, None, None) - with raises(TypeError): - draw.viewbox(10, None, None, None) - with raises(TypeError): - draw.viewbox(10, 10, None, None) - with raises(TypeError): - draw.viewbox(10, 10, 100, None) - draw.viewbox(10, 10, 100, 100) + with Drawing() as draw: + with raises(TypeError): + draw.viewbox(None, None, None, None) + with raises(TypeError): + draw.viewbox(10, None, None, None) + with raises(TypeError): + draw.viewbox(10, 10, None, None) + with raises(TypeError): + draw.viewbox(10, 10, 100, None) + draw.viewbox(10, 10, 100, 100) + def test_regression_issue_163(tmpdir): """https://github.com/dahlia/wand/issues/163""" @@ -651,107 +712,125 @@ def test_regression_issue_163(tmpdir): draw(image) image.save(filename=str(tmpdir.join('out.jpg'))) -# Fix Merge conflicts + def test_set_get_fill_color_user_error(fx_wand): with raises(TypeError): fx_wand.fill_color = "green" + def test_set_get_fill_opacity(fx_wand): - fx_wand.fill_opacity = 1.0; + fx_wand.fill_opacity = 1.0 assert fx_wand.fill_opacity == 1.0 + def test_set_get_fill_opacity_user_error(fx_wand): with raises(TypeError): - fx_wand.fill_opacity = "1.5"; + fx_wand.fill_opacity = "1.5" + def test_set_get_fill_rule(fx_wand): valid = 'evenodd' notvalid = 'error' - invalid = (1,2) + invalid = (1, 2) fx_wand.fill_rule = valid assert fx_wand.fill_rule == valid with raises(ValueError): fx_wand.fill_rule = notvalid with raises(TypeError): fx_wand.fill_rule = invalid - fx_wand.fill_rule = 'undefined' # reset + fx_wand.fill_rule = 'undefined' # reset + def test_set_get_opacity(fx_wand): fx_wand.opacity = 0.3456 - #assert 0.3456 == fx_wand.opacity skip('DrawGetOpacity always returns 1.0') + def test_set_get_stroke_antialias(fx_wand): fx_wand.stroke_antialias = False - assert fx_wand.stroke_antialias == False + assert not fx_wand.stroke_antialias + def test_set_get_stroke_color_user_error(fx_wand): with raises(TypeError): fx_wand.stroke_color = '#333333' + def test_set_get_stroke_dash_array(fx_wand): dash_array = [2, 1, 4, 1] fx_wand.stroke_dash_array = dash_array assert fx_wand.stroke_dash_array == dash_array + def test_set_get_stroke_dash_offset(fx_wand): fx_wand.stroke_dash_offset = 0.5 assert fx_wand.stroke_dash_offset == 0.5 + def test_set_get_stroke_line_cap(fx_wand): fx_wand.stroke_line_cap = 'round' assert fx_wand.stroke_line_cap == 'round' + def test_set_get_stroke_line_cap_user_error(fx_wand): with raises(TypeError): fx_wand.stroke_line_cap = 0x74321870 with raises(ValueError): fx_wand.stroke_line_cap = 'apples' + def test_set_get_stroke_line_join(fx_wand): fx_wand.stroke_line_join = 'miter' assert fx_wand.stroke_line_join == 'miter' + def test_set_get_stroke_line_join_user_error(fx_wand): with raises(TypeError): fx_wand.stroke_line_join = 0x74321870 with raises(ValueError): fx_wand.stroke_line_join = 'apples' + def test_set_get_stroke_miter_limit(fx_wand): fx_wand.stroke_miter_limit = 5 assert fx_wand.stroke_miter_limit == 5 + def test_set_get_stroke_miter_limit_user_error(fx_wand): with raises(TypeError): fx_wand.stroke_miter_limit = '5' + def test_set_get_stroke_opacity(fx_wand): fx_wand.stroke_opacity = 1.0 assert fx_wand.stroke_opacity == 1.0 + def test_set_get_stroke_opacity_user_error(fx_wand): with raises(TypeError): fx_wand.stroke_opacity = '1.0' + def test_set_get_stroke_width_user_error(fx_wand): with raises(TypeError): fx_wand.stroke_width = '0.1234' with raises(ValueError): fx_wand.stroke_width = -1.5 + def test_draw_affine(display, fx_wand): with nested(Color('skyblue'), Color('black')) as (skyblue, black): with Image(width=100, height=100, background=skyblue) as img: img.format = 'png' fx_wand.affine([1.5, 0.5, 0, 1.5, 45, 25]) - fx_wand.rectangle(top=5,left=5, width=25, height=25) + fx_wand.rectangle(top=5, left=5, width=25, height=25) fx_wand.draw(img) display(img) assert img[25, 25] == skyblue assert img[75, 75] == black + def test_draw_clip_path(display, fx_wand): with nested(Color('skyblue'), Color('orange')) as (skyblue, orange): @@ -759,7 +838,7 @@ def test_draw_clip_path(display, fx_wand): fx_wand.push_defs() fx_wand.push_clip_path("eyes_only") fx_wand.push() - fx_wand.rectangle(top=0,left=0, width=50, height=50) + fx_wand.rectangle(top=0, left=0, width=50, height=50) fx_wand.pop() fx_wand.pop_clip_path() fx_wand.pop_defs() diff --git a/tests/image_test.py b/tests/image_test.py index ddc63e07..baf51fa6 100644 --- a/tests/image_test.py +++ b/tests/image_test.py @@ -15,7 +15,6 @@ from wand.compat import PY3, string_type, text, text_type from wand.exceptions import OptionError, MissingDelegateError from wand.font import Font -from wand.version import QUANTUM_DEPTH try: filesystem_encoding = sys.getfilesystemencoding() @@ -1355,6 +1354,7 @@ def test_linear_stretch_user_error(fx_asset): img.linear_stretch(white_point=0.5, black_point='NaN') + def test_normalize_default(display, fx_asset): with Image(filename=str(fx_asset.join('gray_range.jpg'))) as img: display(img) @@ -1400,6 +1400,7 @@ def test_normalize_channel(fx_asset): assert getattr(img[-1, 0], c) == getattr(right_top, c) assert getattr(img[-1, -1], c) == getattr(right_bottom, c) + def test_level_default(fx_asset): with Image(filename=str(fx_asset.join('gray_range.jpg'))) as img: # Adjust the levels to make this image entirely black @@ -1428,6 +1429,7 @@ def test_level_default(fx_asset): assert light.red_int8 <= light.green_int8 <= light.blue_int8 <= 195 assert light.red_int8 >= light.green_int8 >= light.blue_int8 >= 190 + def test_level_channel(fx_asset): for chan in ('red', 'green', 'blue'): c = chan + '_int8' @@ -1454,6 +1456,7 @@ def test_level_channel(fx_asset): assert(getattr(light, c) >= 190) assert(getattr(light, c) <= 195) + def test_level_user_error(fx_asset): with Image(filename=str(fx_asset.join('gray_range.jpg'))) as img: with raises(TypeError): @@ -1488,7 +1491,7 @@ def test_evaluate(fx_asset): with img.clone() as literal_img: literal_img.evaluate('divide', 2, channel='red') with img[0, 0] as org_color: - expected_color = (img[0, 0].red_int8 * 0.5) + expected_color = (org_color.red_int8 * 0.5) with literal_img[0, 0] as actual_color: assert abs(expected_color - actual_color.red_int8) < 1 @@ -1597,6 +1600,7 @@ def test_fx_error(fx_asset): with xc.fx('p[0,0]', True): pass + def test_transpose(fx_asset): with Image(filename=str(fx_asset.join('beach.jpg'))) as img: with img.clone() as transposed: diff --git a/tests/misc_test.py b/tests/misc_test.py index e20eb561..92fcc384 100644 --- a/tests/misc_test.py +++ b/tests/misc_test.py @@ -8,6 +8,7 @@ MAGICK_RELEASE_DATE_STRING, QUANTUM_DEPTH, configure_options, fonts, formats) + def test_version(): """Test version strings.""" match = re.match('^ImageMagick\s+\d+\.\d+\.\d+(?:-\d+)?', MAGICK_VERSION) diff --git a/tox.ini b/tox.ini index 7cca7afd..e6bd10cb 100644 --- a/tox.ini +++ b/tox.ini @@ -9,4 +9,10 @@ deps = coverage == 3.7.1 memory_profiler >= 0.27 psutil >= 1.0.1 -commands = py.test {posargs:--durations=5 --boxed} + flake8 +commands = + py.test {posargs:--durations=5 --boxed} + flake8 . + +[flake8] +exclude = .git,.tox,docs/_themes/ diff --git a/wand/api.py b/wand/api.py index 9c720483..1b58f675 100644 --- a/wand/api.py +++ b/wand/api.py @@ -132,11 +132,13 @@ class MagickPixelPacket(ctypes.Structure): ('opacity', ctypes.c_double), ('index', ctypes.c_double)] + class PointInfo(ctypes.Structure): _fields_ = [('x', ctypes.c_double), ('y', ctypes.c_double)] + class AffineMatrix(ctypes.Structure): _fields_ = [('sx', ctypes.c_double), ('rx', ctypes.c_double), @@ -318,14 +320,15 @@ class AffineMatrix(ctypes.Structure): library.MagickGetImageVirtualPixelMethod.argtypes = [ctypes.c_void_p] - library.MagickSetImageVirtualPixelMethod.argtypes = [ctypes.c_void_p, ctypes.c_int] + library.MagickSetImageVirtualPixelMethod.argtypes = [ctypes.c_void_p, + ctypes.c_int] library.MagickGetImageColorspace.argtypes = [ctypes.c_void_p] library.MagickGetImageColorspace.restype = ctypes.c_int library.MagickSetImageColorspace.argtypes = [ctypes.c_void_p, ctypes.c_int] - library.MagickTransformImageColorspace.argtypes = [ctypes.c_void_p, ctypes.c_int] - + library.MagickTransformImageColorspace.argtypes = [ctypes.c_void_p, + ctypes.c_int] library.MagickGetImageCompression.argtypes = [ctypes.c_void_p] library.MagickGetImageCompression.restype = ctypes.c_int @@ -360,16 +363,20 @@ class AffineMatrix(ctypes.Structure): ctypes.c_ssize_t, # inner_bevel ctypes.c_ssize_t] # outer_bevel - library.MagickFunctionImage.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int, # MagickFunction - ctypes.c_size_t, # number_arguments - ctypes.POINTER(ctypes.c_double)] # arguments + library.MagickFunctionImage.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_int, # MagickFunction + ctypes.c_size_t, # number_arguments + ctypes.POINTER(ctypes.c_double), # arguments + ] - library.MagickFunctionImageChannel.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int, # channel - ctypes.c_int, # MagickFunction - ctypes.c_size_t, # number_arguments - ctypes.POINTER(ctypes.c_double)] # arguments + library.MagickFunctionImageChannel.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_int, # channel + ctypes.c_int, # MagickFunction + ctypes.c_size_t, # number_arguments + ctypes.POINTER(ctypes.c_double), # arguments + ] library.MagickFxImage.argtypes = [ctypes.c_void_p, # wand ctypes.c_char_p] # expression @@ -380,7 +387,6 @@ class AffineMatrix(ctypes.Structure): ctypes.c_char_p] # expression library.MagickFxImageChannel.restype = ctypes.c_void_p - library.MagickResetImagePage.argtypes = [ctypes.c_void_p, ctypes.c_char_p] library.MagickSampleImage.argtypes = [ctypes.c_void_p, ctypes.c_size_t, @@ -542,10 +548,12 @@ class AffineMatrix(ctypes.Structure): ctypes.c_double, # black ctypes.c_double] # white - library.MagickContrastStretchImageChannel.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int, # channel - ctypes.c_double, # black - ctypes.c_double] # white + library.MagickContrastStretchImageChannel.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_int, # channel + ctypes.c_double, # black + ctypes.c_double, # white + ] library.MagickGammaImage.argtypes = [ctypes.c_void_p, ctypes.c_double] @@ -554,7 +562,6 @@ class AffineMatrix(ctypes.Structure): ctypes.c_int, ctypes.c_double] - library.MagickLinearStretchImage.argtypes = [ctypes.c_void_p, # wand ctypes.c_double, # black ctypes.c_double] # white @@ -725,57 +732,63 @@ class AffineMatrix(ctypes.Structure): library.DrawClearException.argtypes = [ctypes.c_void_p] library.DrawClearException.restype = ctypes.c_int - library.DrawAffine.argtypes = [ctypes.c_void_p, # Drawing wand - ctypes.POINTER(AffineMatrix)] # AffineMatrix + library.DrawAffine.argtypes = [ + ctypes.c_void_p, # Drawing wand + ctypes.POINTER(AffineMatrix), # AffineMatrix + ] - library.DrawComment.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p] #comment + library.DrawComment.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_char_p, # comment + ] - library.DrawComposite.argtypes = [ctypes.c_void_p, # DrawingWand wand - ctypes.c_int, # CompositeOperator - ctypes.c_double, # x - ctypes.c_double, # y - ctypes.c_double, # width - ctypes.c_double, # height - ctypes.c_void_p] # MagickWand wand + library.DrawComposite.argtypes = [ + ctypes.c_void_p, # DrawingWand wand + ctypes.c_int, # CompositeOperator + ctypes.c_double, # x + ctypes.c_double, # y + ctypes.c_double, # width + ctypes.c_double, # height + ctypes.c_void_p, # MagickWand wand + ] library.DrawComposite.restype = ctypes.c_uint - library.DrawSetBorderColor.argtypes = [ctypes.c_void_p, # wand - ctypes.c_void_p] # PixelWand color + library.DrawSetBorderColor.argtypes = [ctypes.c_void_p, # wand + ctypes.c_void_p] # PixelWand color - library.DrawSetClipPath.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p] # clip_mask + library.DrawSetClipPath.argtypes = [ctypes.c_void_p, # wand + ctypes.c_char_p] # clip_mask library.DrawSetClipPath.restype = ctypes.c_int - library.DrawSetClipRule.argtypes = [ctypes.c_void_p, # wand - ctypes.c_uint] # FillRule + library.DrawSetClipRule.argtypes = [ctypes.c_void_p, # wand + ctypes.c_uint] # FillRule - library.DrawSetClipUnits.argtypes = [ctypes.c_void_p, # wand - ctypes.c_uint] # ClipPathUnits + library.DrawSetClipUnits.argtypes = [ctypes.c_void_p, # wand + ctypes.c_uint] # ClipPathUnits library.DrawSetFont.argtypes = [ctypes.c_void_p, ctypes.c_char_p] - library.DrawSetFontFamily.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p] # font_family + library.DrawSetFontFamily.argtypes = [ctypes.c_void_p, # wand + ctypes.c_char_p] # font_family library.DrawSetFontFamily.restype = ctypes.c_uint - library.DrawSetFontResolution.argtypes = [ctypes.c_void_p, #wand - ctypes.c_double, # x - ctypes.c_double] # y + library.DrawSetFontResolution.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y library.DrawSetFontResolution.restype = ctypes.c_uint library.DrawSetFontSize.argtypes = [ctypes.c_void_p, ctypes.c_double] - library.DrawSetFontStretch.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int] # font_stretch + library.DrawSetFontStretch.argtypes = [ctypes.c_void_p, # wand + ctypes.c_int] # font_stretch - library.DrawSetFontStyle.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int] # style + library.DrawSetFontStyle.argtypes = [ctypes.c_void_p, # wand + ctypes.c_int] # style - library.DrawSetFontWeight.argtypes = [ctypes.c_void_p, # wand - ctypes.c_size_t] # font_weight + library.DrawSetFontWeight.argtypes = [ctypes.c_void_p, # wand + ctypes.c_size_t] # font_weight library.DrawSetFillColor.argtypes = [ctypes.c_void_p, ctypes.c_void_p] @@ -783,8 +796,8 @@ class AffineMatrix(ctypes.Structure): library.DrawSetFillOpacity.argtypes = [ctypes.c_void_p, ctypes.c_double] - library.DrawSetFillPatternURL.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p] # fill_url + library.DrawSetFillPatternURL.argtypes = [ctypes.c_void_p, # wand + ctypes.c_char_p] # fill_url library.DrawSetFillPatternURL.restype = ctypes.c_uint library.DrawSetFillRule.argtypes = [ctypes.c_void_p, @@ -792,33 +805,41 @@ class AffineMatrix(ctypes.Structure): library.DrawSetOpacity.argtypes = [ctypes.c_void_p, ctypes.c_double] - library.DrawSetStrokeAntialias.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int] # stroke_antialias + library.DrawSetStrokeAntialias.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_int, # stroke_antialias + ] library.DrawSetStrokeColor.argtypes = [ctypes.c_void_p, ctypes.c_void_p] - library.DrawSetStrokeDashArray.argtypes = [ctypes.c_void_p, # wand - ctypes.c_size_t, # number_elements - ctypes.POINTER(ctypes.c_double)] + library.DrawSetStrokeDashArray.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_size_t, # number_elements + ctypes.POINTER(ctypes.c_double), + ] - library.DrawSetStrokeDashOffset.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # dash_offset + library.DrawSetStrokeDashOffset.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # dash_offset + ] - library.DrawSetStrokeLineCap.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int] # linecap + library.DrawSetStrokeLineCap.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_int, # linecap + ] - library.DrawSetStrokeLineJoin.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int] # linejoin + library.DrawSetStrokeLineJoin.argtypes = [ctypes.c_void_p, # wand + ctypes.c_int] # linejoin - library.DrawSetStrokeMiterLimit.argtypes = [ctypes.c_void_p, # wand - ctypes.c_size_t] # miterlimit + library.DrawSetStrokeMiterLimit.argtypes = [ctypes.c_void_p, # wand + ctypes.c_size_t] # miterlimit - library.DrawSetStrokeOpacity.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # stroke_opacity + library.DrawSetStrokeOpacity.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double] # stroke_opacity - library.DrawSetStrokePatternURL.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p] # fill_url + library.DrawSetStrokePatternURL.argtypes = [ctypes.c_void_p, # wand + ctypes.c_char_p] # fill_url library.DrawSetStrokePatternURL.restype = ctypes.c_uint library.DrawSetStrokeWidth.argtypes = [ctypes.c_void_p, @@ -864,14 +885,14 @@ class AffineMatrix(ctypes.Structure): library.DrawResetVectorGraphics.argtypes = [ctypes.c_void_p] - library.DrawSetViewbox.argtypes = [ctypes.c_void_p, # wand - ctypes.c_ssize_t, # x1 - ctypes.c_ssize_t, # y1 - ctypes.c_ssize_t, # x2 - ctypes.c_ssize_t] # y2 + library.DrawSetViewbox.argtypes = [ctypes.c_void_p, # wand + ctypes.c_ssize_t, # x1 + ctypes.c_ssize_t, # y1 + ctypes.c_ssize_t, # x2 + ctypes.c_ssize_t] # y2 - library.DrawGetBorderColor.argtypes = [ctypes.c_void_p, # wand - ctypes.c_void_p] # PixelWand color + library.DrawGetBorderColor.argtypes = [ctypes.c_void_p, # wand + ctypes.c_void_p] # PixelWand color library.DrawGetClipPath.argtypes = [ctypes.c_void_p] library.DrawGetClipPath.restype = c_magick_char_p @@ -889,7 +910,7 @@ class AffineMatrix(ctypes.Structure): library.DrawGetFillOpacity.restype = ctypes.c_double library.DrawGetFillRule.argtypes = [ctypes.c_void_p] - library.DrawGetFillRule.restype = ctypes.c_uint + library.DrawGetFillRule.restype = ctypes.c_uint library.DrawGetOpacity.argtypes = [ctypes.c_void_p] library.DrawGetOpacity.restype = ctypes.c_double @@ -900,8 +921,10 @@ class AffineMatrix(ctypes.Structure): library.DrawGetStrokeColor.argtypes = [ctypes.c_void_p, ctypes.c_void_p] - library.DrawGetStrokeDashArray.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.c_size_t)] + library.DrawGetStrokeDashArray.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_size_t), + ] library.DrawGetStrokeDashArray.restype = ctypes.POINTER(ctypes.c_double) library.DrawGetStrokeDashOffset.argtypes = [ctypes.c_void_p] @@ -928,9 +951,11 @@ class AffineMatrix(ctypes.Structure): library.DrawGetFontFamily.argtypes = [ctypes.c_void_p] library.DrawGetFontFamily.restype = c_magick_char_p - library.DrawGetFontResolution.argtypes = [ctypes.c_void_p, #wand - ctypes.POINTER(ctypes.c_double), # x - ctypes.POINTER(ctypes.c_double)] # y + library.DrawGetFontResolution.argtypes = [ + ctypes.c_void_p, # wand + ctypes.POINTER(ctypes.c_double), # x + ctypes.POINTER(ctypes.c_double), # y + ] library.DrawGetFontResolution.restype = ctypes.c_uint library.DrawGetFontSize.argtypes = [ctypes.c_void_p] @@ -995,14 +1020,15 @@ class AffineMatrix(ctypes.Structure): ctypes.c_char_p] library.MagickAnnotateImage.restype = ctypes.c_int - library.MagickDistortImage.argtypes = [ctypes.c_void_p, # wand - ctypes.c_int, # method - ctypes.c_size_t, # number_arguments - ctypes.POINTER(ctypes.c_double), # arguments - ctypes.c_int] # bestfit + library.MagickDistortImage.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_int, # method + ctypes.c_size_t, # number_arguments + ctypes.POINTER(ctypes.c_double), # arguments + ctypes.c_int, # bestfit + ] library.MagickDistortImage.restype = ctypes.c_int - library.ClearDrawingWand.argtypes = [ctypes.c_void_p] library.MagickDrawImage.argtypes = [ctypes.c_void_p, @@ -1014,36 +1040,36 @@ class AffineMatrix(ctypes.Structure): ctypes.c_double, ctypes.POINTER(ctypes.c_ubyte)] - library.DrawArc.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # sx - ctypes.c_double, # sy - ctypes.c_double, # ex - ctypes.c_double, # ey - ctypes.c_double, # sd - ctypes.c_double] # ed + library.DrawArc.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # sx + ctypes.c_double, # sy + ctypes.c_double, # ex + ctypes.c_double, # ey + ctypes.c_double, # sd + ctypes.c_double] # ed library.DrawBezier.argtypes = [ctypes.c_void_p, ctypes.c_ulong, ctypes.POINTER(PointInfo)] - library.DrawCircle.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # ox - ctypes.c_double, # oy - ctypes.c_double, # px - ctypes.c_double] # py - - library.DrawColor.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double, # y - ctypes.c_uint] # PaintMethod - - library.DrawEllipse.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # ox - ctypes.c_double, # oy - ctypes.c_double, # rx - ctypes.c_double, # ry - ctypes.c_double, # start - ctypes.c_double] # end + library.DrawCircle.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # ox + ctypes.c_double, # oy + ctypes.c_double, # px + ctypes.c_double] # py + + library.DrawColor.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double, # y + ctypes.c_uint] # PaintMethod + + library.DrawEllipse.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # ox + ctypes.c_double, # oy + ctypes.c_double, # rx + ctypes.c_double, # ry + ctypes.c_double, # start + ctypes.c_double] # end library.DrawLine.argtypes = [ctypes.c_void_p, ctypes.c_double, @@ -1051,125 +1077,141 @@ class AffineMatrix(ctypes.Structure): ctypes.c_double, ctypes.c_double] - library.DrawMatte.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double, # y - ctypes.c_uint] # PaintMethod - - library.DrawPathClose.argtypes = [ctypes.c_void_p] # wand - - library.DrawPathCurveToAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x1 - ctypes.c_double, # y1 - ctypes.c_double, # x2 - ctypes.c_double, # y2 - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x1 - ctypes.c_double, # y1 - ctypes.c_double, # x2 - ctypes.c_double, # y2 - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToQuadraticBezierAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x1 - ctypes.c_double, # y1 - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToQuadraticBezierRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x1 - ctypes.c_double, # y1 - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToQuadraticBezierSmoothAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToQuadraticBezierSmoothRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToSmoothAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x2 - ctypes.c_double, # y2 - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathCurveToSmoothRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x2 - ctypes.c_double, # y2 - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathEllipticArcAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # rx - ctypes.c_double, # ry - ctypes.c_double, # rotation - ctypes.c_uint, # arc_flag - ctypes.c_uint, # sweep_flag - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathEllipticArcRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # rx - ctypes.c_double, # ry - ctypes.c_double, # rotation - ctypes.c_uint, # arc_flag - ctypes.c_uint, # sweep_flag - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathFinish.argtypes = [ctypes.c_void_p] # wand - - library.DrawPathLineToAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathLineToRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathLineToHorizontalAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # x - - library.DrawPathLineToHorizontalRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # x - - library.DrawPathLineToVerticalAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # y - - library.DrawPathLineToVerticalRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # y - - library.DrawPathMoveToAbsolute.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathMoveToRelative.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y - - library.DrawPathStart.argtypes = [ctypes.c_void_p] # wand - - library.DrawPoint.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y + library.DrawMatte.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double, # y + ctypes.c_uint] # PaintMethod + + library.DrawPathClose.argtypes = [ctypes.c_void_p] # wand + + library.DrawPathCurveToAbsolute.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x1 + ctypes.c_double, # y1 + ctypes.c_double, # x2 + ctypes.c_double, # y2 + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathCurveToRelative.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x1 + ctypes.c_double, # y1 + ctypes.c_double, # x2 + ctypes.c_double, # y2 + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathCurveToQuadraticBezierAbsolute.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # x1 + ctypes.c_double, # y1 + ctypes.c_double, # x + ctypes.c_double, # y + ] + + library.DrawPathCurveToQuadraticBezierRelative.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # x1 + ctypes.c_double, # y1 + ctypes.c_double, # x + ctypes.c_double, # y + ] + + library.DrawPathCurveToQuadraticBezierSmoothAbsolute.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double, # y + ] + + library.DrawPathCurveToQuadraticBezierSmoothRelative.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double, # y + ] + + library.DrawPathCurveToSmoothAbsolute.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x2 + ctypes.c_double, # y2 + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathCurveToSmoothRelative.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x2 + ctypes.c_double, # y2 + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathEllipticArcAbsolute.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # rx + ctypes.c_double, # ry + ctypes.c_double, # rotation + ctypes.c_uint, # arc_flag + ctypes.c_uint, # sweep_flag + ctypes.c_double, # x + ctypes.c_double, # y + ] + + library.DrawPathEllipticArcRelative.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # rx + ctypes.c_double, # ry + ctypes.c_double, # rotation + ctypes.c_uint, # arc_flag + ctypes.c_uint, # sweep_flag + ctypes.c_double, # x + ctypes.c_double, # y + ] + + library.DrawPathFinish.argtypes = [ctypes.c_void_p] # wand + + library.DrawPathLineToAbsolute.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathLineToRelative.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathLineToHorizontalAbsolute.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # x + ] + + library.DrawPathLineToHorizontalRelative.argtypes = [ + ctypes.c_void_p, # wand + ctypes.c_double, # x + ] + + library.DrawPathLineToVerticalAbsolute.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double] # y + + library.DrawPathLineToVerticalRelative.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double] # y + + library.DrawPathMoveToAbsolute.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathMoveToRelative.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y + + library.DrawPathStart.argtypes = [ctypes.c_void_p] # wand + + library.DrawPoint.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y library.DrawPolygon.argtypes = [ctypes.c_void_p, ctypes.c_ulong, ctypes.POINTER(PointInfo)] library.DrawPolyline.argtypes = [ctypes.c_void_p, - ctypes.c_ulong, - ctypes.POINTER(PointInfo)] + ctypes.c_ulong, + ctypes.POINTER(PointInfo)] - library.DrawRotate.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # degree + library.DrawRotate.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double] # degree library.DrawRectangle.argtypes = [ctypes.c_void_p, ctypes.c_double, @@ -1177,40 +1219,40 @@ class AffineMatrix(ctypes.Structure): ctypes.c_double, ctypes.c_double] - library.DrawRoundRectangle.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x1 - ctypes.c_double, # y1 - ctypes.c_double, # x2 - ctypes.c_double, # y2 - ctypes.c_double, # rx - ctypes.c_double] # ry + library.DrawRoundRectangle.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x1 + ctypes.c_double, # y1 + ctypes.c_double, # x2 + ctypes.c_double, # y2 + ctypes.c_double, # rx + ctypes.c_double] # ry - library.DrawScale.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double, # x - ctypes.c_double] # y + library.DrawScale.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y - library.DrawSkewX.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # degree + library.DrawSkewX.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double] # degree - library.DrawSkewY.argtypes = [ctypes.c_void_p, # wand - ctypes.c_double] # degree + library.DrawSkewY.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double] # degree - library.DrawTranslate.argtypes = [ctypes.c_void_p, #wand - ctypes.c_double, # x - ctypes.c_double] # y + library.DrawTranslate.argtypes = [ctypes.c_void_p, # wand + ctypes.c_double, # x + ctypes.c_double] # y -### Drawing stack management ### + # -- Drawing stack management -- library.PushDrawingWand.argtypes = [ctypes.c_void_p] library.PushDrawingWand.restype = ctypes.c_uint - library.DrawPushClipPath.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p] # clip_mask_id + library.DrawPushClipPath.argtypes = [ctypes.c_void_p, # wand + ctypes.c_char_p] # clip_mask_id library.DrawPushDefs.argtypes = [ctypes.c_void_p] - library.DrawPushPattern.argtypes = [ctypes.c_void_p, # wand - ctypes.c_char_p, # clip_mask_id - ctypes.c_double, # x - ctypes.c_double, # y - ctypes.c_double, # width - ctypes.c_double] # height + library.DrawPushPattern.argtypes = [ctypes.c_void_p, # wand + ctypes.c_char_p, # clip_mask_id + ctypes.c_double, # x + ctypes.c_double, # y + ctypes.c_double, # width + ctypes.c_double] # height library.DrawPushClipPath.restype = ctypes.c_uint library.PopDrawingWand.argtypes = [ctypes.c_void_p] library.PopDrawingWand.restype = ctypes.c_uint @@ -1234,9 +1276,12 @@ class AffineMatrix(ctypes.Structure): library.MagickQueryConfigureOption.argtypes = [ctypes.c_char_p] library.MagickQueryConfigureOption.restype = c_magick_char_p - library.MagickQueryConfigureOptions.argtypes = [ctypes.c_char_p, - ctypes.POINTER(ctypes.c_size_t)] - library.MagickQueryConfigureOptions.restype = ctypes.POINTER(c_magick_char_p) + library.MagickQueryConfigureOptions.argtypes = [ + ctypes.c_char_p, + ctypes.POINTER(ctypes.c_size_t), + ] + library.MagickQueryConfigureOptions.restype = \ + ctypes.POINTER(c_magick_char_p) library.MagickQueryFontMetrics.argtypes = [ctypes.c_void_p, ctypes.c_void_p, diff --git a/wand/compat.py b/wand/compat.py index 9bd94e30..ee063a8a 100644 --- a/wand/compat.py +++ b/wand/compat.py @@ -24,11 +24,11 @@ #: (:class:`type`) Type for text data. :class:`basestring` in Python 2 #: and :class:`str` in Python 3. -string_type = str if PY3 else basestring +string_type = str if PY3 else basestring # noqa #: (:class:`type`) Type for representing Unicode textual data. #: :class:`unicode` in Python 2 and :class:`str` in Python 3. -text_type = str if PY3 else unicode +text_type = str if PY3 else unicode # noqa def binary(string, var=None): @@ -68,7 +68,7 @@ def text(string): #: The :func:`xrange()` function. Alias for :func:`range()` in Python 3. -xrange = range if PY3 else xrange +xrange = range if PY3 else xrange # noqa #: (:class:`type`, :class:`tuple`) Types for file objects that have diff --git a/wand/drawing.py b/wand/drawing.py index 5d601661..e34245cd 100644 --- a/wand/drawing.py +++ b/wand/drawing.py @@ -219,8 +219,11 @@ def clip_path(self): """(:class:`basestring`) The current clip path. It also can be set. .. versionadded:: 0.4.0 + .. versionchanged: 0.4.1 - Safely release allocated memory with MagickRelinquishMemory instead of libc.free. + Safely release allocated memory with + :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. + """ clip_path_p = library.DrawGetClipPath(self.resource) return text(clip_path_p.value) @@ -278,7 +281,9 @@ def font(self): """(:class:`basestring`) The current font name. It also can be set. .. versionchanged: 0.4.1 - Safely release allocated memory with MagickRelinquishMemory instead of libc.free. + Safely release allocated memory with + :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. + """ font_p = library.DrawGetFont(self.resource) return text(font_p.value) @@ -294,8 +299,11 @@ def font_family(self): """(:class:`basestring`) The current font family. It also can be set. .. versionadded:: 0.4.0 + .. versionchanged: 0.4.1 - Safely release allocated memory with MagickRelinquishMemory instead of libc.free. + Safely release allocated memory with + :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. + """ font_family_p = library.DrawGetFontFamily(self.resource) return text(font_family_p.value) @@ -517,8 +525,11 @@ def stroke_dash_array(self): It also can be set. .. versionadded:: 0.4.0 + .. versionchanged: 0.4.1 - Safely release allocated memory with MagickRelinquishMemory instead of libc.free. + Safely release allocated memory with + :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. + """ number_elements = ctypes.c_size_t(0) dash_array_p = library.DrawGetStrokeDashArray( @@ -734,7 +745,9 @@ def text_encoding(self): Although it also can be set, but it's not encouraged. .. versionchanged: 0.4.1 - Safely release allocated memory with MagickRelinquishMemory instead of libc.free. + Safely release allocated memory with + :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. + """ text_encoding_p = library.DrawGetTextEncoding(self.resource) return text(text_encoding_p.value) @@ -770,7 +783,8 @@ def text_interline_spacing(self, spacing): 'ImageMagick does not support ' 'this feature') if not isinstance(spacing, numbers.Real): - raise TypeError('expected a numbers.Real, but got ' + repr(spacing)) + raise TypeError('expected a numbers.Real, but got ' + + repr(spacing)) library.DrawSetTextInterlineSpacing(self.resource, spacing) @property @@ -798,7 +812,8 @@ def text_kerning(self): @text_kerning.setter def text_kerning(self, kerning): if not isinstance(kerning, numbers.Real): - raise TypeError('expected a numbers.Real, but got ' + repr(kerning)) + raise TypeError('expected a numbers.Real, but got ' + + repr(kerning)) library.DrawSetTextKerning(self.resource, kerning) @property @@ -824,15 +839,19 @@ def text_under_color(self, color): @property def vector_graphics(self): - """(:class:`basestring`) The XML text of the Vector Graphics. It also - can be set. The drawing-wand XML is experimental, and subject to change. + """(:class:`basestring`) The XML text of the Vector Graphics. + It also can be set. The drawing-wand XML is experimental, + and subject to change. Setting this property to None will reset all vector graphic properties to the default state. .. versionadded:: 0.4.0 + .. versionchanged: 0.4.1 - Safely release allocated memory with MagickRelinquishMemory instead of libc.free. + Safely release allocated memory with + :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. + """ vector_graphics_p = library.DrawGetVectorGraphics(self.resource) return '' + text(vector_graphics_p.value) + '' @@ -847,7 +866,8 @@ def vector_graphics(self, vector_graphics): library.DrawResetVectorGraphics(self.resource) else: vector_graphics = binary(vector_graphics) - okay = library.DrawSetVectorGraphics(self.resource, vector_graphics) + okay = library.DrawSetVectorGraphics(self.resource, + vector_graphics) if okay == 0: raise ValueError("Vector graphic not understood.") @@ -909,9 +929,9 @@ def affine(self, matrix): | x', y', 1 | = | x, y, 1 | * | ry sy 0 | | tx ty 1 | - :param matrix: a list of :class:`~numbers.Real` to define affine matrix. - `[sx, rx, ry, sy, tx, ty]` - :type matrix: :class:`~collections.Sequence` + :param matrix: a list of :class:`~numbers.Real` to define affine + matrix ``[sx, rx, ry, sy, tx, ty]`` + :type matrix: :class:`collections.Sequence` .. versionadded:: 0.4.0 @@ -992,8 +1012,8 @@ def color(self, x=None, y=None, paint_method='undefined'): if not isinstance(paint_method, string_type): raise TypeError('expected a string, not ' + repr(paint_method)) elif paint_method not in PAINT_METHOD_TYPES: - raise ValueError('expected a string from PAINT_METHOD_TYPES, not ' + - repr(paint_method)) + raise ValueError('expected a string from PAINT_METHOD_TYPES, not ' + + repr(paint_method)) library.DrawColor(self.resource, float(x), float(y), PAINT_METHOD_TYPES.index(paint_method)) @@ -1120,8 +1140,8 @@ def matte(self, x=None, y=None, paint_method='undefined'): if not isinstance(paint_method, string_type): raise TypeError('expected a string, not ' + repr(paint_method)) elif paint_method not in PAINT_METHOD_TYPES: - raise ValueError('expected a string from PAINT_METHOD_TYPES, not ' + - repr(paint_method)) + raise ValueError('expected a string from PAINT_METHOD_TYPES, not ' + + repr(paint_method)) library.DrawMatte(self.resource, float(x), float(y), PAINT_METHOD_TYPES.index(paint_method)) @@ -1138,22 +1158,22 @@ def path_close(self): def path_curve(self, to=None, controls=None, smooth=False, relative=False): """Draws a cubic Bezier curve from the current point to given ``to`` - (x,y) coordinate using ``controls`` points at the beginning & end of the - curve. If ``smooth`` is set to True, only one ``controls`` is expected + (x,y) coordinate using ``controls`` points at the beginning and + the end of the curve. + If ``smooth`` is set to True, only one ``controls`` is expected and the previous control is used, else two pair of coordinates are expected to define the control points. The ``to`` coordinate then becomes the new current point. :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to. + pair which represents coordinates to draw to :type to: :class:`collections.Sequence` :param controls: (:class:`~numbers.Real`, :class:`numbers.Real`) - coordinate to used to influence curve + coordinate to used to influence curve :type controls: :class:`collections.Sequence` :param smooth: :class:`bool` assume last defined control coordinate :type smooth: :class:`bool` - :param relative: :class:`bool` - treat given coordinates as relative to current point + :param relative: treat given coordinates as relative to current point :type relative: :class:`bool` .. versionadded:: 0.4.0 @@ -1190,22 +1210,21 @@ def path_curve_to_quadratic_bezier(self, to=None, control=None, """Draws a quadratic Bezier curve from the current point to given ``to`` coordinate. The control point is assumed to be the reflection of the control point on the previous command if ``smooth`` is True, else a - pair of ``control`` coordinates must be given. Each` coordinates can be + pair of ``control`` coordinates must be given. Each coordinates can be relative, or absolute, to the current point by setting the ``relative`` flag. The ``to`` coordinate then becomes the new current point, and the - ``control`` coordinate will be assumed when called again when ``smooth`` - is set to true. + ``control`` coordinate will be assumed when called again + when ``smooth`` is set to true. :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to. + pair which represents coordinates to draw to :type to: :class:`collections.Sequence` :param control: (:class:`~numbers.Real`, :class:`numbers.Real`) - coordinate to used to influence curve + coordinate to used to influence curve :type control: :class:`collections.Sequence` - :param smooth: :class:`bool` assume last defined control coordinate + :param smooth: assume last defined control coordinate :type smooth: :class:`bool` - :param relative: :class:`bool` - treat given coordinates as relative to current point + :param relative: treat given coordinates as relative to current point :type relative: :class:`bool` .. versionadded:: 0.4.0 @@ -1245,10 +1264,11 @@ def path_curve_to_quadratic_bezier(self, to=None, control=None, def path_elliptic_arc(self, to=None, radius=None, rotation=0.0, large_arc=False, clockwise=False, relative=False): """Draws an elliptical arc from the current point to given ``to`` - coordinates. The ``to`` coordinates can be relative, or absolute, to the - current point by setting the ``relative`` flag. The size and orientation - of the ellipse are defined by two radii (rx, ry) in ``radius`` and an - ``rotation`` parameters, which indicates how the ellipse as a whole is + coordinates. The ``to`` coordinates can be relative, or absolute, + to the current point by setting the ``relative`` flag. + The size and orientation of the ellipse are defined by + two radii (rx, ry) in ``radius`` and an ``rotation`` parameters, + which indicates how the ellipse as a whole is rotated relative to the current coordinate system. The center of the ellipse is calculated automagically to satisfy the constraints imposed by the other parameters. ``large_arc`` and ``clockwise`` contribute to @@ -1258,20 +1278,18 @@ def path_elliptic_arc(self, to=None, radius=None, rotation=0.0, rotation. :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to. + pair which represents coordinates to draw to :type to: :class:`collections.Sequence` :param radius: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents the radii of the ellipse to draw + pair which represents the radii of the ellipse to draw :type radius: :class:`collections.Sequence` - :param rotate: :class:`~numbers.Real` degree to rotate ellipse on x-axis + :param rotate: degree to rotate ellipse on x-axis :type rotate: :class:`~numbers.Real` - :param large_arc: :class:`bool` draw largest available arc + :param large_arc: draw largest available arc :type large_arc: :class:`bool` - :param clockwise: :class:`bool` - draw arc path clockwise from start to target + :param clockwise: draw arc path clockwise from start to target :type clockwise: :class:`bool` - :param relative: :class:`bool` - treat given coordinates as relative to current point + :param relative: treat given coordinates as relative to current point :type relative: :class:`bool` .. versionadded:: 0.4.0 @@ -1513,8 +1531,8 @@ def push_pattern(self, pattern_id, left, top, width, height): :class:`Drawing.pop_pattern` command comprise the definition of a named pattern. The pattern space is assigned top left corner coordinates, a width and height, and becomes its own drawing space. Anything which can - be drawn may be used in a pattern definition. Named patterns may be used - as stroke or brush definitions. + be drawn may be used in a pattern definition. + Named patterns may be used as stroke or brush definitions. :param pattern_id: a unique identifier for the pattern. :type pattern_id: :class:`basestring` @@ -1653,7 +1671,7 @@ def rectangle(self, left=None, top=None, right=None, bottom=None, if xradius is None: xradius = 0.0 if yradius is None: - yraduis = 0.0 + yradius = 0.0 if not isinstance(xradius, numbers.Real): raise TypeError('xradius must be numbers.Real, not ' + repr(xradius)) diff --git a/wand/image.py b/wand/image.py index 4bb64c20..540beebd 100644 --- a/wand/image.py +++ b/wand/image.py @@ -30,7 +30,8 @@ 'COMPOSITE_OPERATORS', 'COMPRESSION_TYPES', 'EVALUATE_OPS', 'FILTER_TYPES', 'GRAVITY_TYPES', 'IMAGE_TYPES', 'ORIENTATION_TYPES', 'UNIT_TYPES', - 'FUNCTION_TYPES', 'BaseImage', 'ChannelDepthDict', 'ChannelImageDict', + 'FUNCTION_TYPES', + 'BaseImage', 'ChannelDepthDict', 'ChannelImageDict', 'ClosedImageError', 'HistogramDict', 'Image', 'ImageProperty', 'Iterator', 'Metadata', 'OptionDict', 'manipulative') @@ -448,11 +449,13 @@ #: - ``'sentinel'`` #: #: .. versionadded:: 0.4.1 -DISTORTION_METHODS = ('undefined', 'affine', 'affine_projection', 'scale_rotate_translate', - 'perspective', 'perspective_projection', 'bilinear_forward', - 'bilinear_reverse', 'polynomial', 'arc', 'polar', 'depolar', - 'cylinder_2_plane', 'plane_2_cylinder', 'barrel', 'barrel_inverse', - 'shepards', 'resize', 'sentinel') +DISTORTION_METHODS = ( + 'undefined', 'affine', 'affine_projection', 'scale_rotate_translate', + 'perspective', 'perspective_projection', 'bilinear_forward', + 'bilinear_reverse', 'polynomial', 'arc', 'polar', 'depolar', + 'cylinder_2_plane', 'plane_2_cylinder', 'barrel', 'barrel_inverse', + 'shepards', 'resize', 'sentinel' +) #: (:class:`tuple`) The list of :attr:`~BaseImage.virtual_pixel` types. #: - ``'undefined'`` @@ -938,8 +941,10 @@ def virtual_pixel(self, method): if method not in VIRTUAL_PIXEL_METHOD: raise ValueError('expected method from VIRTUAL_PIXEL_METHOD,' ' not ' + repr(method)) - library.MagickSetImageVirtualPixelMethod(self.wand, - VIRTUAL_PIXEL_METHOD.index(method)) + library.MagickSetImageVirtualPixelMethod( + self.wand, + VIRTUAL_PIXEL_METHOD.index(method) + ) @property def colorspace(self): @@ -1206,9 +1211,11 @@ def distort(self, method, arguments, best_fit=False): .. versionadded:: 0.4.1 """ if method not in DISTORTION_METHODS: - raise ValueError('expected string from DISTORTION_METHODS, not ' + repr(method)) + raise ValueError('expected string from DISTORTION_METHODS, not ' + + repr(method)) if not isinstance(arguments, collections.Sequence): - raise TypeError('expected sequence of doubles, not ' + repr(arguments)) + raise TypeError('expected sequence of doubles, not ' + + repr(arguments)) argc = len(arguments) argv = (ctypes.c_double * argc)(*arguments) library.MagickDistortImage(self.wand, @@ -1274,8 +1281,7 @@ def crop(self, left=0, top=0, right=None, bottom=None, :attr:`width` and :attr:`height` parameters to be included. :type gravity: :const:`GRAVITY_TYPES` - :raises exceptions.ValueError: - when one or more arguments are invalid + :raises ValueError: when one or more arguments are invalid .. note:: @@ -1305,7 +1311,9 @@ def crop(self, left=0, top=0, right=None, bottom=None, # Define left & top if gravity is given. if gravity: if width is None or height is None: - raise TypeError('Both width and height must be defined with gravity') + raise TypeError( + 'both width and height must be defined with gravity' + ) if gravity not in GRAVITY_TYPES: raise ValueError('expected a string from GRAVITY_TYPES, not ' + repr(gravity)) @@ -1747,12 +1755,14 @@ def evaluate(self, operator=None, value=0.0, channel=None): .. versionadded:: 0.4.1 """ if operator not in EVALUATE_OPS: - raise ValueError('expected value from EVALUATE_OPS, not ' + repr(operator)) + raise ValueError('expected value from EVALUATE_OPS, not ' + + repr(operator)) if not isinstance(value, numbers.Real): raise TypeError('value must be real number, not ' + repr(value)) if channel: if channel not in CHANNELS: - raise ValueError('expected value from CHANNELS, not ' + repr(channel)) + raise ValueError('expected value from CHANNELS, not ' + + repr(channel)) library.MagickEvaluateImageChannel(self.wand, CHANNELS[channel], EVALUATE_OPS.index(operator), @@ -1787,31 +1797,35 @@ def flop(self): self.raise_exception() @manipulative - def frame(self, matte=None, width=1, height=1, inner_bevel=0, outer_bevel=0): - """Creates a bordered frame around image. Inner & Outer bevel can simulate - a 3D effect. + def frame(self, matte=None, width=1, height=1, inner_bevel=0, + outer_bevel=0): + """Creates a bordered frame around image. + Inner & outer bevel can simulate a 3D effect. - :param matte: Color of the frame + :param matte: color of the frame :type matte: :class:`wand.color.Color` - :param width: Total size of frame on x-axis + :param width: total size of frame on x-axis :type width: :class:`numbers.Integral` - :param height: Total size of frame on y-axis + :param height: total size of frame on y-axis :type height: :class:`numbers.Integral` - :param inner_bevel: Inset shadow length + :param inner_bevel: inset shadow length :type inner_bevel: :class:`numbers.Real` - :param outer_bevel: Outset highlight length + :param outer_bevel: outset highlight length :type outer_bevel: :class:`numbers.Real` .. versionadded:: 0.4.1 + """ if matte is None: matte = Color('gray') if not isinstance(matte, Color): - raise TypeError('Expecting instance of Color for matte, not ' + repr(matte)) + raise TypeError('Expecting instance of Color for matte, not ' + + repr(matte)) if not isinstance(width, numbers.Integral): raise TypeError('Expecting integer for width, not ' + repr(width)) if not isinstance(height, numbers.Integral): - raise TypeError('Expecting integer for height, not ' + repr(height)) + raise TypeError('Expecting integer for height, not ' + + repr(height)) if not isinstance(inner_bevel, numbers.Real): raise TypeError('Expecting real number, not ' + repr(inner_bevel)) if not isinstance(outer_bevel, numbers.Real): @@ -1829,39 +1843,45 @@ def function(self, function, arguments, channel=None): Defaults entire image, but can isolate affects to single color channel by passing :const:`CHANNELS` value to ``channel`` parameter. - .. note:: Support for function methods added in the following versions - of ImageMagick. + .. note:: + + Support for function methods added in the following versions + of ImageMagick. - - ``'polynomial'`` >= 6.4.8-8 - - ``'sinusoid'`` >= 6.4.8-8 - - ``'arcsin'`` >= 6.5.3-1 - - ``'arctan'`` >= 6.5.3-1 + - ``'polynomial'`` >= 6.4.8-8 + - ``'sinusoid'`` >= 6.4.8-8 + - ``'arcsin'`` >= 6.5.3-1 + - ``'arctan'`` >= 6.5.3-1 - :param function: A string listed in :const:`FUNCTION_TYPES` + :param function: a string listed in :const:`FUNCTION_TYPES` :type function: :class:`basestring` - :param arguments: A sequence of doubles to apply against ``function`` + :param arguments: a sequence of doubles to apply against ``function`` :type arguments: :class:`collections.Sequence` - :param channel: Optional :const:`CHANNELS`, defaults all. + :param channel: optional :const:`CHANNELS`, defaults all :type channel: :class:`basestring` - :raises exception.ValueError: When a ``function``, or ``channel`` is not - defined in there respected constant. - :raises exception.TypeError: If ``arguments`` is not a sequence. + :raises ValueError: when a ``function``, or ``channel`` is not + defined in there respected constant + :raises TypeError: if ``arguments`` is not a sequence .. versionadded:: 0.4.1 """ if function not in FUNCTION_TYPES: - raise ValueError('expected string from FUNCTION_TYPES, not ' + repr(function)) + raise ValueError('expected string from FUNCTION_TYPES, not ' + + repr(function)) if not isinstance(arguments, collections.Sequence): - raise TypeError('expecting sequence of arguments, not ' + repr(arguments)) + raise TypeError('expecting sequence of arguments, not ' + + repr(arguments)) argc = len(arguments) argv = (ctypes.c_double * argc)(*arguments) index = FUNCTION_TYPES.index(function) if channel is None: library.MagickFunctionImage(self.wand, index, argc, argv) elif channel in CHANNELS: - library.MagickFunctionImageChannel(self.wand, CHANNELS[channel], index, argc, argv) + library.MagickFunctionImageChannel(self.wand, CHANNELS[channel], + index, argc, argv) else: - raise ValueError('expected string from CHANNELS, not ' + repr(channel)) + raise ValueError('expected string from CHANNELS, not ' + + repr(channel)) self.raise_exception() @manipulative @@ -1888,14 +1908,18 @@ def fx(self, expression, channel=None): .. versionadded:: 0.4.1 """ if not isinstance(expression, string_type): - raise TypeError('expected basestring for expression, not' + repr(expression)) + raise TypeError('expected basestring for expression, not' + + repr(expression)) c_expression = binary(expression) if channel is None: new_wand = library.MagickFxImage(self.wand, c_expression) elif channel in CHANNELS: - new_wand = library.MagickFxImageChannel(self.wand, CHANNELS[channel], c_expression) + new_wand = library.MagickFxImageChannel(self.wand, + CHANNELS[channel], + c_expression) else: - raise ValueError('expected string from CHANNELS, not ' + repr(channel)) + raise ValueError('expected string from CHANNELS, not ' + + repr(channel)) if new_wand: return Image(image=BaseImage(new_wand)) self.raise_exception() @@ -2012,8 +2036,8 @@ def composite_channel(self, channel, image, operator, left=0, top=0): :type left: :class:`numbers.Integral` :param top: the row offset of the composited source image :type top: :class:`numbers.Integral` - :raises exceptions.ValueError: when the given ``channel`` or - ``operator`` is invalid + :raises ValueError: when the given ``channel`` or + ``operator`` is invalid .. versionadded:: 0.3.0 @@ -2066,9 +2090,7 @@ def modulate(self, brightness=100.0, saturation=100.0, hue=100.0): :type saturation: :class:`numbers.Real` :param hue: percentage of hue rotation :type hue: :class:`numbers.Real` - - :raises exceptions.ValueError: - when one or more arguments are invalid + :raises ValueError: when one or more arguments are invalid .. versionadded:: 0.3.4 @@ -2259,29 +2281,31 @@ def quantize(self, number_colors, colorspace_type, :param colorspace_type: colorspace_type. available value can be found in the :const:`COLORSPACE_TYPES` :type colorspace_type: :class:`basestring` - :param treedepth: Normally, this integer value is zero or one. A zero or - one tells Quantize to choose a optimal tree depth of - Log4(number_colors). A tree of this depth generally - allows the best representation of the reference image + :param treedepth: normally, this integer value is zero or one. + a zero or one tells :meth:`quantize` to choose + a optimal tree depth of ``log4(number_colors)``. + a tree of this depth generally allows the best + representation of the reference image with the least amount of memory and - the fastest computational speed. In some cases, - such as an image with low color dispersion - (a few number of colors), a value other than - Log4(number_colors) is required. To expand - the color tree completely, use a value of 8. + the fastest computational speed. + in some cases, such as an image with low color + dispersion (a few number of colors), a value other + than ``log4(number_colors)`` is required. + to expand the color tree completely, + use a value of 8 :type treedepth: :class:`numbers.Integral` - :param dither: A value other than zero distributes the difference + :param dither: a value other than zero distributes the difference between an original image and the corresponding color reduced algorithm to neighboring pixels along - a Hilbert curve. + a Hilbert curve :type dither: :class:`bool` - :param measure_error: A value other than zero measures the difference + :param measure_error: a value other than zero measures the difference between the original and quantized images. - This difference is the total quantization error. + this difference is the total quantization error. The error is computed by summing over all pixels in an image the distance squared in RGB space between each reference pixel value and - its quantized value. + its quantized value :type measure_error: :class:`bool` .. versionadded:: 0.4.2 @@ -2308,9 +2332,11 @@ def quantize(self, number_colors, colorspace_type, raise TypeError('measure_error must be a bool, not ' + repr(measure_error)) - r = library.MagickQuantizeImage(self.wand, number_colors, - COLORSPACE_TYPES.index(colorspace_type), - treedepth, dither, measure_error) + r = library.MagickQuantizeImage( + self.wand, number_colors, + COLORSPACE_TYPES.index(colorspace_type), + treedepth, dither, measure_error + ) if not r: self.raise_exception() @@ -2329,7 +2355,10 @@ def transform_colorspace(self, colorspace_type): or colorspace_type not in COLORSPACE_TYPES: raise TypeError('Colorspace value must be a string from ' 'COLORSPACE_TYPES, not ' + repr(colorspace_type)) - r = library.MagickTransformImageColorspace(self.wand, COLORSPACE_TYPES.index(colorspace_type)) + r = library.MagickTransformImageColorspace( + self.wand, + COLORSPACE_TYPES.index(colorspace_type) + ) if not r: self.raise_exception() @@ -2349,7 +2378,7 @@ class Image(BaseImage): :param image: makes an exact copy of the ``image`` :type image: :class:`Image` :param blob: opens an image of the ``blob`` byte array - :type blob: :class:`str` + :type blob: :class:`bytes` :param file: opens an image of the ``file`` object :type file: file object :param filename: opens an image of the ``filename`` string @@ -2494,7 +2523,7 @@ def read(self, file=None, filename=None, blob=None, resolution=None): """Read new image into Image() object. :param blob: reads an image from the ``blob`` byte array - :type blob: :class:`str` + :type blob: :class:`bytes` :param file: reads an image from the ``file`` object :type file: file object :param filename: reads an image from the ``filename`` string @@ -2745,7 +2774,7 @@ def convert(self, format): :type format: :class:`basestring` :returns: a converted image :rtype: :class:`Image` - :raises: :exc:`ValueError` when the given ``format`` is unsupported + :raises ValueError: when the given ``format`` is unsupported .. versionadded:: 0.1.6 @@ -2812,8 +2841,8 @@ def make_blob(self, format=None): it is omittable :type format: :class:`basestring` :returns: a blob (bytes) string - :rtype: :class:`str` - :raises: :exc:`ValueError` when ``format`` is invalid + :rtype: :class:`bytes` + :raises ValueError: when ``format`` is invalid .. versionchanged:: 0.1.6 Removed a side effect that changes the image :attr:`format` @@ -2900,10 +2929,12 @@ def transverse(self): @manipulative def _auto_orient(self): - """Fallback for :attr:`auto_orient()` method (which wraps :c:func:`MagickAutoOrientImage`), + """Fallback for :attr:`auto_orient()` method + (which wraps :c:func:`MagickAutoOrientImage`), fixes orientation by checking EXIF data. .. versionadded:: 0.4.1 + """ exif_orientation = self.metadata.get('exif:orientation') if not exif_orientation: @@ -2934,11 +2965,13 @@ def _auto_orient(self): @manipulative def auto_orient(self): """Adjusts an image so that its orientation is suitable - for viewing (i.e. top-left orientation). if available it uses :c:func:`MagickAutoOrientImage` - (was added in ImageMagick 6.8.9+) if you have an older magick library, - it will use :attr:`_auto_orient()` method for fallback + for viewing (i.e. top-left orientation). If available it uses + :c:func:`MagickAutoOrientImage` (was added in ImageMagick 6.8.9+) + if you have an older magick library, + it will use :attr:`_auto_orient()` method for fallback. .. versionadded:: 0.4.1 + """ try: result = library.MagickAutoOrientImage(self.wand) @@ -2970,29 +3003,31 @@ def border(self, color, width, height): self.raise_exception() @manipulative - def contrast_stretch(self, black_point=0.0, white_point=None, channel=None): + def contrast_stretch(self, black_point=0.0, white_point=None, + channel=None): """Enhance contrast of image by adjusting the span of the available colors. - If only ``black_point`` is given, match the CLI behavior by assuming the - ``white_point`` has the same delta percentage off the top. E.g. contrast - stretch of 15% is calculated as ``black_point`` = 0.15 and - ``white_point`` = 0.85. + If only ``black_point`` is given, match the CLI behavior by assuming + the ``white_point`` has the same delta percentage off the top + e.g. contrast stretch of 15% is calculated as ``black_point`` = 0.15 + and ``white_point`` = 0.85. - :param black_point: Black point between 0.0 and 1.0. Default 0.0 + :param black_point: black point between 0.0 and 1.0. default is 0.0 :type black_point: :class:`numbers.Real` - :param white_point: White point between 0.0 and 1.0. Default value of - 1.0 minus ``black_point``. + :param white_point: white point between 0.0 and 1.0. + default value of 1.0 minus ``black_point`` :type white_point: :class:`numbers.Real` - :param channel: Optional color channel to apply contrast stretch. + :param channel: optional color channel to apply contrast stretch :type channel: :const:`CHANNELS` - :raises: :exc:`ValueError` if ``channel`` is not in :const:`CHANNELS` + :raises ValueError: if ``channel`` is not in :const:`CHANNELS` .. versionadded:: 0.4.1 + """ if not isinstance(black_point, numbers.Real): raise TypeError('expecting float, not ' + repr(black_point)) - if white_point is not None and not isinstance(white_point, numbers.Real): + if not (white_point is None or isinstance(white_point, numbers.Real)): raise TypeError('expecting float, not ' + repr(white_point)) # If only black-point is given, match CLI behavior by # calculating white point @@ -3022,14 +3057,15 @@ def gamma(self, adjustment_value, channel=None): Specific color channels can be correct individual. Typical values range between 0.8 and 2.3. - :param adjustment_value: Value to adjust gamma level + :param adjustment_value: value to adjust gamma level :type adjustment_value: :class:`numbers.Real` - :param channel: Optional channel to apply gamma correction + :param channel: optional channel to apply gamma correction :type channel: :class:`basestring` - :raises: :exc:`TypeError` if ``gamma_point`` is not a :class:`numbers.Real` - :raises: :exc:`ValueError` if ``channel`` is not in :const:`CHANNELS` + :raises TypeError: if ``gamma_point`` is not a :class:`numbers.Real` + :raises ValueError: if ``channel`` is not in :const:`CHANNELS` .. versionadded:: 0.4.1 + """ if not isinstance(adjustment_value, numbers.Real): raise TypeError('expecting float, not ' + repr(adjustment_value))