All pastes #239974 Raw Edit

Anonymous

public text v1 · immutable
·published 2006-11-05 15:44 UTC
rendered paste body
/*
Functions to format text into lines of a specified length.

Copyright (C) 2006 Jussi Peltola <pelzi@pelzi.net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

int find_line_end(const std::string& message, std::string::size_type start, int screen_width);

int calculate_height(const std::string& message, int screen_width, int indent)
{
    /* Calculates the amount of lines format will break message into. */
    assert(screen_width > 0);

    if (screen_width - indent < 4)
        indent = 0;

    std::string::size_type i = 0;   /* The current character */
    int lines = 0;          /* Line count */

    while (i < message.length())
    {
        /* Find out where this line ends. */
        if (i == 0) /* The first line is not indented. */
            i = find_line_end(message, i, screen_width);
        else
            i = find_line_end(message, i, screen_width - indent);
        lines++;

        i++; /* The beginning of the new line is one after the end of the previous. */

        /* Skip whitespace for the next line */
        for (; i < message.length() && isspace(message[i]); i++);

        /* If the previous loop got to the end, the rest is just whitespace
         * The while condition takes care of that. */
    }
    return lines;
}

int find_line_end(const std::string& message, std::string::size_type start, int screen_width)
{
    /* Helper function.
     * Finds the position of the last character which is the last character of a word
     * that fits in its entirety in screen_width.
     */

    assert(screen_width > 0);
    assert(start < message.length());

    /* Is the tail shorter than or equal to the width? */
    if (start + screen_width >= message.length())
        return message.length()-1;

    std::string::size_type i = -1;
    const std::string::size_type too_long_word_length = screen_width / 2;

    /* The tail is longer than the width, so message[start+screen_width] is a valid offset.
     * If message[start+screen_width], which is the character after the maximum length of the line
     * is whitespace, this loop does nothing, since the whole line fits.
     * If there is a word there, the loop will go back until it sees whitespace.
     * However, there is also the possibility of very long words.
     * If the following moves closer than screen_width/2 to start, it is reasonable to forcibly
     * break the line.
     */
    for (i = start+screen_width; i - start > too_long_word_length && !isspace(message[i]); i--);

    /* If the last word was too long, forcibly break it */
    if (i - start <= too_long_word_length)
        return start+screen_width-1;

    /* Ignore trailing whitespace */
    for (; isspace(message[i]); i--);

    /* i now points at the last character. */
    return i;
}


int main()
{
    std::string msg = "aaaa bb cc dd ee ff gggg";
    std::cout << msg.length() << std::endl;
    int h = calculate_height(msg, 4, 2);
    std::cout << h << std::endl;

    msg = "aaaa bbcc ddeeff gggg";
    h = calculate_height(msg, 5, 3);
    std::cout << h << std::endl;
    h = calculate_height("aaaaaaaaaaabbbbbbbbbbbbbbcccccccccc", 5, 3);
    std::cout << h << std::endl;
}