Sdl for Farsi or Persian text

I was wondering if there is a way that SDL library could show Farsi or Persian texts.
for more reference Persian text is similar to Arabic.
if so how can I show Persian text with this library?

As far as I’m aware, SDL doesn’t show any text. What you want is a text-rendering library for SDL; which one depends on what sort of font you’re using.

I am using a Persian font but the text it is showing me is all square and question marks

I can output Arabic text without any obvious problems, using SDL 2.0 + SDL2_ttf, so I would be surprised if Persian was any different (apologies if the text doesn’t make much sense!). This is using the DejaVuSans font:

sdl2arabic

SDL renders no text whatsoever. There is a third party (first party?) library by Sam Lantinga, SDL_ttf, that does text rendering into an SDL_Surface. I am unsure how well it works for right-to-left languages like Persian, or even east Asian languages, but if it doesn’t work well, I’d suggest using FreeType and rendering to an SDL_Surface. Text rendering is an absolutely complicated mess and nobody should do it themselves.

thank you so much could you please send your codes for this Arabic text

thank you so much for helping

The code is rather distributed but it’s using SDL2_ttf in a straightforward way. I create the font with TTF_OpenFont() then to render the text I call TTF_RenderUNICODE_Blended() and convert the returned surface to a SDL2 texture using SDL_CreateTextureFromSurface(). Finally the texture is copied to my render target using SDL_RenderCopy().

Depending on the character encoding you are using you may need to substitute TTF_RenderUTF8_Blended() for TTF_RenderUNICODE_Blended(). SDL 2.0 normally uses UTF-8 throughout but it was more convenient to use UTF-16 (actually UCS-2) for the Arabic text in my app.

To render Arabic, Farsi/Persian or others, you need to patch SDL_ttf so that it can use internally a text shaping engine (e.g. HarfBuzz).

I wrote a patch for this :
https://bugzilla.libsdl.org/show_bug.cgi?id=3046
for RTL (Right-To-Left):
update: I’ve double-checked: you don’t need to reverse the string. Unless there is some Latin substring inside that you want to be LTR.

You need to call from the patch:
TTF_SetDirection( HB_DIRECTION_RTL or HB_DIRECTION_LTR)
TTF_SetScript(HB_SCRIPT_ARABIC or other) to tell more precisely which language you are shaping.
(I think there is a function to auto-detect this)

It exists also another patch, but it needs also HarfBuzz and FriBiDi. I haven’t tested it:
https://bugzilla.libsdl.org/show_bug.cgi?id=3211

No, that’s not necessary. I showed the output from my app, which certainly doesn’t use a ‘patched’ version of SDL_ttf! You do however need to choose the correct ‘presentation form’ glyphs depending on the context (so the glyph may depend on the preceding and following characters) which I should have mentioned, but that’s not difficult and can be done external to SDL_ttf.

I’ve just tried SDL_ttf to render again Arabic without HarfBuzz and:

  • you need to reverse the string first
  • you get something not so much disturbed. Clearly missing some ligatures / substitutions.

As you said

So that’s a beginning of text shaping!

If there is only ligatures, this is possible. But have you got all implemented ?
But there is a bunch of them: https://www.w3.org/TR/alreq/ (especially 2.4: joining)

Also, you may think you have the correct glyphs in this sentence, but you may be missing shaping for other words. Also the correct positions and hinting are probably ignored.

I’m not claiming it is “perfect” but then my English rendering isn’t perfect either because I normally ignore kerning for simplicity! I’m pretty sure all the ligatures are implemented because I haven’t had any reports to the contrary from Arabic users, but it is true that one must be careful with font selection because not every Unicode font includes the full set of presentation forms.

Here’s the original Arabic from which the output I posted was rendered; you might like to compare what your browser produces with the SDL rendering:

  هنا مثال يمكنك من الكتابة من اليمين
  الى اليسار باللغة العربية

If you know of a particular Arabic phrase which you suspect may not be rendered ‘acceptably’ by my existing code please list it here and I will try it.

I don’t know Arabic nor its script, to tell exactly where the corner cases are.
I don’t know your code neither.

