Unsolved How to precisely center and adjust the size of any text ?
-
I've been trying and failing to center in a Rectangle a character from the Google Material font.
In order to demonstrate the problem, here are two images.The first one shows an arrow from the Material font. This font is available here : https://github.com/google/material-design-icons/blob/master/iconfont/MaterialIcons-Regular.ttf
The second one shows two characters from the Arimo regular font. This font is available here : https://fonts.google.com/download?family=ArimoI want a text, from any font, to be centered in a Rectangle.
I also want that the text size increases in order that it completely fills the height of its parent. Top of the glyphs must be at the top of the parent. Bottom of the glyphs must be at the bottom of the parent.None of these two requirements are fulfilled by any of the two fonts.
If the horizontal centering is partly acheived, it's not the case of the vertical centering. In each case, the reference for centering is not the vertical middle of the glyphs.
I have painted in darker color the boundingRect and the tightBoundingRect of each text rendering.
I have put an empty rectangle in the center of the container in order to show where I expect the text to be painted. In the following, I ignore the horizontal misalignement of the Arimo font, but it should be corrected as well.With the Arimo, the text is actually painted below the vertical middle. I guess that one of the reference lines is taken from the font to be used as the « vertical middle » of the font, for any character to be printed.
With the Material, the text is actually painted above the vertical middle. I guess that, for some reason, the Qt rendering engine does not compute the tightBoundingRect in the tightest way : the height of this bounding rect is too large. Consequently, even if the problem of reference does not impact this case, as the calculated height of the glyph is wrong, the glyph is not painted at the vertical middle.So my question is : how can I acheive my goal ?
For the Arimo font : as the tightBoundingRect is correct, I can render the text in an invisible Rect, and the copy the result centered the way I like. But : the size of the result is not tall enough. I see no solution right now but iterating with different Rectangle container height until I find a tightBoundingRect.height that is the closest to my display target height. This look incredibly ugly, but it's feasable at startup.
For the Material font, as the tightBoundingRect looks incorrect, I have to first look for the correct value. I can do it by adding a detection of glyph pixels. Begining at the bottom of the tightBoundingRect , I look on each line going upwards for a pixel that is not of the same colour as the background. The first one found sets the bottom of the glyph, and I have my tighterBoundingRect.
I find this solution cumbersome and it makes me think I've completely missed how this text rendering engine works.
If anyone has a lead, if not a solution, in order to acheive what l took for a simple goal, I'd be grateful. Modifying the font is a possibilty for me. I've used the great Trufont tool to look at the properties of the font. Even though Trufont exportation to .otf font fails on my Windows system, I guess I can manage to use it in a VM if it proves useful.The code for the images :
import QtQuick 2.9 import QtQuick.Window 2.0 Window { visible: true width: 2000 height: 2000 id:root property string theText: "Cg"//"\ue040"//"\uE15F"//"aText"//"This is p"// property string theFont: "Arimo" // property string theFont: "Material" Row { anchors.centerIn: parent spacing: aRect.width/20 Rectangle { id:aRect width: 700 height: 500 color: "yellow" Text { font.family: root.theFont id: theText text: root.theText height: parent.height // not needed when anchors.fill: parent font.pixelSize: height*2 onContentHeightChanged: console.info("height="+height+" contentHeight="+contentHeight+"parent.height="+parent.height+" fontInfo.pixelSize="+fontInfo.pixelSize); fontSizeMode: Text.Fit minimumPixelSize: 10 anchors.alignWhenCentered: false // see: https://stackoverflow.com/questions/43284233/qt-quick-2-qml-place-text-exactly-in-center-of-circle } TextMetrics { id:tm text: theText.text onHeightChanged: console.info("TextMetrics font="+font+" height="+height+" tightBoundingRect.height"+tightBoundingRect.height) font.pixelSize: theText.fontInfo.pixelSize//83//theText.height font.family: theText.font.family } FontMetrics { id: fmm font.family: "Material" font.pixelSize: theText.fontInfo.pixelSize//83 Component.onCompleted: console.info("FontMetrics font="+font+"ascent="+ascent+" descent="+descent+" xHeight="+xHeight+" overlinePosition="+overlinePosition+" underlinePosition="+underlinePosition+" boundingRect("+theText.text+")="+boundingRect(theText.text)+" tightBoundingRect("+theText.text+")="+tightBoundingRect(theText.text)) } FontMetrics { id: fma font.family: "Arial" font.pixelSize: theText.fontInfo.pixelSize//83 Component.onCompleted: console.info("FontMetrics font="+font+"ascent="+ascent+" descent="+descent+" xHeight="+xHeight+" overlinePosition="+overlinePosition+" underlinePosition="+underlinePosition+" boundingRect("+theText.text+")="+boundingRect(theText.text)+" tightBoundingRect("+theText.text+")="+tightBoundingRect(theText.text)) } Rectangle { id: cloned color: "red" opacity: 0.2 width: tm.boundingRect.width height: tm.boundingRect.height ShaderEffectSource { id: anExample sourceItem: anExample.target property var target: theText width: target.width height: target.height } Rectangle { color: "blue" opacity: 0.3 x: tm.tightBoundingRect.x - tm.boundingRect.x y: tm.tightBoundingRect.y - tm.boundingRect.y width: tm.tightBoundingRect.width height: tm.tightBoundingRect.height } } Rectangle { color: "transparent" border.color: "brown" border.width: Math.max(2, Math.min(10, width/20)) opacity: 0.3 anchors.centerIn: parent width: tm.tightBoundingRect.width height: tm.tightBoundingRect.height } } Rectangle { width: 700 height: 500 color: Qt.lighter("yellow") Text { font.family: root.theFont id: theText2 text: root.theText anchors.fill: parent font.pixelSize: height*2 onContentHeightChanged: console.info("height="+height+" contentHeight="+contentHeight+"parent.height="+parent.height+" fontInfo.pixelSize="+fontInfo.pixelSize); fontSizeMode: Text.Fit minimumPixelSize: 10 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.alignWhenCentered: false // see: https://stackoverflow.com/questions/43284233/qt-quick-2-qml-place-text-exactly-in-center-of-circle } TextMetrics { id:tm2 text: theText2.text onHeightChanged: console.info("TextMetrics font="+font+" height="+height+" tightBoundingRect.height"+tightBoundingRect.height) font.pixelSize: theText2.fontInfo.pixelSize//83//theText2.height font.family: theText2.font.family } FontMetrics { id: fmm2 font.family: "Material" font.pixelSize: theText2.fontInfo.pixelSize//83 Component.onCompleted: console.info("FontMetrics font="+font+"ascent="+ascent+" descent="+descent+" xHeight="+xHeight+" overlinePosition="+overlinePosition+" underlinePosition="+underlinePosition+" boundingRect("+theText2.text+")="+boundingRect(theText2.text)+" tightBoundingRect("+theText2.text+")="+tightBoundingRect(theText2.text)) } FontMetrics { id: fma2 font.family: "Arial" font.pixelSize: theText2.fontInfo.pixelSize//83 Component.onCompleted: console.info("FontMetrics font="+font+"ascent="+ascent+" descent="+descent+" xHeight="+xHeight+" overlinePosition="+overlinePosition+" underlinePosition="+underlinePosition+" boundingRect("+theText2.text+")="+boundingRect(theText2.text)+" tightBoundingRect("+theText2.text+")="+tightBoundingRect(theText2.text)) } Rectangle { id: cloned2 color: "red" opacity: 0.2 width: tm2.boundingRect.width height: tm2.boundingRect.height ShaderEffectSource { id: anExample2 sourceItem: anExample2.target property var target: theText2 width: target.width height: target.height } Rectangle { color: "blue" opacity: 0.3 x: tm2.tightBoundingRect.x - tm2.boundingRect.x y: tm2.tightBoundingRect.y - tm2.boundingRect.y width: tm2.tightBoundingRect.width height: tm2.tightBoundingRect.height } } Rectangle { color: "transparent" border.color: "brown" border.width: Math.max(2, Math.min(10, width/20)) opacity: 0.3 anchors.centerIn: parent width: tm2.tightBoundingRect.width height: tm2.tightBoundingRect.height } } } }
-
Your description and code sample are rather big so I try to answer your question more generally.
In summary, you want a text to fill it's parent and if one direction is smaller then its parent it should be centered in that direction?
Does the following do what you want?Rectangle { Text { anchors.fill: parent fontSizeMode: Text.Fit font.pixelSize: 10000 // maximum height of the font minimumPixelSize: 8 // minimum height of the font horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } }
Cheers!
-
@DuBu said in How to precisely center and adjust the size of any text ?:
Your description and code sample are rather big so I try to answer your question more generally.
Sorry, for some reason, I missed your answer.
The code sample is meant to be cut and pasted in your Qt Creator and just work. You have to get the fonts first in order to reproduce the same images, but using other fonts and characters may also work in demonstrating the problem (the "Cg" example should be easy to reproduce with any font).In summary, you want a text to fill it's parent and if one direction is smaller then its parent it should be centered in that direction?
Yes.
Does the following do what you want?
Rectangle { Text { anchors.fill: parent fontSizeMode: Text.Fit font.pixelSize: 10000 // maximum height of the font minimumPixelSize: 8 // minimum height of the font horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } }
No.
I use this kind of parametrization it in the provided code.
It fails in both aims you put in your summary.