Message Boards Message Boards

GROUPS:

How to get the output width of text?

Posted 1 year ago
1932 Views
|
9 Replies
|
6 Total Likes
|

I cannot find any function which delivers the output width of formatted text. I need that either in absolute printer points or scaled to the enclosing context or as the number of characters in standard output font size (13 in my environment).

As an example I want to use Multicolumn for a list of base-b tuples:

Block[{ruler = StringRepeat["1234567890", 7], k, tuples, base, cols},
  base = 16; k = 3;
  tuples = RandomChoice[Tuples[Range[0, base - 1], k], 20];
  tuples = BaseForm[#, base] & /@ tuples;
  cols = 5;
  Print[ruler, "\ncols = ", cols, "\n", Multicolumn[tuples, cols]];
  ];

enter image description here

In this example, I use a fixed number of column cols = 5 which gives a total horizontal width of about 69 standard characters. But of course, this number cols should be dependent on the total horizontal width available and the base-dependent width of each tuple, which in turn is depended on Style-formating.

As a bypass, I calculate cols in some rude way by the following code for twidth and cols. It is done twice, without and with formatting:

Block[{ruler = StringRepeat["1234567890", 7], k, tuples, base, cols, 
   hwidth = 70, ds = 1, twidth, fs},
  (* Build tuples *)
  base = 16; k = 3;
  tuples = RandomChoice[Tuples[Range[0, base - 1], k], 20];
  tuples = BaseForm[#, base] & /@ tuples;
  (* Calculate the width of one tupel *)
  twidth = 
   2 + k - 1 + 
    k*(1 + Switch[Sign[base - 10], 0, 0, +1, 2.00, -1, 1.0]);
  (* Calculate the number of columns and print *)
  cols = Max[Floor[(hwidth + ds)/(twidth + ds)], 1];
  Print[ruler, "\n{twidth, cols} = ", {twidth, cols}, "\n", 
   Multicolumn[tuples, cols]];
  (* The same for a larger font size fs *)
  fs = 18;
  tuples = Style[#, Blue, fs] & /@ tuples;
  twidth = 
   fs/13*(2 + k - 1 + 
      k*(1 + Switch[Sign[base - 10], 0, 0, +1, 2.00, -1, 1.0]));
  cols = Max[Floor[(hwidth + ds)/(twidth + ds)], 1];
  Print[ruler, "\n{twidth, cols} = ", {twidth, cols}, "\n", 
   Multicolumn[tuples, cols]];
  ];

enter image description here

The calculation of twidth above is the crucial point. If I had a WL-function like WidthChr[expr], I could just write:

twidth = WidthChr[First[tuples]];

or any other formatted or non-formatted expression instead of tuples. Of course, this could be given as a read-only property as well.

9 Replies

Werner,

How about something like

ImageDimensions[Rasterize[First[tuples]]]

This gives the x and y dimensions of the rendered text.

Regards,

Neil

I just noticed that "BoundingBox" is an option to Rasterize:

Rasterize[First[tuples],"BoundingBox"]

You can call this and convert the units, you may or may not need some additional options to rasterize to set the resolution, etc. but that should be straightforward.

Putting it together:

charwidth = First[Rasterize["A", "BoundingBox"]]
Widthch[str_] := First[Rasterize[str, "BoundingBox"]]/charwidth // N

should work for you.

Posted 1 year ago

Thank you very much, Neil. Your Rasterice, BoundingBox proposal sounds very promising. I will try that.

I did not come to the idea of rasterizing the object. It's everything but straightforward. I will post a request at Wolfram for a direct object width possibility.

The problem with a "direct width" approach is "what do you mean?" the width in characters does not make sense because there are subscripts involved. If all text were the same size then you can compute the string length. As soon as you get into Mathematica expressions, there is no more connection to strings. In fact, to understand how Mathematica represents your tuples, click on one and hit CMD-Shift-E. you will see the actual internal representation of the expression -- it is not a string by any means. It formats into boxes and then separately renders the boxes for the screen. For example:

If your tuple is

enter image description here

Then the internal representation is actually:

Cell[BoxData[
 TagBox[
  RowBox[{"{", 
   RowBox[{
    InterpretationBox[
     SubscriptBox["\<\"f\"\>", "\<\"16\"\>"],
     15,
     Editable->False], ",", 
    InterpretationBox[
     SubscriptBox["\<\"3\"\>", "\<\"16\"\>"],
     3,
     Editable->False], ",", 
    InterpretationBox[
     SubscriptBox["\<\"0\"\>", "\<\"16\"\>"],
     0,
     Editable->False]}], "}"}],
  BaseForm[#, 16]& ]], "Output",
 CellChangeTimes->{3.788545301833375*^9},
 CellLabel->"Out[143]//BaseForm="]

In summary, I think your only viable approach is to see how the text rasterizes and use that as a length indicator. It will be very accurate and is easy. Besides, isn't that what you actually care about?

I still recommend you talk to Wolfram. Maybe they have a different idea.

Regards,

Neil

I've been using the same Rasterized method for several years now, and it works great as long as your units are printers points, as in Graphics. Unfortunately, there is not a direct conversion to ItemSize units for Grid, so I've generally had to use some trial and error conversion each time I'm faced with that.

If you get a nice implementation, please consider submitting it to the Wolfram Function Repository.

Bob

Robert,

I am not sure I understand the issue with Grid. Why would the Rasterize technique fail for that? Or are you saying that one you have the size of an item it is hard to use that in ImageSize options?

Regards,

Neil

The width of columns and height of rows in Grid (and related functions) is controlled by the ItemSize option. The units for width are ems (roughly the width of the m character in the font used) and the units for height are rows. See http://reference.wolfram.com/language/ref/ItemSize.html.

ImageSize is always printers points, so you can use the result of the rasterized image dimensions directly.

I brought up Grid because you mentioned columns at the beginning of this conversation, but I see now that you are not using Grid (too hasty).

So, if you want to compute the number of columns Multicolumn would fit into the window (or Pane or Panel), then the width of the rasterized image divided into the width of the enclosing element (window, Pane, Panel, etc.) will give you the result you seek.

HTH

Interesting. I agree that Werner should have what he needs, but I was thinking about that Wolfram Function Repository function you were proposing. Would the use case involving grid be:

  • you have the size of several items that you want to put in a grid
  • you run this function for each and get the vertical and horizontal sizes
  • but the problem becomes how to convert them to ItemSize units so you can adjust your Grid?

If my understanding is correct, do you think this will work:

Rasterize an "m" and get its height and width, now you have the conversion from ItemSize units to points. Use this to programmatically set the Grid spacing.

Would this work?

Regards,

Neil

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract