Repository: MyreMylar/pygame_gui
Pull request #418 (2023-03-11 12:49:58)

Wrap freetype.Font to make replacement easier
Replacing the `freetype.Font` submodule is tricky as it contains several subtle features the Pygame GUI makes use of (like adjustment of underlining for example).

This PR should make the process easier by wrapping up the parts of `freetype.Font` that the project uses so it is clearer what needs replacing and emulating for a swap to `pygame.Font`.

 


Patch:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ee1d6b6..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-language: python
-os:
-  - linux
-
-addons:
-  apt:
-    packages:
-      - xclip
-      - xsel
-      - xvfb
-
-jobs:
-  include:
-    - os: osx
-      language: generic
-      env: PYTHON=3.8.0 PYGAME_VERSION=2.0.1
-      before_install:
-        - brew update
-        - brew install openssl readline
-        - brew outdated pyenv || brew upgrade pyenv
-        - brew install pyenv-virtualenv
-        - pyenv install $PYTHON
-        - export PYENV_VERSION=$PYTHON
-        - export PATH="/Users/travis/.pyenv/shims:${PATH}"
-        - pyenv virtualenv py_venv
-        - source /Users/travis/.pyenv/versions/py_venv/bin/activate
-        - python --version
-    - os: osx
-      language: generic
-      env: PYTHON=3.7.5 PYGAME_VERSION=2.1.0
-      before_install:
-        - brew update
-        - brew install openssl readline
-        - brew outdated pyenv || brew upgrade pyenv
-        - brew install pyenv-virtualenv
-        - pyenv install $PYTHON
-        - export PYENV_VERSION=$PYTHON
-        - export PATH="/Users/travis/.pyenv/shims:${PATH}"
-        - pyenv virtualenv py_venv
-        - source /Users/travis/.pyenv/versions/py_venv/bin/activate
-        - python --version
-    - os: windows
-      language: shell
-      before_install:
-        - choco install python3 --params "/InstallDir:C:\\Python"
-        - export PATH="/c/Python:/c/Python/Scripts:$PATH"
-        - python --version
-      env: PYGAME_VERSION=2.1.0
-    - os: windows
-      language: shell
-      before_install:
-        - choco install python3 --params "/InstallDir:C:\\Python"
-        - export PATH="/c/Python:/c/Python/Scripts:$PATH"
-        - python --version
-      env: PYGAME_VERSION=2.0.0
-
-    - os: linux
-      python: 3.7
-      env: PYGAME_VERSION=2.0.1
-    - os: linux
-      python: 3.7
-      env: PYGAME_VERSION=2.1.0
-    - os: linux
-      python: 3.8
-      env: PYGAME_VERSION=2.0.1
-    - os: linux
-      python: 3.8
-      env: PYGAME_VERSION=2.1.0
-    - os: linux
-      python: 3.9
-      env: PYGAME_VERSION=2.0.1
-    - os: linux
-      python: 3.10
-      env: PYGAME_VERSION=2.1.2
-
-install:
-  - python -m pip install -U pip
-  - python -m pip install pytest
-  - python -m pip install pytest-cov
-  - python -m pip install pytest-benchmark
-  - python -m pip install codecov
-  - python -m pip install python-i18n
-  - python -m pip install -q pygame-ce==$PYGAME_VERSION
-  - python setup.py -q install
-
-env:
-  global:
-    - SDL_VIDEODRIVER=dummy
-    - SDL_AUDIODRIVER=disk
-
-
-script:
-  - if [[ "$TRAVIS_OS_NAME" == "linux" ]];
-    then xvfb-run pytest;
-    else python -m pytest --cov-report=xml --cov=pygame_gui tests/;
-    fi
-
-after_success:
-    - codecov
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aaec55c..7269500 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,7 +1,7 @@
 Contributing to Pygame GUI
 --------------------------
 
-Basic guidlines for contributing
+Basic guidelines for contributing
 ================================
 
 1. **Pygame GUI uses PEP8 as a style guide**, mainly because it is enforced by PyCharm. Contributions should be in the same general style.
@@ -38,7 +38,7 @@ All should be pip installable.
 Venv:
 + If developing a [virtual environment](https://docs.python.org/3/library/venv.html) (recommended), use this command while in the environment to download all relevant dependencies
 + ```python -m pip install pygame python-i18n importlib_resources typing_extensions```
-+ Or here's a one liner to set up the virtual environment all at once
++ Or here's a one-liner to set up the virtual environment all at once
 + ```python -m pip install pygame python-i18n importlib_resources typing_extensions sphinx sphinx_rtd_theme pytest pytest-cov pytest-benchmark ```
  
  Helpful checklist to go through before submitting a big pull request
diff --git a/README.md b/README.md
index ff6e36c..7c759e2 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ A GUI system for pygame CE.
 ## Requirements
 
  - Python 3.7+
- - Pygame Community Edition 2.1.3+
+ - Pygame Community Edition 2.1.4+
  - python-i18n (does localization to different languages)
 
 ## How to install
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 1080c30..082a923 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -21,10 +21,10 @@ sys.path.insert(0, os.path.abspath('../../'))
 sys.path.insert(0, os.path.abspath('../../pygame_gui/'))
 sys.path.insert(0, os.path.abspath('../../pygame_gui/windows/'))
 sys.path.insert(0, os.path.abspath('../../pygame_gui/elements/'))
-sys.path.insert(0, os.path.abspath('../../pygame_gui/elements/text/'))
 sys.path.insert(0, os.path.abspath('../../pygame_gui/core/'))
 sys.path.insert(0, os.path.abspath('../../pygame_gui/core/drawable_shapes/'))
 sys.path.insert(0, os.path.abspath('../../pygame_gui/core/interfaces/'))
+sys.path.insert(0, os.path.abspath('../../pygame_gui/core/text/'))
 
 
 # -- Project information -----------------------------------------------------
@@ -72,7 +72,7 @@ master_doc = 'index'
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
@@ -102,90 +102,12 @@ html_theme_options = {'logo_only': True}
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = ['_static']
 
-# Custom sidebar templates, must be a dictionary that maps document names
-# to template names.
-#
-# The default sidebars (for documents that don't match any pattern) are
-# defined by theme itself.  Builtin themes are using these templates by
-# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
-# 'searchbox.html']``.
-#
-# html_sidebars = {}
-
 
 # -- Options for HTMLHelp output ---------------------------------------------
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'pygame_gui_doc'
-
 smartquotes = False
-# -- Options for LaTeX output ------------------------------------------------
-
-latex_elements = {
-    # The paper size ('letterpaper' or 'a4paper').
-    #
-    # 'papersize': 'letterpaper',
-
-    # The font size ('10pt', '11pt' or '12pt').
-    #
-    # 'pointsize': '10pt',
-
-    # Additional stuff for the LaTeX preamble.
-    #
-    # 'preamble': '',
-
-    # Latex figure (float) alignment
-    #
-    # 'figure_align': 'htbp',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-# latex_documents = []
-
-
-# -- Options for manual page output ------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    (master_doc, 'an_example_pypi_project', 'an_example_pypi_project Documentation',
-     [author], 1)
-]
-
-
-# -- Options for Texinfo output ----------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-    (master_doc, 'an_example_pypi_project', 'an_example_pypi_project Documentation',
-     author, 'an_example_pypi_project', 'One line description of project.',
-     'Miscellaneous'),
-]
-
-
-# -- Options for Epub output -------------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = project
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#
-# epub_identifier = ''
-
-# A unique identification for the text.
-#
-# epub_uid = ''
-
-# A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
-
-
-# -- Extension configuration -------------------------------------------------
 
 # -- Options for intersphinx extension ---------------------------------------
 
