// Copyright 2014 Oleku Konko All rights reserved. // Use of this source code is governed by a MIT // license that can be found in the LICENSE file. // This module is a Table writer API for the Go Programming Language. // The protocols were written in pure Go and works on windows and unix systems package internal import ( "math" "strings" "unicode/utf8" ) var ( nl = "\n" sp = " " ) const defaultPenalty = 1e5 // WrapString wraps s into a paragraph of lines of length lim, with minimal // raggedness. func WrapString(s string, lim int) ([]string, int) { words := strings.Split(strings.Replace(strings.TrimSpace(s), nl, sp, -1), sp) var lines []string max := 0 for _, v := range words { max = len(v) if max > lim { lim = max } } for _, line := range WrapWords(words, 1, lim, defaultPenalty) { lines = append(lines, strings.Join(line, sp)) } return lines, lim } // WrapWords is the low-level line-breaking algorithm, useful if you need more // control over the details of the text wrapping process. For most uses, // WrapString will be sufficient and more convenient. // // WrapWords splits a list of words into lines with minimal "raggedness", // treating each rune as one unit, accounting for spc units between adjacent // words on each line, and attempting to limit lines to lim units. Raggedness // is the total error over all lines, where error is the square of the // difference of the length of the line and lim. Too-long lines (which only // happen when a single word is longer than lim units) have pen penalty units // added to the error. func WrapWords(words []string, spc, lim, pen int) [][]string { n := len(words) length := make([][]int, n) for i := 0; i < n; i++ { length[i] = make([]int, n) length[i][i] = utf8.RuneCountInString(words[i]) for j := i + 1; j < n; j++ { length[i][j] = length[i][j-1] + spc + utf8.RuneCountInString(words[j]) } } nbrk := make([]int, n) cost := make([]int, n) for i := range cost { cost[i] = math.MaxInt32 } for i := n - 1; i >= 0; i-- { if length[i][n-1] <= lim { cost[i] = 0 nbrk[i] = n } else { for j := i + 1; j < n; j++ { d := lim - length[i][j-1] c := d*d + cost[j] if length[i][j-1] > lim { c += pen // too-long lines get a worse penalty } if c < cost[i] { cost[i] = c nbrk[i] = j } } } } var lines [][]string i := 0 for i < n { lines = append(lines, words[i:nbrk[i]]) i = nbrk[i] } return lines } // getLines decomposes a multiline string into a slice of strings. func getLines(s string) []string { var lines []string for _, line := range strings.Split(strings.TrimSpace(s), nl) { lines = append(lines, line) } return lines }