Tk_ComputeTextLayout(3tk)

πŸ“¦
version 9.0.4 Β· 2 versions
, in debian trixie, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ’– Tk_ComputeTextLayout β€” the Tk Library Procedures era πŸ’–

okay bestie, sit down, hydrate, because today we're learning about the whole girlboss squad of routines that measure and display single-font, multi-line, justified text. That's the whole vibe. Meet the crew:

  • Tk_ComputeTextLayout
  • Tk_FreeTextLayout
  • Tk_DrawTextLayout
  • Tk_UnderlineTextLayout
  • Tk_PointToChar
  • Tk_CharBbox
  • Tk_DistanceToTextLayout
  • Tk_IntersectTextLayout
  • Tk_TextLayoutToPostscript

They're all family. They all live in this manpage. Periodt. ✨


🌸 SYNOPSIS 🌸

#include <tk.h>

Tk_TextLayout
Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags, widthPtr, heightPtr)

Tk_FreeTextLayout(layout)

Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar)

Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline)

int
Tk_PointToChar(layout, x, y)

int
Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr)

int
Tk_DistanceToTextLayout(layout, x, y)

int
Tk_IntersectTextLayout(layout, x, y, width, height)

Tk_TextLayoutToPostscript(interp, layout)

πŸŽ€ ARGUMENTS πŸŽ€

no cap, this is the part where every single parameter gets its moment. every one of them is a main character. πŸ‘‘

  • Tk_Font tkfont (in) β€” the font used to build and render the text layout. important: this tkfont has to stay valid for the entire lifetime of the text layout, so don't be out here freeing it early. it must be something you got back from an earlier Tk_GetFont call. πŸ’…
  • const char *string (in) β€” the potentially multi-line string whose dimensions are getting computed and stashed in the text layout. also has to stay valid for the whole lifetime of the layout. same energy as the font, honestly. πŸ¦‹
  • int numChars (in) β€” how many characters from string we're considering. if numChars is negative, we assume string is null terminated and use Tcl_NumUtfChars to figure out the length. negative = "figure it out yourself, bestie." πŸ’€
  • int wrapLength (in) β€” the longest line length allowed, measured in pixels. lines in string get auto-broken at word boundaries and wrapped when they hit this length. if wrapLength is too small for even one character to fit on a line, it gets expanded so one character fits per line. and if wrapLength is <= 0, there's no automatic wrapping at all β€” lines get as long as they want and only wrap when they hit a newline/return character. πŸ™ˆ
  • Tk_Justify justify (in) β€” how to justify the lines in a multi-line text layout. the options are TK_JUSTIFY_LEFT, TK_JUSTIFY_CENTER, or TK_JUSTIFY_RIGHT. if the layout is only one single line, justify literally doesn't matter. irrelevant. not invited to the party. πŸŽ€
  • int flags (in) β€” various flag bits OR-ed together, like a little friendship bracelet of options. TK_IGNORE_TABS means tab characters do not get expanded to the next tab stop. TK_IGNORE_NEWLINES means newline/return characters do not cause a line break. and if either tabs or newlines/returns are ignored, they get treated as regular characters β€” measured and displayed in a platform-dependent way as described in Tk_MeasureChars β€” and they lose all their special behaviors. πŸ’…
  • int *widthPtr (out) β€” if it's non-NULL, it gets filled with either the width, in pixels, of the widest line in the text layout, or the width, in pixels, of the bounding box for the character specified by index. two jobs, one pointer. she's versatile. ✨
  • int *heightPtr (out) β€” if it's non-NULL, it gets filled with either the total height, in pixels, of all the lines in the text layout, or the height, in pixels, of the bounding box for the character specified by index. same dual-threat energy. πŸ‘‘
  • Tk_TextLayout layout (in) β€” a token representing all the cached layout info about the single-font, multi-line, justified piece of text. this token comes from Tk_ComputeTextLayout. don't lose her. 🫢
  • Display *display (in) β€” the display you're drawing on.
  • Drawable drawable (in) β€” the window or pixmap you're drawing into.
  • GC gc (in) β€” the graphics context used for drawing the text layout. heads up: the font selected in this gc must correspond to the tkfont used when constructing the text layout. they have to match. no mix-and-match. 😭
  • int x, y (in) β€” either the point, in pixels, where the upper-left hand corner of the text layout goes when it's being drawn, or the coordinates of a point (relative to the upper-left hand corner of the text layout) to check against the text layout. depends on which function you're feeding her to. 🌸
  • Tcl_Size firstChar (in) β€” the index of the first character to draw from the given text layout. 0 means draw from the beginning. the classic. πŸ’–
  • Tcl_Size lastChar (in) β€” the index of the last character up to which to draw. note: the character specified by lastChar itself will not be drawn. a number less than 0 means draw all the characters in the text layout. πŸ¦‹
  • int underline (in) β€” the index of the single character to underline in the text layout, or a negative number counting backwards from the end of the string. any out-of-range number (e.g. INT_MIN) means no underline at all. she said "not today." πŸ’€
  • Tcl_Size index (in) β€” the index of the character whose bounding box you want. the bounding box is computed with respect to the upper-left hand corner of the text layout. πŸŽ€
  • int *xPtr, *yPtr (out) β€” filled with the upper-left hand corner, in pixels, of the bounding box for the character specified by index. either or both of xPtr and yPtr may be NULL, in which case the corresponding value just doesn't get calculated. lowkey optional. πŸ™ˆ
  • int width, height (in) β€” the width and height, in pixels, of the rectangular area you want to compare for intersection against the text layout. ⭐
  • Tcl_Interp *interp (out) β€” postscript code that will print the text layout gets appended to the result of interpreter interp. it's giving "send it to the printer." 🌈

