Why is there a difference between "width" and "actualBoundingBoxLeft + actualBoundingBoxRight" in a "TextMetrics" object of a HTML5 canvas? - text

When I call measureText as shown in the snippet, I get the following result:
{
"width": 45.43333435058594,
"actualBoundingBoxLeft": 0,
"actualBoundingBoxRight": 45.35,
"actualBoundingBoxAscent": 18,
"actualBoundingBoxDescent": 0
}
Why is there a difference between width and actualBoundingBoxRight, if actualBoundingBoxLeft is zero?
c2d = document.getElementById('canvas').getContext('2d');
c2d.direction = 'ltr';
c2d.font = '24px serif';
console.log (c2d.measureText('TeX'));
<canvas id="canvas"></canvas>

Hope I get what you are asking, I'll give it a go.
The width property gives the text's advance width. Space taken excluding left and right-side bearing.
– width attribute
The width of that inline box, in CSS pixels. (The text's advance width.)
– actualBoundingBoxLeft attribute
The distance parallel to the baseline from the alignment point given by the textAlign attribute to the left side of the bounding rectangle of the given text, in CSS pixels; positive numbers indicating a distance going left from the given alignment point.
Where it also gives the note:
The sum of this value and the next (actualBoundingBoxRight) can be wider than the width of the inline box (width), in particular with slanted fonts where characters overhang their advance width.
– actualBoundingBoxRight attribute
The distance parallel to the baseline from the alignment point given by the textAlign attribute to the right side of the bounding rectangle of the given text, in CSS pixels; positive numbers indicating a distance going right from the given alignment point.
Some examples
Take this times f with data from canvas's measuereText (normal left, italic right):
ERR: Switched places for middle and bottom width line (blue ones), but did not update the labels. "middle" is "bottom" and "bottom" is "middle" as for the labels in the picture. I'll try to get time to upload a new later.
Especially the slanted version shows this well. The blue line following the textBaseline (gray horizontal line), and starting at textAlign (gray vertical line) show the width value for the glyph. That is how much the font advances the "typehead".
Bounding box left / right are the extremes in horizontal expansion. If one look on it as an rectangle. Same goes for Ascend and Descend. They are the extremes up / down. But, as font's "overlap" (kerning etc.) it is not a factor for width which represents advanced width.
The sum of the box width is 111 + 39 = 150 but the width is only 72.28.
As for your sample, it is harder to catch with such small fonts. (Relatively speaking). Increasing the for to 1024px or what ever gives a clearer result. There is so small fractions and path calculations that one will miss subtle pixel fractions. With 1024px:
actualBoundingBoxAscent : 747
​actualBoundingBoxDescent : 14
​actualBoundingBoxLeft : -10
​actualBoundingBoxRight : 1933.5
​width : 1938.5
The difference (1933.5 + -10 = 1923.5) is still small, considering the total width, but at least present in the served object.
Another sample with +:
As one observe the glyph advances the text a lot more then what it occupies in painted pixels. One can even have cases where a glyph does not advance the text at all. They can still stand alone in a text, but it's definition applies to the previous glyph in a way ... For example dấu hỏi or hook above has zero width.
But some are still defined as advancing characters for example:
Also interesting with that sample is to see how Descent is negative, (not going down below textBaseline), and Ascent is also present. Logically when one look at it, but can be a gotcha.
Could scale up the test on the canvas, but would have to look at it closer. Way too long since I worked on cavases. This is a close view, but have not validated or checked how precise (down to pixel) the lines are.
If it is correct, it show a subtle diff where the width advances at the end of TeX using 24px font.

Related

How to add border to an image, choosing color dynamically based on image edge color(s)?

I need to add a border to each image in a set of tightly cropped logos. The border color should either match the most commonly used color along the edge, OR, if there are too many different edge colors, it should choose some sort of average of the edge colors.
Here's an example. In this case, I would want the added border to be the same color as the image "background" (using that term in the lay sense). A significant majority of the pixels along the edges are that color, and there are only two other colors, so the decision algorithm would be able to select that rather drecky greenish tan for the added border (not saying anything bad about the organization behind the logo, mind you).
Does Pillow have any functions to simplify this task?
I found answers that show how to use Pillow to add borders and how to determine the average color of an entire image. But I couldn't find any code that looks only at the edges of an image and finds the predominant color, which color could then be used in the border-adding routine. Just in case someone has already done that work, please point me to it. ('Edges' meaning bands of pixels along the top/bottom/left/right margins of the image, whose height or width would be specified as a percentage of the image's total size.)
Short of pointing me to a gist that solves my whole problem, are there Pillow routines that look at edges and/or that count the colors in a pixel range and put them into an array or what not?
I see here that OpenCV can add a border the duplicates the color of the each outermost pixel along all four edges, but that looks funky—I want a solid-color border. And I'd prefer to stick with Pillow—unless another library can do the whole edge-color-analysis-and-add-border procedure in one step, more or less, in which case, please point it out.
Overwrite the center part of the image with some fixed color, that – most likely – won't be present within the edge. For that, maybe use a color with a certain alpha value. Then, there's a function getcolors, which exactly does, what you're looking for. Sort the resulting list, and get the color with the highest count. (That, often, will be the color we used to overwrite the center part. So check for that, and take the second entry, if needed.) Finally, use ImageOps.expand to add the actual border.
That'd be the whole code:
from PIL import Image, ImageDraw, ImageOps
# Open image, enforce RGB with alpha channel
img = Image.open('path/to/your/image.png').convert('RGBA')
w, h = img.size
# Set up edge margin to look for dominant color
me = 3
# Set up border margin to be added in dominant color
mb = 30
# On an image copy, set non edge pixels to (0, 0, 0, 0)
img_copy = img.copy()
draw = ImageDraw.Draw(img_copy)
draw.rectangle((me, me, w - (me + 1), h - (me + 1)), (0, 0, 0, 0))
# Count colors, first entry most likely is color used to overwrite pixels
n_colors = sorted(img_copy.getcolors(2 * me * (w + h) + 1), reverse=True)
dom_color = n_colors[0][1] if n_colors[0][1] != (0, 0, 0, 0) else n_colors[1][1]
# Add border
img = ImageOps.expand(img, mb, dom_color).convert('RGB')
# Save image
img.save('with_border.png')
That'd be the result for your example:
And, that's some output for another image:
It's up to you to decide, whether there are several dominant colors, which you want to mix or average. You'd need to inspect the n_colors appropriately on the several counts for that. That's quite a lot of work, which is left out here.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1
Pillow: 8.2.0
----------------------------------------

