Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
translating every text layer
#1
Hi there,
I made this script that translates every text layer of the layer group selected, or the text layer selected.
As you can see  at lines 50 to 54, I had to skip the text layers witches text are None because it generated an error and interrupted the script. But I don't understand why some of my text layers return the None value when I ask them their text. All those text layers have a text. Any clue anyone ?

Code:
               # lines 50 to 54
                # Vérifier si le texte n'est pas de type NoneType
                if text is not None:
                    # Traduire le texte en utilisant la bibliothèque translate
                    translated_text = translator.translate(text)
                else:
                    translated_text = "ERREUR TEXTE NON TRADUIT\n{}".format(text)


Another question I have is about the libraries I had to include. I added them directly next to the .py that needed them and it works. But I guess it is not the elegant way of doing so. If I do another script needing the same libraries, I would have to include them once more. Is there a way to include libraries in the gimp specific python installation ?
the dependencies are as followed :
traducion.py (my script) needs translate
translate.py needs providers
providers/mymemory_translated.py needs requests

the main script is :

Code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from gimpfu import *
from translate import Translator

def translate_text_layers(image, drawable):
    image.undo_group_start()
    
    translator = Translator(to_lang="fr", from_lang="es")
    print("------------start--------------")
    pdb.gimp_message_set_handler(MESSAGE_BOX)
    
    # Recuperation du groupe de calques selectionne
    group = pdb.gimp_image_get_active_layer(image)
    
    # Récupération du groupe de calques "text-fr" s'il existe
    group_text = None
    for layer in image.layers:
        if pdb.gimp_item_is_group(layer) and pdb.gimp_item_get_name(layer) == "text-fr":
            group_text = layer
            break

    if group_text is None:
        # Créer un groupe de calques nommé "text-fr" s'il n'existe pas
        group_text = pdb.gimp_layer_group_new(image)
        pdb.gimp_item_set_name(group_text, "text-fr")
        pdb.gimp_image_insert_layer(image, group_text, None, 0)

    if pdb.gimp_item_is_group(group):
        print("----------layer is group-------------")
        # Creer un groupe de calques nommé "text-fr"
        group_text = pdb.gimp_layer_group_new(image)
        pdb.gimp_item_set_name(group_text, "text-fr")
        pdb.gimp_image_insert_layer(image, group_text, None, 0)
        # Parcours de tous les calques du groupe
        for layer in group.layers:
            # Vérification si le calque est de type texte
            if pdb.gimp_item_is_text_layer(layer):
                # Dupliquer le calque
                duplicate = pdb.gimp_layer_copy(layer, True)
                
                # Ajouter le calque duplique au groupe "text-fr"
                pdb.gimp_image_insert_layer(image, duplicate, group_text, 0)
                
                # Récupérer le contenu texte du calque dupliqué
                text = pdb.gimp_text_layer_get_text(duplicate)
                
                # Vérifier si le texte n'est pas de type NoneType
                if text is not None:
                    # Traduire le texte en utilisant la bibliothèque translate
                    translated_text = translator.translate(text)
                else:
                    translated_text = "ERREUR TEXTE NON TRADUIT\n{}".format(text)

                
                # Mettre à jour le texte du calque dupliqué avec la traduction
                pdb.gimp_text_layer_set_text(duplicate, translated_text)

        # Actualisation de l'affichage de l'image
        pdb.gimp_displays_flush()
    elif pdb.gimp_item_is_text_layer(group):
        print("----------layer is text-------------")
        # Si le calque sélectionné est un calque de texte
        # Dupliquer le calque
        duplicate = pdb.gimp_layer_copy(group, True)
        
        # Ajouter le calque duplique au groupe "text-fr"
        pdb.gimp_image_insert_layer(image, duplicate, group_text, 0)
        
        # Récupérer le contenu texte du calque dupliqué
        text = pdb.gimp_text_layer_get_text(duplicate)
        
        # Traduire le texte en utilisant la bibliothèque translate
        translated_text = translator.translate(text)
        
        # Mettre à jour le texte du calque dupliqué avec la traduction
        pdb.gimp_text_layer_set_text(duplicate, translated_text)
    else:
        print("--------Veuillez sélectionner un groupe de calques ou un calque de texte.----")
        gimp.message("Veuillez sélectionner un groupe de calques ou un calque de texte.")
    
    pdb.gimp_message("fini")
    # restore stuff
    image.undo_group_end()

register(
    "python-fu-translate-text-layers",
    "Translate text layers from French to English",
    "Translate text layers from French to English",
    "Your Name",
    "Your Name",
    "2023",
    "<Image>/Filters/Language/Translate Text Layers",
    "*",
    [],
    [],
    translate_text_layers)

main()


