2014-07-25 16 views
5

Tôi không chắc liệu mình có thiếu gì hay không, nhưng tôi chưa tìm thấy tài liệu nào về cách chèn phần tử Word (ví dụ như bảng) vào một số vị trí cụ thể trong tài liệu ?điểm chèn python-docx

Tôi tải một tài liệu MS Word .docx hiện bằng cách sử dụng:

my_document = Document('some/path/to/my/document.docx') 

trường hợp sử dụng của tôi sẽ là để có được những 'vị trí' của một bookmark hoặc phần trong tài liệu và sau đó tiến hành để chèn bảng dưới đây điểm đó.

Tôi đang suy nghĩ về một API mà sẽ cho phép tôi làm điều gì đó dọc theo những đường:

insertion_point = my_document.bookmarks['bookmark_name'].position 
my_document.add_table(rows=10, cols=3, position=insertion_point+1) 

tôi thấy rằng có những kế hoạch để thực hiện một cái gì đó tương tự như các 'phạm vi' đối tượng của MS Word API , điều này sẽ giải quyết vấn đề đó một cách hiệu quả. Trong thời gian chờ đợi, có cách nào để hướng dẫn phương thức đối tượng document nơi chèn các phần tử mới không?

Có lẽ tôi có thể dán một số mã lxml để tìm một nút và chuyển cho các phương thức python-docx này? Bất kỳ trợ giúp về chủ đề này sẽ được nhiều đánh giá cao! Cảm ơn.

Trả lời

8

Tôi nhớ một câu ngạn ngữ cũ, "sử dụng nguồn, Luke!", Và có thể tìm ra. Một bài đăng từ chủ sở hữu python-docx trên trang dự án git của nó cũng cho tôi một gợi ý: https://github.com/python-openxml/python-docx/issues/7.

Mô hình tài liệu XML đầy đủ có thể được truy cập bằng cách sử dụng thuộc tính _document_part._element của nó. Nó hoạt động chính xác như một phần tử lxml etree. Từ đó, mọi thứ đều có thể.

Để giải quyết vấn đề điểm chèn cụ thể của tôi, tôi đã tạo đối tượng docx.Document tạm thời mà tôi đã sử dụng để lưu trữ nội dung đã tạo của mình.

import docx 
from docx.oxml.shared import qn 
tmp_doc = docx.Document() 

# Generate content in tmp_doc document 
tmp_doc.add_heading('New heading', 1) 
# more content generation using docx API. 
# ... 

# Reference the tmp_doc XML content 
tmp_doc_body = tmp_doc._document_part._element.body 
# You could pretty print it by using: 
#print(docx.oxml.xmlchemy.serialize_for_reading(tmp_doc_body)) 

Sau đó, tôi đã tải mẫu docx của mình (chứa dấu trang có tên 'insertion_point') vào đối tượng docx.Document thứ hai.

doc = docx.Document('/some/path/example.docx') 
doc_body = doc._document_part._element.body 
#print(docx.oxml.xmlchemy.serialize_for_reading(doc_body)) 

Bước tiếp theo là phân tích cú pháp XML để tìm chỉ mục của điểm chèn. Tôi định nghĩa một hàm nhỏ cho các nhiệm vụ trong tầm tay, mà trả về một yếu tố đoạn mẹ bookmark tên:

def get_bookmark_par_element(document, bookmark_name): 
""" 
Return the named bookmark parent paragraph element. If no matching 
bookmark is found, the result is '1'. If an error is encountered, '2' 
is returned. 
""" 
doc_element = document._document_part._element 
bookmarks_list = doc_element.findall('.//' + qn('w:bookmarkStart')) 
for bookmark in bookmarks_list: 
    name = bookmark.get(qn('w:name')) 
    if name == bookmark_name: 
     par = bookmark.getparent() 
     if not isinstance(par, docx.oxml.CT_P): 
      return 2 
     else: 
      return par 
return 1 

Chức năng mới định nghĩa được sử dụng toget bookmark 'insertion_point' đoạn mẹ. Kiểm soát lỗi được để lại cho người đọc.

bookmark_par = get_bookmark_par_element(doc, 'insertion_point') 

Bây giờ chúng ta có thể sử dụng chỉ số etree bookmark_par để chèn tmp_doc của chúng tôi tạo ra nội dung ở đúng nơi:

bookmark_par_parent = bookmark_par.getparent() 
index = bookmark_par_parent.index(bookmark_par) + 1 
for child in tmp_doc_body: 
    bookmark_par_parent.insert(index, child) 
    index = index + 1 
bookmark_par_parent.remove(bookmark_par) 

Tài liệu này hiện đang hoàn thiện, nội dung được tạo ra sau khi được chèn vào vị trí đánh dấu của một tài liệu Word hiện có.

# Save result 
# print(docx.oxml.xmlchemy.serialize_for_reading(doc_body)) 
doc.save('/some/path/generated_doc.docx') 

Tôi hy vọng điều này có thể giúp ai đó, vì tài liệu liên quan đến vấn đề này vẫn chưa được viết.

0

Cảm ơn bạn đã dành thời gian giải thích tất cả điều này.

Tôi đã trải qua nhiều hoặc ít hơn cùng một vấn đề. Điểm cụ thể của tôi là cách kết hợp hai hoặc nhiều tài liệu docx, cuối cùng.

Nó không hẳn là một giải pháp cho vấn đề của bạn, nhưng đây là chức năng tôi đến với:

def combinate_word(main_file, files, output): 
    main_doc = Document(main_file) 
    for file in files: 
     sub_doc = Document(file) 

     for element in sub_doc._document_part.body._element: 
      main_doc._document_part.body._element.append(element) 

    main_doc.save(output) 

Thật không may, nó chưa thể và cũng không dễ dàng để sao chép hình ảnh với python-docx. Tôi rơi trở lại win32com ...

+0

Cảm ơn đã chia sẻ! Tôi không cần phải thử nghiệm với hình ảnh, vì vậy tôi không chắc chắn về những thách thức ở phía đó. – Apteryx

0

Bạn đặt [image] như một mã thông báo trong tài liệu mẫu của bạn:

for paragraph in document.paragraphs: 
    if "[image]" in paragraph.text: 
     paragraph.text = paragraph.text.strip().replace("[image]", "") 

     run = paragraph.add_run() 
     run.add_picture(image_path, width=Inches(3)) 

bạn có có một đoạn trong một ô trong bảng là tốt. chỉ cần tìm ô và làm như trên.

1

chủ Python-docx cho thấy làm thế nào để chèn một bảng vào giữa một tài liệu hiện có: https://github.com/python-openxml/python-docx/issues/156

Dưới đây là với một số cải tiến:

import re 
from docx import Document 

def move_table_after(document, table, search_phrase): 
    regexp = re.compile(search_phrase) 
    for paragraph in document.paragraphs: 
     if paragraph.text and regexp.search(paragraph.text): 
      tbl, p = table._tbl, paragraph._p 
      p.addnext(tbl) 
      return paragraph 

if __name__ == '__main__': 
    document = Document('Existing_Document.docx')  
    table = document.add_table(rows=..., cols=...) 
    ... 
    move_table_after(document, table, "your search phrase")      
    document.save('Modified_Document.docx') 
Các vấn đề liên quan