diff --git a/icu4c/source/configure b/icu4c/source/configure index c03e6437156..20cb9b2b3e1 100755 --- a/icu4c/source/configure +++ b/icu4c/source/configure @@ -4174,7 +4174,7 @@ trap 'rm -fr `echo "README icudefs.mk \ test/collperf/Makefile \ test/thaitest/Makefile \ test/letest/Makefile \ - samples/Makefile samples/date/Makefile samples/cal/Makefile \ + samples/Makefile samples/date/Makefile samples/cal/Makefile samples/layout/Makefile \ common/unicode/platform.h common/icucfg.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF diff --git a/icu4c/source/configure.in b/icu4c/source/configure.in index 4bbf768d7b9..9e6b18c3198 100644 --- a/icu4c/source/configure.in +++ b/icu4c/source/configure.in @@ -4,7 +4,7 @@ dnl Copyright (c) 1999-2000, International Business Machines Corporation and dnl others. All Rights Reserved. dnl Stephen F. Booth, heavily modified by Yves and others -dnl $Id: configure.in,v 1.134 2001/11/01 17:28:18 grhoten-oss Exp $ +dnl $Id: configure.in,v 1.135 2001/11/07 23:48:17 emader-oss Exp $ dnl Process this file with autoconf to produce a configure script AC_INIT(common/unicode/utypes.h) @@ -856,7 +856,7 @@ AC_OUTPUT([README icudefs.mk \ test/collperf/Makefile \ test/thaitest/Makefile \ test/letest/Makefile \ - samples/Makefile samples/date/Makefile samples/cal/Makefile \ + samples/Makefile samples/date/Makefile samples/cal/Makefile samples/layout/Makefile \ common/unicode/platform.h]) if test $ICU_USE_THREADS = 0; then diff --git a/icu4c/source/samples/layout/FontMap.GDI b/icu4c/source/samples/layout/FontMap.GDI new file mode 100644 index 00000000000..7eba35fe858 --- /dev/null +++ b/icu4c/source/samples/layout/FontMap.GDI @@ -0,0 +1,8 @@ +# This is a sample FontMap file for Windows. +# Fonts are specified by font name, as shown +# in the "Fonts" folder. + + LATIN: Times New Roman +DEVANAGARI: Devanagari MT for IBM + ARABIC: Times New Roman + THAI: Thonburi diff --git a/icu4c/source/samples/layout/FontMap.Gnome b/icu4c/source/samples/layout/FontMap.Gnome new file mode 100644 index 00000000000..8df0fd760e2 --- /dev/null +++ b/icu4c/source/samples/layout/FontMap.Gnome @@ -0,0 +1,7 @@ +# This is a sample FontMap file for Linux. +# Fonts are specified by file names. + + LATIN: Times.TTF +DEVANAGARI: Devamt.ttf + ARABIC: Times.TTF + THAI: THONBURI.TTF diff --git a/icu4c/source/samples/layout/FontMap.cpp b/icu4c/source/samples/layout/FontMap.cpp new file mode 100644 index 00000000000..ea9c160e7ed --- /dev/null +++ b/icu4c/source/samples/layout/FontMap.cpp @@ -0,0 +1,206 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#include +#include +#include + +#include "unicode/utypes.h" +#include "unicode/uscript.h" + +#include "layout/LETypes.h" +#include "layout/LEScripts.h" + +#include "RenderingFontInstance.h" +#include "GUISupport.h" +#include "FontMap.h" + +FontMap::FontMap(const char *fileName, le_int16 pointSize, GUISupport *guiSupport, RFIErrorCode &status) + : fPointSize(pointSize), fFontCount(0), fGUISupport(guiSupport) +{ + le_int32 i, script; + + for (i = 0; i < scriptCodeCount; i += 1) { + fFontIndices[i] = -1; + fFontNames[i] = NULL; + fFontInstances[i] = NULL; + } + + if (LE_FAILURE(status)) { + return; + } + + char *c, *s, *line, buffer[BUFFER_SIZE]; + FILE *file; + + file = fopen(fileName, "r"); + + if (file == NULL) { + sprintf(errorMessage, "Could not open the font map file: %s.", fileName); + fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); + status = RFI_FONT_FILE_NOT_FOUND_ERROR; + return; + } + + while (fgets(buffer, BUFFER_SIZE, file) != NULL) { + UScriptCode scriptCode; + UErrorCode scriptStatus = U_ZERO_ERROR; + + line = strip(buffer); + if (line[0] == '#' || line[0] == 0) { + continue; + } + + c = strchr(line, ':'); + c[0] = 0; + s = strip(&c[1]); + + uscript_getCode(strip(line), &scriptCode, 1, &scriptStatus); + + if (U_FAILURE(scriptStatus) || scriptStatus == U_USING_FALLBACK_WARNING || + scriptStatus == U_USING_DEFAULT_WARNING) { + sprintf(errorMessage, "The script name %s is invalid.", line); + fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); + status = RFI_ILLEGAL_ARGUMENT_ERROR; + fclose(file); + return; + } + + script = (le_int32) scriptCode; + + if (fFontIndices[script] >= 0) { + // FIXME: complain that this is a duplicate entry and bail (?) + fFontIndices[script] = -1; + } + + fFontIndices[script] = getFontIndex(s); + } + + fclose(file); +} + +FontMap::~FontMap() +{ + le_int32 font; + + for (font = 0; font < fFontCount; font += 1) { + if (fFontNames[font] != NULL) { + delete[] (char *) fFontNames[font]; + } + } + + for (font = 0; font < fFontCount; font += 1) { + if (fFontInstances[font] != NULL) { + delete fFontInstances[font]; + } + } +} + +le_int32 FontMap::getFontIndex(const char *fontName) +{ + le_int32 index; + + for (index = 0; index < fFontCount; index += 1) { + if (strcmp(fontName, fFontNames[index]) == 0) { + return index; + } + } + + if (fFontCount < (le_int32) scriptCodeCount) { + index = fFontCount++; + } else { + // The font name table is full. Since there can + // only be scriptCodeCount fonts in use at once, + // there should be at least one that's not being + // reference; find it and resue it's index. + + for (index = 0; index < fFontCount; index += 1) { + le_int32 script; + + for (script = 0; script < scriptCodeCount; script += 1) { + if (fFontIndices[script] == index) { + break; + } + } + + if (script >= scriptCodeCount) { + break; + } + } + } + + if (index >= scriptCodeCount) { + return -1; + } + + le_int32 len = strlen(fontName); + char *s = new char[len + 1]; + + fFontNames[index] = strcpy(s, fontName); + return index; +} + +char *FontMap::strip(char *s) +{ + le_int32 start, end, len; + + start = 0; + len = strlen(s); + + while (start < len && isspace(s[start])) { + start += 1; + } + + end = len - 1; + + while (end > start && isspace(s[end])) { + end -= 1; + } + + if (end < len) { + s[end + 1] = '\0'; + } + + return &s[start]; +} + +const RenderingFontInstance *FontMap::getScriptFont(le_int32 scriptCode, RFIErrorCode &status) +{ + if (LE_FAILURE(status)) { + return NULL; + } + + if (scriptCode <= -1 || scriptCode >= scriptCodeCount) { + status = RFI_ILLEGAL_ARGUMENT_ERROR; + return NULL; + } + + + le_int32 fontIndex = fFontIndices[scriptCode]; + + if (fontIndex < 0) { + sprintf(errorMessage, "No font was set for script %s", uscript_getName((UScriptCode) scriptCode)); + fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); + status = RFI_FONT_FILE_NOT_FOUND_ERROR; + return NULL; + } + + if (fFontInstances[fontIndex] == NULL) { + fFontInstances[fontIndex] = openFont(fFontNames[fontIndex], fPointSize, status); + + if (LE_FAILURE(status)) { + sprintf(errorMessage, "Could not open font file %s", fFontNames[fontIndex]); + fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); + return NULL; + } + } + + return fFontInstances[fontIndex]; +} + + + diff --git a/icu4c/source/samples/layout/FontMap.h b/icu4c/source/samples/layout/FontMap.h new file mode 100644 index 00000000000..36617183049 --- /dev/null +++ b/icu4c/source/samples/layout/FontMap.h @@ -0,0 +1,48 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#ifndef __FONTMAP_H +#define __FONTMAP_H + +#include "layout/LETypes.h" +#include "layout/LEScripts.h" + +#include "RenderingFontInstance.h" +#include "GUISupport.h" + +#define BUFFER_SIZE 128 + +class FontMap +{ +public: + FontMap(const char *fileName, le_int16 pointSize, GUISupport *guiSupport, RFIErrorCode &status); + + virtual ~FontMap(); + + virtual const RenderingFontInstance *getScriptFont(le_int32 scriptCode, RFIErrorCode &status); + +protected: + virtual const RenderingFontInstance *openFont(const char *fontName, le_int16 pointSize, RFIErrorCode &status) = 0; + + char errorMessage[256]; + +private: + static char *strip(char *s); + le_int32 getFontIndex(const char *fontName); + + le_int16 fPointSize; + le_int32 fFontCount; + + GUISupport *fGUISupport; + + const RenderingFontInstance *fFontInstances[scriptCodeCount]; + const char *fFontNames[scriptCodeCount]; + le_int32 fFontIndices[scriptCodeCount]; +}; + +#endif + diff --git a/icu4c/source/samples/layout/GDIFontInstance.cpp b/icu4c/source/samples/layout/GDIFontInstance.cpp new file mode 100644 index 00000000000..5b52adff548 --- /dev/null +++ b/icu4c/source/samples/layout/GDIFontInstance.cpp @@ -0,0 +1,265 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GDIFontInstance.cpp + * + * created on: 08/09/2000 + * created by: Eric R. Mader + */ + +#include + +#include "layout/LETypes.h" +#include "layout/LESwaps.h" + +#include "RenderingFontInstance.h" +#include "GDIFontInstance.h" +#include "sfnt.h" +#include "cmaps.h" + +GDIFontInstance::GDIFontInstance(HDC hdc, TCHAR *faceName, le_int16 pointSize, RFIErrorCode &status) + : fHdc(hdc), fFont(NULL), RenderingFontInstance(hdc, pointSize) +{ + LOGFONT lf; + FLOAT dpiX, dpiY; + POINT pt; + OUTLINETEXTMETRIC otm; + + if (LE_FAILURE(status)) { + return; + } + + SaveDC(hdc); + + SetGraphicsMode(hdc, GM_ADVANCED); + ModifyWorldTransform(hdc, NULL, MWT_IDENTITY); + SetViewportOrgEx(hdc, 0, 0, NULL); + SetWindowOrgEx(hdc, 0, 0, NULL); + + dpiX = (FLOAT) GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = (FLOAT) GetDeviceCaps(hdc, LOGPIXELSY); + +#if 1 + pt.x = (int) (pointSize * dpiX / 72); + pt.y = (int) (pointSize * dpiY / 72); + + DPtoLP(hdc, &pt, 1); +#else + pt.x = pt.y = pointSize; +#endif + + lf.lfHeight = - pt.y; + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + lf.lfWeight = 0; + lf.lfItalic = 0; + lf.lfUnderline = 0; + lf.lfStrikeOut = 0; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = 0; + lf.lfClipPrecision = 0; + lf.lfQuality = 0; + lf.lfPitchAndFamily = 0; + + lstrcpy(lf.lfFaceName, faceName); + + fFont = CreateFontIndirect(&lf); + + if (fFont == NULL) { + status = RFI_FONT_FILE_NOT_FOUND_ERROR; + return; + } + + SelectObject(hdc, fFont); + + UINT ret = GetOutlineTextMetrics(hdc, sizeof otm, &otm); + + if (ret == 0) { + status = RFI_MISSING_FONT_TABLE_ERROR; + goto restore; + } + + fUnitsPerEM = otm.otmEMSquare; + fAscent = otm.otmTextMetrics.tmAscent; + fDescent = otm.otmTextMetrics.tmDescent; + fLeading = otm.otmTextMetrics.tmExternalLeading; + + status = initMapper(); + + if (LE_FAILURE(status)) { + goto restore; + } + + status = initFontTableCache(); + +restore: + RestoreDC(hdc, -1); +} + +GDIFontInstance::GDIFontInstance(HDC hdc, const char *faceName, le_int16 pointSize, RFIErrorCode &status) + : fHdc(hdc), fFont(NULL), RenderingFontInstance(hdc, pointSize) +{ + LOGFONTA lf; + FLOAT dpiX, dpiY; + POINT pt; + OUTLINETEXTMETRIC otm; + + if (LE_FAILURE(status)) { + return; + } + + SaveDC(hdc); + + SetGraphicsMode(hdc, GM_ADVANCED); + ModifyWorldTransform(hdc, NULL, MWT_IDENTITY); + SetViewportOrgEx(hdc, 0, 0, NULL); + SetWindowOrgEx(hdc, 0, 0, NULL); + + dpiX = (FLOAT) GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = (FLOAT) GetDeviceCaps(hdc, LOGPIXELSY); + +#if 1 + pt.x = (int) (pointSize * dpiX / 72); + pt.y = (int) (pointSize * dpiY / 72); + + DPtoLP(hdc, &pt, 1); +#else + pt.x = pt.y = pointSize; +#endif + + lf.lfHeight = - pt.y; + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + lf.lfWeight = 0; + lf.lfItalic = 0; + lf.lfUnderline = 0; + lf.lfStrikeOut = 0; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = 0; + lf.lfClipPrecision = 0; + lf.lfQuality = 0; + lf.lfPitchAndFamily = 0; + + strcpy(lf.lfFaceName, faceName); + + fFont = CreateFontIndirectA(&lf); + + if (fFont == NULL) { + status = RFI_FONT_FILE_NOT_FOUND_ERROR; + return; + } + + SelectObject(hdc, fFont); + + UINT ret = GetOutlineTextMetrics(hdc, sizeof otm, &otm); + + if (ret == 0) { + status = RFI_MISSING_FONT_TABLE_ERROR; + goto restore; + } + + fUnitsPerEM = otm.otmEMSquare; + fAscent = otm.otmTextMetrics.tmAscent; + fDescent = otm.otmTextMetrics.tmDescent; + fLeading = otm.otmTextMetrics.tmExternalLeading; + + status = initMapper(); + + if (LE_FAILURE(status)) { + goto restore; + } + + status = initFontTableCache(); + +restore: + RestoreDC(hdc, -1); +} + +GDIFontInstance::~GDIFontInstance() +{ +#if 0 + flushFontTableCache(); + delete[] fTableCache; +#endif + + if (fFont != NULL) { + // FIXME: call RemoveObject first? + DeleteObject(fFont); + } +} + +const void *GDIFontInstance::readFontTable(LETag tableTag) const +{ + DWORD stag = SWAPL(tableTag); + DWORD len = GetFontData(fHdc, stag, 0, NULL, 0); + void *result = NULL; + + if (len != GDI_ERROR) { + result = new char[len]; + GetFontData(fHdc, stag, 0, result, len); + } + + return result; +} + +void GDIFontInstance::getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const +{ + advance.fX = 0; + advance.fY = 0; + + if (glyph == 0xFFFE || glyph == 0xFFFF) { + return; + } + + GLYPHMETRICS metrics; + DWORD result; + MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; + + result = GetGlyphOutline(fHdc, glyph, GGO_GLYPH_INDEX | GGO_METRICS, &metrics, 0, NULL, &identity); + + if (result == GDI_ERROR) { + return; + } + + advance.fX = metrics.gmCellIncX; + return; +} + +le_bool GDIFontInstance::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const +{ +#if 0 + hsFixedPoint2 pt; + le_bool result; + + result = fFontInstance->getGlyphPoint(glyph, pointNumber, pt); + + if (result) { + point.fX = xUnitsToPoints(pt.fX); + point.fY = yUnitsToPoints(pt.fY); + } + + return result; +#else + return false; +#endif +} + +void GDIFontInstance::drawGlyphs(void *surface, LEGlyphID *glyphs, le_uint32 count, le_int32 *dx, + le_int32 x, le_int32 y, le_int32 width, le_int32 height) const +{ + HDC hdc = (HDC) surface; + RECT clip; + + clip.top = 0; + clip.left = 0; + clip.bottom = height; + clip.right = width; + + ExtTextOut(hdc, x, y - fAscent, ETO_CLIPPED | ETO_GLYPH_INDEX, &clip, glyphs, count, (INT *) dx); +}; diff --git a/icu4c/source/samples/layout/GDIFontInstance.h b/icu4c/source/samples/layout/GDIFontInstance.h new file mode 100644 index 00000000000..2afa804658c --- /dev/null +++ b/icu4c/source/samples/layout/GDIFontInstance.h @@ -0,0 +1,55 @@ + +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GDIFontInstance.h + * + * created on: 08/09/2000 + * created by: Eric R. Mader + */ + +#ifndef __GDIFONTINSTANCE_H +#define __GDIFONTINSTANCE_H + +#include + +#include "layout/LETypes.h" +#include "RenderingFontInstance.h" +#include "cmaps.h" + +class GDIFontInstance : public RenderingFontInstance +{ +protected: + HDC fHdc; + HFONT fFont; + + virtual const void *readFontTable(LETag tableTag) const; + +public: + GDIFontInstance(HDC theHDC, TCHAR *faceName, le_int16 pointSize, RFIErrorCode &status); + GDIFontInstance(HDC theHDC, const char *faceName, le_int16 pointSize, RFIErrorCode &status); + //GDIFontInstance(HDC theHDC, le_int16 pointSize); + + virtual ~GDIFontInstance(); + + virtual void getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const; + + virtual le_bool getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const; + + virtual const void setFont(void *surface) const + { + HDC hdc = (HDC) surface; + + SelectObject(hdc, fFont); + }; + + virtual void drawGlyphs(void *surface, LEGlyphID *glyphs, le_uint32 count, le_int32 *dx, + le_int32 x, le_int32 y, le_int32 width, le_int32 height) const; + +}; + +#endif diff --git a/icu4c/source/samples/layout/GDIFontMap.cpp b/icu4c/source/samples/layout/GDIFontMap.cpp new file mode 100644 index 00000000000..a1e8cbe3c6c --- /dev/null +++ b/icu4c/source/samples/layout/GDIFontMap.cpp @@ -0,0 +1,31 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#include + +#include "RenderingFontInstance.h" +#include "GDIFontInstance.h" + +#include "GUISupport.h" +#include "FontMap.h" +#include "GDIFontMap.h" + +GDIFontMap::GDIFontMap(HDC hdc, const char *fileName, le_int16 pointSize, GUISupport *guiSupport, RFIErrorCode &status) + : FontMap(fileName, pointSize, guiSupport, status), fHdc(hdc) +{ + // nothing to do? +} + +GDIFontMap::~GDIFontMap() +{ + // anything? +} + +const RenderingFontInstance *GDIFontMap::openFont(const char *fontName, le_int16 pointSize, RFIErrorCode &status) +{ + return new GDIFontInstance(fHdc, fontName, pointSize, status); +} diff --git a/icu4c/source/samples/layout/GDIFontMap.h b/icu4c/source/samples/layout/GDIFontMap.h new file mode 100644 index 00000000000..4ff018c7008 --- /dev/null +++ b/icu4c/source/samples/layout/GDIFontMap.h @@ -0,0 +1,37 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#ifndef __GDIFONTMAP_H +#define __GDIFONTMAP_H + +#include + +#include "unicode/uscript.h" + +#include "layout/LETypes.h" + +#include "FontMap.h" +#include "GUISupport.h" +#include "RenderingFontInstance.h" + +#define BUFFER_SIZE 128 + +class GDIFontMap : public FontMap +{ +public: + GDIFontMap(HDC hdc, const char *fileName, le_int16 pointSize, GUISupport *guiSupport, RFIErrorCode &status); + + virtual ~GDIFontMap(); + +protected: + virtual const RenderingFontInstance *openFont(const char *fontName, le_int16 pointSize, RFIErrorCode &status); + +private: + HDC fHdc; +}; + +#endif diff --git a/icu4c/source/samples/layout/GDIGUISupport.cpp b/icu4c/source/samples/layout/GDIGUISupport.cpp new file mode 100644 index 00000000000..9f55ffed61e --- /dev/null +++ b/icu4c/source/samples/layout/GDIGUISupport.cpp @@ -0,0 +1,22 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GDIGUISupport.h + * + * created on: 11/06/2001 + * created by: Eric R. Mader + */ + +#include + +#include "GDIGUISupport.h" + +void GDIGUISupport::postErrorMessage(const char *message, const char *title) +{ + MessageBoxA(NULL, message, title, MB_ICONERROR); +} + diff --git a/icu4c/source/samples/layout/GDIGUISupport.h b/icu4c/source/samples/layout/GDIGUISupport.h new file mode 100644 index 00000000000..24b83502fa8 --- /dev/null +++ b/icu4c/source/samples/layout/GDIGUISupport.h @@ -0,0 +1,28 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GDIGUISupport.h + * + * created on: 11/06/2001 + * created by: Eric R. Mader + */ + +#ifndef __GDIGUISUPPORT_H +#define __GDIGUISUPPORT_H + +#include "GUISupport.h" + +class GDIGUISupport : public GUISupport +{ +public: + GDIGUISupport() {}; + ~GDIGUISupport() {}; + + virtual void postErrorMessage(const char *message, const char *title); +}; + +#endif diff --git a/icu4c/source/samples/layout/GUISupport.h b/icu4c/source/samples/layout/GUISupport.h new file mode 100644 index 00000000000..cf598bc7898 --- /dev/null +++ b/icu4c/source/samples/layout/GUISupport.h @@ -0,0 +1,26 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GUISupport.h + * + * created on: 11/06/2001 + * created by: Eric R. Mader + */ + +#ifndef __GUISUPPORT_H +#define __GUISUPPORT_H + +class GUISupport +{ +public: + GUISupport() {}; + ~GUISupport() {}; + + virtual void postErrorMessage(const char *message, const char *title) = 0; +}; + +#endif diff --git a/icu4c/source/samples/layout/GnomeFontInstance.cpp b/icu4c/source/samples/layout/GnomeFontInstance.cpp new file mode 100644 index 00000000000..057789f38af --- /dev/null +++ b/icu4c/source/samples/layout/GnomeFontInstance.cpp @@ -0,0 +1,276 @@ + +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GnomeFontInstance.cpp + * + * created on: 08/30/2001 + * created by: Eric R. Mader + */ + +#include +#include "freetype/freetype.h" + +#include "layout/LETypes.h" +#include "layout/LESwaps.h" + +#include "GnomeFontInstance.h" +#include "sfnt.h" +#include "cmaps.h" + +GnomeFontInstance::GnomeFontInstance(TT_Engine engine, const TT_Text *fontPathName, le_int16 pointSize, RFIErrorCode &status) + : RenderingFontInstance(NULL, pointSize) +{ + TT_Error error; + TT_Face_Properties faceProperties; + + fFace.z = NULL; + + error = TT_Open_Face(engine, fontPathName, &fFace); + + if (error != 0) { + status = RFI_FONT_FILE_NOT_FOUND_ERROR; + return; + } + + error = TT_New_Instance(fFace, &fInstance); + + if (error != 0) { + status = RFI_OUT_OF_MEMORY_ERROR; + return; + } + + // FIXME: what about the display resolution? + // TT_Set_Instance_Resolutions(fInstance, 72, 72); + TT_Set_Instance_CharSize(fInstance, pointSize << 6); + + TT_Get_Face_Properties(fFace, &faceProperties); + + fUnitsPerEM = faceProperties.header->Units_Per_EM; + + fAscent = (le_int32) yUnitsToPoints(faceProperties.horizontal->Ascender); + fDescent = (le_int32) -yUnitsToPoints(faceProperties.horizontal->Descender); + fLeading = (le_int32) yUnitsToPoints(faceProperties.horizontal->Line_Gap); + + // printf("Face = %s, unitsPerEM = %d, ascent = %d, descent = %d\n", fontPathName, fUnitsPerEM, fAscent, fDescent); + + error = TT_New_Glyph(fFace, &fGlyph); + + if (error != 0) { + status = RFI_OUT_OF_MEMORY_ERROR; + return; + } + + status = initMapper(); + if (LE_SUCCESS(status)) { + status = initFontTableCache(); + } +} + +GnomeFontInstance::~GnomeFontInstance() +{ + if (fFace.z != NULL) { + TT_Close_Face(fFace); + } +} + +const void *GnomeFontInstance::readFontTable(LETag tableTag) const +{ + TT_Long len = 0; + void *result = NULL; + + TT_Get_Font_Data(fFace, tableTag, 0, NULL, &len); + + if (len > 0) { + result = new char[len]; + TT_Get_Font_Data(fFace, tableTag, 0, result, &len); + } + + return result; +} + +void GnomeFontInstance::getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const +{ + advance.fX = 0; + advance.fY = 0; + + if (glyph == 0xFFFF) { + return; + } + + TT_Glyph_Metrics metrics; + TT_Error error; + + error = TT_Load_Glyph(fInstance, fGlyph, glyph, TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH); + + if (error != 0) { + return; + } + + TT_Get_Glyph_Metrics(fGlyph, &metrics); + + advance.fX = metrics.advance >> 6; + return; +} + +le_bool GnomeFontInstance::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const +{ +#if 1 + TT_Outline outline; + TT_Error error; + + error = TT_Load_Glyph(fInstance, fGlyph, glyph, TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH); + + if (error != 0) { + return false; + } + + error = TT_Get_Glyph_Outline(fGlyph, &outline); + + if (error != 0 || pointNumber >= outline.n_points) { + return false; + } + + point.fX = outline.points[pointNumber].x >> 6; + point.fY = outline.points[pointNumber].y >> 6; + + return true; +#else + return false; +#endif +} + +#define _R(b7,b6,b5,b4,b3,b2,b1,b0) ((b0<<7)|(b1<<6)|(b2<<5)|(b3<<4)|(b4<<3)|(b5<<2)|(b6<<1)|b7) +#define _B(b,n) ((b>>n)&1) +#define _H(b) _R(_B(b,7),_B(b,6),_B(b,5),_B(b,4),_B(b,3),_B(b,2),_B(b,1),_B(b,0)) + +const char bitReverse[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + + +// FIXME: this would be much faster if we cached the TT_Glyph objects based on the glyph ID... +void GnomeFontInstance::drawGlyphs(void *surface, LEGlyphID *glyphs, le_uint32 count, le_int32 *dx, + le_int32 x, le_int32 y, le_int32 width, le_int32 height) const +{ + GtkWidget *widget = (GtkWidget *) surface; + le_int32 i, xx = 0, yy = 0, zz; + le_int32 minx = 0, maxx = 0, miny = 0, maxy = 0; + TT_Glyph_Metrics metrics; + TT_Error error; + + //printf("drawGlyphs() - x = %d, y = %d, ", x, y); + + for (i = 0; i < count; i += 1) { + error = TT_Load_Glyph(fInstance, fGlyph, glyphs[i], TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH); + if (error == 0) { + TT_Get_Glyph_Metrics(fGlyph, &metrics); + + zz = xx + metrics.bbox.xMin; + if (minx > zz) { + minx = zz; + } + + zz = xx + metrics.bbox.xMax; + if (maxx < zz) { + maxx = zz; + } + + zz = yy + metrics.bbox.yMin; + if (miny > zz) { + miny = zz; + } + + zz = yy + metrics.bbox.yMax; + if (maxy < zz) { + maxy = zz; + } + } + + xx += (dx[i] * 64); + } + + + minx = (minx & -64) >> 6; + miny = (miny & -64) >> 6; + + maxx = ((maxx + 63) & -64) >> 6; + maxy = ((maxy + 63) & -64) >> 6; + + //printf("minx = %d, maxx = %d, miny = %d, maxy = %d\n", minx, maxx, miny, maxy); + + TT_Raster_Map raster; + unsigned char *bits; + + raster.flow = TT_Flow_Down; + raster.width = maxx - minx; + raster.rows = maxy - miny; + raster.cols = (raster.width + 7) / 8; + raster.size = raster.cols * raster.rows; + + raster.bitmap = bits = new unsigned char[raster.size]; + + for (i = 0; i < raster.size; i += 1) { + bits[i] = 0; + } + + xx = (-minx) * 64; yy = (-miny) * 64; + + for (i = 0; i < count; i += 1) { + if (glyphs[i] < 0xFFFE) { + error = TT_Load_Glyph(fInstance, fGlyph, glyphs[i], TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH); + + if (error == 0) { + TT_Get_Glyph_Bitmap(fGlyph, &raster, xx, yy); + } + } + + xx += (dx[i] * 64); + } + + for (i = 0; i < raster.size; i += 1) { + bits[i] = bitReverse[bits[i]]; + } + + if (raster.width > 0 && raster.rows > 0) { + GdkBitmap *bitmap = gdk_bitmap_create_from_data(NULL, (const gchar *) raster.bitmap, raster.width, raster.rows); + + gint bitsx = x + minx; + gint bitsy = y - maxy; + + gdk_gc_set_clip_origin(widget->style->black_gc, bitsx, bitsy); + gdk_gc_set_clip_mask(widget->style->black_gc, bitmap); + + gdk_draw_rectangle(widget->window, + widget->style->black_gc, + TRUE, + bitsx, bitsy, + raster.width, raster.rows); + + gdk_gc_set_clip_origin(widget->style->black_gc, 0, 0); + gdk_gc_set_clip_mask(widget->style->black_gc, NULL); + + gdk_bitmap_unref(bitmap); + } + + delete[] bits; +} diff --git a/icu4c/source/samples/layout/GnomeFontInstance.h b/icu4c/source/samples/layout/GnomeFontInstance.h new file mode 100644 index 00000000000..cc947af782b --- /dev/null +++ b/icu4c/source/samples/layout/GnomeFontInstance.h @@ -0,0 +1,49 @@ + +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GnomeFontInstance.h + * + * created on: 08/30/2001 + * created by: Eric R. Mader + */ + +#ifndef __GNOMEFONTINSTANCE_H +#define __GNOMEFONTINSTANCE_H + +#include +#include "freetype/freetype.h" + +#include "layout/LETypes.h" +#include "RenderingFontInstance.h" +#include "cmaps.h" + +class GnomeFontInstance : public RenderingFontInstance +{ + protected: + TT_Face fFace; + TT_Instance fInstance; + TT_Glyph fGlyph; + + virtual const void *readFontTable(LETag tableTag) const; + + public: + GnomeFontInstance(TT_Engine engine, const TT_Text *fontPathName, le_int16 pointSize, RFIErrorCode &status); + + virtual ~GnomeFontInstance(); + + + virtual void getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const; + + virtual le_bool getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const; + + virtual void drawGlyphs(void *surface, LEGlyphID *glyphs, le_uint32 count, le_int32 *dx, + le_int32 x, le_int32 y, le_int32 width, le_int32 height) const; + +}; + +#endif diff --git a/icu4c/source/samples/layout/GnomeFontMap.cpp b/icu4c/source/samples/layout/GnomeFontMap.cpp new file mode 100644 index 00000000000..de2ddeda637 --- /dev/null +++ b/icu4c/source/samples/layout/GnomeFontMap.cpp @@ -0,0 +1,33 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#include + +#include "freetype/freetype.h" + +#include "RenderingFontInstance.h" +#include "GnomeFontInstance.h" + +#include "GUISupport.h" +#include "FontMap.h" +#include "GnomeFontMap.h" + +GnomeFontMap::GnomeFontMap(TT_Engine engine, const char *fileName, le_int16 pointSize, GUISupport *guiSupport, RFIErrorCode &status) + : FontMap(fileName, pointSize, guiSupport, status), fEngine(engine) +{ + // nothing to do? +} + +GnomeFontMap::~GnomeFontMap() +{ + // anything? +} + +const RenderingFontInstance *GnomeFontMap::openFont(const char *fontName, le_int16 pointSize, RFIErrorCode &status) +{ + return new GnomeFontInstance(fEngine, fontName, pointSize, status); +} diff --git a/icu4c/source/samples/layout/GnomeFontMap.h b/icu4c/source/samples/layout/GnomeFontMap.h new file mode 100644 index 00000000000..0c4aa8bf3e4 --- /dev/null +++ b/icu4c/source/samples/layout/GnomeFontMap.h @@ -0,0 +1,37 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#ifndef __GNOMEFONTMAP_H +#define __GNOMEFONTMAP_H + +#include "freetype/freetype.h" + +#include "unicode/uscript.h" + +#include "layout/LETypes.h" + +#include "GUISupport.h" +#include "FontMap.h" +#include "RenderingFontInstance.h" + +#define BUFFER_SIZE 128 + +class GnomeFontMap : public FontMap +{ + public: + GnomeFontMap(TT_Engine engine, const char *fileName, le_int16 pointSize, GUISupport *guiSupport, RFIErrorCode &status); + + virtual ~GnomeFontMap(); + + protected: + virtual const RenderingFontInstance *openFont(const char *fontName, le_int16 pointSize, RFIErrorCode &status); + + private: + TT_Engine fEngine; +}; + +#endif diff --git a/icu4c/source/samples/layout/GnomeGUISupport.cpp b/icu4c/source/samples/layout/GnomeGUISupport.cpp new file mode 100644 index 00000000000..632044fdb6a --- /dev/null +++ b/icu4c/source/samples/layout/GnomeGUISupport.cpp @@ -0,0 +1,23 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GnomeGUISupport.h + * + * created on: 11/06/2001 + * created by: Eric R. Mader + */ + +#include + +#include "GnomeGUISupport.h" + +void GnomeGUISupport::postErrorMessage(const char *message, const char *title) +{ + fprintf(stderr, "%s: %s\n", title, message); +} + + diff --git a/icu4c/source/samples/layout/GnomeGUISupport.h b/icu4c/source/samples/layout/GnomeGUISupport.h new file mode 100644 index 00000000000..9986ea05193 --- /dev/null +++ b/icu4c/source/samples/layout/GnomeGUISupport.h @@ -0,0 +1,28 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: GnomeGUISupport.h + * + * created on: 11/06/2001 + * created by: Eric R. Mader + */ + +#ifndef __GNOMEGUISUPPORT_H +#define __GNOMEGUISUPPORT_H + +#include "GUISupport.h" + +class GnomeGUISupport : public GUISupport +{ +public: + GnomeGUISupport() {}; + ~GnomeGUISupport() {}; + + virtual void postErrorMessage(const char *message, const char *title); +}; + +#endif diff --git a/icu4c/source/samples/layout/Makefile.in b/icu4c/source/samples/layout/Makefile.in new file mode 100644 index 00000000000..1a45f9673cd --- /dev/null +++ b/icu4c/source/samples/layout/Makefile.in @@ -0,0 +1,88 @@ +## Makefile.in for ICU - samples/layout +## Copyright (c) 2001, International Business Machines Corporation and +## others. All Rights Reserved. + +## Source directory information +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ + +top_builddir = ../.. + +include $(top_builddir)/icudefs.mk + +## Platform-specific setup +include @platform_make_fragment@ + +## Build directory information +subdir = samples/layout + +## Extra files to remove for 'make clean' +CLEANFILES = *~ $(DEPS) + +## Target information +TARGET = gnomelayout + +DEFS = @DEFS@ +CPPFLAGS = @CPPFLAGS@ `gnome-config --cflags gnomeui` -I$(top_builddir)/common -I$(top_srcdir)/common -I$(top_srcdir)/i18n -I$(top_srcdir)/layout -I$(top_srcdir) +CFLAGS = @CFLAGS@ +CXXFLAGS = @CXXFLAGS@ +ENABLE_RPATH = @ENABLE_RPATH@ +ifeq ($(ENABLE_RPATH),YES) +RPATHLDFLAGS = $(LD_RPATH)$(LD_RPATH_PRE)$(libdir) +endif +LDFLAGS = @LDFLAGS@ $(RPATHLDFLAGS) +INVOKE = $(LDLIBRARYPATH_ENVVAR)=$(top_builddir)/common:$(top_builddir)/i18n:$(top_builddir)/tools/toolutil:$$$(LDLIBRARYPATH_ENVVAR) +LIBS = $(LIBICULE) $(LIBICUUC) $(LIBICUI18N) @LIBS@ @LIB_M@ `gnome-config --libs gnomeui` -lttf + + +OBJECTS=cmaps.o scrptrun.o UnicodeReader.o GnomeGUISupport.o FontMap.o GnomeFontMap.o RenderingFontInstance.o GnomeFontInstance.o paragraph.o gnomelayout.o + +DEPS = $(OBJECTS:.o=.d) + +## List of phony targets +.PHONY : all all-local install install-local clean clean-local \ +distclean distclean-local dist dist-local check check-local + +## Clear suffix list +.SUFFIXES : + +## List of standard targets +all: all-local +install: install-local +clean: clean-local +distclean : distclean-local +dist: dist-local +check: all check-local + +all-local: $(TARGET) + +install-local: + +dist-local: + +clean-local: + test -z "$(CLEANFILES)" || $(RMV) $(CLEANFILES) + $(RMV) $(OBJECTS) $(TARGET) + +distclean-local: clean-local + $(RMV) Makefile + +check-local: all-local + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +$(TARGET) : $(OBJECTS) + $(LINK.cc) -o $@ $^ $(LIBS) + +invoke: + ICU_DATA=$${ICU_DATA:-$(top_builddir)/data/} TZ=PST8PDT $(INVOKE) $(INVOCATION) + +ifeq (,$(MAKECMDGOALS)) +-include $(DEPS) +else +ifneq ($(patsubst %clean,,$(MAKECMDGOALS)),) +-include $(DEPS) +endif +endif diff --git a/icu4c/source/samples/layout/RenderingFontInstance.cpp b/icu4c/source/samples/layout/RenderingFontInstance.cpp new file mode 100644 index 00000000000..c8b8f624817 --- /dev/null +++ b/icu4c/source/samples/layout/RenderingFontInstance.cpp @@ -0,0 +1,164 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: RenderingFontInstance.cpp + * + * created on: 10/22/2001 + * created by: Eric R. Mader + */ + +#include "LETypes.h" +#include "LEFontInstance.h" +#include "RenderingFontInstance.h" +#include "LESwaps.h" +#include "sfnt.h" +#include "cmaps.h" + +#include + +RenderingFontInstance::RenderingFontInstance(void *surface, le_int16 pointSize) + : fSurface(surface), fPointSize(pointSize), fUnitsPerEM(0), fAscent(0), fDescent(), fLeading(0), + fTableCache(NULL), fTableCacheCurr(0), fTableCacheSize(0), fMapper(NULL) +{ + // we expect the subclass to call + // initMapper() and initFontTableCache +} + +RenderingFontInstance::~RenderingFontInstance() +{ + flushFontTableCache(); + delete[] fTableCache; + + delete fMapper; +} + +void RenderingFontInstance::mapCharsToGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, + le_bool reverse, const LECharMapper *mapper, LEGlyphID glyphs[]) const +{ + le_int32 i, out = 0, dir = 1; + + if (reverse) { + out = count - 1; + dir = -1; + } + + for (i = offset; i < offset + count; i += 1, out += dir) { + LEUnicode16 high = chars[i]; + LEUnicode32 code = high; + + if (i < offset + count - 1 && high >= 0xD800 && high <= 0xDBFF) { + LEUnicode16 low = chars[i + 1]; + + if (low >= 0xDC00 && low <= 0xDFFF) { + code = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000; + } + } + + glyphs[out] = mapCharToGlyph(code, mapper); + + if (code >= 0x10000) { + i += 1; + glyphs[out += dir] = 0xFFFF; + } + } +} + +LEGlyphID RenderingFontInstance::mapCharToGlyph(LEUnicode32 ch, const LECharMapper *mapper) const +{ + LEUnicode32 mappedChar = mapper->mapChar(ch); + + if (mappedChar == 0xFFFE || mappedChar == 0xFFFF) { + return 0xFFFF; + } + + if (mappedChar == 0x200C || mappedChar == 0x200D) { + return 1; + } + + return fMapper->unicodeToGlyph(mappedChar); +} + +const void *RenderingFontInstance::getFontTable(LETag tableTag) const +{ + for (int i = 0; i < fTableCacheCurr; i += 1) { + if (fTableCache[i].tag == tableTag) { + return fTableCache[i].table; + } + } + + RenderingFontInstance *realThis = (RenderingFontInstance *) this; + + if (realThis->fTableCacheCurr >= realThis->fTableCacheSize) { + le_int32 newSize = realThis->fTableCacheSize + TABLE_CACHE_GROW; + TableCacheEntry *newTable = new TableCacheEntry[newSize]; + + // FIXME: need a better strategy than this... + if (newTable == NULL) { + return NULL; + } + + memcpy(newTable, realThis->fTableCache, realThis->fTableCacheSize * sizeof realThis->fTableCache[0]); + delete[] realThis->fTableCache; + + for (int i = realThis->fTableCacheSize; i < newSize; i += 1) { + newTable[i].tag = 0; + newTable[i].table = NULL; + } + + realThis->fTableCache = newTable; + realThis->fTableCacheSize = newSize; + } + + realThis->fTableCache[realThis->fTableCacheCurr].tag = tableTag; + realThis->fTableCache[realThis->fTableCacheCurr].table = (void *) realThis->readFontTable(tableTag); + + return fTableCache[realThis->fTableCacheCurr++].table; +}; + +RFIErrorCode RenderingFontInstance::initMapper() +{ + LETag cmapTag = 0x636D6170; // 'cmap' + const CMAPTable *cmap = (const CMAPTable *) readFontTable(cmapTag); + + if (cmap == NULL) { + return RFI_MISSING_FONT_TABLE_ERROR; + } + + fMapper = CMAPMapper::createUnicodeMapper(cmap); + + if (fMapper == NULL) { + return RFI_MISSING_FONT_TABLE_ERROR; + } + + return RFI_NO_ERROR; +} + +RFIErrorCode RenderingFontInstance::initFontTableCache() +{ + fTableCacheSize = TABLE_CACHE_INIT; + fTableCache = new TableCacheEntry[fTableCacheSize]; + + if (fTableCache == 0) { + return RFI_OUT_OF_MEMORY_ERROR; + } + + for (int i = 0; i < fTableCacheSize; i += 1) { + fTableCache[i].tag = 0; + fTableCache[i].table = NULL; + } + + return RFI_NO_ERROR; +} + +void RenderingFontInstance::flushFontTableCache() +{ + for (int i = fTableCacheCurr - 1; i >= 0; i -= 1) { + delete[] (char *) fTableCache[i].table; + } + + fTableCacheCurr = 0; +} diff --git a/icu4c/source/samples/layout/RenderingFontInstance.h b/icu4c/source/samples/layout/RenderingFontInstance.h new file mode 100644 index 00000000000..0c047a359d4 --- /dev/null +++ b/icu4c/source/samples/layout/RenderingFontInstance.h @@ -0,0 +1,175 @@ + +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: RenderingFontInstance.h + * + * created on: 10/23/2001 + * created by: Eric R. Mader + */ + +#ifndef __RENDERINGFONTINSTANCE_H +#define __RENDERINGFONTINSTANCE_H + +#include "LETypes.h" +#include "LEFontInstance.h" +#include "cmaps.h" + +#define TABLE_CACHE_INIT 5 +#define TABLE_CACHE_GROW 5 + +struct TableCacheEntry +{ + LETag tag; + void *table; +}; + +enum RFIErrorCode { + RFI_NO_ERROR = 0, + + RFI_ILLEGAL_ARGUMENT_ERROR = 1, + RFI_FONT_FILE_NOT_FOUND_ERROR = 2, + RFI_MISSING_FONT_TABLE_ERROR = 3, + RFI_OUT_OF_MEMORY_ERROR = 4 +}; + +class RenderingFontInstance : public LEFontInstance +{ +protected: + void *fSurface; + + le_int32 fPointSize; + le_int32 fUnitsPerEM; + le_int32 fAscent; + le_int32 fDescent; + le_int32 fLeading; + + TableCacheEntry *fTableCache; + le_int32 fTableCacheCurr; + le_int32 fTableCacheSize; + + CMAPMapper *fMapper; + + virtual RFIErrorCode initMapper(); + virtual RFIErrorCode initFontTableCache(); + virtual void flushFontTableCache(); + virtual const void *readFontTable(LETag tableTag) const = 0; + +public: + RenderingFontInstance(void *surface, le_int16 pointSize); + + virtual ~RenderingFontInstance(); + + virtual const void *getFontTable(LETag tableTag) const; + + virtual le_bool canDisplay(LEUnicode32 ch) const + { + return fMapper->unicodeToGlyph(ch) != 0; + }; + + virtual le_int32 getUnitsPerEM() const + { + return fUnitsPerEM; + }; + + virtual le_int32 getLineHeight() const + { + return getAscent() + getDescent() + getLeading(); + }; + + virtual le_int32 getAscent() const + { + return fAscent; + }; + + virtual le_int32 getDescent() const + { + return fDescent; + }; + + virtual le_int32 getLeading() const + { + return fLeading; + }; + + virtual void mapCharsToGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse, const LECharMapper *mapper, LEGlyphID glyphs[]) const; + + virtual LEGlyphID mapCharToGlyph(LEUnicode32 ch, const LECharMapper *mapper) const; + + virtual le_int32 getName(le_uint16 platformID, le_uint16 scriptID, le_uint16 languageID, le_uint16 nameID, LEUnicode *name) const + { + // This is only used for CDAC fonts, and we'll have to loose that support anyhow... + //return (le_int32) fFontObject->getName(platformID, scriptID, languageID, nameID, name); + if (name != NULL) { + *name = 0; + } + + return 0; + }; + + virtual void getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const = 0; + + virtual le_bool getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const = 0; + + virtual const void setFont(void *surface) const + { + // default implementation is to ignore this + }; + + virtual void drawGlyphs(void *surface, LEGlyphID *glyphs, le_uint32 count, le_int32 *dx, + le_int32 x, le_int32 y, le_int32 width, le_int32 height) const = 0; + + float getXPixelsPerEm() const + { + return (float) fPointSize; + }; + + float getYPixelsPerEm() const + { + return (float) fPointSize; + }; + + float xUnitsToPoints(float xUnits) const + { + return (xUnits * fPointSize) / (float) fUnitsPerEM; + }; + + float yUnitsToPoints(float yUnits) const + { + return (yUnits * fPointSize) / (float) fUnitsPerEM; + }; + + void unitsToPoints(LEPoint &units, LEPoint &points) const + { + points.fX = xUnitsToPoints(units.fX); + points.fY = yUnitsToPoints(units.fY); + } + + float xPixelsToUnits(float xPixels) const + { + return (xPixels * fUnitsPerEM) / (float) fPointSize; + }; + + float yPixelsToUnits(float yPixels) const + { + return (yPixels * fUnitsPerEM) / (float) fPointSize; + }; + + void pixelsToUnits(LEPoint &pixels, LEPoint &units) const + { + units.fX = xPixelsToUnits(pixels.fX); + units.fY = yPixelsToUnits(pixels.fY); + }; + + void transformFunits(float xFunits, float yFunits, LEPoint &pixels) const + { + pixels.fX = xUnitsToPoints(xFunits); + pixels.fY = yUnitsToPoints(yFunits); + } +}; + +#endif diff --git a/icu4c/source/samples/layout/Sample.utf8 b/icu4c/source/samples/layout/Sample.utf8 new file mode 100644 index 00000000000..272c76145ca --- /dev/null +++ b/icu4c/source/samples/layout/Sample.utf8 @@ -0,0 +1,28 @@ +The LayoutEngine does all the work necessary to display Unicode text written in languages with complex +writing systems such as Hindi (हिन्दी) +Thai (ไทย) and Arabic (العربية). Here's a sample of some text written in Sanskrit: +श्रीमद् भगवद्गीता +अध्याय अर्जुन विषाद +योग धृतराष्ट्र उवाचृ +धर्मक्षेत्रे +कुरुक्षेत्रे समवेता +युयुत्सवः मामकाः +पाण्डवाश्चैव किमकुर्वत +संजव Here's a sample of some text written in Arabic: +أساسًا، تتعامل +الحواسيب فقط مع +الأرقام، وتقوم ب +تخزين الأحرف والمحارف ال +أخرى بعد أن تُعطي رقما م +عينا لكل واحد من +ها. وقبل اختراع +"يونِكود"، كان ه +ناك مئات الأنظمة +للتشفير وتخصيص +هذه الأر +قام للمح +ارف، ولم يوجد نظ +ام تشفير واحد يح +توي على جميع المحارف الض +رورية and here's a sample of some text written in Thai: +บทที่๑พายุไซโคลนโดโรธีอาศัยอยู่ท่ามกลางทุ่งใหญ่ในแคนซัสกับลุงเฮนรีชาวไร่และป้าเอ็มภรรยาชาวไร่บ้านของพวกเขาหลังเล็กเพราะไม้สร้างบ้านต้องขนมาด้วยเกวียนเป็นระยะทางหลายไมล์ diff --git a/icu4c/source/samples/layout/UnicodeReader.cpp b/icu4c/source/samples/layout/UnicodeReader.cpp new file mode 100644 index 00000000000..97aa8105c7d --- /dev/null +++ b/icu4c/source/samples/layout/UnicodeReader.cpp @@ -0,0 +1,116 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#include +#include +#include + +#include "unicode/utypes.h" +#include "unicode/unistr.h" + +#include "GUISupport.h" +#include "UnicodeReader.h" + + +/* + * Read the text from a file. The text must start with a Unicode Byte + * Order Mark (BOM) so that we know what order to read the bytes in. + */ +const UChar *UnicodeReader::readFile(const char *fileName, GUISupport *guiSupport, int32_t &charCount) +{ + FILE *f; + int32_t fileSize; + + UChar *charBuffer; + char *byteBuffer; + char startBytes[4] = {'\xA5', '\xA5', '\xA5', '\xA5'}; + char errorMessage[128]; + char *cp = ""; + int32_t signatureLength = 0; + + f = fopen(fileName, "rb"); + + if( f == NULL ) { + sprintf(errorMessage,"Couldn't open %s: %s \n", fileName, strerror(errno)); + guiSupport->postErrorMessage(errorMessage, "Text File Error"); + return 0; + } + + fseek(f, 0, SEEK_END); + fileSize = ftell(f); + + fseek(f, 0, SEEK_SET); + fread(startBytes, sizeof(char), 4, f); + + if (startBytes[0] == '\xFE' && startBytes[1] == '\xFF') { + cp = "UTF-16BE"; + signatureLength = 2; + } else if (startBytes[0] == '\xFF' && startBytes[1] == '\xFE') { + if (startBytes[2] == '\x00' && startBytes[3] == '\x00') { + cp = "UTF-32LE"; + signatureLength = 4; + } else { + cp = "UTF-16LE"; + signatureLength = 2; + } + } else if (startBytes[0] == '\xEF' && startBytes[1] == '\xBB' && startBytes[2] == '\xBF') { + cp = "UTF-8"; + signatureLength = 3; + } else if (startBytes[0] == '\x0E' && startBytes[1] == '\xFE' && startBytes[2] == '\xFF') { + cp = "SCSU"; + signatureLength = 3; + } else if (startBytes[0] == '\x00' && startBytes[1] == '\x00' && + startBytes[2] == '\xFE' && startBytes[3] == '\xFF') { + cp = "UTF-32BE"; + signatureLength = 4; + } else { + sprintf(errorMessage, "Couldn't detect the encoding of %s: (%2.2X, %2.2X, %2.2X, %2.2X)\n", fileName, + startBytes[0], startBytes[1], startBytes[2], startBytes[3]); + guiSupport->postErrorMessage(errorMessage, "Text File Error"); + fclose(f); + return 0; + } + + fileSize -= signatureLength; + fseek(f, signatureLength, SEEK_SET); + byteBuffer = new char[fileSize]; + + if(byteBuffer == 0) { + sprintf(errorMessage,"Couldn't get memory for reading %s: %s \n", fileName, strerror(errno)); + guiSupport->postErrorMessage(errorMessage, "Text File Error"); + fclose(f); + return 0; + } + + fread(byteBuffer, sizeof(char), fileSize, f); + if( ferror(f) ) { + sprintf(errorMessage,"Couldn't read %s: %s \n", fileName, strerror(errno)); + guiSupport->postErrorMessage(errorMessage, "Text File Error"); + fclose(f); + delete[] byteBuffer; + return 0; + } + fclose(f); + + UnicodeString myText(byteBuffer, fileSize, cp); + + delete[] byteBuffer; + + charCount = myText.length(); + charBuffer = new UChar[charCount]; + if(charBuffer == 0) { + sprintf(errorMessage,"Couldn't get memory for reading %s: %s \n", fileName, strerror(errno)); + guiSupport->postErrorMessage(errorMessage, "Text File Error"); + return 0; + } + + myText.extract(0, myText.length(), charBuffer); + charBuffer[charCount + 1] = 0; // NULL terminate for easier reading in the debugger + + return charBuffer; +} + diff --git a/icu4c/source/samples/layout/UnicodeReader.h b/icu4c/source/samples/layout/UnicodeReader.h new file mode 100644 index 00000000000..47959bb3a49 --- /dev/null +++ b/icu4c/source/samples/layout/UnicodeReader.h @@ -0,0 +1,32 @@ +/* + ****************************************************************************** + * Copyright (C) 1998-2001, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + */ + +#ifndef __UNICODEREADER_H +#define __UNICODEREADER_H + +#include "unicode/utypes.h" + +#include "GUISupport.h" + +class UnicodeReader +{ +public: + UnicodeReader() + { + // nothing... + } + + ~UnicodeReader() + { + // nothing, too + } + + static const UChar *readFile(const char *fileName, GUISupport *guiSupport, int32_t &charCount); +}; + +#endif + diff --git a/icu4c/source/samples/layout/cmaps.cpp b/icu4c/source/samples/layout/cmaps.cpp new file mode 100644 index 00000000000..346d5043a32 --- /dev/null +++ b/icu4c/source/samples/layout/cmaps.cpp @@ -0,0 +1,203 @@ +/* + ****************************************************************************** * + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ****************************************************************************** * + * file name: cmaps.cpp + * + * created on: ??/??/2001 + * created by: Eric R. Mader + */ + +#include "layout/LETypes.h" +#include "layout/LESwaps.h" + +#include "sfnt.h" +#include "cmaps.h" + +// +// Finds the high bit by binary searching +// through the bits in value. +// +le_uint8 highBit(le_uint32 value) +{ + le_uint8 bit = 0; + + if (value >= 1 << 16) { + value >>= 16; + bit += 16; + } + + if (value >= 1 << 8) { + value >>= 8; + bit += 8; + } + + if (value >= 1 << 4) { + value >>= 4; + bit += 4; + } + + if (value >= 1 << 2) { + value >>= 2; + bit += 2; + } + + if (value >= 1 << 1) { + value >>= 1; + bit += 1; + } + + return bit; +} + +CMAPMapper *CMAPMapper::createUnicodeMapper(const CMAPTable *cmap) +{ + le_uint16 i; + le_uint16 nSubtables = SWAPW(cmap->numberSubtables); + const CMAPEncodingSubtable *subtable = NULL; + le_uint32 offset1 = 0, offset10 = 0; + + for (i = 0; i < nSubtables; i += 1) { + const CMAPEncodingSubtableHeader *esh = &cmap->encodingSubtableHeaders[i]; + + if (SWAPW(esh->platformID) == 3) { + switch (SWAPW(esh->platformSpecificID)) { + case 1: + offset1 = SWAPL(esh->encodingOffset); + break; + + case 10: + offset10 = SWAPL(esh->encodingOffset); + break; + } + } + } + + + if (offset10 != 0) + { + subtable = (const CMAPEncodingSubtable *) ((const char *) cmap + offset10); + } else if (offset1 != 0) { + subtable = (const CMAPEncodingSubtable *) ((const char *) cmap + offset1); + } else { + return NULL; + } + + switch (SWAPW(subtable->format)) { + case 4: + return new CMAPFormat4Mapper(cmap, (const CMAPFormat4Encoding *) subtable); + + case 12: + { + const CMAPFormat12Encoding *encoding = (const CMAPFormat12Encoding *) subtable; + + return new CMAPGroupMapper(cmap, encoding->groups, SWAPL(encoding->nGroups)); + } + + default: + break; + } + + return NULL; +} + +CMAPFormat4Mapper::CMAPFormat4Mapper(const CMAPTable *cmap, const CMAPFormat4Encoding *header) + : CMAPMapper(cmap) +{ + le_uint16 segCount = SWAPW(header->segCountX2) / 2; + + fEntrySelector = SWAPW(header->entrySelector); + fRangeShift = SWAPW(header->rangeShift) / 2; + fEndCodes = &header->endCodes[0]; + fStartCodes = &header->endCodes[segCount + 1]; // + 1 for reservedPad... + fIdDelta = &fStartCodes[segCount]; + fIdRangeOffset = &fIdDelta[segCount]; +} + +LEGlyphID CMAPFormat4Mapper::unicodeToGlyph(LEUnicode32 unicode32) const +{ + if (unicode32 >= 0x10000) { + return 0; + } + + LEUnicode16 unicode = (LEUnicode16) unicode32; + le_uint16 index = 0; + le_uint16 probe = 1 << fEntrySelector; + LEGlyphID result = 0; + + if (SWAPW(fStartCodes[fRangeShift]) <= unicode) { + index = fRangeShift; + } + + while (probe > (1 << 0)) { + probe >>= 1; + + if (SWAPW(fStartCodes[index + probe]) <= unicode) { + index += probe; + } + } + + if (unicode >= SWAPW(fStartCodes[index]) && unicode <= SWAPW(fEndCodes[index])) { + if (fIdRangeOffset[index] == 0) { + result = (LEGlyphID) unicode; + } else { + le_uint16 offset = unicode - SWAPW(fStartCodes[index]); + le_uint16 rangeOffset = SWAPW(fIdRangeOffset[index]); + le_uint16 *glyphIndexTable = (le_uint16 *) ((char *) &fIdRangeOffset[index] + rangeOffset); + + result = SWAPW(glyphIndexTable[offset]); + } + + result += SWAPW(fIdDelta[index]); + } else { + result = 0; + } + + return result; +} + +CMAPFormat4Mapper::~CMAPFormat4Mapper() +{ + // parent destructor does it all +} + +CMAPGroupMapper::CMAPGroupMapper(const CMAPTable *cmap, const CMAPGroup *groups, le_uint32 nGroups) + : CMAPMapper(cmap), fGroups(groups) +{ + le_uint8 bit = highBit(nGroups); + fPower = 1 << bit; + fRangeOffset = nGroups - fPower; +} + +LEGlyphID CMAPGroupMapper::unicodeToGlyph(LEUnicode32 unicode32) const +{ + le_int32 probe = fPower; + le_int32 range = 0; + + if (SWAPL(fGroups[fRangeOffset].startCharCode) <= unicode32) { + range = fRangeOffset; + } + + while (probe > (1 << 0)) { + probe >>= 1; + + if (SWAPL(fGroups[range + probe].startCharCode) <= unicode32) { + range += probe; + } + } + + if (SWAPL(fGroups[range].startCharCode) <= unicode32 && SWAPL(fGroups[range].endCharCode) >= unicode32) { + return (LEGlyphID) (SWAPL(fGroups[range].startGlyphCode) + unicode32 - SWAPL(fGroups[range].startCharCode)); + } + + return 0; +} + +CMAPGroupMapper::~CMAPGroupMapper() +{ + // parent destructor does it all +} + diff --git a/icu4c/source/samples/layout/cmaps.h b/icu4c/source/samples/layout/cmaps.h new file mode 100644 index 00000000000..faff9a35abf --- /dev/null +++ b/icu4c/source/samples/layout/cmaps.h @@ -0,0 +1,89 @@ +/* + ****************************************************************************** * + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ****************************************************************************** * + * file name: cmaps.h + * + * created on: ??/??/2001 + * created by: Eric R. Mader + */ + +#ifndef __CMAPS_H +#define __CMAPS_H + +#include "layout/LETypes.h" +#include "sfnt.h" + +class CMAPMapper +{ +public: + virtual LEGlyphID unicodeToGlyph(LEUnicode32 unicode32) const = 0; + + virtual ~CMAPMapper(); + + static CMAPMapper *createUnicodeMapper(const CMAPTable *cmap); + +protected: + CMAPMapper(const CMAPTable *cmap); + + CMAPMapper() {}; + +private: + const CMAPTable *fcmap; +}; + +class CMAPFormat4Mapper : public CMAPMapper +{ +public: + CMAPFormat4Mapper(const CMAPTable *cmap, const CMAPFormat4Encoding *header); + + virtual ~CMAPFormat4Mapper(); + + virtual LEGlyphID unicodeToGlyph(LEUnicode32 unicode32) const; + +protected: + CMAPFormat4Mapper() {}; + +private: + le_uint16 fEntrySelector; + le_uint16 fRangeShift; + const le_uint16 *fEndCodes; + const le_uint16 *fStartCodes; + const le_uint16 *fIdDelta; + const le_uint16 *fIdRangeOffset; +}; + +class CMAPGroupMapper : public CMAPMapper +{ +public: + CMAPGroupMapper(const CMAPTable *cmap, const CMAPGroup *groups, le_uint32 nGroups); + + virtual ~CMAPGroupMapper(); + + virtual LEGlyphID unicodeToGlyph(LEUnicode32 unicode32) const; + +protected: + CMAPGroupMapper() {}; + +private: + le_int32 fPower; + le_int32 fRangeOffset; + const CMAPGroup *fGroups; +}; + +inline CMAPMapper::CMAPMapper(const CMAPTable *cmap) + : fcmap(cmap) +{ + // nothing else to do +} + +inline CMAPMapper::~CMAPMapper() +{ + delete[] (char *) fcmap; +} + +#endif + diff --git a/icu4c/source/samples/layout/gnomelayout.cpp b/icu4c/source/samples/layout/gnomelayout.cpp new file mode 100644 index 00000000000..5ca5c68a720 --- /dev/null +++ b/icu4c/source/samples/layout/gnomelayout.cpp @@ -0,0 +1,201 @@ +/* + ****************************************************************************** * + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ****************************************************************************** * + * file name: gnomelayout.cpp + * + * created on: 09/04/2001 + * created by: Eric R. Mader + */ + +#include +#include "freetype/freetype.h" + +#include "unicode/ustring.h" +#include "unicode/uscript.h" + +#include "unicode/loengine.h" + +#include "GnomeFontInstance.h" + +#include "paragraph.h" + +#include "GnomeGUISupport.h" +#include "GnomeFontMap.h" +#include "UnicodeReader.h" +#include "scrptrun.h" + +#define ARRAY_LENGTH(array) (sizeof array / sizeof array[0]) + +void showabout(GtkWidget *widget, gpointer data) +{ + GtkWidget *aboutBox; + const gchar *writtenBy[] = { + "Eric Mader", + NULL + }; + + aboutBox = gnome_about_new("Gnome Layout Demo", + "0.1", + "Copyright (C) 1998-2001 By International Business Machines Corporation and others. All Rights Reserved.", + writtenBy, + "A simple demo of the ICU LayoutEngine.", + NULL); + + gtk_widget_show(aboutBox); +} + +void notimpl(GtkObject *object, gpointer data) +{ + gnome_ok_dialog("Not implemented..."); +} + +void shutdown(GtkObject *object, gpointer data) +{ + gtk_main_quit(); +} + +GnomeUIInfo fileMenu[] = +{ + GNOMEUIINFO_MENU_OPEN_ITEM(notimpl, NULL), + GNOMEUIINFO_SEPARATOR, + GNOMEUIINFO_MENU_EXIT_ITEM(shutdown, NULL), + GNOMEUIINFO_END +}; + +GnomeUIInfo helpMenu[] = +{ + // GNOMEUIINFO_HELP("gnomelayout"), + GNOMEUIINFO_MENU_ABOUT_ITEM(showabout, NULL), + GNOMEUIINFO_END +}; + +GnomeUIInfo mainMenu[] = +{ + GNOMEUIINFO_SUBTREE(N_("File"), fileMenu), + GNOMEUIINFO_SUBTREE(N_("Help"), helpMenu), + GNOMEUIINFO_END +}; + +struct Context +{ + long width; + long height; + Paragraph *paragraph; +}; + +gint eventDelete(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + return FALSE; +} + +gint eventDestroy(GtkWidget *widget, GdkEvent *event, Context *context) +{ + shutdown(GTK_OBJECT(widget), context); + return 0; +} + +gint eventConfigure(GtkWidget *widget, GdkEventConfigure *event, Context *context) +{ + context->width = event->width; + context->height = event->height; + + if (context->width > 0 && context->height > 0) { + context->paragraph->breakLines(context->width, context->height); + } + + return TRUE; +} + +gint eventExpose(GtkWidget *widget, GdkEvent *event, Context *context) +{ + gint maxLines = context->paragraph->getLineCount() - 1; + gint firstLine = 0, lastLine = context->height / context->paragraph->getLineHeight(); + + context->paragraph->draw(widget, firstLine, (maxLines < lastLine)? maxLines : lastLine); + + return TRUE; +} + +int main (int argc, char *argv[]) +{ + GtkWidget *app; + GtkWidget *area; + GtkStyle *style; + unsigned short status = 0; + Context context = {600, 400, NULL}; + TT_Engine engine; + + TT_Init_FreeType(&engine); + + RFIErrorCode fontStatus = RFI_NO_ERROR; + GnomeGUISupport *guiSupport = new GnomeGUISupport(); + GnomeFontMap *fontMap = new GnomeFontMap(engine, "FontMap.Gnome", 24, guiSupport, fontStatus); + + if (LE_FAILURE(fontStatus)) { + TT_Done_FreeType(engine); + return 1; + } + + // FIXME: is it cheating to pass NULL for surface, since we know that + // GnomeFontInstance won't use it? + context.paragraph = Paragraph::paragraphFactory("Sample.utf8", fontMap, guiSupport, NULL); + + if (context.paragraph != NULL) { + gnome_init("gnomelayout", "1.0", argc, argv); + app = gnome_app_new("gnomelayout", "Gnome Layout"); + + gtk_window_set_default_size(GTK_WINDOW(app), 600 - 24, 400); + + gnome_app_create_menus(GNOME_APP(app), mainMenu); + + gtk_signal_connect(GTK_OBJECT(app), + "delete_event", + GTK_SIGNAL_FUNC(eventDelete), + NULL); + + gtk_signal_connect(GTK_OBJECT(app), + "destroy", + GTK_SIGNAL_FUNC(eventDestroy), + &context); + + area = gtk_drawing_area_new(); + +#if 1 + style = gtk_style_copy(gtk_widget_get_style(area)); + + for (int i = 0; i < 5; i += 1) { + style->fg[i] =style->white; + } + + gtk_widget_set_style(area, style); +#endif + + gnome_app_set_contents(GNOME_APP(app), area); + + gtk_signal_connect(GTK_OBJECT(area), + "expose_event", + GTK_SIGNAL_FUNC(eventExpose), + &context); + + gtk_signal_connect(GTK_OBJECT(area), + "configure_event", + GTK_SIGNAL_FUNC(eventConfigure), + &context); + + gtk_widget_show_all(app); + + gtk_main(); + + delete context.paragraph; + } + + delete fontMap; + + TT_Done_FreeType(engine); + + exit(0); +} diff --git a/icu4c/source/samples/layout/layout.cpp b/icu4c/source/samples/layout/layout.cpp new file mode 100644 index 00000000000..9c9ea47e6d2 --- /dev/null +++ b/icu4c/source/samples/layout/layout.cpp @@ -0,0 +1,246 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: Layout.cpp + * + * created on: 08/03/2000 + * created by: Eric R. Mader + */ + +#include + +//#include "LETypes.h" +//#include "LEFontInstance.h" +//#include "LayoutEngine.h" +//#include "unicode/loengine.h" +#include "unicode/uscript.h" +//#include "LEScripts.h" + +#include "GDIFontInstance.h" + +#include "paragraph.h" + +#include "GDIGUISupport.h" +#include "GDIFontMap.h" +#include "UnicodeReader.h" +#include "scrptrun.h" + +#define ARRAY_LENGTH(array) (sizeof array / sizeof array[0]) + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) +{ + HWND hwnd; + MSG msg; + WNDCLASS wndclass; + TCHAR szAppName[] = TEXT("LayoutDemo"); + TCHAR szTitle[] = TEXT("LayoutDemo: Demo of LayoutEngine"); + RFIErrorCode status = RFI_NO_ERROR; + + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = sizeof(LONG); + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = szAppName; + + if (!RegisterClass(&wndclass)) { + MessageBox(NULL, TEXT("This demo only runs on Windows 2000!"), szAppName, MB_ICONERROR); + + return 0; + } + + hwnd = CreateWindow(szAppName, szTitle, + WS_OVERLAPPEDWINDOW | WS_VSCROLL, + CW_USEDEFAULT, CW_USEDEFAULT, + 600, 400, + NULL, NULL, hInstance, NULL); + + ShowWindow(hwnd, iCmdShow); + UpdateWindow(hwnd); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return msg.wParam; +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + Paragraph *paragraph; + static le_int32 windowCount = 0; + static GDIFontMap *fontMap = NULL; + static GDIGUISupport *guiSupport = new GDIGUISupport(); + + switch (message) { + case WM_CREATE: + { + RFIErrorCode fontStatus = RFI_NO_ERROR; + + hdc = GetDC(hwnd); + + fontMap = new GDIFontMap(hdc, "FontMap.GDI", 24, guiSupport, fontStatus); + + if (LE_FAILURE(fontStatus)) { + ReleaseDC(hwnd, hdc); + return 0; + } + + paragraph = Paragraph::paragraphFactory("Sample.utf8", fontMap, guiSupport, hdc); + SetWindowLong(hwnd, 0, (LONG) paragraph); + + windowCount += 1; + ReleaseDC(hwnd, hdc); + return 0; + } + + case WM_SIZE: + { + le_int32 width = LOWORD(lParam); + le_int32 height = HIWORD(lParam); + SCROLLINFO si; + + + paragraph = (Paragraph *) GetWindowLong(hwnd, 0); + + if (paragraph != NULL) { + // FIXME: does it matter what we put in the ScrollInfo + // if the window's been minimized? + if (width > 0 && height > 0) { + paragraph->breakLines(width, height); + } + + si.cbSize = sizeof si; + si.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = paragraph->getLineCount() - 1; + si.nPage = height / paragraph->getLineHeight(); + SetScrollInfo(hwnd, SB_VERT, &si, true); + } + + return 0; + } + + case WM_VSCROLL: + { + SCROLLINFO si; + le_int32 vertPos; + + si.cbSize = sizeof si; + si.fMask = SIF_ALL; + GetScrollInfo(hwnd, SB_VERT, &si); + + vertPos = si.nPos; + + switch (LOWORD(wParam)) + { + case SB_TOP: + si.nPos = si.nMin; + break; + + case SB_BOTTOM: + si.nPos = si.nMax; + break; + + case SB_LINEUP: + si.nPos -= 1; + break; + + case SB_LINEDOWN: + si.nPos += 1; + break; + + case SB_PAGEUP: + si.nPos -= si.nPage; + break; + + case SB_PAGEDOWN: + si.nPos += si.nPage; + break; + + case SB_THUMBTRACK: + si.nPos = si.nTrackPos; + break; + + default: + break; + } + + si.fMask = SIF_POS; + SetScrollInfo(hwnd, SB_VERT, &si, true); + GetScrollInfo(hwnd, SB_VERT, &si); + + paragraph = (Paragraph *) GetWindowLong(hwnd, 0); + + if (paragraph != NULL && si.nPos != vertPos) { + ScrollWindow(hwnd, 0, paragraph->getLineHeight() * (vertPos - si.nPos), NULL, NULL); + UpdateWindow(hwnd); + } + + return 0; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + SCROLLINFO si; + le_int32 firstLine, lastLine; + + hdc = BeginPaint(hwnd, &ps); + + si.cbSize = sizeof si; + si.fMask = SIF_ALL; + GetScrollInfo(hwnd, SB_VERT, &si); + + firstLine = si.nPos; + + paragraph = (Paragraph *) GetWindowLong(hwnd, 0); + + if (paragraph != NULL) { + // NOTE: si.nPos + si.nPage may include a partial line at the bottom + // of the window. We need this because scrolling assumes that the + // partial line has been painted. + lastLine = min (si.nPos + (le_int32) si.nPage, paragraph->getLineCount() - 1); + + paragraph->draw(hdc, firstLine, lastLine); + } + + EndPaint(hwnd, &ps); + return 0; + } + + case WM_DESTROY: + { + paragraph = (Paragraph *) GetWindowLong(hwnd, 0); + + if (paragraph != NULL) { + delete paragraph; + } + + if (--windowCount <= 0) { + delete fontMap; + + PostQuitMessage(0); + } + + return 0; + } + + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + + return 0; +} diff --git a/icu4c/source/samples/layout/layout.dsp b/icu4c/source/samples/layout/layout.dsp new file mode 100644 index 00000000000..4726d3811fd --- /dev/null +++ b/icu4c/source/samples/layout/layout.dsp @@ -0,0 +1,184 @@ +# Microsoft Developer Studio Project File - Name="layout" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=layout - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "layout.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "layout.mak" CFG="layout - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "layout - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "layout - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "layout - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\include" /I "..\..\..\include\layout" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "UNICODE" /D _WIN32_WINNT=0X500 /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 ..\..\..\lib\icule.lib ..\..\..\lib\icuuc.lib ..\..\..\lib\icuin.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "layout - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\include" /I "..\..\..\include\layout" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "UNICODE" /D _WIN32_WINNT=0X500 /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\..\..\lib\iculed.lib ..\..\..\lib\icuucd.lib ..\..\..\lib\icuind.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "layout - Win32 Release" +# Name "layout - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\cmaps.cpp +# End Source File +# Begin Source File + +SOURCE=.\FontMap.cpp +# End Source File +# Begin Source File + +SOURCE=.\GDIFontInstance.cpp +# End Source File +# Begin Source File + +SOURCE=.\GDIFontMap.cpp +# End Source File +# Begin Source File + +SOURCE=.\GDIGUISupport.cpp +# End Source File +# Begin Source File + +SOURCE=.\layout.cpp +# End Source File +# Begin Source File + +SOURCE=.\paragraph.cpp +# End Source File +# Begin Source File + +SOURCE=.\RenderingFontInstance.cpp +# End Source File +# Begin Source File + +SOURCE=.\scrptrun.cpp +# End Source File +# Begin Source File + +SOURCE=.\UnicodeReader.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\cmaps.h +# End Source File +# Begin Source File + +SOURCE=.\FontMap.h +# End Source File +# Begin Source File + +SOURCE=.\GDIFontInstance.h +# End Source File +# Begin Source File + +SOURCE=.\GDIFontMap.h +# End Source File +# Begin Source File + +SOURCE=.\GDIGUISupport.h +# End Source File +# Begin Source File + +SOURCE=.\GUISupport.h +# End Source File +# Begin Source File + +SOURCE=.\paragraph.h +# End Source File +# Begin Source File + +SOURCE=.\RenderingFontInstance.h +# End Source File +# Begin Source File + +SOURCE=.\scrptrun.h +# End Source File +# Begin Source File + +SOURCE=.\sfnt.h +# End Source File +# Begin Source File + +SOURCE=.\UnicodeReader.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/icu4c/source/samples/layout/layout.dsw b/icu4c/source/samples/layout/layout.dsw new file mode 100644 index 00000000000..611d964b01f --- /dev/null +++ b/icu4c/source/samples/layout/layout.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "layout"=.\layout.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/icu4c/source/samples/layout/paragraph.cpp b/icu4c/source/samples/layout/paragraph.cpp new file mode 100644 index 00000000000..ad1881bc1bd --- /dev/null +++ b/icu4c/source/samples/layout/paragraph.cpp @@ -0,0 +1,481 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: Paragraph.cpp + * + * created on: 09/06/2000 + * created by: Eric R. Mader + */ + +#include "unicode/loengine.h" + +#include "RenderingFontInstance.h" + +#include "unicode/utypes.h" +#include "unicode/unicode.h" +#include "unicode/uchriter.h" +#include "unicode/brkiter.h" +#include "unicode/locid.h" + +#include "paragraph.h" +#include "scrptrun.h" +#include "UnicodeReader.h" +#include "FontMap.h" + +#define MARGIN 10 + +Paragraph::Paragraph(void *surface, RunParams params[], int32_t count) + : fRunCount(count), fRunInfo(NULL), fCharCount(0), fText(NULL), fGlyphCount(0), fGlyphs(NULL), + fCharIndices(NULL), fGlyphIndices(NULL), fDX(NULL), fBreakArray(NULL), fBreakCount(0), + fLineHeight(-1), fAscent(-1) +{ + int32_t i; + + fWidth = fHeight = 0; + + fRunInfo = new RunInfo[count + 1]; + + // Set charBase and rightToLeft for + // each run and count the total characters + for (i = 0; i < count; i += 1) { + fRunInfo[i].charBase = fCharCount; + fRunInfo[i].rightToLeft = params[i].rightToLeft; + fCharCount += params[i].count; + } + + // Set charBase and rightToLeft for the + // fake run at the end. + fRunInfo[count].charBase = fCharCount; + fRunInfo[count].rightToLeft = false; + + fBreakArray = new int32_t[fCharCount + 1]; + fText = new LEUnicode[fCharCount]; + + // Copy the text runs into a single array + for (i = 0; i < count; i += 1) { + int32_t charBase = fRunInfo[i].charBase; + int32_t charCount = fRunInfo[i + 1].charBase - charBase; + + LE_ARRAY_COPY(&fText[charBase], params[i].text, charCount); + } + + Locale thai("th"); + UCharCharacterIterator *iter = new UCharCharacterIterator(fText, fCharCount); + UErrorCode status = U_ZERO_ERROR; + Locale dummyLocale; + + fBrkiter = BreakIterator::createLineInstance(thai, status); + fBrkiter->adoptText(iter); + + ICULayoutEngine **engines = new ICULayoutEngine *[count]; + int32_t maxAscent = -1, maxDescent = -1, maxLeading = -1; + float x = 0, y = 0; + + // Layout each run, set glyphBase and glyphCount + // and count the total number of glyphs + for (i = 0; i < count; i += 1) { + int32_t charBase = fRunInfo[i].charBase; + int32_t charCount = fRunInfo[i + 1].charBase - charBase; + int32_t glyphCount = 0; + int32_t runAscent = 0, runDescent = 0, runLeading = 0; + UErrorCode success = U_ZERO_ERROR; + + fRunInfo[i].fontInstance = params[i].fontInstance; + + fRunInfo[i].fontInstance->setFont(surface); + + runAscent = fRunInfo[i].fontInstance->getAscent(); + runDescent = fRunInfo[i].fontInstance->getDescent(); + runLeading = fRunInfo[i].fontInstance->getLeading(); + + + if (runAscent > maxAscent) { + maxAscent = runAscent; + } + + if (runDescent > maxDescent) { + maxDescent = runDescent; + } + + if (runLeading > maxLeading) { + maxLeading = runLeading; + } + + engines[i] = ICULayoutEngine::createInstance(fRunInfo[i].fontInstance, params[i].scriptCode, dummyLocale, success); + + glyphCount = engines[i]->layoutChars(fText, charBase, charBase + charCount, fCharCount, + fRunInfo[i].rightToLeft, x, y, success); + + engines[i]->getGlyphPosition(glyphCount, x, y, success); + + fRunInfo[i].glyphBase = fGlyphCount; + fGlyphCount += glyphCount; + } + + fLineHeight = maxAscent + maxDescent + maxLeading; + fAscent = maxAscent; + + // Set glyphBase for the fake run at the end + fRunInfo[count].glyphBase = fGlyphCount; + + fGlyphs = new LEGlyphID[fGlyphCount]; + fCharIndices = new int32_t[fGlyphCount]; + fGlyphIndices = new int32_t[fCharCount + 1]; + fDX = new int32_t[fGlyphCount]; + fDY = new int32_t[fGlyphCount]; + + + float *positions = new float[fGlyphCount * 2 + 2]; + + // Build the glyph, charIndices and positions arrays + for (i = 0; i < count; i += 1) { + ICULayoutEngine *engine = engines[i]; + int32_t charBase = fRunInfo[i].charBase; + int32_t glyphBase = fRunInfo[i].glyphBase; + UErrorCode success = U_ZERO_ERROR; + + engine->getGlyphs(&fGlyphs[glyphBase], success); + engine->getCharIndices(&fCharIndices[glyphBase], charBase, success); + engine->getGlyphPositions(&positions[glyphBase * 2], success); + } + + // Filter deleted glyphs, compute logical advances + // and set the char to glyph map + for (i = 0; i < fGlyphCount; i += 1) { + // Filter deleted glyphs + if (fGlyphs[i] == 0xFFFE || fGlyphs[i] == 0xFFFF) { + fGlyphs[i] = 0x0001; + } + + // compute the logical advance + fDX[i] = (int32_t) (positions[i * 2 + 2] - positions[i * 2]); + + // save the Y offset + fDY[i] = (int32_t) positions[i * 2 + 1]; + + // set char to glyph map + fGlyphIndices[fCharIndices[i]] = i; + } + + if (fRunInfo[count - 1].rightToLeft) { + fGlyphIndices[fCharCount] = fRunInfo[count - 1].glyphBase - 1; + } else { + fGlyphIndices[fCharCount] = fGlyphCount; + } + + delete[] positions; + + // Get rid of the LayoutEngine's: + for (i = 0; i < count; i += 1) { + delete engines[i]; + } + + delete[] engines; +} + +Paragraph::~Paragraph() +{ + delete[] fDY; + delete[] fDX; + delete[] fGlyphIndices; + delete[] fCharIndices; + delete[] fGlyphs; + + delete fBrkiter; + delete fText; + + delete[] fBreakArray; + delete[] fRunInfo; +} + +int32_t Paragraph::getLineHeight() +{ + return fLineHeight; +} + +int32_t Paragraph::getLineCount() +{ + return fBreakCount; +} + +int32_t Paragraph::getAscent() +{ + return fAscent; +} + +int32_t Paragraph::previousBreak(int32_t charIndex) +{ + LEUnicode ch = fText[charIndex]; + + // skip over any whitespace or control + // characters, because they can hang in + // the margin. + while (charIndex < fCharCount && + (Unicode::isWhitespace(ch) || + Unicode::isControl(ch))) { + ch = fText[++charIndex]; + } + + // return the break location that's at or before + // the character we stopped on. Note: if we're + // on a break, the "+ 1" will cause preceding to + // back up to it. + return fBrkiter->preceding(charIndex + 1); +} + +void Paragraph::breakLines(int32_t width, int32_t height) +{ + int32_t lineWidth = width - (2 * MARGIN); + int32_t thisWidth = 0; + int32_t thisBreak = -1; + int32_t prevWidth = fWidth; + + fWidth = width; + fHeight = height; + + // don't re-break if the width hasn't changed + if (width == prevWidth) { + return; + } + + fBreakArray[0] = 0; + fBreakCount = 1; + + for (int32_t run = 0; run < fRunCount; run += 1) { + int32_t glyph = fRunInfo[run].glyphBase; + int32_t stop = fRunInfo[run + 1].glyphBase; + int32_t dir = 1; + + if (fRunInfo[run].rightToLeft) { + glyph = stop - 1; + stop = fRunInfo[run].glyphBase - 1; + dir = -1; + } + + while (glyph != stop) { + // Find the first glyph that doesn't fit on the line + while (thisWidth + fDX[glyph] <= lineWidth) { + thisWidth += fDX[glyph]; + glyph += dir; + + if (glyph == stop) { + break; + } + } + + // Check to see if we fell off the + // end of the run + if (glyph == stop) { + break; + } + + + // Find a place before here to break, + thisBreak = previousBreak(fCharIndices[glyph]); + + // If there wasn't one, force one + if (thisBreak <= fBreakArray[fBreakCount - 1]) { + thisBreak = fCharIndices[glyph]; + } + + // Save the break location. + fBreakArray[fBreakCount++] = thisBreak; + + // Reset the accumulated width + thisWidth = 0; + + // Map the character back to a glyph + glyph = fGlyphIndices[thisBreak]; + + // Check to see if the new glyph is off + // the end of the run. + if (glyph == stop) { + break; + } + + // If the glyph's not in the run we stopped in, we + // have to re-synch to the new run + if (glyph < fRunInfo[run].glyphBase || glyph >= fRunInfo[run + 1].glyphBase) { + run = getGlyphRun(glyph, 0, 1); + + if (fRunInfo[run].rightToLeft) { + stop = fRunInfo[run].glyphBase - 1; + dir = -1; + } else { + stop = fRunInfo[run + 1].glyphBase; + dir = 1; + } + } + } + } + + // Make sure the last break is after the last character + if (fBreakArray[--fBreakCount] != fCharCount) { + fBreakArray[++fBreakCount] = fCharCount; + } + + return; +} + +int32_t Paragraph::getGlyphRun(int32_t glyph, int32_t startingRun, int32_t direction) +{ + int32_t limit; + + if (direction < 0) { + limit = -1; + } else { + limit = fRunCount; + } + + for (int32_t run = startingRun; run != limit; run += direction) { + if (glyph >= fRunInfo[run].glyphBase && glyph < fRunInfo[run + 1].glyphBase) { + return run; + } + } + + return limit; +} + +int32_t Paragraph::getCharRun(int32_t ch, int32_t startingRun, int32_t direction) +{ + int32_t limit; + + if (direction < 0) { + limit = -1; + } else { + limit = fRunCount; + } + + for (int32_t run = startingRun; run != limit; run += direction) { + if (ch >= fRunInfo[run].charBase && ch < fRunInfo[run + 1].charBase) { + return run; + } + } + + return limit; +} + +int32_t Paragraph::getRunWidth(int32_t startGlyph, int32_t endGlyph) +{ + int32_t width = 0; + + for (int32_t glyph = startGlyph; glyph <= endGlyph; glyph += 1) { + width += fDX[glyph]; + } + + return width; +} + +int32_t Paragraph::drawRun(void *surface, const RenderingFontInstance *fontInstance, int32_t firstChar, int32_t lastChar, + int32_t x, int32_t y) +{ + int32_t firstGlyph = fGlyphIndices[firstChar]; + int32_t lastGlyph = fGlyphIndices[lastChar]; + + for (int32_t ch = firstChar; ch <= lastChar; ch += 1) { + int32_t glyph = fGlyphIndices[ch]; + + if (glyph < firstGlyph) { + firstGlyph = glyph; + } + + if (glyph > lastGlyph) { + lastGlyph = glyph; + } + } + + int32_t dyStart = firstGlyph, dyEnd = dyStart; + + fontInstance->setFont(surface); + + while (dyEnd <= lastGlyph) { + while (dyEnd <= lastGlyph && fDY[dyStart] == fDY[dyEnd]) { + dyEnd += 1; + } + + fontInstance->drawGlyphs(surface, &fGlyphs[dyStart], dyEnd - dyStart, + &fDX[dyStart], x, y + fDY[dyStart], fWidth, fHeight); + + dyStart = dyEnd; + } + + return getRunWidth(firstGlyph, lastGlyph); +} + +void Paragraph::draw(void *surface, int32_t firstLine, int32_t lastLine) +{ + int32_t line, x, y; + int32_t prevRun = 0; + + y = fAscent; + + for (line = firstLine; line <= lastLine; line += 1) { + int32_t firstChar = fBreakArray[line]; + int32_t lastChar = fBreakArray[line + 1] - 1; + int32_t firstRun = getCharRun(firstChar, prevRun, 1); + int32_t lastRun = getCharRun(lastChar, firstRun, 1); + + x = MARGIN; + + for (int32_t run = firstRun; run <= lastRun; run += 1) { + const RenderingFontInstance *fontInstance = fRunInfo[run].fontInstance; + int32_t nextBase; + + if (run == lastRun) { + nextBase = lastChar + 1; + } else { + nextBase = fRunInfo[run + 1].charBase; + } + + x += drawRun(surface, fontInstance, firstChar, nextBase - 1, x, y); + firstChar = nextBase; + } + + y += fLineHeight; + prevRun = lastRun; + } +} + +Paragraph *Paragraph::paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport, void *surface) +{ + RunParams params[64]; + int32_t paramCount = 0; + int32_t charCount = 0; + RFIErrorCode fontStatus = RFI_NO_ERROR; + const UChar *text = UnicodeReader::readFile(fileName, guiSupport, charCount); + ScriptRun scriptRun(text, charCount); + + if (text == NULL) { + return NULL; + } + + while (scriptRun.next()) { + int32_t start = scriptRun.getScriptStart(); + int32_t end = scriptRun.getScriptEnd(); + UScriptCode code = scriptRun.getScriptCode(); + + params[paramCount].text = &((UChar *) text)[start]; + params[paramCount].count = end - start; + params[paramCount].scriptCode = (UScriptCode) code; + params[paramCount].rightToLeft = false; + + params[paramCount].fontInstance = fontMap->getScriptFont(code, fontStatus); + + if (params[paramCount].fontInstance == NULL) { + return 0; + } + + if (code == USCRIPT_ARABIC) { + params[paramCount].rightToLeft = true; + } + + paramCount += 1; + } + + return new Paragraph(surface, params, paramCount); +} + diff --git a/icu4c/source/samples/layout/paragraph.h b/icu4c/source/samples/layout/paragraph.h new file mode 100644 index 00000000000..2d7373e19e9 --- /dev/null +++ b/icu4c/source/samples/layout/paragraph.h @@ -0,0 +1,96 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: Paragraph.h + * + * created on: 09/06/2000 + * created by: Eric R. Mader + */ +#ifndef __PARAGRAPH_H +#define __PARAGRAPH_H + +#include "unicode/utypes.h" +#include "unicode/uscript.h" +#include "unicode/brkiter.h" + +#include "GUISupport.h" +#include "RenderingFontInstance.h" +#include "FontMap.h" + +U_NAMESPACE_USE + +#define MARGIN 10 + +struct RunParams +{ + const RenderingFontInstance *fontInstance; + UChar *text; + int32_t count; + UScriptCode scriptCode; + UBool rightToLeft; +}; + +struct RunInfo +{ + const RenderingFontInstance *fontInstance; + int32_t charBase; + int32_t glyphBase; + UBool rightToLeft; +}; + +class Paragraph +{ +public: + Paragraph(void *surface,RunParams runs[], int32_t count); + + ~Paragraph(); + + int32_t getAscent(); + int32_t getLineHeight(); + int32_t getLineCount(); + void breakLines(int32_t width, int32_t height); + void draw(void *surface, int32_t firstLine, int32_t lastLine); + + static Paragraph *paragraphFactory(const char *fileName, FontMap *fontMap, GUISupport *guiSupport, void *surface); + +protected: + int32_t previousBreak(int32_t charIndex); + int32_t getCharRun(int32_t ch, int32_t startingRun, int32_t direction); + int32_t getGlyphRun(int32_t glyph, int32_t startingRun, int32_t direction); + + int32_t getRunWidth(int32_t startGlyph, int32_t endGlyph); + int32_t drawRun(void *surface, const RenderingFontInstance *fontInstance, int32_t firstChar, int32_t lastChar, + int32_t x, int32_t y); + + +private: + int32_t fRunCount; + RunInfo *fRunInfo; + + int32_t fCharCount; + UChar *fText; + BreakIterator *fBrkiter; + + int32_t fGlyphCount; + LEGlyphID *fGlyphs; + int32_t *fCharIndices; + int32_t *fGlyphIndices; + int32_t *fDX; + int32_t *fDY; + + int32_t fBreakCount; + int32_t *fBreakArray; + + int32_t fLineHeight; + int32_t fAscent; + int32_t fWidth; + int32_t fHeight; +}; + +#endif + + diff --git a/icu4c/source/samples/layout/readme.html b/icu4c/source/samples/layout/readme.html new file mode 100644 index 00000000000..dedbdc270ff --- /dev/null +++ b/icu4c/source/samples/layout/readme.html @@ -0,0 +1,123 @@ + + + + + + + Readme file for letest and gendata + + + +