How to clip vega-lite text inside a rect?

In a webpage created with node/webpack, vega-lite, and vegaEmbed, I have a layer with rect marks with short annotations inside them using text marks. I'd like to clip the text to its surrounding rect but haven't figured out a way to do this and hope someone can point me in the right direction.
I realize text has a limit property in pixel units. If I could determine the pixel units of my rect marks (I don't know how to do this), using limit seems like a reasonable approach.
Also, if I knew the pixel extents of my rectangle, I can then write code to align the text within the rect which would be desirable. Currently I just use the same x as the rect, with a dx offset.
I've read about background for text which is a similar problem, but not the same.

SVG Text-anchor top left

By default, the anchor for the text element in SVG is at the bottom left, but I want it to be at the top left, since I am also creating a rectangle to act as the background for the text, but it is displayed incorrectly since the text is higher than the rectangle (because rectangle anchor/offset is at the top left). Is there a way to fix this, so both text and rectangle can be drawn at same coordinates and be displayed in the same location.
The dominant-baseline property/attribute worked for me:
svg {
dominant-baseline: hanging;
}
The coordinates (x and y) you supply for text elements is used as the baseline of the text. This makes sense because if there is text with varying font sizes on the same line, you would want their baselines to line up.
There is no "automatic" way to do what you want. SVG elements are always absolutely positioned.
You will just have to move the text down a bit by making the y coordinate a bit larger.
Alternatively, you could add a dy attribute to shift the text down a bit. Or even use a transform attribute to do the same. But using either of those methods wouldn't really be simplifying the process for you.

AndroidPlot - Position TextLabelWidget not consistently on different devices

I have a chart show info of apps, but when run it on devices android. The position of text on chart not consistently.
These images illustrate the problem: https://drive.google.com/folderview?id=0B7-CxJHQ5ZnjYjVlTlFQdElWRGM&usp=sharing
I use TextLabelWidget in AndroidPlot. How to keep position of TextLabelWidget on chart with the same image1 in link above on devices?
The problem appears to be that your plot area is set to fill the width of the screen and since the bars are evenly distributed in that space, their x positions are essentially fractions of the screen width.
At the same time you have labels that appear to be positioned using absolute positioning. As an example, this code will position 4 labels at 0%, 25%, 50% and 75% screen width, 80 pixels down from the top of the screen:
txtWidget1.position(0, XLayoutStyle.RELATIVE_TO_LEFT, PixelUtils.dpToPix(80), YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP);
txtWidget2.position(0.25f, XLayoutStyle.RELATIVE_TO_LEFT, PixelUtils.dpToPix(80), YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP);
txtWidget3.position(0.50f, XLayoutStyle.RELATIVE_TO_LEFT, PixelUtils.dpToPix(80), YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP);
txtWidget4.position(0.75f, XLayoutStyle.RELATIVE_TO_LEFT, PixelUtils.dpToPix(80), YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP);
There are some other factors that you are also probably going to need to deal with such as bar width but this should get you closer. You may find this doc useful as far as a guide for the different positioning methods.
Another tool to consider using if you aren't using it already is the Configurator. This will let you set your positions etc. inside xml and override values based on screen size, orientation, etc.

QuirksMode mouse position incorrect for SVG in a jQueryUI tab?

I've just noticed something strange. If I draw an SVG shape, the co-ordinates I supply as the shape's x and y attributes don't match up with the co-ordinates returned by a mouse event on that shape, when using the standard ways of returning the mouse position.
For example, if I draw a rectangle as follows:
shape = svgDocument.createElementNS(svgNS, "rect");
shape.setAttributeNS(null, "x", rect_x);
shape.setAttributeNS(null, "y", rect_y);
...
shape.addEventListener("mouseover", etc);
in the event routine, I get the mouse position using the QuirksMode algorithm (the "correct script for detecting the mouse co-ordinates", at http://www.quirksmode.org/js/events_properties.html). For the Y co-ordinate, I have to correct the QuirksMode value as follows, in order to get to 'rect_y':
F/F 8 subtract 105
Webkit subtract 103
IE9 subtract 104
Opera subtract 103
What's also curious is that both Webkit and IE9 provide evt.offsetY, and set it to the 'correct' position (exactly equal to 'rect_y'). FF leaves offsetY undefined, and Opera sets it to 1.
I suspect that the issue is that this is in a jQueryUI tab, although I haven't worked this through yet. Anyone seen this? Any thoughts?
In SVG you would use the getScreenCTM method to convert co-ordinates rather than offsetX or offsetY etc. There's an example here: http://www.carto.net/svg/eventhandling/ Search for the Converting clientX to viewBox coordinates part. It's a bit complicated as it tries to cope with old UAs such as ASV3 that don't have a getScreenCTM method.

Resources