Apa yang saya coba capai:

  • Buat gaya paragraf dalam python-docx dengan font dan ukuran Persia yang ditentukan pengguna (bahasa CTL)

Masalah:

  • Saya dapat melakukan ini dengan bahasa non-CTL seperti bahasa Inggris:

    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')
    
  • Namun jika saya mengubah teks ke teks Persia, fontnya tidak akan berubah ke font yang ditentukan.

Mengapa ini terjadi:

  • Font yang saya tentukan hanya mengubah keluarga gaya font barat dan tidak melakukan apa pun pada keluarga font CTL

Bagaimana saya mengetahui hal ini:

  • Jika saya membuka file docx dengan LibreOffice dan membuka gaya dan masuk ke bagian font, saya dapat melihat bahwa font dan ukuran yang saya tentukan ada di "Western Text Font Family" tetapi tidak di "CTL Font Family". Dan hasilnya font teks CTL saya menjadi font default.

Info tambahan:

  1. Saya menggunakan LibreOffice di Linux
  2. Mengubah gaya default tidak ada gunanya bagi saya dalam situasi ini karena saya ingin pengguna menentukan nama dan ukuran font.
  3. Saya tidak punya pengalaman dalam mengubah file xml (apalagi file docx xml)
  4. versi python-docx adalah 0.8.6
2
ROAR 11 Agustus 2017, 08:34

2 jawaban

Jawaban Terbaik

Setelah berjam-jam mengaduk-aduk file docx, saya menyadari banyak ketakutan saya, bahwa jawabannya terletak pada file style.xml dokumen. Berikut ini semacam cara untuk memperbaikinya bagi orang-orang dengan masalah serupa:

Masalah dengan Arah Teks:

  • Jika Anda pernah mengetik dalam bahasa Arab atau Persia, Anda mungkin pernah melihat bahwa menyelaraskan teks dari kanan ke kiri tidak menyelesaikan semua masalah Anda. Karena jika Anda tidak mengubah arah teks, maka kursor dan tanda baca tetap berada di paling kanan layar (bukan mengikuti huruf terakhir) dan tidak ada justifikasi kanan jika Anda membutuhkannya. Sekarang karena saya tidak dapat mengubah arah teks di python-docx bahkan dengan mengubah nilai "textDirection" dari document.xml dari 'lrTb' (Kiri-Kanan/Atas-Bawah) menjadi 'rlTb', saya harus membuat dokumen dengan LibreOffice dan ubah gaya paragraf default ('Normal') menjadi apa yang ada dalam pikiran saya (arah teks rtl, dll). Ini sebenarnya menghemat banyak waktu nanti juga karena Anda tidak perlu melakukannya dengan python.

Penjelasan XML tentang masalah perubahan font:

  • Dokumen dengan gaya default yang diubah menunjukkan beberapa hal berbeda dalam file style.xml-nya. Dalam gaya paragraf Normal di bawah "w:rPr" Anda dapat melihat bahwa ada tambahan "w:szCs" yang menentukan ukuran font skrip kompleks (yang tidak dapat Anda ubah dengan mengubah style.font.size) dan dalam "w :rFonts" nilai untuk "cs" sekarang adalah font Persia yang saya tentukan. Juga nilai "w:lang", "bidi", sekarang menjadi "fa-IR" (untuk bahasa Persia). Inilah bagian xml yang saya bicarakan:

    <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>
    
  • Sekarang mengubah style.font.size hanya mengubah nilai "sz" (ukuran font barat) dan tidak melakukan apa pun pada nilai "szCs" (ukuran font cs). Dan juga style.font.name hanya mengubah nilai "ascii" dan "hAnsi" dari "w:rFonts" dan tidak melakukan apa pun pada nilai "cs". Jadi untuk mengubah nilai-nilai ini saya harus mengubah elemen gaya saya di python.

Solusi:

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')

Edit (peningkatan kode):
Saya mengomentari baris yang dapat menggunakan beberapa perbaikan dan meletakkan baris yang lebih baik di bawahnya.

#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)
3
ROAR 12 Agustus 2017, 10:49

Saya memiliki masalah yang sama dan menambahkan dukungan ke perpustakaan docx. Kode docx bercabang ada di https://github.com/Oritk/python-docx Penggunaan:

run = p.add_run(line)
#ru.font.size = Pt(8) ### This line is redundant - but you can leave it
run.font.cs_size = Pt(8)
run.font.rtl = True
0
OritK 16 November 2018, 11:15