Function: window-text-pixel-size
window-text-pixel-size is a function defined in xdisp.c.
Signature
(window-text-pixel-size &optional WINDOW FROM TO X-LIMIT Y-LIMIT MODE-LINES)
Documentation
Return the size of the text of WINDOW's buffer in pixels.
WINDOW must be a live window and defaults to the selected one. The return value is a cons of the maximum pixel-width of any text line and the pixel-height of all the text lines in the accessible portion of buffer text.
This function exists to allow Lisp programs to adjust the dimensions of WINDOW to the buffer text it needs to display.
The optional argument FROM, if non-nil, specifies the first text position to consider, and defaults to the minimum accessible position of the buffer. If FROM is t, it stands for the minimum accessible position that starts a non-empty line. TO, if non-nil, specifies the last text position and defaults to the maximum accessible position of the buffer. If TO is t, it stands for the maximum accessible position that ends a non-empty line.
The optional argument X-LIMIT, if non-nil, specifies the maximum X coordinate beyond which the text should be ignored. It is therefore also the maximum width that the function can return. X-LIMIT nil or omitted means to use the pixel-width of WINDOW's body. This default means text of truncated lines wider than the window will be ignored; specify a large value for X-LIMIT if lines are truncated and you need to account for the truncated text. Use nil for X-LIMIT if you want to know how high WINDOW should become in order to fit all of its buffer's text with the width of WINDOW unaltered. Use the maximum width WINDOW may assume if you intend to change WINDOW's width. Since calculating the width of long lines can take some time, it's always a good idea to make this argument as small as possible; in particular, if the buffer contains long lines that shall be truncated anyway.
The optional argument Y-LIMIT, if non-nil, specifies the maximum Y coordinate beyond which the text is to be ignored; it is therefore also the maximum height that the function can return (excluding the height of the mode- or header-line, if any). Y-LIMIT nil or omitted means consider all of the accessible portion of buffer text up to the position specified by TO. Since calculating the text height of a large buffer can take some time, it makes sense to specify this argument if the size of the buffer is large or unknown.
Optional argument MODE-LINES nil or omitted means do not include the
height of the mode-, tab- or header-line of WINDOW in the return value.
If it is the symbol mode-line, 'tab-line' or header-line, include
only the height of that line, if present, in the return value. If t,
include the height of any of these, if present, in the return value.
Probably introduced at or before Emacs version 24.4.
Source Code
// Defined in /usr/src/emacs/src/xdisp.c
// Skipping highlighting due to helpful-max-highlight.
{
struct window *w = decode_live_window (window);
Lisp_Object buffer = w->contents;
struct buffer *b;
struct it it;
struct buffer *old_b = NULL;
ptrdiff_t start, end, bpos;
struct text_pos startp;
void *itdata = NULL;
int c, max_x = 0, max_y = 0, x = 0, y = 0;
CHECK_BUFFER (buffer);
b = XBUFFER (buffer);
if (b != current_buffer)
{
old_b = current_buffer;
set_buffer_internal (b);
}
if (NILP (from))
{
start = BEGV;
bpos = BEGV_BYTE;
}
else if (EQ (from, Qt))
{
start = BEGV;
bpos = BEGV_BYTE;
while (bpos < ZV_BYTE)
{
c = FETCH_BYTE (bpos);
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r'))
break;
inc_both (&start, &bpos);
}
while (bpos > BEGV_BYTE)
{
dec_both (&start, &bpos);
c = FETCH_BYTE (bpos);
if (!(c == ' ' || c == '\t'))
break;
}
}
else
{
start = clip_to_bounds (BEGV, fix_position (from), ZV);
bpos = CHAR_TO_BYTE (start);
}
SET_TEXT_POS (startp, start, bpos);
if (NILP (to))
end = ZV;
else if (EQ (to, Qt))
{
end = ZV;
bpos = ZV_BYTE;
while (bpos > BEGV_BYTE)
{
dec_both (&end, &bpos);
c = FETCH_BYTE (bpos);
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r'))
{
inc_both (&end, &bpos);
break;
}
}
while (bpos < ZV_BYTE)
{
c = fetch_char_advance (&end, &bpos);
if (!(c == ' ' || c == '\t'))
break;
}
}
else
end = clip_to_bounds (start, fix_position (to), ZV);
if (!NILP (x_limit) && RANGED_FIXNUMP (0, x_limit, INT_MAX))
max_x = XFIXNUM (x_limit);
if (NILP (y_limit))
max_y = INT_MAX;
else if (RANGED_FIXNUMP (0, y_limit, INT_MAX))
max_y = XFIXNUM (y_limit);
itdata = bidi_shelve_cache ();
start_display (&it, w, startp);
int start_y = it.current_y;
/* It makes no sense to measure dimensions of region of text that
crosses the point where bidi reordering changes scan direction.
By using unidirectional movement here we at least support the use
case of measuring regions of text that have a uniformly R2L
directionality, and regions that begin and end in text of the
same directionality. */
it.bidi_p = false;
/* Start at the beginning of the line containing FROM. Otherwise
IT.current_x will be incorrectly set to zero at some arbitrary
non-zero X coordinate. */
reseat_at_previous_visible_line_start (&it);
it.current_x = it.hpos = 0;
int start_x;
if (IT_CHARPOS (it) != start)
{
void *it1data = NULL;
struct it it1;
SAVE_IT (it1, it, it1data);
move_it_to (&it, start, -1, -1, -1, MOVE_TO_POS);
/* We could have a display property at START, in which case
asking move_it_to to stop at START will overshoot and stop at
position after START. So we try again, stopping before
START, and account for the width of the last buffer position
manually. */
if (IT_CHARPOS (it) > start && start > BEGV)
{
ptrdiff_t it1pos = IT_CHARPOS (it1);
int it1_x = it1.current_x;
RESTORE_IT (&it, &it1, it1data);
/* If START - 1 is the beginning of screen line, move_it_to
will not move, so we need to use a lower-level
move_it_in_display_line subroutine, and tell it to move
just 1 pixel, so it stops at the next display element. */
if (start - 1 > it1pos)
move_it_to (&it, start - 1, -1, -1, -1, MOVE_TO_POS);
else
move_it_in_display_line (&it, start, it1_x + 1,
MOVE_TO_POS | MOVE_TO_X);
start_x = it.current_x;
/* If we didn't change our buffer position, the pixel width
of what's here was not yet accounted for; do it manually. */
if (IT_CHARPOS (it) == start - 1)
start_x += it.pixel_width;
}
else
{
start_x = it.current_x;
bidi_unshelve_cache (it1data, true);
}
}
else
start_x = it.current_x;
/* Now move to TO. */
int move_op = MOVE_TO_POS | MOVE_TO_Y;
int to_x = -1;
it.current_y = start_y;
/* If FROM is on a newline, pretend that we start at the beginning
of the next line, because the newline takes no place on display. */
if (FETCH_BYTE (start) == '\n')
it.current_x = 0;
if (!NILP (x_limit))
{
it.last_visible_x = max_x;
/* Actually, we never want move_it_to stop at to_x. But to make
sure that move_it_in_display_line_to always moves far enough,
we set to_x to INT_MAX and specify MOVE_TO_X. */
move_op |= MOVE_TO_X;
to_x = INT_MAX;
}
void *it2data = NULL;
struct it it2;
SAVE_IT (it2, it, it2data);
x = move_it_to (&it, end, to_x, max_y, -1, move_op);
/* We could have a display property at END, in which case asking
move_it_to to stop at END will overshoot and stop at position
after END. So we try again, stopping before END, and account for
the width of the last buffer position manually. */
if (IT_CHARPOS (it) > end)
{
end--;
RESTORE_IT (&it, &it2, it2data);
x = move_it_to (&it, end, to_x, max_y, -1, move_op);
/* Add the width of the thing at TO, but only if we didn't
overshoot it; if we did, it is already accounted for. Also,
account for the height of the thing at TO. */
if (IT_CHARPOS (it) == end)
{
x += it.pixel_width;
it.max_ascent = max (it.max_ascent, it.ascent);
it.max_descent = max (it.max_descent, it.descent);
}
}
else
bidi_unshelve_cache (it2data, true);
if (!NILP (x_limit))
{
/* Don't return more than X-LIMIT. */
if (x > max_x)
x = max_x;
}
/* If text spans more than one screen line, we don't need to adjust
the x-span for start_x, since the second and subsequent lines
will begin at zero X coordinate. */
if (it.current_y > start_y)
start_x = 0;
/* Subtract height of header-line and tab-line which was counted
automatically by start_display. */
y = it.current_y + it.max_ascent + it.max_descent
- WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
/* Don't return more than Y-LIMIT. */
if (y > max_y)
y = max_y;
if (EQ (mode_lines, Qtab_line) || EQ (mode_lines, Qt))
/* Re-add height of tab-line as requested. */
y = y + WINDOW_TAB_LINE_HEIGHT (w);
if (EQ (mode_lines, Qheader_line) || EQ (mode_lines, Qt))
/* Re-add height of header-line as requested. */
y = y + WINDOW_HEADER_LINE_HEIGHT (w);
if (EQ (mode_lines, Qmode_line) || EQ (mode_lines, Qt))
/* Add height of mode-line as requested. */
y = y + WINDOW_MODE_LINE_HEIGHT (w);
bidi_unshelve_cache (itdata, false);
if (old_b)
set_buffer_internal (old_b);
return Fcons (make_fixnum (x - start_x), make_fixnum (y));
}