1933 lines
65 KiB
Diff
1933 lines
65 KiB
Diff
|
--- mozilla.back/gfx/src/gtk/nsFontMetricsPango.cpp.orig 2007-06-28 14:44:31.000000000 +0200
|
||
|
+++ mozilla.back/gfx/src/gtk/nsFontMetricsPango.cpp 2007-06-28 15:48:04.000000000 +0200
|
||
|
@@ -21,6 +21,8 @@
|
||
|
* are Copyright (C) 2004 the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
+ * Christopher Blizzard <blizzard@mozilla.org>
|
||
|
+ * Behdad Esfahbod <behdad@behdad.org>
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
@@ -36,6 +38,10 @@
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
+#define PANGO_ENABLE_BACKEND
|
||
|
+
|
||
|
+#include "nsFontMetricsPango.h"
|
||
|
+
|
||
|
#include <strings.h>
|
||
|
#include "nsFont.h"
|
||
|
#include "nsIDeviceContext.h"
|
||
|
@@ -43,27 +49,37 @@
|
||
|
#include "nsIPref.h"
|
||
|
#include "nsServiceManagerUtils.h"
|
||
|
|
||
|
-#define PANGO_ENABLE_BACKEND
|
||
|
-#define PANGO_ENABLE_ENGINE
|
||
|
-
|
||
|
-#include "nsFontMetricsPango.h"
|
||
|
-#include "nsRenderingContextGTK.h"
|
||
|
-#include "nsDeviceContextGTK.h"
|
||
|
#include "nsFontConfigUtils.h"
|
||
|
|
||
|
#include "nsUnicharUtils.h"
|
||
|
#include "nsQuickSort.h"
|
||
|
#include "nsFontConfigUtils.h"
|
||
|
+#include "mozilla-decoder.h"
|
||
|
+
|
||
|
+#define FORCE_PR_LOG
|
||
|
+#include "prlog.h"
|
||
|
+
|
||
|
|
||
|
#include <fontconfig/fontconfig.h>
|
||
|
+#include <freetype/tttables.h>
|
||
|
+
|
||
|
+#include <pango/pango.h>
|
||
|
+#include <pango/pangofc-font.h>
|
||
|
+
|
||
|
+#ifdef PSPANGO
|
||
|
+#include <pango/pangoft2.h>
|
||
|
+#include "nsRenderingContextPS.h"
|
||
|
+#include "nsDeviceContextPS.h"
|
||
|
+#include "nsType1.h"
|
||
|
+#else
|
||
|
#include <gdk/gdk.h>
|
||
|
#include <gdk/gdkx.h>
|
||
|
-#include <freetype/tttables.h>
|
||
|
+#include "nsRenderingContextGTK.h"
|
||
|
+#include "nsDeviceContextGTK.h"
|
||
|
+#endif
|
||
|
+
|
||
|
|
||
|
-#include "mozilla-decoder.h"
|
||
|
|
||
|
-#define FORCE_PR_LOG
|
||
|
-#include "prlog.h"
|
||
|
|
||
|
// Globals
|
||
|
|
||
|
@@ -108,6 +124,49 @@ static nsresult EnumFontsPango (nsI
|
||
|
PRUint32* aCount, PRUnichar*** aResult);
|
||
|
static int CompareFontNames (const void* aArg1, const void* aArg2,
|
||
|
void* aClosure);
|
||
|
+static void utf16_to_utf8 (const PRUnichar* aString, PRUint32 aLength,
|
||
|
+ char *&text, gint &text_len);
|
||
|
+
|
||
|
+#ifdef PSPANGO
|
||
|
+static void
|
||
|
+default_substitute (FcPattern *pattern,
|
||
|
+ gpointer data)
|
||
|
+{
|
||
|
+ FcPatternDel (pattern, FC_HINTING);
|
||
|
+ FcPatternAddBool (pattern, FC_HINTING, 0);
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
+static PangoFontMap *
|
||
|
+get_fontmap (void)
|
||
|
+{
|
||
|
+ static PangoFontMap *fontmap = NULL;
|
||
|
+
|
||
|
+ if (!fontmap) {
|
||
|
+#ifdef PSPANGO
|
||
|
+ fontmap = pango_ft2_font_map_new ();
|
||
|
+ pango_ft2_font_map_set_resolution ((PangoFT2FontMap *)fontmap, 72., 72.);
|
||
|
+ pango_ft2_font_map_set_default_substitute ((PangoFT2FontMap *)fontmap, default_substitute, NULL, NULL);
|
||
|
+#else
|
||
|
+ PangoContext* context = gdk_pango_context_get ();
|
||
|
+ fontmap = pango_context_get_font_map (context);
|
||
|
+ g_object_unref (context);
|
||
|
+#endif
|
||
|
+ }
|
||
|
+
|
||
|
+ return fontmap;
|
||
|
+}
|
||
|
+
|
||
|
+static PangoContext *
|
||
|
+get_context (void)
|
||
|
+{
|
||
|
+#ifdef PSPANGO
|
||
|
+ return pango_ft2_font_map_create_context ((PangoFT2FontMap *) get_fontmap ());
|
||
|
+#else
|
||
|
+ return gdk_pango_context_get();
|
||
|
+#endif
|
||
|
+}
|
||
|
+
|
||
|
|
||
|
nsFontMetricsPango::nsFontMetricsPango()
|
||
|
{
|
||
|
@@ -169,14 +228,20 @@ nsFontMetricsPango::Init(const nsFont& a
|
||
|
mLangGroup = aLangGroup;
|
||
|
|
||
|
// Hang on to the device context
|
||
|
+#ifdef PSPANGO
|
||
|
+ mDeviceContext = (nsDeviceContextPS *)aContext;
|
||
|
+#else
|
||
|
mDeviceContext = aContext;
|
||
|
+#endif
|
||
|
|
||
|
mPointSize = NSTwipsToFloatPoints(mFont.size);
|
||
|
|
||
|
+#ifndef PSPANGO
|
||
|
// Make sure to clamp the pixel size to something reasonable so we
|
||
|
// don't make the X server blow up.
|
||
|
nscoord screenPixels = gdk_screen_height();
|
||
|
mPointSize = PR_MIN(screenPixels * FONT_MAX_FONT_SCALE, mPointSize);
|
||
|
+#endif
|
||
|
|
||
|
// enumerate over the font names passed in
|
||
|
mFont.EnumerateFamilies(nsFontMetricsPango::EnumFontCallback, this);
|
||
|
@@ -329,7 +394,7 @@ nsFontMetricsPango::CacheFontMetrics(voi
|
||
|
|
||
|
// mPangoSpaceWidth
|
||
|
PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
- pango_layout_set_text(layout, " ", 1);
|
||
|
+ pango_layout_set_text(layout, " ", -1);
|
||
|
int pswidth, psheight;
|
||
|
pango_layout_get_size(layout, &pswidth, &psheight);
|
||
|
mPangoSpaceWidth = pswidth;
|
||
|
@@ -337,14 +402,14 @@ nsFontMetricsPango::CacheFontMetrics(voi
|
||
|
|
||
|
// mSpaceWidth (width of a space)
|
||
|
nscoord tmpWidth;
|
||
|
- GetWidth(" ", 1, tmpWidth, NULL);
|
||
|
+ GetWidth(" ", 1, tmpWidth CONTEXT_ARG_NULL);
|
||
|
mSpaceWidth = tmpWidth;
|
||
|
|
||
|
// mAveCharWidth (width of an 'average' char)
|
||
|
// XftTextExtents16(GDK_DISPLAY(), xftFont, &xUnichar, 1, &extents);
|
||
|
//rawWidth = extents.width;
|
||
|
//mAveCharWidth = NSToCoordRound(rawWidth * f);
|
||
|
- GetWidth("x", 1, tmpWidth, NULL);
|
||
|
+ GetWidth("x", 1, tmpWidth CONTEXT_ARG_NULL);
|
||
|
mAveCharWidth = tmpWidth;
|
||
|
|
||
|
// mXHeight (height of an 'x' character)
|
||
|
@@ -460,130 +525,96 @@ nsFontMetricsPango::GetFontHandle(nsFont
|
||
|
|
||
|
// nsIFontMetricsPango impl
|
||
|
|
||
|
-nsresult
|
||
|
-nsFontMetricsPango::GetWidth(const char* aString, PRUint32 aLength,
|
||
|
- nscoord& aWidth,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+#ifdef PSPANGO
|
||
|
+NS_IMETHODIMP
|
||
|
+nsFontMetricsPSPango::GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength)
|
||
|
{
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
-
|
||
|
- pango_layout_set_text(layout, aString, aLength);
|
||
|
+ return GetWidth (String, (PRUint32) aLength, aWidth CONTEXT_ARG_NULL);
|
||
|
+}
|
||
|
|
||
|
- if (mPangoSpaceWidth)
|
||
|
- FixupSpaceWidths(layout, aString);
|
||
|
+NS_IMETHODIMP
|
||
|
+nsFontMetricsPSPango::GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength)
|
||
|
+{
|
||
|
+ return GetWidth (aString, (PRUint32)aLength, aWidth, NULL CONTEXT_ARG_NULL);
|
||
|
+}
|
||
|
+#endif
|
||
|
|
||
|
+nsresult
|
||
|
+nsFontMetricsPango::GetWidth(const char* aString, PRUint32 aLength,
|
||
|
+ nscoord& aWidth
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
+{
|
||
|
int width, height;
|
||
|
-
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
pango_layout_get_size(layout, &width, &height);
|
||
|
-
|
||
|
g_object_unref(layout);
|
||
|
|
||
|
- float f;
|
||
|
- f = mDeviceContext->DevUnitsToAppUnits();
|
||
|
+ float f = mDeviceContext->DevUnitsToAppUnits();
|
||
|
aWidth = NSToCoordRound(width * f / PANGO_SCALE);
|
||
|
|
||
|
- // printf("GetWidth (char *) %d\n", aWidth);
|
||
|
-
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsFontMetricsPango::GetWidth(const PRUnichar* aString, PRUint32 aLength,
|
||
|
- nscoord& aWidth, PRInt32 *aFontID,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+ nscoord& aWidth, PRInt32 *aFontID
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
-
|
||
|
- gchar *text = g_utf16_to_utf8(aString, aLength,
|
||
|
- NULL, NULL, NULL);
|
||
|
-
|
||
|
- if (!text) {
|
||
|
- aWidth = 0;
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aString, aLength)
|
||
|
-#endif
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
-
|
||
|
gint width, height;
|
||
|
-
|
||
|
- pango_layout_set_text(layout, text, strlen(text));
|
||
|
- FixupSpaceWidths(layout, text);
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
pango_layout_get_size(layout, &width, &height);
|
||
|
+ g_object_unref(layout);
|
||
|
|
||
|
- float f;
|
||
|
- f = mDeviceContext->DevUnitsToAppUnits();
|
||
|
+ float f = mDeviceContext->DevUnitsToAppUnits();
|
||
|
aWidth = NSToCoordRound(width * f / PANGO_SCALE);
|
||
|
|
||
|
- // printf("GetWidth %d\n", aWidth);
|
||
|
-
|
||
|
- loser:
|
||
|
- g_free(text);
|
||
|
- g_object_unref(layout);
|
||
|
-
|
||
|
- return rv;
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
nsresult
|
||
|
-nsFontMetricsPango::GetTextDimensions(const PRUnichar* aString,
|
||
|
+nsFontMetricsPango::GetTextDimensions(const char* aString,
|
||
|
PRUint32 aLength,
|
||
|
- nsTextDimensions& aDimensions,
|
||
|
- PRInt32* aFontID,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+ nsTextDimensions& aDimensions
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
-
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
+ PangoLayoutLine *line = pango_layout_get_line(layout, 0);
|
||
|
|
||
|
- gchar *text = g_utf16_to_utf8(aString, aLength,
|
||
|
- NULL, NULL, NULL);
|
||
|
-
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetTextDimensions invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aString, aLength)
|
||
|
-#endif
|
||
|
- aDimensions.width = 0;
|
||
|
- aDimensions.ascent = 0;
|
||
|
- aDimensions.descent = 0;
|
||
|
-
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
-
|
||
|
+ PangoRectangle logical;
|
||
|
+ pango_layout_line_get_extents(line, NULL, &logical);
|
||
|
+ g_object_unref(layout);
|
||
|
|
||
|
- pango_layout_set_text(layout, text, strlen(text));
|
||
|
- FixupSpaceWidths(layout, text);
|
||
|
+ float P2T = mDeviceContext->DevUnitsToAppUnits();
|
||
|
|
||
|
- // Get the logical extents
|
||
|
- PangoLayoutLine *line;
|
||
|
- if (pango_layout_get_line_count(layout) != 1) {
|
||
|
- printf("Warning: more than one line!\n");
|
||
|
- }
|
||
|
- line = pango_layout_get_line(layout, 0);
|
||
|
+ aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(logical) * P2T / PANGO_SCALE);
|
||
|
+ aDimensions.descent = NSToCoordRound(PANGO_DESCENT(logical) * P2T / PANGO_SCALE);
|
||
|
+ aDimensions.width = NSToCoordRound(logical.width * P2T / PANGO_SCALE);
|
||
|
|
||
|
- PangoRectangle rect;
|
||
|
- pango_layout_line_get_extents(line, NULL, &rect);
|
||
|
+ return NS_OK;
|
||
|
+}
|
||
|
|
||
|
- float P2T;
|
||
|
- P2T = mDeviceContext->DevUnitsToAppUnits();
|
||
|
+nsresult
|
||
|
+nsFontMetricsPango::GetTextDimensions(const PRUnichar* aString,
|
||
|
+ PRUint32 aLength,
|
||
|
+ nsTextDimensions& aDimensions,
|
||
|
+ PRInt32* aFontID
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
+{
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
+ PangoLayoutLine *line = pango_layout_get_line(layout, 0);
|
||
|
|
||
|
- aDimensions.width = NSToCoordRound(rect.width * P2T / PANGO_SCALE);
|
||
|
- aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(rect) * P2T / PANGO_SCALE);
|
||
|
- aDimensions.descent = NSToCoordRound(PANGO_DESCENT(rect) * P2T / PANGO_SCALE);
|
||
|
+ PangoRectangle logical;
|
||
|
+ pango_layout_line_get_extents(line, NULL, &logical);
|
||
|
+ g_object_unref(layout);
|
||
|
|
||
|
- // printf("GetTextDimensions %d %d %d\n", aDimensions.width,
|
||
|
- //aDimensions.ascent, aDimensions.descent);
|
||
|
+ float P2T = mDeviceContext->DevUnitsToAppUnits();
|
||
|
|
||
|
- loser:
|
||
|
- g_free(text);
|
||
|
- g_object_unref(layout);
|
||
|
+ aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(logical) * P2T / PANGO_SCALE);
|
||
|
+ aDimensions.descent = NSToCoordRound(PANGO_DESCENT(logical) * P2T / PANGO_SCALE);
|
||
|
+ aDimensions.width = NSToCoordRound(logical.width * P2T / PANGO_SCALE);
|
||
|
|
||
|
- return rv;
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
@@ -595,13 +626,13 @@ nsFontMetricsPango::GetTextDimensions(co
|
||
|
nsTextDimensions& aDimensions,
|
||
|
PRInt32& aNumCharsFit,
|
||
|
nsTextDimensions& aLastWordDimensions,
|
||
|
- PRInt32* aFontID,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+ PRInt32* aFontID
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
{
|
||
|
|
||
|
return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks,
|
||
|
aNumBreaks, aDimensions, aNumCharsFit,
|
||
|
- aLastWordDimensions, aContext);
|
||
|
+ aLastWordDimensions CONTEXT_ARG_PASS);
|
||
|
|
||
|
}
|
||
|
|
||
|
@@ -614,8 +645,8 @@ nsFontMetricsPango::GetTextDimensions(co
|
||
|
nsTextDimensions& aDimensions,
|
||
|
PRInt32& aNumCharsFit,
|
||
|
nsTextDimensions& aLastWordDimensions,
|
||
|
- PRInt32* aFontID,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+ PRInt32* aFontID
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
{
|
||
|
nsresult rv = NS_OK;
|
||
|
PRInt32 curBreak = 0;
|
||
|
@@ -623,23 +654,15 @@ nsFontMetricsPango::GetTextDimensions(co
|
||
|
|
||
|
PRInt32 *utf8Breaks = new PRInt32[aNumBreaks];
|
||
|
|
||
|
- gchar *text = g_utf16_to_utf8(aString, (PRInt32)aLength,
|
||
|
- NULL, NULL, NULL);
|
||
|
+ gchar* text;
|
||
|
+ gint text_len;
|
||
|
+ utf16_to_utf8 (aString, aLength, text, text_len);
|
||
|
|
||
|
curChar = text;
|
||
|
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aString, (PRUint32)aLength)
|
||
|
-#endif
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
-
|
||
|
// Covert the utf16 break offsets to utf8 break offsets
|
||
|
for (PRInt32 curOffset=0; curOffset < aLength;
|
||
|
- curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) {
|
||
|
+ curOffset++, curChar = g_utf8_next_char(curChar)) {
|
||
|
if (aBreaks[curBreak] == curOffset) {
|
||
|
utf8Breaks[curBreak] = curChar - text;
|
||
|
curBreak++;
|
||
|
@@ -653,10 +676,10 @@ nsFontMetricsPango::GetTextDimensions(co
|
||
|
utf8Breaks[curBreak] = curChar - text;
|
||
|
|
||
|
#if 0
|
||
|
- if (strlen(text) != aLength) {
|
||
|
- printf("Different lengths for utf16 %d and utf8 %d\n", aLength, strlen(text));
|
||
|
+ if (text_len != aLength) {
|
||
|
+ printf("Different lengths for utf16 %d and utf8 %d\n", aLength, text_len);
|
||
|
DUMP_PRUNICHAR(aString, aLength)
|
||
|
- DUMP_PRUNICHAR(text, strlen(text))
|
||
|
+ DUMP_PRUNICHAR(text, text_len)
|
||
|
for (PRInt32 i = 0; i < aNumBreaks; ++i) {
|
||
|
printf(" break %d utf16 %d utf8 %d\n", i, aBreaks[i], utf8Breaks[i]);
|
||
|
}
|
||
|
@@ -666,9 +689,9 @@ nsFontMetricsPango::GetTextDimensions(co
|
||
|
// We'll use curBreak to indicate which of the breaks end up being
|
||
|
// used for the break point for this line.
|
||
|
curBreak = 0;
|
||
|
- rv = GetTextDimensionsInternal(text, strlen(text), aAvailWidth, utf8Breaks,
|
||
|
+ rv = GetTextDimensionsInternal(text, text_len, aAvailWidth, utf8Breaks,
|
||
|
aNumBreaks, aDimensions, aNumCharsFit,
|
||
|
- aLastWordDimensions, aContext);
|
||
|
+ aLastWordDimensions CONTEXT_ARG_PASS);
|
||
|
|
||
|
// Figure out which of the breaks we ended up using to convert
|
||
|
// back to utf16 - start from the end.
|
||
|
@@ -681,200 +704,365 @@ nsFontMetricsPango::GetTextDimensions(co
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- loser:
|
||
|
- if (text)
|
||
|
- g_free(text);
|
||
|
+ g_free(text);
|
||
|
|
||
|
delete[] utf8Breaks;
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
-nsresult
|
||
|
-nsFontMetricsPango::DrawString(const char *aString, PRUint32 aLength,
|
||
|
- nscoord aX, nscoord aY,
|
||
|
- const nscoord* aSpacing,
|
||
|
- nsRenderingContextGTK *aContext,
|
||
|
- nsDrawingSurfaceGTK *aSurface)
|
||
|
+#ifdef PSPANGO
|
||
|
+
|
||
|
+typedef struct _nsPSPangoRenderer nsPSPangoRenderer;
|
||
|
+typedef struct _nsPSPangoRendererClass nsPSPangoRendererClass;
|
||
|
+
|
||
|
+struct _nsPSPangoRenderer
|
||
|
{
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
+ PangoRenderer parent_instance;
|
||
|
+ nsRenderingContextPS *psContext;
|
||
|
+ nsFontMetricsPSPango *psPangoFontMetrics;
|
||
|
+ float zoom;
|
||
|
+};
|
||
|
|
||
|
- pango_layout_set_text(layout, aString, aLength);
|
||
|
- FixupSpaceWidths(layout, aString);
|
||
|
+struct _nsPSPangoRendererClass
|
||
|
+{
|
||
|
+ PangoRendererClass parent_class;
|
||
|
+};
|
||
|
|
||
|
- int x = aX;
|
||
|
- int y = aY;
|
||
|
+#define _PS_TYPE_PANGO_RENDERER (_ps_pango_renderer_get_type())
|
||
|
+#define _PS_PANGO_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), _PS_TYPE_PANGO_RENDERER, _nsPSPangoRenderer))
|
||
|
+#define _PS_IS_PANGO_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), _PS_TYPE_PANGO_RENDERER))
|
||
|
+#define _PS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), _PS_TYPE_PANGO_RENDERER, _nsPSPangoRendererClass))
|
||
|
+#define _PS_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), _PS_TYPE_PANGO_RENDERER))
|
||
|
+#define _PS_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), _PS_TYPE_PANGO_RENDERER, _nsPSPangoRendererClass))
|
||
|
|
||
|
- aContext->GetTranMatrix()->TransformCoord(&x, &y);
|
||
|
+G_DEFINE_TYPE (_nsPSPangoRenderer, _ps_pango_renderer, PANGO_TYPE_RENDERER)
|
||
|
|
||
|
- PangoLayoutLine *line;
|
||
|
- if (pango_layout_get_line_count(layout) != 1) {
|
||
|
- printf("Warning: more than one line!\n");
|
||
|
- }
|
||
|
- line = pango_layout_get_line(layout, 0);
|
||
|
+static PangoRenderer *
|
||
|
+get_renderer (void)
|
||
|
+{
|
||
|
+ static PangoRenderer *renderer = NULL;
|
||
|
|
||
|
- aContext->UpdateGC();
|
||
|
- GdkGC *gc = aContext->GetGC();
|
||
|
+ if (!renderer)
|
||
|
+ renderer = (PangoRenderer *) g_object_new (_PS_TYPE_PANGO_RENDERER, NULL);
|
||
|
|
||
|
- if (aSpacing && *aSpacing) {
|
||
|
- DrawStringSlowly(aString, NULL, aLength, aSurface->GetDrawable(),
|
||
|
- gc, x, y, line, aSpacing);
|
||
|
- }
|
||
|
- else {
|
||
|
- gdk_draw_layout_line(aSurface->GetDrawable(), gc,
|
||
|
- x, y,
|
||
|
- line);
|
||
|
- }
|
||
|
+ return renderer;
|
||
|
+}
|
||
|
|
||
|
- g_object_unref(gc);
|
||
|
- g_object_unref(layout);
|
||
|
+static void
|
||
|
+_ps_pango_renderer_draw_glyphs (PangoRenderer *renderer,
|
||
|
+ PangoFont *font,
|
||
|
+ PangoGlyphString *glyphs,
|
||
|
+ int x,
|
||
|
+ int y);
|
||
|
|
||
|
- // printf("DrawString (char *)\n");
|
||
|
+static void
|
||
|
+_ps_pango_renderer_class_init (nsPSPangoRendererClass *klass)
|
||
|
+{
|
||
|
+ PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
|
||
|
+
|
||
|
+ renderer_class->draw_glyphs = _ps_pango_renderer_draw_glyphs;
|
||
|
+}
|
||
|
|
||
|
- return NS_OK;
|
||
|
+static void
|
||
|
+_ps_pango_renderer_init (nsPSPangoRenderer *renderer)
|
||
|
+{
|
||
|
+}
|
||
|
+
|
||
|
+class nsPangoType1Generator : public nsPSFontGenerator {
|
||
|
+public:
|
||
|
+ nsPangoType1Generator();
|
||
|
+ ~nsPangoType1Generator();
|
||
|
+ nsresult Init(PangoFont *aFont);
|
||
|
+ void GeneratePSFont(FILE* aFile);
|
||
|
+
|
||
|
+protected:
|
||
|
+ PangoFont *mFont;
|
||
|
+};
|
||
|
+
|
||
|
+nsPangoType1Generator::nsPangoType1Generator()
|
||
|
+{
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
-nsFontMetricsPango::DrawString(const PRUnichar* aString, PRUint32 aLength,
|
||
|
- nscoord aX, nscoord aY,
|
||
|
- PRInt32 aFontID,
|
||
|
- const nscoord* aSpacing,
|
||
|
- nsRenderingContextGTK *aContext,
|
||
|
- nsDrawingSurfaceGTK *aSurface)
|
||
|
+nsPangoType1Generator::Init(PangoFont *aFont)
|
||
|
+ {
|
||
|
+ NS_ENSURE_TRUE(aFont, NS_ERROR_FAILURE);
|
||
|
+ mFont = aFont;
|
||
|
+ g_object_ref (mFont);
|
||
|
+ return NS_OK;
|
||
|
+}
|
||
|
+
|
||
|
+nsPangoType1Generator::~nsPangoType1Generator()
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
- int x = aX;
|
||
|
- int y = aY;
|
||
|
+ g_object_unref (mFont);
|
||
|
+ mFont = nsnull;
|
||
|
+}
|
||
|
|
||
|
- aContext->UpdateGC();
|
||
|
- GdkGC *gc = aContext->GetGC();
|
||
|
+void nsPangoType1Generator::GeneratePSFont(FILE* aFile)
|
||
|
+{
|
||
|
+ FT_Face face = pango_fc_font_lock_face ((PangoFcFont *) mFont);
|
||
|
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
+ if (face == nsnull)
|
||
|
+ return;
|
||
|
|
||
|
- gchar *text = g_utf16_to_utf8(aString, aLength,
|
||
|
- NULL, NULL, NULL);
|
||
|
+ int wmode = 0;
|
||
|
+ if (mGlyphSubset->Count())
|
||
|
+ FT2SubsetToType1FontSet(face, mGlyphSubset, wmode, aFile);
|
||
|
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::DrawString invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aString, aLength)
|
||
|
-#endif
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
+ pango_fc_font_unlock_face ((PangoFcFont *) mFont);
|
||
|
+}
|
||
|
|
||
|
- pango_layout_set_text(layout, text, strlen(text));
|
||
|
- FixupSpaceWidths(layout, text);
|
||
|
+typedef struct
|
||
|
+{
|
||
|
+ nsCString *FontNameBase;
|
||
|
+ nsCStringKey *key;
|
||
|
+ int font_size;
|
||
|
+} PSPangoFontData;
|
||
|
|
||
|
- aContext->GetTranMatrix()->TransformCoord(&x, &y);
|
||
|
+static void
|
||
|
+ps_pango_font_data_destroy (PSPangoFontData *data)
|
||
|
+{
|
||
|
+ delete data->key;
|
||
|
+ delete data->FontNameBase;
|
||
|
+ g_free (data);
|
||
|
+}
|
||
|
|
||
|
- PangoLayoutLine *line;
|
||
|
- if (pango_layout_get_line_count(layout) != 1) {
|
||
|
- printf("Warning: more than one line!\n");
|
||
|
- }
|
||
|
- line = pango_layout_get_line(layout, 0);
|
||
|
+static void
|
||
|
+_ps_pango_renderer_draw_glyphs (PangoRenderer *renderer,
|
||
|
+ PangoFont *font,
|
||
|
+ PangoGlyphString *glyphs,
|
||
|
+ int x,
|
||
|
+ int y)
|
||
|
+{
|
||
|
+ if (!glyphs->num_glyphs)
|
||
|
+ return;
|
||
|
|
||
|
- if (aSpacing && *aSpacing) {
|
||
|
- DrawStringSlowly(text, aString, aLength, aSurface->GetDrawable(),
|
||
|
- gc, x, y, line, aSpacing);
|
||
|
- }
|
||
|
- else {
|
||
|
- gdk_draw_layout_line(aSurface->GetDrawable(), gc,
|
||
|
- x, y,
|
||
|
- line);
|
||
|
- }
|
||
|
+ static GQuark data_quark = 0;
|
||
|
+ if (!data_quark)
|
||
|
+ data_quark = g_quark_from_static_string ("ps-pango-font-data");
|
||
|
|
||
|
- loser:
|
||
|
+ PSPangoFontData *data;
|
||
|
+ if (!(data = (PSPangoFontData *) g_object_get_qdata (G_OBJECT (font), data_quark)))
|
||
|
+ {
|
||
|
+ data = g_new (PSPangoFontData, 1);
|
||
|
|
||
|
- g_free(text);
|
||
|
- g_object_unref(gc);
|
||
|
- g_object_unref(layout);
|
||
|
+ FT_Face face = pango_fc_font_lock_face ((PangoFcFont *) font);
|
||
|
+ if (face == nsnull)
|
||
|
+ return;
|
||
|
+ int wmode = 0;
|
||
|
+ data->FontNameBase = new nsCString ();
|
||
|
+ if (NS_FAILED(FT2ToType1FontName(face, wmode, *data->FontNameBase))) {
|
||
|
+ g_free (data);
|
||
|
+ pango_fc_font_unlock_face ((PangoFcFont *) font);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ pango_fc_font_unlock_face ((PangoFcFont *) font);
|
||
|
|
||
|
- // printf("DrawString\n");
|
||
|
+ PangoFontDescription *desc = pango_font_describe (font);
|
||
|
+ data->font_size = pango_font_description_get_size (desc);
|
||
|
+ pango_font_description_free (desc);
|
||
|
+
|
||
|
+ data->key = new nsCStringKey (*data->FontNameBase);
|
||
|
+
|
||
|
+ g_object_set_qdata_full (G_OBJECT (font), data_quark, data, (GDestroyNotify) ps_pango_font_data_destroy);
|
||
|
+ }
|
||
|
+
|
||
|
+ nsPSPangoRenderer *ps_renderer = (nsPSPangoRenderer *)renderer;
|
||
|
+ nsRenderingContextPS *aContext = ps_renderer->psContext;
|
||
|
+ nsFontMetricsPSPango *metrics = ps_renderer->psPangoFontMetrics;
|
||
|
+ nsDeviceContextPS* dc = NS_REINTERPRET_CAST (nsDeviceContextPS*, metrics->GetDeviceContext());
|
||
|
+ nsPostScriptObj* psObj = aContext->GetPostScriptObj();
|
||
|
+ nsHashtable *psFGList = dc->GetPSFontGeneratorList();
|
||
|
+ g_return_if_fail (psFGList);
|
||
|
+ nsPSFontGenerator* psFontGen = (nsPSFontGenerator*) psFGList->Get(data->key);
|
||
|
+ if (!psFontGen) {
|
||
|
+ nsresult rv;
|
||
|
+ psFontGen = new nsPangoType1Generator;
|
||
|
+ g_return_if_fail (psFontGen);
|
||
|
+ rv = ((nsPangoType1Generator*)psFontGen)->Init(font);
|
||
|
+ if (NS_FAILED(rv)) {
|
||
|
+ delete psFontGen;
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ psFGList->Put(data->key, (void *) psFontGen);
|
||
|
+ }
|
||
|
+ nscoord font_size = NSToCoordRound (ps_renderer->zoom * data->font_size / PANGO_SCALE);
|
||
|
+
|
||
|
+ g_return_if_fail (aContext);
|
||
|
+ g_return_if_fail (psObj);
|
||
|
+
|
||
|
+ nscoord aX = NSToCoordRound(ps_renderer->zoom * x / PANGO_SCALE);
|
||
|
+ nscoord aY = NSToCoordRound(ps_renderer->zoom * y / PANGO_SCALE);
|
||
|
+ psObj->moveto(aX, aY);
|
||
|
+
|
||
|
+ PRInt32 currSubFont, prevSubFont = -1;
|
||
|
+ PRUint32 i;
|
||
|
+ PangoGlyphString gl;
|
||
|
+
|
||
|
+ gl.glyphs = glyphs->glyphs;
|
||
|
+ gl.num_glyphs = 0;
|
||
|
+ currSubFont = prevSubFont;
|
||
|
+ for (i = 0; i < glyphs->num_glyphs; ++i) {
|
||
|
+ PangoGlyph glyph = glyphs->glyphs[i].glyph;
|
||
|
+
|
||
|
+ if (glyph != PANGO_GLYPH_EMPTY)
|
||
|
+ currSubFont = psFontGen->AddToGlyphSubset(glyph > 0x0fffffff ? 0 : glyph);
|
||
|
+
|
||
|
+ if (prevSubFont != currSubFont) {
|
||
|
+ if (prevSubFont != -1)
|
||
|
+ psObj->show(&gl, ps_renderer->zoom, psFontGen, prevSubFont);
|
||
|
+
|
||
|
+ psObj->setfont(*data->FontNameBase, (PRUint32) font_size, currSubFont);
|
||
|
+ prevSubFont = currSubFont;
|
||
|
+ gl.glyphs = glyphs->glyphs + i;
|
||
|
+ gl.num_glyphs = 0;
|
||
|
+ }
|
||
|
|
||
|
- return rv;
|
||
|
+ gl.num_glyphs++;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (prevSubFont != -1)
|
||
|
+ psObj->show(&gl, ps_renderer->zoom, psFontGen, prevSubFont);
|
||
|
}
|
||
|
+#endif
|
||
|
+
|
||
|
+static void
|
||
|
+draw_layout_line (int x, int y,
|
||
|
+ PangoLayoutLine *line,
|
||
|
+ nsFontMetricsPango *fm
|
||
|
+ CONTEXT_AND_SURFACE_ARG_DEF)
|
||
|
+{
|
||
|
+#ifdef PSPANGO
|
||
|
+ PangoRenderer *renderer = get_renderer ();
|
||
|
+ nsPSPangoRenderer *ps_renderer = (nsPSPangoRenderer *)renderer;
|
||
|
+ ps_renderer->psContext = aContext;
|
||
|
+ ps_renderer->psPangoFontMetrics = fm;
|
||
|
+ nsDeviceContextPS* dc = NS_REINTERPRET_CAST (nsDeviceContextPS*, fm->GetDeviceContext());
|
||
|
+ ps_renderer->zoom = dc->DevUnitsToAppUnits();
|
||
|
+
|
||
|
+ pango_renderer_draw_layout_line (renderer, line,
|
||
|
+ NSToCoordRound (x * PANGO_SCALE / ps_renderer->zoom),
|
||
|
+ NSToCoordRound (y * PANGO_SCALE / ps_renderer->zoom));
|
||
|
+#else
|
||
|
+ aContext->UpdateGC();
|
||
|
+ GdkGC *gc = aContext->GetGC();
|
||
|
+ gdk_draw_layout_line(aSurface->GetDrawable(), gc, x, y, line);
|
||
|
+ g_object_unref(gc);
|
||
|
+#endif
|
||
|
+}
|
||
|
+
|
||
|
|
||
|
-#ifdef MOZ_MATHML
|
||
|
nsresult
|
||
|
-nsFontMetricsPango::GetBoundingMetrics(const char *aString, PRUint32 aLength,
|
||
|
- nsBoundingMetrics &aBoundingMetrics,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+nsFontMetricsPango::DrawString(const char *aString, PRUint32 aLength,
|
||
|
+ nscoord aX, nscoord aY,
|
||
|
+ const nscoord* aSpacing
|
||
|
+ CONTEXT_AND_SURFACE_ARG_DEF)
|
||
|
{
|
||
|
- printf("GetBoundingMetrics (char *)\n");
|
||
|
- return NS_ERROR_FAILURE;
|
||
|
+ int x = aX;
|
||
|
+ int y = aY;
|
||
|
+
|
||
|
+ aContext->GetTranMatrix()->TransformCoord(&x, &y);
|
||
|
+
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
+ PangoLayoutLine *line = pango_layout_get_line(layout, 0);
|
||
|
+
|
||
|
+ ApplySpacing(aString, aLength, line, aSpacing);
|
||
|
+ draw_layout_line(x, y, line, this CONTEXT_AND_SURFACE_ARG_PASS);
|
||
|
+
|
||
|
+ g_object_unref(layout);
|
||
|
+
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
-nsFontMetricsPango::GetBoundingMetrics(const PRUnichar *aString,
|
||
|
- PRUint32 aLength,
|
||
|
- nsBoundingMetrics &aBoundingMetrics,
|
||
|
- PRInt32 *aFontID,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+nsFontMetricsPango::DrawString(const PRUnichar* aString, PRUint32 aLength,
|
||
|
+ nscoord aX, nscoord aY,
|
||
|
+ PRInt32 aFontID,
|
||
|
+ const nscoord* aSpacing
|
||
|
+ CONTEXT_AND_SURFACE_ARG_DEF)
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
+ int x = aX;
|
||
|
+ int y = aY;
|
||
|
|
||
|
- gchar *text = g_utf16_to_utf8(aString, aLength,
|
||
|
- NULL, NULL, NULL);
|
||
|
+ aContext->GetTranMatrix()->TransformCoord(&x, &y);
|
||
|
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetBoundingMetrics invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aString, aLength)
|
||
|
-#endif
|
||
|
- aBoundingMetrics.Clear();
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
+ PangoLayoutLine *line = pango_layout_get_line(layout, 0);
|
||
|
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
+ ApplySpacing(aString, aLength, line, aSpacing);
|
||
|
+ draw_layout_line(x, y, line, this CONTEXT_AND_SURFACE_ARG_PASS);
|
||
|
|
||
|
- pango_layout_set_text(layout, text, -1);
|
||
|
- FixupSpaceWidths(layout, text);
|
||
|
+ g_object_unref(layout);
|
||
|
+
|
||
|
+ return NS_OK;
|
||
|
+}
|
||
|
|
||
|
- PangoLayoutLine *line;
|
||
|
- if (pango_layout_get_line_count(layout) != 1) {
|
||
|
- printf("Warning: more than one line!\n");
|
||
|
- }
|
||
|
- line = pango_layout_get_line(layout, 0);
|
||
|
+
|
||
|
+#ifdef MOZ_MATHML
|
||
|
+void
|
||
|
+nsFontMetricsPango::GetBoundingMetricsInternal(PangoLayout *aLayout,
|
||
|
+ nsBoundingMetrics &aBoundingMetrics
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
+{
|
||
|
+ PangoLayoutLine *line = pango_layout_get_line(aLayout, 0);
|
||
|
|
||
|
// Get the ink and logical extents
|
||
|
PangoRectangle ink, logical;
|
||
|
pango_layout_line_get_extents(line, &ink, &logical);
|
||
|
|
||
|
- float P2T;
|
||
|
- P2T = mDeviceContext->DevUnitsToAppUnits();
|
||
|
+ float P2T = mDeviceContext->DevUnitsToAppUnits();
|
||
|
|
||
|
aBoundingMetrics.leftBearing = NSToCoordRound(PANGO_LBEARING(ink) * P2T / PANGO_SCALE);
|
||
|
aBoundingMetrics.rightBearing = NSToCoordRound(PANGO_RBEARING(ink) * P2T / PANGO_SCALE);
|
||
|
aBoundingMetrics.ascent = NSToCoordRound(PANGO_ASCENT(ink) * P2T / PANGO_SCALE);
|
||
|
aBoundingMetrics.descent = NSToCoordRound(PANGO_DESCENT(ink) * P2T / PANGO_SCALE);
|
||
|
aBoundingMetrics.width = NSToCoordRound(logical.width * P2T / PANGO_SCALE);
|
||
|
+}
|
||
|
|
||
|
- loser:
|
||
|
- g_free(text);
|
||
|
+nsresult
|
||
|
+nsFontMetricsPango::GetBoundingMetrics(const char *aString, PRUint32 aLength,
|
||
|
+ nsBoundingMetrics &aBoundingMetrics
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
+{
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
+ GetBoundingMetricsInternal (layout, aBoundingMetrics CONTEXT_ARG_PASS);
|
||
|
g_object_unref(layout);
|
||
|
|
||
|
- return rv;
|
||
|
+ return NS_OK;
|
||
|
+}
|
||
|
+
|
||
|
+nsresult
|
||
|
+nsFontMetricsPango::GetBoundingMetrics(const PRUnichar *aString,
|
||
|
+ PRUint32 aLength,
|
||
|
+ nsBoundingMetrics &aBoundingMetrics,
|
||
|
+ PRInt32 *aFontID
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
+{
|
||
|
+ PangoLayout *layout = GetLayout(aString, aLength);
|
||
|
+ GetBoundingMetricsInternal (layout, aBoundingMetrics CONTEXT_ARG_PASS);
|
||
|
+ g_object_unref(layout);
|
||
|
+
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
#endif /* MOZ_MATHML */
|
||
|
|
||
|
+#ifndef PSPANGO
|
||
|
GdkFont*
|
||
|
nsFontMetricsPango::GetCurrentGDKFont(void)
|
||
|
{
|
||
|
return nsnull;
|
||
|
}
|
||
|
+#endif
|
||
|
|
||
|
nsresult
|
||
|
nsFontMetricsPango::SetRightToLeftText(PRBool aIsRTL)
|
||
|
{
|
||
|
if (aIsRTL) {
|
||
|
if (!mRTLPangoContext) {
|
||
|
- mRTLPangoContext = gdk_pango_context_get();
|
||
|
+ mRTLPangoContext = get_context();
|
||
|
pango_context_set_base_dir(mRTLPangoContext, PANGO_DIRECTION_RTL);
|
||
|
-
|
||
|
- gdk_pango_context_set_colormap(mRTLPangoContext, gdk_rgb_get_cmap());
|
||
|
pango_context_set_language(mRTLPangoContext, GetPangoLanguage(mLangGroup));
|
||
|
pango_context_set_font_description(mRTLPangoContext, mPangoFontDesc);
|
||
|
}
|
||
|
@@ -899,34 +1087,18 @@ nsFontMetricsPango::GetClusterInfo(const
|
||
|
PRUint32 aLength,
|
||
|
PRUint8 *aClusterStarts)
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
PangoLogAttr *attrs = NULL;
|
||
|
gint n_attrs = 0;
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
-
|
||
|
- // Convert the incoming UTF-16 to UTF-8
|
||
|
- gchar *text = g_utf16_to_utf8(aText, aLength, NULL, NULL, NULL);
|
||
|
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aText, aLength)
|
||
|
-#endif
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
-
|
||
|
- // Set up the pango layout
|
||
|
- pango_layout_set_text(layout, text, strlen(text));
|
||
|
- FixupSpaceWidths(layout, text);
|
||
|
+ PangoLayout *layout = GetLayout(aText, aLength);
|
||
|
+ pango_layout_get_log_attrs(layout, &attrs, &n_attrs);
|
||
|
+ g_object_unref(layout);
|
||
|
|
||
|
// Convert back to UTF-16 while filling in the cluster info
|
||
|
// structure.
|
||
|
- pango_layout_get_log_attrs(layout, &attrs, &n_attrs);
|
||
|
-
|
||
|
for (PRUint32 pos = 0; pos < aLength; pos++) {
|
||
|
if (IS_HIGH_SURROGATE(aText[pos])) {
|
||
|
- aClusterStarts[pos] = 1;
|
||
|
+ aClusterStarts[pos] = 1;//FIXME: shouldn't this be zero?! --be
|
||
|
pos++;
|
||
|
}
|
||
|
else {
|
||
|
@@ -934,56 +1106,34 @@ nsFontMetricsPango::GetClusterInfo(const
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- loser:
|
||
|
- if (attrs)
|
||
|
- g_free(attrs);
|
||
|
- if (text)
|
||
|
- g_free(text);
|
||
|
- if (layout)
|
||
|
- g_object_unref(layout);
|
||
|
+ g_free(attrs);
|
||
|
|
||
|
- return rv;
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
PRInt32
|
||
|
-nsFontMetricsPango::GetPosition(const PRUnichar *aText, PRUint32 aLength,
|
||
|
- nsPoint aPt)
|
||
|
+nsFontMetricsPango::GetPosition(const PRUnichar *aText, PRUint32 aLength, nsPoint aPt)
|
||
|
{
|
||
|
int trailing = 0;
|
||
|
int inx = 0;
|
||
|
- const gchar *curChar;
|
||
|
PRInt32 retval = 0;
|
||
|
|
||
|
float f = mDeviceContext->AppUnitsToDevUnits();
|
||
|
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
PRUint32 localX = (PRUint32)(aPt.x * PANGO_SCALE * f);
|
||
|
PRUint32 localY = (PRUint32)(aPt.y * PANGO_SCALE * f);
|
||
|
|
||
|
- // Convert the incoming UTF-16 to UTF-8
|
||
|
- gchar *text = g_utf16_to_utf8(aText, aLength, NULL, NULL, NULL);
|
||
|
-
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aText, aLength)
|
||
|
-#endif
|
||
|
- retval = -1;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
-
|
||
|
- // Set up the pango layout
|
||
|
- pango_layout_set_text(layout, text, strlen(text));
|
||
|
- FixupSpaceWidths(layout, text);
|
||
|
+ PangoLayout *layout = GetLayout(aText, aLength);
|
||
|
|
||
|
pango_layout_xy_to_index(layout, localX, localY,
|
||
|
&inx, &trailing);
|
||
|
|
||
|
// Convert the index back to the utf-16 index
|
||
|
- curChar = text;
|
||
|
+ const gchar *text = pango_layout_get_text (layout);
|
||
|
+ const gchar *curChar = text;
|
||
|
|
||
|
for (PRUint32 curOffset=0; curOffset < aLength;
|
||
|
- curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) {
|
||
|
+ curOffset++, curChar = g_utf8_next_char(curChar)) {
|
||
|
|
||
|
// Check for a match before checking for a surrogate pair
|
||
|
if (curChar - text == inx) {
|
||
|
@@ -1006,13 +1156,9 @@ nsFontMetricsPango::GetPosition(const PR
|
||
|
trailing--;
|
||
|
}
|
||
|
|
||
|
- loser:
|
||
|
- if (text)
|
||
|
- g_free(text);
|
||
|
- if (layout)
|
||
|
- g_object_unref(layout);
|
||
|
+ g_object_unref(layout);
|
||
|
|
||
|
- return retval;
|
||
|
+ return retval;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
@@ -1022,28 +1168,21 @@ nsFontMetricsPango::GetRangeWidth(const
|
||
|
PRUint32 aEnd,
|
||
|
PRUint32 &aWidth)
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
PRUint32 utf8Start = 0;
|
||
|
PRUint32 utf8End = 0;
|
||
|
|
||
|
aWidth = 0;
|
||
|
|
||
|
// Convert the incoming UTF-16 to UTF-8
|
||
|
- gchar *text = g_utf16_to_utf8(aText, aLength, NULL, NULL, NULL);
|
||
|
- gchar *curChar = text;
|
||
|
|
||
|
- if (!text) {
|
||
|
-#ifdef DEBUG
|
||
|
- NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow");
|
||
|
- DUMP_PRUNICHAR(aText, aLength)
|
||
|
-#endif
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
+ gchar* text;
|
||
|
+ gint text_len;
|
||
|
+ utf16_to_utf8 (aText, aLength, text, text_len);
|
||
|
+ gchar *curChar = text;
|
||
|
|
||
|
// Convert the utf16 offsets into utf8 offsets
|
||
|
for (PRUint32 curOffset = 0; curOffset < aLength;
|
||
|
- curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) {
|
||
|
+ curOffset++, curChar = g_utf8_next_char(curChar)) {
|
||
|
|
||
|
if (curOffset == aStart)
|
||
|
utf8Start = curChar - text;
|
||
|
@@ -1057,15 +1196,13 @@ nsFontMetricsPango::GetRangeWidth(const
|
||
|
|
||
|
// Special case where the end index is the same as the length
|
||
|
if (aLength == aEnd)
|
||
|
- utf8End = strlen(text);
|
||
|
+ utf8End = text_len;
|
||
|
|
||
|
- rv = GetRangeWidth(text, strlen(text), utf8Start, utf8End, aWidth);
|
||
|
+ GetRangeWidth(text, text_len, utf8Start, utf8End, aWidth);
|
||
|
|
||
|
- loser:
|
||
|
- if (text)
|
||
|
- g_free(text);
|
||
|
+ g_free(text);
|
||
|
|
||
|
- return rv;
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
@@ -1075,43 +1212,26 @@ nsFontMetricsPango::GetRangeWidth(const
|
||
|
PRUint32 aEnd,
|
||
|
PRUint32 &aWidth)
|
||
|
{
|
||
|
- nsresult rv = NS_OK;
|
||
|
int *ranges = NULL;
|
||
|
int n_ranges = 0;
|
||
|
float f;
|
||
|
|
||
|
aWidth = 0;
|
||
|
|
||
|
- PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
-
|
||
|
- if (!aText) {
|
||
|
- rv = NS_ERROR_FAILURE;
|
||
|
- goto loser;
|
||
|
- }
|
||
|
-
|
||
|
- pango_layout_set_text(layout, aText, aLength);
|
||
|
- FixupSpaceWidths(layout, aText);
|
||
|
-
|
||
|
- PangoLayoutLine *line;
|
||
|
- if (pango_layout_get_line_count(layout) != 1) {
|
||
|
- printf("Warning: more than one line!\n");
|
||
|
- }
|
||
|
- line = pango_layout_get_line(layout, 0);
|
||
|
+ PangoLayout *layout = GetLayout(aText, aLength);
|
||
|
+ PangoLayoutLine *line = pango_layout_get_line(layout, 0);
|
||
|
|
||
|
pango_layout_line_get_x_ranges(line, aStart, aEnd, &ranges, &n_ranges);
|
||
|
|
||
|
aWidth = (ranges[((n_ranges - 1) * 2) + 1] - ranges[0]);
|
||
|
|
||
|
f = mDeviceContext-> DevUnitsToAppUnits();
|
||
|
- aWidth = nscoord(aWidth * f / PANGO_SCALE);
|
||
|
+ aWidth = NSToCoordRound(aWidth * f / PANGO_SCALE);
|
||
|
|
||
|
- loser:
|
||
|
- if (ranges)
|
||
|
- g_free(ranges);
|
||
|
- if (layout)
|
||
|
- g_object_unref(layout);
|
||
|
+ g_free(ranges);
|
||
|
+ g_object_unref(layout);
|
||
|
|
||
|
- return rv;
|
||
|
+ return NS_OK;
|
||
|
}
|
||
|
|
||
|
/* static */
|
||
|
@@ -1134,7 +1254,7 @@ nsFontMetricsPango::FamilyExists(nsIDevi
|
||
|
NS_ConvertUTF16toUTF8 name(aName);
|
||
|
|
||
|
nsresult rv = NS_ERROR_FAILURE;
|
||
|
- PangoContext *context = gdk_pango_context_get();
|
||
|
+ PangoContext *context = get_context();
|
||
|
PangoFontFamily **familyList;
|
||
|
int n;
|
||
|
|
||
|
@@ -1233,16 +1353,13 @@ nsFontMetricsPango::RealizeFont(void)
|
||
|
|
||
|
// Now that we have the font description set up, create the
|
||
|
// context.
|
||
|
- mLTRPangoContext = gdk_pango_context_get();
|
||
|
+ mLTRPangoContext = get_context();
|
||
|
mPangoContext = mLTRPangoContext;
|
||
|
|
||
|
// Make sure to set the base direction to LTR - if layout needs to
|
||
|
// render RTL text it will use ::SetRightToLeftText()
|
||
|
pango_context_set_base_dir(mPangoContext, PANGO_DIRECTION_LTR);
|
||
|
|
||
|
- // Set the color map so we can draw later.
|
||
|
- gdk_pango_context_set_colormap(mPangoContext, gdk_rgb_get_cmap());
|
||
|
-
|
||
|
// Set the pango language now that we have a context
|
||
|
pango_context_set_language(mPangoContext, GetPangoLanguage(mLangGroup));
|
||
|
|
||
|
@@ -1280,79 +1397,268 @@ nsFontMetricsPango::EnumFontCallback(con
|
||
|
* This is only used when there's per-character spacing happening.
|
||
|
* Well, really it can be either line or character spacing but it's
|
||
|
* just turtles all the way down!
|
||
|
+ *
|
||
|
+ * To do it correctly (ligatures, etc) we need machinery that is private
|
||
|
+ * in Pango. IMPORT IT:
|
||
|
+ */
|
||
|
+
|
||
|
+#define _PangoGlyphItemIter _nsFontMetricsPangoGlyphItemIter
|
||
|
+#define PangoGlyphItemIter nsFontMetricsPangoGlyphItemIter
|
||
|
+
|
||
|
+#define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0)
|
||
|
+
|
||
|
+/* Structure holding state when we're iterating over a GlyphItem.
|
||
|
+ * start_index/cluster_end (and range_start/range_end in
|
||
|
+ * apply_attrs()) are offsets into the text, so note the difference
|
||
|
+ * of glyph_item->item->offset between them and clusters in the
|
||
|
+ * log_clusters[] array.
|
||
|
*/
|
||
|
+typedef struct _PangoGlyphItemIter PangoGlyphItemIter;
|
||
|
+
|
||
|
+struct _PangoGlyphItemIter
|
||
|
+{
|
||
|
+ PangoGlyphItem *glyph_item;
|
||
|
+ const gchar *text;
|
||
|
+
|
||
|
+ int start_glyph;
|
||
|
+ int start_index;
|
||
|
+ int start_char;
|
||
|
+
|
||
|
+ int end_glyph;
|
||
|
+ int end_index;
|
||
|
+ int end_char;
|
||
|
+};
|
||
|
+
|
||
|
+/**
|
||
|
+ * _pango_glyph_item_iter_next_cluster:
|
||
|
+ * @iter: a #PangoGlyphItemIter
|
||
|
+ *
|
||
|
+ * Advances the iterator to the next cluster in the glyph item.
|
||
|
+ *
|
||
|
+ * Return value: %TRUE if the iterator was advanced, %FALSE if we were already on the
|
||
|
+ * last cluster.
|
||
|
+ **/
|
||
|
+static gboolean
|
||
|
+_pango_glyph_item_iter_next_cluster (PangoGlyphItemIter *iter)
|
||
|
+{
|
||
|
+ int glyph_index = iter->end_glyph;
|
||
|
+ PangoGlyphString *glyphs = iter->glyph_item->glyphs;
|
||
|
+ PangoItem *item = iter->glyph_item->item;
|
||
|
+
|
||
|
+ if (LTR (iter->glyph_item))
|
||
|
+ {
|
||
|
+ if (glyph_index == glyphs->num_glyphs)
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ if (glyph_index < 0)
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ iter->start_glyph = iter->end_glyph;
|
||
|
+ iter->start_index = iter->end_index;
|
||
|
+ iter->start_char = iter->end_char;
|
||
|
+
|
||
|
+ if (LTR (iter->glyph_item))
|
||
|
+ {
|
||
|
+ while (TRUE)
|
||
|
+ {
|
||
|
+ glyph_index++;
|
||
|
+
|
||
|
+ if (glyph_index == glyphs->num_glyphs)
|
||
|
+ {
|
||
|
+ iter->end_index = item->offset + item->length;
|
||
|
+ iter->end_char = item->num_chars;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (item->offset + glyphs->log_clusters[glyph_index] != iter->start_index)
|
||
|
+ {
|
||
|
+ iter->end_index = item->offset + glyphs->log_clusters[glyph_index];
|
||
|
+ iter->end_char += g_utf8_strlen (iter->text + iter->start_index,
|
||
|
+ iter->end_index - iter->start_index);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else /* RTL */
|
||
|
+ {
|
||
|
+ while (TRUE)
|
||
|
+ {
|
||
|
+ glyph_index--;
|
||
|
+
|
||
|
+ if (glyph_index < 0)
|
||
|
+ {
|
||
|
+ iter->end_index = item->offset + item->length;
|
||
|
+ iter->end_char = item->num_chars;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (item->offset + glyphs->log_clusters[glyph_index] != iter->start_index)
|
||
|
+ {
|
||
|
+ iter->end_index = item->offset + glyphs->log_clusters[glyph_index];
|
||
|
+ iter->end_char += g_utf8_strlen (iter->text + iter->start_index,
|
||
|
+ iter->end_index - iter->start_index);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ iter->end_glyph = glyph_index;
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * _pango_glyph_item_iter_init_start:
|
||
|
+ * @iter: pointer to a #PangoGlyphItemIter structure
|
||
|
+ * @glyph_item: the glyph item that the iter points into
|
||
|
+ * @text: text corresponding to the glyph item
|
||
|
+ *
|
||
|
+ * Initializes a #PangoGlyphItemIter structure to point to the
|
||
|
+ * first cluster in a glyph item.
|
||
|
+ *
|
||
|
+ * Return value: %FALSE if there are no clusters in the glyph item;
|
||
|
+ * in this case, the state of the iter is undefined.
|
||
|
+ **/
|
||
|
+static gboolean
|
||
|
+_pango_glyph_item_iter_init_start (PangoGlyphItemIter *iter,
|
||
|
+ PangoGlyphItem *glyph_item,
|
||
|
+ const char *text)
|
||
|
+{
|
||
|
+ iter->glyph_item = glyph_item;
|
||
|
+ iter->text = text;
|
||
|
+
|
||
|
+ if (LTR (glyph_item))
|
||
|
+ iter->end_glyph = 0;
|
||
|
+ else
|
||
|
+ iter->end_glyph = glyph_item->glyphs->num_glyphs - 1;
|
||
|
+
|
||
|
+ iter->end_index = glyph_item->item->offset;
|
||
|
+ iter->end_char = 0;
|
||
|
+
|
||
|
+ /* Advance onto the first cluster of the glyph item */
|
||
|
+ return _pango_glyph_item_iter_next_cluster (iter);
|
||
|
+}
|
||
|
+
|
||
|
|
||
|
void
|
||
|
-nsFontMetricsPango::DrawStringSlowly(const gchar *aText,
|
||
|
- const PRUnichar *aOrigString,
|
||
|
- PRUint32 aLength,
|
||
|
- GdkDrawable *aDrawable,
|
||
|
- GdkGC *aGC, gint aX, gint aY,
|
||
|
- PangoLayoutLine *aLine,
|
||
|
- const nscoord *aSpacing)
|
||
|
-{
|
||
|
- float app2dev;
|
||
|
- app2dev = mDeviceContext->AppUnitsToDevUnits();
|
||
|
- gint offset = 0;
|
||
|
+nsFontMetricsPango::ApplySpacing(const gchar *aText,
|
||
|
+ PRUint32 aLength,
|
||
|
+ PangoLayoutLine *aLine,
|
||
|
+ const nscoord *aSpacing)
|
||
|
+{
|
||
|
+ if (!(aSpacing && *aSpacing))
|
||
|
+ return;
|
||
|
+
|
||
|
+ float app2dev = mDeviceContext->AppUnitsToDevUnits();
|
||
|
|
||
|
/*
|
||
|
* We walk the list of glyphs returned in each layout run,
|
||
|
* matching up the glyphs with the characters in the source text.
|
||
|
* We use the aSpacing argument to figure out where to place those
|
||
|
- * glyphs. It's important to note that since the string we're
|
||
|
- * working with is in UTF-8 while the spacing argument assumes
|
||
|
- * that offset will be part of the UTF-16 string. Logical
|
||
|
- * attributes in pango are in byte offsets in the UTF-8 string, so
|
||
|
- * we need to store the offsets based on the UTF-8 string.
|
||
|
+ * glyphs.
|
||
|
*/
|
||
|
- nscoord *utf8spacing = new nscoord[strlen(aText)];
|
||
|
+ for (GSList *tmpList = aLine->runs; tmpList && tmpList->data;
|
||
|
+ tmpList = tmpList->next) {
|
||
|
+ PangoGlyphItem *glyph_item = (PangoGlyphItem *)tmpList->data;
|
||
|
+ PangoGlyphItemIter iter;
|
||
|
+ gboolean have_cluster;
|
||
|
+ PangoGlyphInfo *glyphs = glyph_item->glyphs->glyphs;
|
||
|
+ int residualWidth = 0;
|
||
|
+
|
||
|
+ for (have_cluster = _pango_glyph_item_iter_init_start (&iter, glyph_item, aText);
|
||
|
+ have_cluster;
|
||
|
+ have_cluster = _pango_glyph_item_iter_next_cluster (&iter))
|
||
|
+ {
|
||
|
+ int clusterOldWidth = 0;
|
||
|
+ int clusterNewWidth = 0;
|
||
|
+ int dir = iter.start_glyph < iter.end_glyph ? +1 : -1;
|
||
|
+ gboolean has_zero_width = FALSE;
|
||
|
+
|
||
|
+ for (const char *p = iter.text + iter.start_index;
|
||
|
+ p < iter.text + iter.end_index;
|
||
|
+ p = g_utf8_next_char (p))
|
||
|
+ clusterNewWidth += aSpacing[p - iter.text];
|
||
|
+
|
||
|
+ clusterNewWidth = (gint)(clusterNewWidth * app2dev * PANGO_SCALE);
|
||
|
+
|
||
|
+ for (gint i = iter.start_glyph; i != iter.end_glyph; i += dir) {
|
||
|
+ if (!glyphs[i].geometry.width)
|
||
|
+ has_zero_width = TRUE;
|
||
|
+ clusterOldWidth += glyphs[i].geometry.width;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* if a zero-width glyph exists, don't touch the glyph widths.
|
||
|
+ * required for combining marks. ff thinks they have a width.
|
||
|
+ * instead, we charge the difference to the next space glyph. */
|
||
|
+ if (has_zero_width) {
|
||
|
+ residualWidth += clusterNewWidth - clusterOldWidth;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
|
||
|
- if (aOrigString) {
|
||
|
- const gchar *curChar = aText;
|
||
|
- bzero(utf8spacing, sizeof(nscoord) * strlen(aText));
|
||
|
-
|
||
|
- // Covert the utf16 spacing offsets to utf8 spacing offsets
|
||
|
- for (PRUint32 curOffset=0; curOffset < aLength;
|
||
|
- curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) {
|
||
|
- utf8spacing[curChar - aText] = aSpacing[curOffset];
|
||
|
+ /* If a space glyph is found, charge it whatever residual we
|
||
|
+ * have accumulated so far. */
|
||
|
+ if (iter.end_index - iter.start_index == 1 &&
|
||
|
+ *(iter.text + iter.start_index) == ' ') {
|
||
|
+ clusterNewWidth += residualWidth;
|
||
|
+ residualWidth = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+#ifndef PSPANGO
|
||
|
+ /* do some hinting for display */
|
||
|
+
|
||
|
+ if (clusterOldWidth % PANGO_SCALE == 0 && clusterNewWidth % PANGO_SCALE != 0) {
|
||
|
+ int tmp = clusterNewWidth;
|
||
|
+ clusterNewWidth = PANGO_PIXELS (clusterNewWidth) * PANGO_SCALE;
|
||
|
+ residualWidth += tmp - clusterNewWidth;
|
||
|
+ }
|
||
|
+#endif
|
||
|
|
||
|
- if (IS_HIGH_SURROGATE(aOrigString[curOffset]))
|
||
|
- curOffset++;
|
||
|
+ /* find the first non-zero-width glyph and adjust its width */
|
||
|
+ for (gint i = iter.start_glyph; i != iter.end_glyph; i += dir)
|
||
|
+ if (glyphs[i].geometry.width) {
|
||
|
+ glyphs[i].geometry.width += clusterNewWidth - clusterOldWidth;
|
||
|
+ break;
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
- else {
|
||
|
- memcpy(utf8spacing, aSpacing, (sizeof(nscoord *) * aLength));
|
||
|
- }
|
||
|
+}
|
||
|
|
||
|
- gint curRun = 0;
|
||
|
+void
|
||
|
+nsFontMetricsPango::ApplySpacing(const PRUnichar *aText,
|
||
|
+ PRUint32 aLength,
|
||
|
+ PangoLayoutLine *aLine,
|
||
|
+ const nscoord *aSpacing)
|
||
|
+{
|
||
|
+ if (!(aSpacing && *aSpacing))
|
||
|
+ return;
|
||
|
|
||
|
- for (GSList *tmpList = aLine->runs; tmpList && tmpList->data;
|
||
|
- tmpList = tmpList->next, curRun++) {
|
||
|
- PangoLayoutRun *layoutRun = (PangoLayoutRun *)tmpList->data;
|
||
|
- gint tmpOffset = 0;
|
||
|
+ const char *utf8Text = pango_layout_get_text (aLine->layout);
|
||
|
+ int utf8Text_len = aLine->start_index + aLine->length;
|
||
|
|
||
|
- /* printf(" Rendering run %d: \"%s\"\n", curRun,
|
||
|
- &aText[layoutRun->item->offset]); */
|
||
|
+ /* Since the string we're
|
||
|
+ * working with is in UTF-8 while the spacing argument assumes
|
||
|
+ * that offset will be part of the UTF-16 string. Logical
|
||
|
+ * attributes in pango are in byte offsets in the UTF-8 string, so
|
||
|
+ * we need to store the offsets based on the UTF-8 string.
|
||
|
+ */
|
||
|
+ nscoord *utf8spacing = g_new0 (nscoord, utf8Text_len);
|
||
|
|
||
|
- for (gint i=0; i < layoutRun->glyphs->num_glyphs; i++) {
|
||
|
- /* printf("glyph %d offset %d orig width %d new width %d\n", i,
|
||
|
- * layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset,
|
||
|
- * layoutRun->glyphs->glyphs[i].geometry.width,
|
||
|
- * (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset] * app2dev * PANGO_SCALE));
|
||
|
- */
|
||
|
- gint thisOffset = (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset]
|
||
|
- * app2dev * PANGO_SCALE);
|
||
|
- layoutRun->glyphs->glyphs[i].geometry.width = thisOffset;
|
||
|
- tmpOffset += thisOffset;
|
||
|
- }
|
||
|
+ const gchar *curChar = utf8Text + aLine->start_index;
|
||
|
|
||
|
- /* printf(" rendering at X coord %d\n", aX + offset); */
|
||
|
- offset += tmpOffset;
|
||
|
+ // Covert the utf16 spacing offsets to utf8 spacing offsets
|
||
|
+ for (PRUint32 curOffset=0; curOffset < aLength;
|
||
|
+ curOffset++, curChar = g_utf8_next_char(curChar)) {
|
||
|
+ utf8spacing[curChar - utf8Text] = aSpacing[curOffset];
|
||
|
+
|
||
|
+ if (IS_HIGH_SURROGATE(aText[curOffset]))
|
||
|
+ curOffset++;
|
||
|
}
|
||
|
|
||
|
- gdk_draw_layout_line(aDrawable, aGC, aX, aY, aLine);
|
||
|
+ ApplySpacing (utf8Text, utf8Text_len, aLine, utf8spacing);
|
||
|
|
||
|
- delete[] utf8spacing;
|
||
|
+ g_free (utf8spacing);
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
@@ -1363,8 +1669,8 @@ nsFontMetricsPango::GetTextDimensionsInt
|
||
|
PRInt32 aNumBreaks,
|
||
|
nsTextDimensions& aDimensions,
|
||
|
PRInt32& aNumCharsFit,
|
||
|
- nsTextDimensions& aLastWordDimensions,
|
||
|
- nsRenderingContextGTK *aContext)
|
||
|
+ nsTextDimensions& aLastWordDimensions
|
||
|
+ CONTEXT_ARG_DEF)
|
||
|
{
|
||
|
NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
|
||
|
|
||
|
@@ -1410,7 +1716,7 @@ nsFontMetricsPango::GetTextDimensionsInt
|
||
|
// All the characters should fit
|
||
|
numChars = aLength - start;
|
||
|
breakIndex = aNumBreaks - 1;
|
||
|
- }
|
||
|
+ }
|
||
|
else {
|
||
|
breakIndex = prevBreakState_BreakIndex;
|
||
|
while (((breakIndex + 1) < aNumBreaks) &&
|
||
|
@@ -1431,7 +1737,7 @@ nsFontMetricsPango::GetTextDimensionsInt
|
||
|
if ((1 == numChars) && (aString[start] == ' '))
|
||
|
GetSpaceWidth(twWidth);
|
||
|
else if (numChars > 0)
|
||
|
- GetWidth(&aString[start], numChars, twWidth, aContext);
|
||
|
+ GetWidth(&aString[start], numChars, twWidth CONTEXT_ARG_PASS);
|
||
|
|
||
|
// See if the text fits
|
||
|
PRBool textFits = (twWidth + width) <= aAvailWidth;
|
||
|
@@ -1481,8 +1787,7 @@ nsFontMetricsPango::GetTextDimensionsInt
|
||
|
if ((1 == numChars) && (aString[start] == ' '))
|
||
|
GetSpaceWidth(twWidth);
|
||
|
else if (numChars > 0)
|
||
|
- GetWidth(&aString[start], numChars, twWidth,
|
||
|
- aContext);
|
||
|
+ GetWidth(&aString[start], numChars, twWidth CONTEXT_ARG_PASS);
|
||
|
width -= twWidth;
|
||
|
aNumCharsFit = start;
|
||
|
breakIndex--;
|
||
|
@@ -1504,9 +1809,16 @@ nsFontMetricsPango::GetTextDimensionsInt
|
||
|
}
|
||
|
|
||
|
void
|
||
|
-nsFontMetricsPango::FixupSpaceWidths (PangoLayout *aLayout,
|
||
|
- const char *aString)
|
||
|
+nsFontMetricsPango::FixupSpaceWidths (PangoLayout *aLayout)
|
||
|
{
|
||
|
+ if (!mPangoSpaceWidth)
|
||
|
+ return;
|
||
|
+
|
||
|
+ const char *aString = pango_layout_get_text (aLayout);
|
||
|
+
|
||
|
+ if (pango_layout_get_line_count(aLayout) != 1) {
|
||
|
+ printf("Warning: more than one line!\n");
|
||
|
+ }
|
||
|
PangoLayoutLine *line = pango_layout_get_line(aLayout, 0);
|
||
|
|
||
|
gint curRun = 0;
|
||
|
@@ -1523,6 +1835,107 @@ nsFontMetricsPango::FixupSpaceWidths (Pa
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+PangoLayout*
|
||
|
+nsFontMetricsPango::GetLayout (const PRUnichar* aText,
|
||
|
+ PRUint32 aLength)
|
||
|
+{
|
||
|
+ gchar* text;
|
||
|
+ gint length;
|
||
|
+ utf16_to_utf8 (aText, aLength, text, length);
|
||
|
+
|
||
|
+ PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
+ pango_layout_set_text (layout, text, length);
|
||
|
+ FixupSpaceWidths (layout);
|
||
|
+
|
||
|
+ g_free ((gpointer) text);
|
||
|
+
|
||
|
+ return layout;
|
||
|
+}
|
||
|
+
|
||
|
+PangoLayout*
|
||
|
+nsFontMetricsPango::GetLayout (const gchar* aText,
|
||
|
+ PRInt32 aLength)
|
||
|
+{
|
||
|
+ gboolean has_nul = FALSE;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for (i = 0; i < aLength; i++)
|
||
|
+ if (!aText[i]) {
|
||
|
+ has_nul = TRUE;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (has_nul) {
|
||
|
+ /* Pango doesn't correctly handle nuls. We convert them to 0xff. */
|
||
|
+
|
||
|
+ char *p = (char *) g_memdup (aText, aLength);
|
||
|
+
|
||
|
+ /* don't need to reset i */
|
||
|
+ for (; i < aLength; i++)
|
||
|
+ if (!p[i])
|
||
|
+ p[i] = (char) 0xff;
|
||
|
+
|
||
|
+ aText = p;
|
||
|
+ }
|
||
|
+
|
||
|
+ PangoLayout *layout = pango_layout_new(mPangoContext);
|
||
|
+ pango_layout_set_text (layout, aText, aLength);
|
||
|
+ FixupSpaceWidths (layout);
|
||
|
+
|
||
|
+ if (has_nul)
|
||
|
+ g_free ((gpointer) aText);
|
||
|
+
|
||
|
+ return layout;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+utf16_to_utf8 (const PRUnichar* aText, PRUint32 aLength, char *&text, gint &length)
|
||
|
+{
|
||
|
+ gboolean need_copy = FALSE;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for (i = 0; i < aLength; i++) {
|
||
|
+ if (!aText[i] || IS_LOW_SURROGATE (aText[i]))
|
||
|
+ need_copy = TRUE;
|
||
|
+ else if (IS_HIGH_SURROGATE (aText[i])) {
|
||
|
+ if (i < aLength - 1 && IS_LOW_SURROGATE (aText[i+1]))
|
||
|
+ i++;
|
||
|
+ else
|
||
|
+ need_copy = TRUE;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (need_copy) {
|
||
|
+
|
||
|
+ /* Pango doesn't correctly handle nuls. We convert them to 0xff. */
|
||
|
+ /* Also "validate" UTF-16 text to make sure conversion doesn't fail. */
|
||
|
+
|
||
|
+ PRUnichar *p = (PRUnichar *) g_memdup (aText, aLength * sizeof (aText[0]));
|
||
|
+
|
||
|
+ /* don't need to reset i */
|
||
|
+ for (i = 0; i < aLength; i++) {
|
||
|
+ if (!p[i] || IS_LOW_SURROGATE (p[i]))
|
||
|
+ p[i] = 0xFFFD;
|
||
|
+ else if (IS_HIGH_SURROGATE (p[i])) {
|
||
|
+ if (i < aLength - 1 && IS_LOW_SURROGATE (aText[i+1]))
|
||
|
+ i++;
|
||
|
+ else
|
||
|
+ p[i] = 0xFFFD;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ aText = p;
|
||
|
+ }
|
||
|
+
|
||
|
+ glong items_written;
|
||
|
+ text = g_utf16_to_utf8 (aText, aLength, NULL, &items_written, NULL);
|
||
|
+ length = items_written;
|
||
|
+
|
||
|
+ if (need_copy)
|
||
|
+ g_free ((gpointer) aText);
|
||
|
+
|
||
|
+}
|
||
|
+
|
||
|
/* static */
|
||
|
PangoLanguage *
|
||
|
GetPangoLanguage(nsIAtom *aLangGroup)
|
||
|
--- mozilla.back/gfx/src/gtk/nsFontMetricsPango.h.orig 2006-06-30 01:18:34.000000000 +0200
|
||
|
+++ mozilla.back/gfx/src/gtk/nsFontMetricsPango.h 2007-06-28 15:16:39.000000000 +0200
|
||
|
@@ -37,17 +37,53 @@
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
+
|
||
|
#include "nsIFontMetrics.h"
|
||
|
#include "nsIFontEnumerator.h"
|
||
|
#include "nsCRT.h"
|
||
|
#include "nsIAtom.h"
|
||
|
#include "nsString.h"
|
||
|
#include "nsVoidArray.h"
|
||
|
+
|
||
|
+#ifdef PSPANGO
|
||
|
+#include "nsFontMetricsPS.h"
|
||
|
+#else
|
||
|
#include "nsIFontMetricsGTK.h"
|
||
|
+#endif
|
||
|
|
||
|
#include <pango/pango.h>
|
||
|
|
||
|
-class nsFontMetricsPango : public nsIFontMetricsGTK
|
||
|
+#ifdef PSPANGO
|
||
|
+
|
||
|
+#define CONTEXT_ARG_DEF
|
||
|
+#define CONTEXT_ARG_PASS
|
||
|
+#define CONTEXT_ARG_NULL
|
||
|
+#define CONTEXT_AND_SURFACE_ARG_DEF , nsRenderingContextPS *aContext
|
||
|
+#define CONTEXT_AND_SURFACE_ARG_PASS , aContext
|
||
|
+
|
||
|
+#else
|
||
|
+
|
||
|
+#define CONTEXT_ARG_DEF , nsRenderingContextGTK *aContext
|
||
|
+#define CONTEXT_ARG_PASS , aContext
|
||
|
+#define CONTEXT_ARG_NULL , NULL
|
||
|
+#define CONTEXT_AND_SURFACE_ARG_DEF , nsRenderingContextGTK *aContext, nsDrawingSurfaceGTK *aSurface
|
||
|
+#define CONTEXT_AND_SURFACE_ARG_PASS , aContext, aSurface
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
+
|
||
|
+#ifdef PSPANGO
|
||
|
+
|
||
|
+#define nsFontMetricsPango nsFontMetricsPSPango
|
||
|
+#define PSPANGO_PARENT_CLASS nsFontMetricsPS
|
||
|
+
|
||
|
+#else
|
||
|
+
|
||
|
+#define PSPANGO_PARENT_CLASS nsIFontMetricsGTK
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
+class nsFontMetricsPango : public PSPANGO_PARENT_CLASS
|
||
|
{
|
||
|
public:
|
||
|
nsFontMetricsPango();
|
||
|
@@ -136,20 +172,30 @@ public:
|
||
|
|
||
|
PRInt32 GetMaxStringLength() { return mMaxStringLength; }
|
||
|
|
||
|
- // nsIFontMetricsGTK (calls from the font rendering layer)
|
||
|
- virtual nsresult GetWidth(const char* aString, PRUint32 aLength,
|
||
|
- nscoord& aWidth,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
- virtual nsresult GetWidth(const PRUnichar* aString, PRUint32 aLength,
|
||
|
- nscoord& aWidth, PRInt32 *aFontID,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
+ // nsIFontMetrics (calls from the font rendering layer)
|
||
|
|
||
|
- virtual nsresult GetTextDimensions(const PRUnichar* aString,
|
||
|
+#ifdef PSPANGO
|
||
|
+ NS_IMETHOD GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength);
|
||
|
+ NS_IMETHOD GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength);
|
||
|
+#endif
|
||
|
+
|
||
|
+ NS_METHOD GetWidth(const char* aString, PRUint32 aLength,
|
||
|
+ nscoord& aWidth
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+ NS_METHOD GetWidth(const PRUnichar* aString, PRUint32 aLength,
|
||
|
+ nscoord& aWidth, PRInt32 *aFontID
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+
|
||
|
+ NS_METHOD GetTextDimensions(const char* aString,
|
||
|
+ PRUint32 aLength,
|
||
|
+ nsTextDimensions& aDimensions
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+ NS_METHOD GetTextDimensions(const PRUnichar* aString,
|
||
|
PRUint32 aLength,
|
||
|
nsTextDimensions& aDimensions,
|
||
|
- PRInt32* aFontID,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
- virtual nsresult GetTextDimensions(const char* aString,
|
||
|
+ PRInt32* aFontID
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+ NS_METHOD GetTextDimensions(const char* aString,
|
||
|
PRInt32 aLength,
|
||
|
PRInt32 aAvailWidth,
|
||
|
PRInt32* aBreaks,
|
||
|
@@ -157,9 +203,9 @@ public:
|
||
|
nsTextDimensions& aDimensions,
|
||
|
PRInt32& aNumCharsFit,
|
||
|
nsTextDimensions& aLastWordDimensions,
|
||
|
- PRInt32* aFontID,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
- virtual nsresult GetTextDimensions(const PRUnichar* aString,
|
||
|
+ PRInt32* aFontID
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+ NS_METHOD GetTextDimensions(const PRUnichar* aString,
|
||
|
PRInt32 aLength,
|
||
|
PRInt32 aAvailWidth,
|
||
|
PRInt32* aBreaks,
|
||
|
@@ -167,38 +213,37 @@ public:
|
||
|
nsTextDimensions& aDimensions,
|
||
|
PRInt32& aNumCharsFit,
|
||
|
nsTextDimensions& aLastWordDimensions,
|
||
|
- PRInt32* aFontID,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
+ PRInt32* aFontID
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
|
||
|
- virtual nsresult DrawString(const char *aString, PRUint32 aLength,
|
||
|
+ NS_METHOD DrawString(const char *aString, PRUint32 aLength,
|
||
|
nscoord aX, nscoord aY,
|
||
|
- const nscoord* aSpacing,
|
||
|
- nsRenderingContextGTK *aContext,
|
||
|
- nsDrawingSurfaceGTK *aSurface);
|
||
|
- virtual nsresult DrawString(const PRUnichar* aString, PRUint32 aLength,
|
||
|
+ const nscoord* aSpacing
|
||
|
+ CONTEXT_AND_SURFACE_ARG_DEF);
|
||
|
+
|
||
|
+ NS_METHOD DrawString(const PRUnichar* aString, PRUint32 aLength,
|
||
|
nscoord aX, nscoord aY,
|
||
|
PRInt32 aFontID,
|
||
|
- const nscoord* aSpacing,
|
||
|
- nsRenderingContextGTK *aContext,
|
||
|
- nsDrawingSurfaceGTK *aSurface);
|
||
|
+ const nscoord* aSpacing
|
||
|
+ CONTEXT_AND_SURFACE_ARG_DEF);
|
||
|
|
||
|
#ifdef MOZ_MATHML
|
||
|
- virtual nsresult GetBoundingMetrics(const char *aString, PRUint32 aLength,
|
||
|
- nsBoundingMetrics &aBoundingMetrics,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
- virtual nsresult GetBoundingMetrics(const PRUnichar *aString,
|
||
|
+ NS_METHOD GetBoundingMetrics(const char *aString, PRUint32 aLength,
|
||
|
+ nsBoundingMetrics &aBoundingMetrics
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+ NS_METHOD GetBoundingMetrics(const PRUnichar *aString,
|
||
|
PRUint32 aLength,
|
||
|
nsBoundingMetrics &aBoundingMetrics,
|
||
|
- PRInt32 *aFontID,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
+ PRInt32 *aFontID
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
#endif /* MOZ_MATHML */
|
||
|
-
|
||
|
+#ifndef PSPANGO
|
||
|
virtual GdkFont* GetCurrentGDKFont(void);
|
||
|
-
|
||
|
- virtual nsresult SetRightToLeftText(PRBool aIsRTL);
|
||
|
+#endif
|
||
|
virtual PRBool GetRightToLeftText();
|
||
|
-
|
||
|
- virtual nsresult GetClusterInfo(const PRUnichar *aText,
|
||
|
+ NS_METHOD SetRightToLeftText(PRBool aIsRTL);
|
||
|
+
|
||
|
+ NS_METHOD GetClusterInfo(const PRUnichar *aText,
|
||
|
PRUint32 aLength,
|
||
|
PRUint8 *aClusterStarts);
|
||
|
|
||
|
@@ -206,32 +251,35 @@ public:
|
||
|
PRUint32 aLength,
|
||
|
nsPoint aPt);
|
||
|
|
||
|
- virtual nsresult GetRangeWidth(const PRUnichar *aText,
|
||
|
+ NS_METHOD GetRangeWidth(const PRUnichar *aText,
|
||
|
PRUint32 aLength,
|
||
|
PRUint32 aStart,
|
||
|
PRUint32 aEnd,
|
||
|
PRUint32 &aWidth);
|
||
|
|
||
|
- virtual nsresult GetRangeWidth(const char *aText,
|
||
|
+ NS_METHOD GetRangeWidth(const char *aText,
|
||
|
PRUint32 aLength,
|
||
|
PRUint32 aStart,
|
||
|
PRUint32 aEnd,
|
||
|
PRUint32 &aWidth);
|
||
|
|
||
|
// get hints for the font
|
||
|
- static PRUint32 GetHints (void);
|
||
|
+#ifndef PSPANGO
|
||
|
+ static
|
||
|
+#endif
|
||
|
+ PRUint32 GetHints (void);
|
||
|
|
||
|
// drawing surface methods
|
||
|
static nsresult FamilyExists (nsIDeviceContext *aDevice,
|
||
|
const nsString &aName);
|
||
|
|
||
|
+
|
||
|
private:
|
||
|
|
||
|
// generic font metrics class bits
|
||
|
nsCStringArray mFontList;
|
||
|
nsAutoVoidArray mFontIsGeneric;
|
||
|
|
||
|
- nsIDeviceContext *mDeviceContext;
|
||
|
nsCOMPtr<nsIAtom> mLangGroup;
|
||
|
nsCString *mGenericFont;
|
||
|
float mPointSize;
|
||
|
@@ -246,6 +294,9 @@ private:
|
||
|
PangoAttrList *mPangoAttrList;
|
||
|
PRBool mIsRTL;
|
||
|
|
||
|
+#ifndef PSPANGO
|
||
|
+ nsIDeviceContext *mDeviceContext;
|
||
|
+
|
||
|
// Cached font metrics
|
||
|
nscoord mXHeight;
|
||
|
nscoord mSuperscriptOffset;
|
||
|
@@ -263,6 +314,7 @@ private:
|
||
|
nscoord mMaxDescent;
|
||
|
nscoord mMaxAdvance;
|
||
|
nscoord mSpaceWidth;
|
||
|
+#endif
|
||
|
nscoord mPangoSpaceWidth;
|
||
|
nscoord mAveCharWidth;
|
||
|
PRInt32 mMaxStringLength;
|
||
|
@@ -274,13 +326,14 @@ private:
|
||
|
static PRBool EnumFontCallback(const nsString &aFamily,
|
||
|
PRBool aIsGeneric, void *aData);
|
||
|
|
||
|
- void DrawStringSlowly(const gchar *aText,
|
||
|
- const PRUnichar *aOrigString,
|
||
|
- PRUint32 aLength,
|
||
|
- GdkDrawable *aDrawable,
|
||
|
- GdkGC *aGC, gint aX, gint aY,
|
||
|
- PangoLayoutLine *aLine,
|
||
|
- const nscoord *aSpacing);
|
||
|
+ void ApplySpacing(const gchar *aText,
|
||
|
+ PRUint32 aLength,
|
||
|
+ PangoLayoutLine *aLine,
|
||
|
+ const nscoord *aSpacing);
|
||
|
+ void ApplySpacing(const PRUnichar *aText,
|
||
|
+ PRUint32 aLength,
|
||
|
+ PangoLayoutLine *aLine,
|
||
|
+ const nscoord *aSpacing);
|
||
|
|
||
|
nsresult GetTextDimensionsInternal(const gchar* aString,
|
||
|
PRInt32 aLength,
|
||
|
@@ -289,10 +342,20 @@ private:
|
||
|
PRInt32 aNumBreaks,
|
||
|
nsTextDimensions& aDimensions,
|
||
|
PRInt32& aNumCharsFit,
|
||
|
- nsTextDimensions& aLastWordDimensions,
|
||
|
- nsRenderingContextGTK *aContext);
|
||
|
+ nsTextDimensions& aLastWordDimensions
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+#ifdef MOZ_MATHML
|
||
|
+ void GetBoundingMetricsInternal(PangoLayout *aLayout,
|
||
|
+ nsBoundingMetrics &aBoundingMetrics
|
||
|
+ CONTEXT_ARG_DEF);
|
||
|
+#endif /* MOZ_MATHML */
|
||
|
+
|
||
|
+ void FixupSpaceWidths (PangoLayout *aLayout);
|
||
|
|
||
|
- void FixupSpaceWidths (PangoLayout *aLayout, const char *aString);
|
||
|
+ PangoLayout* GetLayout (const PRUnichar* aText,
|
||
|
+ PRUint32 aLength);
|
||
|
+ PangoLayout* GetLayout (const gchar* aText,
|
||
|
+ PRInt32 aLength);
|
||
|
};
|
||
|
|
||
|
class nsFontEnumeratorPango : public nsIFontEnumerator
|