πŸ’– DESCRIPTION πŸ’–

Okay bestie, so these routines? They're for measuring and displaying single-font, multi-line, justified text. That's the whole vibe. ✨

If you just wanna measure and display simple single-font, single-line strings, go read the docs for Tk_MeasureChars instead, that's more your speed for that job. And real talk: there is no programming interface in the core of Tk that supports multi-font, multi-line text. None. Nada. If you want that, you have to build it yourself on top of simpler layers. It's giving DIY era. πŸ› οΈ

Also super important: unlike the lower level text display routines, every single function down here operates on character-oriented lengths and indices, not byte-oriented values. Confused about converting between character and byte offsets? Check the description of Tcl_UtfAtIndex, it explains it. πŸ“–

These routines are built on top of the programming interface described in the Tk_MeasureChars documentation. Tab characters and newline/return characters may be treated specially by these procedures, but every other character just gets passed straight through to the lower level. No notes. πŸ’…


🌸 Tk_ComputeTextLayout

Tk_ComputeTextLayout computes the layout information you need to display a single-font, multi-line, justified string of text, and it hands you back a Tk_TextLayout token that holds all that info. πŸŽ€

That token is what you use in all the follow-up calls like Tk_DrawTextLayout, Tk_DistanceToTextLayout, and Tk_FreeTextLayout. Heads up though β€” the string and the tkfont you used when computing the layout must stay valid for the entire lifetime of that token. Like, do not let them wander off. 🫢

πŸ¦‹ Tk_FreeTextLayout

Tk_FreeTextLayout is the one you call to release the storage associated with layout when you're done with it.

⚠️ Once a layout has been released, do not use it in any other text layout procedure. It's over. It's done. Leave it alone. πŸ’€

🌈 Tk_DrawTextLayout

Tk_DrawTextLayout uses the info in layout to actually display a single-font, multi-line, justified string of text at the location you specify. The main character moment. πŸ‘‘

✨ Tk_UnderlineTextLayout

Tk_UnderlineTextLayout uses the info in layout to draw an underline underneath one individual character. Key detail: this procedure does not draw the text, just the underline. That's it. Just the lil line. πŸŽ€

If you want natively underlined text, you should construct an underlined font and use that instead. This method is the workaround lane.

All characters β€” including tabs, newline/return characters, and spaces at the ends of lines β€” can be underlined using this method. But (and this is a big but):

  • the underline will never be drawn outside of the computed width of layout
  • the underline will stop at the edge for any character that would extend partially outside of layout
  • and the underline will not be visible at all for any character that would be located completely outside of the layout

So it's giving "clipped at the border," not "spilling out." 🚧

πŸ’œ Tk_PointToChar

Tk_PointToChar uses the info in layout to figure out which character is closest to a given point. πŸ“

The point is specified relative to the upper-left hand corner of the layout, which is considered to be located at (0, 0).

Here's how the edge cases behave:

  • any point whose y-value is less than 0 β†’ considered closest to the first character in the text layout
  • any point whose y-value is greater than the height of the text layout β†’ considered closest to the last character in the text layout
  • any point whose x-value is negative β†’ considered closest to the first character on that line
  • any point whose x-value is greater than the width of the text layout β†’ considered closest to the last character on that line

The return value is the index of the character that was closest to the point, or one more than the index of any character β€” that extra one signals that the point was after the end of the string, and that the corresponding caret would be at the end of the string. 🎯

And if you hand it a layout with no characters? The value 0 will always be returned, referring to a hypothetical zero-width placeholder character. Ghost character energy. πŸ‘»

🌸 Tk_CharBbox

Tk_CharBbox uses the info in layout to return the bounding box for the character specified by index.

  • the width of the bounding box is the advance width of the character, and does not include any left or right bearing
  • any character that extends partially outside of layout is considered to be truncated at the edge
  • any character that would be located completely outside of layout is considered to be zero-width and pegged against the edge
  • the height of the bounding box is the line height for this font, extending from the top of the ascent to the bottom of the descent β€” info about the actual height of individual letters is just not available, sorry bestie 😭

