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:
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.