Как мне объявить новые теги oxml/xmlchemy в python-docx?

Я пытаюсь встроить базовую функциональность уравнений в python-docx для вывода формул в файлы docx. Может кто-нибудь пройти через стандартную процедуру регистрации нового класса в oxml? Глядя на исходный код, кажется, что тег объявляется путем создания класса сложного типа

class CT_P(BaseOxmlElement):
    """
    ''<w:p>'' element, containing the properties and text for a paragraph.
    """
    pPr = ZeroOrOne('w:pPr')
    r = ZeroOrMore('w:r')

а затем зарегистрировать его с помощью функции register_element_cls()

from .text.paragraph import CT_P
register_element_cls('w:p', CT_P)

Некоторые классы включают в себя другие методы, но многие нет, поэтому, похоже, что минимальный рабочий пример будет следующим:

from docx import Document
from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne, ZeroOrMore, OxmlElement
import docx.oxml
docx.oxml.ns.nsmap['m'] = ('http://schemas.openxmlformats.org/officeDocument/2006/math')

class CT_OMathPara(BaseOxmlElement):
    r = ZeroOrMore('w:r')

docx.oxml.register_element_cls('m:oMathPara',CT_OMathPara)  
p = CT_OMathPara()

(Обратите внимание, что я должен объявить пространство имен m, так как оно не используется в пакете). К сожалению, это не работает для меня вообще. Если я объявляю новый класс, полученный как в примере выше, а затем проверяю, например, __repr__ этого нового класса, это вызывает исключение

>> p

File "C:\ProgramData\Anaconda3\lib\site-packages\docx\oxml\ns.py", line 50, in from_clark_name
    nsuri, local_name = clark_name[1:].split('}')

ValueError: not enough values to unpack (expected 2, got 1)

Это происходит потому, что тег в моем классе сильно отличается от тега a w:p, созданного из пакета python-docx

>> paragraph._element.tag
 '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p'

>> p.tag
 'CT_OMathPara'

Но я не знаю, почему это так. Поиск файлов по исходному коду не обнаруживает никаких других упоминаний о классе CT_P, поэтому я немного озадачен.

1 ответ

Решение

Я думаю, что ошибка исходит от префикса пространства имен 'm' (nspfx), отсутствующего в docx.oxml.ns.pfxmap ДИКТ. Пространство имен необходимо искать в обоих направлениях (от nspfx до url пространства имен и от url до nspfx).

Таким образом, чтобы добавить новое пространство имен снаружи, то есть после ns Модуль загружен, вам нужно сделать оба (если вы должны были ns Код модуля напрямую, этот второй шаг будет обрабатываться автоматически во время загрузки):

nsmap, pfxmap = docx.oxml.ns.nsmap, docx.oxml.ns.pfxmap
nsmap['m'] = 'http://schemas.openxmlformats.org/officeDocument/2006/math'
pfxmap['http://schemas.openxmlformats.org/officeDocument/2006/math'] = 'm'

Это должно помочь вам преодолеть ошибку, которую вы получаете, однако есть еще немного, чтобы понять.

CT_OMathPara Класс является примером того, что известно как класс пользовательских элементов. Это означает, что lxml создает экземпляр объекта этого класса для каждого элемента с зарегистрированным тегом (m:oMathPara) вместо общего lxml_Element учебный класс.

Главное, вы должны позволить lxml сделать конструирование, которое происходит, когда он анализирует XML. Вы не можете получить значимый объект, создав этот класс самостоятельно.

Самый простой способ создать новый "свободный" элемент (не помещенный в дерево документов XML) - это использовать docx.oxml.OxmlElement():

oMathPara = OxmlElement('m:oMathPara')

Хотя чаще всего docx.oxml.parse_xml() Функция используется для анализа всего фрагмента XML. Парсер должен быть настроен на использование пользовательских классов элементов, и эти элементы должны быть зарегистрированы парсером, так что вы, вероятно, не захотите делать это для себя, когда один из oxml Модуль позаботится обо всем необходимом.

Так обычно, чтобы получить экземпляр CT_OMathPara, вы бы просто открыть документ, который содержал m:oMathPara элемент (после регистрации нового пространства имен и класса пользовательских элементов), но вы также можете просто проанализировать фрагмент XML. Если вы ищете parse_xml в oxml Модули вы найдете множество примеров. Вы должны получить декларации пространства имен прямо в верхней части предоставленного вами XML-документа, что может быть немного сложнее, но вы можете, конечно, просто изложить весь фрагмент XML-кода в тексте, если хотите, он просто становится немного многословным.

Другие вопросы по тегам