Add BiDi infrastructure

Shachar Shemesh wine-devel at sun.consumer.org.il
Sun Aug 18 13:13:24 CDT 2002


Alexandre Julliard wrote:

>If you think using the external library is the way to go, fine, do it
>that way and submit a patch. Then we can start really discussing
>it. And don't worry about configure checks or packaging issues; we'll
>deal with that later on.
>  
>
No actual patch, yet, as I still have a few heap corruptions in certain 
cases. Attached, however, is the preliminary "Wine with external 
libfribidi". Do your worst, come back with an opinion. This is not, yet, 
cleaned up, but the interface is a pretty good representation of what 
the final version is going to be.

As a bynote, I will add that it seems that libfribidi may not be as 
mature as I have hoped, and if problems keep popping up, I will put it 
into our code myself (or, more preciseley, try). It will not be an easy 
task, as it currently carries it's own unicode tables, configure 
scripts, and so on. Since you vulenteered to help with this.... ;-).

            Shachar


-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/sun/sources/wine/cvs/wine/configure.ac,v
retrieving revision 1.70
diff -u -r1.70 configure.ac
--- configure.ac	15 Aug 2002 22:08:41 -0000	1.70
+++ configure.ac	16 Aug 2002 06:44:27 -0000
@@ -434,6 +434,18 @@
 fi
 AC_SUBST(FREETYPEINCL)
 
+dnl **** Check for fribidi ****
+# AC_CHECK_LIB(libfribidi,fribidi_log2vis,fribidi_lib=yes,fribidi_lib=no,)
+AC_CHECK_HEADER([fribidi/fribidi.h],fbd_lib=yes,fbd_lib=no)
+if test "$fbd_lib"="yes"
+then
+	AC_DEFINE(HAVE_FRIBIDI,1)
+	AC_DEFINE(HAVE_FRIBIDI_H,1)
+else		
+	AC_DEFINE(HAVE_FRIBIDI,0)
+	AC_DEFINE(HAVE_FRIBIDI_H,0)
+fi
+
 dnl **** Check for parport (currently Linux only) ****
 AC_CACHE_CHECK([for parport header/ppdev.h], ac_cv_c_ppdev,
  AC_TRY_COMPILE(
Index: dlls/gdi/Makefile.in
===================================================================
RCS file: /home/sun/sources/wine/cvs/wine/dlls/gdi/Makefile.in,v
retrieving revision 1.32
diff -u -r1.32 Makefile.in
--- dlls/gdi/Makefile.in	16 Aug 2002 00:42:06 -0000	1.32
+++ dlls/gdi/Makefile.in	16 Aug 2002 06:44:59 -0000
@@ -42,6 +42,7 @@
 	enhmfdrv/mapping.c \
 	enhmfdrv/objects.c \
 	freetype.c \
+	fribidi.c \
 	gdi16.c \
 	gdi_main.c \
 	mfdrv/bitblt.c \
Index: include/config.h.in
===================================================================
RCS file: /home/sun/sources/wine/cvs/wine/include/config.h.in,v
retrieving revision 1.127
diff -u -r1.127 config.h.in
--- include/config.h.in	9 Aug 2002 19:49:32 -0000	1.127
+++ include/config.h.in	14 Aug 2002 18:48:23 -0000
@@ -113,6 +113,12 @@
 /* Define to 1 if you have the <freetype/tttables.h> header file. */
 #undef HAVE_FREETYPE_TTTABLES_H
 
+/* Define to 1 if fribidi is available during compilation */
+#undef HAVE_FRIBIDI
+
+/* Define to 1 if you have the <fribidi/fribidi.h> header file. */
+#undef HAVE_FRIBIDI_H
+
 /* Define to 1 if you have the `ftruncate' function. */
 #undef HAVE_FTRUNCATE
 
Index: objects/gdiobj.c
===================================================================
RCS file: /home/sun/sources/wine/cvs/wine/objects/gdiobj.c,v
retrieving revision 1.78
diff -u -r1.78 gdiobj.c
--- objects/gdiobj.c	16 Aug 2002 00:42:07 -0000	1.78
+++ objects/gdiobj.c	16 Aug 2002 06:44:28 -0000
@@ -633,6 +633,10 @@
 
     WineEngInit();
 
+#if HAVE_FRIBIDI
+    WineBidiInit();
+#endif
+
     return TRUE;
 }
 
Index: objects/text.c
===================================================================
RCS file: /home/sun/sources/wine/cvs/wine/objects/text.c,v
retrieving revision 1.49
diff -u -r1.49 text.c
--- objects/text.c	16 Aug 2002 00:42:07 -0000	1.49
+++ objects/text.c	18 Aug 2002 17:55:07 -0000
@@ -28,6 +28,7 @@
 #include "gdi.h"
 #include "wine/debug.h"
 #include "winnls.h"
+#include "bidi.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(text);
 
@@ -158,29 +159,20 @@
 		else if(dc->funcs->pExtTextOut)
 		{
 			DWORD fontLangInfo=0;
-			if( !(flags&(ETO_GLYPH_INDEX|ETO_IGNORELANGUAGE)) &&
-			   ((fontLangInfo=GetFontLanguageInfo( hdc ))&(GCP_REORDER|GCP_GLYPHSHAPE)) )
+			if( !bidi_available && (!(flags&(ETO_GLYPH_INDEX|ETO_IGNORELANGUAGE)) &&
+			   ((fontLangInfo=GetFontLanguageInfo( hdc ))&(GCP_REORDER|GCP_GLYPHSHAPE))) )
 			{
 				/* The caller did not specify that language processing was already done,
 				 * and the font idetifies iteself as requiring language processing.
 				 */
-				GCP_RESULTSW gcp;
+				WCHAR *lpOutString=HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR));
 
-				gcp.lStructSize=sizeof(gcp);
-				gcp.lpOutString=HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR));
-				gcp.lpOrder=NULL;
-				gcp.lpDx=NULL;
-				gcp.lpCaretPos=NULL;
-				gcp.lpClass=NULL;
-				gcp.lpGlyphs=NULL;
-				gcp.nGlyphs=0;
-				gcp.nMaxFit=0;
-
-				GetCharacterPlacementW(hdc, str, count, 0, &gcp, GCP_REORDER );
+				/* XXX need to support explicit requests for LTR or RTL */
+				BIDI_log2vis(str, count, 0, lpOutString, NULL, NULL, NULL);
 
 				ret = dc->funcs->pExtTextOut(dc->physDev,x,y,flags|ETO_IGNORELANGUAGE,
-					lprect,gcp.lpOutString,count,lpDx);
-				HeapFree(GetProcessHeap(), 0, gcp.lpOutString);
+					lprect,lpOutString,count,lpDx);
+				HeapFree(GetProcessHeap(), 0, lpOutString);
 			} else
 				ret = dc->funcs->pExtTextOut(dc->physDev,x,y,flags,lprect,str,count,lpDx);
 		}
