$f->big($f->green("This text is big and green!")) . $f->br() . $f->bold($f->red("This text is red and bold")) . $f->br();
While this is more in-line with good programming practices, it is the solution I wanted to avoid in the first place. Compared to what I have at the moment, this solution has two flaws:
1) More verbose, but then it compensates for this by not having to worry about closing tags, which is a really good idea I haven't considered (and would hate it for table rows). Hm, actually $bbig . $bgreen . "This text is big and green!" . $ecolor . $ebig . $n . $bbold . $bred . "This text is red and bold" . $ecolor . $ebold . $n is longer and I guess I could live with the object syntax.
2) My way has some magic action at a distance (which is usually a bad thing, except I know exactly how it works, so that makes it OK, right?). With OO solution, I'd still need some factory and a my variable hidden in the output module to hold the type of output I need, so each function would begin with my $fmt = get_fmt;.
Basically I just call set_output when processing program options, then use those magic variables everywhere and not care what their actual content is. This way my concatenated strings look the same, which I like for it's simplicity. I just have to never ever write to those varables outside of that one file. With your way, I guess they would look the same too.
3) I have one smallish file that takes care of output formatting. It is stupid and rather easy to edit. Your solution is a maze of a lot of small files. I do not like that.
I didn't want to have too many variables, so I skipped html table title row, LaTeX output and some other stuff. With OO way, maybe I would be less inclined to cut corners.
For an even more proper solution, there would need to be a function that formats a table in a standard array of hashes (or complex stuff), but when I tried it, it quickly became so complex that I decided it's easier if each module builds it's own table. It eventually came down to having different classes for various types of table cells that know how to ToString() themselves.
Cocnlusion: Meh, I think the object way would work fine after all, but I'll use mine out of habbit.
Ugh, I've edited this several times and is still as messy as my code. I may be too tired.