From 07e0b64822da53a7a958a605ab8812eb95d17e63 Mon Sep 17 00:00:00 2001
From: Karen Holland <karen@brickfieldlabs.ie>
Date: Mon, 17 Jul 2023 16:43:20 +0000
Subject: [PATCH] MDL-73974 tool_brickfield: Processing alpha fontsizes

---
 .../brickfield_accessibility_color_test.php   |  47 +++++++-
 .../common/checks/css_text_has_contrast.php   |   3 +-
 .../checks/css_text_has_contrast_test.php     | 112 ++++++++++++++++++
 3 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php b/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php
index fb12c17f962..a392d1106e4 100644
--- a/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php
+++ b/admin/tool/brickfield/classes/local/htmlchecker/common/brickfield_accessibility_color_test.php
@@ -170,6 +170,19 @@ class brickfield_accessibility_color_test extends brickfield_accessibility_test
         'yellowgreen' => '9acd32'
     ];
 
+    /** @var string[] Define estimated relative font-size codes to pt values. */
+    public $fontsizenames = [
+        'xx-small' => 9,
+        'x-small' => 10,
+        'small' => 11,
+        'smaller' => 11,
+        'medium' => 12,
+        'large' => 14,
+        'larger' => 14,
+        'x-large' => 18,
+        'xx-large' => 24,
+    ];
+
     /**
      * Helper method that finds the luminosity between the provided
      * foreground and background parameters.
@@ -227,7 +240,8 @@ class brickfield_accessibility_color_test extends brickfield_accessibility_test
             $l2 = (.2126 * $r4 + 0.7152 * $g4 + 0.0722 * $b4);
         }
 
-        $luminosity = round(($l1 + 0.05) / ($l2 + 0.05), 2);
+        // Increase round to 4 to avoid a 4.49 contrast being round up to a false pass of 4.5.
+        $luminosity = round(($l1 + 0.05) / ($l2 + 0.05), 4);
         return $luminosity;
     }
 
@@ -362,4 +376,35 @@ class brickfield_accessibility_color_test extends brickfield_accessibility_test
             : $backrgb['b'] - $forergb['b'];
         return ['red' => $reddiff, 'green' => $greendiff, 'blue' => $bluediff];
     }
+
+    /**
+     * Helper method that finds the estimated font-size for the provided
+     * string font-size parameter.
+     * @param string $fontsize The css font-size, in various formats
+     * @return int The estimated font-size
+     */
+    public function get_fontsize(string $fontsize): int {
+        $newfontsize = 12; // Default value, in pt, equivalent to 16px.
+
+        // Search for rem, em, and px initially, typical font-size values.
+        $pos1 = stripos($fontsize, 'rem');
+        $pos2 = stripos($fontsize, 'em');
+        $pos3 = stripos($fontsize, 'px');
+        if ($pos1 !== false) {
+            $rem = substr($fontsize, 0, -3);
+            $newfontsize = $newfontsize * $rem;
+        } else if ($pos2 !== false) {
+            $em = substr($fontsize, 0, -2);
+            $newfontsize = $newfontsize * $em;
+        } else if ($pos3 !== false) {
+            $px = substr($fontsize, 0, -2);
+            $newfontsize = 0.75 * $px;
+        } else if (in_array($fontsize, array_keys($this->fontsizenames))) {
+            $newfontsize = $this->fontsizenames[$fontsize];
+        } else {
+            preg_match_all('!\d+!', $fontsize, $matches);
+            $newfontsize = $matches[0][0] ?? $newfontsize;
+        }
+        return (int) $newfontsize;
+    }
 }
