Center Rust string in a number of characters - format

I have been reading up on format! and all of the formatting options and saw there was a beautiful way of centering a string of text using either <, ^ or >. Rather than centering a string in whitespace, is it possible to center it in a character?
{:^32} // Centers it nicely in 32 characters of whitespace
I was thinking something like:
{:^32'c'} // But this does not work
I have tried almost every way thinkable, except those that seem way too crazy. But luckily there is Stack Overflow!
Is it possible to center a string using numbers to achieve something like the following?
--------SO is AWESOME--------

Is it possible to center a string using numbers to achieve something like the following?
Yes, kind of, as long as you truly mean "something like"...
fn main() {
let s = format!("{:-^30}", "SO is AWESOME");
assert_eq!(s, "--------SO is AWESOME---------");
// ^-- 8 here ^-- 9 here
}
Referring to the formatting syntax, here's the relevant parts:
format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type]
fill := character
align := '<' | '^' | '>'
width := count
We specify a fill (-), an alignment (^ / center), and a width (30). However, the string is 13 units wide, so to pad it out to 30 units, the fill has to be bigger on one side.

Related

Healpy: Rotate a mask together with the map in hp.ma() vs. separately produce different results?

I would like to understand a bit better the behavior of rotate_map_pixel() in this particular case. Below I try to provide a minimal toy example:
map = hp.ma(hp.read_map(mapFile))
map.mask = np.logical_not(hp.read_map(maskFile))
rot = hp.Rotator(coord=['G','C'])
map = rot.rotate_map_pixel(map)
mask = hp.read_map(maskFile)
mask = rot.rotate_map_pixel(mask)
This produces a slightly different map.mask and mask. By "different", I mean
skycut = np.where(map.mask[obj_pix]==False))
and
skycut = np.where(mask[obj_pix]==1.)
or
skycut = np.where(np.isclose(mask[obj_pix],1.))
all produce different skycut. I'd guess this is due to the interpolation procedure?
If I wish to use the mask for selection purpose, what should I do?
If you display the 2 masks, you can see that in one case the mask is a float, in the other case it is a boolean.
Now one case healpy fills the map with UNSEEN and then interpolation is handled by HEALPix C++. I don't know how internally HEALPix handles that.
In the other case we pass a map of 0 and 1 and HEALPix does the interpolation, but we don't trigger any special case of handling UNSEEN values.
See https://zonca.dev/2021/06/healpy-rotate-mask.html a full notebook investigating that.
I find out experimentally that if I round the mask with a number very close to 1 I find the same mask.

Correct combining characters positions with Harfbuzz

I am trying to render text with Harfbuzz and a signed distance field atlas.
The code is basically this:
void drawText(const std::wstring &str, Vec2 pos)
{
// Init harfbuzz
hb_buffer_t *hbBuf = hb_buffer_create();
hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR);
hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN);
hb_buffer_set_language(hbBuf, hb_language_from_string("en", 2));
// Process string
hb_buffer_add_utf32(hbBuf, reinterpret_cast<const uint32_t*>(str.c_str()), -1, 0, -1);
hb_shape(font.hb, hbBuf, nullptr, 0);
// Display string
unsigned int nbGlyphs;
hb_glyph_info_t *glyphInfos = hb_buffer_get_glyph_infos(hbBuf, &nbGlyphs);
hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(hbBuf, &nbGlyphs);
for(unsigned int i = 0; i < nbGlyphs; i++)
{
Vec2 drawPos = pos + Vec2(glyphPos[i].x_offset, glyphPos[i].y_offset) / 64.f;
drawGlyph(glyphInfos[i].codepoint, drawPos);
pos.x += glyphPos[i].x_advance / 64.f;
pos.y += glyphPos[i].y_advance / 64.f;
}
}
The text looks correctly shaped for an English phrase, but when I test it with diacritics, they look misplaced.
I am testing it with aâa aâ̈a bb̂b bb̂̈b bb̧b bb͜b bb︠︡b. The Unicode string does not contain precombined characters. Harfbuzz uses the precombined character â, which makes this one look good. Most other diacritics are off.
Text with diacritics on the left of where they should be
When I multiply x_offset by 0.5, the combining characters are better placed. The accents and the cedilla are at the right x position. The accents do not stack and are too low on the b. The arc under BBB (U+035C) should join the two last letters instead of being centered on the 2nd b.
I also tried with U+FE20 and U+FE21 on the previous group of b. In my tests, U+FE21 is on the 2nd b, but it looks like it should be on the 3rd.
Test with glyphPos[i].x_offset * 0.5f, better but still wrong
I tried with several fonts, but of those fonts, only NotoSansDisplay-Regular.ttf had combining characters. I did not manage to make a program display that string as expected on my Debian system (testing, with HarfBuzz 2.6.4-1).
With Windows, I got better results. Here is what I expect: the accents are stacked, the combining double breve below it at the right place, the cedilla is off.
Text rendering closer to what I expect
Am I doing something wrong with HarfBuzz, or I am testing to niche cases that HarfBuzz does support yet?
EDIT:
The actual problem was not described above.
I loaded a font with FreeType FT_New_Face then created a hb_font_t with hb_ft_font_create.
For every string drawn, I called FT_Set_Pixel_Sizes but kept that hb_font_t.
You should try shaping the same text and font with hb-view / hb-shape. That would help you narrow down where the problem is. I'm making a wild guess that the problem is in how / whether you are accounting for glyph origin in your atlas.
Create a new hb_font_t with hb_ft_font_create every time the font size i changed with FT_Set_Pixel_Sizes.

