pax_global_header00006660000000000000000000000064125565156630014530gustar00rootroot0000000000000052 comment=e0531db4fa3d99d88cbaf381cc61e16c23928ffc wcag-contrast-ratio-0.9/000077500000000000000000000000001255651566300152705ustar00rootroot00000000000000wcag-contrast-ratio-0.9/.gitignore000066400000000000000000000034511255651566300172630ustar00rootroot00000000000000# Copyright (c) 2014 GitHub, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging MANIFEST .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .hypothesis/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ wcag-contrast-ratio-0.9/.travis.yml000066400000000000000000000003601255651566300174000ustar00rootroot00000000000000language: python python: - "2.6" - "2.7" - "3.2" - "3.3" - "3.4" - "pypy" - "pypy3" sudo: false cache: directories: - $HOME/.cache/pip install: - pip install -U pytest hypothesis-pytest script: - py.test test.py wcag-contrast-ratio-0.9/LICENSE000066400000000000000000000020471255651566300163000ustar00rootroot00000000000000Copyright (c) 2015 Geoffrey Sneddon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wcag-contrast-ratio-0.9/MANIFEST.in000066400000000000000000000000431255651566300170230ustar00rootroot00000000000000include README.rst include test.py wcag-contrast-ratio-0.9/README.rst000066400000000000000000000011321255651566300167540ustar00rootroot00000000000000wcag-contrast-ratio =================== A library for computing contrast ratios, as required by `WCAG 2.0`. Usage ----- Simple usage follows this pattern: .. code-block:: python >> import wcag_contrast_ratio as contrast >> black = (0.0, 0.0, 0.0) >> white = (1.0, 1.0, 1.0) >> contrast.rgb(black, white) 21.0 Two useful helper functions are provided, to check if contrast meets the required level: .. code-block:: python >> import wcag_contrast_ratio as contrast >> contrast.passes_AA(21.0) True >> contrast.passes_AAA(21.0) True .. _WCAG 2.0: http://www.w3.org/TR/WCAG20/ wcag-contrast-ratio-0.9/setup.py000066400000000000000000000023151255651566300170030ustar00rootroot00000000000000from distutils.core import setup import codecs import os.path _classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Topic :: Software Development' ] _current_dir = os.path.dirname(__file__) with codecs.open(os.path.join(_current_dir, 'README.rst'), 'r', 'utf8') as readme_file: _long_description = readme_file.read() setup( name="wcag-contrast-ratio", version="0.9", url="https://github.com/gsnedders/wcag-contrast-ratio", license="MIT License", description="A library for computing contrast ratios, as required by WCAG 2.0", long_description=_long_description, classifiers=_classifiers, maintainer="Geoffrey Sneddon", maintainer_email="geoffers@gmail.com", packages = ["wcag_contrast_ratio"] ) wcag-contrast-ratio-0.9/test.py000066400000000000000000000045341255651566300166270ustar00rootroot00000000000000from hypothesis import given from hypothesis.strategies import floats, tuples import pytest import wcag_contrast_ratio as contrast color_channel = floats(0.0, 1.0) color = tuples(color_channel, color_channel, color_channel) @pytest.mark.parametrize("rgb1,rgb2,expected", [[(0.0, 0.0, 0.0), (1.0, 1.0, 1.0), 21.0], [(0.0, 0.0, 0.0), (0.0, 0.0, 0.0), 1.0], [(0.0, 198/255.0, 0.0), (0.0, 0.0, 198/255.0), 5.000229313902297]]) def test_contrast(rgb1, rgb2, expected): c1 = contrast.rgb(rgb1, rgb2) c2 = contrast.rgb(rgb2, rgb1) assert c1 == expected assert c2 == expected @pytest.mark.parametrize("c,expected,large_expected", [(0.0, False, False), (2.9, False, False), (3.0, False, True), (3.1, False, True), (4.4, False, True), (4.5, True, True), (4.6, True, True), (6.9, True, True), (7.0, True, True), (7.1, True, True), (21.0, True, True)]) def test_passes_AA(c, expected, large_expected): got = contrast.passes_AA(c, large=False) assert got == expected got_large = contrast.passes_AA(c, large=True) assert got_large == large_expected @pytest.mark.parametrize("c,expected,large_expected", [(0.0, False, False), (2.9, False, False), (3.0, False, False), (3.1, False, False), (4.4, False, False), (4.5, False, True), (4.6, False, True), (6.9, False, True), (7.0, True, True), (7.1, True, True), (21.0, True, True)]) def test_passes_AAA(c, expected, large_expected): got = contrast.passes_AAA(c, large=False) assert got == expected got_large = contrast.passes_AAA(c, large=True) assert got_large == large_expected @given(color, color) def test_hypothesis_contrast(rgb1, rgb2): c1 = contrast.rgb(rgb1, rgb2) c2 = contrast.rgb(rgb1, rgb2) assert 1.0 <= c1 <= 21.0 assert c1 == c2 wcag-contrast-ratio-0.9/tox.ini000066400000000000000000000002151255651566300166010ustar00rootroot00000000000000[tox] envlist = py26,py27,py32,py33,py34,pypy,pypy3 [testenv] deps = pytest hypothesis-pytest commands = {envbindir}/py.test test.py wcag-contrast-ratio-0.9/wcag_contrast_ratio/000077500000000000000000000000001255651566300213245ustar00rootroot00000000000000wcag-contrast-ratio-0.9/wcag_contrast_ratio/__init__.py000066400000000000000000000000301255651566300234260ustar00rootroot00000000000000from .contrast import * wcag-contrast-ratio-0.9/wcag_contrast_ratio/contrast.py000066400000000000000000000021651255651566300235370ustar00rootroot00000000000000from __future__ import division __all__ = ["rgb", "passes_AA", "passes_AAA"] def rgb(rgb1, rgb2): for r, g, b in (rgb1, rgb2): if not 0.0 <= r <= 1.0: raise ValueError("r is out of valid range (0.0 - 1.0)") if not 0.0 <= g <= 1.0: raise ValueError("g is out of valid range (0.0 - 1.0)") if not 0.0 <= b <= 1.0: raise ValueError("b is out of valid range (0.0 - 1.0)") l1 = _relative_luminance(*rgb1) l2 = _relative_luminance(*rgb2) if l1 > l2: return (l1 + 0.05) / (l2 + 0.05) else: return (l2 + 0.05) / (l1 + 0.05) def _relative_luminance(r, g, b): r = _linearize(r) g = _linearize(g) b = _linearize(b) return 0.2126 * r + 0.7152 * g + 0.0722 * b def _linearize(v): if v <= 0.03928: return v / 12.92 else: return ((v + 0.055) / 1.055) ** 2.4 def passes_AA(contrast, large=False): if large: return contrast >= 3.0 else: return contrast >= 4.5 def passes_AAA(contrast, large=False): if large: return contrast >= 4.5 else: return contrast >= 7.0