--- /dev/null	2002-07-08 21:54:59.000000000 +0300
+++ dlls/gdi/fribidi.c	2002-08-18 20:49:35.000000000 +0300
@@ -0,0 +1,372 @@
+/*
+ * The main BiDirectional functions, and interface to FriBiDi
+ *
+ * Copyright 2002 Shachar Shemesh.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#if HAVE_FRIBIDI
+
+#include "windef.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wingdi.h"
+#include "wine/unicode.h"
+#include "wine/port.h"
+#include "wine/debug.h"
+#include "gdi.h"
+#include "font.h"
+
+#include "bidi.h"
+#if HAVE_FRIBIDI_H
+#include <fribidi/fribidi.h>
+#endif /* HAVE_FRIBIDI_H */
+
+#include <limits.h>
+
+#ifndef SONAME_LIBFRIBIDI
+#define SONAME_LIBFRIBIDI "libfribidi.so"
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(text);
+
+/*
+ * A global variable stating whether runtime support for BiDi
+ * is available
+ */
+BOOL bidi_available=FALSE;
+
+static void *fbd_handle = NULL;
+
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
+
+MAKE_FUNCPTR(fribidi_log2vis)
+MAKE_FUNCPTR(fribidi_remove_bidi_marks)
+MAKE_FUNCPTR(fribidi_mirroring_status)
+MAKE_FUNCPTR(fribidi_set_mirroring)
+
+
+/*
+ * Load the fribidi library, and initialize the "bidi_available" var.
+ */
+BOOL WineBidiInit(void)
+{
+	if( bidi_available || fbd_handle!=NULL )
+	{
+		WARN("WineBidiInit called after already being initialized\n");
+
+		return TRUE;
+	}
+
+	fbd_handle = wine_dlopen(SONAME_LIBFRIBIDI, RTLD_NOW, NULL, 0 );
+	if( !fbd_handle ) {
+		TRACE("Wine cannot find the FriBiDi library. Wine will not have Bidirectional\n"
+		      "support. To gain BiDirectional support, install libfribidi (available\n"
+		      "as a package to your system, or from http://fribidi.sourceforge.net)\n"
+		     );
+
+		return FALSE;
+	}
+
+#define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(fbd_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
+	
+	LOAD_FUNCPTR(fribidi_log2vis)
+	LOAD_FUNCPTR(fribidi_remove_bidi_marks)
+	LOAD_FUNCPTR(fribidi_mirroring_status)
+	LOAD_FUNCPTR(fribidi_set_mirroring)
+	
+#undef LOAD_FUNCPTR
+
+	bidi_available=TRUE;
+
+	return TRUE;
+
+sym_not_found:
+	WINE_MESSAGE(
+	  "Wine cannot find some of the libfribidi function necessary for proper BiDirectional\n"
+	  "support. Please make sure you are using at least version 0.10.4 of libfribidi. If\n"
+	  "it is not available as a package for your system, you can download it from\n"
+	  "http://fribidi.sf.net/\n"
+		    );
+
+	wine_dlclose(fbd_handle, NULL, 0);
+	fbd_handle=NULL;
+	
+	return FALSE;
+}
+
+#define UTF16_SARROGATE_MASK (0xfc00)
+#define UTF16_SARROGATE_HI_VAL (0xd800)
+#define UTF16_SARROGATE_LO_VAL (0xdc00)
+#define UTF16_SARROGATE_MASK_NOT (0x03ff)
+#define UTF16_SARROGATE_ADJUST (0x10000)
+
+#define UTF16_IS_HI(val) (((val)&UTF16_SARROGATE_MASK)==UTF16_SARROGATE_HI_VAL)
+#define UTF16_IS_LO(val) (((val)&UTF16_SARROGATE_MASK)==UTF16_SARROGATE_LO_VAL)
+
+#define UTF16_HIGH_MARK (0xffff) /* If a value is higher than
+                                    this, it needs to be sarrogated */
+
+#define UTF32_SARROGATE_COMPOSE(hi,lo) ((((FriBidiChar)((hi)&(UTF16_SARROGATE_MASK_NOT)))<<10)+\
+		((lo)&(UTF16_SARROGATE_MASK_NOT))+UTF16_SARROGATE_ADJUST)
+
+#define UTF32_SARROGATE_DECOMPOSE_HI(w) ((((w)-UTF16_SARROGATE_ADJUST)>>10)|UTF16_SARROGATE_HI_VAL)
+#define UTF32_SARROGATE_DECOMPOSE_LO(w) ((((w)-UTF16_SARROGATE_ADJUST)&UTF16_SARROGATE_MASK_NOT)|UTF16_SARROGATE_LO_VAL)
+
+#define UNICODE_LRM	0x200e
+#define UNICODE_RLM 0x200f
+
+/***********************************************************************
+ * 		BIDI_wctoFriBidiChar
+ *
+ * RETURNS
+ * 
+ *  String provided in WCHARs (UTF-16) in str as FriBidiChar (UTF-32),
+ *  for easy use with the FriBidi library. String will always be NULL
+ *  terminated.
+ *
+ *  Will return NULL on error
+ *
+ *  It is up to the caller to free the returned buffer from the process'
+ *  heap.
+ *
+ * ERRORS
+ *
+ * 	ERROR_BAD_LENGTH - count (or string length) is so big that
+ * 	                   calculating number of bytes required results
+ * 	                   in an integer overflow
+ * 	All errors returnable by HeapAlloc
+ *
+ */
+FriBidiChar *BIDI_wctoFribidiChar(
+		LPCWSTR str, /* [in] Source UTF-16 string */
+		INT count, /* [in] Number of characters in str, or -1 if str is NULL
+		              terminated */
+		INT *plen, /* [out] If non-NULL, on return it will point to the number
+		              of UTF-32 characters in the string, including nPrePad
+						and nPostPad */
+		INT nPrePad, /* [in] Number of characters to pad at begining of string. */
+		INT nPostPad /* [in] Number of characters to pad at end of string. */
+		)
+{
+	int nChars=count;
+
+	FriBidiChar *ret=NULL;
+
+	if( count==-1 )
+	{
+		int i;
+
+		for( nChars=0,i=0; str[i]!=0; ++nChars )
+		{
+			if( UTF16_IS_HI(str[i]) )
+			{
+				/* We have the start of a sarrogate pair here. */
+				if( UTF16_IS_LO(str[i+1]) )
+				{
+					++i;
+				}
+			}
+		}
+	}
+
+	/* If we were passed "-1" in count, it will now be the real characters. If not, we
+	 * will just assume that UTF-32 will have the same number of characters as UTF-16.
+	 * We may be allocating more than necessary, but it's cheaper than counting
+	 * sarrogates and then allocating
+	 */
+
+	/* Make sure we won't copy more than we allocate */
+	if( ((DWORD)(nChars+nPrePad+nPostPad+1))>(UINT_MAX/sizeof(FriBidiChar)) )
+	{
+		WARN("BIDI_wctoFribidiChar: Someone tried an interget overflow attack on us\n");
+
+		SetLastError( ERROR_BAD_LENGTH );
+
+		return NULL;
+	}
+
+	ret=(FriBidiChar *)HeapAlloc( GetProcessHeap(), 0, sizeof(FriBidiChar)*(nChars+nPrePad+nPostPad+1));
+
+	if(ret!=NULL)
+	{
+		int len;
+		int i;
+
+		if( plen==NULL )
+			plen=&len;
+
+		for( *plen=0; *plen<nPrePad; ++*plen )
+			ret[*plen]=0;
+
+		if( count==-1 )
+		{
+			for( i=0; str[i]!=0; ++i, ++*plen )
+			{
+				if( !UTF16_IS_HI(str[i]) || !UTF16_IS_LO(str[i+1]) )
+				{
+					ret[*plen]=str[i];
+				} else
+				{
+					ret[*plen]=UTF32_SARROGATE_COMPOSE(str[i], str[i+1]);
+					++i;
+				}
+			}
+		} else /* number of chars given in advance */
+		{
+			for( i=0; i<count; ++i, ++*plen )
+			{
+				if( !UTF16_IS_HI(str[i]) || (i+1)>=count || !UTF16_IS_LO(str[i+1]) )
+				{
+					ret[*plen]=str[i];
+				} else
+				{
+					ret[*plen]=UTF32_SARROGATE_COMPOSE(str[i], str[i+1]);
+					++i;
+				}
+			}
+		}
+
+		for( i=0; i<=nPostPad; ++i, ++*plen )
+			ret[*plen]=0;
+	}
+
+	return ret;
+}
+
+/***********************************************************************
+ * 		BIDI_FribidiChartow
+ *
+ * RETURNS
+ * 
+ *  On success - number of characters copied.
+ *  On failure - zero.
+ *
+ * BUGS
+ * 
+ *  None known.
+ *
+ */
+DWORD BIDI_FribidiChartow(
+		FriBidiChar *src, /* [in] Source UTF-32 string, NULL terminated */
+		LPWSTR dst, /* [out] Buffer to receive the UTF-16 string */
+		INT dstlen	/* [in] Number of characters in "dst" */
+		)
+{
+	DWORD len=0;
+	int i;
+
+	for( i=0; len<dstlen && src[i]!=0; ++i,++len )
+	{
+		if( src[i]<=UTF16_HIGH_MARK )
+		{
+			dst[len]=src[i];
+		} else /* We need to place a sarrogate here */
+		{
+			dst[len]=UTF32_SARROGATE_DECOMPOSE_HI(src[i]);
+			if( (++len)<dstlen )
+			{
+				dst[len]=UTF32_SARROGATE_DECOMPOSE_LO(src[i]);
+			}
+		}
+	}
+
+	if( src[i]!=0 )
+	{
+		SetLastError( ERROR_MORE_DATA );
+		len=0;
+	}
+
+	return len;
+}
+
+/***********************************************************************
+ * 		BIDI_log2vis
+ *
+ * RETURNS
+ * 
+ *  TRUE if succesful, FALSE if not.
+ *
+ * BUGS
+ *
+ *  Only handles what libfribidi knows how to handle, which is not, yet,
+ *  everything. Hopefully, through the magic of dynamic linking, this
+ *  will improve.
+ *
+ *  lpClass is unused at this time.
+ */
+BOOL BIDI_log2vis(
+		LPCWSTR str,	/* [in] Logical order string (wide chars) */
+		INT len,	/* [in] Length of src */
+		DWORD flags,	/* [in] Flags for modifying behaviour */
+		LPWSTR visual_str,	/* [out] The visual order string */
+		UINT *position_L_to_V_list,	/* [out] Array matching logical index
+									   (array index) with the visual location */
+		UINT *position_V_to_L_list,	/* [out] Array matching visual index
+									   with the logical location */
+		BYTE *embedding_level_list	/* [out] Array to be filled with the embedding
+									   levels as defined in the BiDi algorithm.
+									   Even levels mean left-to-right, odd right-to-left */
+		)
+{
+	int len32;
+	int ret=FALSE;
+	FriBidiChar *src32=BIDI_wctoFribidiChar( str, len, &len32, 0, 0 );
+
+	if( src32!=NULL )
+	{
+		FriBidiChar *dst32=(FriBidiChar *)HeapAlloc(GetProcessHeap(), 0, len32*sizeof(FriBidiChar) );
+		FriBidiChar dir;
+
+		switch( flags&BIDI_FLAGS_CONTEXT_MASK )
+		{
+		case BIDI_FLAGS_LTR_CONTEXT:
+			dir=FRIBIDI_TYPE_L;
+			break;
+		case BIDI_FLAGS_RTL_CONTEXT:
+			dir=FRIBIDI_TYPE_R;
+			break;
+		default:
+			dir=FRIBIDI_TYPE_N;
+			break;
+		}
+
+		if( pfribidi_log2vis( src32, len32, &dir, dst32,
+					position_L_to_V_list, position_V_to_L_list,
+					embedding_level_list ) )
+		{
+			BIDI_FribidiChartow( dst32, visual_str, len );
+
+			ret=TRUE;
+		} else
+		{
+			ERR("fribidi_log2vis failed\n");
+		}
+
+		HeapFree( GetProcessHeap(), 0, dst32 );
+		HeapFree( GetProcessHeap(), 0, src32 );
+	} else
+	{
+		ERR("libfribidi_log2vis failed\n");
+	}
+
+	return ret;
+}
+
+#endif /* HAVE_FRIBIDI */
+
--- /dev/null	2002-07-08 21:54:59.000000000 +0300
+++ include/bidi.h	2002-08-17 21:50:42.000000000 +0300
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2002 Shachar Shemesh
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_FRIBIDI
+
+/*
+ * A global variable stating whether runtime support for BiDi
+ * is available
+ */
+extern BOOL bidi_available;
+
+/*
+ * Load the fribidi library, and initialize the "bidi_available" var.
+ */
+BOOL WineBidiInit(void);
+
+/*
+ * Convert logical to visual order
+ */
+BOOL BIDI_log2vis( LPCWSTR str, INT len, DWORD flags, LPWSTR visual_str,
+		UINT *position_L_to_V_list, UINT *position_V_to_L_list, BYTE *embedding_level_list );
+
+/* Don't leave the paragraph context to be guessed */
+#define BIDI_FLAGS_LTR_CONTEXT	0x00000001
+#define BIDI_FLAGS_RTL_CONTEXT	0x00000002
+#define BIDI_FLAGS_CONTEXT_MASK	0x00000003
+
+#else
+/* The optimizer should be able to figure out that an "if(FALSE)" will never
+ * take place. This saves callers from checking the "HAVE_FRIBIDI" macro.
+ */
+#define bidi_available FALSE
+#endif /* HAVE_FRIBIDI */
+


More information about the wine-devel mailing list