Why is my number format choking?

I have a bunch of numbers in the millions. I want to use them in labels on my d3 bar chart, formatted like $23M -- but for some reason, my decimals are sticking around.
My read of the documentation says that $,.0f should work, if I first divide by 1000000.
I have var millions = d3.format("^$,.f"); and then I call it later with
.text(function(d) {
return (millions(d.money / 1000000) + "M");
})
I'm still seeing digits after the decimal. What am I doing wrong?
I found a handy site that demonstrates a variety of formatting strings on any number you give it, and when I plug my format in, it looks like it ought to work, but in my code, it doesn't work.
Tinkerable version at https://jsfiddle.net/a1ppt754/
Instead of
var millions = d3.format("^$,.f");
do this way
var millions = d3.format("^$f");
working code here

Unwanted padding in CATextLayer

I am having a problem of drawing a character in a CATextLayer such that the layer is exactly the size of the character.
I use the code below to get the size of the glyph corresponding to the character in the string. At the moment I am neglecting diacretics, so that I am assuming that there is a one to one correlation between glyphs and characters.
I also get nice bounding box values for several characters for the Helvetica font at size 128pt:
Character | x | y | width | height |
B | 9.4 | 0.0 | 70.8 | 91.8 |
y | 1.3 | -27.4 | 61.2 | 96.0 |
I am not sure where the origin of the coordinate system is located in which the coordinates are expressed. I am assuming that (0,0) is at the very left and vertically located on the baseline of the font. That is why 'y' has a negative y value.
I am using this code to calculate the size of a capital B and resize its CATextLayer accordingly.
- (CATextLayer *) testTextLayer
{
CATextLayer *l = [CATextLayer layer];
l.string = #"B";
NSUInteger len = [l.string length];
l.fontSize =128.f;
CGColorRef blackColor = CGColorCreateGenericGray(0.f, 1.f);
l.foregroundColor = blackColor;
CGColorRelease(blackColor);
// need to set CGFont explicitly to convert font property to a CGFontRef
CGFontRef layerFont = CGFontCreateWithFontName((CFStringRef)#"Helvetica");
l.font = layerFont;
// get characters from NSString
UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len);
CFStringGetCharacters((__bridge CFStringRef)l.string, CFRangeMake(0, [l.string length]), characters);
// Get CTFontRef from CGFontRef
CTFontRef coreTextFont = CTFontCreateWithGraphicsFont(layerFont, l.fontSize, NULL, NULL);
// allocate glyphs and bounding box arrays for holding the result
// assuming that each character is only one glyph, which is wrong
CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph)*len);
CTFontGetGlyphsForCharacters(coreTextFont, characters, glyphs, len);
// get bounding boxes for glyphs
CGRect *bb = (CGRect *)malloc(sizeof(CGRect)*len);
CTFontGetBoundingRectsForGlyphs(coreTextFont, kCTFontDefaultOrientation, glyphs, bb, len);
CFRelease(coreTextFont);
l.position = CGPointMake(200.f, 100.f);
l.bounds = bb[0];
l.backgroundColor = CGColorCreateGenericRGB(0.f, .5f, .9f, 1.f);
free(characters);
free(glyphs);
free(bb);
return l;
}
This is the result I am getting from the above code. It seems to me that the size is correct, however there is some kind of padding taking place around the character.
Now my questions
Am I right with the assumption of the origin of the bounding box of the glyph?
How can one draw the letter such that it fits neatly into the layer, without this padding? Or alternatively, how can one control this padding?
Maybe I am missing an obvious point here. Is there now way after setting the size and the font of the layer to shrink wrap the layer around the character in a defined way (meaning with optional padding, a bit like in CSS)?
How about creating a CGPath from a glyph with CTFontCreatePathForGlyph and then getting its bounding box with CGPathGetBoundingBox?
An alternative would be to create a CTRun somehow and use the CTRunGetImageBounds function which also returns a "tight" bounding box, but this probably requires more lines of code than the aforementioned approach and you'd need to have a graphics context.
I assume this has to do with the built-in space around letters. Their bounding box usually includes some amount of space that serves as a general spacing if you assemble glyphs in a line. These are then optimized with kerning tables.
And yes, extracting the actual bezier path is a very good method of getting a tight bounding box. I have been doing that for years, though I have no experience using CATextLayers.

