2017-08-11 2 views
1

Ce que je suis en train d'accomplir:python-docx add_style avec le langage CTL (de mise en page de texte complexe)

  • Créer un style de paragraphe en python-docx avec la police persane définie par l'utilisateur et la taille (une langue CTL)

problème:

  • je peux le faire avec langues non-CTL comme l'anglais:

    from docx import Document 
    from docx.enum.style import WD_STYLE_TYPE 
    from docx.shared import Pt 
    
    user_font_name = 'FreeMono' 
    user_font_size = 14 
    
    doc = Document() 
    my_style = doc.styles.add_style('style_name',WD_STYLE_TYPE.PARAGRAPH) 
    my_font = my_style.font 
    my_font.name = user_font_name 
    my_font.size = Pt(user_font_size) 
    p = doc.add_paragraph('some text',my_style) 
    
    # persian_p = doc.add_paragraph('نوشته',my_style) 
    # FreeMono supports Persian language so the problem is not the font 
    
    doc.save('file.docx') 
    
  • Cependant, si je change le texte à un texte persan, sa police ne changera pas à la police spécifiée.

Pourquoi cela se produit:

  • Ma police spécifiée ne change que la famille de polices occidentale de style et ne fait rien à la famille de polices CTL

Comment je sais :

  • Si j'ouvre le fichier docx avec LibreOffice et ouvre le style et va dans la section de police, je peux voir que la police et la taille spécifiées sont dans "Western Text Font Family" mais pas dans "CTL Font Family". Et par conséquent ma police de texte CTL devient la police par défaut.

Informations complémentaires:

  1. J'utilise LibreOffice sous Linux
  2. Modification du style par défaut ne me fera pas bien dans cette situation parce que je veux que l'utilisateur de spécifier un nom de police et taille.
  3. Je n'ai aucune expérience dans le changement des fichiers xml (encore moins docx fichiers xml)
  4. la version python-docx est 0.8.6

Répondre

1

Après de nombreuses heures farfouillé le fichier docx je me suis rendu à ma grande horreur, que la réponse a menti dans le fichier style.xml du document. Voici une sorte de moyen de le réparer pour les personnes ayant des problèmes similaires:

Problèmes avec l'orientation du texte:

  • Si vous avez déjà saisi en arabe ou en persan que vous pourriez avoir vu que l'alignement du texte à droite à gauche ne résout pas tous vos problèmes. Parce que si vous ne changez pas la direction du texte, le curseur et les signes de ponctuation restent à l'extrême droite de l'écran (au lieu de suivre la dernière lettre) et il n'y a pas de justification à droite si vous en avez besoin. Maintenant, parce que je ne pouvais pas changer la direction du texte dans python-docx même en changeant la valeur "textDirection" de document.xml de 'lrTb' (Left-Right/Top-Bottom) en 'rlTb', j'ai dû créer un document avec LibreOffice et changez son style de paragraphe par défaut ('Normal') à ce que j'avais en tête (direction du texte rtl, etc).Cela permet d'économiser beaucoup de temps plus tard car vous n'avez pas besoin de le faire en python.

explication Xml du problème de changement de police:

  • Le document avec style par défaut modifié montre deux choses différentes dans son fichier style.xml. Dans le style de paragraphe normal sous "w: rPr", vous pouvez voir qu'il y a un "w: szCs" supplémentaire qui détermine la taille de la police de script complexe (que vous ne pouvez pas changer en changeant style.font.size) et dans " w: rFonts "la valeur pour" cs "est maintenant ma police perse spécifiée. Aussi la valeur "w: lang", "bidi", est maintenant "fa-IR" (pour le persan). Voici la partie xml Je parle:

    <w:rPr> 
    <w:rFonts w:ascii="FreeMono" w:hAnsi="FreeMono" w:cs="FreeFarsi"/> 
    <w:sz w:val="40"/> 
    <w:rtl/> 
    <w:cs/> 
    <w:szCs w:val="40"/> 
    <w:lang w:val="en-Us" w:bidi="fa-IR"/> 
    </w:rPr> 
    
  • maintenant changer le style.font.size ne change de valeur « sz » (taille de la police occidentale) et ne fait rien à la valeur de « szCs » (cs taille de police). De même, style.font.name ne modifie que les valeurs "ascii" et "hAnsi" de "w: rFonts" et ne fait rien à la valeur "cs". Donc, pour changer ces valeurs, j'ai dû changer mes éléments de style en python.

Solution:

from docx import Document 
from docx.shared import Pt 

#path to doc with altered style: 
base_doc_location = 'base.docx' 
doc = Document(base_doc_location) 
my_style = doc.styles['Normal'] 

