I would like to write a function in Python that compares two colours. Let's say I have a background colour and a font colour. And I want wo check if the font has enough contrast to be visible on the background. I receive the colours in RGB format like 16777215 (white). How can I do that?
What does the difference between two colour values, e.g. 16777215 - 12788622 mean? Does the difference say anything about the contrast?
Edit: A solution based on the comments of @VC.One that loops through a pdf document to find text with low contrast on a white background:
import streamlit as st
import pymupdf
def main():
# File Uploader
uploaded_file = st.file_uploader("Select PDF file: ", type='pdf')
# Search document
if uploaded_file is not None:
find_low_contrast_font(uploaded_file)
def find_low_contrast_font(uploaded_file):
# Open document
doc = pymupdf.open("pdf", uploaded_file.read())
# Loop through the pages
for page_num in range(doc.page_count):
page = doc[page_num]
for text in page.get_text("dict")["blocks"]:
for line in text["lines"]:
for span in line["spans"]:
if contrast_is_low(span["color"]):
st.write(f"Low contrast font on page {page_num + 1}: {span['text']}")
# Close document
doc.close()
def contrast_is_low(font_color):
background_color = 16777215 # White
minimum_distance = 40 # (DF,DF,DF) / (223,223,223) / 14671839 / light grey: distance to white is 32
low_contrast = False
background_color_rgb_dict = get_rgb_values_from_integer(background_color)
background_color_luminance = calculate_luminance(background_color_rgb_dict)
font_color_rgb_dict = get_rgb_values_from_integer(font_color)
font_color_luminance = calculate_luminance(font_color_rgb_dict)
distance = calculate_distance(background_color_luminance, font_color_luminance)
if distance <= minimum_distance:
low_contrast = True
return low_contrast
def get_rgb_values_from_integer(rgb_int):
blue = rgb_int & 255
green = (rgb_int >> 8) & 255
red = (rgb_int >> 16) & 255
rgb_dict = {
"r": red,
"g": green,
"b": blue
}
return rgb_dict
def calculate_luminance(rgb_dict):
return 0.299 * rgb_dict["r"] + 0.587 * rgb_dict["g"] + 0.114 * rgb_dict["b"]
def calculate_distance(background_color_luminance, font_color_luminance):
return abs(background_color_luminance - font_color_luminance)
if __name__ == "__main__":
main()
I would like to write a function in Python that compares two colours. Let's say I have a background colour and a font colour. And I want wo check if the font has enough contrast to be visible on the background. I receive the colours in RGB format like 16777215 (white). How can I do that?
What does the difference between two colour values, e.g. 16777215 - 12788622 mean? Does the difference say anything about the contrast?
Edit: A solution based on the comments of @VC.One that loops through a pdf document to find text with low contrast on a white background:
import streamlit as st
import pymupdf
def main():
# File Uploader
uploaded_file = st.file_uploader("Select PDF file: ", type='pdf')
# Search document
if uploaded_file is not None:
find_low_contrast_font(uploaded_file)
def find_low_contrast_font(uploaded_file):
# Open document
doc = pymupdf.open("pdf", uploaded_file.read())
# Loop through the pages
for page_num in range(doc.page_count):
page = doc[page_num]
for text in page.get_text("dict")["blocks"]:
for line in text["lines"]:
for span in line["spans"]:
if contrast_is_low(span["color"]):
st.write(f"Low contrast font on page {page_num + 1}: {span['text']}")
# Close document
doc.close()
def contrast_is_low(font_color):
background_color = 16777215 # White
minimum_distance = 40 # (DF,DF,DF) / (223,223,223) / 14671839 / light grey: distance to white is 32
low_contrast = False
background_color_rgb_dict = get_rgb_values_from_integer(background_color)
background_color_luminance = calculate_luminance(background_color_rgb_dict)
font_color_rgb_dict = get_rgb_values_from_integer(font_color)
font_color_luminance = calculate_luminance(font_color_rgb_dict)
distance = calculate_distance(background_color_luminance, font_color_luminance)
if distance <= minimum_distance:
low_contrast = True
return low_contrast
def get_rgb_values_from_integer(rgb_int):
blue = rgb_int & 255
green = (rgb_int >> 8) & 255
red = (rgb_int >> 16) & 255
rgb_dict = {
"r": red,
"g": green,
"b": blue
}
return rgb_dict
def calculate_luminance(rgb_dict):
return 0.299 * rgb_dict["r"] + 0.587 * rgb_dict["g"] + 0.114 * rgb_dict["b"]
def calculate_distance(background_color_luminance, font_color_luminance):
return abs(background_color_luminance - font_color_luminance)
if __name__ == "__main__":
main()
Share
Improve this question
edited Jan 30 at 10:49
mike3467
asked Jan 23 at 10:04
mike3467mike3467
557 bronze badges
11
|
Show 6 more comments
1 Answer
Reset to default 0PDF uses colors in many ways however most are counted as textual floats at a position. So if you have 1.00 red in the same position on 1.00 green then there is Human contrast (unless red/green colour blind)
Both are symbolically ÿ
(when decompressed for rendering) and thus have a decimalised value of 255 (we tend to say 1.00 = \O000 or \x00 in such cases). This makes it difficult to determine PDF contrast in a simplistic fashion.
Here the foreground text Hello
is value 127 and the background is [.5 .5 .5] yet both look like the same grey however may print as different colours by a printers profile). Mathematically wildly different, yet have no contrast whatsoever. There are many many similar ways to set PDF bytes as transparently the same contrast visually yet mathematically their bytes will be different values.
stream
q /GS0 gs .5 .5 .5 rg 0 0 438 540 re f 0.5 g BT /F0 12 Tf 1 0 0 1 100 400 Tm [(Hello)] TJ ET Q
endstream
When viewed in Adobe Reader there is a contrast seen even if mild!
But we can enhance Colour Contrast for viewing or printing
For this reason PDF/A verification requires a Human (Pre-flight attendant) be used to decide if a PDF has sufficiently contrasting colours.
These are very contrasting colours but all just the same 2 characters since colours in a PDF are predominantly described in ASCII or ANSI or Hex encoded textual terms (many other encodings are used but usually those are compressed as raw single byte colours).
And 2 colours as seen above are "Black" but render as 2 white squares.
grey = R * 0.299 + G* 0.587 + B * 0.114
then it returns the new grey value)... (3) There is more contrast if there is a larger distance between grey of BG versus grey of the font, so just test + calibrate distance + re-test until your code knows a good target value/distance to classify as making a colour "more visible" against another colour. – VC.One Commented Jan 25 at 19:59contrast_best_distance = 110
" variable. PS: A Grey of 128 is neutral grey.. Any Grey value going above 128 is now getting more White added into it, and a Grey that is going below 128 is now getting more Black added. Maybe use that knowledge to give advantage of quick "contrast" decisions by your app/code. – VC.One Commented Jan 29 at 14:58