diff --git a/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php b/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php
index 1ae273c3180..76be8c8ca4f 100644
--- a/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php
+++ b/admin/tool/brickfield/classes/local/htmlchecker/common/checks/css_text_has_contrast.php
@@ -92,8 +92,7 @@ class css_text_has_contrast extends brickfield_accessibility_color_test {
                     $italic = false;
 
                     if (isset($style['font-size'])) {
-                        preg_match_all('!\d+!', $style['font-size'], $matches);
-                        $fontsize = $matches[0][0];
+                        $fontsize = $this->get_fontsize($style['font-size']);
                     }
 
                     if (isset($style['font-weight'])) {
diff --git a/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php b/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php
index ddc6763a054..f19f09bb147 100644
--- a/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php
+++ b/admin/tool/brickfield/tests/local/htmlchecker/common/checks/css_text_has_contrast_test.php
@@ -178,6 +178,54 @@ EOD;
     </html>
 EOD;
 
+    /** @var string HTML with px18 fail colour values. */
+    private $px18 = <<<EOD
+    <body><p style="color:#EF0000; background-color:white; font-size: 18px">
+    This is not contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with px19bold pass colour values. */
+    private $px19bold = <<<EOD
+    <body><p style="color:#EF0000; background-color:white; font-size: 19px; font-weight: bold;">
+    This is contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with px18 pass colour values. */
+    private $px18pass = <<<EOD
+    <body><p style="color:#E60000; background-color:white; font-size: 18px">
+    This is contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with medium size colour values. */
+    private $mediumfail = <<<EOD
+    <body><p style="color:#EF0000; background-color:white; font-size: medium">
+    This is not contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with px18 colour values. */
+    private $mediumpass = <<<EOD
+    <body><p style="color:#E60000; background-color:white; font-size: medium">
+    This is contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with larger fail colour values. */
+    private $largerfail = <<<EOD
+    <body><p style="color:#FF6161; background-color:white; font-size: larger">
+    This is not contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with px18 colour values. */
+    private $largerpass = <<<EOD
+    <body><p style="color:#FF5C5C; background-color:white; font-size: larger;">
+    This is contrasty enough.</p></body>
+EOD;
+
+    /** @var string HTML with px18 colour values. */
+    private $largerboldpass = <<<EOD
+    <body><p style="color:#FF5C5C; background-color:white; font-size: larger; font-weight: bold;">
+    This is contrasty enough.</p></body>
+EOD;
+
     /**
      * Test for the area assign intro
      */
@@ -235,4 +283,68 @@ EOD;
         $results = $this->get_checker_results($this->emptyvalue);
         $this->assertEmpty($results);
     }
+
+    /**
+     * Test for text px18 with insufficient contrast of 4.49.
+     */
+    public function test_check_for_px18_fail() {
+        $results = $this->get_checker_results($this->px18);
+        $this->assertTrue($results[0]->element->tagName == 'p');
+    }
+
+    /**
+     * Test for text px19 bold with sufficient contrast of 4.49.
+     */
+    public function test_check_for_px19bold_pass() {
+        $results = $this->get_checker_results($this->px19bold);
+        $this->assertEmpty($results);
+    }
+
+    /**
+     * Test for text px18 with sufficient contrast of 4.81.
+     */
+    public function test_check_for_px18_pass() {
+        $results = $this->get_checker_results($this->px18pass);
+        $this->assertEmpty($results);
+    }
+
+    /**
+     * Test for medium (12pt) text with insufficient contrast of 4.49.
+     */
+    public function test_check_for_medium_fail() {
+        $results = $this->get_checker_results($this->mediumfail);
+        $this->assertTrue($results[0]->element->tagName == 'p');
+    }
+
+    /**
+     * Test for medium (12pt) text with sufficient contrast of 4.81.
+     */
+    public function test_check_for_medium_pass() {
+        $results = $this->get_checker_results($this->mediumpass);
+        $this->assertEmpty($results);
+    }
+
+    /**
+     * Test for larger (14pt) text with insufficient contrast of 2.94.
+     */
+    public function test_check_for_larger_fail() {
+        $results = $this->get_checker_results($this->largerfail);
+        $this->assertTrue($results[0]->element->tagName == 'p');
+    }
+
+    /**
+     * Test for larger (14pt) text with insufficient contrast of 3.02.
+     */
+    public function test_check_for_larger_pass() {
+        $results = $this->get_checker_results($this->largerpass);
+        $this->assertTrue($results[0]->element->tagName == 'p');
+    }
+
+    /**
+     * Test for larger (14pt) bold text with sufficient contrast of 3.02.
+     */
+    public function test_check_for_largerbold_pass() {
+        $results = $this->get_checker_results($this->largerboldpass);
+        $this->assertEmpty($results);
+    }
 }