You have all substitutions rules in GPOS / GSUB font tables ( https://fontforge.github.io/gposgsub.html ).
For instance, if you have only implemented “Ligatures Substitution”, you’ll have issues with “Contextual substitution” and “Chaining Contextual Substitution”.

Probably it’s only ‘Ligatures Substitution’ but it would need somebody who knows the language to say how important the other features are. Realistically I don’t have the option of patching SDL2_ttf because my app is multi-platform and on some of those platforms (notably Linux/86 and Raspberry Pi) it uses the standard SDL2_ttf library downloaded from the distro’s repository.

Ultimately it’s up to the OP to decide how critical some of the more subtle features of Arabic (or Farsi) are to his application, and therefore whether the relatively simple approach I’ve adopted will meet his needs or not.

I am trying this today for 7 complete hours , went thro all all sdl related functions and tens of github codes, not working…

This is my code ; sorry to annoy you with along code, just wanned to be precise, using last version of everything ( including the IDE hh ) and using same font as yours

#define SDL_MAIN_HANDLED
//Using SDL, SDL_image, SDL_ttf, standard IO, math, and strings
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <stdio.h>
#include <string>
#include <cmath>

//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

//Texture wrapper class
class LTexture
{
	public:
		//Initializes variables
		LTexture();

		//Deallocates memory
		~LTexture();

		//Loads image at specified path
		bool loadFromFile( std::string path );

		//Creates image from font string
		bool loadFromRenderedText( std::string textureText, SDL_Color textColor );

		//Deallocates texture
		void free();

		//Set color modulation
		void setColor( Uint8 red, Uint8 green, Uint8 blue );

		//Set blending
		void setBlendMode( SDL_BlendMode blending );

		//Set alpha modulation
		void setAlpha( Uint8 alpha );

		//Renders texture at given point
		void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );

		//Gets image dimensions
		int getWidth();
		int getHeight();

	private:
		//The actual hardware texture
		SDL_Texture* mTexture;

		//Image dimensions
		int mWidth;
		int mHeight;
};

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The window renderer
SDL_Renderer* gRenderer = NULL;

//Globally used font
TTF_Font *gFont = NULL;

//Rendered texture
LTexture gTextTexture;


LTexture::LTexture()
{
	//Initialize
	mTexture = NULL;
	mWidth = 0;
	mHeight = 0;
}

LTexture::~LTexture()
{
	//Deallocate
	free();
}

bool LTexture::loadFromFile( std::string path )
{
	//Get rid of preexisting texture
	free();

	//The final texture
	SDL_Texture* newTexture = NULL;

	//Load image at specified path
	SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
	if( loadedSurface == NULL )
	{
		printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
	}
	else
	{
		//Color key image
		SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0xFF, 0xFF ) );

		//Create texture from surface pixels
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
		if( newTexture == NULL )
		{
			printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
		}
		else
		{
			//Get image dimensions
			mWidth = loadedSurface->w;
			mHeight = loadedSurface->h;
		}

		//Get rid of old loaded surface
		SDL_FreeSurface( loadedSurface );
	}

	//Return success
	mTexture = newTexture;
	return mTexture != NULL;
}

bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor )
{
	//Get rid of preexisting texture
	free();

	//Render text surface
	SDL_Surface* textSurface = TTF_RenderUTF8_Blended( gFont,(const char*) "الحب الكبير ", textColor );
	if( textSurface == NULL )
	{
		printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
	}
	else
	{
		//Create texture from surface pixels
        mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
		if( mTexture == NULL )
		{
			printf( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() );
		}
		else
		{
			//Get image dimensions
			mWidth = textSurface->w;
			mHeight = textSurface->h;
		}

		//Get rid of old surface
		SDL_FreeSurface( textSurface );
	}

	//Return success
	return mTexture != NULL;
}

void LTexture::free()
{
	//Free texture if it exists
	if( mTexture != NULL )
	{
		SDL_DestroyTexture( mTexture );
		mTexture = NULL;
		mWidth = 0;
		mHeight = 0;
	}
}

void LTexture::setColor( Uint8 red, Uint8 green, Uint8 blue )
{
	//Modulate texture rgb
	SDL_SetTextureColorMod( mTexture, red, green, blue );
}

void LTexture::setBlendMode( SDL_BlendMode blending )
{
	//Set blending function
	SDL_SetTextureBlendMode( mTexture, blending );
}

void LTexture::setAlpha( Uint8 alpha )
{
	//Modulate texture alpha
	SDL_SetTextureAlphaMod( mTexture, alpha );
}