diff --git a/pygame_gui/core/gui_font_freetype.py b/pygame_gui/core/gui_font_freetype.py
new file mode 100644
index 0000000..120d465
--- /dev/null
+++ b/pygame_gui/core/gui_font_freetype.py
@@ -0,0 +1,101 @@
+import pygame
+
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
+from pygame.freetype import Font
+from typing import Union, IO, Optional, Dict, Tuple
+from os import PathLike
+from pygame import Color, Surface, Rect
+
+AnyPath = Union[str, bytes, PathLike]
+FileArg = Union[AnyPath, IO]
+
+
+class GUIFontFreetype(IGUIFontInterface):
+
+    def __init__(self, file: Optional[FileArg], size: Union[int, float],
+                 force_style: bool = False, style: Optional[Dict[str, bool]] = None):
+        self.__internal_font: Font = Font(file, size, resolution=72)
+        self.__internal_font.pad = True
+        self.__internal_font.origin = True
+
+        self.__underline = False
+        self.__underline_adjustment = 0.0
+
+        self.point_size = size
+
+        if force_style and style is not None:
+            self.__internal_font.strong = style['bold']
+            self.__internal_font.oblique = style['italic']
+
+    @property
+    def underline(self) -> bool:
+        return self.__internal_font.underline
+
+    @underline.setter
+    def underline(self, value: bool):
+        self.__internal_font.underline = value
+
+    @property
+    def underline_adjustment(self) -> float:
+        return self.__internal_font.underline_adjustment
+
+    @underline_adjustment.setter
+    def underline_adjustment(self, value: float):
+        self.__internal_font.underline_adjustment = value
+
+    def get_point_size(self):
+        return self.point_size
+
+    def get_font(self) -> Font:
+        return self.__internal_font
+
+    def get_text_height(self, text: str) -> int:
+        pass
+
+    def get_text_width(self, text: str) -> int:
+        pass
+
+    def get_rect(self, text: str) -> Rect:
+        return self.__internal_font.get_rect(text)
+
+    def get_metrics(self, text: str):
+        return self.__internal_font.get_metrics(text)
+
+    def render_premul(self, text: str, text_color: Color) -> Surface:
+        text_surface, text_rect = self.__internal_font.render(text, text_color)
+        text_surface = text_surface.convert_alpha()
+        if text_surface.get_width() > 0 and text_surface.get_height() > 0:
+            text_surface = text_surface.premul_alpha()
+        return text_surface
+
+    def render_premul_to(self, text: str, text_colour: Color,
+                         surf_size: Tuple[int, int], surf_position: Tuple[int, int]) -> Surface:
+        text_surface = pygame.Surface(surf_size, depth=32, flags=pygame.SRCALPHA)
+        self.__internal_font.render_to(text_surface, surf_position, text, fgcolor=text_colour)
+        if text_surface.get_width() > 0 and text_surface.get_height() > 0:
+            text_surface = text_surface.premul_alpha()
+        return text_surface
+
+    def get_padding_height(self):
+        # 'font padding' this determines the amount of padding that
+        # font.pad adds to the top of text excluding
+        # any padding added to make glyphs even - this is useful
+        # for 'base-line centering' when we want to center text
+        # that doesn't drop below the base line (no y's, g's, p's etc)
+        # but also don't want it to flicker on and off. Base-line
+        # centering is the default for chunks on a single style row.
+        padding_state = self.__internal_font.pad
+
+        self.__internal_font.pad = False
+        no_pad_origin = self.__internal_font.get_rect('A').y
+
+        self.__internal_font.pad = True
+        pad_origin = self.__internal_font.get_rect('A').y
+
+        self.__internal_font.pad = padding_state
+        return pad_origin - no_pad_origin
+
+    def remove_font_pad_and_origin(self):
+        self.__internal_font.pad = False
+        self.__internal_font.origin = False
+
diff --git a/pygame_gui/core/interfaces/__init__.py b/pygame_gui/core/interfaces/__init__.py
index c82ea7d..6bad351 100644
--- a/pygame_gui/core/interfaces/__init__.py
+++ b/pygame_gui/core/interfaces/__init__.py
@@ -9,6 +9,7 @@ from pygame_gui.core.interfaces.window_stack_interface import IUIWindowStackInte
 from pygame_gui.core.interfaces.tool_tip_interface import IUITooltipInterface
 from pygame_gui.core.interfaces.manager_interface import IUIManagerInterface
 from pygame_gui.core.interfaces.text_owner_interface import IUITextOwnerInterface
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
 
 __all__ = ['IColourGradientInterface',
@@ -21,4 +22,5 @@ __all__ = ['IColourGradientInterface',
            'IUIWindowStackInterface',
            'IUITooltipInterface',
            'IUIManagerInterface',
-           'IUITextOwnerInterface']
+           'IUITextOwnerInterface',
+           'IGUIFontInterface']
diff --git a/pygame_gui/core/interfaces/appearance_theme_interface.py b/pygame_gui/core/interfaces/appearance_theme_interface.py
index 47112d3..38f9938 100644
--- a/pygame_gui/core/interfaces/appearance_theme_interface.py
+++ b/pygame_gui/core/interfaces/appearance_theme_interface.py
@@ -9,6 +9,7 @@ import pygame
 
 from pygame_gui.core.interfaces.font_dictionary_interface import IUIFontDictionaryInterface
 from pygame_gui.core.interfaces.colour_gradient_interface import IColourGradientInterface
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
 
 class IUIAppearanceThemeInterface(metaclass=ABCMeta):
@@ -93,14 +94,14 @@ class IUIAppearanceThemeInterface(metaclass=ABCMeta):
         """
 
     @abstractmethod
-    def get_font(self, combined_element_ids: List[str]) -> pygame.freetype.Font:
+    def get_font(self, combined_element_ids: List[str]) -> IGUIFontInterface:
         """
         Uses some data about a UIElement to get a font object.
 
         :param combined_element_ids: A list of IDs representing an element's location in a
                                      interleaved hierarchy of elements.
 
-        :return pygame.freetype.Font: A pygame font object.
+        :return IGUIFontInterface: An interface to a pygame font object wrapper.
         """
 
     @abstractmethod
diff --git a/pygame_gui/core/interfaces/font_dictionary_interface.py b/pygame_gui/core/interfaces/font_dictionary_interface.py
index 03901e9..1bf85de 100644
--- a/pygame_gui/core/interfaces/font_dictionary_interface.py
+++ b/pygame_gui/core/interfaces/font_dictionary_interface.py
@@ -1,6 +1,6 @@
 from abc import ABCMeta, abstractmethod
 
-import pygame.freetype
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
 
 class IUIFontDictionaryInterface(metaclass=ABCMeta):
@@ -13,7 +13,7 @@ class IUIFontDictionaryInterface(metaclass=ABCMeta):
 
     @abstractmethod
     def find_font(self, font_size: int, font_name: str,
-                  bold: bool = False, italic: bool = False) -> pygame.freetype.Font:
+                  bold: bool = False, italic: bool = False) -> IGUIFontInterface:
         """
         Find a loaded font from the font dictionary. Will load a font if it does not already exist
         and we have paths to the needed files, however it will issue a warning after doing so
@@ -28,12 +28,12 @@ class IUIFontDictionaryInterface(metaclass=ABCMeta):
         :param bold: Whether the font is bold or not.
         :param italic: Whether the font is italic or not.
 
-        :return pygame.freetype.Font: Returns either the font we asked for, or the default font.
+        :return IGUIFontInterface: Returns either the font we asked for, or the default font.
 
         """
 
     @abstractmethod
-    def get_default_font(self) -> pygame.freetype.Font:
+    def get_default_font(self) -> IGUIFontInterface:
         """
         Grab the default font.
 
@@ -105,7 +105,7 @@ class IUIFontDictionaryInterface(metaclass=ABCMeta):
 
         :param html_size: Size in HTML style.
 