+What is the layout demo?

+The layout demo displays a paragraph of text that is laid out using the +LayoutEngine. There are two versions of this demo, "layout.exe" which runs +on Windows 2000, and "gnomelayout" which runs on Linux. Both programs read +a file containing the Unicode text to display, and a file that says which +font to use to display each script. +
  +

+How do I build the layout demo?

+First, you need to build ICU, including the LayoutEngine. +

On Windows, the layout project should be listed as a dependency of all, +so layout will build when you build all. If it doesn't for some reason, +just select the layout project in the project toolbar and build it. +

On Linux systems, you need to add the "--enable-layout=yes" option when +you invoke the runConfigureICU script. When you've done that, layout should +build when you do "make all install" +

To build the demo on Windows, just open the layout project in <icu>\source\samples\layout +and build it. On Linux systems, connect to <top-build-dir>/samples/layout +and do "make all" +
  +

+How do I run the demo?

+Before you can run the demo, you'll need to get the fonts it uses. For +legal reasons, we can't include these fonts with ICU, but you can get them +for free from the web. To do this, you'll need access to a computer running +Windows. Here's how to get the fonts: +

Download the 1.3 version of the JDK from the IBM +WebSphere preview technologies page. From this page, follow the "Download" +link on the right had side. You'll need to register with them if you haven't +downloaded before. Download and install the "Runtime Environment Package." +You'll need three fonts from this package. If you've let the installer +use it's defaults, the fonts will be in C:\Program Files\IBM\Java13\jre\lib\fonts. +The files you want are "Devamt.ttf" "LucidaSansRegular.ttf" and "Thonburi.ttf" +On Windows, copy these font files to your Fonts folder, on LInux, copy +these font files to the directory from which you'll run the layout demo. +

