fit text inside rectangle

Previous topic - Next topic

djtoon

is there a lib around?

Ian Price

#1
PeeJay did a text routine that wrapped text IIRC.

Here it is - http://www.glbasic.com/forum/index.php?topic=1340.msg9019#msg9019

May need modification since this is really old code.
I came. I saw. I played.

MrTAToad

Use Moru's one as it more up-to-date...

djtoon

where can i find it?
i searched the forum but nothing :(

okee

You can get it here

http://gamecorner.110mb.com/

but the site seems to be down at the moment
Android: Samsung Galaxy S2 -  ZTE Blade (Orange San Francisco) - Ainol Novo 7 Aurora 2
IOS: 2 x Ipod Touch (1G)

Slydog

If you are creating your own font routines, you can have a look at mine to get an idea of how I did it.
The following code wont run as it references my other libraries, but may be good for a reference.

The 'AsciiSet()' function takes your input string, and steps through each character and adds its width to a total.  Once that total becomes too wide, it adds a new line to the list and starts over again.  It handles justification too.  It uses my 'TGlyph' type below to reference the character dimensions.  The final result is a list of line types that holds the line's position, plus the individual ascii characters in that line.  The function is only called once when setting the string initially.

Then the 'Draw()' function takes the result of the above function and draws each line, character by character.  This function is called per frame that you want to display that string.

I have 'styles' too that let you predefine a style for displaying text, such as margins, font_id, etc.  This saves me specifying those details every time I want to display a string, since mostly they all share the same characteristics.

Code (glbasic) Select
TYPE TTextLine
ascii%[]
xy AS TPoint
ENDTYPE

TYPE TText
enabled%
string$
style AS TTextStyle
lines[] AS TTextLine
pos AS TXyWh
justify%

FUNCTION New%: string$, style AS TTextStyle, pos AS TXyWh, colour AS TPolyColour, justify%=BLANK, spacing%=BLANK
self.enabled = TRUE
self.string$ = string$
self.pos.Set(pos.x, pos.y, pos.w, pos.h)
self.style = style
IF colour.TL <> BLANK THEN self.style.colour = colour // Override default style
IF justify <> BLANK THEN self.style.justify = justify // Override default style
IF spacing <> BLANK THEN self.style.spacing = spacing // Override default style
self.AsciiSet(string$)
ENDFUNCTION

FUNCTION Clear%:
self.enabled = FALSE
self.string$ = ""
self.style.Clear()
DIM self.lines[0]
ENDFUNCTION

FUNCTION Update: enabled%, string_new$, colour_new AS TPolyColour, x_new%=BLANK, y_new%=BLANK
self.enabled = enabled // Update 'ENABLED'
IF string_new$ <> "" THEN self.AsciiSet(string_new$) // New 'STRING'?
IF (x_new <> BLANK) THEN self.pos.x = x_new // New 'X POS'?
IF (y_new <> BLANK) THEN self.pos.y = y_new // New 'Y POS'?
IF (colour_new.TL <> BLANK) THEN self.style.colour = colour_new // New 'COLOUR'?
ENDFUNCTION

FUNCTION Draw: colour AS TPolyColour
LOCAL fx%
LOCAL lx%, ax%
LOCAL y_pos%
LOCAL cursor AS TPoints
LOCAL uv AS TPoints

cursor.xy1.Set(self.pos.x, 0) // Start drawing from this x,y point
IF (colour=BLANK_POLY_COLOUR) THEN colour = self.style.colour

// Step thru each line in text message
FOR lx = 0 TO BOUNDS(self.lines[], 0) - 1
cursor.xy1.x = self.pos.x + self.lines[lx].xy.x + _text_offset.x
y_pos = self.pos.y + self.lines[lx].xy.y + _text_offset.y
// Step thru each ascii character in the current line
FOR ax = 0 TO BOUNDS(self.lines[lx].ascii[], 0) - 1
LOCAL cx%
cx = self.lines[lx].ascii[ax]
cursor.xy1.y = y_pos
cursor.xy2.y = cursor.xy1.y + _glyphs[self.style.font_id].chars[cx].size.h
cursor.xy2.x = cursor.xy1.x + _glyphs[self.style.font_id].chars[cx].size.w
uv.xy1 = _glyphs[self.style.font_id].chars[cx].xy
uv.xy2 = uv.xy1
INC uv.xy2.x, _glyphs[self.style.font_id].chars[cx].size.w
INC uv.xy2.y, _glyphs[self.style.font_id].chars[cx].size.h
Poly_Draw_Gradient(cursor, uv, colour)
INC cursor.xy1.x, _glyphs[self.style.font_id].chars[cx].size.w + self.style.spacing
NEXT
NEXT
ENDFUNCTION


// Fill 'ascii[]' array from 'string$'
FUNCTION AsciiSet: string$
LOCAL sx%, ax%, bx%
LOCAL ascii%
LOCAL ascii_word%
LOCAL line AS TTextLine
LOCAL word_width% = 0
LOCAL line_width% = 0
LOCAL b_newline% = TRUE
LOCAL y_pos%
DIM self.lines[0]
y_pos = self.style.margin.y1

FOR sx = 0 TO LEN(string$)
// Add current line to list?
IF b_newline = TRUE
IF line_width > 0
DEC line_width, self.style.spacing
ENDIF

SELECT self.style.justify
CASE JUSTIFY_LEFT
line.xy.x = self.style.margin.x1
CASE JUSTIFY_CENTER
IF self.pos.w > 0
line.xy.x = (self.pos.w - line_width) / 2
ELSE
line.xy.x = -(line_width / 2)
ENDIF
CASE JUSTIFY_RIGHT
IF self.pos.w > 0
line.xy.x = self.pos.w - line_width - self.style.margin.x2
ELSE
line.xy.x = -line_width
ENDIF
ENDSELECT
line.xy.y = y_pos
IF line_width > 0
DIMPUSH self.lines[], line
INC y_pos, _glyphs[self.style.font_id].height
ENDIF
IF sx = LEN(string$) THEN BREAK
DIM line.ascii[0]
ax = 0
line_width = 0
b_newline = FALSE
ENDIF

ascii% = ASC(MID$(string$, sx, 1))
ascii = self.AsciiValidate(ascii, self.style.font_id) // Validate ASCII value

// New Line Ascii?
IF ascii = ASC_NEWLINE
b_newline = TRUE
CONTINUE
ENDIF

REDIM line.ascii[ax+1]

IF (self.pos.w > 0) AND (ascii = 32)
// Calculate width of next 'word'
word_width = 0
FOR bx = sx + 1 TO LEN(string$) - 1
ascii_word = ASC(MID$(string$, bx, 1))
ascii_word = self.AsciiValidate(ascii_word, self.style.font_id) // Validate ASCII value
IF ascii_word <> 32
INC word_width, _glyphs[self.style.font_id].chars[ascii_word].size.w
// Only add text spacing if NOT last character
IF bx < LEN(string$) - 1 THEN INC word_width, self.style.spacing
ENDIF
IF (ascii_word = 32) OR (ascii_word = ASC_NEWLINE) THEN BREAK // End of word?
NEXT

// Is the next word too wide?
IF (line_width + word_width) > self.pos.w - self.style.margin.x2 - self.style.margin.x1
b_newline = TRUE
CONTINUE
ENDIF
ENDIF

line.ascii[ax] = ascii
INC ax

INC line_width, _glyphs[self.style.font_id].chars[ascii].size.w + self.style.spacing

// If last character, finish off last line
IF sx = LEN(string$) - 1 THEN b_newline = TRUE
NEXT
ENDFUNCTION

FUNCTION AsciiValidate%: ascii%, font_id%
IF font_id >= FONT_TOTAL THEN RETURN
ALIAS font AS _glyphs[font_id]
IF (ascii >= ASC_A) AND (ascii <= ASC_Z) AND (font.chars[ascii].size.w <= 0) THEN INC ascii, (ASC_a - ASC_A) // If a CAPTIAL letter, and no ascii defined for that, use a SMALL CASE version
IF (ascii >= ASC_a) AND (ascii <= ASC_z) AND (font.chars[ascii].size.w <= 0) THEN DEC ascii, (ASC_a - ASC_A) // If a SMALL letter, and no ascii defined for that, use a CAPITIAL version
IF font.chars[ascii].size.w <= 0 THEN ascii = 32 // Ascii not defined in font?  -> Default to 'space'
IF ascii > ASC_MAX THEN ascii = 32 // Aascii value out of range?  -> Default to 'space'
RETURN ascii
ENDFUNCTION

ENDTYPE




The code above references my 'TGlyph' type, which handles loading my font files (png + character location data file).
It keeps information of each individual character, such as size and position in the .png file.

Code (glbasic) Select
TYPE TGlyphChar
xy AS TPoint // x,y position of character in font texture
size AS TSize // Size (w,h) of character
ENDTYPE

TYPE TGlyph
id% // Font ID (and the sprite that holds this font texture)
sprite_size  AS TSize // Width / Height of font texture
spacing%
height% // General height of a font character
chars[] AS TGlyphChar

FUNCTION New%: fn$, height%, spacing%, space_size%
//LOCAL fh% // File Handle
LOCAL font AS TGlyph // Temporary 'TGlyph' type
LOCAL ascii% // Ascii value of current character
LOCAL line$ // Current line data from '.fnt' file
LOCAL pos AS TPoint
LOCAL size AS TSize
LOCAL file AS TFile
fn$ = FONT_PATH$ + fn$

//LOG(">Font_New: " + fn$ + ", id:" + font_id)
//clear prev sprite
self.id = Sprite_Load(fn$ + ".png") // Load font texture sprite
//LOG("font.id: " + font.id)
self.spacing = spacing // Default spacing between characters
self.height = height // Default character height
Sprite_GetSize(self.id, self.sprite_size) // Get font texture width, height
size.w = self.sprite_size.w / FONT_TEXTURE_QTY_X
size.h = self.sprite_size.h / FONT_TEXTURE_QTY_Y
DIM self.chars[ASC_MAX + 1] // Initialize character array

// --------------------------------------------------------------------
// PARSE ".DAT" FILE
// --------------------------------------------------------------------
IF NOT file.Open(FILE_MODE_READ, fn$ + ".dat") // Open font's '.fnt' file
LOG(">FON:{TGlyph.New%     } *** Error Opening Glyph File: [" + fn$ + ".dat" + "] ***")
RETURN FALSE // Exit and return 'FALSE' if can't open
ENDIF

// Read and parse character data from file
REPEAT
line$ = file.GetLine$()
IF file.eof THEN GOTO SKIP // End of file
IF LEN(line$) <= 0 THEN GOTO SKIP // Skip blank lines
ascii = ASC(MID$(line$, 0, 1)) // Ascii value
IF (ascii < 0) OR (ascii > ASC_MAX) THEN GOTO SKIP // Ascii value is out of range, skip to next line
self.chars[ascii].xy.x = MID$(line$,  2, 3) // [X Position]
INC self.chars[ascii].xy.x, MOD(ascii, FONT_TEXTURE_QTY_X) * size.w
self.chars[ascii].xy.y = MID$(line$,  6, 3) // [Y Position]
INC self.chars[ascii].xy.y, INTEGER(ascii / FONT_TEXTURE_QTY_X) * size.h
self.chars[ascii].size.w = MID$(line$,  10, 3) // [Width]
self.chars[ascii].size.h = MID$(line$,  14, 3) // [Height]
SKIP:
UNTIL file.eof
file.Close()
self.chars[32].xy.x = 0
self.chars[32].size.w = space_size
//Debug_Font(font)

RETURN TRUE // Font added successfully
ENDFUNCTION

ENDTYPE
GLOBAL _glyphs[] AS TGlyph // FONT_TITLE
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]