-        :return int: A 'point' font size we can use with pygame.freetype.font
+        :return int: A 'point' font size.
 
         """
 
diff --git a/pygame_gui/core/interfaces/gui_font_interface.py b/pygame_gui/core/interfaces/gui_font_interface.py
new file mode 100644
index 0000000..fa4f004
--- /dev/null
+++ b/pygame_gui/core/interfaces/gui_font_interface.py
@@ -0,0 +1,111 @@
+from abc import ABCMeta, abstractmethod
+from typing import Tuple
+from pygame import Surface, Color, Rect
+
+
+class IGUIFontInterface(metaclass=ABCMeta):
+    """
+    A font interface so we can easily switch between pygame.freetype.Font and pygame.Font.
+    """
+
+    @abstractmethod
+    def render_premul(self, text: str, text_color: Color) -> Surface:
+        """
+        Draws text to a surface ready for premultiplied alpha-blending
+        """
+
+    def render_premul_to(self, text: str, text_colour: Color, surf_size: Tuple[int, int], surf_position: Tuple[int, int]):
+        """
+
+
+        :param text:
+        :param text_colour:
+        :param surf_size:
+        :param surf_position:
+        :return:
+        """
+
+    @abstractmethod
+    def get_text_width(self, text: str) -> int:
+        """
+        Returns the width of text drawn by this font in pixels
+        :param text:
+        :return: The pixel width of the text.
+        """
+
+    @abstractmethod
+    def get_text_height(self, text: str) -> int:
+        """
+        Returns the height of text drawn by this font in pixels
+        :param text:
+        :return: The pixel height of the text.
+        """
+
+    @abstractmethod
+    def get_rect(self, text: str) -> Rect:
+        """
+        Not sure if we want this.
+        :return:
+        """
+
+    @abstractmethod
+    def get_metrics(self, text: str):
+        """
+
+        :param text:
+        :return:
+        """
+
+    @abstractmethod
+    def get_point_size(self):
+        """
+
+        """
+
+    @abstractmethod
+    def get_padding_height(self):
+        """
+
+        :return:
+        """
+
+    @property
+    @abstractmethod
+    def underline(self) -> bool:
+        """
+
+        :return:
+        """
+
+    @underline.setter
+    @abstractmethod
+    def underline(self, value: bool):
+        """
+
+        :param value:
+        :return:
+        """
+
+    @property
+    @abstractmethod
+    def underline_adjustment(self) -> float:
+        """
+
+        :return:
+        """
+
+    @underline_adjustment.setter
+    @abstractmethod
+    def underline_adjustment(self, value: float):
+        """
+
+        :param value:
+        :return:
+        """
+
+    @abstractmethod
+    def remove_font_pad_and_origin(self):
+        """
+
+        :return:
+        """
diff --git a/pygame_gui/core/text/html_parser.py b/pygame_gui/core/text/html_parser.py
index d6b15b7..04f1a8a 100644
--- a/pygame_gui/core/text/html_parser.py
+++ b/pygame_gui/core/text/html_parser.py
@@ -6,7 +6,6 @@ from pathlib import Path
 from _markupbase import ParserBase
 
 import pygame
-import pygame.freetype
 
 from pygame_gui.core.interfaces import IUIAppearanceThemeInterface
 
diff --git a/pygame_gui/core/text/hyperlink_text_chunk.py b/pygame_gui/core/text/hyperlink_text_chunk.py
index 908b516..a04531b 100644
--- a/pygame_gui/core/text/hyperlink_text_chunk.py
+++ b/pygame_gui/core/text/hyperlink_text_chunk.py
@@ -1,7 +1,6 @@
 from typing import Optional, Tuple
 
-import pygame.freetype
-
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 from pygame.color import Color
 
 
@@ -16,7 +15,7 @@ class HyperlinkTextChunk(TextLineChunkFTFont):
     def __init__(self,
                  href: str,
                  text: str,
-                 font: pygame.freetype.Font,
+                 font: IGUIFontInterface,
                  underlined: bool,
                  colour: Color,
                  bg_colour: Color,
diff --git a/pygame_gui/core/text/text_box_layout_row.py b/pygame_gui/core/text/text_box_layout_row.py
index 62ca6f0..3bc8af0 100644
--- a/pygame_gui/core/text/text_box_layout_row.py
+++ b/pygame_gui/core/text/text_box_layout_row.py
@@ -1,7 +1,7 @@
 from typing import Optional, Tuple, List, Union
 import math
 import pygame
-import pygame.freetype
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
 from pygame_gui.core.text.text_layout_rect import TextLayoutRect
 from pygame_gui.core.text.text_line_chunk import TextLineChunkFTFont
@@ -432,13 +432,10 @@ class TextBoxLayoutRow(pygame.Rect):
         return cursor_index, cursor_draw_width
 
     def get_last_text_chunk(self):
-        last_item = None
         for item in reversed(self.items):
             if isinstance(item, TextLineChunkFTFont):
-                last_item = item
-                break
-
-        return last_item
+                return item
+        return None
 
     def set_cursor_position(self, cursor_pos):
         """
@@ -511,8 +508,8 @@ class TextBoxLayoutRow(pygame.Rect):
                 if chunk == chunk_to_insert_after:
                     chunk_insert_index = chunk_index + 1
                     break
-            current_font: pygame.freetype.Font = chunk_to_insert_after.font
-            current_font_size = current_font.size
+            current_font: IGUIFontInterface = chunk_to_insert_after.font
+            current_font_size = current_font.get_point_size()
             dimensions = (current_font.get_rect(' ').width,
                           int(round(current_font_size *
                                     self.line_spacing)))
@@ -527,12 +524,6 @@ class TextBoxLayoutRow(pygame.Rect):
                     return True
         return False
 
-    def get_last_text_chunk(self):
-        for item in reversed(self.items):
-            if isinstance(item, TextLineChunkFTFont):
-                return item
-        return None
-
     def get_last_text_or_line_break_chunk(self):
         for item in reversed(self.items):
             if isinstance(item, TextLineChunkFTFont) or isinstance(item, LineBreakLayoutRect):
diff --git a/pygame_gui/core/text/text_line_chunk.py b/pygame_gui/core/text/text_line_chunk.py
index d5525b9..108ba44 100644
--- a/pygame_gui/core/text/text_line_chunk.py
+++ b/pygame_gui/core/text/text_line_chunk.py
@@ -13,10 +13,10 @@ chunk onto it's final destination.
 """
 from typing import Optional, Union, Tuple
 
-import pygame.freetype
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
-from pygame.color import Color
-from pygame.surface import Surface
+from pygame import Color, Surface, Rect, BLEND_PREMULTIPLIED, BLEND_RGBA_MULT, SRCALPHA
+from pygame.transform import rotozoom, rotate
 
 from pygame_gui.core.text.text_layout_rect import TextLayoutRect
 from pygame_gui.core.colour_gradient import ColourGradient
@@ -24,17 +24,16 @@ from pygame_gui.core.colour_gradient import ColourGradient
 
 class TextLineChunkFTFont(TextLayoutRect):
     """
-    A Text line chunk (text on the same horizontal line in the same style) using pygame's freetype
-    module.
+    A Text line chunk (text on the same horizontal line in the same style)
     """
 
     def __init__(self, text: str,
-                 font: pygame.freetype.Font,
+                 font: IGUIFontInterface,
                  underlined: bool,
                  colour: Union[Color, ColourGradient],
                  using_default_text_colour: bool,
                  bg_colour: Union[Color, ColourGradient],
-                 text_shadow_data: Optional[Tuple[int, int, int, pygame.Color, bool]] = None,
+                 text_shadow_data: Optional[Tuple[int, int, int, Color, bool]] = None,
                  max_dimensions: Optional[Tuple[int, int]] = None,
                  effect_id: Optional[str] = None):
         self.text_shadow_data = text_shadow_data
@@ -45,10 +44,10 @@ class TextLineChunkFTFont(TextLayoutRect):
 
         # style that needs to be matched to merge or insert
         # chunks (are those basically the same operation?)
-        self.font = font
+        self.font: IGUIFontInterface = font
         self.underlined = underlined
         self.colour = colour
-        self.shadow_colour = pygame.Color('#000000')
+        self.shadow_colour = Color('#000000')
         self.using_default_text_colour = using_default_text_colour
         self.using_default_text_shadow_colour = False
         self.bg_colour = bg_colour
@@ -66,7 +65,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         self.split_points = [pos+1 for pos, char in enumerate(self.text) if char == ' ']
         self.letter_count = len(self.text)
 
-        self.target_surface: Optional[pygame.Surface] = None
+        self.target_surface: Optional[Surface] = None
         self.target_surface_area = None
         self.row_chunk_origin = 0
         self.row_chunk_height = 0
@@ -75,14 +74,14 @@ class TextLineChunkFTFont(TextLayoutRect):
 
         self.is_selected = False
         self.is_active = False
-        self.selection_colour = pygame.Color(128, 128, 128, 255)
+        self.selection_colour = Color(128, 128, 128, 255)
 
         self.should_centre_from_baseline = False
 
         self.effect_id = effect_id
 
         # this is the rect before we add it to a row and start moving the origin
-        self.pre_row_rect = pygame.Rect(self.topleft, self.size)
+        self.pre_row_rect = Rect(self.topleft, self.size)
         self.origin_row_y_adjust = 0
 
         # effects stuff that should be reset
@@ -93,7 +92,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         self.effects_scale = 1.0
         self.effects_rotation = 0
         self.effects_offset_pos = (0, 0)
-        self.transform_effect_rect = pygame.Rect(self.topleft, self.size)
+        self.transform_effect_rect = Rect(self.topleft, self.size)
 
     def __repr__(self):
         return "< '" + self.text + "' " + super().__repr__() + " >"
@@ -124,16 +123,8 @@ class TextLineChunkFTFont(TextLayoutRect):
         # that doesn't drop below the base line (no y's, g's, p's etc)
         # but also don't want it to flicker on and off. Base-line
         # centering is the default for chunks on a single style row.
-        padding_state = self.font.pad
 
-        self.font.pad = False
-        no_pad_origin = self.font.get_rect('A').y
-
-        self.font.pad = True
-        pad_origin = self.font.get_rect('A').y
-
-        self.font.pad = padding_state
-        return pad_origin - no_pad_origin
+        return self.font.get_padding_height()
 
     def style_match(self, other_text_chunk: 'TextLineChunkFTFont'):
         """
