/*
* print - versatile printing routines. $Id: print.js 111 2008-07-18 22:16:23Z nilton $
*
* print(x, y, msg, type, alignH, alignV) - print a message
*
* @param {integer} x Print at X
* @param {integer} y Print at Y
* @param {string} msg Text to print
* @param {string} type String with font effects, Multiple options are posible. Or one summed number.
* 1 = 'S'hadow = print.fx.SHADOW (change print.color.shadow if you like)
* 2 = 'O'utlined = print.fx.OUTLINED
* 4 = 'B'old = print.fx.BOLD
* 8 = 'U'nderlined = print.fx.UNDERLINE
* 16 = 'I'talic = print.fx.ITALIC
* 32 = 'W' Zoom X-axis = print.fx.ZOOMX
* 64 = 'H' Zoom Y-axis = print.fx.ZOOMY
* 128 = 'R' Rotate = print.fx.ROTATE
* 256 = 'X' Skew X = print.fx.SKEWX
* 512 = 'Y' Skew Y = print.fx.SKEWY
* 1024 = '-' Strikethrough = print.fx.STRIKETHROUGH
* 2048 = '=' double Strikethrough = print.fx.DOUBLESTRIKETHROUGH
* 4096 = 'N' Nervous text = print.fx.NERVOUS (you can change the value of print.nervous=3 to make it shake harder)
*
* So Bold+Italic is: "BI", "IB", 20, print.fx.BOLD+print.fx.ITALIC or even "bi"
*
* @param {integer} alignH Horizontal Justification. -1/'left' (default), '0'/center', 1/'right'
* @param {integer} alignV Vertical Justification. -1/'top' (default), 0/'center', 1/'bottom'
* Alignment works like this: The (x,y) parameters given are by default at the top and left of the printed font.
* To align something at the topright of the screen you set 'right' and 'top' (see examples below)
*
* the parameters x and y are optional. print() stores its last cursor position in print.pos.x and print.pos.y and uses those if no x and y are given.
* It also stores where it started printing in print.X and print.Y, should you need it.
*
* Other functions:
*
* print.ln(x, y, msg, type, alignH, alignV) - same as print(x, y, msg+"\n", type, alignH, alignV)
* print.addFont(font, fontname) - Add fonts we can then set with print.ml() and print.setFont();
* print.setAngle(radians) - To change the italic skew (default: Math.PI/10)
* print.setVSpace(space) - To change the autoprint interline space (default: 2)
* print.tint(color) - To change the colormask of the font (default: no colormask)
* print.setZoom(zoomfactorX, zoomfactorY) - Zoom factor (you also need to tell print() to print.fx.ZOOMX / 'W' and/or print.fx.ZOOMY / 'H')
* print.setVSpace(pixels) - Space between two lines (default: 2) when using "\n" or print.ln()
* print.setItalicAngle(angle) - Angle for Italics (default: Math.PI / 10)
* print.setAngle(angle) - Angle for rotation. (Dont forget to also set the 'R' effect in your print statement)
* print.setSkew(skewXpixels, skewYpixels) - skew the image.
* print.ml(x, y, msg, type, alignH, alignV) - similar to print(), but the string is markup language.
* The following syntax is supported:
*
Paragraph - double linebreak
*
Linebreak (similar to \n for print() )
* <#ABCDEF> A font color, in hex. Examples: white = <#FFFFFF>, red = <#FF0000>
* <@fontname> Changes font, you must load the font with print.addfont() first.
* Headers n=1 to 5. Font effects are defined in the object print.Hfx, for example: print.HFX.TYPE[0] contains the type for
* <^n> superscript n=1 to 9, makes the font move up in steps of quarter of the font height.
* <_n> subscript n=1 to 9, makes the font move down in steps of one quarter of the font height.
*
unnumbered listitem
* After this I stopped adding features...
* note: align parameters are buggy and not fixable without a complete rewrite.
*
* Advanced:
* print.setup() will create print.surface, a surface you can then re-use.
* print.font the current font.
* Most of the commands return print, so you can chain like so: print.setAngle(0.4).setVspace(1)(0,0)("This is my text\n.", "R");
* You might also want to look at Neologix's NTML
*
* examples:
* print(0,0, "This is printed on the topleft in bold and italics\n", 'BI');
* print("This line is just below the first one and has a shadow.", 'S');
* print(" This is the continuation of the second line", 'S');
* print(GetScreenWidth()>>1,GetScreenHeight(), "This is plain text centered at the bottom", undefined, 0, 1);
* print(GetScreenWidth(), 0, "This is text is right-justified at the top", undefined, 1, -1);
*
*/
function print(x, y, msg, type, alignH, alignV){
// You can skip x and y:
if(typeof x == 'string'){
alignV = type;
alignH = msg;
type = y;
msg = x;
y = undefined;
x = undefined;
}
// If you want to set the print cursor, use an empty msg string.
if (msg == "" || msg === undefined){
print.calcXY(x, y, print.font.getStringWidth(msg), print.font.getHeight(), alignH, alignV);
return print;
}
// Replaced type.match(/S/) by numbers:
if(typeof type == 'string'){
var i=type.length;
var n=0;
while(i--){
n += print.fx[type[i]];
}
type = n;
}
var newline = msg[msg.length-1] == "\n";
if(newline)
msg = msg.slice(0,-1);
// multiline string...
if(msg.match(/\n/)){
print(x,y, "");
var A = msg.split(/\n/);
var i;
for (i=0;i>1);
y += Math.random()*print.nervous - (print.nervous>>1);
}
// Easy blit, if possible
if(type < 16){
print.surface.blit(x,y);
return print;
}
// Calculate the four corners of our image
var x1 = x;
var y1 = y;
var x2 = x + print.W -1;
var y2 = y ;
var x3 = x2;
var y3 = y + print.H -1;
var x4 = x;
var y4 = y3;
if(type & print.fx.ZOOMX){
var factor = print.W*(print.zoomX-1);
if(alignH !== undefined){
if(alignH == 1 || alignH == 'right'){
x1 -= factor;
x4 -= factor;
}else if (alignH == 0 || alignH == 'center'){
x1 -= factor>>1;
x2 += factor>>1;
x3 += factor>>1;
x4 -= factor>>1;
}
}else{
x2 += factor;
x3 += factor;
}
}
if(type & print.fx.ZOOMY){
var factor = print.H*(print.zoomY-1);
if(alignV !== undefined){
if(alignV == 1 || alignV == 'bottom'){
y1 -= factor;
y2 -= factor;
}else if (alignV == 0 || alignV == 'center'){
y1 -= factor>>1;
y2 -= factor>>1;
y3 += factor>>1;
y4 += factor>>1;
}
}else{
y3 += factor;
y4 += factor;
}
}
if(type & print.fx.ITALIC){
print.calcSkew();
x1 += print.skew;
x2 += print.skew;
}
if(type & print.fx.SKEWX){
x1 += print.skewX;
x2 += print.skewX;
}
if(type & print.fx.SKEWY){
y2 += print.skewY;
y3 += print.skewY;
}
if(type & print.fx.ROTATE){
var ox;
var oy;
if(alignV === undefined || alignV == -1 || alignV == 'top'){
if(alignH === undefined || alignH == -1 || alignH == 'left'){
ox = x1;
oy = y1;
}else if (alignH == 0 || alignH == 'center'){
ox = (x1+x2)>>1;
oy = (y1+y2)>>1;
}else{ // if(alignH == 1 || alignH == 'right'){
ox = x2;
oy = y2;
}
}else if (alignV == 0 || alignV == 'center'){
if(alignH === undefined || alignH == -1 || alignH == 'left'){
ox = (x1+x4)>>1;
oy = (y1+y4)>>1;
}else if (alignH == 0 || alignH == 'center'){
ox = (x1+x2+x3+x4)>>2;
oy = (y1+y2+y3+y4)>>2;
}else{ // if(alignH == 1 || alignH == 'right'){
ox = (x2+x3)>>1;
oy = (y2+y3)>>1;
}
}else{ // if(alignV == 1 || alignV == 'bottom'){
if(alignH === undefined || alignH == -1 || alignH == 'left'){
ox = x4;
oy = y4;
}else if (alignH == 0 || alignH == 'center'){
ox = (x4+x3)>>1;
oy = (y4+y3)>>1;
}else{ // if(alignH == 1 || alignH == 'right'){
ox = x3;
oy = y3;
}
};
// 2D Rotation http://en.wikipedia.org/wiki/Rotation_(mathematics) and ???
// A point can be rotated around the origin <0,0> by running it through the following equations to get the new point :
// x' = cos(theta)*x - sin(theta)*y
// y' = sin(theta)*x + cos(theta)*y
// where theta is the angle by which to rotate the point.
// Why doesnt sphere have RotatePointX(origin_x, origin_y, x, y, angle) ?
var Ca = Math.cos(print.angle);
var Sa = Math.sin(print.angle);
x1 = Ca*(x1-ox) - Sa*(y1-oy) + ox;
y1 = Sa*(x1-ox) + Ca*(y1-oy) + oy;
x2 = Ca*(x2-ox) - Sa*(y2-oy) + ox;
y2 = Sa*(x2-ox) + Ca*(y2-oy) + oy;
x3 = Ca*(x3-ox) - Sa*(y3-oy) + ox;
y3 = Sa*(x3-ox) + Ca*(y3-oy) + oy;
x4 = Ca*(x4-ox) - Sa*(y4-oy) + ox;
y4 = Sa*(x4-ox) + Ca*(y4-oy) + oy;
}
// Print to screen
print.surface.createImage().transformBlit( x1, y1, x2, y2, x3, y3, x4, y4);
return print;
}
// Print Line. Same as printing the message with an "\n" at the end.
print.ln = function(x, y, msg, type, alignH, alignV){
// You can skip x and y:
if(typeof x == 'string'){
alignV = type;
alignH = msg;
type = y;
msg = x;
y = undefined;
x = undefined;
}
return print(x, y, (msg||"")+"\n", type, alignH, alignV);
}
print.oldcolor = new Array(); // used in print.ml()
print.oldfont = new Array(); // used in print.ml()
print.oldsituation = new Array(); // used in print.ml()
print.fonts = new Object(); // used in print.ml(), print.addFont() and print.setFont()
// Add fonts we can then set with print.ml() and print.setFont();
print.addFont = function(fnt,name){
if(typeof fnt == 'string' && !print.fonts[name||fnt])
print.fonts[name||fnt] = LoadFont(fnt);
else
print.fonts[name||'_'] = fnt;
}
// Print Simple Markup Language. Caution: alignments not implemented correctly.
print.ml = function(x, y, msg, type, alignH, alignV){
// You can skip x and y:
if(typeof x == 'string'){
alignV = type;
alignH = msg;
type = y;
msg = x;
y = undefined;
x = undefined;
}
print(x, y, "");
if(msg == "") return;
msg = msg.replace("\n", "");
var TYPE = type || 0;
var msgArr = msg.split("<");
var reverse = false;
if(alignH==1){
msgArr.reverse();
reverse = true;
}
var tb, tbl, p, fx;
for(tb=0, tbl = msgArr.length ; tb/)) ){
fx = msgArr[tb].slice(1,p);
if(print.fx[fx])
TYPE ^= print.fx[fx];
else if(fx == 'p')
print(x, print.Y, "\n\n");
else if(fx[0] == '#'){
print.color.none = print.oldcolor.pop();
print.font.setColorMask(print.color.none);
}
else if(fx[0] == '@'){
var colormask = print.font.getColorMask();
print.font = print.oldfont.pop();
print.font.setColorMask(colormask);
}
else if(fx.match(/h[12345]/)){
print.oldsituation.pop()();
TYPE = print.TYPE;
var h = parseInt(fx[1]) -1;
if(h<3)
print(x, print.Y, "\n\n");
else
print(x, print.Y, "\n");
}
else if(fx == "^" || fx == "_")
print.oldsituation.pop()();
msgArr[tb] = msgArr[tb].slice(p+1);
}
else if( -1<(p=msgArr[tb].search(/>/)) ){
fx = msgArr[tb].slice(0,p);
if(print.fx[fx])
TYPE |= print.fx[fx];
else if(fx == 'p')
print(x, print.Y, "\n\n");
else if(fx.match(/^br ?\/?/))
print(x, print.Y, "\n");
else if(fx[0] == '#'){
print.oldcolor.push(print.font.getColorMask());
print.font.setColorMask( CreateColor(
parseInt(fx.slice(1,3), 16),
parseInt(fx.slice(3,5), 16),
parseInt(fx.slice(5,7), 16),
fx.length>7 ? parseInt(fx.slice(7,9), 16) : 255
));
print.color.none = print.font.getColorMask();
}
else if(fx[0] == '@'){
print.oldfont.push(print.font);
print.setFont(fx.slice(1));
}
else if (fx.match(/h[12345]/)){
print.oldsituation.push( Function("print.zoomX=" + print.zoomX + ";print.zoomY=" + print.zoomY + ";print.TYPE=" + TYPE));
var h = parseInt(fx[1]) -1;
print.zoomX = print.Hfx.zoomX[h];
print.zoomY = print.Hfx.zoomY[h];
TYPE = print.Hfx.TYPE[h];
print(x, print.Y, "\n\n");
}
else if (fx.match(/\^[1-9]/)){
print.oldsituation.push( Function("print.pos.y=" + print.pos.y) );
print.pos.y -= parseInt(fx[1]) * (print.font.getHeight()>>2);
}
else if (fx.match(/\_[1-9]/)){
print.oldsituation.push( Function("print.pos.y=" + print.pos.y) );
print.pos.y += parseInt(fx[1]) * (print.font.getHeight()>>2);
}
else if (fx == 'li'){
msgArr[tb] = msgArr[tb].replace(">", "> *");
msgArr[tb] += "\n";
}
msgArr[tb] = msgArr[tb].slice(p+1);
}
// At least try fixing alignment a little...
if(alignH == 1)
print.pos.x = print.X;
print(msgArr[tb], TYPE, alignH, alignV);
}
return print;
}
// Font effects. MAYBE: OVERSCORE,
print.fx = {
SHADOW:1, OUTLINED:2, BOLD:4, UNDERLINE:8, ITALIC:16, ZOOMX:32, ZOOMY:64, ROTATE:128, SKEWX:256, SKEWY:512, STRIKETHROUGH:1024, DOUBLESTRIKETHROUGH:2048, NERVOUS:4096,
S:1, O:2, B:4, U:8, I:16, W:32, H:64, R:128, X:256, Y:512, '-':1024, '=':2048, N:4096,
s:1, o:2, b:4, u:8, i:16, w:32, h:64, r:128, x:256, y:512, n:4096,
}
// Html h1..h5 effects
print.Hfx = {
zoomX:[2, 2, 1.5, 1.1, 1],
zoomY:[2, 2, 1.5, 1.1, 1],
TYPE:[
print.fx.BOLD + print.fx.OUTLINED + print.fx.ZOOMX + print.fx.ZOOMY,
print.fx.BOLD + print.fx.ZOOMX + print.fx.ZOOMY,
print.fx.BOLD + print.fx.ZOOMX + print.fx.ZOOMY,
print.fx.BOLD,
print.fx.BOLD
]
}
// How nervous is the text?
print.nervous = 3;
// Set the zoom factor
print.zoomX = 1;
print.zoomY = 1;
print.setZoom = function(zoomX, zoomY){
if(zoomX!==undefined)
print.zoomX = zoomX;
if(zoomY!==undefined)
print.zoomY = zoomY;
return print;
}
// Internal function. Define our origin x,y depending on the alignments
print.calcXY = function(x, y, W, H , alignH, alignV, newline){
// Define x and y
if(x === undefined) x = print.pos.x;
if(y === undefined) y = print.pos.y;
// Save next x and y
if(newline){
print.pos.x = x;
print.pos.y = y + H + print.vspace;
}else{
print.pos.x = x + (W||0);
print.pos.y = y;
}
// Prepare vertical and horizontal alignment
if(alignH !== undefined){
if(alignH == 1 || alignH == 'right')
x -= W;
else if (alignH == 0 || alignH == 'center')
x -= W>>1;
}
if(alignV !== undefined){
if(alignV == 1 || alignV == 'bottom')
y -= H;
else if (alignV == 0 || alignV == 'center')
y -= H>>1;
}
print.X = x;
print.Y = y;
return print;
}
// Width and Height of our canvas (created in print.setup)
print.H = 0;
print.W = 0;
// Internal function. Create our surface, which we can blit.
print.setup = function(msg, type, alignH, alignV){
// If we just calculated this same print statement, use the cached version
if(print.previous == type+';'+msg)
return;
// calculate surface canvas size
var cx = print.font.getStringWidth(msg);
var cy = print.font.getHeight();
var dx = 0;
var dy = 0;
if(type & print.fx.OUTLINED){
cx+=2; cy+=2;
dx+=2; dy+=2;
}
if(type & print.fx.SHADOW){
cx+=2; cy+=2;
}
if(type & print.fx.BOLD){
++cx;
++dx;
}
// Create a canvas to print our font to
print.createSurface(cx, cy);
print.W = cx;
print.H = cy;
// Underlined
if(type & print.fx.UNDERLINE)
print.surface.line(dx, cy-1, dx+cx, cy-1, print.color.none);
if(type & print.fx.UNDERLINE & print.fx.SHADOW)
print.surface.line(dx+1, cy, dx+cx+1, cy, print.color.shadow);
// Strikethrough
if(type & print.fx.STRIKETHROUGH)
print.surface.line(dx, dy+(cy>>1), dx+cx, dy+(cy>>1), print.color.none);
if(type & print.fx.DOUBLESTRIKETHROUGH){
print.surface.line(dx, dy+(cy>>1)-1, dx+cx, dy+(cy>>1)-1, print.color.none);
print.surface.line(dx, dy+(cy>>1)+1, dx+cx, dy+(cy>>1)+1, print.color.none);
}
// Shadow
if(type & print.fx.SHADOW){
print.font.setColorMask(print.color.shadow);
var i = dx+1;
do{
print.surface.drawText(print.font, i, dy+1, msg);
}while(--i);
}
dx=0;
dy=0;
// Outline
if(type & print.fx.OUTLINED){
print.font.setColorMask(print.color.outline);
var i,j;
for(i=0;i<3;++i){
for(j=0;j<3;++j){
print.surface.drawText(print.font, i, j, msg);
}
}
dx=1;
dy=1;
}
// Draw our 'plain' text:
print.font.setColorMask(print.color.none);
print.surface.drawText(print.font, dx, dy, msg);
// Bold
if(type & print.fx.BOLD)
print.surface.drawText(print.font, dx+1, dy, msg);
// Set Cache
print.previous = type+';'+msg;
return print;
}
// Remember the cursor position
print.pos = { x:0, y:0 }
// Colors
print.color = {
shadow: CreateColor(55,55,55, 155),
outline: CreateColor(32,32,32, 200),
mark: CreateColor(255,255,0, 255),
none: CreateColor(255,255,255, 255),
transparent: CreateColor(0,0,0, 0)
}
// Set your font colormask
print.tint = function(color){
print.color.none = color;
print.font.setColorMask(color);
return print;
}
// Default font is defined for you
print.font = GetSystemFont();
print.fonts['systemfont'] = print.font;
// Change your font
print.setFont = function(fnt){
if(fnt === undefined)
fnt = 'systemfont';
if(typeof fnt == 'string')
fnt = print.fonts[fnt] || LoadFont(fnt);
print.font = fnt;
return print;
}
// Add fonts we can then set with print.ml() and print.setFont();
print.addFont = function(fnt,name){
if(typeof fnt == 'string' && !print.fonts[name||fnt])
print.fonts[name||fnt] = LoadFont(fnt);
else
print.fonts[name||'_'] = fnt;
}
// Space between two lines (in pixels)
print.vspace = 2;
print.setVSpace = function(vs){
print.vspace = vs;
return print;
}
// Internal function. Creates a canvas where we can print our text and font effects.
print.previous= "";
print.surface = undefined;
print.createSurface = function(w, h){
return print.surface = CreateSurface(w, h, print.color.transparent);
}
// Angle for Italics, default:
print.italicAngle = Math.PI / 10;
print.setItalicAngle = function(italicAngle){
print.italicAngle = italicAngle;
return print;
}
// Internal Function. Skew for Italic angle (depends on font height)
print.skew = 0;
print.calcSkew = function(italicAngle){
if(italicAngle)
print.italicAngle = italicAngle;
return print.skew = print.font.getHeight() * Math.tan(print.italicAngle);
}
// Angle for Rotation, default:
print.angle = 0;
print.setAngle = function(Angle){
print.angle = Angle;
return print;
}
// Skew pixels
print.skewX = 0;
print.skewY = 0;
print.setSkew = function(skewX, skewY){
if(skewX !== undefined) print.skewX = skewX;
if(skewY !== undefined) print.skewY = skewY;
return print;
}