There's still one more font to get. Go to the Microsoft TrueType +core fonts for the Web page and download the "Times New Roman" font. +This will download an installer program, called "Times32.exe" which will +install the Times New Roman fonts in your fonts folder. (If you've already +got these fonts in you fonts folder, you may want to move them to another +folder before you install these fonts.) +

NOTE: this installer will display an End User License Agreement (EULA) +which you must accept before proceeding. Be sure that you read and understand +this agreement before you install the font. +

After you run the installer program, it will add the Times Roman fonts +to your fonts folder. If you're going to run the demo on Linux, open the +Fonts folder and copy the "Times New Roman" font (the file name will be +"Times.TTF") to the directory from which you'll run the demo. +

That's it! Now all you have to do is run letest (CTRL+F5 in Visual C++, +or "./gnomelayout" in Linux) +

+How can I customize the layout demo?

+The text that the layout demo displays is read from the file "Sample.utf8." +You can change the text by editing this file using a Unicode-aware text +editor. (it is in UTF8 format with a BOM as the first character; the demo +can also read UTF16 and UTF32 format files) Remember that the text will +be displayed in a single paragraph; you can include CR and LF characters +in the text, but they will be ignored. +

If you add scripts to the text other than Arabic, Devanagari, Latin +or Thai, you'll need to find a font which contains the characters in that +script, and add an entry to the FontMap file ("FontMap.GDI" on Windows, +"FontMap.Gnome" on Linux) This file contains a single entry per line. Each +entry contains a script name followed by a colon, and then a font name. +