@@ -154,7 +145,7 @@ class TextLineChunkFTFont(TextLayoutRect):
 
     def finalise(self,
                  target_surface: Surface,
-                 target_area: pygame.Rect,
+                 target_area: Rect,
                  row_chunk_origin: int,
                  row_chunk_height: int,
                  row_bg_height: int,
@@ -224,26 +215,30 @@ class TextLineChunkFTFont(TextLayoutRect):
         remaining_rhs_space = target_area.width - (final_pos[0] - target_area.left)
         rhs_overlap = max(0, ((self.width - lhs_overlap) - remaining_rhs_space))
         target_width = (self.width - lhs_overlap) - rhs_overlap + (2 * text_shadow_width)
-        final_target = pygame.Rect(lhs_overlap,
-                                   target_area.top,
-                                   # we only want to grab as much as we can show
-                                   min(target_width, target_area.width),
-                                   target_area.height)
+        final_target = Rect(lhs_overlap,
+                            target_area.top,
+                            # we only want to grab as much as we can show
+                            min(target_width, target_area.width),
+                            target_area.height)
         if target_width > 0:
             target_surface.blit(surface, final_pos, area=final_target,
-                                special_flags=pygame.BLEND_PREMULTIPLIED)
+                                special_flags=BLEND_PREMULTIPLIED)
         return target_surface
 
     def _draw_text_no_gradient(self, bg_col, chunk_draw_height, chunk_draw_width, chunk_x_origin,
                                final_str_text, row_bg_height, row_chunk_origin):
+
+        text_surface = self.font.render_premul_to(final_str_text, self.colour,
+                                                  surf_size=(chunk_draw_width, chunk_draw_height),
+                                                  surf_position=(chunk_x_origin, row_chunk_origin))
         # This is a hacky way to convert text to pre-multiplied alpha with a SDL2 alpha blit
-        text_surface = pygame.Surface((chunk_draw_width, chunk_draw_height),
-                                      flags=pygame.SRCALPHA, depth=32)
-        temp_text_surface = text_surface.copy()
-        self.font.render_to(temp_text_surface, (chunk_x_origin, row_chunk_origin),
-                            final_str_text, fgcolor=self.colour)
-        text_surface.blit(temp_text_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)
-        surface = Surface((chunk_draw_width, row_bg_height), flags=pygame.SRCALPHA, depth=32)
+        # text_surface = Surface((chunk_draw_width, chunk_draw_height),
+        #                        flags=SRCALPHA, depth=32)
+        # temp_text_surface = text_surface.copy()
+        # self.font.render_to(temp_text_surface, (chunk_x_origin, row_chunk_origin),
+        #                     final_str_text, fgcolor=self.colour)
+        # text_surface.blit(temp_text_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)
+        surface = Surface((chunk_draw_width, row_bg_height), flags=SRCALPHA, depth=32)
         surface.fill(bg_col)
         # center the text in the line
         text_rect = text_surface.get_rect()
@@ -257,22 +252,25 @@ class TextLineChunkFTFont(TextLayoutRect):
         # apply any shadow effects
         self._apply_shadow_effect(surface, text_rect, final_str_text,
                                   text_surface, (chunk_x_origin, row_chunk_origin))
-        surface.blit(text_surface, text_rect, special_flags=pygame.BLEND_PREMULTIPLIED)
+        surface.blit(text_surface, text_rect, special_flags=BLEND_PREMULTIPLIED)
         return surface
 
     def _draw_text_bg_gradient(self, bg_col, chunk_draw_height, chunk_draw_width, chunk_x_origin,
                                final_str_text, row_bg_height, row_chunk_origin):
         # draw the text first
+        text_surface = self.font.render_premul_to(final_str_text, self.colour,
+                                                  surf_size=(chunk_draw_width, chunk_draw_height),
+                                                  surf_position=(chunk_x_origin, row_chunk_origin))
         # This is a hacky way to convert text to pre-multiplied alpha with a SDL2 alpha blit
-        text_surface = pygame.Surface((chunk_draw_width, chunk_draw_height),
-                                      flags=pygame.SRCALPHA, depth=32)
-        temp_text_surface = text_surface.copy()
-        self.font.render_to(temp_text_surface, (chunk_x_origin, row_chunk_origin),
-                            final_str_text, fgcolor=self.colour)
-        text_surface.blit(temp_text_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)
+        # text_surface = Surface((chunk_draw_width, chunk_draw_height),
+        #                               flags=SRCALPHA, depth=32)
+        # temp_text_surface = text_surface.copy()
+        # self.font.render_to(temp_text_surface, (chunk_x_origin, row_chunk_origin),
+        #                     final_str_text, fgcolor=self.colour)
+        # text_surface.blit(temp_text_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)
         # then make the background
         surface = Surface((chunk_draw_width, row_bg_height),
-                          flags=pygame.SRCALPHA, depth=32)
+                          flags=SRCALPHA, depth=32)
         surface.fill(Color('#FFFFFFFF'))
         bg_col.apply_gradient_to_surface(surface)
         # center the text in the line
@@ -286,7 +284,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         # apply any shadow effects
         self._apply_shadow_effect(surface, text_rect, final_str_text,
                                   text_surface, (chunk_x_origin, row_chunk_origin))
-        surface.blit(text_surface, text_rect, special_flags=pygame.BLEND_PREMULTIPLIED)
+        surface.blit(text_surface, text_rect, special_flags=BLEND_PREMULTIPLIED)
         return surface
 
     def _draw_text_fg_gradient(self, bg_col, chunk_draw_height, chunk_draw_width, chunk_x_origin,
@@ -296,16 +294,19 @@ class TextLineChunkFTFont(TextLayoutRect):
         # this text onto a surface with a normal blit before it can enter the pre-multipled
         # blitting pipeline. This current setup may be a bit wrong but it works OK for gradients
         # on the normal text colour.
+        text_surface = self.font.render_premul_to(final_str_text, Color('#FFFFFFFF'),
+                                                  surf_size=(chunk_draw_width, chunk_draw_height),
+                                                  surf_position=(chunk_x_origin, row_chunk_origin))
         # This is a hacky way to convert text to pre-multiplied alpha with a SDL2 alpha blit
-        text_surface = pygame.Surface((chunk_draw_width, chunk_draw_height),
-                                      flags=pygame.SRCALPHA, depth=32)
-        temp_text_surface = text_surface.copy()
-        self.font.render_to(temp_text_surface, (chunk_x_origin, row_chunk_origin),
-                            final_str_text, fgcolor=Color('#FFFFFFFF'))
-        text_surface.blit(temp_text_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)
+        # text_surface = Surface((chunk_draw_width, chunk_draw_height),
+        #                        flags=SRCALPHA, depth=32)
+        # temp_text_surface = text_surface.copy()
+        # self.font.render_to(temp_text_surface, (chunk_x_origin, row_chunk_origin),
+        #                     final_str_text, fgcolor=Color('#FFFFFFFF'))
+        # text_surface.blit(temp_text_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)
         self.colour.apply_gradient_to_surface(text_surface)
         # then make the background
-        surface = Surface((chunk_draw_width, row_bg_height), flags=pygame.SRCALPHA, depth=32)
+        surface = Surface((chunk_draw_width, row_bg_height), flags=SRCALPHA, depth=32)
         if isinstance(bg_col, ColourGradient):
             surface.fill(Color('#FFFFFFFF'))
             bg_col.apply_gradient_to_surface(surface)
@@ -322,7 +323,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         # apply any shadow effects
         self._apply_shadow_effect(surface, text_rect, final_str_text,
                                   text_surface, (chunk_x_origin, row_chunk_origin))
-        surface.blit(text_surface, text_rect, special_flags=pygame.BLEND_PREMULTIPLIED)
+        surface.blit(text_surface, text_rect, special_flags=BLEND_PREMULTIPLIED)
         return surface
 
     def _apply_shadow_effect(self, surface, text_rect, text_str, text_surface, origin):
@@ -332,46 +333,50 @@ class TextLineChunkFTFont(TextLayoutRect):
             shadow_offset = (self.text_shadow_data[1], self.text_shadow_data[2])
             shadow_colour = self.shadow_colour
             # we have a shadow
+
+            shadow_surface = self.font.render_premul_to(text_str, shadow_colour,
+                                                        surf_size=text_surface.get_size(),
+                                                        surf_position=origin)
             # This is a hacky way to convert text to
             # pre-multiplied alpha with a SDL2 alpha blit
-            shadow_surface = text_surface.copy()
-            temp_shadow_surface = shadow_surface.copy()
-
-            self.font.render_to(temp_shadow_surface, origin,
-                                text_str,
-                                fgcolor=shadow_colour)
-            shadow_surface.blit(temp_shadow_surface, (0, 0),
-                                special_flags=pygame.BLEND_ALPHA_SDL2)
+            # shadow_surface = text_surface.copy()
+            # temp_shadow_surface = shadow_surface.copy()
+            #
+            # self.font.render_to(temp_shadow_surface, origin,
+            #                     text_str,
+            #                     fgcolor=shadow_colour)
+            # shadow_surface.blit(temp_shadow_surface, (0, 0),
+            #                     special_flags=pygame.BLEND_ALPHA_SDL2)
 
             for y_pos in range(-shadow_size, shadow_size + 1):
-                shadow_text_rect = pygame.Rect((text_rect.x + shadow_offset[0],
-                                                text_rect.y + shadow_offset[1]
-                                                + y_pos),
-                                               text_rect.size)
+                shadow_text_rect = Rect((text_rect.x + shadow_offset[0],
+                                         text_rect.y + shadow_offset[1]
+                                         + y_pos),
+                                        text_rect.size)
 
                 surface.blit(shadow_surface, shadow_text_rect,
-                             special_flags=pygame.BLEND_PREMULTIPLIED)
+                             special_flags=BLEND_PREMULTIPLIED)
             for x_pos in range(-shadow_size, shadow_size + 1):