JLabel not displaying all the characters even after dynamically changing font size

I am trying to fit a sentence that changes often, in to a few jlabels. Widths of my 3 jlabels stay unchanged all the time. What I am doing is changing the font size so all the characters can fit with out non being out of the display range of the labels. What I do is call below code snippet when ever sentence is changed.
Here is my code
String sentence = "Some long sentence";
int SentenceLength = sentence.length();
int FontSize = 0;
// sum of widths of the three labels
int TotalLblLength=lbl_0ValueInWords.getWidth()+lbl_1ValueInWords.getWidth()+lbl_1ValueInWords.getWidth();
/*decide the font size so that all the characters can be displayed
with out exceeding the display renge(horizontal) of the 3 labels
Inconsolata -> monopace font
font size == width of the font*2 (something I observed, not sure
if this is true always) */
FontSize=(TotalLblLength/SentenceLength)*2;
// max font size is 20 - based on label height
FontSize=(FontSize>20)?20:FontSize;
lbl_0ValueInWords.setFont(new java.awt.Font("Inconsolata", 0,FontSize));
lbl_1ValueInWords.setFont(new java.awt.Font("Inconsolata", 0,FontSize));
lbl_2ValueInWords.setFont(new java.awt.Font("Inconsolata", 0,FontSize));
int CharCount_lbl0 = width_lbl0 / (FontSize / 2);
int CharCount_lbl1 = width_lbl1 / (FontSize / 2);
int CharsCount_lbl2 = width_lbl2 / (FontSize / 2);
/*Set texts of each label
if sentence has more than the number of characters that can fit in the
1st label, excessive characters are moved to the 2nd label. same goes
for the 2nd and 3rd labels*/
if (SentenceLength > CharCount_lbl0) {
lbl_0ValueInWords.setText(sentence.substring(0, CharCount_lbl0));
if (SentenceLength > CharCount_lbl0 + CharCount_lbl1) {
lbl_1ValueInWords.setText(sentence.substring(CharCount_lbl0, CharCount_lbl0 + CharCount_lbl1));
lbl_2ValueInWords.setText(sentence.substring(CharCount_lbl0 + CharCount_lbl1, SentenceLength));
} else {
lbl_1ValueInWords.setText(sentence.substring(CharCount_lbl0, SentenceLength));
}
} else {
lbl_0ValueInWords.setText(sentence);
}
But even after resetting font size sometimes the last character goes out of the display range. I have removed margines from the jlabels that may cause this. This happens for random length sentences. I can solve the problem for the application by reducing label width used for the calculations(hopefully)
Can anyone explain me the reason? Could be because of some defect in the fonts symmetry?
There is no such thing as font symmetry?
There are 2 types of fonts for what you are dealing with. Monospace fonts, and non-monospace fonts. Monospace fonts have the same exact width for every single character you can type. The others do not.
On top of that, fonts are rendered differently across different OS's. Something on windows will be around 10-20% longer on Mac because they space out the fonts differently.
Whatever it is you are trying to do with JLabels, stop. You should not be using 3 JLabels to show 3 lines of text because they dont fit. Scrap them and use a JTextArea. It has text wrap, you can set the font, and remove the margin/border/padding and make it non-editable. You can customize it very easily so it is indistinguishable from a JLabel, but it will save you a ton of work.
Pick the right tool for the right job.

Resources