blob: f913149c69bfb6e951295da31292afd12814d107 [file] [log] [blame]
Abhay Kumara61c5222025-11-10 07:32:50 +00001// Copyright 2014 Oleku Konko All rights reserved.
2// Use of this source code is governed by a MIT
3// license that can be found in the LICENSE file.
4
5// This module is a Table Writer API for the Go Programming Language.
6// The protocols were written in pure Go and works on windows and unix systems
7
8// Create & Generate text based table
9package tablewriter
10
11import (
12 "bytes"
13 "fmt"
14 "io"
15 "regexp"
16 "strings"
17)
18
19const (
20 MAX_ROW_WIDTH = 30
21)
22
23const (
24 CENTER = "+"
25 ROW = "-"
26 COLUMN = "|"
27 SPACE = " "
28 NEWLINE = "\n"
29)
30
31const (
32 ALIGN_DEFAULT = iota
33 ALIGN_CENTER
34 ALIGN_RIGHT
35 ALIGN_LEFT
36)
37
38var (
39 decimal = regexp.MustCompile(`^-?(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d+)?$`)
40 percent = regexp.MustCompile(`^-?\d+\.?\d*$%$`)
41)
42
43type Border struct {
44 Left bool
45 Right bool
46 Top bool
47 Bottom bool
48}
49
50type Table struct {
51 out io.Writer
52 rows [][]string
53 lines [][][]string
54 cs map[int]int
55 rs map[int]int
56 headers [][]string
57 footers [][]string
58 caption bool
59 captionText string
60 autoFmt bool
61 autoWrap bool
62 reflowText bool
63 mW int
64 pCenter string
65 pRow string
66 pColumn string
67 tColumn int
68 tRow int
69 hAlign int
70 fAlign int
71 align int
72 newLine string
73 rowLine bool
74 autoMergeCells bool
75 columnsToAutoMergeCells map[int]bool
76 noWhiteSpace bool
77 tablePadding string
78 hdrLine bool
79 borders Border
80 colSize int
81 headerParams []string
82 columnsParams []string
83 footerParams []string
84 columnsAlign []int
85}
86
87// Start New Table
88// Take io.Writer Directly
89func NewWriter(writer io.Writer) *Table {
90 t := &Table{
91 out: writer,
92 rows: [][]string{},
93 lines: [][][]string{},
94 cs: make(map[int]int),
95 rs: make(map[int]int),
96 headers: [][]string{},
97 footers: [][]string{},
98 caption: false,
99 captionText: "Table caption.",
100 autoFmt: true,
101 autoWrap: true,
102 reflowText: true,
103 mW: MAX_ROW_WIDTH,
104 pCenter: CENTER,
105 pRow: ROW,
106 pColumn: COLUMN,
107 tColumn: -1,
108 tRow: -1,
109 hAlign: ALIGN_DEFAULT,
110 fAlign: ALIGN_DEFAULT,
111 align: ALIGN_DEFAULT,
112 newLine: NEWLINE,
113 rowLine: false,
114 hdrLine: true,
115 borders: Border{Left: true, Right: true, Bottom: true, Top: true},
116 colSize: -1,
117 headerParams: []string{},
118 columnsParams: []string{},
119 footerParams: []string{},
120 columnsAlign: []int{}}
121 return t
122}
123
124// Render table output
125func (t *Table) Render() {
126 if t.borders.Top {
127 t.printLine(true)
128 }
129 t.printHeading()
130 if t.autoMergeCells {
131 t.printRowsMergeCells()
132 } else {
133 t.printRows()
134 }
135 if !t.rowLine && t.borders.Bottom {
136 t.printLine(true)
137 }
138 t.printFooter()
139
140 if t.caption {
141 t.printCaption()
142 }
143}
144
145const (
146 headerRowIdx = -1
147 footerRowIdx = -2
148)
149
150// Set table header
151func (t *Table) SetHeader(keys []string) {
152 t.colSize = len(keys)
153 for i, v := range keys {
154 lines := t.parseDimension(v, i, headerRowIdx)
155 t.headers = append(t.headers, lines)
156 }
157}
158
159// Set table Footer
160func (t *Table) SetFooter(keys []string) {
161 //t.colSize = len(keys)
162 for i, v := range keys {
163 lines := t.parseDimension(v, i, footerRowIdx)
164 t.footers = append(t.footers, lines)
165 }
166}
167
168// Set table Caption
169func (t *Table) SetCaption(caption bool, captionText ...string) {
170 t.caption = caption
171 if len(captionText) == 1 {
172 t.captionText = captionText[0]
173 }
174}
175
176// Turn header autoformatting on/off. Default is on (true).
177func (t *Table) SetAutoFormatHeaders(auto bool) {
178 t.autoFmt = auto
179}
180
181// Turn automatic multiline text adjustment on/off. Default is on (true).
182func (t *Table) SetAutoWrapText(auto bool) {
183 t.autoWrap = auto
184}
185
186// Turn automatic reflowing of multiline text when rewrapping. Default is on (true).
187func (t *Table) SetReflowDuringAutoWrap(auto bool) {
188 t.reflowText = auto
189}
190
191// Set the Default column width
192func (t *Table) SetColWidth(width int) {
193 t.mW = width
194}
195
196// Set the minimal width for a column
197func (t *Table) SetColMinWidth(column int, width int) {
198 t.cs[column] = width
199}
200
201// Set the Column Separator
202func (t *Table) SetColumnSeparator(sep string) {
203 t.pColumn = sep
204}
205
206// Set the Row Separator
207func (t *Table) SetRowSeparator(sep string) {
208 t.pRow = sep
209}
210
211// Set the center Separator
212func (t *Table) SetCenterSeparator(sep string) {
213 t.pCenter = sep
214}
215
216// Set Header Alignment
217func (t *Table) SetHeaderAlignment(hAlign int) {
218 t.hAlign = hAlign
219}
220
221// Set Footer Alignment
222func (t *Table) SetFooterAlignment(fAlign int) {
223 t.fAlign = fAlign
224}
225
226// Set Table Alignment
227func (t *Table) SetAlignment(align int) {
228 t.align = align
229}
230
231// Set No White Space
232func (t *Table) SetNoWhiteSpace(allow bool) {
233 t.noWhiteSpace = allow
234}
235
236// Set Table Padding
237func (t *Table) SetTablePadding(padding string) {
238 t.tablePadding = padding
239}
240
241func (t *Table) SetColumnAlignment(keys []int) {
242 for _, v := range keys {
243 switch v {
244 case ALIGN_CENTER:
245 break
246 case ALIGN_LEFT:
247 break
248 case ALIGN_RIGHT:
249 break
250 default:
251 v = ALIGN_DEFAULT
252 }
253 t.columnsAlign = append(t.columnsAlign, v)
254 }
255}
256
257// Set New Line
258func (t *Table) SetNewLine(nl string) {
259 t.newLine = nl
260}
261
262// Set Header Line
263// This would enable / disable a line after the header
264func (t *Table) SetHeaderLine(line bool) {
265 t.hdrLine = line
266}
267
268// Set Row Line
269// This would enable / disable a line on each row of the table
270func (t *Table) SetRowLine(line bool) {
271 t.rowLine = line
272}
273
274// Set Auto Merge Cells
275// This would enable / disable the merge of cells with identical values
276func (t *Table) SetAutoMergeCells(auto bool) {
277 t.autoMergeCells = auto
278}
279
280// Set Auto Merge Cells By Column Index
281// This would enable / disable the merge of cells with identical values for specific columns
282// If cols is empty, it is the same as `SetAutoMergeCells(true)`.
283func (t *Table) SetAutoMergeCellsByColumnIndex(cols []int) {
284 t.autoMergeCells = true
285
286 if len(cols) > 0 {
287 m := make(map[int]bool)
288 for _, col := range cols {
289 m[col] = true
290 }
291 t.columnsToAutoMergeCells = m
292 }
293}
294
295// Set Table Border
296// This would enable / disable line around the table
297func (t *Table) SetBorder(border bool) {
298 t.SetBorders(Border{border, border, border, border})
299}
300
301func (t *Table) SetBorders(border Border) {
302 t.borders = border
303}
304
305// Append row to table
306func (t *Table) Append(row []string) {
307 rowSize := len(t.headers)
308 if rowSize > t.colSize {
309 t.colSize = rowSize
310 }
311
312 n := len(t.lines)
313 line := [][]string{}
314 for i, v := range row {
315
316 // Detect string width
317 // Detect String height
318 // Break strings into words
319 out := t.parseDimension(v, i, n)
320
321 // Append broken words
322 line = append(line, out)
323 }
324 t.lines = append(t.lines, line)
325}
326
327// Append row to table with color attributes
328func (t *Table) Rich(row []string, colors []Colors) {
329 rowSize := len(t.headers)
330 if rowSize > t.colSize {
331 t.colSize = rowSize
332 }
333
334 n := len(t.lines)
335 line := [][]string{}
336 for i, v := range row {
337
338 // Detect string width
339 // Detect String height
340 // Break strings into words
341 out := t.parseDimension(v, i, n)
342
343 if len(colors) > i {
344 color := colors[i]
345 out[0] = format(out[0], color)
346 }
347
348 // Append broken words
349 line = append(line, out)
350 }
351 t.lines = append(t.lines, line)
352}
353
354// Allow Support for Bulk Append
355// Eliminates repeated for loops
356func (t *Table) AppendBulk(rows [][]string) {
357 for _, row := range rows {
358 t.Append(row)
359 }
360}
361
362// NumLines to get the number of lines
363func (t *Table) NumLines() int {
364 return len(t.lines)
365}
366
367// Clear rows
368func (t *Table) ClearRows() {
369 t.lines = [][][]string{}
370}
371
372// Clear footer
373func (t *Table) ClearFooter() {
374 t.footers = [][]string{}
375}
376
377// Center based on position and border.
378func (t *Table) center(i int) string {
379 if i == -1 && !t.borders.Left {
380 return t.pRow
381 }
382
383 if i == len(t.cs)-1 && !t.borders.Right {
384 return t.pRow
385 }
386
387 return t.pCenter
388}
389
390// Print line based on row width
391func (t *Table) printLine(nl bool) {
392 fmt.Fprint(t.out, t.center(-1))
393 for i := 0; i < len(t.cs); i++ {
394 v := t.cs[i]
395 fmt.Fprintf(t.out, "%s%s%s%s",
396 t.pRow,
397 strings.Repeat(string(t.pRow), v),
398 t.pRow,
399 t.center(i))
400 }
401 if nl {
402 fmt.Fprint(t.out, t.newLine)
403 }
404}
405
406// Print line based on row width with our without cell separator
407func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
408 fmt.Fprint(t.out, t.pCenter)
409 for i := 0; i < len(t.cs); i++ {
410 v := t.cs[i]
411 if i > len(displayCellSeparator) || displayCellSeparator[i] {
412 // Display the cell separator
413 fmt.Fprintf(t.out, "%s%s%s%s",
414 t.pRow,
415 strings.Repeat(string(t.pRow), v),
416 t.pRow,
417 t.pCenter)
418 } else {
419 // Don't display the cell separator for this cell
420 fmt.Fprintf(t.out, "%s%s",
421 strings.Repeat(" ", v+2),
422 t.pCenter)
423 }
424 }
425 if nl {
426 fmt.Fprint(t.out, t.newLine)
427 }
428}
429
430// Return the PadRight function if align is left, PadLeft if align is right,
431// and Pad by default
432func pad(align int) func(string, string, int) string {
433 padFunc := Pad
434 switch align {
435 case ALIGN_LEFT:
436 padFunc = PadRight
437 case ALIGN_RIGHT:
438 padFunc = PadLeft
439 }
440 return padFunc
441}
442
443// Print heading information
444func (t *Table) printHeading() {
445 // Check if headers is available
446 if len(t.headers) < 1 {
447 return
448 }
449
450 // Identify last column
451 end := len(t.cs) - 1
452
453 // Get pad function
454 padFunc := pad(t.hAlign)
455
456 // Checking for ANSI escape sequences for header
457 is_esc_seq := false
458 if len(t.headerParams) > 0 {
459 is_esc_seq = true
460 }
461
462 // Maximum height.
463 max := t.rs[headerRowIdx]
464
465 // Print Heading
466 for x := 0; x < max; x++ {
467 // Check if border is set
468 // Replace with space if not set
469 if !t.noWhiteSpace {
470 fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
471 }
472
473 for y := 0; y <= end; y++ {
474 v := t.cs[y]
475 h := ""
476
477 if y < len(t.headers) && x < len(t.headers[y]) {
478 h = t.headers[y][x]
479 }
480 if t.autoFmt {
481 h = Title(h)
482 }
483 pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn)
484 if t.noWhiteSpace {
485 pad = ConditionString((y == end && !t.borders.Left), SPACE, t.tablePadding)
486 }
487 if is_esc_seq {
488 if !t.noWhiteSpace {
489 fmt.Fprintf(t.out, " %s %s",
490 format(padFunc(h, SPACE, v),
491 t.headerParams[y]), pad)
492 } else {
493 fmt.Fprintf(t.out, "%s %s",
494 format(padFunc(h, SPACE, v),
495 t.headerParams[y]), pad)
496 }
497 } else {
498 if !t.noWhiteSpace {
499 fmt.Fprintf(t.out, " %s %s",
500 padFunc(h, SPACE, v),
501 pad)
502 } else {
503 // the spaces between breaks the kube formatting
504 fmt.Fprintf(t.out, "%s%s",
505 padFunc(h, SPACE, v),
506 pad)
507 }
508 }
509 }
510 // Next line
511 fmt.Fprint(t.out, t.newLine)
512 }
513 if t.hdrLine {
514 t.printLine(true)
515 }
516}
517
518// Print heading information
519func (t *Table) printFooter() {
520 // Check if headers is available
521 if len(t.footers) < 1 {
522 return
523 }
524
525 // Only print line if border is not set
526 if !t.borders.Bottom {
527 t.printLine(true)
528 }
529
530 // Identify last column
531 end := len(t.cs) - 1
532
533 // Get pad function
534 padFunc := pad(t.fAlign)
535
536 // Checking for ANSI escape sequences for header
537 is_esc_seq := false
538 if len(t.footerParams) > 0 {
539 is_esc_seq = true
540 }
541
542 // Maximum height.
543 max := t.rs[footerRowIdx]
544
545 // Print Footer
546 erasePad := make([]bool, len(t.footers))
547 for x := 0; x < max; x++ {
548 // Check if border is set
549 // Replace with space if not set
550 fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
551
552 for y := 0; y <= end; y++ {
553 v := t.cs[y]
554 f := ""
555 if y < len(t.footers) && x < len(t.footers[y]) {
556 f = t.footers[y][x]
557 }
558 if t.autoFmt {
559 f = Title(f)
560 }
561 pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn)
562
563 if erasePad[y] || (x == 0 && len(f) == 0) {
564 pad = SPACE
565 erasePad[y] = true
566 }
567
568 if is_esc_seq {
569 fmt.Fprintf(t.out, " %s %s",
570 format(padFunc(f, SPACE, v),
571 t.footerParams[y]), pad)
572 } else {
573 fmt.Fprintf(t.out, " %s %s",
574 padFunc(f, SPACE, v),
575 pad)
576 }
577
578 //fmt.Fprintf(t.out, " %s %s",
579 // padFunc(f, SPACE, v),
580 // pad)
581 }
582 // Next line
583 fmt.Fprint(t.out, t.newLine)
584 //t.printLine(true)
585 }
586
587 hasPrinted := false
588
589 for i := 0; i <= end; i++ {
590 v := t.cs[i]
591 pad := t.pRow
592 center := t.pCenter
593 length := len(t.footers[i][0])
594
595 if length > 0 {
596 hasPrinted = true
597 }
598
599 // Set center to be space if length is 0
600 if length == 0 && !t.borders.Right {
601 center = SPACE
602 }
603
604 // Print first junction
605 if i == 0 {
606 if length > 0 && !t.borders.Left {
607 center = t.pRow
608 }
609 fmt.Fprint(t.out, center)
610 }
611
612 // Pad With space of length is 0
613 if length == 0 {
614 pad = SPACE
615 }
616 // Ignore left space as it has printed before
617 if hasPrinted || t.borders.Left {
618 pad = t.pRow
619 center = t.pCenter
620 }
621
622 // Change Center end position
623 if center != SPACE {
624 if i == end && !t.borders.Right {
625 center = t.pRow
626 }
627 }
628
629 // Change Center start position
630 if center == SPACE {
631 if i < end && len(t.footers[i+1][0]) != 0 {
632 if !t.borders.Left {
633 center = t.pRow
634 } else {
635 center = t.pCenter
636 }
637 }
638 }
639
640 // Print the footer
641 fmt.Fprintf(t.out, "%s%s%s%s",
642 pad,
643 strings.Repeat(string(pad), v),
644 pad,
645 center)
646
647 }
648
649 fmt.Fprint(t.out, t.newLine)
650}
651
652// Print caption text
653func (t Table) printCaption() {
654 width := t.getTableWidth()
655 paragraph, _ := WrapString(t.captionText, width)
656 for linecount := 0; linecount < len(paragraph); linecount++ {
657 fmt.Fprintln(t.out, paragraph[linecount])
658 }
659}
660
661// Calculate the total number of characters in a row
662func (t Table) getTableWidth() int {
663 var chars int
664 for _, v := range t.cs {
665 chars += v
666 }
667
668 // Add chars, spaces, seperators to calculate the total width of the table.
669 // ncols := t.colSize
670 // spaces := ncols * 2
671 // seps := ncols + 1
672
673 return (chars + (3 * t.colSize) + 2)
674}
675
676func (t Table) printRows() {
677 for i, lines := range t.lines {
678 t.printRow(lines, i)
679 }
680}
681
682func (t *Table) fillAlignment(num int) {
683 if len(t.columnsAlign) < num {
684 t.columnsAlign = make([]int, num)
685 for i := range t.columnsAlign {
686 t.columnsAlign[i] = t.align
687 }
688 }
689}
690
691// Print Row Information
692// Adjust column alignment based on type
693
694func (t *Table) printRow(columns [][]string, rowIdx int) {
695 // Get Maximum Height
696 max := t.rs[rowIdx]
697 total := len(columns)
698
699 // TODO Fix uneven col size
700 // if total < t.colSize {
701 // for n := t.colSize - total; n < t.colSize ; n++ {
702 // columns = append(columns, []string{SPACE})
703 // t.cs[n] = t.mW
704 // }
705 //}
706
707 // Pad Each Height
708 pads := []int{}
709
710 // Checking for ANSI escape sequences for columns
711 is_esc_seq := false
712 if len(t.columnsParams) > 0 {
713 is_esc_seq = true
714 }
715 t.fillAlignment(total)
716
717 for i, line := range columns {
718 length := len(line)
719 pad := max - length
720 pads = append(pads, pad)
721 for n := 0; n < pad; n++ {
722 columns[i] = append(columns[i], " ")
723 }
724 }
725 //fmt.Println(max, "\n")
726 for x := 0; x < max; x++ {
727 for y := 0; y < total; y++ {
728
729 // Check if border is set
730 if !t.noWhiteSpace {
731 fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
732 fmt.Fprintf(t.out, SPACE)
733 }
734
735 str := columns[y][x]
736
737 // Embedding escape sequence with column value
738 if is_esc_seq {
739 str = format(str, t.columnsParams[y])
740 }
741
742 // This would print alignment
743 // Default alignment would use multiple configuration
744 switch t.columnsAlign[y] {
745 case ALIGN_CENTER: //
746 fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
747 case ALIGN_RIGHT:
748 fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
749 case ALIGN_LEFT:
750 fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
751 default:
752 if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
753 fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
754 } else {
755 fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
756
757 // TODO Custom alignment per column
758 //if max == 1 || pads[y] > 0 {
759 // fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
760 //} else {
761 // fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
762 //}
763
764 }
765 }
766 if !t.noWhiteSpace {
767 fmt.Fprintf(t.out, SPACE)
768 } else {
769 fmt.Fprintf(t.out, t.tablePadding)
770 }
771 }
772 // Check if border is set
773 // Replace with space if not set
774 if !t.noWhiteSpace {
775 fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
776 }
777 fmt.Fprint(t.out, t.newLine)
778 }
779
780 if t.rowLine {
781 t.printLine(true)
782 }
783}
784
785// Print the rows of the table and merge the cells that are identical
786func (t *Table) printRowsMergeCells() {
787 var previousLine []string
788 var displayCellBorder []bool
789 var tmpWriter bytes.Buffer
790 for i, lines := range t.lines {
791 // We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
792 previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
793 if i > 0 { //We don't need to print borders above first line
794 if t.rowLine {
795 t.printLineOptionalCellSeparators(true, displayCellBorder)
796 }
797 }
798 tmpWriter.WriteTo(t.out)
799 }
800 //Print the end of the table
801 if t.rowLine {
802 t.printLine(true)
803 }
804}
805
806// Print Row Information to a writer and merge identical cells.
807// Adjust column alignment based on type
808
809func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) {
810 // Get Maximum Height
811 max := t.rs[rowIdx]
812 total := len(columns)
813
814 // Pad Each Height
815 pads := []int{}
816
817 // Checking for ANSI escape sequences for columns
818 is_esc_seq := false
819 if len(t.columnsParams) > 0 {
820 is_esc_seq = true
821 }
822 for i, line := range columns {
823 length := len(line)
824 pad := max - length
825 pads = append(pads, pad)
826 for n := 0; n < pad; n++ {
827 columns[i] = append(columns[i], " ")
828 }
829 }
830
831 var displayCellBorder []bool
832 t.fillAlignment(total)
833 for x := 0; x < max; x++ {
834 for y := 0; y < total; y++ {
835
836 // Check if border is set
837 fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
838
839 fmt.Fprintf(writer, SPACE)
840
841 str := columns[y][x]
842
843 // Embedding escape sequence with column value
844 if is_esc_seq {
845 str = format(str, t.columnsParams[y])
846 }
847
848 if t.autoMergeCells {
849 var mergeCell bool
850 if t.columnsToAutoMergeCells != nil {
851 // Check to see if the column index is in columnsToAutoMergeCells.
852 if t.columnsToAutoMergeCells[y] {
853 mergeCell = true
854 }
855 } else {
856 // columnsToAutoMergeCells was not set.
857 mergeCell = true
858 }
859 //Store the full line to merge mutli-lines cells
860 fullLine := strings.TrimRight(strings.Join(columns[y], " "), " ")
861 if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" && mergeCell {
862 // If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
863 displayCellBorder = append(displayCellBorder, false)
864 str = ""
865 } else {
866 // First line or different content, keep the content and print the cell border
867 displayCellBorder = append(displayCellBorder, true)
868 }
869 }
870
871 // This would print alignment
872 // Default alignment would use multiple configuration
873 switch t.columnsAlign[y] {
874 case ALIGN_CENTER: //
875 fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
876 case ALIGN_RIGHT:
877 fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
878 case ALIGN_LEFT:
879 fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
880 default:
881 if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
882 fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
883 } else {
884 fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
885 }
886 }
887 fmt.Fprintf(writer, SPACE)
888 }
889 // Check if border is set
890 // Replace with space if not set
891 fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
892 fmt.Fprint(writer, t.newLine)
893 }
894
895 //The new previous line is the current one
896 previousLine = make([]string, total)
897 for y := 0; y < total; y++ {
898 previousLine[y] = strings.TrimRight(strings.Join(columns[y], " "), " ") //Store the full line for multi-lines cells
899 }
900 //Returns the newly added line and wether or not a border should be displayed above.
901 return previousLine, displayCellBorder
902}
903
904func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
905 var (
906 raw []string
907 maxWidth int
908 )
909
910 raw = getLines(str)
911 maxWidth = 0
912 for _, line := range raw {
913 if w := DisplayWidth(line); w > maxWidth {
914 maxWidth = w
915 }
916 }
917
918 // If wrapping, ensure that all paragraphs in the cell fit in the
919 // specified width.
920 if t.autoWrap {
921 // If there's a maximum allowed width for wrapping, use that.
922 if maxWidth > t.mW {
923 maxWidth = t.mW
924 }
925
926 // In the process of doing so, we need to recompute maxWidth. This
927 // is because perhaps a word in the cell is longer than the
928 // allowed maximum width in t.mW.
929 newMaxWidth := maxWidth
930 newRaw := make([]string, 0, len(raw))
931
932 if t.reflowText {
933 // Make a single paragraph of everything.
934 raw = []string{strings.Join(raw, " ")}
935 }
936 for i, para := range raw {
937 paraLines, _ := WrapString(para, maxWidth)
938 for _, line := range paraLines {
939 if w := DisplayWidth(line); w > newMaxWidth {
940 newMaxWidth = w
941 }
942 }
943 if i > 0 {
944 newRaw = append(newRaw, " ")
945 }
946 newRaw = append(newRaw, paraLines...)
947 }
948 raw = newRaw
949 maxWidth = newMaxWidth
950 }
951
952 // Store the new known maximum width.
953 v, ok := t.cs[colKey]
954 if !ok || v < maxWidth || v == 0 {
955 t.cs[colKey] = maxWidth
956 }
957
958 // Remember the number of lines for the row printer.
959 h := len(raw)
960 v, ok = t.rs[rowKey]
961
962 if !ok || v < h || v == 0 {
963 t.rs[rowKey] = h
964 }
965 //fmt.Printf("Raw %+v %d\n", raw, len(raw))
966 return raw
967}