Attached Files
.zip   traduction.zip (Size: 470.93 KB / Downloads: 343)
Reply
#2
Can you share an example XCF? I can't see why/how a text layer wouldn't return text.

For the modules, you can either install them in your Gimp python runtime (if you are the sole user, because it will be complicated to ask the random Gimp user to do this), or you put them in the same directory as you script file, and alter the python path  sys.path to include the path to you scripts (sys.argv[0]) before you do the import:

So you have a directory in the Gimp  plug-ins directory that bears the name of your plugin (same name as the main Python file)

Code:
importModule/
├── importModule.py
└── someModule.py

Main plugin file (importModule.py)

Code:
#! /bin/env python

import os,sys

whereIAm=os.path.dirname(sys.argv[0]) # find location of executed file

sys.path.append(whereIAm) # add to Python path
import someModule         # we can now import a module that's there

print(someModule.variable)

Imported library (someModule.py):

Code:
variable="imported OK"  # Just to check we access this OK, so module as imported OK
Reply
#3
(07-13-2023, 06:51 AM)Ofnuts Wrote: Can you share an example XCF? I can't see why/how a text layer wouldn't return text.
Yes, i finally could shrink my xcf enough to insert it in this forum.


Attached Files
.xcf   test.xcf (Size: 564.21 KB / Downloads: 72)
Reply
#4
They all have text for me:

Code:
➤> for l in group.layers:
...     dup=pdb.gimp_layer_copy(l, True)
...     pdb.gimp_image_insert_layer(image, dup, group, 0)
...     print "%s,%r" % (dup, pdb.gimp_text_layer_get_text(dup))
...     gimp.delete(dup)
...    
...
<gimp.Layer 'ils le feront. Ils n'ont pas l... #1'>,"ils le feront. Ils n'ont pas le choix."
<gimp.Layer 'j'envoie le rapport d'aujourd'... #1'>,"j'envoie le rapport d'aujourd' hui, exigeant une r\xc3\xa9ponse rapide."
<gimp.Layer 'Et la réunion ? quand est-elle... #1'>,'Et la r\xc3\xa9union\xc2\xa0? quand est-elle reprogramm\xc3\xa9e\xc2\xa0?'
<gimp.Layer 'nos hôtes vous proposeront dem... #1'>,'nos h\xc3\xb4tes vous proposeront demain \xc3\xa0 8h'
<gimp.Layer 'parfait. plus vite c'est mieux #1'>,"parfait. plus vite c'est mieux"
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #7'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'mpfff ! #1'>,'mpfff\xc2\xa0!'
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #8'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'qu'on me vire #1'>,"qu'on me vire"
<gimp.Layer 'LOL. #1'>,'LOL.'
<gimp.Layer 'pour ce qui m'importe #1'>,"pour ce qui m'importe"
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #9'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #10'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'nous avons gagné du temps. vou... #1'>,"nous avons gagn\xc3\xa9 du temps. vous l'avez dit vous-m\xc3\xaame"
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #11'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #12'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'ERREUR TEXTE NON TRADUIT ... #13'>,'ERREUR TEXTE NON TRADUIT\nNone'
<gimp.Layer 'j'essaierai de ne pas vous dér... #1'>,"j'essaierai de ne pas vous d\xc3\xa9ranger. profitez de votre promenade"
<gimp.Layer 'merci #1'>,'merci'
<gimp.Layer 'accompagné de Rodolphe #1'>,'accompagn\xc3\xa9 de Rodolphe'
<gimp.Layer '-- Oui, monsieur. #1'>,'-- Oui, monsieur.'
<gimp.Layer 'contact radio #2'>,'contact radio'
<gimp.Layer 'le palais de justice ne peut l... #1'>,"le palais de justice ne peut l'ignorer."
➤>
Now it seems this is the state of the image **after** the translation script and not before.