# define your desired fonts 
user_cs_font_size = 16 
user_cs_font_name = 'FreeFarsi' 
user_en_font_size = 12 
user_en_font_name = 'FreeMono' 

# get <w:rPr> element of this style 
rpr = my_style.element.rPr 

#================================================== 
'''This probably isn't necessary if you already 
have a document with altered style, but just to be 
safe I'm going to add this here''' 

if rpr.rFonts is None: 
    rpr._add_rFonts() 
if rpr.sz is None: 
    rpr._add_sz() 
#================================================== 

'''Get the nsmap string for rpr. This is that "w:" 
at the start of elements and element values in xml. 
Like these: 
    <w:rPr> 
    <w:rFonts> 
    w:val 

The nsmap is like a url: 
http://schemas.openxmlformats.org/... 

Now w:rPr translates to: 
{nsmap url string}rPr 

So I made the w_nsmap string like this:''' 

w_nsmap = '{'+rpr.nsmap['w']+'}' 
#================================================== 

'''Because I didn't find any better ways to get an 
element based on its tag here's a not so great way 
of getting it: 
''' 
szCs = None 
lang = None 

for element in rpr: 
    if element.tag == w_nsmap + 'szCs': 
     szCs = element 
    elif element.tag == w_nsmap + 'lang': 
     lang = element 

'''if there is a szCs and lang element in your style 
those variables will be assigned to it, and if not 
we make those elements and add them to rpr''' 

if szCs is None: 
    szCs = rpr.makeelement(w_nsmap+'szCs',nsmap=rpr.nsmap) 
if lang is None: 
    lang = rpr.makeelement(w_nsmap+'lang',nsmap =rpr.nsmap) 

rpr.append(szCs) 
rpr.append(lang) 
#================================================== 

'''Now to set our desired values to these elements 
we have to get attrib dictionary of these elements 
and set the name of value as key and our value as 
value for that dict''' 

szCs_attrib = szCs.attrib 
lang_attrib = lang.attrib 
rFonts_atr = rpr.rFonts.attrib 

'''sz and szCs values are string values and 2 times 
the font size so if you want font size to be 11 you 
have to set sz (for western fonts) or szCs (for CTL 
fonts) to "22" ''' 
szCs_attrib[w_nsmap+'val'] =str(int(user_cs_font_size*2)) 

'''Now to change cs font and bidi lang values''' 
rFonts_atr[w_nsmap+'cs'] = user_cs_font_name 
lang_attrib[w_nsmap+'bidi'] = 'fa-IR' # For Persian 
#================================================== 

'''Because we changed default style we don't even 
need to set style every time we add a new paragraph 
And if you change font name or size the normal way 
it won't change these cs values so you can have a 
font for CTL language and a different font for 
western language 
''' 
persian_p = doc.add_paragraph('نوشته') 
en_font = my_style.font 
en_font.name = user_en_font_name 
en_font.size = Pt(user_en_font_size) 
english_p = doc.add_paragraph('some text') 

doc.save('ex.docx') 

Modifier (amélioration du code):
Je commente les lignes qui pourraient utiliser une certaine amélioration et de mettre mieux les lignes en dessous.

#rpr = my_style.element.rPr # If None it'll throw errors later 
rpr = my_style.element.get_or_add_rPr() # this avoids potential errors 
#if rpr.rFonts is None: 
# rpr._add_rFonts() 
rFonts = rpr.get_or_add_rFonts() 
#if rpr.sz is None: 
# rpr._add_sz() 
rpr.get_or_add_sz() 

#by importing these you can make elements and set values quicker 
from docx.oxml.shared import OxmlElement, qn 
#szCs = rpr.makeelement(w_nsmap+'szCs',nsmap=rpr.nsmap) 
szCs = OxmlElement('w:szCs') 
#lang = rpr.makeelement(w_nsmap+'lang',nsmap =rpr.nsmap) 
lang = OxmlElement('w:lang') 

#szCs_attrib = szCs.attrib 
#lang_attrib = lang.attrib 
#rFonts_atr = rpr.rFonts.attrib 
#szCs_attrib[w_nsmap+'val'] =str(int(user_cs_font_size*2)) 
#rFonts_atr[w_nsmap+'cs'] = user_cs_font_name 
#lang_attrib[w_nsmap+'bidi'] = 'fa-IR' 

szCs.set(qn('w:val'),str(int(user_cs_font_size*2))) 
lang.set(qn('w:bidi'),'fa-IR') 
rFonts.set(qn('w:cs'),user_cs_font_name)