Here is the list of legal script names: +

ARABIC +
ARMENIAN +
BENGALI +
BOPOMOFO +
CANADIAN-ABORIGINAL +
CHEROKEE +
CYRILLIC +
DESERET +
DEVANAGARI +
ETHIOPIC +
GEORGIAN +
GOTHIC +
GREEK +
GUJARATI +
GURMUKHI +
HAN +
HANGUL +
HEBREW +
HIRAGANA +
KANNADA +
KATAKANA +
KHMER +
LATIN +
MALAYALAM +
MONGOLIAN +
MYANMAR +
OGHAM +
OLD-ITALIC +
ORIYA +
RUNIC +
SINHALA +
SYRIAC +
TAMIL +
TELUGU +
THAANA +
THAI +
TIBETAN +
UCAS +
YI
+On Windows use the full name of the font as it appears in the Windows Fonts +folder (eg. "Times New Roman") On Linux, use the file name of the font +file (e.g. "Times.TTF") If you're running on Windows, you'll need to install +the new fonts in your Fonts folder. If you're running on Linux, put them +in the directory from which you'll run the demo. +
  +
  + + diff --git a/icu4c/source/samples/layout/scrptrun.cpp b/icu4c/source/samples/layout/scrptrun.cpp new file mode 100644 index 00000000000..2fe6f19dbc6 --- /dev/null +++ b/icu4c/source/samples/layout/scrptrun.cpp @@ -0,0 +1,67 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: scrptrun.cpp + * + * created on: 10/17/2001 + * created by: Eric R. Mader + */ + +#include "unicode/utypes.h" +#include "unicode/uscript.h" + +#include "scrptrun.h" + +UBool ScriptRun::sameScript(int32_t scriptOne, int32_t scriptTwo) +{ + return scriptOne <= USCRIPT_INHERITED || scriptTwo <= USCRIPT_INHERITED || scriptOne == scriptTwo; +} + +UBool ScriptRun::next() +{ + UErrorCode error = U_ZERO_ERROR; + + if (scriptEnd >= charLimit) { + return false; + } + + scriptCode = USCRIPT_COMMON; + + for (scriptStart = scriptEnd; scriptEnd < charLimit; scriptEnd += 1) { + UChar high = charArray[scriptEnd]; + UChar32 ch = high; + + if (scriptEnd < charLimit - 1 && high >= 0xD800 && high <= 0xDBFF) + { + UChar low = charArray[scriptEnd + 1]; + + if (low >= 0xDC00 && low <= 0xDFFF) { + ch = (high - 0xD800) * 0x0400 + low - 0xDC00 + 0x10000; + scriptEnd += 1; + } + } + + UScriptCode sc = uscript_getScript(ch, &error); + + if (sameScript(scriptCode, sc)) { + if (scriptCode <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) { + scriptCode = sc; + } + } else { + // if the run broke on a surrogate pair, + // end it before the high surrogate + if (ch >= 0x10000) { + scriptEnd -= 1; + } + + break; + } + } + + return true; +} + diff --git a/icu4c/source/samples/layout/scrptrun.h b/icu4c/source/samples/layout/scrptrun.h new file mode 100644 index 00000000000..86dfab0653d --- /dev/null +++ b/icu4c/source/samples/layout/scrptrun.h @@ -0,0 +1,116 @@ +/* + ******************************************************************************* + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: scrptrun.h + * + * created on: 10/17/2001 + * created by: Eric R. Mader + */ + +#ifndef __SCRPTRUN_H +#define __SCRPTRUN_H + +#include "unicode/utypes.h" +#include "unicode/uscript.h" + +struct ScriptRecord +{ + UChar32 startChar; + UChar32 endChar; + UScriptCode scriptCode; +}; + +class ScriptRun +{ +public: + ScriptRun(); + + ScriptRun(const UChar chars[], int32_t length); + + ScriptRun(const UChar chars[], int32_t start, int32_t length); + + void reset(); + + void reset(int32_t start, int32_t count); + + void reset(const UChar chars[], int32_t start, int32_t length); + + int32_t getScriptStart(); + + int32_t getScriptEnd(); + + UScriptCode getScriptCode(); + + UBool next(); + +private: + + static UBool sameScript(int32_t scriptOne, int32_t scriptTwo); + + int32_t charStart; + int32_t charLimit; + const UChar *charArray; + + int32_t scriptStart; + int32_t scriptEnd; + UScriptCode scriptCode; +}; + +inline ScriptRun::ScriptRun() +{ + reset(NULL, 0, 0); +} + +inline ScriptRun::ScriptRun(const UChar chars[], int32_t length) +{ + reset(chars, 0, length); +} + +inline ScriptRun::ScriptRun(const UChar chars[], int32_t start, int32_t length) +{ + reset(chars, start, length); +} + +inline int32_t ScriptRun::getScriptStart() +{ + return scriptStart; +} + +inline int32_t ScriptRun::getScriptEnd() +{ + return scriptEnd; +} + +inline UScriptCode ScriptRun::getScriptCode() +{ + return scriptCode; +} + +inline void ScriptRun::reset() +{ + scriptStart = charStart; + scriptEnd = charStart; + scriptCode = USCRIPT_INVALID_CODE; +} + +inline void ScriptRun::reset(int32_t start, int32_t length) +{ + charStart = start; + charLimit = start + length; + + reset(); +} + +inline void ScriptRun::reset(const UChar chars[], int32_t start, int32_t length) +{ + charArray = chars; + + reset(start, length); +} + + +#endif diff --git a/icu4c/source/samples/layout/sfnt.h b/icu4c/source/samples/layout/sfnt.h new file mode 100644 index 00000000000..3b3d991adcc --- /dev/null +++ b/icu4c/source/samples/layout/sfnt.h @@ -0,0 +1,219 @@ +/* + ****************************************************************************** * + * + * Copyright (C) 1999-2001, International Business Machines + * Corporation and others. All Rights Reserved. + * + ****************************************************************************** * + * file name: sfnt.h + * + * created on: ??/??/2001 + * created by: Eric R. Mader + */ + +#ifndef __SFNT_H +#define __SFNT_H + +#include "LETypes.h" + + +#ifndef ANY_NUMBER +#define ANY_NUMBER 1 +#endif + +struct DirectoryEntry +{ + le_uint32 tag; + le_uint32 checksum; + le_uint32 offset; + le_uint32 length; +}; + +struct SFNTDirectory +{ + le_uint32 scalerType; + le_uint16 numTables; + le_uint16 searchRange; + le_uint16 entrySelector; + le_uint16 rangeShift; + DirectoryEntry tableDirectory[ANY_NUMBER]; +}; + + +struct CMAPEncodingSubtableHeader +{ + le_uint16 platformID; + le_uint16 platformSpecificID; + le_uint32 encodingOffset; +}; + +struct CMAPTable +{ + le_uint16 version; + le_uint16 numberSubtables; + CMAPEncodingSubtableHeader encodingSubtableHeaders[ANY_NUMBER]; +}; + +struct CMAPEncodingSubtable +{ + le_uint16 format; + le_uint16 length; + le_uint16 language; +}; + +struct CMAPFormat0Encoding : CMAPEncodingSubtable +{ + le_uint8 glyphIndexArray[256]; +}; + +struct CMAPFormat2Subheader +{ + le_uint16 firstCode; + le_uint16 entryCount; + le_int16 idDelta; + le_uint16 idRangeOffset; +}; + +struct CMAPFormat2Encoding : CMAPEncodingSubtable +{ + le_uint16 subHeadKeys[256]; + CMAPFormat2Subheader subheaders[ANY_NUMBER]; +}; + +struct CMAPFormat4Encoding : CMAPEncodingSubtable +{ + le_uint16 segCountX2; + le_uint16 searchRange; + le_uint16 entrySelector; + le_uint16 rangeShift; + le_uint16 endCodes[ANY_NUMBER]; +// le_uint16 reservedPad; +// le_uint16 startCodes[ANY_NUMBER]; +// le_uint16 idDelta[ANY_NUMBER]; +// le_uint16 idRangeOffset[ANY_NUMBER]; +// le_uint16 glyphIndexArray[ANY_NUMBER]; +}; + +struct CMAPFormat6Encoding : CMAPEncodingSubtable +{ + le_uint16 firstCode; + le_uint16 entryCount; + le_uint16 glyphIndexArray[ANY_NUMBER]; +}; + +struct CMAPEncodingSubtable32 +{ + le_uint32 format; + le_uint32 length; + le_uint32 language; +}; + +struct CMAPGroup +{ + le_uint32 startCharCode; + le_uint32 endCharCode; + le_uint32 startGlyphCode; +}; + +struct CMAPFormat8Encoding : CMAPEncodingSubtable32 +{ + le_uint32 is32[65536/32]; + le_uint32 nGroups; + CMAPGroup groups[ANY_NUMBER]; +}; + +struct CMAPFormat10Encoding : CMAPEncodingSubtable32 +{ + le_uint32 startCharCode; + le_uint32 numCharCodes; + le_uint16 glyphs[ANY_NUMBER]; +}; + +struct CMAPFormat12Encoding : CMAPEncodingSubtable32 +{ + le_uint32 nGroups; + CMAPGroup groups[ANY_NUMBER]; +}; + +typedef le_int32 fixed; + +struct BigDate +{ + le_uint32 bc; + le_uint32 ad; +}; + +struct HEADTable +{ + fixed version; + fixed fontRevision; + le_uint32 checksumAdjustment; + le_uint32 magicNumber; + le_uint16 flags; + le_uint16 unitsPerEm; + BigDate created; + BigDate modified; + le_int16 xMin; + le_int16 yMin; + le_int16 xMax; + le_int16 yMax; + le_int16 lowestRecPPEM; + le_int16 fontDirectionHint; + le_int16 indexToLocFormat; + le_int16 glyphDataFormat; +}; + +struct MAXPTable +{ + fixed version; + le_uint16 numGlyphs; + le_uint16 maxPoints; + le_uint16 maxContours; + le_uint16 maxComponentPoints; + le_uint16 maxComponentContours; + le_uint16 maxZones; + le_uint16 maxTwilightPoints; + le_uint16 maxStorage; + le_uint16 maxFunctionDefs; + le_uint16 maxInstructionDefs; + le_uint16 maxStackElements; + le_uint16 maxSizeOfInstructions; + le_uint16 maxComponentElements; + le_uint16 maxComponentDepth; +}; + +struct HHEATable +{ + fixed version; + le_int16 ascent; + le_int16 descent; + le_int16 lineGap; + le_uint16 advanceWidthMax; + le_int16 minLeftSideBearing; + le_int16 minRightSideBearing; + le_int16 xMaxExtent; + le_int16 caretSlopeRise; + le_int16 caretSlopeRun; + le_int16 caretOffset; + le_int16 reserved1; + le_int16 reserved2; + le_int16 reserved3; + le_int16 reserved4; + le_int16 metricDataFormat; + le_uint16 numOfLongHorMetrics; +}; + +struct LongHorMetric +{ + le_uint16 advanceWidth; + le_int16 leftSideBearing; +}; + +struct HMTXTable +{ + LongHorMetric hMetrics[ANY_NUMBER]; // ANY_NUMBER = numOfLongHorMetrics from hhea table +// le_int16 leftSideBearing[ANY_NUMBER]; // ANY_NUMBER = numGlyphs - numOfLongHorMetrics +}; + +#endif +