Also, the input text has "tweaks" since some letters are bigger than others (ó and í), and if I use local tweaks in the text (for instance I enlarged "gagné") then this makes the text unrecoverable:
Code:
➤> for l in group.layers:
...     print "%s,%r" % (l, pdb.gimp_text_layer_get_text(l))
...
<gimp.Layer 'contact radio #2'>,'contact radio'
<gimp.Layer 'accompagné de Rodolphe #1'>,'accompagn\xc3\xa9 de Rodolphe'
<gimp.Layer 'merci #1'>,'merci'
<gimp.Layer 'nous avons gagné du temps. vou...'>,None
<gimp.Layer 'pour ce qui m'importe #1'>,"pour ce qui m'importe"
<gimp.Layer 'LOL. #1'>,'LOL.'
<gimp.Layer 'qu'on me vire #1'>,"qu'on me vire"
<gimp.Layer 'mpfff ! #1'>,'mpfff\xc2\xa0!'
But when the text is "complex" it can be retrieved with pdb.gimp_text_layer_get_markup():
Code:
➤> for l in group.layers:
...     text=pdb.gimp_text_layer_get_text(l)
...     if text:
...         print "%s,%r" % (l,text)
...     else:
...         print "%s,%r" % (l, pdb.gimp_text_layer_get_markup(l))
...
<gimp.Layer 'contact radio #2'>,'contact radio'
<gimp.Layer 'accompagné de Rodolphe #1'>,'accompagn\xc3\xa9 de Rodolphe'
<gimp.Layer 'merci #1'>,'merci'
<gimp.Layer 'nous avons gagné du temps. vou...'>,'<markup>nous avo<span size="7618">ns </span><span size="22609">gagn\xc3\xa9</span> du temps. vous l&apos;avez dit vous-m\xc3\xaame</markup>'
<gimp.Layer 'pour ce qui m'importe #1'>,"pour ce qui m'importe"
<gimp.Layer 'LOL. #1'>,'LOL.'
<gimp.Layer 'qu'on me vire #1'>,"qu'on me vire"
<gimp.Layer 'mpfff ! #1'>,'mpfff\xc2\xa0!'
➤>    

Of course, you now have to remove the markup.... (and also decode it since it seems to be UTF-8 encoded)
Reply
#5
ok, so there are two functions you can use for a text layer : gimp_text_layer_get_text and gimp_text_layer_get_markup. Either one xor the other returns a string, the other returns None. There is surely a good reason for that beyond my understanding. Now I get the text this way :

Code:
import re  
# Récupérer le contenu texte du calque dupliqué
text = pdb.gimp_text_layer_get_text(layer)
if text is None :
          print ("----------text had markup-------------")
          text = pdb.gimp_text_layer_get_markup(layer)
          # quitter les markups avec une RegEx
          text= re.sub(r"<.*?>", "", text)

And it works ! thanks for the help.

about the encoding in uft-8 I don't understand what you mean. The texts you looked up are the ones returned by the translator tool. They display normally in gimp. Is "r\xc3\xa9ponse" instead of "réponse" uft-8 ? would it be better if it were uft-16 ?
Reply
#6
If your text layer is  "Déjà" and you retrieve the text/markup, you get 'D\xc3\xa9j\xc3\xa0'. The len() of this is 6, when Déjà is 4 characters. This is because what you got is a sequence of bytes which is the UTF-8 encoding of the Unicode representation of "Déjà" , where the  é (U+00E9) and the à (U+00E0) are replaced by their UTF-8 encodings(*), that use two bytes.

This is because in Python2 plain strings are just arrays of bytes. Since Gimp supports the whole Unicode set, when you obtain a text from the Gimp API, Gimp returns the UTF-8 encoding of the text.

Python2 however supports text that uses all Unicode characters, using the  unicode type, and you can convert string to unicode and vice-versa using decode() and encode() methods.

So, if we go back to the text layer, and do pdb.gimp_text_layer_get_text(layer).decode('utf-8'), we get a unicode object that has a length of 4 and is u'D\xe9j\xe0', so non ASCII characters are replaced by their Unicode encoding that fits a single element of the sequence.



(*) technically,  the whole string is encoded in UTF-8, but, by design, the plain ASCII characters (up to 0x7F) are UTF-8 encoded by themselves so when you are only concerned by American English not handling UTF-8 sort of works.
Reply
#7
Im not sure I got it right. The function gimp_text_layer_get_text will return a uft-8 text, and it's better to decode it before using it with the function translator.translate(text) ? According to its documentation, the translator function returns a unicode text. Should I re-code it in uft-8 before using the gimp_text_layer_set_text function ? the code for getting the text, translate it and update the layer's text would look like this :


Code:
            # Récupérer le contenu texte du calque dupliqué
            text = pdb.gimp_text_layer_get_text(duplicate)
            if text is None :
                print ("----------text had markup-------------")
                text = pdb.gimp_text_layer_get_markup(duplicate)
                # quitter les markups avec une RegEx
                text= re.sub(r"<.*?>", "", text)
        
            # essayer de traduir
            try:
                text = text.decode("uft-8")
                translated_text = translator.translate(text).encode("uft-8")
            except Exception as e:
                translated_text = "Erreur lors de la traduction : {}".format(e)

            # Mettre à jour le texte du calque dupliqué avec la traduction
            pdb.gimp_text_layer_set_text(duplicate, translated_text)
Reply
#8
You have to decode the values returned for both text and markup.

No need to re-encode, if function to set the text will check the type of the parameter (plain string\ or \unicode and do the Right Thing.

But to be correct most of your code should be unicode-aware (the parts that tread the translations from file, for instance).
Reply


Forum Jump: