├── .gitignore ├── COPYING ├── DEPENDENCIES ├── LICENSE ├── META-INF └── MANIFEST.MF ├── Makefile ├── README ├── cowsay2img ├── dist └── archlinux │ ├── bleeding │ └── PKGBUILD │ └── stable │ └── PKGBUILD ├── fdl.texinfo ├── img2cowsay ├── img2ponysay ├── img2unisay ├── lowcaps.linux.conf ├── lowcaps.rxvt.conf ├── lowcaps.tango.conf ├── lowcaps.xterm.conf ├── ponysay2img ├── ponysay2ttyponysay ├── ponytool ├── src └── se │ └── kth │ └── maandree │ └── utilsay │ ├── Cat.java │ ├── Colour.java │ ├── Common.java │ ├── Cowsay.java │ ├── Image.java │ ├── Pony.java │ ├── Ponysay.java │ ├── PonysayHaiku.java │ ├── PonysayLinux.java │ ├── PonysaySubmodule.java │ ├── PonysayXterm.java │ ├── Program.java │ ├── Raw.java │ ├── Test.java │ ├── Unisay.java │ └── imgsrcrecover.java ├── unisay2img └── util-say.texinfo /.gitignore: -------------------------------------------------------------------------------- 1 | ## Non-staging area ## 2 | ######################## 3 | 4 | _/ 5 | 6 | 7 | ## compiled files ## 8 | ###################### 9 | 10 | /bin/ 11 | /doc/javadoc/ 12 | *.class 13 | /*.jar 14 | /*.info 15 | /*.info.* 16 | /*.dvi 17 | /*.dvi.* 18 | /*.ps 19 | /*.ps.* 20 | /*.pdf 21 | /*.pdf.* 22 | 23 | 24 | ## backup files ## 25 | #################### 26 | 27 | *~ 28 | \#*\# 29 | .\#* 30 | *.swp 31 | *.bak 32 | 33 | 34 | ## distribution files ## 35 | ########################## 36 | 37 | /dist/*/*/* 38 | !/dist/*/*/PKGBUILD 39 | 40 | 41 | ## auxiliary files ## 42 | ####################### 43 | 44 | /*.aux 45 | /*.cp 46 | /*.cps 47 | /*.fn 48 | /*.ky 49 | /*.log 50 | /*.op 51 | /*.ops 52 | /*.pg 53 | /*.toc 54 | /*.tp 55 | /*.vr 56 | 57 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | util-say — Collection of tools for creating ponies for cowsay and cowsay-like programs 2 | 3 | Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 4 | 5 | util-say is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | util-say is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with util-say. If not, see . 17 | -------------------------------------------------------------------------------- /DEPENDENCIES: -------------------------------------------------------------------------------- 1 | # REQUIRED 2 | 3 | java-runtime>=6 4 | 5 | coreutils 6 | 7 | texinfo 8 | #for using the Makefile to generate "util-say.info.gz" || or if people type "make all" 9 | 10 | # OPTIONAL 11 | 12 | imagemagick 13 | # for bursting gif images 14 | 15 | perl 16 | # required for cowsay2unisay 17 | -------------------------------------------------------------------------------- /META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Created-By: Mattias Andrée 3 | Main-Class: se.kth.maandree.utilsay.Program 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX=/usr 2 | BIN=/bin 3 | DATA=/share 4 | 5 | JAR=jar 6 | JAVAC=javac 7 | 8 | SRC=$(shell find src | grep '\.java$$') 9 | OBJ=$(shell find src | grep '\.java$$' | sed -e 's_/[a-zA-Z]*\.java$$_/\*\.class_g' -e 's_^src/__g' | sort | uniq) 10 | 11 | all: util-say.jar info 12 | 13 | .PHONY: util-say.jar 14 | util-say.jar: 15 | "$(JAVAC)" -O -cp src -s src -d . $(SRC) -encoding UTF-8 16 | "$(JAR)" -cfm "$@" "META-INF/MANIFEST.MF" $(OBJ) 17 | 18 | 19 | info: util-say.info.gz 20 | %.info: %.texinfo 21 | $(MAKEINFO) "$<" 22 | %.info.gz: %.info 23 | gzip -9c < "$<" > "$@" 24 | 25 | 26 | pdf: util-say.pdf 27 | %.pdf: %.texinfo 28 | texi2pdf "$<" 29 | 30 | pdf.gz: util-say.pdf.gz 31 | %.pdf.gz: %.pdf 32 | gzip -9c < "$<" > "$@" 33 | 34 | pdf.xz: util-say.pdf.xz 35 | %.pdf.xz: %.pdf 36 | xz -e9 < "$<" > "$@" 37 | 38 | 39 | dvi: util-say.dvi 40 | %.dvi: %.texinfo 41 | $(TEXI2DVI) "$<" 42 | 43 | dvi.gz: util-say.dvi.gz 44 | %.dvi.gz: %.dvi 45 | gzip -9c < "$<" > "$@" 46 | 47 | dvi.xz: util-say.dvi.xz 48 | %.dvi.xz: %.dvi 49 | xz -e9 < "$<" > "$@" 50 | 51 | 52 | install: util-say.jar util-say.info.gz 53 | install -d -m 755 "$(DESTDIR)$(PREFIX)$(BIN)" 54 | install -m 755 util-say.jar "$(DESTDIR)$(PREFIX)$(BIN)" 55 | install -m 755 img2{pony,uni,cow}say "$(DESTDIR)$(PREFIX)$(BIN)" 56 | install -m 755 {pony,uni,cow}say2img "$(DESTDIR)$(PREFIX)$(BIN)" 57 | install -m 755 ponysay2ttyponysay "$(DESTDIR)$(PREFIX)$(BIN)" 58 | ln -sf ponysay2ttyponysay "$(DESTDIR)$(PREFIX)$(BIN)"/tty2colourfultty 59 | ln -sf /usr/bin/cat "$(DESTDIR)$(PREFIX)$(BIN)"/unzebra 60 | install -m 755 ponytool "$(DESTDIR)$(PREFIX)$(BIN)" 61 | install -d -m 755 "$(DESTDIR)$(PREFIX)$(DATA)/licenses/util-say" 62 | install -m 644 LICENSE COPYING "$(DESTDIR)$(PREFIX)$(DATA)/licenses/util-say" 63 | install -d -m 755 "$(DESTDIR)$(PREFIX)$(DATA)/info" 64 | install -m 644 util-say.info.gz "$(DESTDIR)$(PREFIX)$(DATA)/info" 65 | 66 | 67 | uninstall: 68 | unlink "$(DESTDIR)$(PREFIX)$(BIN)/util-say.jar" 69 | unlink "$(DESTDIR)$(PREFIX)$(BIN)/ponytool" 70 | unlink "$(DESTDIR)$(PREFIX)$(DATA)/info/util-say.info.gz" 71 | rm -- "$(DESTDIR)$(PREFIX)$(BIN)/"img2{pony,uni,cow}say 72 | rm -- "$(DESTDIR)$(PREFIX)$(BIN)/"{pony,uni,cow}say2img 73 | rm -- "$(DESTDIR)$(PREFIX)$(BIN)/"ponysay2ttyponysay 74 | rm -- "$(DESTDIR)$(PREFIX)$(BIN)/"tty2colourfultty 75 | rm -- "$(DESTDIR)$(PREFIX)$(BIN)/"unzebra 76 | yes | rm -r "$(DESTDIR)$(PREFIX)$(DATA)/licenses/util-say" 77 | 78 | 79 | .PHONY: clean 80 | clean: 81 | yes | rm -r se || true 82 | rm util-say.jar || true 83 | rm *.{info,pdf,ps,dvi}{,.*} || true 84 | rm *.{aux,cp,cps,fn,ky,log,op,ops,pg,toc,tp,vr} || true 85 | 86 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Tools for creating ponies for cowsay and cowsay-like programs. 2 | -------------------------------------------------------------------------------- /cowsay2img: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | magnified='1' 4 | file='-' 5 | dash=0 6 | 7 | for arg in "$@"; do 8 | if [ $dash = 1 ]; then 9 | file="$arg" 10 | elif [ "$arg" = "--" ]; then 11 | dash=1 12 | elif [ "$arg" = "-2" ]; then 13 | magnified=2 14 | else 15 | file="$arg" 16 | fi 17 | done 18 | 19 | java -jar "$(dirname "$0")/util-say.jar" \ 20 | --import cowsay --file - --export image --magnified $magnified --file "$file" --left - --right - --bottom - --top - 21 | 22 | -------------------------------------------------------------------------------- /dist/archlinux/bleeding/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Mattias Andrée <`base64 -d`(bWFhbmRyZWUK)@member.fsf.org> 2 | pkgname=util-say-git 3 | pkgver=20130405.2 4 | pkgrel=1 5 | pkgdesc="Tools for creating ponies for cowsay and cowsay-like programs" 6 | arch=('any') 7 | url="https://github.com/maandree/util-say" 8 | license=('GPL3') 9 | depends=('java-runtime>=6') 10 | makedepends=('git' 'java-environment>=6' 'bash') 11 | optdepends=('imagemagick: allows imgsrcrecovery to read frames in GIF files' 'perl: required for cowsay import support') 12 | provides=('util-say') 13 | conflicts=('util-say') 14 | 15 | _gitroot=https://github.com/maandree/util-say.git 16 | _gitname=util-say 17 | 18 | build() { 19 | cd "$srcdir" 20 | msg "Connecting to GIT server...." 21 | 22 | if [[ -d "$_gitname" ]]; then 23 | cd "$_gitname" && git pull origin 24 | msg "The local files are updated." 25 | else 26 | git clone "$_gitroot" "$_gitname" 27 | fi 28 | 29 | msg "GIT checkout done or server timeout" 30 | msg "Starting build..." 31 | 32 | rm -rf "$srcdir/$_gitname-build" 33 | git clone "$srcdir/$_gitname" "$srcdir/$_gitname-build" 34 | cd "$srcdir/$_gitname-build" 35 | 36 | make -B DESTDIR="$pkgdir/" 37 | } 38 | 39 | package() { 40 | cd "$srcdir/$_gitname-build" 41 | make DESTDIR="$pkgdir/" install 42 | } 43 | 44 | -------------------------------------------------------------------------------- /dist/archlinux/stable/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Mattias Andrée <`base64 -d`(bWFhbmRyZWUK)@member.fsf.org> 2 | pkgname=util-say 3 | pkgver=3.2.1 4 | pkgrel=1 5 | pkgdesc="Tools for creating ponies for cowsay and cowsay-like programs" 6 | arch=('any') 7 | url="https://github.com/maandree/util-say" 8 | license=('GPL3') 9 | depends=('java-runtime>=6') 10 | makedepends=('java-environment>=6' 'bash') 11 | optdepends=('imagemagick: allows imgsrcrecovery to read frames in GIF files' 'perl: required for cowsay import support') 12 | provides=('util-say') 13 | conflicts=('util-say') 14 | source=(https://github.com/maandree/util-say/archive/$pkgver.tar.gz) 15 | sha256sums=(38539e74d97afb719ad71f694ff08fc024ae71a33f60dd5d94e163d5bc66e98f) 16 | 17 | build() { 18 | cd "$srcdir/util-say-$pkgver" 19 | make -B DESTDIR="$pkgdir/" 20 | } 21 | 22 | package() { 23 | cd "$srcdir/util-say-$pkgver" 24 | make DESTDIR="$pkgdir/" install 25 | } 26 | -------------------------------------------------------------------------------- /fdl.texinfo: -------------------------------------------------------------------------------- 1 | @c The GNU Free Documentation License. 2 | @center Version 1.3, 3 November 2008 3 | 4 | @c This file is intended to be included within another document, 5 | @c hence no sectioning command or @node. 6 | 7 | @display 8 | Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. 9 | @uref{http://fsf.org/} 10 | 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | @end display 14 | 15 | @enumerate 0 16 | @item 17 | PREAMBLE 18 | 19 | The purpose of this License is to make a manual, textbook, or other 20 | functional and useful document @dfn{free} in the sense of freedom: to 21 | assure everyone the effective freedom to copy and redistribute it, 22 | with or without modifying it, either commercially or noncommercially. 23 | Secondarily, this License preserves for the author and publisher a way 24 | to get credit for their work, while not being considered responsible 25 | for modifications made by others. 26 | 27 | This License is a kind of ``copyleft'', which means that derivative 28 | works of the document must themselves be free in the same sense. It 29 | complements the GNU General Public License, which is a copyleft 30 | license designed for free software. 31 | 32 | We have designed this License in order to use it for manuals for free 33 | software, because free software needs free documentation: a free 34 | program should come with manuals providing the same freedoms that the 35 | software does. But this License is not limited to software manuals; 36 | it can be used for any textual work, regardless of subject matter or 37 | whether it is published as a printed book. We recommend this License 38 | principally for works whose purpose is instruction or reference. 39 | 40 | @item 41 | APPLICABILITY AND DEFINITIONS 42 | 43 | This License applies to any manual or other work, in any medium, that 44 | contains a notice placed by the copyright holder saying it can be 45 | distributed under the terms of this License. Such a notice grants a 46 | world-wide, royalty-free license, unlimited in duration, to use that 47 | work under the conditions stated herein. The ``Document'', below, 48 | refers to any such manual or work. Any member of the public is a 49 | licensee, and is addressed as ``you''. You accept the license if you 50 | copy, modify or distribute the work in a way requiring permission 51 | under copyright law. 52 | 53 | A ``Modified Version'' of the Document means any work containing the 54 | Document or a portion of it, either copied verbatim, or with 55 | modifications and/or translated into another language. 56 | 57 | A ``Secondary Section'' is a named appendix or a front-matter section 58 | of the Document that deals exclusively with the relationship of the 59 | publishers or authors of the Document to the Document's overall 60 | subject (or to related matters) and contains nothing that could fall 61 | directly within that overall subject. (Thus, if the Document is in 62 | part a textbook of mathematics, a Secondary Section may not explain 63 | any mathematics.) The relationship could be a matter of historical 64 | connection with the subject or with related matters, or of legal, 65 | commercial, philosophical, ethical or political position regarding 66 | them. 67 | 68 | The ``Invariant Sections'' are certain Secondary Sections whose titles 69 | are designated, as being those of Invariant Sections, in the notice 70 | that says that the Document is released under this License. If a 71 | section does not fit the above definition of Secondary then it is not 72 | allowed to be designated as Invariant. The Document may contain zero 73 | Invariant Sections. If the Document does not identify any Invariant 74 | Sections then there are none. 75 | 76 | The ``Cover Texts'' are certain short passages of text that are listed, 77 | as Front-Cover Texts or Back-Cover Texts, in the notice that says that 78 | the Document is released under this License. A Front-Cover Text may 79 | be at most 5 words, and a Back-Cover Text may be at most 25 words. 80 | 81 | A ``Transparent'' copy of the Document means a machine-readable copy, 82 | represented in a format whose specification is available to the 83 | general public, that is suitable for revising the document 84 | straightforwardly with generic text editors or (for images composed of 85 | pixels) generic paint programs or (for drawings) some widely available 86 | drawing editor, and that is suitable for input to text formatters or 87 | for automatic translation to a variety of formats suitable for input 88 | to text formatters. A copy made in an otherwise Transparent file 89 | format whose markup, or absence of markup, has been arranged to thwart 90 | or discourage subsequent modification by readers is not Transparent. 91 | An image format is not Transparent if used for any substantial amount 92 | of text. A copy that is not ``Transparent'' is called ``Opaque''. 93 | 94 | Examples of suitable formats for Transparent copies include plain 95 | ASCII without markup, Texinfo input format, La@TeX{} input 96 | format, SGML or XML using a publicly available 97 | DTD, and standard-conforming simple HTML, 98 | PostScript or PDF designed for human modification. Examples 99 | of transparent image formats include PNG, XCF and 100 | JPG. Opaque formats include proprietary formats that can be 101 | read and edited only by proprietary word processors, SGML or 102 | XML for which the DTD and/or processing tools are 103 | not generally available, and the machine-generated HTML, 104 | PostScript or PDF produced by some word processors for 105 | output purposes only. 106 | 107 | The ``Title Page'' means, for a printed book, the title page itself, 108 | plus such following pages as are needed to hold, legibly, the material 109 | this License requires to appear in the title page. For works in 110 | formats which do not have any title page as such, ``Title Page'' means 111 | the text near the most prominent appearance of the work's title, 112 | preceding the beginning of the body of the text. 113 | 114 | The ``publisher'' means any person or entity that distributes copies 115 | of the Document to the public. 116 | 117 | A section ``Entitled XYZ'' means a named subunit of the Document whose 118 | title either is precisely XYZ or contains XYZ in parentheses following 119 | text that translates XYZ in another language. (Here XYZ stands for a 120 | specific section name mentioned below, such as ``Acknowledgements'', 121 | ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' 122 | of such a section when you modify the Document means that it remains a 123 | section ``Entitled XYZ'' according to this definition. 124 | 125 | The Document may include Warranty Disclaimers next to the notice which 126 | states that this License applies to the Document. These Warranty 127 | Disclaimers are considered to be included by reference in this 128 | License, but only as regards disclaiming warranties: any other 129 | implication that these Warranty Disclaimers may have is void and has 130 | no effect on the meaning of this License. 131 | 132 | @item 133 | VERBATIM COPYING 134 | 135 | You may copy and distribute the Document in any medium, either 136 | commercially or noncommercially, provided that this License, the 137 | copyright notices, and the license notice saying this License applies 138 | to the Document are reproduced in all copies, and that you add no other 139 | conditions whatsoever to those of this License. You may not use 140 | technical measures to obstruct or control the reading or further 141 | copying of the copies you make or distribute. However, you may accept 142 | compensation in exchange for copies. If you distribute a large enough 143 | number of copies you must also follow the conditions in section 3. 144 | 145 | You may also lend copies, under the same conditions stated above, and 146 | you may publicly display copies. 147 | 148 | @item 149 | COPYING IN QUANTITY 150 | 151 | If you publish printed copies (or copies in media that commonly have 152 | printed covers) of the Document, numbering more than 100, and the 153 | Document's license notice requires Cover Texts, you must enclose the 154 | copies in covers that carry, clearly and legibly, all these Cover 155 | Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on 156 | the back cover. Both covers must also clearly and legibly identify 157 | you as the publisher of these copies. The front cover must present 158 | the full title with all words of the title equally prominent and 159 | visible. You may add other material on the covers in addition. 160 | Copying with changes limited to the covers, as long as they preserve 161 | the title of the Document and satisfy these conditions, can be treated 162 | as verbatim copying in other respects. 163 | 164 | If the required texts for either cover are too voluminous to fit 165 | legibly, you should put the first ones listed (as many as fit 166 | reasonably) on the actual cover, and continue the rest onto adjacent 167 | pages. 168 | 169 | If you publish or distribute Opaque copies of the Document numbering 170 | more than 100, you must either include a machine-readable Transparent 171 | copy along with each Opaque copy, or state in or with each Opaque copy 172 | a computer-network location from which the general network-using 173 | public has access to download using public-standard network protocols 174 | a complete Transparent copy of the Document, free of added material. 175 | If you use the latter option, you must take reasonably prudent steps, 176 | when you begin distribution of Opaque copies in quantity, to ensure 177 | that this Transparent copy will remain thus accessible at the stated 178 | location until at least one year after the last time you distribute an 179 | Opaque copy (directly or through your agents or retailers) of that 180 | edition to the public. 181 | 182 | It is requested, but not required, that you contact the authors of the 183 | Document well before redistributing any large number of copies, to give 184 | them a chance to provide you with an updated version of the Document. 185 | 186 | @item 187 | MODIFICATIONS 188 | 189 | You may copy and distribute a Modified Version of the Document under 190 | the conditions of sections 2 and 3 above, provided that you release 191 | the Modified Version under precisely this License, with the Modified 192 | Version filling the role of the Document, thus licensing distribution 193 | and modification of the Modified Version to whoever possesses a copy 194 | of it. In addition, you must do these things in the Modified Version: 195 | 196 | @enumerate A 197 | @item 198 | Use in the Title Page (and on the covers, if any) a title distinct 199 | from that of the Document, and from those of previous versions 200 | (which should, if there were any, be listed in the History section 201 | of the Document). You may use the same title as a previous version 202 | if the original publisher of that version gives permission. 203 | 204 | @item 205 | List on the Title Page, as authors, one or more persons or entities 206 | responsible for authorship of the modifications in the Modified 207 | Version, together with at least five of the principal authors of the 208 | Document (all of its principal authors, if it has fewer than five), 209 | unless they release you from this requirement. 210 | 211 | @item 212 | State on the Title page the name of the publisher of the 213 | Modified Version, as the publisher. 214 | 215 | @item 216 | Preserve all the copyright notices of the Document. 217 | 218 | @item 219 | Add an appropriate copyright notice for your modifications 220 | adjacent to the other copyright notices. 221 | 222 | @item 223 | Include, immediately after the copyright notices, a license notice 224 | giving the public permission to use the Modified Version under the 225 | terms of this License, in the form shown in the Addendum below. 226 | 227 | @item 228 | Preserve in that license notice the full lists of Invariant Sections 229 | and required Cover Texts given in the Document's license notice. 230 | 231 | @item 232 | Include an unaltered copy of this License. 233 | 234 | @item 235 | Preserve the section Entitled ``History'', Preserve its Title, and add 236 | to it an item stating at least the title, year, new authors, and 237 | publisher of the Modified Version as given on the Title Page. If 238 | there is no section Entitled ``History'' in the Document, create one 239 | stating the title, year, authors, and publisher of the Document as 240 | given on its Title Page, then add an item describing the Modified 241 | Version as stated in the previous sentence. 242 | 243 | @item 244 | Preserve the network location, if any, given in the Document for 245 | public access to a Transparent copy of the Document, and likewise 246 | the network locations given in the Document for previous versions 247 | it was based on. These may be placed in the ``History'' section. 248 | You may omit a network location for a work that was published at 249 | least four years before the Document itself, or if the original 250 | publisher of the version it refers to gives permission. 251 | 252 | @item 253 | For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve 254 | the Title of the section, and preserve in the section all the 255 | substance and tone of each of the contributor acknowledgements and/or 256 | dedications given therein. 257 | 258 | @item 259 | Preserve all the Invariant Sections of the Document, 260 | unaltered in their text and in their titles. Section numbers 261 | or the equivalent are not considered part of the section titles. 262 | 263 | @item 264 | Delete any section Entitled ``Endorsements''. Such a section 265 | may not be included in the Modified Version. 266 | 267 | @item 268 | Do not retitle any existing section to be Entitled ``Endorsements'' or 269 | to conflict in title with any Invariant Section. 270 | 271 | @item 272 | Preserve any Warranty Disclaimers. 273 | @end enumerate 274 | 275 | If the Modified Version includes new front-matter sections or 276 | appendices that qualify as Secondary Sections and contain no material 277 | copied from the Document, you may at your option designate some or all 278 | of these sections as invariant. To do this, add their titles to the 279 | list of Invariant Sections in the Modified Version's license notice. 280 | These titles must be distinct from any other section titles. 281 | 282 | You may add a section Entitled ``Endorsements'', provided it contains 283 | nothing but endorsements of your Modified Version by various 284 | parties---for example, statements of peer review or that the text has 285 | been approved by an organization as the authoritative definition of a 286 | standard. 287 | 288 | You may add a passage of up to five words as a Front-Cover Text, and a 289 | passage of up to 25 words as a Back-Cover Text, to the end of the list 290 | of Cover Texts in the Modified Version. Only one passage of 291 | Front-Cover Text and one of Back-Cover Text may be added by (or 292 | through arrangements made by) any one entity. If the Document already 293 | includes a cover text for the same cover, previously added by you or 294 | by arrangement made by the same entity you are acting on behalf of, 295 | you may not add another; but you may replace the old one, on explicit 296 | permission from the previous publisher that added the old one. 297 | 298 | The author(s) and publisher(s) of the Document do not by this License 299 | give permission to use their names for publicity for or to assert or 300 | imply endorsement of any Modified Version. 301 | 302 | @item 303 | COMBINING DOCUMENTS 304 | 305 | You may combine the Document with other documents released under this 306 | License, under the terms defined in section 4 above for modified 307 | versions, provided that you include in the combination all of the 308 | Invariant Sections of all of the original documents, unmodified, and 309 | list them all as Invariant Sections of your combined work in its 310 | license notice, and that you preserve all their Warranty Disclaimers. 311 | 312 | The combined work need only contain one copy of this License, and 313 | multiple identical Invariant Sections may be replaced with a single 314 | copy. If there are multiple Invariant Sections with the same name but 315 | different contents, make the title of each such section unique by 316 | adding at the end of it, in parentheses, the name of the original 317 | author or publisher of that section if known, or else a unique number. 318 | Make the same adjustment to the section titles in the list of 319 | Invariant Sections in the license notice of the combined work. 320 | 321 | In the combination, you must combine any sections Entitled ``History'' 322 | in the various original documents, forming one section Entitled 323 | ``History''; likewise combine any sections Entitled ``Acknowledgements'', 324 | and any sections Entitled ``Dedications''. You must delete all 325 | sections Entitled ``Endorsements.'' 326 | 327 | @item 328 | COLLECTIONS OF DOCUMENTS 329 | 330 | You may make a collection consisting of the Document and other documents 331 | released under this License, and replace the individual copies of this 332 | License in the various documents with a single copy that is included in 333 | the collection, provided that you follow the rules of this License for 334 | verbatim copying of each of the documents in all other respects. 335 | 336 | You may extract a single document from such a collection, and distribute 337 | it individually under this License, provided you insert a copy of this 338 | License into the extracted document, and follow this License in all 339 | other respects regarding verbatim copying of that document. 340 | 341 | @item 342 | AGGREGATION WITH INDEPENDENT WORKS 343 | 344 | A compilation of the Document or its derivatives with other separate 345 | and independent documents or works, in or on a volume of a storage or 346 | distribution medium, is called an ``aggregate'' if the copyright 347 | resulting from the compilation is not used to limit the legal rights 348 | of the compilation's users beyond what the individual works permit. 349 | When the Document is included in an aggregate, this License does not 350 | apply to the other works in the aggregate which are not themselves 351 | derivative works of the Document. 352 | 353 | If the Cover Text requirement of section 3 is applicable to these 354 | copies of the Document, then if the Document is less than one half of 355 | the entire aggregate, the Document's Cover Texts may be placed on 356 | covers that bracket the Document within the aggregate, or the 357 | electronic equivalent of covers if the Document is in electronic form. 358 | Otherwise they must appear on printed covers that bracket the whole 359 | aggregate. 360 | 361 | @item 362 | TRANSLATION 363 | 364 | Translation is considered a kind of modification, so you may 365 | distribute translations of the Document under the terms of section 4. 366 | Replacing Invariant Sections with translations requires special 367 | permission from their copyright holders, but you may include 368 | translations of some or all Invariant Sections in addition to the 369 | original versions of these Invariant Sections. You may include a 370 | translation of this License, and all the license notices in the 371 | Document, and any Warranty Disclaimers, provided that you also include 372 | the original English version of this License and the original versions 373 | of those notices and disclaimers. In case of a disagreement between 374 | the translation and the original version of this License or a notice 375 | or disclaimer, the original version will prevail. 376 | 377 | If a section in the Document is Entitled ``Acknowledgements'', 378 | ``Dedications'', or ``History'', the requirement (section 4) to Preserve 379 | its Title (section 1) will typically require changing the actual 380 | title. 381 | 382 | @item 383 | TERMINATION 384 | 385 | You may not copy, modify, sublicense, or distribute the Document 386 | except as expressly provided under this License. Any attempt 387 | otherwise to copy, modify, sublicense, or distribute it is void, and 388 | will automatically terminate your rights under this License. 389 | 390 | However, if you cease all violation of this License, then your license 391 | from a particular copyright holder is reinstated (a) provisionally, 392 | unless and until the copyright holder explicitly and finally 393 | terminates your license, and (b) permanently, if the copyright holder 394 | fails to notify you of the violation by some reasonable means prior to 395 | 60 days after the cessation. 396 | 397 | Moreover, your license from a particular copyright holder is 398 | reinstated permanently if the copyright holder notifies you of the 399 | violation by some reasonable means, this is the first time you have 400 | received notice of violation of this License (for any work) from that 401 | copyright holder, and you cure the violation prior to 30 days after 402 | your receipt of the notice. 403 | 404 | Termination of your rights under this section does not terminate the 405 | licenses of parties who have received copies or rights from you under 406 | this License. If your rights have been terminated and not permanently 407 | reinstated, receipt of a copy of some or all of the same material does 408 | not give you any rights to use it. 409 | 410 | @item 411 | FUTURE REVISIONS OF THIS LICENSE 412 | 413 | The Free Software Foundation may publish new, revised versions 414 | of the GNU Free Documentation License from time to time. Such new 415 | versions will be similar in spirit to the present version, but may 416 | differ in detail to address new problems or concerns. See 417 | @uref{http://www.gnu.org/copyleft/}. 418 | 419 | Each version of the License is given a distinguishing version number. 420 | If the Document specifies that a particular numbered version of this 421 | License ``or any later version'' applies to it, you have the option of 422 | following the terms and conditions either of that specified version or 423 | of any later version that has been published (not as a draft) by the 424 | Free Software Foundation. If the Document does not specify a version 425 | number of this License, you may choose any version ever published (not 426 | as a draft) by the Free Software Foundation. If the Document 427 | specifies that a proxy can decide which future versions of this 428 | License can be used, that proxy's public statement of acceptance of a 429 | version permanently authorizes you to choose that version for the 430 | Document. 431 | 432 | @item 433 | RELICENSING 434 | 435 | ``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any 436 | World Wide Web server that publishes copyrightable works and also 437 | provides prominent facilities for anybody to edit those works. A 438 | public wiki that anybody can edit is an example of such a server. A 439 | ``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the 440 | site means any set of copyrightable works thus published on the MMC 441 | site. 442 | 443 | ``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 444 | license published by Creative Commons Corporation, a not-for-profit 445 | corporation with a principal place of business in San Francisco, 446 | California, as well as future copyleft versions of that license 447 | published by that same organization. 448 | 449 | ``Incorporate'' means to publish or republish a Document, in whole or 450 | in part, as part of another Document. 451 | 452 | An MMC is ``eligible for relicensing'' if it is licensed under this 453 | License, and if all works that were first published under this License 454 | somewhere other than this MMC, and subsequently incorporated in whole 455 | or in part into the MMC, (1) had no cover texts or invariant sections, 456 | and (2) were thus incorporated prior to November 1, 2008. 457 | 458 | The operator of an MMC Site may republish an MMC contained in the site 459 | under CC-BY-SA on the same site at any time before August 1, 2009, 460 | provided the MMC is eligible for relicensing. 461 | 462 | @end enumerate 463 | 464 | @page 465 | @heading ADDENDUM: How to use this License for your documents 466 | 467 | To use this License in a document you have written, include a copy of 468 | the License in the document and put the following copyright and 469 | license notices just after the title page: 470 | 471 | @smallexample 472 | @group 473 | Copyright (C) @var{year} @var{your name}. 474 | Permission is granted to copy, distribute and/or modify this document 475 | under the terms of the GNU Free Documentation License, Version 1.3 476 | or any later version published by the Free Software Foundation; 477 | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover 478 | Texts. A copy of the license is included in the section entitled ``GNU 479 | Free Documentation License''. 480 | @end group 481 | @end smallexample 482 | 483 | If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, 484 | replace the ``with@dots{}Texts.''@: line with this: 485 | 486 | @smallexample 487 | @group 488 | with the Invariant Sections being @var{list their titles}, with 489 | the Front-Cover Texts being @var{list}, and with the Back-Cover Texts 490 | being @var{list}. 491 | @end group 492 | @end smallexample 493 | 494 | If you have Invariant Sections without Cover Texts, or some other 495 | combination of the three, merge those two alternatives to suit the 496 | situation. 497 | 498 | If your document contains nontrivial examples of program code, we 499 | recommend releasing these examples in parallel under your choice of 500 | free software license, such as the GNU General Public License, 501 | to permit their use in free software. 502 | 503 | @c Local Variables: 504 | @c ispell-local-pdict: "ispell-dict" 505 | @c End: 506 | -------------------------------------------------------------------------------- /img2cowsay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | magnified='1' 4 | file='-' 5 | dash=0 6 | chroma=1 7 | c=0 8 | platform=xterm 9 | 10 | for arg in "$@"; do 11 | if [ $c = 1 ]; then 12 | c=0 13 | chroma="$arg" 14 | elif [ $dash = 1 ]; then 15 | file="$arg" 16 | elif [ "$arg" = "--" ]; then 17 | dash=1 18 | elif [ "$arg" = "-2" ]; then 19 | magnified=2 20 | elif [ "$arg" = "-c" ]; then 21 | c=1 22 | elif [ "$arg" = "-p" ]; then 23 | platform=linux 24 | else 25 | file="$arg" 26 | fi 27 | done 28 | 29 | java -jar "$(dirname "$0")/util-say.jar" \ 30 | --import image --magnified $magnified --file "$file" --balloon n --export cowsay --balloon y --file - --chroma "$chroma" --platform $platform 31 | 32 | -------------------------------------------------------------------------------- /img2ponysay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | magnified='1' 4 | file='-' 5 | dash=0 6 | chroma=1 7 | c=0 8 | platform=xterm 9 | 10 | for arg in "$@"; do 11 | if [ $c = 1 ]; then 12 | c=0 13 | chroma="$arg" 14 | elif [ $dash = 1 ]; then 15 | file="$arg" 16 | elif [ "$arg" = "--" ]; then 17 | dash=1 18 | elif [ "$arg" = "-2" ]; then 19 | magnified=2 20 | elif [ "$arg" = "-c" ]; then 21 | c=1 22 | elif [ "$arg" = "-p" ]; then 23 | platform=linux 24 | else 25 | file="$arg" 26 | fi 27 | done 28 | 29 | java -jar "$(dirname "$0")/util-say.jar" \ 30 | --import image --magnified $magnified --file "$file" --balloon n --export ponysay --balloon y --file - --chroma "$chroma" --platform $platform 31 | 32 | -------------------------------------------------------------------------------- /img2unisay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | magnified='1' 4 | file='-' 5 | dash=0 6 | chroma=1 7 | c=0 8 | platform=xterm 9 | 10 | for arg in "$@"; do 11 | if [ $c = 1 ]; then 12 | c=0 13 | chroma="$arg" 14 | elif [ $dash = 1 ]; then 15 | file="$arg" 16 | elif [ "$arg" = "--" ]; then 17 | dash=1 18 | elif [ "$arg" = "-2" ]; then 19 | magnified=2 20 | elif [ "$arg" = "-c" ]; then 21 | c=1 22 | elif [ "$arg" = "-p" ]; then 23 | platform=linux 24 | else 25 | file="$arg" 26 | fi 27 | done 28 | 29 | java -jar "$(dirname "$0")/util-say.jar" \ 30 | --import image --magnified $magnified --file "$file" --balloon n --export unisay --balloon y --file - --chroma "$chroma" --platform $platform 31 | 32 | -------------------------------------------------------------------------------- /lowcaps.linux.conf: -------------------------------------------------------------------------------- 1 | FREE ZONE:: 2 | 3 | This is an example configuration best on Linux VT's cap:s, 4 | without OSI P and with xterms default font. 5 | 6 | 7 | CHROMA:: 8 | 1.0 9 | 10 | 11 | FOREGROUND:: 12 | 13 | [30m 000000 [39m 14 | [31m AA0000 [39m 15 | [32m 00AA00 [39m 16 | [33m AA5500 [39m 17 | [34m 0000AA [39m 18 | [35m AA00AA [39m 19 | [36m 00AAAA [39m 20 | [37m AAAAAA [39m 21 | [30;1m 555555 [39;21m 22 | [31;1m FF5555 [39;21m 23 | [32;1m 55FF55 [39;21m 24 | [33;1m FFFF55 [39;21m 25 | [34;1m 5555FF [39;21m 26 | [35;1m FF55FF [39;21m 27 | [36;1m 55FFFF [39;21m 28 | [37;1m FFFFFF [39;21m 29 | 30 | 31 | BACKGROUND:: 32 | 33 | [41m 000000 [49m 34 | [42m CD0000 [49m 35 | [43m 00CD00 [49m 36 | [44m CDCD00 [49m 37 | [45m 1E90FF [49m 38 | [46m CD00CD [49m 39 | [47m 00CDCD [49m 40 | [48m E5E5E5 [49m 41 | 42 | 43 | DITHERER:: 44 | 45 | 78 46 | ▒ 39 47 | ▓ 57 48 | ░ 21 49 | # 20 50 | @ 24 51 | & 16 52 | X 17 53 | Q 22 54 | G 18 55 | g 19 56 | ? 12 57 | B 23 58 | * 11 59 | I 13 60 | f 14 61 | w 15 62 | = 10 63 | + 9 64 | - 5 65 | ~ 7 66 | ' 3 67 | ` 2 68 | , 4 69 | 70 | 71 | FREE ZONE:: 72 | 73 | $ 21 74 | 75 | . 5 76 | ^ 5 77 | _ 5 78 | " 6 79 | ! 8 80 | | 9 81 | \ 9 82 | / 9 83 | ; 9 84 | < 9 85 | > 9 86 | : 10 87 | i 10 88 | x 10 89 | r 10 90 | v 11 91 | ( 11 92 | ) 11 93 | l 12 94 | c 12 95 | 7 13 96 | J 13 97 | L 13 98 | T 13 99 | Y 13 100 | j 13 101 | s 13 102 | t 13 103 | u 14 104 | n 14 105 | o 14 106 | z 14 107 | { 14 108 | } 14 109 | k 15 110 | ] 15 111 | [ 15 112 | C 15 113 | 1 15 114 | 0 16 115 | 2 16 116 | F 16 117 | V 16 118 | e 16 119 | a 16 120 | h 17 121 | m 17 122 | Z 17 123 | p 17 124 | q 17 125 | S 17 126 | 4 17 127 | % 17 128 | y 18 129 | 3 18 130 | K 18 131 | P 18 132 | U 19 133 | b 19 134 | d 19 135 | E 20 136 | O 20 137 | 5 20 138 | 6 20 139 | 9 20 140 | A 20 141 | 8 21 142 | H 21 143 | W 22 144 | M 22 145 | R 22 146 | D 22 147 | N 24 148 | -------------------------------------------------------------------------------- /lowcaps.rxvt.conf: -------------------------------------------------------------------------------- 1 | FREE ZONE:: 2 | 3 | This is an example configuration best on xterm's cap:s, 4 | but not with it's full cap:s. 5 | 6 | 7 | CHROMA:: 8 | 1.0 9 | 10 | 11 | FOREGROUND:: 12 | 13 | [30m 000000 [39m 14 | [31m CD0000 [39m 15 | [32m 00CD00 [39m 16 | [33m CDCD00 [39m 17 | [34m 0000CD [39m 18 | [35m CD00CD [39m 19 | [36m 00CDCD [39m 20 | [37m FAEBD7 [39m 21 | [90m 404040 [39m 22 | [91m FF0000 [39m 23 | [92m 00FF00 [39m 24 | [93m FFFF00 [39m 25 | [94m 0000FF [39m 26 | [95m FF00FF [39m 27 | [96m 00FFFF [39m 28 | [97m FFFFFF [39m 29 | 30 | 31 | BACKGROUND:: 32 | 33 | [40m 000000 [49m 34 | [41m CD0000 [49m 35 | [42m 00CD00 [49m 36 | [43m CDCD00 [49m 37 | [44m 0000CD [49m 38 | [45m CD00CD [49m 39 | [46m 00CDCD [49m 40 | [47m FAEBD7 [49m 41 | [100m 404040 [49m 42 | [101m FF0000 [49m 43 | [102m 00FF00 [49m 44 | [103m FFFF00 [49m 45 | [104m 0000FF [49m 46 | [105m FF00FF [49m 47 | [106m 00FFFF [49m 48 | [107m FFFFFF [49m 49 | 50 | 51 | DITHERER:: 52 | 53 | 78 54 | ▒ 39 55 | ▓ 57 56 | ░ 21 57 | # 20 58 | @ 24 59 | & 16 60 | X 17 61 | Q 22 62 | G 18 63 | g 19 64 | ? 12 65 | B 23 66 | * 11 67 | I 13 68 | f 14 69 | w 15 70 | = 10 71 | + 9 72 | - 5 73 | ~ 7 74 | ' 3 75 | ` 2 76 | , 4 77 | 78 | 79 | FREE ZONE:: 80 | 81 | $ 21 82 | 83 | . 5 84 | ^ 5 85 | _ 5 86 | " 6 87 | ! 8 88 | | 9 89 | \ 9 90 | / 9 91 | ; 9 92 | < 9 93 | > 9 94 | : 10 95 | i 10 96 | x 10 97 | r 10 98 | v 11 99 | ( 11 100 | ) 11 101 | l 12 102 | c 12 103 | 7 13 104 | J 13 105 | L 13 106 | T 13 107 | Y 13 108 | j 13 109 | s 13 110 | t 13 111 | u 14 112 | n 14 113 | o 14 114 | z 14 115 | { 14 116 | } 14 117 | k 15 118 | ] 15 119 | [ 15 120 | C 15 121 | 1 15 122 | 0 16 123 | 2 16 124 | F 16 125 | V 16 126 | e 16 127 | a 16 128 | h 17 129 | m 17 130 | Z 17 131 | p 17 132 | q 17 133 | S 17 134 | 4 17 135 | % 17 136 | y 18 137 | 3 18 138 | K 18 139 | P 18 140 | U 19 141 | b 19 142 | d 19 143 | E 20 144 | O 20 145 | 5 20 146 | 6 20 147 | 9 20 148 | A 20 149 | 8 21 150 | H 21 151 | W 22 152 | M 22 153 | R 22 154 | D 22 155 | N 24 156 | -------------------------------------------------------------------------------- /lowcaps.tango.conf: -------------------------------------------------------------------------------- 1 | FREE ZONE:: 2 | 3 | This is an example configuration best on xterm's cap:s, 4 | but not with it's full cap:s, but with tango colours. 5 | 6 | 7 | CHROMA:: 8 | 1.0 9 | 10 | 11 | FOREGROUND:: 12 | 13 | [30m 000000 [39m 14 | [31m CC0000 [39m 15 | [32m AE9A06 [39m 16 | [33m C4A000 [39m 17 | [34m 3465A4 [39m 18 | [35m 75507B [39m 19 | [36m 06989A [39m 20 | [37m D3D7CF [39m 21 | [90m 555753 [39m 22 | [91m EF2929 [39m 23 | [92m 8AE234 [39m 24 | [93m FCE94F [39m 25 | [94m 729FCF [39m 26 | [95m AD7FA8 [39m 27 | [96m 34E2E2 [39m 28 | [97m EEEEEC [39m 29 | 30 | 31 | BACKGROUND:: 32 | 33 | [40m 000000 [39m 34 | [41m CC0000 [39m 35 | [42m AE9A06 [39m 36 | [43m C4A000 [39m 37 | [44m 3465A4 [39m 38 | [45m 75507B [39m 39 | [46m 06989A [39m 40 | [47m D3D7CF [39m 41 | [100m 555753 [39m 42 | [101m EF2929 [39m 43 | [102m 8AE234 [39m 44 | [193m FCE94F [39m 45 | [104m 729FCF [39m 46 | [105m AD7FA8 [39m 47 | [106m 34E2E2 [39m 48 | [107m EEEEEC [39m 49 | 50 | 51 | DITHERER:: 52 | 53 | 78 54 | ▒ 39 55 | ▓ 57 56 | ░ 21 57 | # 20 58 | @ 24 59 | & 16 60 | X 17 61 | Q 22 62 | G 18 63 | g 19 64 | ? 12 65 | B 23 66 | * 11 67 | I 13 68 | f 14 69 | w 15 70 | = 10 71 | + 9 72 | - 5 73 | ~ 7 74 | ' 3 75 | ` 2 76 | , 4 77 | 78 | 79 | FREE ZONE:: 80 | 81 | $ 21 82 | 83 | . 5 84 | ^ 5 85 | _ 5 86 | " 6 87 | ! 8 88 | | 9 89 | \ 9 90 | / 9 91 | ; 9 92 | < 9 93 | > 9 94 | : 10 95 | i 10 96 | x 10 97 | r 10 98 | v 11 99 | ( 11 100 | ) 11 101 | l 12 102 | c 12 103 | 7 13 104 | J 13 105 | L 13 106 | T 13 107 | Y 13 108 | j 13 109 | s 13 110 | t 13 111 | u 14 112 | n 14 113 | o 14 114 | z 14 115 | { 14 116 | } 14 117 | k 15 118 | ] 15 119 | [ 15 120 | C 15 121 | 1 15 122 | 0 16 123 | 2 16 124 | F 16 125 | V 16 126 | e 16 127 | a 16 128 | h 17 129 | m 17 130 | Z 17 131 | p 17 132 | q 17 133 | S 17 134 | 4 17 135 | % 17 136 | y 18 137 | 3 18 138 | K 18 139 | P 18 140 | U 19 141 | b 19 142 | d 19 143 | E 20 144 | O 20 145 | 5 20 146 | 6 20 147 | 9 20 148 | A 20 149 | 8 21 150 | H 21 151 | W 22 152 | M 22 153 | R 22 154 | D 22 155 | N 24 156 | -------------------------------------------------------------------------------- /lowcaps.xterm.conf: -------------------------------------------------------------------------------- 1 | FREE ZONE:: 2 | 3 | This is an example configuration best on xterm's cap:s, 4 | but not with it's full cap:s. 5 | 6 | 7 | CHROMA:: 8 | 1.0 9 | 10 | 11 | FOREGROUND:: 12 | 13 | [30m 000000 [39m 14 | [31m CD0000 [39m 15 | [32m 00CD00 [39m 16 | [33m CDCD00 [39m 17 | [34m 1E90FF [39m 18 | [35m CD00CD [39m 19 | [36m 00CDCD [39m 20 | [37m E5E5E5 [39m 21 | [90m 4C4C4C [39m 22 | [91m FF0000 [39m 23 | [92m 00FF00 [39m 24 | [93m FFFF00 [39m 25 | [94m 4682B4 [39m 26 | [95m FF00FF [39m 27 | [96m 00FFFF [39m 28 | [97m FFFFFF [39m 29 | 30 | 31 | BACKGROUND:: 32 | 33 | [40m 000000 [49m 34 | [41m CD0000 [49m 35 | [42m 00CD00 [49m 36 | [43m CDCD00 [49m 37 | [44m 1E90FF [49m 38 | [45m CD00CD [49m 39 | [46m 00CDCD [49m 40 | [47m E5E5E5 [49m 41 | [100m 4C4C4C [49m 42 | [101m FF0000 [49m 43 | [102m 00FF00 [49m 44 | [103m FFFF00 [49m 45 | [104m 4682B4 [49m 46 | [105m FF00FF [49m 47 | [106m 00FFFF [49m 48 | [107m FFFFFF [49m 49 | 50 | 51 | DITHERER:: 52 | 53 | 78 54 | ▒ 39 55 | ▓ 57 56 | ░ 21 57 | # 20 58 | @ 24 59 | & 16 60 | X 17 61 | Q 22 62 | G 18 63 | g 19 64 | ? 12 65 | B 23 66 | * 11 67 | I 13 68 | f 14 69 | w 15 70 | = 10 71 | + 9 72 | - 5 73 | ~ 7 74 | ' 3 75 | ` 2 76 | , 4 77 | 78 | 79 | FREE ZONE:: 80 | 81 | $ 21 82 | 83 | . 5 84 | ^ 5 85 | _ 5 86 | " 6 87 | ! 8 88 | | 9 89 | \ 9 90 | / 9 91 | ; 9 92 | < 9 93 | > 9 94 | : 10 95 | i 10 96 | x 10 97 | r 10 98 | v 11 99 | ( 11 100 | ) 11 101 | l 12 102 | c 12 103 | 7 13 104 | J 13 105 | L 13 106 | T 13 107 | Y 13 108 | j 13 109 | s 13 110 | t 13 111 | u 14 112 | n 14 113 | o 14 114 | z 14 115 | { 14 116 | } 14 117 | k 15 118 | ] 15 119 | [ 15 120 | C 15 121 | 1 15 122 | 0 16 123 | 2 16 124 | F 16 125 | V 16 126 | e 16 127 | a 16 128 | h 17 129 | m 17 130 | Z 17 131 | p 17 132 | q 17 133 | S 17 134 | 4 17 135 | % 17 136 | y 18 137 | 3 18 138 | K 18 139 | P 18 140 | U 19 141 | b 19 142 | d 19 143 | E 20 144 | O 20 145 | 5 20 146 | 6 20 147 | 9 20 148 | A 20 149 | 8 21 150 | H 21 151 | W 22 152 | M 22 153 | R 22 154 | D 22 155 | N 24 156 | -------------------------------------------------------------------------------- /ponysay2img: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | magnified='1' 4 | file='-' 5 | dash=0 6 | 7 | for arg in "$@"; do 8 | if [ $dash = 1 ]; then 9 | file="$arg" 10 | elif [ "$arg" = "--" ]; then 11 | dash=1 12 | elif [ "$arg" = "-2" ]; then 13 | magnified=2 14 | else 15 | file="$arg" 16 | fi 17 | done 18 | 19 | java -jar "$(dirname "$0")/util-say.jar" \ 20 | --import ponysay --file - --export image --magnified $magnified --file "$file" --left - --right - --bottom - --top - 21 | 22 | -------------------------------------------------------------------------------- /ponysay2ttyponysay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | p=0 4 | palette='' 5 | 6 | for arg in "$@"; do 7 | if [ $p = 1 ]; then 8 | p=0 9 | palette="$arg" 10 | elif [ "$arg" = '-p' ]; then 11 | p=1 12 | fi 13 | done 14 | 15 | java -jar "$(dirname "$0")/util-say.jar" \ 16 | --import ponysay --file - --export ponysay --file - --left - --right - --bottom - --top - --palette "$palette" 17 | 18 | -------------------------------------------------------------------------------- /ponytool: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -jar "$(dirname "$0")/util-say.jar" "$@" 3 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Cat.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.io.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | *

cat printable pony module

27 | *

28 | * Note that the cat-printablilty may be disrupted by stores and recalls! 29 | *

30 | * 31 | * @author Mattias Andrée, m@maandree.se 32 | */ 33 | public class Cat extends Ponysay 34 | { 35 | /** 36 | * Constructor 37 | * 38 | * @param flags Flags passed to the module 39 | */ 40 | public Cat(HashMap flags) 41 | { 42 | super(Cat.modifyFlags(flags)); 43 | } 44 | 45 | 46 | /** 47 | * Modify the flags to fit this module 48 | * 49 | * @param flag The flags 50 | * @return The flags 51 | */ 52 | private static HashMap modifyFlags(HashMap flags) 53 | { 54 | flags.put("ignoreballoon", "y"); 55 | flags.put("ignorelink", "y"); 56 | flags.put("balloon", "-"); 57 | flags.put("version", "2.8"); 58 | return flags; 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Colour.java: -------------------------------------------------------------------------------- 1 | //#################################################################################### 2 | //## The following code is pasted from TWT, but the class is made package private ## 3 | //## and the package has been changed to se.kth.maandree.utilsay ## 4 | //## toLab(...) as also been publicised. ## 5 | //#################################################################################### 6 | 7 | 8 | /** 9 | * TWT — Terminal Window Toolkit, a free pure Java terminal toolkit. 10 | * Copyright (C) 2011 Mattias Andrée 11 | * 12 | * This library is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this library. If not, see . 24 | */ 25 | package se.kth.maandree.utilsay; 26 | 27 | 28 | /** 29 | * Terminal colour class 30 | * 31 | * @author Mattias Andrée, m@maandree.se 32 | */ 33 | public class Colour 34 | { 35 | /** 36 | * Possible colour intensitivities on mixed colours 37 | */ 38 | public static final int[] COLOUR_INTENSITIVITY = {0, 95, 135, 175, 215, 255}; 39 | 40 | /** 41 | * Possible intensitivities on grey colours, excluding the mixed colours' intensitivities 42 | */ 43 | public static final int[] GREY_EXTRA_INTENSITIVITY = {8, 18, 28, 38, 48, 58, 68, 78, 88, 98, 108, 118, 128, 44 | 138, 148, 158, 168, 178, 188, 198, 208, 218, 228, 238}; 45 | 46 | /** 47 | * Possible intensitivities on mixed colours, including the mixed colours' intensitivities 48 | */ 49 | public static final int[] GREY_FULL_INTENSITIVITY = {0, 8, 18, 28, 38, 48, 58, 68, 78, 88, 95, 98, 108, 118, 128, 135, 50 | 138, 148, 158, 168, 175, 178, 188, 198, 208, 218, 215, 228, 238, 255}; 51 | 52 | 53 | 54 | /** 55 | *

Constructor

56 | *

57 | * Selects the colour by index 58 | *

59 | * 60 | * @param index The colour's index [0–255] 61 | */ 62 | @SuppressWarnings("hiding") 63 | public Colour(final byte index) 64 | { 65 | final int[] I = COLOUR_INTENSITIVITY; 66 | 67 | int i = index; if (i < 0) i += 1 << 8; 68 | 69 | if ((this.index = i) < 16) 70 | { 71 | this.systemColour = true; 72 | this.red = new int[] { 0, 128, 0, 128, 0, 128, 0, 192, 73 | 128, 255, 0, 255, 0, 255, 0, 255} [i]; 74 | this.green = new int[] { 0, 0, 128, 128, 0, 0, 128, 192, 75 | 128, 0, 255, 255, 0, 0, 255, 255} [i]; 76 | this.blue = new int[] { 0, 0, 0, 0, 128, 128, 128, 192, 77 | 128, 0, 0, 0, 255, 255, 255, 255} [i]; 78 | } 79 | else 80 | { 81 | this.systemColour = false; 82 | if (i < 232) 83 | { 84 | final int j = i - 16, b, g; 85 | this.blue = COLOUR_INTENSITIVITY[b = j % 6]; 86 | this.green = COLOUR_INTENSITIVITY[g = ((j - b) / 6) % 6]; 87 | this.red = COLOUR_INTENSITIVITY[(j - b - g * 6) / (6 * 6)]; 88 | } 89 | else 90 | this.red = this.green = this.blue = (i - 232) * 10 + 8; 91 | } 92 | } 93 | 94 | /** 95 | *

Constructor

96 | *

97 | * Selects the colour the closest the a proper terminal colour 98 | *

99 | * 100 | * @param red The red intensity [0–255] 101 | * @param green The green intensity [0–255] 102 | * @param blue The blue intensity [0–255] 103 | */ 104 | @SuppressWarnings("hiding") 105 | public Colour(final byte red, final byte green, final byte blue) 106 | { 107 | final int[] I = COLOUR_INTENSITIVITY; 108 | 109 | int r = red ; if (r < 0) r += 1 << 8; 110 | int g = green; if (g < 0) g += 1 << 8; 111 | int b = blue ; if (b < 0) b += 1 << 8; 112 | 113 | int d, ð, dr, db, dg; dr = db = dg = 0; 114 | 115 | int ir = -1, ig = -1, ib = -1, ii = -1; 116 | 117 | d = 500; for (int cr : I) if (d > (ð = Math.abs(cr - r))) {d = ð; dr = cr; ir++;} else break; 118 | d = 500; for (int cg : I) if (d > (ð = Math.abs(cg - g))) {d = ð; dg = cg; ig++;} else break; 119 | d = 500; for (int cb : I) if (d > (ð = Math.abs(cb - b))) {d = ð; db = cb; ib++;} else break; 120 | 121 | d = (dr - r)*(dr - r) + (dg - g)*(dg - g) + (db - b)*(db - b); 122 | 123 | for (int gr = 8; gr <= 238; gr += 10) 124 | { 125 | int ðr = gr - r; 126 | int ðg = gr - g; 127 | int ðb = gr - b; 128 | 129 | ð = ðr*ðr + ðg*ðg + ðb*ðb; 130 | 131 | if (d > ð) 132 | { 133 | d = ð; 134 | dr = gr; 135 | dg = gr; 136 | db = gr; 137 | ii = (gr - 8) / 10; 138 | } 139 | } 140 | 141 | this.systemColour = false; 142 | this.red = dr; 143 | this.green = dg; 144 | this.blue = db; 145 | this.index = ii < 0 ? (16 + (ir * 6 + ig) * 6 + ib) : ii + 232; 146 | } 147 | 148 | /** 149 | *

Constructor

150 | *

151 | * Selects the colour by index 152 | *

153 | * 154 | * @param index The colour's index [0–255] 155 | */ 156 | @SuppressWarnings("hiding") 157 | public Colour(final int index) 158 | { 159 | this((byte)index); 160 | } 161 | 162 | /** 163 | *

Constructor

164 | *

165 | * Selects the colour the closest the a proper terminal colour 166 | *

167 | * 168 | * @param red The red intensity [0–255] 169 | * @param green The green intensity [0–255] 170 | * @param blue The blue intensity [0–255] 171 | */ 172 | @SuppressWarnings("hiding") 173 | public Colour(final int red, final int green, final int blue) 174 | { 175 | this((byte)red, (byte)green, (byte)blue); 176 | } 177 | 178 | /** 179 | *

Constructor

180 | *

181 | * Selects the colour the closest the a proper terminal colour 182 | *

183 | * 184 | * @param red The red intensity [0–255] 185 | * @param green The green intensity [0–255] 186 | * @param blue The blue intensity [0–255] 187 | * @param chromaWeight The weight of chromaticity [0–∞[, 1 is unweighted 188 | */ 189 | @SuppressWarnings("hiding") 190 | public Colour(final int red, final int green, final int blue, final double chromaWeight) 191 | { 192 | if ((labs == null) || (chromaWeight != lastCW)) 193 | { 194 | if (labs == null) 195 | labs = new double[240][]; 196 | for (int b = 0; b < 6; b++) 197 | for (int g = 0; g < 6; g++) 198 | for (int r = 0; r < 6; r++) 199 | labs[r * 36 + g * 6 + b] = toLab(COLOUR_INTENSITIVITY[r], 200 | COLOUR_INTENSITIVITY[g], 201 | COLOUR_INTENSITIVITY[b], 202 | chromaWeight); 203 | 204 | for (int s = 0; s < 24; s++) 205 | labs[216 + s] = toLab(GREY_EXTRA_INTENSITIVITY[s], 206 | GREY_EXTRA_INTENSITIVITY[s], 207 | GREY_EXTRA_INTENSITIVITY[s], 208 | chromaWeight); 209 | } 210 | 211 | final double[] lab = toLab(red, green, blue, chromaWeight); 212 | final double L = lab[0], a = lab[1], b = lab[2]; 213 | 214 | double d = -100.; 215 | int best = 0; 216 | 217 | for (int i = 0; i < 240; i++) 218 | { 219 | final double[] tLab = labs[i]; 220 | double ðL = L - tLab[0]; 221 | double ða = a - tLab[1]; 222 | double ðb = b - tLab[2]; 223 | 224 | double ð = ðL*ðL + ða*ða + ðb*ðb; 225 | 226 | if ((d > ð) || (d < -50.)) 227 | { 228 | d = ð; 229 | best = i; 230 | } 231 | } 232 | 233 | final Colour that = new Colour(best + 16); 234 | 235 | this.red = that.red; 236 | this.green = that.green; 237 | this.blue = that.blue; 238 | this.index = that.index; 239 | this.systemColour = that.systemColour; 240 | } 241 | 242 | 243 | 244 | /** 245 | * The red intensity [0–255] 246 | */ 247 | public final int red; 248 | 249 | /** 250 | * The green intensity [0–255] 251 | */ 252 | public final int green; 253 | 254 | /** 255 | * The blue intensity [0–255] 256 | */ 257 | public final int blue; 258 | 259 | /** 260 | * The colour's index [0–255] 261 | */ 262 | public final int index; 263 | 264 | /** 265 | * Whether the colour is a system colour 266 | */ 267 | public final boolean systemColour; 268 | 269 | 270 | /** 271 | * The static colours converted to CIELAB 272 | */ 273 | private static double[][] labs = null; 274 | 275 | 276 | /** 277 | * The chroma weight used when creating {@link #labs} 278 | */ 279 | private static double lastCW = 0; 280 | 281 | 282 | 283 | /** 284 | * {@inheritDoc} 285 | */ 286 | @Override 287 | public int hashCode() 288 | { 289 | return this.index; 290 | } 291 | 292 | /** 293 | * {@inheritDoc} 294 | */ 295 | @Override 296 | public boolean equals(final Object o) 297 | { 298 | if ((o == null) || !(o instanceof Colour)) 299 | return false; 300 | 301 | return ((Colour)o).index == this.index; 302 | } 303 | 304 | 305 | /** 306 | * Converts from sRGB to CIELAB 307 | * 308 | * @param red The red intensity [0–255] 309 | * @param green The green intensity [0–255] 310 | * @param blue The blue intensity [0–255] 311 | * @param chromaWeight The weight of chromaticity [0–∞[, 1 is unweighted 312 | * @return {L*, a*, b} 313 | */ 314 | public static double[] toLab(final int red, final int green, final int blue, final double chromaWeight) 315 | { 316 | int ir = red ; if (ir < 0) ir += 1 << 8; 317 | int ig = green; if (ig < 0) ig += 1 << 8; 318 | int ib = blue ; if (ib < 0) ib += 1 << 8; 319 | 320 | double r = ir / 255.; r = r <= 0.4045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4); 321 | double g = ig / 255.; g = g <= 0.4045 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4); 322 | double b = ib / 255.; b = b <= 0.4045 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4); 323 | 324 | double x = (0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / 0.95047; 325 | double y = (0.2126729 * r + 0.7151522 * g + 0.0721750 * b); 326 | double z = (0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / 1.08883; 327 | 328 | x = x > 0.00885642 ? Math.pow(x, 1. / 3.) : (7.78 + 703. / 99900.) * x + 0.1379310; 329 | y = y > 0.00885642 ? Math.pow(y, 1. / 3.) : (7.78 + 703. / 99900.) * y + 0.1379310; 330 | z = z > 0.00885642 ? Math.pow(z, 1. / 3.) : (7.78 + 703. / 99900.) * z + 0.1379310; 331 | 332 | final double rcL = 116 * y - 16; 333 | final double rca = 500 * (x - y) * chromaWeight; 334 | final double rcb = 200 * (y - z) * chromaWeight; 335 | 336 | return new double[] {rcL, rca, rcb}; 337 | } 338 | 339 | 340 | 341 | /** 342 | * System colour initialisation index counter 343 | */ 344 | private static byte sciic = 0; 345 | 346 | 347 | /** 348 | * System colour: black 349 | */ 350 | public static final Colour SYSTEM_BLACK = new Colour(sciic++); 351 | 352 | /** 353 | * System colour: medium red 354 | */ 355 | public static final Colour SYSTEM_RED = new Colour(sciic++); 356 | 357 | /** 358 | * System colour: medium green 359 | */ 360 | public static final Colour SYSTEM_GREEN = new Colour(sciic++); 361 | 362 | /** 363 | * System colour: medium yellow, dark orange or brown 364 | */ 365 | public static final Colour SYSTEM_YELLOW = new Colour(sciic++); 366 | 367 | /** 368 | * System colour: medium blue 369 | */ 370 | public static final Colour SYSTEM_BLUE = new Colour(sciic++); 371 | 372 | /** 373 | * System colour: medium magenta or medium lilac 374 | */ 375 | public static final Colour SYSTEM_MAGENTA = new Colour(sciic++); 376 | 377 | /** 378 | * System colour: medium cyan or medium turquoise 379 | */ 380 | public static final Colour SYSTEM_CYAN = new Colour(sciic++); 381 | 382 | /** 383 | * System colour: dark grey 384 | */ 385 | public static final Colour SYSTEM_GREY = new Colour(sciic++); 386 | 387 | /** 388 | * System colour: light grey 389 | */ 390 | public static final Colour SYSTEM_INTENSIVE_BLACK = new Colour(sciic++); 391 | 392 | /** 393 | * System colour: light red 394 | */ 395 | public static final Colour SYSTEM_INTENSIVE_RED = new Colour(sciic++); 396 | 397 | /** 398 | * System colour: light green 399 | */ 400 | public static final Colour SYSTEM_INTENSIVE_GREEN = new Colour(sciic++); 401 | 402 | /** 403 | * System colour: light yellow or medium orange 404 | */ 405 | public static final Colour SYSTEM_INTENSIVE_YELLOW = new Colour(sciic++); 406 | 407 | /** 408 | * System colour: light blue 409 | */ 410 | public static final Colour SYSTEM_INTENSIVE_BLUE = new Colour(sciic++); 411 | 412 | /** 413 | * System colour: light magenta or light lilac 414 | */ 415 | public static final Colour SYSTEM_INTENSIVE_MAGENTA = new Colour(sciic++); 416 | 417 | /** 418 | * System colour: light cyan or light turquoise 419 | */ 420 | public static final Colour SYSTEM_INTENSIVE_CYAN = new Colour(sciic++); 421 | 422 | /** 423 | * System colour: white 424 | */ 425 | public static final Colour SYSTEM_INTENSIVE_GREY = new Colour(sciic++); 426 | 427 | 428 | /** 429 | * System independent colour: pitch black 430 | */ 431 | public static final Colour PURE_BLACK = new Colour(0, 0, 0); 432 | 433 | /** 434 | * System independent colour: medium red 435 | */ 436 | public static final Colour PURE_RED = new Colour(175, 0, 0); 437 | 438 | /** 439 | * System independent colour: medium green 440 | */ 441 | public static final Colour PURE_GREEN = new Colour(0, 175, 0); 442 | 443 | /** 444 | * System independent colour: medium yellow 445 | */ 446 | public static final Colour PURE_YELLOW = new Colour(175, 175, 0); 447 | 448 | /** 449 | * System independent colour: medium blue 450 | */ 451 | public static final Colour PURE_BLUE = new Colour(0, 0, 175); 452 | 453 | /** 454 | * System independent colour: medium magenta 455 | */ 456 | public static final Colour PURE_MAGENTA = new Colour(175, 0, 175); 457 | 458 | /** 459 | * System independent colour: medium cyan 460 | */ 461 | public static final Colour PURE_CYAN = new Colour(0, 175, 175); 462 | 463 | /** 464 | * System independent colour: dark grey 465 | */ 466 | public static final Colour PURE_GREY = new Colour(198, 192, 192); 467 | 468 | /** 469 | * System independent colour: light grey 470 | */ 471 | public static final Colour PURE_INTENSIVE_BLACK = new Colour(127, 128, 128); 472 | 473 | /** 474 | * System independent colour: light red 475 | */ 476 | public static final Colour PURE_INTENSIVE_RED = new Colour(255, 0, 0); 477 | 478 | /** 479 | * System independent colour: light green 480 | */ 481 | public static final Colour PURE_INTENSIVE_GREEN = new Colour(0, 255, 0); 482 | 483 | /** 484 | * System independent colour: light yellow 485 | */ 486 | public static final Colour PURE_INTENSIVE_YELLOW = new Colour(255, 255, 0); 487 | 488 | /** 489 | * System independent colour: light blue 490 | */ 491 | public static final Colour PURE_INTENSIVE_BLUE = new Colour(0, 0, 255); 492 | 493 | /** 494 | * System independent colour: light magenta 495 | */ 496 | public static final Colour PURE_INTENSIVE_MAGENTA = new Colour(255, 0, 255); 497 | 498 | /** 499 | * System independent colour: light cyan 500 | */ 501 | public static final Colour PURE_INTENSIVE_CYAN = new Colour(0, 255, 255); 502 | 503 | /** 504 | * System independent colour: pure white 505 | */ 506 | public static final Colour PURE_INTENSIVE_GREY = new Colour(0, 255, 255); 507 | 508 | } 509 | 510 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Common.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | 22 | /** 23 | * Module common functionallity 24 | * 25 | * @author Mattias Andrée, m@maandree.se 26 | */ 27 | public class Common 28 | { 29 | /** 30 | * Non-constructor 31 | */ 32 | private Common() 33 | { 34 | assert false : "This class [Common] is not meant to be instansiated."; 35 | } 36 | // TODO padding should not move the balloon 37 | 38 | 39 | 40 | /** 41 | * Place a balloon in the top left of a {@link Pony} and create a link 42 | * 43 | * @param pony The pony to edit 44 | * @param space The additional space at the top 45 | */ 46 | public static void insertBalloon(Pony pony, int space) 47 | { 48 | int y = 0, x = 0, h = pony.matrix.length; 49 | outer: 50 | for (; y <= h; y++) 51 | { if (y == h) 52 | { y = x = -1; 53 | break; 54 | } 55 | int w = pony.matrix[y].length; 56 | for (x = 0; x < w; x++) 57 | { Pony.Cell cell = pony.matrix[y][x]; 58 | int character = cell == null ? ' ' : cell.character; 59 | if (character >= 0) 60 | { if ((character != ' ') && (character != ' ')) 61 | break outer; 62 | } 63 | else if (character == Pony.Cell.PIXELS) 64 | if ((cell.upperColour != null) && (cell.lowerColour != null)) 65 | break outer; 66 | } } 67 | 68 | if (y >= 0) 69 | { System.arraycopy(pony.matrix, 0, pony.matrix = new Pony.Cell[h + 1 + space][], 1 + space, h); 70 | System.arraycopy(pony.metamatrix, 0, pony.metamatrix = new Pony.Meta[h + 1 + space][][], 1 + space, h); 71 | int w = pony.matrix[1 + space].length; 72 | for (int i = 0, mw = w + 1; i <= space; i++) 73 | { pony.matrix[i] = new Pony.Cell[w]; 74 | pony.metamatrix[i] = new Pony.Meta[mw][]; 75 | } 76 | pony.height = h += 1 + space; 77 | y += 1 + space; 78 | if (y > x) 79 | for (int i = 0; i < h; i++) 80 | { w = pony.matrix[i].length; 81 | System.arraycopy(pony.matrix[i], 0, pony.matrix[i] = new Pony.Cell[w], 0, w); 82 | System.arraycopy(pony.metamatrix[i], 0, pony.metamatrix[i] = new Pony.Meta[w + 1][], 0, w + 1); 83 | } 84 | x -= y; 85 | if (x < -1) 86 | x = -1; 87 | 88 | for (int i = 1; i < y; i++) 89 | pony.matrix[i][x + i] = new Pony.Cell(Pony.Cell.NNW_SSE, null, null, null); 90 | } 91 | else if ((h == 0) || (pony.matrix[0].length == 0)) 92 | { pony.height = pony.width = 1; 93 | pony.matrix = new Pony.Cell[1][1]; 94 | pony.metamatrix = new Pony.Meta[1][1][]; 95 | } 96 | 97 | Pony.Balloon speechballoon = new Pony.Balloon(null, null, new Integer(Math.max(x, 5)), null, null, null, Pony.Balloon.NONE); 98 | if (pony.metamatrix[0][0] == null) 99 | pony.metamatrix[0][0] = new Pony.Meta[] { speechballoon }; 100 | else 101 | { System.arraycopy(pony.metamatrix[0][0], 0, pony.metamatrix[0][0] = new Pony.Meta[pony.metamatrix[0][0].length + 1], 1, pony.metamatrix[0][0].length - 1); 102 | pony.metamatrix[0][0][0] = speechballoon; 103 | } 104 | } 105 | 106 | 107 | /** 108 | * Change the margins in a {@link Pony} 109 | * 110 | * @param pony The the pony, the attributes {@link Pony#matrix} and {@link Pony#metamatrix} but not {@link Pony#height} nor {@link Pony#width} will be updated 111 | * @param left The left margin, negative for unmodified 112 | * @param right The right margin, negative for unmodified 113 | * @param top The top margin, negative for unmodified 114 | * @param bottom The bottom margin, negative for unmodified 115 | * @return The update {@code {left, right, top, bottom}} 116 | */ 117 | public static int[] changeMargins(Pony pony, int left, int right, int top, int bottom) 118 | { 119 | /*if ((bottom >= 0) && (top >= 0)) 120 | bottom += top;*/ 121 | 122 | Pony.Cell[][] matrix = pony.matrix; 123 | Pony.Meta[][][] metamatrix = pony.metamatrix; 124 | 125 | if (left >= 0) 126 | { 127 | int cur = 0; 128 | outer: 129 | for (int n = matrix[0].length; cur < n; cur++) 130 | for (int j = 0, m = matrix.length; j < m; j++) 131 | { 132 | boolean cellpass = true; 133 | Pony.Cell cell = matrix[j][cur]; 134 | if (cell != null) 135 | if ((cell.character != ' ') || (cell.lowerColour != null)) 136 | if ((cell.character != Pony.Cell.PIXELS) || (cell.lowerColour != null) || (cell.upperColour != null)) 137 | cellpass = false; 138 | Pony.Meta[] meta = metamatrix[j][cur]; 139 | if ((meta != null) && (meta.length != 0)) 140 | { for (int k = 0, l = meta.length; k < l; k++) 141 | if ((meta[k] != null) && ((meta[k] instanceof Pony.Store) == false)) 142 | if ((cellpass == false) || (meta[k] instanceof Pony.Balloon)) 143 | break outer; 144 | } 145 | else 146 | if (cellpass == false) 147 | break outer; 148 | } 149 | left -= cur; 150 | // if (left < 0) 151 | // { 152 | // int w = matrix[0].length; 153 | // for (int j = 0, n = matrix.length; j < n; j++) 154 | // { System.arraycopy(matrix[j], 0, matrix[j] = new Pony.Cell[w - left], -left, w); 155 | // System.arraycopy(metamatrix[j], 0, metamatrix[j] = new Pony.Meta[w + 1 - left][], -left, w + 1); 156 | // } 157 | // left = 0; 158 | // } 159 | } 160 | else 161 | left = 0; 162 | if (right >= 0) 163 | { 164 | int cur = 0; 165 | outer: 166 | for (int n = matrix[0].length - 1; cur <= n; cur++) 167 | for (int j = 0, m = matrix.length; j < m; j++) 168 | { 169 | boolean cellpass = true; 170 | Pony.Cell cell = matrix[j][n - cur]; 171 | if (cell != null) 172 | if ((cell.character != ' ') || (cell.lowerColour != null)) 173 | if ((cell.character != Pony.Cell.PIXELS) || (cell.lowerColour != null) || (cell.upperColour != null)) 174 | cellpass = false; 175 | Pony.Meta[] meta = metamatrix[j][n - cur]; 176 | if ((meta != null) && (meta.length != 0)) 177 | { for (int k = 0, l = meta.length; k < l; k++) 178 | if ((meta[k] != null) && ((meta[k] instanceof Pony.Store) == false)) 179 | if ((cellpass == false) || (meta[k] instanceof Pony.Balloon)) 180 | break outer; 181 | } 182 | else 183 | if (cellpass == false) 184 | break outer; 185 | } 186 | right -= cur; 187 | // if (right < 0) 188 | // { 189 | // int w = matrix[0].length; 190 | // for (int j = 0, n = matrix.length; j < n; j++) 191 | // { System.arraycopy(matrix[j], 0, matrix[j] = new Pony.Cell[w - right], 0, w); 192 | // System.arraycopy(metamatrix[j], 0, metamatrix[j] = new Pony.Meta[w + 1 - right][], 0, w + 1); 193 | // } 194 | // right = 0; 195 | // } 196 | } 197 | else 198 | right = 0; 199 | if (top >= 0) 200 | { 201 | int cur = 0, m = Math.min(matrix[0].length + right, matrix[0].length); 202 | outer: 203 | for (int n = matrix.length; cur < n; cur++) 204 | { Pony.Cell[] row = matrix[cur]; 205 | Pony.Meta[][] metarow = metamatrix[cur]; 206 | for (int j = Math.max(-left, 0); j < m; j++) 207 | { 208 | boolean cellpass = true; 209 | Pony.Cell cell = row[j]; 210 | if (cell != null) 211 | if ((cell.character != ' ') || (cell.lowerColour != null)) 212 | if ((cell.character != Pony.Cell.PIXELS) || (cell.lowerColour != null) || (cell.upperColour != null)) 213 | cellpass = false; 214 | Pony.Meta[] meta = metarow[j]; 215 | if ((meta != null) && (meta.length != 0)) 216 | { for (int k = 0, l = meta.length; k < l; k++) 217 | if ((meta[k] != null) && ((meta[k] instanceof Pony.Store) == false)) 218 | if ((cellpass == false) || (meta[k] instanceof Pony.Balloon)) 219 | break outer; 220 | } 221 | else 222 | if (cellpass == false) 223 | break outer; 224 | } } 225 | top -= cur; 226 | //if (top < 0) 227 | // { 228 | // int w = matrix[0].length; 229 | // System.arraycopy(matrix, 0, matrix = new Pony.Cell[matrix.length - top][], -top, matrix.length + top); 230 | // System.arraycopy(new Pony.Cell[-top][w], 0, matrix, 0, -top); 231 | // System.arraycopy(metamatrix, 0, metamatrix = new Pony.Meta[metamatrix.length - top][][], -top, metamatrix.length + top); 232 | // System.arraycopy(new Pony.Meta[-top][w + 1][], 0, metamatrix, 0, -top); 233 | // top = 0; 234 | // } 235 | } 236 | else 237 | top = 0; 238 | if (bottom >= 0) 239 | { 240 | int cur = 0, m = Math.min(matrix[0].length + right, matrix[0].length); 241 | outer: 242 | for (int n = matrix.length - 1/* + top*/; cur <= n; cur++) 243 | if (n - cur < matrix.length) 244 | { Pony.Cell[] row = matrix[n - cur]; 245 | Pony.Meta[][] metarow = metamatrix[n - cur]; 246 | for (int j = Math.max(-left, 0); j < m; j++) 247 | { 248 | boolean cellpass = true; 249 | Pony.Cell cell = row[j]; 250 | if (cell != null) 251 | if ((cell.character != ' ') || (cell.lowerColour != null)) 252 | if ((cell.character != Pony.Cell.PIXELS) || (cell.lowerColour != null) || (cell.upperColour != null)) 253 | cellpass = false; 254 | Pony.Meta[] meta = metarow[j]; 255 | if ((meta != null) && (meta.length != 0)) 256 | { for (int k = 0, l = meta.length; k < l; k++) 257 | if ((meta[k] != null) && ((meta[k] instanceof Pony.Store) == false)) 258 | if ((cellpass == false) || (meta[k] instanceof Pony.Balloon)) 259 | break outer; 260 | } 261 | else 262 | if (cellpass == false) 263 | break outer; 264 | } } 265 | bottom -= cur; 266 | // if (bottom < 0) 267 | // { 268 | // int h = matrix.length; 269 | // System.arraycopy(matrix, 0, matrix = new Pony.Cell[matrix.length - bottom][], 0, matrix.length + bottom); 270 | // System.arraycopy(new Pony.Cell[-bottom][matrix[0].length], 0, matrix, h, -bottom); 271 | // System.arraycopy(metamatrix, 0, metamatrix = new Pony.Meta[metamatrix.length - bottom][][], 0, metamatrix.length + bottom); 272 | // System.arraycopy(new Pony.Meta[-bottom][metamatrix[0].length][], 0, metamatrix, h, -bottom); 273 | // bottom = 0; 274 | // } 275 | } 276 | else 277 | bottom = 0; 278 | 279 | 280 | if (left > 0) 281 | { int w = matrix[0].length; 282 | for (int y = 0, h = matrix.length; y < h; y++) 283 | { 284 | System.arraycopy(matrix[y], 0, matrix[y] = new Pony.Cell[w + left], left, w); 285 | System.arraycopy(metamatrix[y], 0, metamatrix[y] = new Pony.Meta[w + 1 + left][], left, w + 1); 286 | } 287 | left = 0; 288 | } 289 | else 290 | left = -left; 291 | 292 | if (right > 0) 293 | { int w = matrix[0].length; 294 | for (int y = 0, h = matrix.length; y < h; y++) 295 | { 296 | System.arraycopy(matrix[y], 0, matrix[y] = new Pony.Cell[w + right], 0, w); 297 | System.arraycopy(metamatrix[y], 0, metamatrix[y] = new Pony.Meta[w + 1 + right][], 0, w + 1); 298 | } 299 | right = 0; 300 | } 301 | else 302 | right = -right; 303 | 304 | if (top > 0) 305 | { 306 | int h = matrix.length, w = matrix[0].length; 307 | Pony.Cell[][] appendix = new Pony.Cell[top][w]; 308 | System.arraycopy(matrix, 0, matrix = new Pony.Cell[h + top][], top, h); 309 | System.arraycopy(appendix, 0, matrix, 0, top); 310 | Pony.Meta[][][] metaappendix = new Pony.Meta[top][w + 1][]; 311 | System.arraycopy(metamatrix, 0, metamatrix = new Pony.Meta[h + top][w + 1][], top, h); 312 | System.arraycopy(metaappendix, 0, metamatrix, 0, top); 313 | top = 0; 314 | } 315 | else 316 | top = -top; 317 | 318 | if (bottom > 0) 319 | { 320 | int h = matrix.length, w = matrix[0].length; 321 | Pony.Cell[][] appendix = new Pony.Cell[bottom][w]; 322 | System.arraycopy(matrix, 0, matrix = new Pony.Cell[h + bottom][], 0, h); 323 | System.arraycopy(appendix, 0, matrix, h, bottom); 324 | Pony.Meta[][][] metaappendix = new Pony.Meta[bottom][w + 1][]; 325 | System.arraycopy(metamatrix, 0, metamatrix = new Pony.Meta[h + bottom][][], 0, h); 326 | System.arraycopy(metaappendix, 0, metamatrix, h, bottom); 327 | bottom = 0; 328 | } 329 | else 330 | bottom = -bottom; 331 | 332 | pony.matrix = matrix; 333 | pony.metamatrix = metamatrix; 334 | return new int[] { left, right, top, bottom }; 335 | } 336 | 337 | 338 | /** 339 | * Converts an integer array to a string with only 16-bit charaters 340 | * 341 | * @param ints The int array 342 | * @return The string 343 | */ 344 | public static String utf32to16(final int... ints) 345 | { 346 | int len = ints.length; 347 | for (final int i : ints) 348 | if (i > 0xFFFF) 349 | len++; 350 | else if (i > 0x10FFFF) 351 | throw new RuntimeException("Be serious, there is no character above plane 16."); 352 | 353 | final char[] chars = new char[len]; 354 | int ptr = 0; 355 | 356 | for (final int i : ints) 357 | if (i <= 0xFFFF) 358 | chars[ptr++] = (char)i; 359 | else 360 | { 361 | /* 10000₁₆ + (H − D800₁₆) ⋅ 400₁₆ + (L − DC00₁₆) */ 362 | 363 | int c = i - 0x10000; 364 | int L = (c & 0x3FF) + 0xDC00; 365 | int H = (c >>> 10) + 0xD800; 366 | 367 | chars[ptr++] = (char)H; 368 | chars[ptr++] = (char)L; 369 | } 370 | 371 | return new String(chars); 372 | } 373 | 374 | /** 375 | * Parse double value 376 | * 377 | * @param value String representation 378 | * @return Raw representation, -1 if not a number 379 | */ 380 | public static double parseDouble(String value) 381 | { 382 | try 383 | { return Double.parseDouble(value); 384 | } 385 | catch (Throwable err) 386 | { return -1.0; 387 | } 388 | } 389 | 390 | /** 391 | * Parse double value 392 | * 393 | * @param value String representation 394 | * @param defaultValue Default value that will be used if that string starts with a ‘y’ or ‘Y’ 395 | * @return Raw representation, -1 if not a number 396 | */ 397 | public static double parseDouble(String value, double defaultValue) 398 | { 399 | if (value.startsWith("y") || value.startsWith("Y")) 400 | return defaultValue; 401 | try 402 | { return Double.parseDouble(value); 403 | } 404 | catch (Throwable err) 405 | { return -1.0; 406 | } 407 | } 408 | 409 | /** 410 | * Parse integer value 411 | * 412 | * @param value String representation 413 | * @return Raw representation, -1 if not an integer 414 | */ 415 | public static int parseInteger(String value) 416 | { 417 | try 418 | { return Integer.parseInt(value); 419 | } 420 | catch (Throwable err) 421 | { return -1; 422 | } 423 | } 424 | 425 | /** 426 | * Parse integer value 427 | * 428 | * @param value String representation 429 | * @param defaultValue Default value that will be used if that string starts with a ‘y’ or ‘Y’ 430 | * @return Raw representation, -1 if not an integer 431 | */ 432 | public static int parseInteger(String value, int defaultValue) 433 | { 434 | if (value.startsWith("y") || value.startsWith("Y")) 435 | return defaultValue; 436 | try 437 | { return Integer.parseInt(value); 438 | } 439 | catch (Throwable err) 440 | { return -1; 441 | } 442 | } 443 | 444 | } 445 | 446 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Cowsay.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.io.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * Cowsay support module 27 | * 28 | * @author Mattias Andrée, m@maandree.se 29 | */ 30 | public class Cowsay extends Ponysay 31 | { 32 | /** 33 | * Constructor 34 | * 35 | * @param flags Flags passed to the module 36 | */ 37 | public Cowsay(HashMap flags) 38 | { 39 | super(Cowsay.modifyFlags(flags)); 40 | this.flags = flags; 41 | } 42 | 43 | 44 | 45 | /** 46 | * Flags passed to the module 47 | */ 48 | private HashMap flags; 49 | 50 | 51 | 52 | /** 53 | * Import the pony from file 54 | * 55 | * @return The pony 56 | * 57 | * @throws IOException On I/O error 58 | */ 59 | public Pony importCow() throws IOException 60 | { 61 | InputStream in = System.in; 62 | if (this.file != null) 63 | in = new BufferedInputStream(new FileInputStream(this.file)); 64 | Scanner sc = new Scanner(in, "UTF-8"); 65 | 66 | 67 | StringBuilder cow = new StringBuilder(); 68 | StringBuilder data = new StringBuilder(); 69 | boolean meta = false; 70 | 71 | cow.append("#!/usr/bin/perl\n"); 72 | cow.append("$thoughts = \"\\$\\\\\\$\";\n"); 73 | cow.append("$tongue = \"\\$tongue\\$\";\n"); 74 | cow.append("$eyes = \"\\$eye\\$eye\";\n"); 75 | while (sc.hasNextLine()) 76 | { 77 | String line = sc.nextLine(); 78 | if (line.replace("\t", "").replace(" ", "").startsWith("#")) 79 | { 80 | if (meta == false) 81 | { 82 | meta = true; 83 | data.append("$$$\n"); 84 | } 85 | line = line.substring(line.indexOf("#") + 1); 86 | if (line.equals("$$$")) 87 | line = "$$$(!)"; 88 | data.append(line + "\n"); 89 | data.append('\n'); 90 | } 91 | else 92 | { 93 | cow.append(line.replace("chop($eyes);", "\"\\$eye\\$\";\n$eyes = \"\\$eye\\$\";")); 94 | cow.append('\n'); 95 | } 96 | } 97 | if (meta) 98 | data.append("$$$\n"); 99 | cow.append("print \"$the_cow\";\n"); 100 | 101 | 102 | if (in != System.in) 103 | in.close(); 104 | 105 | 106 | String pony = new String(execCow(cow.toString()), "UTF-8"); 107 | String line = pony.substring(0, pony.indexOf('\n')); 108 | int pos = line.indexOf("$\\$") + 3; 109 | 110 | if (pos > 3) 111 | data.append("$balloon" + pos + "$"); 112 | else 113 | data.append("$balloon$"); 114 | data.append(pony); 115 | 116 | 117 | InputStream stdin = System.in; 118 | try 119 | { 120 | final byte[] streamdata = data.toString().getBytes("UTF-8"); 121 | System.setIn(new InputStream() 122 | { 123 | int ptr = 0; 124 | @Override 125 | public int read() 126 | { 127 | if (this.ptr == streamdata.length) 128 | return -1; 129 | return streamdata[this.ptr++] & 255; 130 | } 131 | @Override 132 | public int available() 133 | { 134 | return streamdata.length - this.ptr; 135 | } 136 | }); 137 | this.flags.put("file", null); 138 | Ponysay ponysay = new Ponysay(this.flags); 139 | if (ponysay.version == this.version) 140 | throw new Error("Default ponysay version should not be the cowsay version"); 141 | return ponysay.importPony(); 142 | } 143 | finally 144 | { 145 | System.setIn(stdin); 146 | } 147 | } 148 | 149 | 150 | /** 151 | * Executes a perl script 152 | * 153 | * @param cow The cow cowsay→ponysay converting script in perl 154 | * @return The cow in ponysay 155 | * 156 | * @throws IOException On perl executing failure 157 | */ 158 | public static byte[] execCow(final String cow) throws IOException 159 | { 160 | Process process = (new ProcessBuilder("perl")).start(); 161 | OutputStream cowout = process.getOutputStream(); 162 | InputStream stream = process.getInputStream(); 163 | 164 | cowout.write(cow.getBytes("UTF-8")); 165 | cowout.flush(); 166 | try 167 | { 168 | cowout.close(); 169 | } 170 | catch (final Throwable err) 171 | { 172 | //Ignore 173 | } 174 | 175 | byte[] buf = new byte[2048]; 176 | int ptr = 0; 177 | for (int d; (d = stream.read()) != -1;) 178 | { 179 | buf[ptr++] = (byte)d; 180 | if (ptr == buf.length) 181 | System.arraycopy(buf, 0, buf = new byte[ptr << 1], 0, ptr); 182 | } 183 | try 184 | { 185 | stream.close(); 186 | } 187 | catch (final Throwable err) 188 | { 189 | //Ignore 190 | } 191 | System.arraycopy(buf, 0, buf = new byte[ptr], 0, ptr); 192 | return buf; 193 | } 194 | 195 | 196 | /** 197 | * Modify the flags to fit this module 198 | * 199 | * @param flag The flags 200 | * @return The flags 201 | */ 202 | private static HashMap modifyFlags(HashMap flags) 203 | { 204 | flags.put("version", "0.1"); 205 | return flags; 206 | } 207 | 208 | } 209 | 210 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Image.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.io.*; 22 | import java.awt.Color; 23 | import java.awt.image.*; 24 | import java.util.*; 25 | import javax.imageio.*; 26 | 27 | 28 | /** 29 | * Non-terminal image support module 30 | * 31 | * @author Mattias Andrée, m@maandree.se 32 | */ 33 | public class Image 34 | { 35 | /** 36 | * Constructor 37 | * 38 | * @param flags Flags passed to the module 39 | */ 40 | public Image(HashMap flags) 41 | { 42 | this.file = (flags.containsKey("file") ? (this.file = flags.get("file")).equals("-") : true) ? null : this.file; 43 | this.balloon = this.encoded ? false : ((flags.containsKey("balloon") == false) || flags.get("balloon").toLowerCase().startsWith("y")); 44 | this.left = (flags.containsKey("left") == false) ? 3 : Common.parseInteger(flags.get("left"), 3); 45 | this.right = (flags.containsKey("right") == false) ? 0 : Common.parseInteger(flags.get("right"), 0); 46 | this.top = (flags.containsKey("top") == false) ? (this.balloon ? 3 : 1) : Common.parseInteger(flags.get("top"), this.balloon ? 3 : 1); 47 | this.bottom = (flags.containsKey("bottom") == false) ? 1 : Common.parseInteger(flags.get("bottom"), 1); 48 | this.magnified = (flags.containsKey("magnified") == false) ? 2 : Common.parseInteger(flags.get("magnified"), 2); 49 | this.encoded = flags.containsKey("encoded") && flags.get("encoded").toLowerCase().startsWith("y"); 50 | this.format = flags.containsKey("format") ? flags.get("format") : null; 51 | } 52 | 53 | 54 | 55 | /** 56 | * Input/output option: pony file 57 | */ 58 | protected String file; 59 | 60 | /** 61 | * Input/output option: left margin, negative for unmodified 62 | */ 63 | protected int left; 64 | 65 | /** 66 | * Input/output option: right margin, negative for unmodified 67 | */ 68 | protected int right; 69 | 70 | /** 71 | *

Output option: top margin, negative for unmodified

72 | *

Input option: extra number of lines between the pony and balloon (must not be negative)

73 | */ 74 | protected int top; 75 | 76 | /** 77 | * Input/Output option: bottom margin, negative for unmodified 78 | */ 79 | protected int bottom; 80 | 81 | /** 82 | * Input/output option: pixel magnification 83 | */ 84 | protected int magnified; 85 | 86 | /** 87 | * Input/output option: balloon encoding in the image 88 | */ 89 | protected boolean encoded; 90 | 91 | /** 92 | * Input/output option: insert balloon into the image 93 | */ 94 | protected boolean balloon; 95 | 96 | /** 97 | * Output option: image file format 98 | */ 99 | protected String format; 100 | 101 | 102 | 103 | /** 104 | * Import the pony from file 105 | * 106 | * @return The pony 107 | * 108 | * @throws IOException On I/O error 109 | */ 110 | public Pony importPony() throws IOException 111 | { 112 | BufferedImage image = ImageIO.read(new BufferedInputStream(this.file == null ? System.in : new FileInputStream(this.file))); 113 | int width = image.getWidth() / this.magnified; 114 | int height = image.getHeight() / this.magnified; 115 | int div = this.magnified * this.magnified; 116 | 117 | Pony.Cell cell; 118 | Pony pony = new Pony((height >> 1) + (height & 1), width, null, null); 119 | for (int y = 0; y < height; y += 2) 120 | for (int x = 0; x < width; x++) 121 | { 122 | int a = 0, r = 0, g = 0, b = 0; 123 | for (int yy = 0; yy < this.magnified; yy++) 124 | for (int xx = 0; xx < this.magnified; xx++) 125 | { int argb = image.getRGB(x * this.magnified + xx, y * this.magnified + yy); 126 | a += (argb >> 24) & 255; 127 | r += (argb >> 16) & 255; 128 | g += (argb >> 8) & 255; 129 | b += argb & 255; 130 | } 131 | a /= div; r /= div; g /= div; b /= div; 132 | pony.matrix[y >> 1][x] = cell = new Pony.Cell(Pony.Cell.PIXELS, a == 0 ? null : new Color(r, g, b, a), null, null); 133 | 134 | if ((y + 2) * this.magnified <= image.getHeight()) 135 | { 136 | a = r = g = b = 0; 137 | for (int yy = 0; yy < this.magnified; yy++) 138 | for (int xx = 0; xx < this.magnified; xx++) 139 | { int argb = image.getRGB(x * this.magnified + xx, (y + 1) * this.magnified + yy); 140 | a += (argb >> 24) & 255; 141 | r += (argb >> 16) & 255; 142 | g += (argb >> 8) & 255; 143 | b += argb & 255; 144 | } 145 | a /= div; r /= div; g /= div; b /= div; 146 | cell.lowerColour = a == 0 ? null : new Color(r, g, b, a); 147 | } 148 | 149 | if (encoded && (cell.upperColour.getAlpha() == cell.lowerColour.getAlpha())) 150 | { r = cell.upperColour.getRed(); 151 | g = cell.upperColour.getGreen(); 152 | b = cell.upperColour.getBlue(); 153 | int r2 = cell.upperColour.getRed(); 154 | int g2 = cell.upperColour.getGreen(); 155 | int b2 = cell.upperColour.getBlue(); 156 | switch (cell.upperColour.getAlpha()) 157 | { 158 | case 100: 159 | if ((r == 255) && (g == 0) && (b == 0)) 160 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.NNW_SSE, null, null, null); 161 | else if ((r == 0) && (g == 0) && (b == 255)) 162 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.NNE_SSW, null, null, null); 163 | else if ((r == 255) && (g == 0) && (b == 255)) 164 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.CROSS, null, null, null); 165 | else if ((r == 0) && (g == 255) && (b == 0)) 166 | { 167 | int bw = x, _x = x; 168 | for (x++; x < width; x++) 169 | { a = r = g = b = 0; 170 | for (int yy = 0; yy < this.magnified; yy++) 171 | for (int xx = 0; xx < this.magnified; xx++) 172 | { int argb = image.getRGB(x * this.magnified + xx, y * this.magnified + yy); 173 | a += (argb >> 24) & 255; 174 | r += (argb >> 16) & 255; 175 | g += (argb >> 8) & 255; 176 | b += argb & 255; 177 | } 178 | if ((a != 100) || (r != 0) || (g != 255) || (b != 0)) 179 | break; 180 | } 181 | bw = x-- - bw; 182 | pony.matrix[y][_x] = null; 183 | pony.metamatrix[y][_x] = new Pony.Meta[] { new Pony.Balloon( 184 | null, null, new Integer(bw), null, 185 | null, null, Pony.Balloon.NONE) }; 186 | } 187 | break; 188 | 189 | case 99: 190 | boolean jl = (r & 128) == 128; 191 | boolean jr = (g & 128) == 128; 192 | int left = r & 127; 193 | int minw = g & 127; 194 | int maxw = b; 195 | boolean jt = (r2 & 128) == 128; 196 | boolean jb = (g2 & 128) == 128; 197 | int top = r2 & 127; 198 | int minh = g2 & 127; 199 | int maxh = b2; 200 | int justification = (jl ? Pony.Balloon.LEFT : Pony.Balloon.NONE) 201 | | (jr ? Pony.Balloon.RIGHT : Pony.Balloon.NONE) 202 | | (jt ? Pony.Balloon.TOP : Pony.Balloon.NONE) 203 | | (jb ? Pony.Balloon.BOTTOM : Pony.Balloon.NONE); 204 | pony.matrix[y][x] = null; 205 | pony.metamatrix[y][x] = new Pony.Meta[] { new Pony.Balloon( 206 | left == 0 ? null : new Integer(left), top == 0 ? null : new Integer(top), 207 | minw == 0 ? null : new Integer(minw), minh == 0 ? null : new Integer(minh), 208 | maxw == 0 ? null : new Integer(maxw), maxh == 0 ? null : new Integer(maxh), 209 | justification) }; 210 | break; 211 | } } 212 | } 213 | 214 | Common.changeMargins(pony, this.left, this.right, this.balloon ? -1 : this.top, this.bottom); 215 | 216 | if (this.balloon) 217 | Common.insertBalloon(pony, this.top); 218 | 219 | pony.height = pony.matrix.length; 220 | pony.width = pony.height == 0 ? 0 : pony.matrix[0].length; 221 | return pony; 222 | } 223 | 224 | 225 | /** 226 | * Export a pony to the file 227 | * 228 | * @param pony The pony 229 | * 230 | * @throws IOException On image export error 231 | */ 232 | public void exportPony(Pony pony) throws IOException 233 | { 234 | Common.changeMargins(pony, this.left, this.right, this.top, this.bottom); 235 | int h = Math.max(1, pony.matrix.length); 236 | int w = h == 0 ? 1 : Math.max(1, pony.matrix[0].length); 237 | 238 | BufferedImage img = new BufferedImage(w * this.magnified, (h << 1) * this.magnified, BufferedImage.TYPE_INT_ARGB); 239 | Color TRANSPARENT = new Color(0, 0, 0, 0); 240 | 241 | for (int y = 0; y < h; y++) 242 | { Pony.Cell[] row = pony.matrix[y]; 243 | Pony.Meta[][] metarow = pony.metamatrix[y]; 244 | x_loop: for (int x = 0; x <= w; x++) 245 | { Pony.Meta[] metacell = metarow[x]; 246 | if (metacell != null) 247 | for (Pony.Meta meta : metacell) 248 | if (meta.getClass() == Pony.Recall.class) 249 | System.err.println("\033[01;31mutil-say: warning: ignoring recall in image, no way to parse in an image module\033[00m"); 250 | else if (meta.getClass() == Pony.Combining.class) 251 | System.err.println("\033[01;31mutil-say: warning: cannot include text in images\033[00m"); 252 | else if (this.encoded && (meta.getClass() == Pony.Balloon.class)) 253 | { 254 | Pony.Balloon balloon = (Pony.Balloon)meta; 255 | int r = 0, g = 0, b = 0, r2 = 0, g2 = 0, b2 = 0, j = balloon.justification; 256 | if ((j & Pony.Balloon.LEFT) != 0) r |= 128; 257 | if ((j & Pony.Balloon.RIGHT) != 0) g |= 128; 258 | if ((j & Pony.Balloon.TOP) != 0) r2 |= 128; 259 | if ((j & Pony.Balloon.BOTTOM) != 0) g2 |= 128; 260 | r |= balloon.left == null ? 0 : (balloon.left.intValue() & 127); 261 | g |= balloon.minWidth == null ? 0 : (balloon.minWidth.intValue() & 127); 262 | b |= balloon.maxWidth == null ? 0 : (balloon.maxWidth.intValue() & 255); 263 | r2 |= balloon.top == null ? 0 : (balloon.top.intValue() & 127); 264 | g2 |= balloon.minHeight == null ? 0 : (balloon.minHeight.intValue() & 127); 265 | b2 |= balloon.maxHeight == null ? 0 : (balloon.maxHeight.intValue() & 255); 266 | Color upper = new Color(r, g, b, 99); 267 | Color lower = new Color(r2, g2, b2, 99); 268 | int _x = x * this.magnified; 269 | int uy = (y << 1) * this.magnified; 270 | int ly = ((y << 1) | 1) * this.magnified; 271 | int u = upper.getRGB(), l = lower.getRGB(); 272 | for (int my = 0; my < this.magnified; my++) 273 | for (int mx = 0; mx < this.magnified; mx++) 274 | { img.setRGB(_x + mx, uy + my, u); 275 | img.setRGB(_x + mx, ly + my, l); 276 | } 277 | // TODO add warning for if cells should override this 278 | continue x_loop; 279 | } 280 | /*else if (meta.getClass() == Pony.Store.class) 281 | ; // Do nothing*/ 282 | 283 | if (x != w) 284 | { 285 | Pony.Cell cell = row[x]; 286 | Color upper, lower; 287 | if (cell == null) 288 | upper = lower = TRANSPARENT; 289 | else 290 | { boolean whitespace = (cell.character == ' ') || (cell.character == ' '); 291 | upper = whitespace ? cell.lowerColour : cell.upperColour; 292 | lower = cell.lowerColour; 293 | whitespace |= cell.character == Pony.Cell.PIXELS; 294 | if (cell.character == Pony.Cell.NNW_SSE) 295 | upper = lower = this.encoded ? new Color(255, 0, 0, 100) : TRANSPARENT; 296 | else if (cell.character == Pony.Cell.NNE_SSW) 297 | upper = lower = this.encoded ? new Color(0, 0, 255, 100) : TRANSPARENT; 298 | else if (cell.character == Pony.Cell.CROSS) 299 | upper = lower = this.encoded ? new Color(255, 0, 255, 100) : TRANSPARENT; 300 | else 301 | { if (!whitespace) 302 | System.err.println("\033[01;31mutil-say: warning: cannot include text in images\033[00m"); 303 | if ((upper == null) || !whitespace) upper = TRANSPARENT; 304 | if ((lower == null) || !whitespace) lower = TRANSPARENT; 305 | } } 306 | int _x = x * this.magnified; 307 | int uy = (y << 1) * this.magnified; 308 | int ly = ((y << 1) | 1) * this.magnified; 309 | int u = upper.getRGB(), l = lower.getRGB(); 310 | for (int my = 0; my < this.magnified; my++) 311 | for (int mx = 0; mx < this.magnified; mx++) 312 | { img.setRGB(_x + mx, uy + my, u); 313 | img.setRGB(_x + mx, ly + my, l); 314 | } 315 | } } } 316 | 317 | if ((pony.comment != null) && (pony.comment.length() != 0)) 318 | System.err.println("\033[01;31mutil-say: warning: not capable of exporting metadata to images\033[00m"); 319 | else if ((pony.tags != null) && (pony.tags.length != 0)) 320 | System.err.println("\033[01;31mutil-say: warning: not capable of exporting metadata to images\033[00m"); 321 | 322 | String fmt = this.format; 323 | if (fmt == null) 324 | if (this.file == null) 325 | fmt = "png"; 326 | else 327 | { fmt = this.file.contains("/") ? this.file.substring(this.file.lastIndexOf("/") + 1) : this.file; 328 | fmt = this.file.contains(".") ? this.file.substring(this.file.lastIndexOf(".") + 1) : "png"; 329 | } 330 | ImageIO.write(img, fmt, new BufferedOutputStream(this.file == null ? System.out : new FileOutputStream(this.file))); 331 | } 332 | 333 | } 334 | 335 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Pony.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.awt.Color; 22 | 23 | 24 | /** 25 | * I'm a pony! 26 | * 27 | * @author Mattias Andrée, m@maandree.se 28 | */ 29 | public class Pony 30 | { 31 | /** 32 | * Constructor 33 | * 34 | * @param heigth The height of the pony, in lines (paired pixels) 35 | * @param width The width of the pony, in columns (pixels) 36 | * @param comment Metadata comment on pony, may be {@code null} 37 | * @param tags Metadata tags, one {key, value} format, may be {@code null} 38 | */ 39 | public Pony(int height, int width, String comment, String[][] tags) 40 | { 41 | this.height = height; 42 | this.width = width; 43 | this.comment = comment; 44 | this.tags = tags; 45 | 46 | this.matrix = new Pony.Cell[height][width]; 47 | this.metamatrix = new Pony.Meta[height][width + 1][]; 48 | } 49 | 50 | 51 | 52 | /** 53 | * The height of the pony, in lines (paired pixels) 54 | */ 55 | public int height; 56 | 57 | /** 58 | * The width of the pony, in columns (pixels) 59 | */ 60 | public int width; 61 | 62 | /** 63 | * Metadata comment on pony, may be {@code null} 64 | */ 65 | public String comment; 66 | 67 | /** 68 | * Metadata tags, one {key, value} format, may be {@code null} 69 | */ 70 | public String[][] tags; 71 | 72 | /** 73 | * The cells in the pony 74 | */ 75 | public Pony.Cell[][] matrix; 76 | 77 | /** 78 | * The metacells in the pony 79 | */ 80 | public Pony.Meta[][][] metamatrix; 81 | 82 | 83 | 84 | /** 85 | * {@inheritDoc} 86 | */ 87 | @Override 88 | public Pony clone() 89 | { 90 | String[][] _tags = this.tags == null ? null : new String[this.tags.length][2]; 91 | if (this.tags != null) 92 | for (int i = 0, n = this.tags.length; i < n; i ++) 93 | { _tags[i][0] = this.tags[i][0]; 94 | _tags[i][1] = this.tags[i][1]; 95 | } 96 | Pony rc = new Pony(this.height, this.width, this.comment, _tags); 97 | for (int y = 0; y < this.height; y++) 98 | { 99 | Cell[] trow = this.matrix[y]; 100 | Cell[] rrow = rc.matrix[y]; 101 | Meta[][] tmetarow = this.metamatrix[y]; 102 | Meta[][] rmetarow = rc.metamatrix[y]; 103 | for (int x = 0; x <= this.width; x++) 104 | { if (x != this.width) 105 | rrow[x] = trow[x] == null ? null : trow[x].clone(); 106 | if (tmetarow[x] != null) 107 | { Meta[] tmetacell = tmetarow[x]; 108 | Meta[] rmetacell = rmetarow[x] = new Meta[tmetacell.length]; 109 | for (int z = 0, m = tmetacell.length; z < m; z++) 110 | rmetacell[z] = tmetacell[z] == null ? null : tmetacell[z].clone(); 111 | } } 112 | } 113 | return rc; 114 | } 115 | 116 | 117 | 118 | /** 119 | * A charcter cell in the pony 120 | */ 121 | public static class Cell 122 | { 123 | /** 124 | * Pixel pair 125 | */ 126 | public static final int PIXELS = -1; 127 | 128 | /** 129 | * NNE–SSW link 130 | */ 131 | public static final int NNE_SSW = -2; 132 | 133 | /** 134 | * NNW–SSE link 135 | */ 136 | public static final int NNW_SSE = -3; 137 | 138 | /** 139 | * Cross link 140 | */ 141 | public static final int CROSS = -4; 142 | 143 | 144 | 145 | /** 146 | * Constructor 147 | * 148 | * @param character The character duo in UTF-32, if negative it will have a special meaning, for example pixels or boolean link 149 | * @param upperColour The colour to apply to the upper pixed if a pixel, foreground colour if character, otherwise ignored, {@code null} is preferable for fully transparent 150 | * @param lowerColour The colour to apply to the lower pixed if a pixel, background colour if character, otherwise ignored, {@code null} is preferable for fully transparent 151 | * @param format Formatting to apply, nine booleans 152 | */ 153 | public Cell(int character, Color upperColour, Color lowerColour, boolean[] format) 154 | { 155 | this.character = character; 156 | this.upperColour = upperColour; 157 | this.lowerColour = lowerColour; 158 | if (format == null) 159 | this.format = new boolean[9]; 160 | else 161 | System.arraycopy(format, 0, this.format = new boolean[9], 0, 9); 162 | if ((character == PIXELS) || (character == ' ') || (character == ' ')) 163 | this.format[0] = false; 164 | } 165 | 166 | 167 | 168 | /** 169 | * The character in UTF-32, if negative it will have a special meaning, for example pixels or boolean link 170 | */ 171 | public int character; 172 | 173 | /** 174 | * The colour to apply to the upper pixed if a pixel, foreground colour if character, otherwise ignored, {@code null} is preferable for fully transparent 175 | */ 176 | public Color upperColour; 177 | 178 | /** 179 | * The colour to apply to the lower pixed if a pixel, background colour if character, otherwise ignored, {@code null} is preferable for fully transparent 180 | */ 181 | public Color lowerColour; 182 | 183 | /** 184 | * Formatting to apply, nine booleans 185 | */ 186 | public boolean[] format; 187 | 188 | 189 | 190 | /** 191 | * {@inheritDoc} 192 | */ 193 | @Override 194 | public Cell clone() 195 | { 196 | boolean[] _format = this.format == null ? null : new boolean[this.format.length]; 197 | if (this.format != null) 198 | System.arraycopy(this.format, 0, _format, 0, this.format.length); 199 | return new Cell(this.character, this.upperColour, this.lowerColour, _format); 200 | } 201 | 202 | } 203 | 204 | 205 | 206 | /** 207 | * Weird stuff going on before a cell 208 | */ 209 | public interface Meta 210 | { 211 | // Marker interface with clone support 212 | 213 | /** 214 | * {@inheritDoc} 215 | */ 216 | public Meta clone(); 217 | } 218 | 219 | 220 | 221 | /** 222 | * Combining character 223 | */ 224 | public static class Combining implements Meta 225 | { 226 | /** 227 | * Constructor 228 | * 229 | * @param character The character in UTF-32 230 | * @param foregroundColour Foreground colour to apply to the character 231 | * @param backgroundColour Background colour to apply to the character 232 | * @param format Formatting to apply, nine booleans 233 | */ 234 | public Combining(int character, Color foregroundColour, Color backgroundColour, boolean[] format) 235 | { 236 | this.character = character; 237 | this.foregroundColour = foregroundColour; 238 | this.backgroundColour = backgroundColour; 239 | if (format == null) 240 | this.format = new boolean[9]; 241 | else 242 | System.arraycopy(format, 0, this.format = new boolean[9], 0, 9); 243 | } 244 | 245 | 246 | 247 | /** 248 | * The character in UTF-32 249 | */ 250 | public int character; 251 | 252 | /** 253 | * Foreground colour to apply to the character 254 | */ 255 | public Color foregroundColour; 256 | 257 | /** 258 | * Background colour to apply to the character 259 | */ 260 | public Color backgroundColour; 261 | 262 | /** 263 | * Formatting to apply, nine booleans 264 | */ 265 | public boolean[] format; 266 | 267 | 268 | 269 | /** 270 | * {@inheritDoc} 271 | */ 272 | @Override 273 | public Combining clone() 274 | { 275 | boolean[] _format = this.format == null ? null : new boolean[this.format.length]; 276 | if (this.format != null) 277 | System.arraycopy(this.format, 0, _format, 0, this.format.length); 278 | return new Combining(this.character, this.foregroundColour, this.backgroundColour, _format); 279 | } 280 | 281 | } 282 | 283 | 284 | /** 285 | * Variable usage 286 | */ 287 | public static class Recall implements Meta 288 | { 289 | /** 290 | * Constructor 291 | * 292 | * @param name The name of the variable 293 | * @param foregroundColour Foreground colour to apply to the region 294 | * @param backgroundColour Background colour to apply to the region 295 | * @param format Formatting to apply, nine booleans 296 | */ 297 | public Recall(String name, Color foregroundColour, Color backgroundColour, boolean[] format) 298 | { 299 | this.name = name; 300 | this.foregroundColour = foregroundColour; 301 | this.backgroundColour = backgroundColour; 302 | if (format == null) 303 | this.format = new boolean[9]; 304 | else 305 | System.arraycopy(format, 0, this.format = new boolean[9], 0, 9); 306 | } 307 | 308 | 309 | 310 | /** 311 | * The name of the variable 312 | */ 313 | public String name; 314 | 315 | /** 316 | * Foreground colour to apply to the region 317 | */ 318 | public Color foregroundColour; 319 | 320 | /** 321 | * Background colour to apply to the region 322 | */ 323 | public Color backgroundColour; 324 | 325 | /** 326 | * Formatting to apply, nine booleans 327 | */ 328 | public boolean[] format; 329 | 330 | 331 | 332 | /** 333 | * {@inheritDoc} 334 | */ 335 | @Override 336 | public Recall clone() 337 | { 338 | boolean[] _format = this.format == null ? null : new boolean[this.format.length]; 339 | if (this.format != null) 340 | System.arraycopy(this.format, 0, _format, 0, this.format.length); 341 | return new Recall(this.name, this.foregroundColour, this.backgroundColour, _format); 342 | } 343 | 344 | } 345 | 346 | 347 | /** 348 | * Variable storing 349 | */ 350 | public static class Store implements Meta 351 | { 352 | /** 353 | * Constructor 354 | * 355 | * @param name The name of the variable 356 | * @parma value The value of the variable 357 | */ 358 | public Store(String name, String value) 359 | { 360 | this.name = name; 361 | this.value = value; 362 | } 363 | 364 | 365 | 366 | /** 367 | * The name of the variable 368 | */ 369 | public String name; 370 | 371 | /** 372 | * The value of the variable 373 | */ 374 | public String value; 375 | 376 | 377 | 378 | /** 379 | * {@inheritDoc} 380 | */ 381 | @Override 382 | public Store clone() 383 | { 384 | return new Store(this.name, this.value); 385 | } 386 | 387 | } 388 | 389 | 390 | /** 391 | * Balloon 392 | */ 393 | public static class Balloon implements Meta 394 | { 395 | /** 396 | * No justification applied 397 | */ 398 | public static final int NONE = 0; 399 | 400 | /** 401 | * Left justification, fill to right 402 | */ 403 | public static final int LEFT = 1; 404 | 405 | /** 406 | * Right justification, fill to left 407 | */ 408 | public static final int RIGHT = 2; 409 | 410 | /** 411 | * Centre justification, center the balloon 412 | */ 413 | public static final int CENTRE = 3; 414 | 415 | /** 416 | * Top vertical justification, fill down 417 | */ 418 | public static final int TOP = 4; 419 | 420 | /** 421 | * Bottom vertical justification, fill up 422 | */ 423 | public static final int BOTTOM = 8; 424 | 425 | /** 426 | * Middle vertical justification, center vertically 427 | */ 428 | public static final int MIDDLE = 12; 429 | 430 | 431 | 432 | /** 433 | * Constructor 434 | * 435 | * @param left Extra left offset 436 | * @param top Extra top offset 437 | * @param minWidth The minimum width of the balloon 438 | * @param minHeight The minimum height of the balloon 439 | * @param maxWidth The minimum width of the balloon 440 | * @param maxHeight The maximum height of the balloon 441 | * @param justification Balloon placement justification 442 | */ 443 | public Balloon(Integer left, Integer top, Integer minWidth, Integer minHeight, Integer maxWidth, Integer maxHeigth, int justification) 444 | { 445 | this.left = left; 446 | this.top = top; 447 | this.minWidth = minWidth; 448 | this.minHeight = minHeight; 449 | this.maxWidth = maxWidth; 450 | this.maxHeight = maxHeight; 451 | this.justification = justification; 452 | } 453 | 454 | 455 | 456 | /** 457 | * Extra left offset 458 | */ 459 | public Integer left; 460 | 461 | /** 462 | * Extra top offset 463 | */ 464 | public Integer top; 465 | 466 | /** 467 | * The minimum width of the balloon 468 | */ 469 | public Integer minWidth; 470 | 471 | /** 472 | * The minimum height of the balloon 473 | */ 474 | public Integer minHeight; 475 | 476 | /** 477 | * The maximum width of the balloon 478 | */ 479 | public Integer maxWidth; 480 | 481 | /** 482 | * The maximum height of the balloon 483 | */ 484 | public Integer maxHeight; 485 | 486 | /** 487 | * Balloon placement justification 488 | */ 489 | public int justification; 490 | 491 | 492 | 493 | /** 494 | * {@inheritDoc} 495 | */ 496 | @Override 497 | public Balloon clone() 498 | { 499 | return new Balloon(this.left, this.top, this.minWidth, this.minHeight, this.maxWidth, this.maxHeight, this.justification); 500 | } 501 | 502 | } 503 | 504 | } 505 | 506 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/PonysayHaiku.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.awt.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * Haiku xterm submodule for {@link Ponysay} 27 | * 28 | * @author Mattias Andrée, m@maandree.se 29 | */ 30 | public class PonysayHaiku extends PonysaySubmodule 31 | { 32 | /** 33 | * Constructor 34 | * 35 | * @param flags Flags passed to the module 36 | */ 37 | public PonysayHaiku(HashMap flags) 38 | { 39 | this.chroma = (flags.containsKey("chroma") == false) ? 1 : Common.parseDouble(flags.get("chroma")); 40 | this.colourful = (flags.containsKey("colourful") == false) || flags.get("colourful").toLowerCase().startsWith("y"); 41 | this.fullcolour = flags.containsKey("fullcolour") && flags.get("fullcolour").toLowerCase().startsWith("y"); 42 | this.palette = (flags.containsKey("palette") == false) ? null : PonysayXterm.parsePalette(flags.get("palette")); 43 | } 44 | 45 | 46 | 47 | /** 48 | * Output option: chroma weight, negative for sRGB distance 49 | */ 50 | protected double chroma; 51 | 52 | /** 53 | * Output option: colourful TTY 54 | */ 55 | protected boolean colourful; 56 | 57 | /** 58 | * Output option: do not limit to xterm 256 standard colours 59 | */ 60 | protected boolean fullcolour; 61 | 62 | /** 63 | * Input/output option: colour palette 64 | */ 65 | protected Color[] palette; 66 | 67 | /** 68 | * Auxiliary: whether the parsing is currectly in a CSI sequence 69 | */ 70 | private boolean csi = false; 71 | 72 | /** 73 | * Auxiliary: whether the parsing is currectly in a OSI sequence 74 | */ 75 | private boolean osi = false; 76 | 77 | /** 78 | * Auxiliary: parsing buffer 79 | */ 80 | private int[] buf = new int[256]; 81 | 82 | /** 83 | * Auxiliary: parsing buffer pointer 84 | */ 85 | private int ptr = 0; 86 | 87 | /** 88 | * Auxiliary: parsing return array 89 | */ 90 | private Object[] rcbuf = new Object[3]; 91 | 92 | 93 | 94 | /** 95 | * {@inheritDoc} 96 | */ 97 | public boolean[][] getPlains() 98 | { 99 | boolean bold = this.fullcolour; 100 | return new boolean[][] { 101 | {false, false, false, false, false, false, false, false, false}, 102 | {bold, false, false, false, false, false, false, false, false, false}, 103 | {bold, false, false, false, false, false, false, false, false}}; 104 | } 105 | 106 | 107 | /** 108 | * {@inheritDoc} 109 | */ 110 | public void initImport(Color[] colours) 111 | { 112 | if (this.palette != null) 113 | System.arraycopy(this.palette, 0, colours, 0, 16); 114 | else 115 | this.palette = parsePalette(""); 116 | } 117 | 118 | 119 | /** 120 | * {@inheritDoc} 121 | */ 122 | public String initExport(Color[] colours) 123 | { 124 | if (this.palette != null) 125 | System.arraycopy(this.palette, 0, colours, 0, 16); 126 | else 127 | this.palette = parsePalette(""); 128 | 129 | StringBuilder resetPalette = null; 130 | if (this.fullcolour) 131 | { resetPalette = new StringBuilder(); 132 | if (this.colourful) 133 | for (int i = 0; i < 16; i++) 134 | { Colour colour = new Colour(i); 135 | resetPalette.append("\033]4;"); 136 | resetPalette.append(i); 137 | resetPalette.append(";rgb:"); 138 | resetPalette.append("0123456789ABCDEF".charAt(colour.red >>> 4)); 139 | resetPalette.append("0123456789ABCDEF".charAt(colour.red & 15)); 140 | resetPalette.append('/'); 141 | resetPalette.append("0123456789ABCDEF".charAt(colour.green >>> 4)); 142 | resetPalette.append("0123456789ABCDEF".charAt(colour.green & 15)); 143 | resetPalette.append('/'); 144 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue >>> 4)); 145 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue & 15)); 146 | resetPalette.append("\033\\"); 147 | } 148 | else 149 | for (int i : new int[] { 7, 15 }) 150 | { Colour colour = new Colour(i); 151 | resetPalette.append("\033]4;"); 152 | resetPalette.append(i); 153 | resetPalette.append(";rgb:"); 154 | resetPalette.append("0123456789ABCDEF".charAt(colour.red >>> 4)); 155 | resetPalette.append("0123456789ABCDEF".charAt(colour.red & 15)); 156 | resetPalette.append('/'); 157 | resetPalette.append("0123456789ABCDEF".charAt(colour.green >>> 4)); 158 | resetPalette.append("0123456789ABCDEF".charAt(colour.green & 15)); 159 | resetPalette.append('/'); 160 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue >>> 4)); 161 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue & 15)); 162 | resetPalette.append("\033\\"); 163 | } } 164 | 165 | return resetPalette == null ? null : resetPalette.toString(); 166 | } 167 | 168 | 169 | /** 170 | * {@inheritDoc} 171 | */ // TODO cache colour matching 172 | public String applyColour(Color[] palette, Color oldBackground, Color oldForeground, boolean[] oldFormat, Color newBackground, Color newForeground, boolean[] newFormat) 173 | { 174 | StringBuilder rc = new StringBuilder(); 175 | 176 | int colourindex1back = -1, colourindex2back = -1; 177 | int colourindex1fore = -1, colourindex2fore = -1; 178 | 179 | if ((oldBackground != null) && (newBackground == null)) 180 | rc.append(";40"); 181 | else if ((oldBackground == null) || (oldBackground.equals(newBackground) == false)) 182 | if (newBackground != null) 183 | { 184 | colourindex1back = matchColour(newBackground, palette, 16, 256, this.chroma); 185 | if (this.fullcolour) 186 | colourindex2back = this.colourful ? matchColour(this.fullcolour ? newBackground : palette[colourindex1back], this.palette, 1, 6, this.chroma) : 7; 187 | else 188 | colourindex2back = colourindex1back; 189 | } 190 | 191 | if ((oldForeground != null) && (newForeground == null)) 192 | rc.append(";37"); 193 | else if ((oldForeground == null) || (oldForeground.equals(newForeground) == false)) 194 | if (newForeground != null) 195 | { 196 | colourindex1fore = matchColour(newForeground, palette, 16, 256, this.chroma); 197 | if (this.fullcolour) 198 | { int s = ((newFormat.length > 9) && newFormat[9]) ? 0 : (newFormat[0] ? 9 : 1); 199 | int e = ((newFormat.length > 9) && newFormat[9]) ? 16 : (s + 5); 200 | colourindex2fore = this.colourful ? matchColour(this.fullcolour ? newForeground : palette[colourindex1fore], this.palette, s, e, this.chroma) : 15; 201 | if (((colourindex2fore == 0) && (newBackground == null)) || (colourindex2fore == colourindex2back)) 202 | colourindex2fore ^= 8; 203 | } 204 | else 205 | colourindex2fore = colourindex1fore; 206 | } 207 | 208 | if (colourindex2back != -1) 209 | if (this.fullcolour) 210 | { Color colour = newBackground; 211 | rc.append("m\033]4;"); 212 | rc.append(colourindex2back); 213 | rc.append(";rgb:"); 214 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 215 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 216 | rc.append('/'); 217 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 218 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 219 | rc.append('/'); 220 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 221 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 222 | rc.append("\033\\\033[4"); 223 | rc.append(colourindex2back); 224 | palette[colourindex2back] = colour; 225 | } 226 | else if (colourindex2back < 16) 227 | { rc.append(";4"); 228 | rc.append(colourindex2back); 229 | } 230 | else 231 | { rc.append(";48;5;"); 232 | rc.append(colourindex2back); 233 | } 234 | 235 | if (colourindex2fore != -1) 236 | if (this.fullcolour) 237 | { Color colour = newForeground; 238 | rc.append("m\033]4;"); 239 | rc.append(colourindex2fore); 240 | rc.append(";rgb:"); 241 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 242 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 243 | rc.append('/'); 244 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 245 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 246 | rc.append('/'); 247 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 248 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 249 | rc.append("\033\\\033[3"); 250 | rc.append(colourindex2fore & 7); 251 | palette[colourindex2fore] = colour; 252 | } 253 | else if (colourindex2fore < 16) 254 | { rc.append(";3"); 255 | rc.append(colourindex2fore & 7); 256 | } 257 | else 258 | { rc.append(";38;5;"); 259 | rc.append(colourindex2fore); 260 | } 261 | 262 | boolean temp = newFormat[0]; 263 | newFormat[0] = (colourindex2fore == -1) ? oldFormat[0] : ((8 <= colourindex2fore) && (colourindex2fore < 16)); 264 | for (int i = 0; i < 9; i++) 265 | if (newFormat[i] ^ oldFormat[i]) 266 | if ((oldFormat[i] = newFormat[i])) 267 | { rc.append(";0"); 268 | rc.append(i + 1); 269 | } 270 | else 271 | { rc.append(";2"); 272 | rc.append(i + 1); 273 | } 274 | newFormat[0] = temp; 275 | 276 | String _rc = rc.toString(); 277 | if (_rc.isEmpty()) 278 | return ""; 279 | return ("\033[" + _rc.substring(1)).replace("\033[\033]", "\033]") + "m"; 280 | } 281 | 282 | 283 | /** 284 | * {@inheritDoc} 285 | */ 286 | public Object[] parseEscape(int c, Color background, Color foreground, boolean[] format, Color[] colours) 287 | { 288 | boolean escape = true; 289 | if (osi) 290 | if (this.ptr > 0) 291 | { this.buf[this.ptr++ - 1] = c; 292 | if (this.ptr == 8) 293 | { this.ptr = 0; 294 | osi = escape = false; 295 | int index = (this.buf[0] <= '9') ? (this.buf[0] & 15) : ((this.buf[0] & 15) + 9); 296 | int red = (this.buf[1] <= '9') ? (this.buf[1] & 15) : ((this.buf[1] & 15) + 9); 297 | red = (red << 4) | ((this.buf[2] <= '9') ? (this.buf[2] & 15) : ((this.buf[2] & 15) + 9)); 298 | int green = (this.buf[3] <= '9') ? (this.buf[3] & 15) : ((this.buf[3] & 15) + 9); 299 | green = (green << 4) | ((this.buf[4] <= '9') ? (this.buf[4] & 15) : ((this.buf[4] & 15) + 9)); 300 | int blue = (this.buf[5] <= '9') ? (this.buf[5] & 15) : ((this.buf[5] & 15) + 9); 301 | blue = (blue << 4) | ((this.buf[6] <= '9') ? (this.buf[6] & 15) : ((this.buf[6] & 15) + 9)); 302 | colours[index] = new Color(red, green, blue); 303 | } 304 | } 305 | else if (this.ptr < 0) 306 | { if (~this.ptr == this.buf.length) 307 | System.arraycopy(this.buf, 0, this.buf = new int[~this.ptr << 1], 0, ~this.ptr); 308 | if (c == '\\') 309 | { this.ptr = ~this.ptr; 310 | this.ptr--; 311 | if ((this.ptr > 8) && (this.buf[this.ptr] == '\033') && (this.buf[0] == ';')) 312 | { int[] _code = new int[this.ptr - 1]; 313 | System.arraycopy(this.buf, 1, _code, 0, this.ptr - 1); 314 | String[] code = Common.utf32to16(_code).split(";"); 315 | if (code.length == 2) 316 | { int index = Integer.parseInt(code[0]); 317 | code = code[1].split("/"); 318 | if ((code.length == 3) && (code[0].startsWith("rgb:"))) 319 | { code[0] = code[0].substring(4); 320 | int red = Integer.parseInt(code[0], 16); 321 | int green = Integer.parseInt(code[1], 16); 322 | int blue = Integer.parseInt(code[2], 16); 323 | colours[index] = new Color(red, green, blue); 324 | } } } 325 | this.ptr = 0; 326 | osi = escape = false; 327 | } 328 | else 329 | { this.buf[~this.ptr] = c; 330 | this.ptr--; 331 | } 332 | } 333 | else if (c == 'P') this.ptr = 1; 334 | else if (c == '4') this.ptr = ~0; 335 | else 336 | { osi = escape = false; 337 | /*items.add(new Pony.Cell('\033', foreground, background, format)); 338 | items.add(new Pony.Cell(']', foreground, background, format)); 339 | items.add(new Pony.Cell(c, foreground, background, format));*/ 340 | System.err.println("\033[01;31mutil-say: warning: bad escape sequence: OSI 0x" + Integer.toString(c) + "\033[00m"); 341 | } 342 | else if (csi) 343 | { if (this.ptr == this.buf.length) 344 | System.arraycopy(this.buf, 0, this.buf = new int[this.ptr << 1], 0, this.ptr); 345 | this.buf[this.ptr++] = c; 346 | if ((('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')) || (c == '~')) 347 | { csi = escape = false; 348 | this.ptr--; 349 | if (c == 'm') 350 | { int[] _code = new int[this.ptr]; 351 | System.arraycopy(this.buf, 0, _code, 0, this.ptr); 352 | String[] code = Common.utf32to16(_code).split(";"); 353 | int xterm256 = 0; 354 | boolean back = false; 355 | int forei = -2; 356 | for (String seg : code) 357 | { int value = Integer.parseInt(seg); 358 | if (xterm256 == 2) 359 | { xterm256 = 0; 360 | if (back) background = colours[value]; 361 | else forei = value; 362 | } 363 | else if (value == 0) 364 | { for (int i = 0; i < 9; i++) 365 | format[i] = false; 366 | background = null; 367 | forei = -1; 368 | } 369 | else if (xterm256 == 1) 370 | xterm256 = value == 5 ? 2 : 0; 371 | else if (value < 10) 372 | format[value - 1] = true; 373 | else if ((20 < value) && (value < 30)) 374 | format[value - 21] = false; 375 | else if (value == 37) forei = -1; /* Haiku uses 37 instead instead of 39 */ 376 | else if (value == 40) background = null; /* Haiku uses 40 instead instead of 49 */ 377 | else if (value == 38) xterm256 = 1; 378 | else if (value == 48) xterm256 = 1; 379 | else if (value < 38) forei = value - 30; 380 | else if (value < 48) background = colours[value - 40]; 381 | else if ((90 <= value) && (value < 98)) 382 | background = colours[value - 90 + 8]; 383 | else if ((100 <= value) && (value < 108)) 384 | forei = value - 100 + 8; 385 | if (xterm256 == 1) 386 | back = value == 48; 387 | } 388 | if (forei == -1) 389 | foreground = null; 390 | else if (forei >= 0) 391 | if ((forei < 8) && format[0]) 392 | foreground = colours[forei | 8]; 393 | else 394 | foreground = colours[forei]; 395 | } 396 | this.ptr = 0; 397 | } 398 | } 399 | else if (c == '[') 400 | { csi = true; 401 | this.ptr = 0; 402 | } 403 | else if (c == ']') 404 | osi = true; 405 | else 406 | { escape = false; 407 | /*items.add(new Pony.Cell('\033', foreground, background, format)); 408 | items.add(new Pony.Cell(c, foreground, background, format));*/ 409 | System.err.println("\033[01;31mutil-say: warning: bad escape sequence: ESC 0x" + Integer.toString(c, 16) + "\033[00m"); 410 | } 411 | 412 | this.rcbuf[0] = background; 413 | this.rcbuf[1] = foreground; 414 | this.rcbuf[2] = escape ? Boolean.TRUE : Boolean.FALSE; 415 | return this.rcbuf; 416 | } 417 | 418 | 419 | 420 | /** 421 | * Parse palette 422 | * 423 | * @param value String representation, without ESC, ] or P 424 | * @return Raw representation 425 | */ 426 | public static Color[] parsePalette(String value) 427 | { 428 | String val = null; 429 | { int ptr = 0; 430 | char[] buf = new char[value.length()]; 431 | for (int i = 0, n = buf.length; i < n; i++) 432 | { char c = value.charAt(i); 433 | if ((c != '\033') && (c != ']')) 434 | if (c == ';') 435 | if ((i > 0) && (buf[ptr - 1] == '4')) 436 | buf[ptr - 1] = '/'; 437 | else 438 | buf[ptr++] = '/'; 439 | else if (c == ':') 440 | ptr -= 3; 441 | else 442 | buf[ptr++] = c; 443 | } 444 | val = (new String(buf, 0, ptr)).toUpperCase(); 445 | } 446 | String defvalue = "00000001CD0000200CD003CDCD0041E90FF5CD00CD600CDCD7E5E5E5" 447 | + "84C4C4C9FF0000A00FF00BFFFF00C4682B4DFF00FFE00FFFFFFFFFFF"; 448 | Color[] palette = new Color[16]; 449 | for (int ptr = 0, n = defvalue.length(); ptr < n; ptr += 7) 450 | { 451 | int index = Integer.parseInt(defvalue.substring(ptr + 0, ptr + 1), 16); 452 | int red = Integer.parseInt(defvalue.substring(ptr + 1, ptr + 3), 16); 453 | int green = Integer.parseInt(defvalue.substring(ptr + 3, ptr + 5), 16); 454 | int blue = Integer.parseInt(defvalue.substring(ptr + 5, ptr + 7), 16); 455 | palette[index] = new Color(red, green, blue); 456 | } 457 | for (int ptr = 0, n = val.length(); ptr < n;) 458 | { String v = val.substring(ptr + 1, val.indexOf('\\', ptr)); 459 | ptr = v.length() + 2; 460 | String[] vs = v.split("/"); 461 | int index = Integer.parseInt(vs[0], 10); 462 | int red = Integer.parseInt(vs[1], 16); 463 | int green = Integer.parseInt(vs[2], 16); 464 | int blue = Integer.parseInt(vs[3], 16); 465 | palette[index] = new Color(red, green, blue); 466 | } 467 | return palette; 468 | } 469 | 470 | } 471 | 472 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/PonysayLinux.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.awt.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * linux (Linux VT) submodule for {@link Ponysay} 27 | * 28 | * @author Mattias Andrée, m@maandree.se 29 | */ 30 | public class PonysayLinux extends PonysaySubmodule 31 | { 32 | /** 33 | * Constructor 34 | * 35 | * @param flags Flags passed to the module 36 | */ 37 | public PonysayLinux(HashMap flags) 38 | { 39 | this.chroma = (flags.containsKey("chroma") == false) ? 1 : Common.parseDouble(flags.get("chroma")); 40 | this.colourful = (flags.containsKey("colourful") == false) || flags.get("colourful").toLowerCase().startsWith("y"); 41 | this.fullcolour = flags.containsKey("fullcolour") && flags.get("fullcolour").toLowerCase().startsWith("y"); 42 | this.palette = (flags.containsKey("palette") == false) ? null : PonysayXterm.parsePalette(flags.get("palette")); 43 | } 44 | 45 | 46 | 47 | /** 48 | * Output option: chroma weight, negative for sRGB distance 49 | */ 50 | protected double chroma; 51 | 52 | /** 53 | * Output option: colourful TTY 54 | */ 55 | protected boolean colourful; 56 | 57 | /** 58 | * Output option: do not limit to xterm 256 standard colours 59 | */ 60 | protected boolean fullcolour; 61 | 62 | /** 63 | * Input/output option: colour palette 64 | */ 65 | protected Color[] palette; 66 | 67 | /** 68 | * Auxiliary: whether the parsing is currectly in a CSI sequence 69 | */ 70 | private boolean csi = false; 71 | 72 | /** 73 | * Auxiliary: whether the parsing is currectly in a OSI sequence 74 | */ 75 | private boolean osi = false; 76 | 77 | /** 78 | * Auxiliary: parsing buffer 79 | */ 80 | private int[] buf = new int[256]; 81 | 82 | /** 83 | * Auxiliary: parsing buffer pointer 84 | */ 85 | private int ptr = 0; 86 | 87 | /** 88 | * Auxiliary: parsing return array 89 | */ 90 | private Object[] rcbuf = new Object[3]; 91 | 92 | 93 | 94 | /** 95 | * {@inheritDoc} 96 | */ 97 | public boolean[][] getPlains() 98 | { 99 | return new boolean[][] { 100 | {false, false, false, false, false, false, false, false, false}, 101 | {true, false, false, false, false, false, false, false, false, false}, 102 | {true, false, false, false, false, false, false, false, false}}; 103 | } 104 | 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | public void initImport(Color[] colours) 110 | { 111 | if (this.palette != null) 112 | System.arraycopy(this.palette, 0, colours, 0, 16); 113 | else 114 | this.palette = parsePalette(""); 115 | } 116 | 117 | 118 | /** 119 | * {@inheritDoc} 120 | */ 121 | public String initExport(Color[] colours) 122 | { 123 | if (this.palette != null) 124 | System.arraycopy(this.palette, 0, colours, 0, 16); 125 | else 126 | this.palette = parsePalette(""); 127 | 128 | StringBuilder resetPalette = new StringBuilder(); 129 | if (this.colourful) 130 | for (int i = 0; i < 16; i++) 131 | { Colour colour = new Colour(i); 132 | resetPalette.append("\033]P"); 133 | resetPalette.append("0123456789ABCDEF".charAt(i)); 134 | resetPalette.append("0123456789ABCDEF".charAt(colour.red >>> 4)); 135 | resetPalette.append("0123456789ABCDEF".charAt(colour.red & 15)); 136 | resetPalette.append("0123456789ABCDEF".charAt(colour.green >>> 4)); 137 | resetPalette.append("0123456789ABCDEF".charAt(colour.green & 15)); 138 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue >>> 4)); 139 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue & 15)); 140 | } 141 | else 142 | for (int i : new int[] { 7, 15 }) 143 | { Colour colour = new Colour(i); 144 | resetPalette.append("\033]P"); 145 | resetPalette.append("0123456789ABCDEF".charAt(i)); 146 | resetPalette.append("0123456789ABCDEF".charAt(colour.red >>> 4)); 147 | resetPalette.append("0123456789ABCDEF".charAt(colour.red & 15)); 148 | resetPalette.append("0123456789ABCDEF".charAt(colour.green >>> 4)); 149 | resetPalette.append("0123456789ABCDEF".charAt(colour.green & 15)); 150 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue >>> 4)); 151 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue & 15)); 152 | } 153 | 154 | return resetPalette == null ? null : resetPalette.toString(); 155 | } 156 | 157 | 158 | /** 159 | * {@inheritDoc} 160 | */ // TODO cache colour matching 161 | public String applyColour(Color[] palette, Color oldBackground, Color oldForeground, boolean[] oldFormat, Color newBackground, Color newForeground, boolean[] newFormat) 162 | { 163 | StringBuilder rc = new StringBuilder(); 164 | 165 | int colourindex1back = -1, colourindex2back = -1; 166 | int colourindex1fore = -1, colourindex2fore = -1; 167 | 168 | if ((oldBackground != null) && (newBackground == null)) 169 | { Color colour = palette[0] = this.palette[0]; 170 | rc.append("m\033]P0"); 171 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 172 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 173 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 174 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 175 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 176 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 177 | rc.append("\033[49"); 178 | } 179 | else if ((oldBackground == null) || (oldBackground.equals(newBackground) == false)) 180 | if (newBackground != null) 181 | { if (this.fullcolour == false) 182 | colourindex1back = matchColour(newBackground, palette, 16, 256, this.chroma); 183 | colourindex2back = this.colourful ? matchColour(this.fullcolour ? newBackground : palette[colourindex1back], this.palette, 0, 8, this.chroma) : 7; 184 | } 185 | 186 | if ((oldForeground != null) && (newForeground == null)) 187 | { Color colour = palette[7] = this.palette[7]; 188 | rc.append("m\033]P7"); 189 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 190 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 191 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 192 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 193 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 194 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 195 | rc.append("\033[39"); 196 | } 197 | else if ((oldForeground == null) || (oldForeground.equals(newForeground) == false)) 198 | if (newForeground != null) 199 | { if (this.fullcolour == false) 200 | colourindex1fore = matchColour(newForeground, palette, 16, 256, this.chroma); 201 | int s = ((newFormat.length > 9) && newFormat[9]) ? 0 : (newFormat[0] ? 8 : 0); 202 | int e = ((newFormat.length > 9) && newFormat[9]) ? 16 : (s + 8); 203 | colourindex2fore = this.colourful ? matchColour(this.fullcolour ? newForeground : palette[colourindex1fore], this.palette, s, e, this.chroma) : 15; 204 | if (((colourindex2fore == 0) && (newBackground == null)) || (colourindex2fore == colourindex2back)) 205 | colourindex2fore ^= 8; 206 | } 207 | 208 | if (colourindex2back != -1) 209 | { Color colour = this.fullcolour ? newBackground : palette[colourindex1back]; 210 | rc.append("m\033]P"); 211 | rc.append("0123456789ABCDEF".charAt(colourindex2back)); 212 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 213 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 214 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 215 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 216 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 217 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 218 | rc.append("\033[4"); 219 | rc.append(colourindex2back); 220 | } 221 | 222 | if (colourindex2fore != -1) 223 | { Color colour = this.fullcolour ? newForeground : palette[colourindex1fore]; 224 | rc.append("m\033]P"); 225 | rc.append("0123456789ABCDEF".charAt(colourindex2fore)); 226 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 227 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 228 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 229 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 230 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 231 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 232 | rc.append("\033[3"); 233 | rc.append(colourindex2fore & 7); 234 | } 235 | 236 | boolean temp = newFormat[0]; 237 | newFormat[0] = (colourindex2fore == -1) ? oldFormat[0] : ((8 <= colourindex2fore) && (colourindex2fore < 16)); 238 | for (int i = 0; i < 9; i++) 239 | if (newFormat[i] ^ oldFormat[i]) 240 | if ((oldFormat[i] = newFormat[i])) 241 | { rc.append(";0"); 242 | rc.append(i + 1); 243 | } 244 | else 245 | { rc.append(";2"); 246 | rc.append(i + 1); 247 | } 248 | newFormat[0] = temp; 249 | 250 | String _rc = rc.toString(); 251 | if (_rc.isEmpty()) 252 | return ""; 253 | return ("\033[" + _rc.substring(1)).replace("\033[\033]", "\033]") + "m"; 254 | } 255 | 256 | 257 | /** 258 | * {@inheritDoc} 259 | */ 260 | public Object[] parseEscape(int c, Color background, Color foreground, boolean[] format, Color[] colours) 261 | { 262 | boolean escape = true; 263 | if (osi) 264 | if (this.ptr > 0) 265 | { this.buf[this.ptr++ - 1] = c; 266 | if (this.ptr == 8) 267 | { this.ptr = 0; 268 | osi = escape = false; 269 | int index = (this.buf[0] <= '9') ? (this.buf[0] & 15) : ((this.buf[0] & 15) + 9); 270 | int red = (this.buf[1] <= '9') ? (this.buf[1] & 15) : ((this.buf[1] & 15) + 9); 271 | red = (red << 4) | ((this.buf[2] <= '9') ? (this.buf[2] & 15) : ((this.buf[2] & 15) + 9)); 272 | int green = (this.buf[3] <= '9') ? (this.buf[3] & 15) : ((this.buf[3] & 15) + 9); 273 | green = (green << 4) | ((this.buf[4] <= '9') ? (this.buf[4] & 15) : ((this.buf[4] & 15) + 9)); 274 | int blue = (this.buf[5] <= '9') ? (this.buf[5] & 15) : ((this.buf[5] & 15) + 9); 275 | blue = (blue << 4) | ((this.buf[6] <= '9') ? (this.buf[6] & 15) : ((this.buf[6] & 15) + 9)); 276 | colours[index] = new Color(red, green, blue); 277 | } 278 | } 279 | else if (this.ptr < 0) 280 | { if (~this.ptr == this.buf.length) 281 | System.arraycopy(this.buf, 0, this.buf = new int[~this.ptr << 1], 0, ~this.ptr); 282 | if (c == '\\') 283 | { this.ptr = ~this.ptr; 284 | this.ptr--; 285 | if ((this.ptr > 8) && (this.buf[this.ptr] == '\033') && (this.buf[0] == ';')) 286 | { int[] _code = new int[this.ptr - 1]; 287 | System.arraycopy(this.buf, 1, _code, 0, this.ptr - 1); 288 | String[] code = Common.utf32to16(_code).split(";"); 289 | if (code.length == 2) 290 | { int index = Integer.parseInt(code[0]); 291 | code = code[1].split("/"); 292 | if ((code.length == 3) && (code[0].startsWith("rgb:"))) 293 | { code[0] = code[0].substring(4); 294 | int red = Integer.parseInt(code[0], 16); 295 | int green = Integer.parseInt(code[1], 16); 296 | int blue = Integer.parseInt(code[2], 16); 297 | colours[index] = new Color(red, green, blue); 298 | } } } 299 | this.ptr = 0; 300 | osi = escape = false; 301 | } 302 | else 303 | { this.buf[~this.ptr] = c; 304 | this.ptr--; 305 | } 306 | } 307 | else if (c == 'P') this.ptr = 1; 308 | else if (c == '4') this.ptr = ~0; 309 | else 310 | { osi = escape = false; 311 | /*items.add(new Pony.Cell('\033', foreground, background, format)); 312 | items.add(new Pony.Cell(']', foreground, background, format)); 313 | items.add(new Pony.Cell(c, foreground, background, format));*/ 314 | System.err.println("\033[01;31mutil-say: warning: bad escape sequence: OSI 0x" + Integer.toString(c) + "\033[00m"); 315 | } 316 | else if (csi) 317 | { if (this.ptr == this.buf.length) 318 | System.arraycopy(this.buf, 0, this.buf = new int[this.ptr << 1], 0, this.ptr); 319 | this.buf[this.ptr++] = c; 320 | if ((('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')) || (c == '~')) 321 | { csi = escape = false; 322 | this.ptr--; 323 | if (c == 'm') 324 | { int[] _code = new int[this.ptr]; 325 | System.arraycopy(this.buf, 0, _code, 0, this.ptr); 326 | String[] code = Common.utf32to16(_code).split(";"); 327 | int xterm256 = 0; 328 | boolean back = false; 329 | int forei = -2; 330 | for (String seg : code) 331 | { int value = Integer.parseInt(seg); 332 | if (xterm256 == 2) 333 | { xterm256 = 0; 334 | if (back) background = colours[value]; 335 | else forei = value; 336 | } 337 | else if (value == 0) 338 | { for (int i = 0; i < 9; i++) 339 | format[i] = false; 340 | background = colours[0].equals(this.palette[0]) ? null : colours[0]; 341 | forei = colours[7].equals(this.palette[7]) ? -1 : 7; 342 | } 343 | else if (xterm256 == 1) 344 | xterm256 = value == 5 ? 2 : 0; 345 | else if (value < 10) 346 | format[value - 1] = true; 347 | else if ((20 < value) && (value < 30)) 348 | format[value - 21] = false; 349 | else if (value == 39) forei = colours[7].equals(this.palette[7]) ? -1 : 7; 350 | else if (value == 49) background = colours[0].equals(this.palette[0]) ? null : colours[0]; 351 | else if (value == 38) xterm256 = 1; 352 | else if (value == 48) xterm256 = 1; 353 | else if (value < 38) forei = value - 30; 354 | else if (value < 48) background = colours[value - 40]; 355 | if (xterm256 == 1) 356 | back = value == 48; 357 | } 358 | if (forei == -1) 359 | foreground = null; 360 | else if (forei >= 0) 361 | if ((forei < 8) && format[0]) 362 | foreground = colours[forei | 8]; 363 | else 364 | foreground = colours[forei]; 365 | } 366 | this.ptr = 0; 367 | } 368 | } 369 | else if (c == '[') 370 | { csi = true; 371 | this.ptr = 0; 372 | } 373 | else if (c == ']') 374 | osi = true; 375 | else 376 | { escape = false; 377 | /*items.add(new Pony.Cell('\033', foreground, background, format)); 378 | items.add(new Pony.Cell(c, foreground, background, format));*/ 379 | System.err.println("\033[01;31mutil-say: warning: bad escape sequence: ESC 0x" + Integer.toString(c, 16) + "\033[00m"); 380 | } 381 | 382 | this.rcbuf[0] = background; 383 | this.rcbuf[1] = foreground; 384 | this.rcbuf[2] = escape ? Boolean.TRUE : Boolean.FALSE; 385 | return this.rcbuf; 386 | } 387 | 388 | 389 | 390 | /** 391 | * Parse palette 392 | * 393 | * @param value String representation, without ESC, ] or P 394 | * @return Raw representation 395 | */ 396 | public static Color[] parsePalette(String value) 397 | { 398 | String val = null; 399 | { int ptr = 0; 400 | char[] buf = new char[value.length()]; 401 | for (int i = 0, n = buf.length; i < n; i++) 402 | { char c = value.charAt(i); 403 | if ((c != '\033') && (c != ']') && (c != 'P')) 404 | buf[ptr++] = c; 405 | } 406 | val = (new String(buf, 0, ptr)).toUpperCase(); 407 | } 408 | String defvalue = "00000001AA0000200AA003AA550040000AA5AA00AA600AAAA7AAAAAA" 409 | + "85555559FF5555A55FF55BFFFF55C5555FFDFF55FFE55FFFFFFFFFFF"; 410 | Color[] palette = new Color[16]; 411 | for (int ptr = 0, n = defvalue.length(); ptr < n; ptr += 7) 412 | { 413 | int index = Integer.parseInt(defvalue.substring(ptr + 0, ptr + 1), 16); 414 | int red = Integer.parseInt(defvalue.substring(ptr + 1, ptr + 3), 16); 415 | int green = Integer.parseInt(defvalue.substring(ptr + 3, ptr + 5), 16); 416 | int blue = Integer.parseInt(defvalue.substring(ptr + 5, ptr + 7), 16); 417 | palette[index] = new Color(red, green, blue); 418 | } 419 | for (int ptr = 0, n = val.length(); ptr < n; ptr += 7) 420 | { 421 | int index = Integer.parseInt(val.substring(ptr + 0, ptr + 1), 16); 422 | int red = Integer.parseInt(val.substring(ptr + 1, ptr + 3), 16); 423 | int green = Integer.parseInt(val.substring(ptr + 3, ptr + 5), 16); 424 | int blue = Integer.parseInt(val.substring(ptr + 5, ptr + 7), 16); 425 | palette[index] = new Color(red, green, blue); 426 | } 427 | return palette; 428 | } 429 | 430 | } 431 | 432 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/PonysaySubmodule.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.awt.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * Platform submodule interface for {@link Ponysay} 27 | * 28 | * @author Mattias Andrée, m@maandree.se 29 | */ 30 | public abstract class PonysaySubmodule 31 | { 32 | /** 33 | * Get the plain format styles to use 34 | * 35 | * @return {plain for text, plain for one pixel, plain for two pixels} 36 | */ 37 | public abstract boolean[][] getPlains(); 38 | 39 | /** 40 | * Initialise the import 41 | * 42 | * @param colours The supermodule's colour palette 43 | */ 44 | public abstract void initImport(Color[] colours); 45 | 46 | /** 47 | * Initialise the export and return a string used to reset the colour palette 48 | * 49 | * @param colours The supermodule's colour palette 50 | * @return String to print to reset the colour palette 51 | */ 52 | public abstract String initExport(Color[] colours); 53 | 54 | /** 55 | * Get ANSI colour sequence to append to the output 56 | * 57 | * @param palette The current colour palette 58 | * @param oldBackground The current background colour 59 | * @param oldForeground The current foreground colour 60 | * @param oldFormat The current text format 61 | * @param newBackground The new background colour 62 | * @param newForeground The new foreground colour 63 | * @param newFormat The new text format 64 | */ 65 | public abstract String applyColour(Color[] palette, Color oldBackground, Color oldForeground, boolean[] oldFormat, Color newBackground, Color newForeground, boolean[] newFormat); 66 | 67 | /** 68 | * Parse escape sequences 69 | * 70 | * @param c The current character in the parsing 71 | * @param background The current background colour 72 | * @param foreground The current foreground colour 73 | * @param format The current format, may be updated in-place 74 | * @param colours The current palette, may be updated in-place 75 | * @return The background colour, the foreground colour and state: {@code {background, foreground, Boolean.TRUE|Boolean.FALSE}}, 76 | * wheere the state is true as along as the escape parsing has not completed 77 | */ 78 | public abstract Object[] parseEscape(int c, Color background, Color foreground, boolean[] format, Color[] colours); 79 | 80 | 81 | 82 | /** 83 | * Colour CIELAB value cache 84 | */ 85 | private static ThreadLocal> labMap = new ThreadLocal>(); 86 | 87 | /** 88 | * Chroma weight using in {@link #labMap} 89 | */ 90 | private static ThreadLocal labMapWeight = new ThreadLocal(); 91 | 92 | 93 | 94 | /** 95 | * Get the closest matching colour 96 | * 97 | * @param colour The colour to match 98 | * @param palette The palette for which to match 99 | * @param paletteStart The beginning of the usable part of the palette 100 | * @param paletteEnd The exclusive end of the usable part of the palette 101 | * @param chromaWeight The chroma weight, negative for sRGB distance 102 | * @return The index of the closest colour in the palette 103 | */ 104 | protected static int matchColour(Color colour, Color[] palette, int paletteStart, int paletteEnd, double chromaWeight) 105 | { 106 | if (chromaWeight < 0.0) 107 | { 108 | int bestI = paletteStart; 109 | int bestD = 4 * 256 * 256; 110 | for (int i = paletteStart; i < paletteEnd; i++) 111 | { 112 | int ðr = colour.getRed() - palette[i].getRed(); 113 | int ðg = colour.getGreen() - palette[i].getGreen(); 114 | int ðb = colour.getBlue() - palette[i].getBlue(); 115 | 116 | int ð = ðr*ðr + ðg*ðg + ðb*ðb; 117 | if (bestD > ð) 118 | { bestD = ð; 119 | bestI = i; 120 | } 121 | } 122 | return bestI; 123 | } 124 | 125 | Double _chroma = labMapWeight.get(); 126 | HashMap _labMap = ((_chroma == null) || (_chroma.doubleValue() != chromaWeight)) ? null : labMap.get(); 127 | if (_labMap == null) 128 | { labMap.set(_labMap = new HashMap()); 129 | labMapWeight.set(new Double(chromaWeight)); 130 | } 131 | 132 | double[] lab = _labMap.get(colour); 133 | /* if (lab == null) */ // FIXME Why does this not work!? 134 | _labMap.put(colour, lab = Colour.toLab(colour.getRed(), colour.getGreen(), colour.getBlue(), chromaWeight)); 135 | double L = lab[0], a = lab[1], b = lab[2]; 136 | 137 | int bestI = -1; 138 | double bestD = 0.0; 139 | Color p; 140 | for (int i = paletteStart; i < paletteEnd; i++) 141 | { 142 | double[] tLab = _labMap.get(p = palette[i]); 143 | /* if (tLab == null) */ // FIXME Why does this not work!? 144 | _labMap.put(colour, tLab = Colour.toLab(p.getRed(), p.getGreen(), p.getBlue(), chromaWeight)); 145 | double ðL = L - tLab[0]; 146 | double ða = a - tLab[1]; 147 | double ðb = b - tLab[2]; 148 | 149 | double ð = ðL*ðL + ða*ða + ðb*ðb; 150 | if ((bestD > ð) || (bestI < 0)) 151 | { bestD = ð; 152 | bestI = i; 153 | } 154 | } 155 | 156 | return bestI; 157 | } 158 | } 159 | 160 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/PonysayXterm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.awt.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * xterm-256color submodule for {@link Ponysay} 27 | * 28 | * @author Mattias Andrée, m@maandree.se 29 | */ 30 | public class PonysayXterm extends PonysaySubmodule 31 | { 32 | /** 33 | * Constructor 34 | * 35 | * @param flags Flags passed to the module 36 | */ 37 | public PonysayXterm(HashMap flags) 38 | { 39 | this.chroma = (flags.containsKey("chroma") == false) ? 1 : Common.parseDouble(flags.get("chroma")); 40 | this.colourful = (flags.containsKey("colourful") == false) || flags.get("colourful").toLowerCase().startsWith("y"); 41 | this.fullcolour = flags.containsKey("fullcolour") && flags.get("fullcolour").toLowerCase().startsWith("y"); 42 | this.palette = (flags.containsKey("palette") == false) ? null : PonysayXterm.parsePalette(flags.get("palette")); 43 | } 44 | 45 | 46 | 47 | /** 48 | * Output option: chroma weight, negative for sRGB distance 49 | */ 50 | protected double chroma; 51 | 52 | /** 53 | * Output option: colourful TTY 54 | */ 55 | protected boolean colourful; 56 | 57 | /** 58 | * Output option: do not limit to xterm 256 standard colours 59 | */ 60 | protected boolean fullcolour; 61 | 62 | /** 63 | * Input/output option: colour palette 64 | */ 65 | protected Color[] palette; 66 | 67 | // KEYWORD when colourlabs supports convertion from sRGB, enabled preceptional distance 68 | 69 | /** 70 | * Auxiliary: whether the parsing is currectly in a CSI sequence 71 | */ 72 | private boolean csi = false; 73 | 74 | /** 75 | * Auxiliary: whether the parsing is currectly in a OSI sequence 76 | */ 77 | private boolean osi = false; 78 | 79 | /** 80 | * Auxiliary: parsing buffer 81 | */ 82 | private int[] buf = new int[256]; 83 | 84 | /** 85 | * Auxiliary: parsing buffer pointer 86 | */ 87 | private int ptr = 0; 88 | 89 | /** 90 | * Auxiliary: parsing return array 91 | */ 92 | private Object[] rcbuf = new Object[3]; 93 | 94 | 95 | 96 | /** 97 | * {@inheritDoc} 98 | */ 99 | public boolean[][] getPlains() 100 | { 101 | boolean bold = this.fullcolour; 102 | return new boolean[][] { 103 | {false, false, false, false, false, false, false, false, false}, 104 | {bold, false, false, false, false, false, false, false, false, false}, 105 | {bold, false, false, false, false, false, false, false, false}}; 106 | } 107 | 108 | 109 | /** 110 | * {@inheritDoc} 111 | */ 112 | public void initImport(Color[] colours) 113 | { 114 | if (this.palette != null) 115 | System.arraycopy(this.palette, 0, colours, 0, 16); 116 | else 117 | this.palette = parsePalette(""); 118 | } 119 | 120 | 121 | /** 122 | * {@inheritDoc} 123 | */ 124 | public String initExport(Color[] colours) 125 | { 126 | if (this.palette != null) 127 | System.arraycopy(this.palette, 0, colours, 0, 16); 128 | else 129 | this.palette = parsePalette(""); 130 | 131 | StringBuilder resetPalette = null; 132 | if (this.fullcolour) 133 | { resetPalette = new StringBuilder(); 134 | if (this.colourful) 135 | for (int i = 0; i < 16; i++) 136 | { Colour colour = new Colour(i); 137 | resetPalette.append("\033]4;"); 138 | resetPalette.append(i); 139 | resetPalette.append(";rgb:"); 140 | resetPalette.append("0123456789ABCDEF".charAt(colour.red >>> 4)); 141 | resetPalette.append("0123456789ABCDEF".charAt(colour.red & 15)); 142 | resetPalette.append('/'); 143 | resetPalette.append("0123456789ABCDEF".charAt(colour.green >>> 4)); 144 | resetPalette.append("0123456789ABCDEF".charAt(colour.green & 15)); 145 | resetPalette.append('/'); 146 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue >>> 4)); 147 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue & 15)); 148 | resetPalette.append("\033\\"); 149 | } 150 | else 151 | for (int i : new int[] { 7, 15 }) 152 | { Colour colour = new Colour(i); 153 | resetPalette.append("\033]4;"); 154 | resetPalette.append(i); 155 | resetPalette.append(";rgb:"); 156 | resetPalette.append("0123456789ABCDEF".charAt(colour.red >>> 4)); 157 | resetPalette.append("0123456789ABCDEF".charAt(colour.red & 15)); 158 | resetPalette.append('/'); 159 | resetPalette.append("0123456789ABCDEF".charAt(colour.green >>> 4)); 160 | resetPalette.append("0123456789ABCDEF".charAt(colour.green & 15)); 161 | resetPalette.append('/'); 162 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue >>> 4)); 163 | resetPalette.append("0123456789ABCDEF".charAt(colour.blue & 15)); 164 | resetPalette.append("\033\\"); 165 | } } 166 | 167 | return resetPalette == null ? null : resetPalette.toString(); 168 | } 169 | 170 | 171 | /** 172 | * {@inheritDoc} 173 | */ // TODO cache colour matching 174 | public String applyColour(Color[] palette, Color oldBackground, Color oldForeground, boolean[] oldFormat, Color newBackground, Color newForeground, boolean[] newFormat) 175 | { 176 | StringBuilder rc = new StringBuilder(); 177 | 178 | int colourindex1back = -1, colourindex2back = -1; 179 | int colourindex1fore = -1, colourindex2fore = -1; 180 | 181 | if ((oldBackground != null) && (newBackground == null)) 182 | rc.append(";49"); 183 | else if ((oldBackground == null) || (oldBackground.equals(newBackground) == false)) 184 | if (newBackground != null) 185 | { 186 | colourindex1back = matchColour(newBackground, palette, 16, 256, this.chroma); 187 | if (this.fullcolour) 188 | colourindex2back = this.colourful ? matchColour(this.fullcolour ? newBackground : palette[colourindex1back], this.palette, 0, 8, this.chroma) : 7; 189 | else 190 | colourindex2back = colourindex1back; 191 | } 192 | 193 | if ((oldForeground != null) && (newForeground == null)) 194 | rc.append(";39"); 195 | else if ((oldForeground == null) || (oldForeground.equals(newForeground) == false)) 196 | if (newForeground != null) 197 | { 198 | colourindex1fore = matchColour(newForeground, palette, 16, 256, this.chroma); 199 | if (this.fullcolour) 200 | { int s = ((newFormat.length > 9) && newFormat[9]) ? 0 : (newFormat[0] ? 8 : 0); 201 | int e = ((newFormat.length > 9) && newFormat[9]) ? 16 : (s + 8); 202 | colourindex2fore = this.colourful ? matchColour(this.fullcolour ? newForeground : palette[colourindex1fore], this.palette, s, e, this.chroma) : 15; 203 | if (((colourindex2fore == 0) && (newBackground == null)) || (colourindex2fore == colourindex2back)) 204 | colourindex2fore ^= 8; 205 | } 206 | else 207 | colourindex2fore = colourindex1fore; 208 | } 209 | 210 | if (colourindex2back != -1) 211 | if (this.fullcolour) 212 | { Color colour = newBackground; 213 | rc.append("m\033]4;"); 214 | rc.append(colourindex2back); 215 | rc.append(";rgb:"); 216 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 217 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 218 | rc.append('/'); 219 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 220 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 221 | rc.append('/'); 222 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 223 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 224 | rc.append("\033\\\033[4"); 225 | rc.append(colourindex2back); 226 | palette[colourindex2back] = colour; 227 | } 228 | else if (colourindex2back < 16) 229 | { rc.append(";4"); 230 | rc.append(colourindex2back); 231 | } 232 | else 233 | { rc.append(";48;5;"); 234 | rc.append(colourindex2back); 235 | } 236 | 237 | if (colourindex2fore != -1) 238 | if (this.fullcolour) 239 | { Color colour = newForeground; 240 | rc.append("m\033]4;"); 241 | rc.append(colourindex2fore); 242 | rc.append(";rgb:"); 243 | rc.append("0123456789ABCDEF".charAt(colour.getRed() >>> 4)); 244 | rc.append("0123456789ABCDEF".charAt(colour.getRed() & 15)); 245 | rc.append('/'); 246 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() >>> 4)); 247 | rc.append("0123456789ABCDEF".charAt(colour.getGreen() & 15)); 248 | rc.append('/'); 249 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() >>> 4)); 250 | rc.append("0123456789ABCDEF".charAt(colour.getBlue() & 15)); 251 | rc.append("\033\\\033[3"); 252 | rc.append(colourindex2fore & 7); 253 | palette[colourindex2fore] = colour; 254 | } 255 | else if (colourindex2fore < 16) 256 | { rc.append(";3"); 257 | rc.append(colourindex2fore & 7); 258 | } 259 | else 260 | { rc.append(";38;5;"); 261 | rc.append(colourindex2fore); 262 | } 263 | 264 | boolean temp = newFormat[0]; 265 | newFormat[0] = (colourindex2fore == -1) ? oldFormat[0] : ((8 <= colourindex2fore) && (colourindex2fore < 16)); 266 | for (int i = 0; i < 9; i++) 267 | if (newFormat[i] ^ oldFormat[i]) 268 | if ((oldFormat[i] = newFormat[i])) 269 | { rc.append(";0"); 270 | rc.append(i + 1); 271 | } 272 | else 273 | { rc.append(";2"); 274 | rc.append(i + 1); 275 | } 276 | newFormat[0] = temp; 277 | 278 | String _rc = rc.toString(); 279 | if (_rc.isEmpty()) 280 | return ""; 281 | return ("\033[" + _rc.substring(1)).replace("\033[\033]", "\033]") + "m"; 282 | } 283 | 284 | 285 | /** 286 | * {@inheritDoc} 287 | */ 288 | public Object[] parseEscape(int c, Color background, Color foreground, boolean[] format, Color[] colours) 289 | { 290 | boolean escape = true; 291 | if (osi) 292 | if (this.ptr > 0) 293 | { this.buf[this.ptr++ - 1] = c; 294 | if (this.ptr == 8) 295 | { this.ptr = 0; 296 | osi = escape = false; 297 | int index = (this.buf[0] <= '9') ? (this.buf[0] & 15) : ((this.buf[0] & 15) + 9); 298 | int red = (this.buf[1] <= '9') ? (this.buf[1] & 15) : ((this.buf[1] & 15) + 9); 299 | red = (red << 4) | ((this.buf[2] <= '9') ? (this.buf[2] & 15) : ((this.buf[2] & 15) + 9)); 300 | int green = (this.buf[3] <= '9') ? (this.buf[3] & 15) : ((this.buf[3] & 15) + 9); 301 | green = (green << 4) | ((this.buf[4] <= '9') ? (this.buf[4] & 15) : ((this.buf[4] & 15) + 9)); 302 | int blue = (this.buf[5] <= '9') ? (this.buf[5] & 15) : ((this.buf[5] & 15) + 9); 303 | blue = (blue << 4) | ((this.buf[6] <= '9') ? (this.buf[6] & 15) : ((this.buf[6] & 15) + 9)); 304 | colours[index] = new Color(red, green, blue); 305 | } 306 | } 307 | else if (this.ptr < 0) 308 | { if (~this.ptr == this.buf.length) 309 | System.arraycopy(this.buf, 0, this.buf = new int[~this.ptr << 1], 0, ~this.ptr); 310 | if (c == '\\') 311 | { this.ptr = ~this.ptr; 312 | this.ptr--; 313 | if ((this.ptr > 8) && (this.buf[this.ptr] == '\033') && (this.buf[0] == ';')) 314 | { int[] _code = new int[this.ptr - 1]; 315 | System.arraycopy(this.buf, 1, _code, 0, this.ptr - 1); 316 | String[] code = Common.utf32to16(_code).split(";"); 317 | if (code.length == 2) 318 | { int index = Integer.parseInt(code[0]); 319 | code = code[1].split("/"); 320 | if ((code.length == 3) && (code[0].startsWith("rgb:"))) 321 | { code[0] = code[0].substring(4); 322 | int red = Integer.parseInt(code[0], 16); 323 | int green = Integer.parseInt(code[1], 16); 324 | int blue = Integer.parseInt(code[2], 16); 325 | colours[index] = new Color(red, green, blue); 326 | } } } 327 | this.ptr = 0; 328 | osi = escape = false; 329 | } 330 | else 331 | { this.buf[~this.ptr] = c; 332 | this.ptr--; 333 | } 334 | } 335 | else if (c == 'P') this.ptr = 1; 336 | else if (c == '4') this.ptr = ~0; 337 | else 338 | { osi = escape = false; 339 | /*items.add(new Pony.Cell('\033', foreground, background, format)); 340 | items.add(new Pony.Cell(']', foreground, background, format)); 341 | items.add(new Pony.Cell(c, foreground, background, format));*/ 342 | System.err.println("\033[01;31mutil-say: warning: bad escape sequence: OSI 0x" + Integer.toString(c) + "\033[00m"); 343 | } 344 | else if (csi) 345 | { if (this.ptr == this.buf.length) 346 | System.arraycopy(this.buf, 0, this.buf = new int[this.ptr << 1], 0, this.ptr); 347 | this.buf[this.ptr++] = c; 348 | if ((('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')) || (c == '~')) 349 | { csi = escape = false; 350 | this.ptr--; 351 | if (c == 'm') 352 | { int[] _code = new int[this.ptr]; 353 | System.arraycopy(this.buf, 0, _code, 0, this.ptr); 354 | String[] code = Common.utf32to16(_code).split(";"); 355 | int xterm256 = 0; 356 | boolean back = false; 357 | int forei = -2; 358 | for (String seg : code) 359 | { int value = Integer.parseInt(seg); 360 | if (xterm256 == 2) 361 | { xterm256 = 0; 362 | if (back) background = colours[value]; 363 | else forei = value; 364 | } 365 | else if (value == 0) 366 | { for (int i = 0; i < 9; i++) 367 | format[i] = false; 368 | background = null; 369 | forei = -1; 370 | } 371 | else if (xterm256 == 1) 372 | xterm256 = value == 5 ? 2 : 0; 373 | else if (value < 10) 374 | format[value - 1] = true; 375 | else if ((20 < value) && (value < 30)) 376 | format[value - 21] = false; 377 | else if (value == 39) forei = -1; 378 | else if (value == 49) background = null; 379 | else if (value == 38) xterm256 = 1; 380 | else if (value == 48) xterm256 = 1; 381 | else if (value < 38) forei = value - 30; 382 | else if (value < 48) background = colours[value - 40]; 383 | else if ((90 <= value) && (value < 98)) 384 | background = colours[value - 90 + 8]; 385 | else if ((100 <= value) && (value < 108)) 386 | forei = value - 100 + 8; 387 | if (xterm256 == 1) 388 | back = value == 48; 389 | } 390 | if (forei == -1) 391 | foreground = null; 392 | else if (forei >= 0) 393 | if ((forei < 8) && format[0]) 394 | foreground = colours[forei | 8]; 395 | else 396 | foreground = colours[forei]; 397 | } 398 | this.ptr = 0; 399 | } 400 | } 401 | else if (c == '[') 402 | { csi = true; 403 | this.ptr = 0; 404 | } 405 | else if (c == ']') 406 | osi = true; 407 | else 408 | { escape = false; 409 | /*items.add(new Pony.Cell('\033', foreground, background, format)); 410 | items.add(new Pony.Cell(c, foreground, background, format));*/ 411 | System.err.println("\033[01;31mutil-say: warning: bad escape sequence: ESC 0x" + Integer.toString(c, 16) + "\033[00m"); 412 | } 413 | 414 | this.rcbuf[0] = background; 415 | this.rcbuf[1] = foreground; 416 | this.rcbuf[2] = escape ? Boolean.TRUE : Boolean.FALSE; 417 | return this.rcbuf; 418 | } 419 | 420 | 421 | 422 | /** 423 | * Parse palette 424 | * 425 | * @param value String representation, without ESC, ] or P 426 | * @return Raw representation 427 | */ 428 | public static Color[] parsePalette(String value) 429 | { 430 | String val = null; 431 | { int ptr = 0; 432 | char[] buf = new char[value.length()]; 433 | for (int i = 0, n = buf.length; i < n; i++) 434 | { char c = value.charAt(i); 435 | if ((c != '\033') && (c != ']') && (c != 'P')) 436 | if (c == ';') 437 | if ((i > 0) && (buf[ptr - 1] == '4')) 438 | buf[ptr - 1] = '/'; 439 | else 440 | buf[ptr++] = '/'; 441 | else if (c == ':') 442 | ptr -= 3; 443 | else 444 | buf[ptr++] = c; 445 | } 446 | val = (new String(buf, 0, ptr)).toUpperCase(); 447 | } 448 | String defvalue = "00000001AA0000200AA003AA550040000AA5AA00AA600AAAA7AAAAAA" 449 | + "85555559FF5555A55FF55BFFFF55C5555FFDFF55FFE55FFFFFFFFFFF"; 450 | Color[] palette = new Color[16]; 451 | for (int ptr = 0, n = defvalue.length(); ptr < n; ptr += 7) 452 | { 453 | int index = Integer.parseInt(defvalue.substring(ptr + 0, ptr + 1), 16); 454 | int red = Integer.parseInt(defvalue.substring(ptr + 1, ptr + 3), 16); 455 | int green = Integer.parseInt(defvalue.substring(ptr + 3, ptr + 5), 16); 456 | int blue = Integer.parseInt(defvalue.substring(ptr + 5, ptr + 7), 16); 457 | palette[index] = new Color(red, green, blue); 458 | } 459 | for (int ptr = 0, n = val.length(); ptr < n;) 460 | { int index, red, green, blue; 461 | if (val.charAt(ptr) == '/') 462 | { String v = val.substring(ptr + 1, val.indexOf('\\', ptr)); 463 | ptr = v.length() + 2; 464 | String[] vs = v.split("/"); 465 | index = Integer.parseInt(vs[0], 10); 466 | red = Integer.parseInt(vs[1], 16); 467 | green = Integer.parseInt(vs[2], 16); 468 | blue = Integer.parseInt(vs[3], 16); 469 | } 470 | else 471 | { index = Integer.parseInt(val.substring(ptr + 0, ptr + 1), 16); 472 | red = Integer.parseInt(val.substring(ptr + 1, ptr + 3), 16); 473 | green = Integer.parseInt(val.substring(ptr + 3, ptr + 5), 16); 474 | blue = Integer.parseInt(val.substring(ptr + 5, ptr + 7), 16); 475 | ptr += 7; 476 | } 477 | palette[index] = new Color(red, green, blue); 478 | } 479 | return palette; 480 | } 481 | 482 | } 483 | 484 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Program.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.io.*; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * Program selector for util-say 27 | * 28 | * @author Mattias Andrée, m@maandree.se 29 | */ 30 | public class Program 31 | { 32 | /** 33 | * Non-constructor 34 | */ 35 | private Program() 36 | { 37 | assert false : "This class [Program] is not meant to be instansiated."; 38 | } 39 | 40 | 41 | 42 | /** 43 | * This is the main entry point of the program 44 | * 45 | * @param args Startup arguments 46 | * 47 | * @throws IOException On I/O exception 48 | */ 49 | public static void main(final String... args) throws IOException 50 | { 51 | if ((args.length == 0) || ((args.length == 1) && args[0].equals("--help"))) 52 | { 53 | System.out.println("Copyright (C) 2012, 2013 Mattias Andrée "); 54 | System.out.println(); 55 | System.out.println("USAGE: ponytool --import module [param*] {--export module [param*]}"); 56 | System.out.println(); 57 | System.out.println(); 58 | System.out.println("This program is free software: you can redistribute it and/or modify"); 59 | System.out.println("it under the terms of the GNU General Public License as published by"); 60 | System.out.println("the Free Software Foundation, either version 3 of the License, or"); 61 | System.out.println("(at your option) any later version."); 62 | System.out.println(); 63 | System.out.println("This program is distributed in the hope that it will be useful,"); 64 | System.out.println("but WITHOUT ANY WARRANTY; without even the implied warranty of"); 65 | System.out.println("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"); 66 | System.out.println("GNU General Public License for more details."); 67 | System.out.println(); 68 | System.out.println("You should have received a copy of the GNU General Public License"); 69 | System.out.println("along with this program. If not, see ."); 70 | System.out.println(); 71 | System.out.println(); 72 | return; 73 | } 74 | 75 | HashMap params = new HashMap(); 76 | String pname = null; 77 | HashMap inparams = null; 78 | String intype = null; 79 | final ArrayList> outparams = new ArrayList>(); 80 | final ArrayList outtypes = new ArrayList(); 81 | 82 | for (final String arg : args) 83 | if (arg.equals("--in") || arg.equals("--import") || arg.equals("--out") || arg.equals("--export")) 84 | { if (pname != null) 85 | params.put(pname, "yes"); 86 | pname = arg.intern(); 87 | } 88 | else if ((pname == "--in") || (pname == "--import")) 89 | { inparams = params = new HashMap(); 90 | intype = arg.toLowerCase().intern(); 91 | pname = null; 92 | } 93 | else if ((pname == "--out") || (pname == "--export")) 94 | { outparams.add(params = new HashMap()); 95 | outtypes.add(arg.toLowerCase().intern()); 96 | pname = null; 97 | } 98 | else if (arg.startsWith("--") || (pname == null)) 99 | { if (pname != null) 100 | params.put(pname, "yes"); 101 | int eq = arg.indexOf("="); 102 | if (eq < 0) 103 | pname = arg.replace("-", ""); 104 | else 105 | { pname = null; 106 | params.put(arg.substring(0, eq).replace("-", ""), arg.substring(eq + 1)); 107 | } } 108 | else 109 | { params.put(pname, arg); 110 | pname = null; 111 | } 112 | 113 | Pony pony = null; 114 | if (intype == "ponysay") pony = (new Ponysay(inparams)).importPony(); 115 | else if (intype == "unisay") pony = (new Unisay (inparams)).importPony(); 116 | else if (intype == "cowsay") pony = (new Cowsay (inparams)).importPony(); 117 | else if (intype == "cat") pony = (new Cat (inparams)).importPony(); 118 | else if (intype == "image") pony = (new Image (inparams)).importPony(); 119 | else if (intype == "test") pony = (new Test (inparams)).importPony(); 120 | //TODO add warning 121 | 122 | for (int i = 0, n = outtypes.size(); i < n; i++) 123 | { final String outtype = outtypes.get(i); 124 | params = outparams.get(i); 125 | 126 | if (outtype == "ponysay") (new Ponysay(params)).exportPony(pony); 127 | else if (outtype == "unisay") (new Unisay (params)).exportPony(pony); 128 | else if (outtype == "cowsay") (new Cowsay (params)).exportPony(pony); 129 | else if (outtype == "cat") (new Cat (params)).exportPony(pony); 130 | else if (outtype == "image") (new Image (params)).exportPony(pony); 131 | else if (outtype == "test") (new Test (params)).exportPony(pony); 132 | //TODO add warning 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Raw.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.io.*; 22 | import java.awt.Color; 23 | import java.util.*; 24 | 25 | 26 | /** 27 | * Uncooked pony module 28 | * 29 | * @author Mattias Andrée, m@maandree.se 30 | */ 31 | public class Raw 32 | { 33 | /** 34 | * Constructor 35 | * 36 | * @param flags Flags passed to the module 37 | */ 38 | public Raw(HashMap flags) 39 | { 40 | this.file = (flags.containsKey("file") ? (this.file = flags.get("file")).equals("-") : true) ? null : this.file; 41 | } 42 | 43 | 44 | 45 | /** 46 | * Input/output option: pony file 47 | */ 48 | protected String file; 49 | 50 | 51 | 52 | /** 53 | * Import the pony from file 54 | * 55 | * @return The pony 56 | * 57 | * @throws IOException On I/O error 58 | */ 59 | public Pony importPony() throws IOException 60 | { 61 | return null; 62 | } 63 | 64 | 65 | /** 66 | * Export a pony to the file 67 | * 68 | * @param pony The pony 69 | * 70 | * @throws IOException On I/O error 71 | */ 72 | public void exportPony(Pony pony) throws IOException 73 | { 74 | OutputStream out = null; 75 | try 76 | { out = this.file == null ? System.out : new BufferedOutputStream(new FileOutputStream(this.file)); 77 | 78 | print(pony.height, out); 79 | print(pony.width, out); 80 | print(pony.comment, out); 81 | 82 | if (pony.tags == null) 83 | out.write('-'); 84 | else 85 | { out.write('{'); 86 | for (String[] tag : pony.tags) 87 | { if (tag == null) 88 | { out.write('-'); 89 | continue; 90 | } 91 | out.write('('); 92 | for (String s : tag) 93 | print(s, out); 94 | out.write(')'); 95 | } 96 | out.write('}'); 97 | } 98 | 99 | if (pony.matrix == null) 100 | out.write('-'); 101 | else 102 | { out.write('{'); 103 | for (Pony.Cell[] row : pony.matrix) 104 | { if (row == null) 105 | { out.write('-'); 106 | continue; 107 | } 108 | out.write('['); 109 | for (Pony.Cell cell : row) 110 | { if (cell == null) 111 | { out.write('-'); 112 | continue; 113 | } 114 | print(cell.character, out); 115 | print(cell.upperColour, out); 116 | print(cell.lowerColour, out); 117 | print(cell.format, out); 118 | } 119 | out.write(']'); 120 | } 121 | out.write('}'); 122 | } 123 | 124 | if (pony.metamatrix == null) 125 | out.write('-'); 126 | else 127 | { out.write('{'); 128 | for (Pony.Meta[][] row : pony.metamatrix) 129 | { if (row == null) 130 | { out.write('-'); 131 | continue; 132 | } 133 | out.write('['); 134 | for (Pony.Meta[] cell : row) 135 | { if (cell == null) 136 | { out.write('-'); 137 | continue; 138 | } 139 | out.write('('); 140 | for (Pony.Meta meta : cell) 141 | if (meta == null) 142 | out.write('-'); 143 | else if (meta instanceof Pony.Combining) 144 | { out.write('T'); 145 | Pony.Combining m = (Pony.Combining)meta; 146 | print(m.character, out); 147 | print(m.foregroundColour, out); 148 | print(m.backgroundColour, out); 149 | print(m.format, out); 150 | } 151 | else if (meta instanceof Pony.Recall) 152 | { out.write('$'); 153 | Pony.Recall m = (Pony.Recall)meta; 154 | print(m.name, out); 155 | print(m.foregroundColour, out); 156 | print(m.backgroundColour, out); 157 | print(m.format, out); 158 | } 159 | else if (meta instanceof Pony.Store) 160 | { out.write('='); 161 | Pony.Store m = (Pony.Store)meta; 162 | print(m.name, out); 163 | print(m.value, out); 164 | } 165 | else if (meta instanceof Pony.Balloon) 166 | { out.write('B'); 167 | Pony.Balloon m = (Pony.Balloon)meta; 168 | print(m.left, out); 169 | print(m.top, out); 170 | print(m.minWidth, out); 171 | print(m.minHeight, out); 172 | print(m.maxWidth, out); 173 | print(m.maxHeight, out); 174 | print(m.justification, out); 175 | } 176 | out.write(')'); 177 | } 178 | out.write(']'); 179 | } 180 | out.write('}'); 181 | } 182 | 183 | out.flush(); 184 | } 185 | finally 186 | { if ((out != null) && (out != System.out)) 187 | try 188 | { out.close(); 189 | } 190 | catch (Throwable ignore) 191 | { // Ignore 192 | } } 193 | } 194 | 195 | 196 | private void print(byte value, OutputStream out) throws IOException 197 | { 198 | if ((value >>> 4) != 0) 199 | { out.write((value & 15) + 'a'); 200 | value >>>= 4; 201 | } 202 | out.write((value & 15) + 'A'); 203 | } 204 | 205 | private void print(int value, OutputStream out) throws IOException 206 | { 207 | if (value < 0) 208 | { out.write('/'); 209 | value = ~value; 210 | } 211 | while ((value >>> 4) != 0) 212 | { out.write((value & 15) + 'a'); 213 | value >>>= 4; 214 | } 215 | out.write((value & 15) + 'A'); 216 | } 217 | 218 | private void print(Integer value, OutputStream out) throws IOException 219 | { 220 | if (value == null) 221 | out.write('-'); 222 | else 223 | print(value.intValue(), out); 224 | } 225 | 226 | private void print(String value, OutputStream out) throws IOException 227 | { 228 | if (value == null) 229 | { out.write(1); 230 | return; 231 | } 232 | for (byte c : value.getBytes("UTF-8")) 233 | if ((c == 0) || (c == 1)) 234 | { out.write(192); 235 | out.write(128 | c); 236 | } 237 | else 238 | out.write(c); 239 | out.write(0); 240 | } 241 | 242 | private void print(boolean[] value, OutputStream out) throws IOException 243 | { 244 | if (value == null) 245 | { out.write('-'); 246 | return; 247 | } 248 | if (value.length == 0) 249 | { out.write('@'); 250 | return; 251 | } 252 | _if:{ 253 | if (value.length <= 18) 254 | { for (boolean b : value) 255 | if (b) 256 | break _if; 257 | out.write(value.length + '-'); 258 | return; 259 | }} 260 | for (int i = 0, n = value.length; i < n; i += 4) 261 | { 262 | int c = Math.max(n - i, 4); 263 | int v = 0; 264 | for (int j = 0; j < c; j++) 265 | v |= value[i + j] ? (1 << c) : 0; 266 | if (c == 4) v += 'a'; 267 | else if (c == 3) v += 'q'; 268 | else if (c == 2) v += 'y'; 269 | else if (c == 1) v += ']'; 270 | if (i + 4 < n) 271 | v += 0x20; 272 | out.write(v); 273 | } 274 | } 275 | 276 | private void print(Color value, OutputStream out) throws IOException 277 | { 278 | if (value == null) 279 | { out.write('-'); 280 | return; 281 | } 282 | print(value.getAlpha(), out); 283 | print(value.getRed(), out); 284 | print(value.getGreen(), out); 285 | print(value.getBlue(), out); 286 | } 287 | 288 | } 289 | 290 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Test.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.io.*; 22 | import java.awt.Color; 23 | import java.util.*; 24 | 25 | 26 | /** 27 | * Test module 28 | * 29 | * @author Mattias Andrée, m@maandree.se 30 | */ 31 | public class Test 32 | { 33 | /** 34 | * Constructor 35 | * 36 | * @param flags Flags passed to the module 37 | */ 38 | public Test(HashMap flags) 39 | { 40 | // Do nothing 41 | } 42 | 43 | 44 | 45 | /** 46 | * Import a test pony 47 | * 48 | * @return The pony 49 | * 50 | * @throws IOException On I/O error 51 | */ 52 | public Pony importPony() throws IOException 53 | { 54 | int Y = 10, X = 20; 55 | Pony pony = new Pony(Y, X, "Test pony", new String[][] {{"PONY", "test"}}); 56 | /**/ 57 | for (int y = 0; y < Y; y++) 58 | for (int x = 0; x < X; x++) 59 | if ((y < 5) ^ (x < 10)) 60 | if ((x & 2) == 2) 61 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.PIXELS, Color.BLUE, Color.RED, null); 62 | else 63 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.PIXELS, Color.RED, Color.BLUE, null); 64 | else 65 | if ((x & 2) == 2) 66 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.PIXELS, Color.GREEN, Color.YELLOW, null); 67 | else 68 | pony.matrix[y][x] = new Pony.Cell(Pony.Cell.PIXELS, Color.YELLOW, Color.GREEN, null); 69 | /**/ 70 | return pony; 71 | } 72 | 73 | 74 | /** 75 | * Test a pony against a test pony 76 | * 77 | * @param pony The pony 78 | */ 79 | public void exportPony(Pony pony) 80 | { 81 | } 82 | 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/se/kth/maandree/utilsay/Unisay.java: -------------------------------------------------------------------------------- 1 | /** 2 | * util-say — Utilities for cowsay and cowsay-like programs 3 | * 4 | * Copyright © 2012, 2013 Mattias Andrée (m@maandree.se) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package se.kth.maandree.utilsay; 20 | 21 | import java.util.*; 22 | 23 | 24 | /** 25 | * Unisay support module 26 | * 27 | * @author Mattias Andrée, m@maandree.se 28 | */ 29 | public class Unisay extends Ponysay 30 | { 31 | /** 32 | * Constructor 33 | * 34 | * @param flags Flags passed to the module 35 | */ 36 | public Unisay(HashMap flags) 37 | { 38 | super(Unisay.modifyFlags(flags)); 39 | } 40 | 41 | 42 | 43 | /** 44 | * Modify the flags to fit this module 45 | * 46 | * @param flag The flags 47 | * @return The flags 48 | */ 49 | private static HashMap modifyFlags(HashMap flags) 50 | { 51 | flags.put("version", "2.1"); 52 | return flags; 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /unisay2img: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | magnified='1' 4 | file='-' 5 | dash=0 6 | 7 | for arg in "$@"; do 8 | if [ $dash = 1 ]; then 9 | file="$arg" 10 | elif [ "$arg" = "--" ]; then 11 | dash=1 12 | elif [ "$arg" = "-2" ]; then 13 | magnified=2 14 | else 15 | file="$arg" 16 | fi 17 | done 18 | 19 | java -jar "$(dirname "$0")/util-say.jar" \ 20 | --import unisay --file - --export image --magnified $magnified --file "$file" --left - --right - --bottom - --top - 21 | 22 | -------------------------------------------------------------------------------- /util-say.texinfo: -------------------------------------------------------------------------------- 1 | \input texinfo @c -*-texinfo-*- 2 | 3 | @c %**start of header 4 | @setfilename util-say.info 5 | @settitle util-say 6 | @afourpaper 7 | @documentencoding UTF-8 8 | @documentlanguage en 9 | @finalout 10 | @c %**end of header 11 | @set VERSION 3.0 12 | 13 | @defindex op 14 | 15 | 16 | @copying 17 | This manual is for util-say 18 | (version @value{VERSION}). 19 | 20 | Copyright @copyright{} 2013 Mattias Andrée 21 | 22 | @quotation 23 | Permission is granted to copy, distribute and/or modify this document 24 | under the terms of the GNU Free Documentation License, Version 1.3 or 25 | any later version published by the Free Software Foundation; with no 26 | Invariant Sections, with no Front-Cover Texts, and with no Back-Cover 27 | Texts. A copy of the license is included in the section entitled 28 | ``GNU Free Documentation License''. 29 | @end quotation 30 | @end copying 31 | 32 | @ifnottex 33 | @node Top 34 | @top util-say: Tools for creating ponies for ponysay and ponysay-like programs 35 | @insertcopying 36 | @end ifnottex 37 | 38 | @titlepage 39 | @title util-say 40 | @subtitle Tools for creating ponies for ponysay and ponysay-like programs 41 | @c ** start of front page image ** 42 | @c If print make a pdf or hard copy with the front cover 43 | @c you may or may not want to remove this. 44 | @c @image{infoimage,423.5px} 45 | @c ** end of front page image ** 46 | @author by Mattias Andrée (maandree) 47 | 48 | @page 49 | @vskip 0pt plus 1filll 50 | @insertcopying 51 | @end titlepage 52 | 53 | @contents 54 | 55 | 56 | @menu 57 | * Overview:: Brief overview of util-say. 58 | * Background:: Brief background. 59 | * Using the converter:: Using the converter, @command{ponytool}. 60 | * Usage examples:: Examples of basic commands. 61 | * GNU Free Documentation License:: Copying and sharing this manual. 62 | * Concept index:: Concept index. 63 | * Option index:: Option index. 64 | @end menu 65 | 66 | 67 | 68 | 69 | @node Overview 70 | @chapter Overview 71 | @cindex overview 72 | 73 | util-say was historically collection of utilities for creating images for ponysay, 74 | cowsay and similiar programs, hence its name. Nowadays however, it is basically 75 | one utility with a set of modules, called @command{ponytool}, but more utilities 76 | will be added over time. 77 | 78 | @command{ponytool} is used convert an image into another format or change the image's 79 | configurations. Theses images are images be simply rendered in a terminal, and 80 | naturally normal images formats are supported so normal images can be made into 81 | terminal renderable. 82 | 83 | 84 | 85 | @node Background 86 | @chapter Background 87 | @cindex background 88 | @cindex terminal images 89 | @cindex images, terminal 90 | 91 | @cindex glyphs 92 | @cindex Unicode 93 | @cindex UCS 94 | @cindex monospaced fonts 95 | @cindex fonts, monospaces 96 | @cindex block elements 97 | Unicode defines glyphs made for drawing, specifically it defines block elements, 98 | these include glyphs that splits the glyph area vertically in half and definied 99 | all for combinations for filling or keeping empty each half. The empty glyphs 100 | beginng the blankspace character. In monospaced fonts theses are definied to 101 | be of same size. 102 | 103 | @cindex font dimensions 104 | @cindex dimensions, font 105 | @cindex pixel perfect 106 | @cindex mosiac, structure 107 | A font that precisly adheres to the previous paragraph and have the total font 108 | exactly height twice of the width is called pixel perfect to the extend of the 109 | our usage. This lets or use just four, or even only three, glyphs to build 110 | mosiac pictures of characters representing two pixels each, vertically stacked. 111 | 112 | @cindex colour 113 | An essential part of letting one glyph represent two pixels is that terminals 114 | support both foreground and background colours, as well as 240 well definied 115 | colours as well as full transparency. 116 | 117 | This is the foundation that lets use print nice looking images in the terminal, 118 | and it can be done in @command{cat}:able text files, for details on the formats 119 | please refer to the documentation for that specific program. 120 | 121 | 122 | 123 | @node Using the converter 124 | @chapter Using the converter 125 | @cindex converter 126 | @cindex @command{ponytool} 127 | 128 | @cindex importing 129 | @cindex exporting 130 | The command @command{ponytool} is used to convert images between formats and 131 | configurations, it has the capability of importing one file and exporting 132 | multiple files. As you may notice, @command{ponytool} does not support short 133 | options, only long options, and all options takes exactly one argument. 134 | 135 | @cindex invoking 136 | @cindex selecting file 137 | @cindex file, select 138 | @opindex @option{import} 139 | @opindex @option{export} 140 | @opindex @option{in} 141 | @opindex @option{out} 142 | @opindex @option{file} 143 | To use the converter, run command 144 | @command{ponytool --import module [parameters...] @{--export module [parameters...]@}}. 145 | Note that parameters must be added after their @option{--import} or 146 | @option{--export}. Alternatively you can use @option{--in} instead of 147 | @option{--import} or @option{--out} instead of @option{--export}. 148 | Associated with @option{--import} and @option{--export} is a module take 149 | must follow directly. All modules, before for export and import, honors 150 | @option{--file} that takes the file to read to or write from and defaults 151 | to stdin or stdout, @code{-} begin the explicit value for those, and will 152 | note be listed in the module's documention's. If you want your file to 153 | start with @code{--} you will either need to prepend @code{./} or make 154 | @option{--file} and the file name one argument seperated with @code{=}, 155 | the latter works for all options. 156 | 157 | @cindex modules 158 | Supported modules are @code{ponysay}, @code{unisay}, @code{cowsay} and 159 | @code{image}, all being case insensitive and support both import and export. 160 | 161 | 162 | @menu 163 | * Ponysay module:: Using the @code{ponysay} module. 164 | * Unisay module:: Using the @code{unisay} module. 165 | * Cowsay module:: Using the @code{cowsay} module. 166 | * Cat module:: Using the @code{cat} module. 167 | * Image module:: Using the @code{image} module. 168 | @end menu 169 | 170 | 171 | @node Ponysay module 172 | @section @code{ponysay} module 173 | @cindex @code{ponysay} module 174 | @cindex module, @code{ponysay} 175 | 176 | @opindex @option{version} 177 | The @code{ponysay} module lets you import and export @command{ponysay} 178 | ponies of any @command{ponysay} version. To select the which version of 179 | @command{ponysay} to use, use the option @option{--version}, the value 180 | is version string (numerals seperated by dots). The version will always 181 | default to the latest version of @command{ponysay}, if not yet stabil. 182 | 183 | The following version spans exists: 184 | 185 | @table @asis 186 | @item @bullet{} 0 @math{<=} @option{version} @math{<} 2.1 187 | @cindex @command{cowsay} 188 | @command{cowsay} format. 189 | @item @bullet{} 2.1 @math{<=} @option{version} @math{<} 2.9 190 | @cindex @command{unisay} 191 | @command{unisay} format. 192 | @item @bullet{} 2.9 @math{<=} @option{version} @math{<} 3 193 | Metadata support is added. 194 | @item @bullet{} 3 @math{<=} @option{version} 195 | Horizontal balloon justification is added. 196 | @end table 197 | 198 | The additional options are supported: 199 | 200 | @table @asis 201 | @item @option{--ignoreballoon} (export/import) 202 | @opindex @option{ignoreballoon} 203 | @cindex ignore balloons 204 | @cindex balloons, ignore 205 | Specifies whether to remove all balloons but not links and substitute with 206 | transparent pixels. To enable this, use a value starting with @code{y} or 207 | @code{Y}. 208 | 209 | @item @option{--ignorelink} (export/import) 210 | @opindex @option{ignorelink} 211 | @cindex ignore links 212 | @cindex links, ignore 213 | Specifies whether to remove all balloons links and substitute with 214 | transparent pixels. To enable this, use a value starting with @code{y} or 215 | @code{Y}, the value defaults to the value of @option{--ignoreballoon}. 216 | 217 | @item @option{--even} (export) 218 | @opindex @option{even} 219 | @cindex right padding 220 | @cindex padding, right 221 | @cindex padding, even 222 | @cindex even out 223 | Specifies whether to pad the the lines to make all lines of same visual 224 | lenght. To disable this, use a value starting with @code{n} or @code{N}. 225 | This option is only available in @option{--export}. 226 | 227 | @item @option{--fullblocks} (export) 228 | @opindex @option{fullblocks} 229 | @cindex block elements 230 | @cindex full blocks 231 | Specifies whether full block elements can be used when a pixel pair has the 232 | same colours; in modes where the paletter is modified, this allows better 233 | selection of colour index. To disable this, use a value starting with 234 | @code{n} or @code{N}. This option is only available in @option{--export}. 235 | 236 | @item @option{--spacesave} (export) 237 | @opindex @option{spacesave} 238 | @cindex compression 239 | @cindex file size 240 | Specifies whether to prefer small files over making the image look good 241 | while marked in a terminal. To enable this, use a value starting with 242 | @code{y} or @code{Y}. This option is only available in @option{--export}. 243 | 244 | @item @option{--zebra} (export) 245 | @opindex @option{zebra} 246 | @cindex zebra effect 247 | Specifies whether to use half block elements when the pixel's an pixel 248 | pair has the same colours. To enable this, use a value starting with 249 | @code{y} or @code{Y}. This option is only available in @option{--export}. 250 | 251 | @item @option{--utf8} (export) 252 | @opindex @option{utf8} 253 | @cindex cowsay encoding 254 | @cindex encoding, escaping 255 | @cindex UTF-8 256 | This option is only available when using the @command{cowsay} format in 257 | @option{--export}, it is manditorly enabled when not using the 258 | @command{cowsay} format. It specifies whether to encode the files in 259 | UTF-8, rather then using escaped strings. To enable this, use a value 260 | starting with @code{y} or @code{Y}. 261 | 262 | @item @option{--escesc} (export) 263 | @opindex @option{escesc} 264 | @cindex cowsay encoding 265 | @cindex encoding, escaping 266 | This option is only available when using the @command{cowsay} format in 267 | @option{--export}, it is manditorly disabled when not using the 268 | @command{cowsay} format. It specifies whether to escape the escape 269 | characters in the files. To enable this, use a value starting with 270 | @code{y} or @code{Y}. 271 | 272 | @item @option{--left} (export) 273 | @opindex @option{left} 274 | @cindex margins 275 | Fix the number emptying columns at the left side of the pony, if the value 276 | cannot be parsed as a non-negative integer, for example @code{-}, the margin 277 | will not be modified from the original image. This options is import 278 | supported in @option{--export}. 279 | Use a value starting with a @code{y} or @code{Y} make the program use a 280 | default value. 281 | 282 | @item @option{--right} (export) 283 | @opindex @option{right} 284 | @cindex margins 285 | Fix the number emptying columns at the right side of the pony, if the value 286 | cannot be parsed as a non-negative integer, for example @code{-}, the margin 287 | will not be modified from the original image. This options is import 288 | supported in @option{--export}. 289 | 290 | Use a value starting with a @code{y} or @code{Y} make the program use a 291 | default value. 292 | 293 | @item @option{--top} (export) 294 | @opindex @option{top} 295 | @cindex margins 296 | Fix the number emptying lines at the top of the pony, if the value cannot 297 | be parsed as a non-negative integer, for example @code{-}, the margin will 298 | not be modified from the original image. This options is import supported 299 | in @option{--export}. 300 | 301 | Use a value starting with a @code{y} or @code{Y} make the program use a 302 | default value. 303 | 304 | @item @option{--bottom} (export) 305 | @opindex @option{bottom} 306 | @cindex margins 307 | Fix the number emptying lines at the bottom of the pony, if the value cannot 308 | be parsed as a non-negative integer, for example @code{-}, the margin will 309 | not be modified from the original image. This options is import supported 310 | in @option{--export}. 311 | 312 | Use a value starting with a @code{y} or @code{Y} make the program use a 313 | default value. 314 | 315 | @item @option{--balloon} (export/import) 316 | @opindex @option{balloon} 317 | @cindex balloon, insert 318 | This specifies whether to insert a balloon and its link at the top left of 319 | the image. Specift the number of lines between the balloon and the pony or 320 | a value starting with a @code{y} or @code{Y} for a default value. 321 | 322 | @item @option{--platform} (export/import) 323 | @opindex @option{platform} 324 | @cindex platform 325 | @cindex system 326 | Specifies a submodule for which platform the pony is or should be encoded. 327 | These submodules supports additional options. The platform string is case 328 | insensitive and ignores hyphens, underscores and blankspaces and trets 329 | @code{colours}, @code{colour}, @code{colors} and @code{color} all as the 330 | same word. 331 | @end table 332 | 333 | 334 | @menu 335 | * XTerm submodule:: Using the @code{xterm-256color} submodule. 336 | * Linux VT submodule:: Using the @code{linux} submodule. 337 | * Haiku submodule:: Using the @code{haiku} submodule. 338 | @end menu 339 | 340 | 341 | @node XTerm submodule 342 | @subsection XTerm submodule 343 | @cindex @code{xterm}, platform 344 | @cindex platform, @code{xterm} 345 | @cindex system, @code{xterm} 346 | 347 | @code{xterm}, or @code{xterm-256color}, is the default module, in @option{--import} 348 | is reading files created with the @code{linux} submodule. @code{xterm} is used for 349 | normal @code{xterm-256color} capable terminals. 350 | 351 | @opindex @option{fullcolour} 352 | @opindex @option{colourful} 353 | @cindex 24-bit colour support 354 | @cindex colour support, 24 bits 355 | @code{xterm} will use the well defined colours with the indices 16 through 255 356 | (inclusive.) If you want to enable use any colour @code{xterm} can modify the palette, 357 | however terminals will normally not support this as the palette will be reset and 358 | normally everything is redrawn when the palette is modified. To do this, use the 359 | option @option{--fullcolour} with a value starting with @code{y} or @code{Y}. In this 360 | mode only the colour indices 7 (white) and 15 (bright white) will be modified and 361 | used, if you want 16 colour indices to be used, use the @option{--colourful} with a 362 | value starting with @code{y} or @code{Y}. 363 | 364 | @opindex @option{palette} 365 | @cindex palette, custom 366 | @cindex custom palette 367 | @cindex @option{fullcolour} 368 | You can specify which palette you want to use by adding it as the value of the option 369 | @option{--palette}, the value does not need to contain the characters ESC, @code{[} 370 | and @code{P}, and both XTerm and Linux VT palette strings are supported. When using 371 | @option{--fullcolour} it is a good idea to use this so the palette you are using does 372 | not get changed when displaying the image. 373 | 374 | @opindex @option{chroma} 375 | @cindex colour matching 376 | @cindex colour distance 377 | @cindex matching, colour 378 | @cindex distance, colour 379 | @cindex CIELAB 380 | @cindex sRGB 381 | @cindex standard RGB 382 | @cindex RGB, standard RGB 383 | The select which colour index to use, as it is limited, the best colour is choosen 384 | by selecting the first enumerated colour with minimal distance. By default the 385 | distances aer calculated as CIELAB distances. If you want to make the chroma more 386 | important than distance, you can specify a non-negative floating value in the 387 | option @option{--chroma}, which as the 1 as its default value. If you prefer 388 | sRGB distances, you can use an invalid value for @option{--chroma}, for example, 389 | @code{-}. 390 | 391 | 392 | @node Linux VT submodule 393 | @subsection Linux VT submodule 394 | @cindex @code{linux}, platform 395 | @cindex platform, @code{linux} 396 | @cindex system, @code{linux} 397 | @cindex @code{tty}, platform 398 | @cindex platform, @code{tty} 399 | @cindex system, @code{tty} 400 | 401 | @code{linux}, or @code{tty}, is the submodule for parse or, primarly, create images 402 | printable in the Linux virtual terminal. You may use @code{xterm} for parsing Linux VT 403 | image, if but if you use @code{linux}, errors the pony will be visible. 404 | 405 | @opindex @option{fullcolour} 406 | @opindex @option{colourful} 407 | @cindex 24-bit colour support 408 | @cindex colour support, 24 bits 409 | @cindex VT switching 410 | @cindex switching VT 411 | @code{linux} will use XTerm's the well defined colours with the indices 16 through 255 412 | (inclusive.), use the option @option{--fullcolour} with a value starting with @code{y} 413 | or @code{Y}, to support any colour. By default ony the colour indices 7 (white) and 15 414 | (bright white) will be used and modified, if you want 16 colour indices to be used, 415 | use the @option{--colourful} with a value starting with @code{y} or @code{Y}. 416 | 417 | @opindex @option{palette} 418 | @cindex palette, custom 419 | @cindex custom palette 420 | @cindex @option{colourful} 421 | You can specify which palette you want to use by adding it as the value of the option 422 | @option{--palette}, the value does not need to contain the characters ESC, @code{[} and 423 | @code{P}. It is recommand no to use @option{--colourful} unless this option is used. 424 | 425 | 426 | @opindex @option{chroma} 427 | @cindex colour matching 428 | @cindex colour distance 429 | @cindex matching, colour 430 | @cindex distance, colour 431 | @cindex CIELAB 432 | @cindex sRGB 433 | @cindex standard RGB 434 | @cindex RGB, standard RGB 435 | The select which colour index to use, as it is limited, the best colour is choosen 436 | by selecting the first enumerated colour with minimal distance. By default the 437 | distances aer calculated as CIELAB distances. If you want to make the chroma more 438 | important than distance, you can specify a non-negative floating value in the 439 | option @option{--chroma}, which as the 1 as its default value. If you prefer 440 | sRGB distances, you can use an invalid value for @option{--chroma}, for example, 441 | @code{-}. 442 | 443 | 444 | @node Haiku submodule 445 | @subsection Haiku submodule 446 | @cindex @code{haiku}, platform 447 | @cindex platform, @code{haiku} 448 | @cindex system, @code{haiku} 449 | 450 | The @code{haiku} submodule works as the @code{xterm} submodule, but does not 451 | support @code{linux} import. The difference between @code{haiku} and @code{xterm} 452 | files is that transparency/default is encoded in conflicting escape sequences. 453 | 454 | @code{haiku} uses @code{CSI 40m} instead of @code{CSI 49m} for transparent 455 | backgrounds, and @code{CSI 37m} instead of @code{CSI 39m} for default 456 | foreground colour. More precisly, colour index 0 is used for the background 457 | colour instead of black, and colour index 7 is used for the foreground colour, 458 | while @code{CSI 49m} and @code{CSI 39m} is not recognised. 459 | 460 | 461 | 462 | @node Unisay module 463 | @section @code{unisay} module 464 | @cindex @code{unisay} module 465 | @cindex module, @code{unisay} 466 | 467 | The @code{unisay} module is identical to @code{ponysay} with 468 | @option{--version=2.1}. (Which as the version of @command{ponysay}, that 469 | dropped the use of cowsay and switch to @command{unisay}'s format.) 470 | 471 | 472 | @node Cowsay module 473 | @section @code{cowsay} module 474 | @cindex @code{cowsay} module 475 | @cindex module, @code{cowsay} 476 | 477 | The @code{cowsay} module is an extension of the @code{ponysay} with 478 | @option{--version=0.1}, so the same options are supported, except 479 | @option{--version}. It can construct images usable with @code{cowsay}. 480 | For import you will need @command{perl} installed as it is used to 481 | parse the cow file, which is a partial @command{perl} script. 482 | 483 | 484 | @node Cat module 485 | @section @code{cat} module 486 | @cindex @code{cat} module 487 | @cindex module, @code{cat} 488 | 489 | The @code{cat} module is identical to @code{ponysay} with 490 | @option{--version=2.8 --balloon=- --ignoreballoon=y --ignorelink=y}. 491 | It is designed to create pure images that are displayable by just 492 | printing them to the terminal as is, for example with @command{cat}. 493 | However the purity will be disrupted if the image contains stores 494 | and recalls, which they does not do as it is not intended for 495 | cross-format use. 496 | 497 | 498 | @node Image module 499 | @section @code{image} module 500 | @cindex @code{image} module 501 | @cindex module, @code{image} 502 | 503 | The @code{image} module lets you import and export regular graphics images, 504 | such as Portable Network Graphics (PNG) files. The module suports the 505 | following options. 506 | 507 | @table @asis 508 | @item @option{--left} (export/import) 509 | @opindex @option{left} 510 | @cindex margins 511 | Fix the number emptying columns at the left side of the pony, if the value 512 | cannot be parsed as a non-negative integer, for example @code{-}, the margin 513 | will not be modified from the original image. 514 | 515 | Use a value starting with a @code{y} or @code{Y} make the program use a 516 | default value. 517 | 518 | @item @option{--right} (export/import) 519 | @opindex @option{right} 520 | @cindex margins 521 | Fix the number emptying columns at the right side of the pony, if the value 522 | cannot be parsed as a non-negative integer, for example @code{-}, the margin 523 | will not be modified from the original image. 524 | 525 | Use a value starting with a @code{y} or @code{Y} make the program use a 526 | default value. 527 | 528 | @item @option{--top} (export/import) 529 | @opindex @option{top} 530 | @cindex margins 531 | Fix the number emptying lines at the top of the pony, if the value cannot 532 | be parsed as a non-negative integer, for example @code{-}, the margin will 533 | not be modified from the original image. However, in @option{--import} 534 | this value must be a non-negative integer, and is the number of additional 535 | lines between the pony and the balloon. Defaults to 3 in @option{--import}. 536 | 537 | Use a value starting with a @code{y} or @code{Y} make the program use a 538 | default value. 539 | 540 | @item @option{--bottom} (export/import) 541 | @opindex @option{bottom} 542 | @cindex margins 543 | Fix the number emptying lines at the bottom of the pony, if the value cannot 544 | be parsed as a non-negative integer, for example @code{-}, the margin will 545 | not be modified from the original image. 546 | 547 | Use a value starting with a @code{y} or @code{Y} make the program use a 548 | default value. 549 | 550 | @item @option{--magnified} (export/import) 551 | @opindex @option{magnified} 552 | @cindex scale up 553 | @cindex magnification 554 | @cindex pixel dimension 555 | Pixel magnification, this specifies the nearest neighbour scale up used on 556 | the image, and default to 2. In @option{--export} this means that every 557 | pixel in the pony be @math{M} by @math{M} pixels if the value is @math{M}. 558 | In @option{--import} it means that the imported image has that magnification 559 | and the avarage pixel value will be used for the created pony. The value 560 | must be a positive integer. 561 | 562 | Use a value starting with a @code{y} or @code{Y} make the program use a 563 | default value. 564 | 565 | @item @option{--encoded} (export/import) 566 | @opindex @option{encoded} 567 | @cindex balloon, encode 568 | @cindex balloon link, encode 569 | @cindex link, encode 570 | @cindex encoded images 571 | @cindex image, encoded 572 | This specifies whether the balloon and balloon link is or should be encoded 573 | into to image. Use a value starting with a @code{y} or @code{Y} to enable this. 574 | 575 | @item @option{--balloon} (export/import) 576 | @opindex @option{balloon} 577 | @cindex balloon, insert 578 | This specifies whether to insert a balloon and its link at the top left of 579 | the image. Use a value starting with a @code{y} or @code{Y} to enable this. 580 | 581 | @item @option{--format} (export) 582 | @opindex @option{format} 583 | @cindex image format 584 | @cindex format, image 585 | @cindex PNG 586 | @cindex Portable Network Graphics 587 | @cindex GIF 588 | @cindex Graphics Interchange Format 589 | This specified what format to export the image with, the image is automatically 590 | select on import. If a value is not specified it will be determined from the 591 | file name and if fall back to Portable Network Graphics (PNG) if it is not 592 | possible to determine. Note that the program will fail if the format is not 593 | recognised, which is a possibility even for determination by file name. 594 | All formats support by your Java installation will be supported; you count on 595 | PNG and Graphics Interchange Format (GIF) being supported. 596 | @end table 597 | 598 | 599 | 600 | @node Usage examples 601 | @chapter Usage examples 602 | @cindex usage examples 603 | @cindex examples, usage 604 | 605 | @cindex create pony file 606 | @cindex make pony file 607 | @cindex pony file, create 608 | @cindex image to pony 609 | @cindex pony from image 610 | To create a pony file from an image, use the command 611 | @command{ponytool --import image --file file.png --export ponysay --balloon y}. 612 | 613 | @cindex make TTY pony 614 | @cindex make Linux VT pony 615 | @cindex create TTY pony 616 | @cindex create Linux VT pony 617 | @cindex TTY pony, create 618 | @cindex Linux VT pony, create 619 | To create a Linux VT pony file from and XTerm pony file, use the command 620 | @command{ponytool --import ponysay --file file.pony --export ponysay --platform tty --left - --right - --top - --bottom - --file tty.pony}. 621 | 622 | 623 | 624 | @node GNU Free Documentation License 625 | @appendix GNU Free Documentation License 626 | @include fdl.texinfo 627 | 628 | 629 | @node Concept index 630 | @appendix Concept index 631 | @printindex cp 632 | 633 | @node Option index 634 | @appendix Option index 635 | @printindex op 636 | 637 | 638 | @bye 639 | 640 | --------------------------------------------------------------------------------