For measurement purposes, a layout that contains no characters is considered to contain a single zero-width placeholder character at index 0.

Return value rules, listen up:

  • if index was not a valid character index β†’ the return value is 0 and *xPtr, *yPtr, *widthPtr, and *heightPtr are unmodified
  • otherwise, if index did specify a valid one β†’ the return value is non-zero, and *xPtr, *yPtr, *widthPtr, and *heightPtr are filled with the bounding box information for the character
  • and if any of xPtr, yPtr, widthPtr, or heightPtr are NULL, that corresponding value is simply not calculated or stored 🫑

πŸ”₯ Tk_DistanceToTextLayout

Tk_DistanceToTextLayout computes the shortest distance in pixels from the given point (x, y) to the characters in layout. πŸ“

Newline/return characters and non-displaying space characters that occur at the end of individual lines in the text layout are ignored for hit detection purposes β€” but tab characters are not ignored. Tabs count. Always. πŸ’…

Return values:

  • 0 if the point actually hits the layout
  • otherwise, the distance in pixels from the point to the layout

⭐ Tk_IntersectTextLayout

Tk_IntersectTextLayout determines whether a layout lies entirely inside, entirely outside, or overlaps a given rectangle. πŸ“

Newline/return characters and non-displaying space characters that occur at the end of individual lines in the layout are ignored for intersection calculations.

Return values, and they're a whole mood board:

  • -1 β†’ the layout is entirely outside of the rectangle
  • 0 β†’ it overlaps
  • 1 β†’ it is entirely inside of the rectangle

🧚 Tk_TextLayoutToPostscript

Tk_TextLayoutToPostscript outputs code consisting of a Postscript array of strings that represent the individual lines in layout. πŸ“œ

It is the responsibility of the caller β€” that's you, bestie β€” to take that Postscript array of strings and add some Postscript function that operates on the array to render each of the lines. It does not do the rendering for you, it just sets you up.

The code that represents the Postscript array of strings is appended to interpreter interp's result. ✨


πŸ¦‹ DISPLAY MODEL πŸ¦‹

Okay so this is where it gets kinda juicy, pay attention. πŸ‘€

When measuring a text layout, space characters that occur at the end of a line are ignored. They still exist β€” the insertion point can still be positioned amongst them β€” but their additional width is ignored when justifying lines or returning the total width of a text layout. πŸ’…

All end-of-line space characters are considered to be attached to the right edge of the line. This behavior is logical for left-justified text and reasonable for center-justified text, but honestly not very useful when editing right-justified text. 🫠

Spaces are considered variable width characters:

  • the first space that extends past the edge of the text layout is clipped to the edge
  • any subsequent spaces on the line are considered zero width and pegged against the edge

Space characters that occur in the middle of a line of text are not suppressed and occupy their normal space width. Middle-of-the-line spaces are living their best life, unbothered. 🌸


Tab characters are not ignored for measurement calculations. And if wrapping is turned on and there are enough tabs on a line, the next tab will wrap to the beginning of the next line.

⚠️ Warning, bestie: there are some possible strange interactions between tabs and justification. Tab positions are calculated and the line length computed in a left-justified world, and then the whole resulting line is shifted so it's centered or right-justified β€” which causes the tab columns not to align any more. It's giving chaos. 😭

When wrapping is turned on, lines may wrap at word breaks (space or tab characters) or newline/returns.

  • a dash or hyphen character in the middle of a word is not considered a word break
  • Tk_ComputeTextLayout always attempts to place at least one word on each line
  • if it can't, because the wrapLength is too small, the word will be broken and as much as fits is placed on the line, with the rest on subsequent line(s)
  • if wrapLength is so small that not even one character can fit on a given line, wrapLength is ignored for that line and one character will be placed on the line anyhow. One character. Minimum. Periodt. πŸ’…
  • when wrapping is turned off, only newline/return characters may cause a line break

And one last lil thing πŸŽ€: when a text layout has been created using an underlined tkfont, then any space characters that occur at the end of individual lines, newlines/returns, and tabs will not be displayed underlined when Tk_DrawTextLayout is called. Why? Because those characters are never actually drawn β€” they are merely placeholders maintained in the layout. Placeholder era, iconic behavior. ✨


πŸ‘‘ KEYWORDS πŸ‘‘

  • font

And that's the whole page, bestie!! 🌈 This one comes to you from Tk, version 8.1, dated back in the day, and it's the Tk_ComputeTextLayout(3tk) manual page β€” so go forth, compute those layouts, and slay. πŸ’…πŸ’–πŸ¦‹

πŸ“¦ where this page comes from