void LTexture::render( int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip )
{
	//Set rendering space and render to screen
	SDL_Rect renderQuad = { x, y, mWidth, mHeight };

	//Set clip rendering dimensions
	if( clip != NULL )
	{
		renderQuad.w = clip->w;
		renderQuad.h = clip->h;
	}

	//Render to screen
	SDL_RenderCopyEx( gRenderer, mTexture, clip, &renderQuad, angle, center, flip );
}

int LTexture::getWidth()
{
	return mWidth;
}

int LTexture::getHeight()
{
	return mHeight;
}

bool init()
{
	//Initialization flag
	bool success = true;

	//Initialize SDL
	if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
	{
		printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
		success = false;
	}
	else
	{
		//Set texture filtering to linear
		if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
		{
			printf( "Warning: Linear texture filtering not enabled!" );
		}

		//Create window
		gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
		if( gWindow == NULL )
		{
			printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
			success = false;
		}
		else
		{
			//Create vsynced renderer for window
			gRenderer = SDL_CreateRenderer( gWindow, -1, 0 );
			if( gRenderer == NULL )
			{
				printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
				success = false;
			}
			else
			{
				//Initialize renderer color
				SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

				//Initialize PNG loading
				int imgFlags = IMG_INIT_PNG;
				if( !( IMG_Init( imgFlags ) & imgFlags ) )
				{
					printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
					success = false;
				}

				 //Initialize SDL_ttf
				if( TTF_Init() == -1 )
				{
					printf( "SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError() );
					success = false;
				}
			}
		}
	}

	return success;
}

bool loadMedia()
{
	//Loading success flag
	bool success = true;

	//Open the font
	gFont = TTF_OpenFont( "fonts/DejaVuSans.ttf", 28 );
	if( gFont == NULL )
	{
		printf( "Failed to load lazy font! SDL_ttf Error: %s\n", TTF_GetError() );
		success = false;
	}
	else
	{
		//Render text
		SDL_Color textColor = { 0, 0, 0 };
		if( !gTextTexture.loadFromRenderedText( "The quick brown fox jumps over the lazy dog", textColor ) )
		{
			printf( "Failed to render text texture!\n" );
			success = false;
		}
	}

	return success;
}

void close()
{
	//Free loaded images
	gTextTexture.free();

	//Free global font
	TTF_CloseFont( gFont );
	gFont = NULL;

	//Destroy window
	SDL_DestroyRenderer( gRenderer );
	SDL_DestroyWindow( gWindow );
	gWindow = NULL;
	gRenderer = NULL;

	//Quit SDL subsystems
	TTF_Quit();
	IMG_Quit();
	SDL_Quit();
}

int main( int argc, char* args[] )
{
	//Start up SDL and create window
	if( !init() )
	{
		printf( "Failed to initialize!\n" );
	}
	else
	{
		//Load media
		if( !loadMedia() )
		{
			printf( "Failed to load media!\n" );
		}
		else
		{
			//Main loop flag
			bool quit = false;

			//Event handler
			SDL_Event e;

			//While application is running
			while( !quit )
			{
				//Handle events on queue
				while( SDL_PollEvent( &e ) != 0 )
				{
					//User requests quit
					if( e.type == SDL_QUIT )
					{
						quit = true;
					}
				}

				//Clear screen
				SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
				SDL_RenderClear( gRenderer );

				//Render current frame
				gTextTexture.render( ( SCREEN_WIDTH - gTextTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gTextTexture.getHeight() ) / 2 );

				//Update screen
				SDL_RenderPresent( gRenderer );
			}
		}
	}

	//Free resources and close SDL
	close();

	return 0;
}

What i get

This looks like the issue discussed earlier in the thread. You either need to use a text shaping engine as suggested by Sylvain or substitute the appropriate Presentation Forms as I do (which may not be perfect). My reference was this table but it is for Arabic not for Farsi.

If you can find a similar table for Farsi, and if the required Presentation Forms are available in the font you are using, it is a case of scanning your text string and substituting the appropriate ‘isolated’, ‘beginning’, ‘middle’ and ‘end’ glyphs according to context.

My code is in BASIC so probably wouldn’t be useful to you, but it is not very complicated. However if you need ‘perfect’ Arabic (or Farsi) it would be better to use a proper text shaping engine.