Skip to content

Commit

Permalink
Make font size measurements translatable (#13575)
Browse files Browse the repository at this point in the history
Fixes #13573

Summary of the issue:
"pt" when reporting font size measurements is not translatable.
Font size measurements are reported inconsistently, for example in excel and PowerPoint are reported as "11.0" instead of "11 pt".

Description of how this pull request fixes the issue:
Makes the font size measurement "pt" translatable across APIs.

Browsers provide "font-size" attribute directly as "12pt|px|em|%|rem|ex" or "large", "small", etc.
This requires a more strategic approach for catching each font-size case and replacing it with a translated string.
Documentation has been added for this.
https://developer.mozilla.org/en-US/docs/Web/CSS/font-size
  • Loading branch information
seanbudd authored Jun 17, 2022
1 parent c75bc4d commit 6f211a1
Show file tree
Hide file tree
Showing 19 changed files with 169 additions and 44 deletions.
2 changes: 1 addition & 1 deletion nvdaHelper/remote/displayModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void displayModelChunk_t::generateXML(wstring& text) {
s<<L"baseline=\""<<baseline<<L"\" ";
s<<L"direction=\""<<direction<<L"\" ";
s<<L" font-name=\""<<formatInfo.fontName<<L"\" ";
s<<L" font-size=\""<<formatInfo.fontSize<<L"pt\" ";
s<<L" font-size=\""<<formatInfo.fontSize<<L"\" ";
if(this->formatInfo.bold) s<<L" bold=\"true\"";
if(this->formatInfo.italic) s<<L" italic=\"true\"";
if(this->formatInfo.underline) s<<L" underline=\"true\"";
Expand Down
2 changes: 1 addition & 1 deletion nvdaHelper/remote/winword.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ void generateXMLAttribsForFormatting(IDispatch* pDispatchRange, int startOffset,
}
float fVal=0.0;
if((formatConfig&formatConfig_reportFontSize)&&(_com_dispatch_raw_propget(pDispatchFont,wdDISPID_FONT_SIZE,VT_R4,&fVal)==S_OK)) {
formatAttribsStream<<L"font-size=\""<<fVal<<L"pt\" ";
formatAttribsStream<<L"font-size=\""<<fVal<<L"\" ";
}
if((formatConfig&formatConfig_reportColor)&&(_com_dispatch_raw_propget(pDispatchFont,wdDISPID_FONT_COLOR,VT_I4,&iVal)==S_OK)) {
formatAttribsStream<<L"color=\""<<iVal<<L"\" ";
Expand Down
2 changes: 1 addition & 1 deletion nvdaHelper/vbufBackends/adobeAcrobat/adobeAcrobat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ VBufStorage_fieldNode_t* renderText(VBufStorage_buffer_t* buffer,
if (fontSize > 0) {
wostringstream s;
s.setf(ios::fixed);
s << setprecision(1) << fontSize << "pt";
s << setprecision(1) << fontSize;
previousNode->addAttribute(L"font-size", s.str());
}
if ((fontFlags&PDDOM_FONTATTR_ITALIC)==PDDOM_FONTATTR_ITALIC) previousNode->addAttribute(L"italic", L"1");
Expand Down
8 changes: 7 additions & 1 deletion source/NVDAObjects/IAccessible/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2021 NV Access Limited, Babbage B.V.
# Copyright (C) 2006-2022 NV Access Limited, Babbage B.V.
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

import typing
from typing import (
Optional,
Expand Down Expand Up @@ -46,6 +47,7 @@
import config
import controlTypes
from controlTypes import TextPosition
from controlTypes.formatFields import FontSize
from NVDAObjects.window import Window
from NVDAObjects import NVDAObject, NVDAObjectTextInfo, InvalidNVDAObject
import NVDAObjects.JAB
Expand Down Expand Up @@ -141,6 +143,10 @@ def normalizeIA2TextFormatField(formatField):
except KeyError:
formatField["text-position"] = TextPosition.BASELINE

fontSize = formatField.get("font-size")
if fontSize is not None:
formatField["font-size"] = FontSize.translateFromAttribute(fontSize)

class IA2TextTextInfo(textInfos.offsets.OffsetsTextInfo):

detectFormattingAfterCursorMaybeSlow=False
Expand Down
6 changes: 4 additions & 2 deletions source/NVDAObjects/JAB/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2021 NV Access Limited, Leonard de Ruijter, Joseph Lee, Renaud Paquay, pvagner
# Copyright (C) 2006-2022 NV Access Limited, Leonard de Ruijter, Joseph Lee, Renaud Paquay, pvagner
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

Expand Down Expand Up @@ -177,10 +177,12 @@ def _getParagraphOffsets(self,offset):
return self._getLineOffsets(offset)

def _getFormatFieldAndOffsets(self, offset, formatConfig, calculateOffsets=True):
attribs: JABHandler.AccessibleTextAttributesInfo
attribs, length = self.obj.jabContext.getTextAttributesInRange(offset, self._endOffset - 1)
field = textInfos.FormatField()
field["font-family"] = attribs.fontFamily
field["font-size"] = "%dpt" % attribs.fontSize
# Translators: Abbreviation for points, a measurement of font size.
field["font-size"] = pgettext("font size", "%s pt") % str(attribs.fontSize)
field["bold"] = bool(attribs.bold)
field["italic"] = bool(attribs.italic)
field["strikethrough"] = bool(attribs.strikethrough)
Expand Down
5 changes: 3 additions & 2 deletions source/NVDAObjects/UIA/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,9 @@ def _getFormatFieldAtRange(self,textRange,formatConfig,ignoreMixedValues=False):
formatField["font-name"]=val
if formatConfig["reportFontSize"]:
val=fetcher.getValue(UIAHandler.UIA_FontSizeAttributeId,ignoreMixedValues=ignoreMixedValues)
if isinstance(val,numbers.Number):
formatField['font-size']="%g pt"%float(val)
if isinstance(val, numbers.Number):
# Translators: Abbreviation for points, a measurement of font size.
formatField['font-size'] = pgettext("font size", "%s pt") % float(val)
if formatConfig["reportFontAttributes"]:
val=fetcher.getValue(UIAHandler.UIA_FontWeightAttributeId,ignoreMixedValues=ignoreMixedValues)
if isinstance(val,int):
Expand Down
9 changes: 6 additions & 3 deletions source/NVDAObjects/window/edit.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2021 NV Access Limited, Babbage B.V.
# Copyright (C) 2006-2022 NV Access Limited, Babbage B.V.
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

Expand Down Expand Up @@ -262,7 +262,9 @@ def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
if formatConfig["reportFontSize"]:
if charFormat is None: charFormat=self._getCharFormat(offset)
# Font size is supposed to be an integral value
formatField["font-size"]="%spt"%(charFormat.yHeight//20)
fontSize = charFormat.yHeight // 20
# Translators: Abbreviation for points, a measurement of font size.
formatField["font-size"] = pgettext("font size", "%s pt") % fontSize
if formatConfig["reportFontAttributes"]:
if charFormat is None: charFormat=self._getCharFormat(offset)
formatField["bold"]=bool(charFormat.dwEffects&CFE_BOLD)
Expand Down Expand Up @@ -507,7 +509,8 @@ def _getFormatFieldAtRange(self, textRange, formatConfig):
if formatConfig["reportFontSize"]:
if not fontObj:
fontObj = textRange.font
formatField["font-size"]="%spt"%fontObj.size
# Translators: Abbreviation for points, a measurement of font size.
formatField["font-size"] = pgettext("font size", "%s pt") % fontObj.size
if formatConfig["reportFontAttributes"]:
if not fontObj:
fontObj = textRange.font
Expand Down
5 changes: 3 additions & 2 deletions source/NVDAObjects/window/excel.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2020 NV Access Limited, Dinesh Kaushal, Siddhartha Gupta, Accessolutions, Julien Cochuyt
# Copyright (C) 2006-2022 NV Access Limited, Dinesh Kaushal, Siddhartha Gupta, Accessolutions, Julien Cochuyt
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

Expand Down Expand Up @@ -994,7 +994,8 @@ def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
if formatConfig['reportFontName']:
formatField['font-name']=fontObj.name
if formatConfig['reportFontSize']:
formatField['font-size']=str(fontObj.size)
# Translators: Abbreviation for points, a measurement of font size.
formatField['font-size'] = pgettext("font size", "%s pt") % fontObj.size
if formatConfig['reportFontAttributes']:
formatField['bold']=fontObj.bold
formatField['italic']=fontObj.italic
Expand Down
4 changes: 3 additions & 1 deletion source/NVDAObjects/window/scintilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
winKernel.virtualFreeEx(self.obj.processHandle,internalBuf,0,winKernel.MEM_RELEASE)
formatField["font-name"]=fontNameBuf.value.decode("utf-8")
if formatConfig["reportFontSize"]:
formatField["font-size"]="%spt"%watchdog.cancellableSendMessage(self.obj.windowHandle,SCI_STYLEGETSIZE,style,0)
fontSize = watchdog.cancellableSendMessage(self.obj.windowHandle, SCI_STYLEGETSIZE, style, 0)
# Translators: Abbreviation for points, a measurement of font size.
formatField["font-size"] = pgettext("font size", "%s pt") % fontSize
if formatConfig["reportLineNumber"]:
formatField["line-number"]=self._getLineNumFromOffset(offset)+1
if formatConfig["reportFontAttributes"]:
Expand Down
15 changes: 7 additions & 8 deletions source/NVDAObjects/window/winword.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2020 NV Access Limited, Manish Agrawal, Derek Riemer, Babbage B.V.
# Copyright (C) 2006-2022 NV Access Limited, Manish Agrawal, Derek Riemer, Babbage B.V.
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

Expand All @@ -14,10 +14,6 @@
from comtypes import COMError, GUID, BSTR
import comtypes.client
import comtypes.automation
import uuid
import operator
import locale
import collections
import colorsys
import eventHandler
import braille
Expand All @@ -29,7 +25,6 @@
from logHandler import log
import winUser
import oleacc
import globalVars
import speech
import config
import textInfos
Expand Down Expand Up @@ -900,6 +895,10 @@ def _normalizeFormatField(self,field,extraDetail=False):
bullet=field.get('line-prefix')
if bullet and len(bullet)==1:
field['line-prefix']=mapPUAToUnicode.get(bullet,bullet)
fontSize = field.get("font-size")
if fontSize is not None:
# Translators: Abbreviation for points, a measurement of font size.
field["font-size"] = pgettext("font size", "%s pt") % fontSize
return field

def expand(self,unit):
Expand Down Expand Up @@ -1476,8 +1475,8 @@ def getLocalizedMeasurementTextForPointSize(self,offset):
# Translators: a measurement in Microsoft Word
return _("{offset:.3g} millimeters").format(offset=offset)
elif unit==wdPoints:
# Translators: a measurement in Microsoft Word
return _("{offset:.3g} points").format(offset=offset)
# Translators: a measurement in Microsoft Word (points)
return _("{offset:.3g} pt").format(offset=offset)
elif unit==wdPicas:
offset=offset/12.0
# Translators: a measurement in Microsoft Word
Expand Down
13 changes: 7 additions & 6 deletions source/appModules/powerpnt.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#appModules/powerpnt.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2012-2018 NV Access Limited
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2012-2022 NV Access Limited
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

from typing import (
Optional,
Dict,
Expand Down Expand Up @@ -929,7 +929,8 @@ def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
if formatConfig['reportFontName']:
formatField['font-name']=font.name
if formatConfig['reportFontSize']:
formatField['font-size']=str(font.size)
# Translators: Abbreviation for points, a measurement of font size.
formatField['font-size'] = pgettext("font size", "%s pt") % font.size
if formatConfig['reportFontAttributes']:
formatField['bold']=bool(font.bold)
formatField['italic']=bool(font.italic)
Expand Down
5 changes: 3 additions & 2 deletions source/appModules/soffice.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2006-2021 NV Access Limited, Bill Dengler, Leonard de Ruijter
# Copyright (C) 2006-2022 NV Access Limited, Bill Dengler, Leonard de Ruijter

from comtypes import COMError
from IAccessibleHandler import IA2, splitIA2Attribs
Expand Down Expand Up @@ -53,7 +53,8 @@ def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
except KeyError:
pass
try:
formatField["font-size"] = "%spt" % formatField["CharHeight"]
# Translators: Abbreviation for points, a measurement of font size.
formatField["font-size"] = pgettext("font size", "%s pt") % formatField["CharHeight"]
except KeyError:
pass
try:
Expand Down
67 changes: 66 additions & 1 deletion source/controlTypes/formatFields.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2021 NV Access Limited, Cyrille Bougot
# Copyright (C) 2021-2022 NV Access Limited, Cyrille Bougot

import re
from typing import Dict

from logHandler import log
from utils.displayString import DisplayStringStrEnum


Expand Down Expand Up @@ -34,3 +36,66 @@ def _displayStringLabels(self):
# Translators: Reported for superscript text.
TextPosition.SUPERSCRIPT: _("superscript"),
}


class FontSize:
_unitToTranslatableString: Dict[str, str] = {
# Translators: Abbreviation for pixels, a measurement of font size.
"px": pgettext("font size", "%s px"),
# Translators: A measurement unit of font size.
"em": pgettext("font size", "%s em"),
# Translators: A measurement unit of font size.
"ex": pgettext("font size", "%s ex"),
# Translators: Abbreviation for relative em, a measurement of font size.
"rem": pgettext("font size", "%s rem"),
# Translators: Abbreviation for points, a measurement of font size.
"pt": pgettext("font size", "%s pt"),
# Translators: Font size measured as a percentage
"%": pgettext("font size", "%s%%"),
}

_keywordToTranslatableString: Dict[str, str] = {
# Translators: A measurement unit of font size.
"xx-small": pgettext("font size", "xx-small"),
# Translators: A measurement unit of font size.
"x-small": pgettext("font size", "x-small"),
# Translators: A measurement unit of font size.
"small": pgettext("font size", "small"),
# Translators: A measurement unit of font size.
"medium": pgettext("font size", "medium"),
# Translators: A measurement unit of font size.
"large": pgettext("font size", "large"),
# Translators: A measurement unit of font size.
"x-large": pgettext("font size", "x-large"),
# Translators: A measurement unit of font size.
"xx-large": pgettext("font size", "xx-large"),
# Translators: A measurement unit of font size.
"xxx-large": pgettext("font size", "xxx-large"),
# Translators: A measurement unit of font size.
"larger": pgettext("font size", "larger"),
# Translators: A measurement unit of font size.
"smaller": pgettext("font size", "smaller")
}

_measurementRe = re.compile(r"([0-9\.]+)(px|em|ex|rem|pt|%)")

@staticmethod
def translateFromAttribute(fontSize: str) -> str:
"""
@param fontSize:
Browsers provide the value of the font-size attribute directly,
either as a measurement or as keywords. These aren't translated.
https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#values
@return: a translated font size string.
"""
if fontSize in FontSize._keywordToTranslatableString:
return FontSize._keywordToTranslatableString[fontSize]
fontSizeMeasurement = re.match(FontSize._measurementRe, fontSize)
if fontSizeMeasurement:
measurement = fontSizeMeasurement.group(1)
measurementUnit = fontSizeMeasurement.group(2)
if measurementUnit in FontSize._unitToTranslatableString:
return FontSize._unitToTranslatableString[measurementUnit] % measurement
log.debugWarning(f"Unknown font-size value, can't translate '{fontSize}'")
return fontSize
7 changes: 5 additions & 2 deletions source/displayModel.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2006-2021 NV Access Limited, Babbage B.V., Joseph Lee
# Copyright (C) 2006-2022 NV Access Limited, Babbage B.V., Joseph Lee

import ctypes
from ctypes import *
from ctypes.wintypes import RECT
from comtypes import BSTR
import unicodedata
import math
Expand Down Expand Up @@ -446,6 +445,10 @@ def _normalizeFormatField(self,field):
bkColor=field.get('background-color')
if bkColor is not None:
field['background-color'] = colors.RGB.fromDisplayModelFormatColor_t(int(bkColor))
fontSize = field.get("font-size")
if fontSize is not None:
# Translators: Abbreviation for points, a measurement of font size.
field["font-size"] = pgettext("font size", "%s pt") % fontSize

def _getOffsetFromPoint(self, x, y):
# Accepts physical coordinates.
Expand Down
8 changes: 4 additions & 4 deletions source/speech/speech.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2006-2021 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Babbage B.V., Bill Dengler,
# Copyright (C) 2006-2022 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Babbage B.V., Bill Dengler,
# Julien Cochuyt

"""High-level functions to speak information.
Expand Down Expand Up @@ -2206,9 +2206,9 @@ def getFormatFieldSpeech( # noqa: C901
if fontName and fontName!=oldFontName:
textList.append(fontName)
if formatConfig["reportFontSize"]:
fontSize=attrs.get("font-size")
oldFontSize=attrsCache.get("font-size") if attrsCache is not None else None
if fontSize and fontSize!=oldFontSize:
fontSize = attrs.get("font-size")
oldFontSize = attrsCache.get("font-size") if attrsCache is not None else None
if fontSize and fontSize != oldFontSize:
textList.append(fontSize)
if formatConfig["reportColor"]:
color=attrs.get("color")
Expand Down
7 changes: 5 additions & 2 deletions source/virtualBuffers/MSHTML.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# virtualBuffers/MSHTML.py
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2009-2020 NV Access Limited, Babbage B.V., Accessolutions, Julien Cochuyt
# Copyright (C) 2009-2022 NV Access Limited, Babbage B.V., Accessolutions, Julien Cochuyt

from comtypes import COMError
import eventHandler
from . import VirtualBuffer, VirtualBufferTextInfo, VBufStorage_findMatch_word, VBufStorage_findMatch_notEmpty
import controlTypes
from controlTypes import TextPosition
from controlTypes.formatFields import FontSize
import NVDAObjects.IAccessible.MSHTML
import winUser
import NVDAHelper
Expand Down Expand Up @@ -56,6 +56,9 @@ def _normalizeFormatField(self, attrs):
textPosition = attrs.get('textPosition')
textPosition = self._getTextPositionAttribute(attrs)
attrs['text-position'] = textPosition
fontSize = attrs.get("font-size")
if fontSize is not None:
attrs["font-size"] = FontSize.translateFromAttribute(fontSize)
return attrs

def _getIsCurrentAttribute(self, attrs: dict) -> controlTypes.IsCurrent:
Expand Down
Loading

0 comments on commit 6f211a1

Please sign in to comment.