summaryrefslogtreecommitdiff
path: root/glcdgraphics/font.c
diff options
context:
space:
mode:
authorandreas 'randy' weinberger <vdr@smue.org>2010-02-21 19:58:27 +0100
committerandreas 'randy' weinberger <vdr@smue.org>2010-02-21 19:58:27 +0100
commit10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3 (patch)
tree60ad7c856565f03e145b2996d1bb5f9cd64c0532 /glcdgraphics/font.c
downloadgraphlcd-base-10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3.tar.gz
graphlcd-base-10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3.tar.bz2
initial git upload, based on graphlcd-base-0.1.5
Diffstat (limited to 'glcdgraphics/font.c')
-rw-r--r--glcdgraphics/font.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/glcdgraphics/font.c b/glcdgraphics/font.c
new file mode 100644
index 0000000..1735cf2
--- /dev/null
+++ b/glcdgraphics/font.c
@@ -0,0 +1,581 @@
+/*
+ * GraphLCD graphics library
+ *
+ * font.c - font handling
+ *
+ * based on graphlcd plugin 0.1.1 for the Video Disc Recorder
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ *
+ * This file is released under the GNU General Public License. Refer
+ * to the COPYING file distributed with this package.
+ *
+ * (c) 2004 Andreas Regel <andreas.regel AT powarman.de>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "common.h"
+#include "font.h"
+
+#ifdef HAVE_FREETYPE2
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <iconv.h>
+#endif
+
+namespace GLCD
+{
+
+static const char * kFontFileSign = "FNT3";
+static const uint32_t kFontHeaderSize = 16;
+static const uint32_t kCharHeaderSize = 4;
+
+//#pragma pack(1)
+//struct tFontHeader
+//{
+// char sign[4]; // = FONTFILE_SIGN
+// unsigned short height; // total height of the font
+// unsigned short ascent; // ascender of the font
+// unsigned short line; // line height
+// unsigned short reserved;
+// unsigned short space; // space between characters of a string
+// unsigned short count; // number of chars in this file
+//};
+//
+//struct tCharHeader
+//{
+// unsigned short character;
+// unsigned short width;
+//};
+//#pragma pack()
+
+cFont::cFont()
+{
+ Init();
+}
+
+cFont::~cFont()
+{
+ Unload();
+}
+
+bool cFont::LoadFNT(const std::string & fileName)
+{
+ // cleanup if we already had a loaded font
+ Unload();
+
+ FILE * fontFile;
+ int i;
+ uint8_t buffer[10000];
+ uint16_t fontHeight;
+ uint16_t numChars;
+ int maxWidth = 0;
+
+ fontFile = fopen(fileName.c_str(), "rb");
+ if (!fontFile)
+ return false;
+
+ fread(buffer, kFontHeaderSize, 1, fontFile);
+ if (buffer[0] != kFontFileSign[0] ||
+ buffer[1] != kFontFileSign[1] ||
+ buffer[2] != kFontFileSign[2] ||
+ buffer[3] != kFontFileSign[3])
+ {
+ fclose(fontFile);
+ return false;
+ }
+
+ fontHeight = buffer[4] | (buffer[5] << 8);
+ totalAscent = buffer[6] | (buffer[7] << 8);
+ lineHeight = buffer[8] | (buffer[9] << 8);
+ spaceBetween = buffer[12] | (buffer[13] << 8);
+ numChars = buffer[14] | (buffer[15] << 8);
+ for (i = 0; i < numChars; i++)
+ {
+ uint8_t chdr[kCharHeaderSize];
+ uint16_t charWidth;
+ uint16_t character;
+ fread(chdr, kCharHeaderSize, 1, fontFile);
+ character = chdr[0] | (chdr[1] << 8);
+ charWidth = chdr[2] | (chdr[3] << 8);
+ fread(buffer, fontHeight * ((charWidth + 7) / 8), 1, fontFile);
+ if (characters[character])
+ delete characters[character];
+ characters[character] = new cBitmap(charWidth, fontHeight, buffer);
+ if (characters[character]->Width() > maxWidth)
+ maxWidth = characters[character]->Width();
+ }
+ fclose(fontFile);
+
+ totalWidth = maxWidth;
+ totalHeight = fontHeight;
+
+ return true;
+}
+
+bool cFont::SaveFNT(const std::string & fileName) const
+{
+ FILE * fontFile;
+ uint8_t fhdr[kFontHeaderSize];
+ uint8_t chdr[kCharHeaderSize];
+ uint16_t numChars;
+ int i;
+
+ fontFile = fopen(fileName.c_str(),"w+b");
+ if (!fontFile)
+ {
+ syslog(LOG_ERR, "cFont::SaveFNT(): Cannot open file: %s for writing\n",fileName.c_str());
+ return false;
+ }
+
+ numChars = 0;
+ for (i = 0; i < 256; i++)
+ {
+ if (characters[i])
+ {
+ numChars++;
+ }
+ }
+
+ memcpy(fhdr, kFontFileSign, 4);
+ fhdr[4] = (uint8_t) totalHeight;
+ fhdr[5] = (uint8_t) (totalHeight >> 8);
+ fhdr[6] = (uint8_t) totalAscent;
+ fhdr[7] = (uint8_t) (totalAscent >> 8);
+ fhdr[8] = (uint8_t) lineHeight;
+ fhdr[9] = (uint8_t) (lineHeight >> 8);
+ fhdr[10] = 0;
+ fhdr[11] = 0;
+ fhdr[12] = (uint8_t) spaceBetween;
+ fhdr[13] = (uint8_t) (spaceBetween >> 8);
+ fhdr[14] = (uint8_t) numChars;
+ fhdr[15] = (uint8_t) (numChars >> 8);
+
+ // write font file header
+ fwrite(fhdr, kFontHeaderSize, 1, fontFile);
+
+ for (i = 0; i < 256; i++)
+ {
+ if (characters[i])
+ {
+ chdr[0] = (uint8_t) i;
+ chdr[1] = (uint8_t) (i >> 8);
+ chdr[2] = (uint8_t) characters[i]->Width();
+ chdr[3] = (uint8_t) (characters[i]->Width() >> 8);
+ fwrite(chdr, kCharHeaderSize, 1, fontFile);
+ fwrite(characters[i]->Data(), totalHeight * characters[i]->LineSize(), 1, fontFile);
+ }
+ }
+
+ fclose(fontFile);
+
+ syslog(LOG_DEBUG, "cFont::SaveFNT(): Font file '%s' written successfully\n", fileName.c_str());
+
+ return true;
+}
+
+bool cFont::LoadFT2(const std::string & fileName, const std::string & encoding,
+ int size, bool dingBats)
+{
+ // cleanup if we already had a loaded font
+ Unload();
+#ifdef HAVE_FREETYPE2
+ if (access(fileName.c_str(), F_OK) != 0)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Font file (%s) does not exist!!", fileName.c_str());
+ return false;
+ }
+ // file exists
+ FT_Library library;
+ FT_Face face;
+ FT_GlyphSlot slot;
+
+ int error = FT_Init_FreeType(&library);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Could not init freetype library");
+ return false;
+ }
+ error = FT_New_Face(library, fileName.c_str(), 0, &face);
+ // everything ok?
+ if (error == FT_Err_Unknown_File_Format)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Font file (%s) could be opened and read, but it appears that its font format is unsupported", fileName.c_str());
+ error = FT_Done_Face(face);
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_Face(..) returned (%d)", error);
+ error = FT_Done_FreeType(library);
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_FreeType(..) returned (%d)", error);
+ return false;
+ }
+ else if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Font file (%s) could not be opened or read, or simply it is broken,\n error code was %x", fileName.c_str(), error);
+ error = FT_Done_Face(face);
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_Face(..) returned (%d)", error);
+ error = FT_Done_FreeType(library);
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_FreeType(..) returned (%d)", error);
+ return false;
+ }
+
+ // set slot
+ slot = face->glyph;
+
+ // set Size
+ FT_Set_Char_Size(face, 0, size * 64, 0, 0);
+
+ wchar_t utf_buff[256];
+ if (dingBats)
+ {
+/*
+ FT_CharMap charmap = 0;
+ for (int n = 0; n < face->num_charmaps; n++)
+ {
+ if (face->charmaps[n]->platform_id == 3 &&
+ face->charmaps[n]->encoding_id == 0)
+ {
+ charmap = face->charmaps[n];
+ //break;
+ }
+ }
+ if (charmap)
+ syslog(LOG_ERR, "cFont::LoadFT2: platform_id: %d, encoding_id: %d", charmap->platform_id, charmap->encoding_id);
+ error = FT_Set_Charmap(_face, charmap);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Select_Charmap encoding not supported: %d", charmap->encoding_id);
+ }
+*/
+ }
+ else
+ {
+ iconv_t cd;
+ if ((cd = iconv_open("WCHAR_T", encoding.c_str())) == (iconv_t) -1)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: Iconv encoding not supported: %s", encoding.c_str());
+ error = FT_Done_Face(face);
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_Face(..) returned (%d)", error);
+ error = FT_Done_FreeType(library);
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_FreeType(..) returned (%d)", error);
+ return false;
+ }
+ for (int c = 0; c < 256; c++)
+ {
+ char char_buff = c;
+ wchar_t wchar_buff;
+ char * in_buff,* out_buff;
+ size_t in_len, out_len, count;
+
+ in_len = 1;
+ out_len = 4;
+ in_buff = (char *) &char_buff;
+ out_buff = (char *) &wchar_buff;
+ count = iconv(cd, &in_buff, &in_len, &out_buff, &out_len);
+ if ((size_t) -1 == count)
+ {
+ utf_buff[c] = 0;
+ }
+ utf_buff[c] = wchar_buff;
+ }
+ iconv_close(cd);
+ }
+
+ // get some global parameters
+ totalHeight = (face->size->metrics.ascender >> 6) - (face->size->metrics.descender >> 6);
+ totalWidth = face->size->metrics.max_advance >> 6;
+ totalAscent = face->size->metrics.ascender >> 6;
+ lineHeight = face->size->metrics.height >> 6;
+ spaceBetween = 0;
+#if 0
+ syslog(LOG_DEBUG, "cFont::LoadFT2: totalHeight = %d", totalHeight);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: totalWidth = %d", totalWidth);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: totalAscent = %d", totalAscent);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: lineHeight = %d", lineHeight);
+ syslog(LOG_DEBUG, "cFont::LoadFT2: spaceBetween = %d", spaceBetween);
+#endif
+ // render glyphs for ASCII codes 0 to 255 in our bitmap class
+ FT_UInt glyph_index;
+ int num_char;
+
+ for (num_char = 0; num_char < 256; num_char++)
+ {
+ if (dingBats)
+ {
+ //Get FT char index & load the char
+ error = FT_Load_Char(face, num_char, FT_LOAD_DEFAULT);
+ }
+ else
+ {
+ //Get FT char index
+ glyph_index = FT_Get_Char_Index(face, utf_buff[num_char]);
+ //Load the char
+ error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
+ }
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: ERROR when calling FT_Load_Glyph: %x", error);
+ }
+
+ // convert to a mono bitmap
+ error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: ERROR when calling FT_Render_Glyph: %x", error);
+ }
+
+ // now, fill our pixel data
+ cBitmap * charBitmap = new cBitmap(face->glyph->advance.x >> 6, totalHeight);
+ charBitmap->Clear();
+ unsigned char * bufPtr = face->glyph->bitmap.buffer;
+ unsigned char pixel;
+ for (int y = 0; y < face->glyph->bitmap.rows; y++)
+ {
+ for (int x = 0; x < face->glyph->bitmap.width; x++)
+ {
+ pixel = (bufPtr[x / 8] >> (7 - x % 8)) & 1;
+ if (pixel)
+ charBitmap->DrawPixel((face->glyph->metrics.horiBearingX >> 6) + x,
+ (face->size->metrics.ascender >> 6) - (face->glyph->metrics.horiBearingY >> 6) + y,
+ GLCD::clrBlack);
+ }
+ bufPtr += face->glyph->bitmap.pitch;
+ }
+ SetCharacter((char) num_char, charBitmap);
+ }
+ error = FT_Done_Face(face);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_Face(..) returned (%d)", error);
+ }
+ error = FT_Done_FreeType(library);
+ if (error)
+ {
+ syslog(LOG_ERR, "cFont::LoadFT2: FT_Done_FreeType(..) returned (%d)", error);
+ }
+ return true;
+#else
+ syslog(LOG_ERR, "cFont::LoadFT2: glcdgraphics was compiled without FreeType2 support!!!");
+ return false;
+#endif
+}
+
+int cFont::Width(char ch) const
+{
+ if (characters[(unsigned char) ch])
+ return characters[(unsigned char) ch]->Width();
+ else
+ return 0;
+}
+
+int cFont::Width(const std::string & str) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length(); i++)
+ {
+ sum += Width(str[i]);
+ }
+ if (str.length() > 1)
+ {
+ sum += spaceBetween * (str.length() - 1);
+ }
+ return sum;
+}
+
+int cFont::Width(const std::string & str, unsigned int len) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length() && i < len; i++)
+ {
+ sum += Width(str[i]);
+ }
+ if (std::min(str.length(), (size_t) len) > 1)
+ {
+ sum += spaceBetween * (std::min(str.length(), (size_t) len) - 1);
+ }
+ return sum;
+}
+
+int cFont::Height(char ch) const
+{
+ if (characters[(unsigned char) ch])
+ return characters[(unsigned char) ch]->Height();
+ else
+ return 0;
+}
+
+int cFont::Height(const std::string & str) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length(); i++)
+ sum = std::max(sum, Height(str[i]));
+ return sum;
+}
+
+int cFont::Height(const std::string & str, unsigned int len) const
+{
+ unsigned int i;
+ int sum = 0;
+
+ for (i = 0; i < str.length() && i < len; i++)
+ sum = std::max(sum, Height(str[i]));
+ return sum;
+}
+
+const cBitmap * cFont::GetCharacter(char ch) const
+{
+ return characters[(unsigned char) ch];
+}
+
+void cFont::SetCharacter(char ch, cBitmap * bitmapChar)
+{
+ // adjust maxwidth if necessary
+ if (totalWidth < bitmapChar->Width())
+ totalWidth = bitmapChar->Width();
+
+ // delete if already allocated
+ if (characters[(unsigned char) ch])
+ delete characters[(unsigned char) ch];
+
+ // store new character
+ characters[(unsigned char) ch] = bitmapChar;
+}
+
+void cFont::Init()
+{
+ totalWidth = 0;
+ totalHeight = 0;
+ totalAscent = 0;
+ spaceBetween = 0;
+ lineHeight = 0;
+ for (int i = 0; i < 256; i++)
+ {
+ characters[i] = NULL;
+ }
+}
+
+void cFont::Unload()
+{
+ // cleanup
+ for (int i = 0; i < 256; i++)
+ {
+ if (characters[i])
+ {
+ delete characters[i];
+ }
+ }
+ // re-init
+ Init();
+}
+
+void cFont::WrapText(int Width, int Height, std::string & Text,
+ std::vector <std::string> & Lines, int * ActualWidth) const
+{
+ int maxLines;
+ int lineCount;
+ int textWidth;
+ std::string::size_type start;
+ std::string::size_type pos;
+ std::string::size_type posLast;
+
+ Lines.clear();
+ maxLines = 2000;
+ if (Height > 0)
+ {
+ maxLines = Height / LineHeight();
+ if (maxLines == 0)
+ maxLines = 1;
+ }
+ lineCount = 0;
+
+ pos = 0;
+ start = 0;
+ posLast = 0;
+ textWidth = 0;
+ while (pos < Text.length() && (Height == 0 || lineCount < maxLines))
+ {
+ if (Text[pos] == '\n')
+ {
+ Lines.push_back(trim(Text.substr(start, pos - start)));
+ start = pos + 1;
+ posLast = pos + 1;
+ textWidth = 0;
+ lineCount++;
+ }
+ else if (textWidth > Width && (lineCount + 1) < maxLines)
+ {
+ if (posLast > start)
+ {
+ Lines.push_back(trim(Text.substr(start, posLast - start)));
+ start = posLast + 1;
+ posLast = start;
+ textWidth = this->Width(Text.substr(start, pos - start + 1)) + spaceBetween;
+ }
+ else
+ {
+ Lines.push_back(trim(Text.substr(start, pos - start)));
+ start = pos + 1;
+ posLast = start;
+ textWidth = this->Width(Text[pos]) + spaceBetween;
+ }
+ lineCount++;
+ }
+ else if (Text[pos] == ' ')
+ {
+ posLast = pos;
+ textWidth += this->Width(Text[pos]) + spaceBetween;
+ }
+ else
+ {
+ textWidth += this->Width(Text[pos]) + spaceBetween;
+ }
+ pos++;
+ }
+
+ if (Height == 0 || lineCount < maxLines)
+ {
+ if (textWidth > Width && (lineCount + 1) < maxLines)
+ {
+ if (posLast > start)
+ {
+ Lines.push_back(trim(Text.substr(start, posLast - start)));
+ start = posLast + 1;
+ posLast = start;
+ textWidth = this->Width(Text.substr(start, pos - start + 1)) + spaceBetween;
+ }
+ else
+ {
+ Lines.push_back(trim(Text.substr(start, pos - start)));
+ start = pos + 1;
+ posLast = start;
+ textWidth = this->Width(Text[pos]) + spaceBetween;
+ }
+ lineCount++;
+ }
+ if (pos > start)
+ {
+ Lines.push_back(trim(Text.substr(start)));
+ lineCount++;
+ }
+ textWidth = 0;
+ for (int i = 0; i < lineCount; i++)
+ textWidth = std::max(textWidth, this->Width(Lines[i]));
+ textWidth = std::min(textWidth, Width);
+ }
+ else
+ textWidth = Width;
+ if (ActualWidth)
+ *ActualWidth = textWidth;
+}
+
+} // end of namespace