-                shadow_text_rect = pygame.Rect((text_rect.x + shadow_offset[0]
-                                                + x_pos,
-                                                text_rect.y + shadow_offset[1]),
-                                               text_rect.size)
+                shadow_text_rect = Rect((text_rect.x + shadow_offset[0]
+                                         + x_pos,
+                                         text_rect.y + shadow_offset[1]),
+                                        text_rect.size)
                 surface.blit(shadow_surface, shadow_text_rect,
-                             special_flags=pygame.BLEND_PREMULTIPLIED)
+                             special_flags=BLEND_PREMULTIPLIED)
             for x_and_y in range(-shadow_size, shadow_size + 1):
-                shadow_text_rect = pygame.Rect(
+                shadow_text_rect = Rect(
                     (text_rect.x + shadow_offset[0] + x_and_y,
                      text_rect.y + shadow_offset[1] + x_and_y),
                     text_rect.size)
                 surface.blit(shadow_surface, shadow_text_rect,
-                             special_flags=pygame.BLEND_PREMULTIPLIED)
+                             special_flags=BLEND_PREMULTIPLIED)
             for x_and_y in range(-shadow_size, shadow_size + 1):
-                shadow_text_rect = pygame.Rect(
+                shadow_text_rect = Rect(
                     (text_rect.x + shadow_offset[0] - x_and_y,
                      text_rect.y + shadow_offset[1] + x_and_y),
                     text_rect.size)
                 surface.blit(shadow_surface, shadow_text_rect,
-                             special_flags=pygame.BLEND_PREMULTIPLIED)
+                             special_flags=BLEND_PREMULTIPLIED)
 
     def split(self,
               requested_x: int,
@@ -519,7 +524,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         right_side_chunk.effect_id = self.effect_id
         return right_side_chunk
 
-    def clear(self, optional_rect: Optional[pygame.Rect] = None):
+    def clear(self, optional_rect: Optional[Rect] = None):
         """
         Clear the finalised/rendered text in this chunk to an invisible/empty surface.
         """
@@ -528,7 +533,7 @@ class TextLineChunkFTFont(TextLayoutRect):
                 clear_rect = optional_rect
             else:
                 clear_rect = self
-            self.target_surface.fill(pygame.Color('#00000000'), clear_rect)
+            self.target_surface.fill(Color('#00000000'), clear_rect)
 
     def add_text(self, input_text: str):
         """
@@ -701,11 +706,10 @@ class TextLineChunkFTFont(TextLayoutRect):
             if self.pre_effect_target_surface is not None:
                 self.alpha = alpha
                 self.target_surface.blit(self.pre_effect_target_surface, self, self)
-                pre_mul_alpha_colour = pygame.Color(self.alpha, self.alpha,
-                                                    self.alpha, self.alpha)
+                pre_mul_alpha_colour = Color(self.alpha, self.alpha, self.alpha, self.alpha)
                 self.target_surface.fill(pre_mul_alpha_colour,
                                          rect=self,
-                                         special_flags=pygame.BLEND_RGBA_MULT)
+                                         special_flags=BLEND_RGBA_MULT)
 
     def set_offset_pos(self, offset_pos: Tuple[int, int]):
         """
@@ -716,14 +720,14 @@ class TextLineChunkFTFont(TextLayoutRect):
         if self.effects_offset_pos != (0, 0) and offset_pos == (0, 0):
             # returned to no offset so redraw chunk
             self.effects_offset_pos = offset_pos
-            self.transform_effect_rect = pygame.Rect(self.topleft, self.size)
+            self.transform_effect_rect = Rect(self.topleft, self.size)
             self.redraw()
         else:
             if self.pre_effect_target_surface is not None:
                 self.effects_offset_pos = offset_pos
-                self.transform_effect_rect = pygame.Rect(self.left + self.effects_offset_pos[0],
-                                                         self.top + self.effects_offset_pos[1],
-                                                         self.width, self.height)
+                self.transform_effect_rect = Rect(self.left + self.effects_offset_pos[0],
+                                                  self.top + self.effects_offset_pos[1],
+                                                  self.width, self.height)
 
                 self.target_surface.blit(self.pre_effect_target_surface,
                                          self.transform_effect_rect, self)
@@ -737,7 +741,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         if self.effects_rotation != 0 and rotation == 0:
             # returned to no rotation so redraw chunk
             self.effects_rotation = rotation
-            self.transform_effect_rect = pygame.Rect(self.topleft, self.size)
+            self.transform_effect_rect = Rect(self.topleft, self.size)
             self.redraw()
         else:
             if self.pre_effect_target_surface is not None:
@@ -745,11 +749,11 @@ class TextLineChunkFTFont(TextLayoutRect):
                 # other chunks
                 self.effects_rotation = rotation
 
-                temp_surf = pygame.surface.Surface(self.size, flags=pygame.SRCALPHA)
+                temp_surf = Surface(self.size, flags=SRCALPHA)
                 temp_surf.blit(self.pre_effect_target_surface, (0, 0), self)
-                rotated_surf = pygame.transform.rotate(temp_surf, rotation)
+                rotated_surf = rotate(temp_surf, rotation)
 
-                self.transform_effect_rect = pygame.Rect(self.topleft, rotated_surf.get_size())
+                self.transform_effect_rect = Rect(self.topleft, rotated_surf.get_size())
                 self.transform_effect_rect.center = self.center
 
                 self.target_surface.blit(rotated_surf, self.transform_effect_rect)
@@ -763,7 +767,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         if self.effects_scale != 1.0 and scale == 1.0:
             # returned to no rotation so redraw chunk
             self.effects_scale = scale
-            self.transform_effect_rect = pygame.Rect(self.topleft, self.size)
+            self.transform_effect_rect = Rect(self.topleft, self.size)
             self.redraw()
         else:
             if self.pre_effect_target_surface is not None:
@@ -771,11 +775,11 @@ class TextLineChunkFTFont(TextLayoutRect):
                 # other chunks
                 self.effects_scale = scale
 
-                temp_surf = pygame.surface.Surface(self.size, flags=pygame.SRCALPHA)
+                temp_surf = Surface(self.size, flags=SRCALPHA)
                 temp_surf.blit(self.pre_effect_target_surface, (0, 0), self)
-                scaled_surf = pygame.transform.rotozoom(temp_surf, 0, self.effects_scale)
+                scaled_surf = rotozoom(temp_surf, 0, self.effects_scale)
 
-                self.transform_effect_rect = pygame.Rect(self.topleft, scaled_surf.get_size())
+                self.transform_effect_rect = Rect(self.topleft, scaled_surf.get_size())
                 self.transform_effect_rect.center = self.center
 
                 self.target_surface.blit(scaled_surf, self.transform_effect_rect)
@@ -792,7 +796,7 @@ class TextLineChunkFTFont(TextLayoutRect):
         self.effects_scale = 1.0
         self.effects_rotation = 0
         self.effects_offset_pos = (0, 0)
-        self.transform_effect_rect = pygame.Rect(self.topleft, self.size)
+        self.transform_effect_rect = Rect(self.topleft, self.size)
 
     def grab_pre_effect_surface(self):
         """
diff --git a/pygame_gui/core/ui_appearance_theme.py b/pygame_gui/core/ui_appearance_theme.py
index 5efe4a4..d491472 100644
--- a/pygame_gui/core/ui_appearance_theme.py
+++ b/pygame_gui/core/ui_appearance_theme.py
@@ -7,8 +7,8 @@ from contextlib import contextmanager
 from typing import Union, List, Dict, Any, Optional
 
 import pygame
-import pygame.freetype
 
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 from pygame_gui.core.interfaces.font_dictionary_interface import IUIFontDictionaryInterface
 from pygame_gui.core.interfaces.colour_gradient_interface import IColourGradientInterface
 from pygame_gui.core.interfaces.appearance_theme_interface import IUIAppearanceThemeInterface
@@ -520,14 +520,14 @@ class UIAppearanceTheme(IUIAppearanceThemeInterface):
 
         return self.font_dict.default_font.info
 
-    def get_font(self, combined_element_ids: List[str]) -> pygame.freetype.Font:
+    def get_font(self, combined_element_ids: List[str]) -> IGUIFontInterface:
         """
         Uses some data about a UIElement to get a font object.
 
         :param combined_element_ids: A list of IDs representing an element's location in a
                                      interleaved hierarchy of elements.
 
-        :return pygame.freetype.Font: A pygame font object.
+        :return IGUIFontInterface: An interface to a pygame font object wrapper.
         """
         # set the default font as the final fall back
         font = self.font_dict.get_default_font()
diff --git a/pygame_gui/core/ui_font_dictionary.py b/pygame_gui/core/ui_font_dictionary.py
index a40cc55..1001937 100644
--- a/pygame_gui/core/ui_font_dictionary.py
+++ b/pygame_gui/core/ui_font_dictionary.py
@@ -5,6 +5,7 @@ from typing import Dict, Union, Tuple, List, Optional
 
 
 import pygame
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
 from pygame_gui.core.interfaces.font_dictionary_interface import IUIFontDictionaryInterface
 from pygame_gui.core.resource_loaders import IResourceLoader
@@ -168,7 +169,7 @@ class UIFontDictionary(IUIFontDictionaryInterface):
                              resource=self.default_font.bold_italic_file_name), False)]
 
     def find_font(self, font_size: int, font_name: str,
-                  bold: bool = False, italic: bool = False) -> pygame.freetype.Font:
+                  bold: bool = False, italic: bool = False) -> IGUIFontInterface:
         """
         Find a loaded font from the font dictionary. Will load a font if it does not already exist
         and we have paths to the needed files, however it will issue a warning after doing so
@@ -183,7 +184,7 @@ class UIFontDictionary(IUIFontDictionaryInterface):
         :param bold: Whether the font is bold or not.
         :param italic: Whether the font is italic or not.
 
-        :return pygame.freetype.Font: Returns either the font we asked for, or the default font.
+        :return IGUIFontInterface: Returns either the font we asked for, or the default font.
 
         """
         return self.find_font_resource(font_size, font_name, bold, italic).loaded_font
@@ -237,7 +238,7 @@ class UIFontDictionary(IUIFontDictionaryInterface):
         else:
             return self.loaded_fonts[self.default_font.idx]
 
-    def get_default_font(self) -> pygame.freetype.Font:
+    def get_default_font(self) -> IGUIFontInterface:
         """
         Grab the default font.
 
@@ -413,7 +414,7 @@ class UIFontDictionary(IUIFontDictionaryInterface):
 
         :param html_size: Size in HTML style.
 
-        :return int: A 'point' font size we can use with pygame.freetype
+        :return int: A 'point' font size.
 
         """
         if html_size in UIFontDictionary._html_font_sizes:
diff --git a/pygame_gui/core/utility.py b/pygame_gui/core/utility.py
index 8fa9029..35899ec 100644
--- a/pygame_gui/core/utility.py
+++ b/pygame_gui/core/utility.py
@@ -20,9 +20,9 @@ from queue import Queue
 import i18n
 
 import pygame
-import pygame.freetype
 
-from pygame_gui.core.interfaces import IUIManagerInterface
+from pygame_gui.core.interfaces import IUIManagerInterface, IGUIFontInterface
+from pygame_gui.core.gui_font_freetype import GUIFontFreetype
 
 
 __default_manager = None  # type: Optional[IUIManagerInterface]
@@ -274,23 +274,14 @@ def premul_alpha_surface(surface: pygame.surface.Surface) -> pygame.surface.Surf
     return surface
 
 
-def render_white_text_alpha_black_bg(font: pygame.freetype.Font,
+def render_white_text_alpha_black_bg(font: IGUIFontInterface,
                                      text: str) -> pygame.surface.Surface:
     """
     Render text with a zero alpha background with 0 in the other colour channels. Appropriate for
     use with BLEND_PREMULTIPLIED and for colour/gradient multiplication.
     """
-    text_surface, text_rect = font.render(text,
-                                          pygame.Color('#FFFFFFFF'),
-                                          pygame.Color('#00000001'))
-    text_rect.height -= 1
-    text_rect.topleft = (0, 0)
-    final_surface = pygame.surface.Surface(text_rect.size,
-                                           flags=pygame.SRCALPHA, depth=32)
-    final_surface.fill(pygame.Color('#00000001'))
-    final_surface.blit(text_surface, text_rect, special_flags=pygame.BLEND_PREMULTIPLIED)
-
-    return final_surface
+    text_surface = font.render_premul(text, pygame.Color("white"))
+    return text_surface
 
 
 def basic_blit(destination: pygame.surface.Surface,
@@ -383,7 +374,7 @@ class FontResource:
         self.style = style
         self.location = location[0]
         self.force_style = location[1]
-        self.loaded_font = None  # type: Union[pygame.freetype.Font, None]
+        self.loaded_font = None  # type: Union[GUIFontFreetype, None]
 
     def load(self):
         """
@@ -395,27 +386,17 @@ class FontResource:
         error = None
         if isinstance(self.location, PackageResource):
             try:
-                self.loaded_font = pygame.freetype.Font(
+                self.loaded_font = GUIFontFreetype(
                     io.BytesIO((resources.files(self.location.package) /
                                 self.location.resource).read_bytes()),
-                    self.size, resolution=72)
-                self.loaded_font.pad = True
-                self.loaded_font.origin = True
-                if self.force_style:
-                    self.loaded_font.strong = self.style['bold']
-                    self.loaded_font.oblique = self.style['italic']
+                    self.size, self.force_style, self.style)
             except (pygame.error, FileNotFoundError, OSError):
                 error = FileNotFoundError('Unable to load resource with path: ' +
                                           str(self.location))
 
         elif isinstance(self.location, str):
             try:
-                self.loaded_font = pygame.freetype.Font(self.location, self.size, resolution=72)
-                self.loaded_font.pad = True
-                self.loaded_font.origin = True
-                if self.force_style:
-                    self.loaded_font.strong = self.style['bold']
-                    self.loaded_font.oblique = self.style['italic']
+                self.loaded_font = GUIFontFreetype(self.location, self.size, self.force_style, self.style)
             except (pygame.error, FileNotFoundError, OSError):
                 error = FileNotFoundError('Unable to load resource with path: ' +
                                           str(self.location))
@@ -423,12 +404,7 @@ class FontResource:
         elif isinstance(self.location, bytes):
             try:
                 file_obj = io.BytesIO(base64.standard_b64decode(self.location))
-                self.loaded_font = pygame.freetype.Font(file_obj, self.size, resolution=72)
-                self.loaded_font.pad = True
-                self.loaded_font.origin = True
-                if self.force_style:
-                    self.loaded_font.strong = self.style['bold']
-                    self.loaded_font.oblique = self.style['italic']
+                self.loaded_font = GUIFontFreetype(file_obj, self.size, self.force_style, self.style)
             except (pygame.error, FileNotFoundError, OSError):
                 error = FileNotFoundError('Unable to load resource with path: ' +
                                           str(self.location))
@@ -616,9 +592,3 @@ def translate(text_to_translate: str, **keywords) -> str:
              is passed back.
     """
     return i18n.t(text_to_translate, **keywords)
-
-
-
-
-
-
diff --git a/pygame_gui/elements/ui_text_box.py b/pygame_gui/elements/ui_text_box.py
index e634c4d..d514d00 100644
--- a/pygame_gui/elements/ui_text_box.py
+++ b/pygame_gui/elements/ui_text_box.py
@@ -547,8 +547,7 @@ class UITextBox(UIElement, IUITextOwnerInterface):
 
     def parse_html_into_style_data(self):
         """
-        Parses HTML styled string text into a format more useful for styling pygame.freetype
-        rendered text.
+        Parses HTML styled string text into a format more useful for styling rendered text.
         """
         feed_input = self.html_text
         if self.plain_text_display_only:
diff --git a/pygame_gui/elements/ui_text_entry_line.py b/pygame_gui/elements/ui_text_entry_line.py
index 7efa44c..95f2afc 100644
--- a/pygame_gui/elements/ui_text_entry_line.py
+++ b/pygame_gui/elements/ui_text_entry_line.py
@@ -5,7 +5,7 @@ from typing import Union, List, Dict, Optional, Tuple
 
 import pygame
 
-import pygame.freetype
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 
 from pygame_gui.core import ObjectID
 from pygame_gui._constants import UI_TEXT_ENTRY_FINISHED, UI_TEXT_ENTRY_CHANGED, OldType
@@ -97,7 +97,7 @@ class UITextEntryLine(UIElement):
             self.placeholder_text = placeholder_text
 
         # theme font
-        self.font: Optional[pygame.freetype.Font] = None
+        self.font: Optional[IGUIFontInterface] = None
 
         self.shadow_width = None
         self.border_width = None
diff --git a/setup.py b/setup.py
index 8841786..192b64e 100644
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@ setup(
       zip_safe=False,
       python_requires='>=3.7',
       setup_requires=[],
-      install_requires=['pygame-ce>=2.1.3',
+      install_requires=['pygame-ce>=2.1.4',
                         'python-i18n>=0.3.9',
                         'importlib_resources>1.3; python_version < "3.9"',
                         'typing-extensions; python_version < "3.8"'],


Test patch:
diff --git a/.github/workflows/run_tests_ci.yml b/.github/workflows/run_tests_ci.yml
index a4841f5..71d27ad 100644
--- a/.github/workflows/run_tests_ci.yml
+++ b/.github/workflows/run_tests_ci.yml
@@ -9,7 +9,7 @@ jobs:
       max-parallel: 4
       matrix:
         python-version: ['3.8', '3.9', '3.10', '3.11']
-        pygame-ce-version: ['2.1.3', '2.1.4']
+        pygame-ce-version: ['2.1.4']
     steps:
     - uses: actions/checkout@v1
     - name: Set up Python ${{ matrix.python-version }}
diff --git a/tests/test_core/test_text/test_hyperlink_text_chunk.py b/tests/test_core/test_text/test_hyperlink_text_chunk.py
index 7b5aeb9..a1c82e2 100644
--- a/tests/test_core/test_text/test_hyperlink_text_chunk.py
+++ b/tests/test_core/test_text/test_hyperlink_text_chunk.py
@@ -1,5 +1,5 @@
 import pygame
-import pygame.freetype
+from pygame_gui.core.gui_font_freetype import GUIFontFreetype
 import pytest
 
 from pygame_gui.ui_manager import UIManager
@@ -8,7 +8,7 @@ from pygame_gui.core.text import HyperlinkTextChunk
 
 class TestHyperlinkTextChunk:
     def test_creation(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         style = {'link_text':       pygame.Color('#FF0000'),
                  'bg_colour':       pygame.Color('#808080'),
                  'link_hover':      pygame.Color('#FF00FF'),
@@ -28,7 +28,8 @@ class TestHyperlinkTextChunk:
                            text_shadow_data=style['shadow_data'])
 
     def test_on_hovered(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
+        the_font.remove_font_pad_and_origin()
         style = {'link_text':       pygame.Color('#FF0000'),
                  'bg_colour':       pygame.Color('#808080'),
                  'link_hover':      pygame.Color('#FF00FF'),
@@ -61,7 +62,8 @@ class TestHyperlinkTextChunk:
         assert rendered_chunk_surf.get_at((1, 5)) == pygame.Color('#FF00FF')
 
     def test_on_unhovered(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
+        the_font.remove_font_pad_and_origin()
         style = {'link_text':       pygame.Color('#FF0000'),
                  'bg_colour':       pygame.Color('#808080'),
                  'link_hover':      pygame.Color('#FF00FF'),
@@ -97,7 +99,8 @@ class TestHyperlinkTextChunk:
         assert rendered_chunk_surf.get_at((1, 5)) == pygame.Color('#FF0000')
 
     def test_set_active(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
+        the_font.remove_font_pad_and_origin()
         style = {'link_text':       pygame.Color('#FF0000'),
                  'bg_colour':       pygame.Color('#808080'),
                  'link_hover':      pygame.Color('#FF00FF'),
@@ -130,7 +133,8 @@ class TestHyperlinkTextChunk:
         assert rendered_chunk_surf.get_at((1, 5)) == pygame.Color('#FFFF00')
 
     def test_set_inactive(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
+        the_font.remove_font_pad_and_origin()
         style = {'link_text':       pygame.Color('#FF0000'),
                  'bg_colour':       pygame.Color('#808080'),
                  'link_hover':      pygame.Color('#FF00FF'),
diff --git a/tests/test_core/test_text/test_text_box_layout.py b/tests/test_core/test_text/test_text_box_layout.py
index 69f683b..5f0d0f0 100644
--- a/tests/test_core/test_text/test_text_box_layout.py
+++ b/tests/test_core/test_text/test_text_box_layout.py
@@ -1,6 +1,6 @@
 from collections import deque
 import pygame
-import pygame.freetype
+from pygame_gui.core.gui_font_freetype import GUIFontFreetype
 import pytest
 
 from pygame_gui.ui_manager import UIManager
@@ -234,14 +234,14 @@ class TestTextBoxLayout:
         assert layout_surface.get_at((10, 10)) != pygame.Color(0, 0, 0, 0)
 
     def test_update_text_with_new_text_end_pos(self, _init_pygame, default_ui_manager: UIManager):
+        default_font = default_ui_manager.get_theme().get_font_dictionary().get_default_font()
         input_data = deque([TextLineChunkFTFont(text='hello',
-                                                font=pygame.freetype.Font(None, 20),
+                                                font=default_font,
                                                 underlined=False,
                                                 colour=pygame.Color('#FFFFFF'),
                                                 using_default_text_colour=False,
                                                 bg_colour=pygame.Color('#FF0000'))])
 
-        default_font = default_ui_manager.get_theme().get_font_dictionary().get_default_font()
         default_font_data = {"font": default_font,
                              "font_colour": pygame.Color("#FFFFFF"),
                              "bg_colour": pygame.Color("#00000000")
@@ -254,10 +254,10 @@ class TestTextBoxLayout:
 
         layout_surface = layout.finalise_to_new()
         layout.update_text_with_new_text_end_pos(0)  # this does nothing unless we pass in text
-        assert layout_surface.get_at((10, 10)) == pygame.Color(0, 0, 0, 0)
+        assert layout_surface.get_at((1, 1)) == pygame.Color(0, 0, 0, 0)
 
         layout.update_text_with_new_text_end_pos(3)
-        assert layout_surface.get_at((10, 10)) == pygame.Color(255, 0, 0, 255)
+        assert layout_surface.get_at((1, 1)) == pygame.Color(255, 0, 0, 255)
 
     def test_clear_final_surface(self,  _init_pygame, default_ui_manager: UIManager):
         input_data = deque([SimpleTestLayoutRect(dimensions=(50, 20)),
@@ -316,14 +316,14 @@ class TestTextBoxLayout:
 
     def test_add_chunks_to_hover_group(self, _init_pygame, default_ui_manager: UIManager):
         input_data = deque([TextLineChunkFTFont(text='hello',
-                                                font=pygame.freetype.Font(None, 20),
+                                                font=GUIFontFreetype(None, 20),
                                                 underlined=False,
                                                 colour=pygame.Color('#FFFFFF'),
                                                 using_default_text_colour=False,
                                                 bg_colour=pygame.Color('#FF0000')),
                             HyperlinkTextChunk(href='test',
                                                text='a link',
-                                               font=pygame.freetype.Font(None, 20),
+                                               font=GUIFontFreetype(None, 20),
                                                underlined=False,
                                                colour=pygame.Color('#FFFFFF'),
                                                bg_colour=pygame.Color('#FF0000'),
@@ -348,7 +348,7 @@ class TestTextBoxLayout:
         assert len(links_found) == 1
 
     def test_insert_layout_rects(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -442,7 +442,7 @@ class TestTextBoxLayout:
                                    chunk_index=46)
 
     def test_horiz_centre_all_rows(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -481,7 +481,7 @@ class TestTextBoxLayout:
         assert row.centerx == 250
 
     def test_align_left_all_rows(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -525,7 +525,7 @@ class TestTextBoxLayout:
         assert row.centerx != 250
 
     def test_align_right_all_rows(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -563,7 +563,7 @@ class TestTextBoxLayout:
         assert row.right == 495
 
     def test_vert_center_all_rows(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -601,7 +601,7 @@ class TestTextBoxLayout:
         assert row.centery == 150
 
     def test_vert_align_top_all_rows(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -640,7 +640,7 @@ class TestTextBoxLayout:
         assert row.y == 10
 
     def test_vert_align_bottom_all_rows(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -677,7 +677,7 @@ class TestTextBoxLayout:
         assert row.bottom == 292
 
     def test_set_cursor_position(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -718,7 +718,7 @@ class TestTextBoxLayout:
         layout.set_cursor_position(10)
 
     def test_set_cursor_from_click_pos(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -758,7 +758,7 @@ class TestTextBoxLayout:
         layout.set_cursor_from_click_pos((17, 24))
 
     def test_toggle_cursor(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -799,7 +799,7 @@ class TestTextBoxLayout:
         assert not layout.cursor_text_row.edit_cursor_active
 
     def test_set_text_selection(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -850,7 +850,7 @@ class TestTextBoxLayout:
         layout.set_text_selection(5, 20)
 
     def test_set_default_text_colour(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -886,7 +886,7 @@ class TestTextBoxLayout:
         assert default_chunk_colour == pygame.Color('#00FF00')
 
     def test_insert_text(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -938,7 +938,7 @@ class TestTextBoxLayout:
             layout.insert_text('this should fail', 0)
 
     def test_delete_selected_text(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -1033,7 +1033,7 @@ class TestTextBoxLayout:
         layout.delete_selected_text()
 
     def test_delete_at_cursor(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
@@ -1078,7 +1078,7 @@ class TestTextBoxLayout:
         assert remaining_text == 'hello this is test'
 
     def test_backspace_at_cursor(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 20)
+        the_font = GUIFontFreetype(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
                                                 font=the_font,
                                                 underlined=False,
diff --git a/tests/test_core/test_text/test_text_box_layout_row.py b/tests/test_core/test_text/test_text_box_layout_row.py
index 012bf00..a9b5060 100644
--- a/tests/test_core/test_text/test_text_box_layout_row.py
+++ b/tests/test_core/test_text/test_text_box_layout_row.py
@@ -1,9 +1,9 @@
 from collections import deque
 
-import pygame
-import pygame.freetype
 import pytest
+import pygame
 
+from pygame_gui.core.gui_font_freetype import GUIFontFreetype
 from pygame_gui.core.text import HTMLParser, TextBoxLayoutRow, TextBoxLayout, TextFloatPosition
 from pygame_gui.core.text import SimpleTestLayoutRect, TextLineChunkFTFont
 from pygame_gui.ui_manager import UIManager
@@ -256,9 +256,9 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_2 = pygame.freetype.Font(None, 20)
-        font_3 = pygame.freetype.Font(None, 10)
+        font_1 = GUIFontFreetype(None, 30)
+        font_2 = GUIFontFreetype(None, 20)
+        font_3 = GUIFontFreetype(None, 10)
         text_chunk_1 = TextLineChunkFTFont(text='hello ',
                                            font=font_1,
                                            underlined=False,
@@ -284,14 +284,14 @@ class TestTextBoxLayoutRow:
         # not sure this is right, need to do some more visual testing of vertical
         # alignment of text rects with different height text on a single row.
         assert layout_row.items[0].y == 0
-        assert layout_row.items[1].y == 7
-        assert layout_row.items[2].y == 15
+        assert layout_row.items[1].y == 8
+        assert layout_row.items[2].y == 16
 
         layout_row.vert_align_items_to_row()
 
         assert layout_row.items[0].y == 0
-        assert layout_row.items[1].y == 7
-        assert layout_row.items[2].y == 15
+        assert layout_row.items[1].y == 8
+        assert layout_row.items[2].y == 16
 
     def test_merge_adjacent_compatible_chunks(self, _init_pygame, default_ui_manager: UIManager):
         input_data = deque([])
@@ -312,8 +312,8 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_2 = pygame.freetype.Font(None, 20)
+        font_1 = GUIFontFreetype(None, 30)
+        font_2 = GUIFontFreetype(None, 20)
         text_chunk_1 = TextLineChunkFTFont(text='hello ',
                                            font=font_1,
                                            underlined=False,
@@ -361,8 +361,8 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_2 = pygame.freetype.Font(None, 20)
+        font_1 = GUIFontFreetype(None, 30)
+        font_2 = GUIFontFreetype(None, 20)
         text_chunk_1 = TextLineChunkFTFont(text='hello ',
                                            font=font_1,
                                            underlined=False,
@@ -410,9 +410,7 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_1.origin = True
-        font_1.pad = True
+        font_1 = GUIFontFreetype(None, 30)
         text_chunk_1 = TextLineChunkFTFont(text='D',
                                            font=font_1,
                                            underlined=False,
@@ -449,9 +447,8 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_1.origin = True
-        font_1.pad = True
+        font_1 = GUIFontFreetype(None, 30)
+
         text_chunk_1 = TextLineChunkFTFont(text='test',
                                            font=font_1,
                                            underlined=False,
@@ -496,9 +493,8 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_1.origin = True
-        font_1.pad = True
+        font_1 = GUIFontFreetype(None, 30)
+
         text_chunk_1 = TextLineChunkFTFont(text='D',
                                            font=font_1,
                                            underlined=False,
@@ -534,9 +530,8 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_1.origin = True
-        font_1.pad = True
+        font_1 = GUIFontFreetype(None, 30)
+
         text_chunk_1 = TextLineChunkFTFont(text='test',
                                            font=font_1,
                                            underlined=False,
@@ -593,9 +588,8 @@ class TestTextBoxLayoutRow:
                                       line_spacing=line_spacing,
                                       layout=text_box_layout)
 
-        font_1 = pygame.freetype.Font(None, 30)
-        font_1.origin = True
-        font_1.pad = True
+        font_1 = GUIFontFreetype(None, 30)
+
         text_chunk_1 = TextLineChunkFTFont(text='test',
                                            font=font_1,
                                            underlined=False,
diff --git a/tests/test_core/test_text/test_text_line_chunk.py b/tests/test_core/test_text/test_text_line_chunk.py
index 42098ec..aa0e96e 100644
--- a/tests/test_core/test_text/test_text_line_chunk.py
+++ b/tests/test_core/test_text/test_text_line_chunk.py
@@ -1,5 +1,5 @@
 import pygame
-import pygame.freetype
+from pygame_gui.core.gui_font_freetype import GUIFontFreetype
 import pytest
 
 from pygame_gui.core.colour_gradient import ColourGradient
@@ -10,9 +10,7 @@ from pygame_gui.ui_manager import UIManager
 class TestTextLineChunkFTFont:
     def test_creation(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test',
                                     font=the_font,
@@ -25,13 +23,8 @@ class TestTextLineChunkFTFont:
 
     def test_style_match(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
-
-        the_second_font = pygame.freetype.Font(None, 14)
-        the_second_font.origin = True
-        the_second_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
+        the_second_font = GUIFontFreetype(None, 14)
 
         chunk_1 = TextLineChunkFTFont(text='test',
                                       font=the_font,
@@ -91,9 +84,7 @@ class TestTextLineChunkFTFont:
         assert not chunk_1.style_match(chunk_6)
 
     def test_finalise(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk_1 = TextLineChunkFTFont(text='test',
                                       font=the_font,
@@ -173,9 +164,7 @@ class TestTextLineChunkFTFont:
 
     def test_split(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -200,9 +189,7 @@ class TestTextLineChunkFTFont:
 
     def test_split_index(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -229,9 +216,7 @@ class TestTextLineChunkFTFont:
 
     def test_clear(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -256,9 +241,7 @@ class TestTextLineChunkFTFont:
 
     def test_add_text(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -275,9 +258,7 @@ class TestTextLineChunkFTFont:
 
     def test_insert_text(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -318,9 +299,7 @@ class TestTextLineChunkFTFont:
 
     def test_delete_letter_at_index(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -383,9 +362,7 @@ class TestTextLineChunkFTFont:
 
     def test_backspace_letter_at_index(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -451,9 +428,7 @@ class TestTextLineChunkFTFont:
         chunk.backspace_letter_at_index(index=1)
 
     def test_x_pos_to_letter_index(self):
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -476,9 +451,7 @@ class TestTextLineChunkFTFont:
 
     def test_redraw(self, _init_pygame, default_ui_manager: UIManager):
 
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
 
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
@@ -506,9 +479,8 @@ class TestTextLineChunkFTFont:
         assert layout_surface.get_at((10, 10)) == pygame.Color('#FF00FF')
 
     def test_set_alpha(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
+
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
                                     underlined=False,
@@ -534,9 +506,8 @@ class TestTextLineChunkFTFont:
         assert chunk.alpha == 0
 
     def test_set_offset_position(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
+
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
                                     underlined=False,
@@ -558,9 +529,8 @@ class TestTextLineChunkFTFont:
         assert chunk.effects_offset_pos == (0, 0)
 
     def test_set_scale(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
+
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
                                     underlined=False,
@@ -582,9 +552,8 @@ class TestTextLineChunkFTFont:
         assert chunk.effects_scale == 1.0
 
     def test_set_rotation(self, _init_pygame, default_ui_manager: UIManager):
-        the_font = pygame.freetype.Font(None, 30)
-        the_font.origin = True
-        the_font.pad = True
+        the_font = GUIFontFreetype(None, 30)
+
         chunk = TextLineChunkFTFont(text='test this',
                                     font=the_font,
                                     underlined=False,
diff --git a/tests/test_core/test_ui_appearance_theme.py b/tests/test_core/test_ui_appearance_theme.py
index e2ed7dd..281f919 100644
--- a/tests/test_core/test_ui_appearance_theme.py
+++ b/tests/test_core/test_ui_appearance_theme.py
@@ -2,6 +2,7 @@ import os
 import pytest
 import pygame
 
+from pygame_gui.core.interfaces.gui_font_interface import IGUIFontInterface
 from pygame_gui.core.ui_appearance_theme import UIAppearanceTheme
 from pygame_gui.core.resource_loaders import BlockingThreadedResourceLoader
 from pygame_gui import PackageResource
@@ -33,7 +34,7 @@ class TestUIAppearanceTheme:
         loader.start()
         loader.update()
         font = theme.get_font(combined_element_ids=['button'])
-        assert isinstance(font, pygame.freetype.Font)
+        assert isinstance(font, IGUIFontInterface)
 
     def test_get_colour_from_gradient(self, _init_pygame):
         theme = UIAppearanceTheme(BlockingThreadedResourceLoader(), locale='en')