Source code for silx.gui._glutils.font

# /*##########################################################################
#
# Copyright (c) 2016-2022 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/
"""Text rasterisation feature leveraging Qt font and text layout support."""

__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "13/10/2016"


import logging
import numpy

from .. import qt
from ..utils.image import convertQImageToArray

try:
    from ..utils.matplotlib import rasterMathText
except ImportError:
    rasterMathText = None

_logger = logging.getLogger(__name__)


[docs] def getDefaultFontFamily(): """Returns the default font family of the application""" return qt.QApplication.instance().font().family()
# Font weights ULTRA_LIGHT = 0 """Lightest characters: Minimum font weight""" LIGHT = 25 """Light characters""" NORMAL = 50 """Normal characters""" SEMI_BOLD = 63 """Between normal and bold characters""" BOLD = 74 """Thicker characters""" BLACK = 87 """Really thick characters""" ULTRA_BLACK = 99 """Thickest characters: Maximum font weight"""
[docs] def rasterTextQt(text, font, size=-1, weight=-1, italic=False, devicePixelRatio=1.0): """Raster text using Qt. It supports multiple lines. :param str text: The text to raster :param font: Font name or QFont to use :type font: str or :class:`QFont` :param int size: Font size in points Used only if font is given as name. :param int weight: Font weight in [0, 99], see QFont.Weight. Used only if font is given as name. :param bool italic: True for italic font (default: False). Used only if font is given as name. :param float devicePixelRatio: The current ratio between device and device-independent pixel (default: 1.0) :return: Corresponding image in gray scale and baseline offset from top :rtype: (HxW numpy.ndarray of uint8, int) """ if not text: _logger.info("Trying to raster empty text, replaced by white space") text = " " # Replace empty text by white space to produce an image if not isinstance(font, qt.QFont): font = qt.QFont(font, size, weight, italic) # get text size image = qt.QImage(1, 1, qt.QImage.Format_RGB888) painter = qt.QPainter() painter.begin(image) painter.setPen(qt.Qt.white) painter.setFont(font) bounds = painter.boundingRect( qt.QRect(0, 0, 4096, 4096), qt.Qt.TextExpandTabs, text ) painter.end() metrics = qt.QFontMetrics(font) # This does not provide the correct text bbox on macOS # size = metrics.size(qt.Qt.TextExpandTabs, text) # bounds = metrics.boundingRect( # qt.QRect(0, 0, size.width(), size.height()), # qt.Qt.TextExpandTabs, # text) # Add extra border and handle devicePixelRatio width = bounds.width() * devicePixelRatio + 2 # align line size to 32 bits to ease conversion to numpy array width = 4 * ((width + 3) // 4) image = qt.QImage( int(width), int(bounds.height() * devicePixelRatio + 2), qt.QImage.Format_RGB888 ) image.setDevicePixelRatio(devicePixelRatio) # TODO if Qt5 use Format_Grayscale8 instead image.fill(0) # Raster text painter = qt.QPainter() painter.begin(image) painter.setPen(qt.Qt.white) painter.setFont(font) painter.drawText(bounds, qt.Qt.TextExpandTabs, text) painter.end() array = convertQImageToArray(image) # RGB to R array = numpy.ascontiguousarray(array[:, :, 0]) # Remove leading and trailing empty columns/rows but one on each side filled_rows = numpy.nonzero(numpy.sum(array, axis=1))[0] filled_columns = numpy.nonzero(numpy.sum(array, axis=0))[0] if len(filled_rows) == 0 or len(filled_columns) == 0: return array, metrics.ascent() min_row = max(0, filled_rows[0] - 1) array = array[ min_row : filled_rows[-1] + 2, max(0, filled_columns[0] - 1) : filled_columns[-1] + 2, ] return array, metrics.ascent() - min_row
[docs] def rasterText(text, font, size=-1, weight=-1, italic=False, devicePixelRatio=1.0): """Raster text using Qt or matplotlib if there may be math syntax. It supports multiple lines. :param str text: The text to raster :param font: Font name or QFont to use :type font: str or :class:`QFont` :param int size: Font size in points Used only if font is given as name. :param int weight: Font weight in [0, 99], see QFont.Weight. Used only if font is given as name. :param bool italic: True for italic font (default: False). Used only if font is given as name. :param float devicePixelRatio: The current ratio between device and device-independent pixel (default: 1.0) :return: Corresponding image in gray scale and baseline offset from top :rtype: (HxW numpy.ndarray of uint8, int) """ if rasterMathText is not None and text.count("$") >= 2: return rasterMathText(text, font, size, weight, italic, devicePixelRatio) else: return rasterTextQt(text, font, size, weight, italic, devicePixelRatio)