2025-10-10 09:46:41 +02:00

196 lines
6.4 KiB
Python

#!/usr/bin/env python
#coding:utf-8
# Purpose: text objects
# Created: 03.01.2011
# Copyright (C) 2011, Manfred Moitzi
# License: MIT license
from __future__ import unicode_literals, print_function, division
__author__ = "mozman <mozman@gmx.at>"
from.compatibility import is_string
from .xmlns import CN, register_class, subelement, wrap
from .base import GenericWrapper, safelen
from .whitespaces import encode_whitespaces
from .protection import random_protection_key
from .propertymixins import StringProperty, TextNumberingMixin, BooleanProperty
from .propertymixins import IntegerWithLowerLimitProperty
@register_class
class Span(GenericWrapper):
TAG = CN('text:span')
style_name = StringProperty(CN('text:style-name'))
def __init__(self, text="", style_name="", xmlnode=None):
super(Span, self).__init__(xmlnode)
if xmlnode is None:
if style_name:
self.style_name = style_name
if text:
self.append_text(text)
@property
def textlen(self):
# NOTE: do not cache this value before you can guarantee that
# you detect ALL text changes in this node and all of it child nodes.
length = safelen(self.xmlnode.text)
for element in iter(self):
length += (element.textlen + safelen(element.xmlnode.tail))
return length
def plaintext(self):
# NOTE: do not cache this value before you can guarantee that
# you detect ALL text changes in this node and all of it child nodes.
text = [self.xmlnode.text]
for element in iter(self):
text.append(element.plaintext())
text.append(element.xmlnode.tail)
return "".join(filter(None, text))
def append_text(self, text):
def append(text, new):
return text + new if text else new
for tag in encode_whitespaces(text):
if is_string(tag):
if len(self.xmlnode) > 0:
lastchild = self[-1]
lastchild.tail = append(lastchild.tail, tag)
else:
self.text = append(self.text, tag)
else:
self.append(tag)
@register_class
class Paragraph(Span):
TAG = CN('text:p')
cond_style_name = StringProperty(CN('text:cond-style-name'))
ID = StringProperty(CN('text:id'))
@register_class
class NumberedParagraph(GenericWrapper, TextNumberingMixin):
TAG = CN('text:numbered-paragraph')
level = IntegerWithLowerLimitProperty(CN('text:level'), 1)
def __init__(self, paragraph=None, xmlnode=None):
super(NumberedParagraph, self).__init__(xmlnode)
if xmlnode is None:
if paragraph is not None:
if isinstance(paragraph, GenericWrapper):
self.append(paragraph)
else:
raise TypeError("Parameter 'paragraph' has to be a subclass of class 'GenericWrapper'")
@property
def content(self):
p = self.xmlnode.find(CN('text:h'))
if p is None:
p = subelement(self.xmlnode, CN('text:p'))
return wrap(p)
@register_class
class Heading(Span, TextNumberingMixin):
TAG = CN('text:h')
outline_level = IntegerWithLowerLimitProperty(CN('text:outline-level'), 1)
restart_numbering = BooleanProperty(CN('text:restart-numbering'))
suppress_numbering = BooleanProperty(CN('text:is-list-header'))
def __init__(self, text="", outline_level=1, style_name="", xmlnode=None):
super(Heading, self).__init__(text, style_name, xmlnode)
if xmlnode is None:
self.outline_level = outline_level
@register_class
class Hyperlink(Span):
TAG = CN('text:a')
name = StringProperty(CN('office:name'))
href = StringProperty(CN('xlink:href'))
def __init__(self, href="", text="", style_name="", xmlnode=None):
super(Hyperlink, self).__init__(text, style_name, xmlnode)
if xmlnode is None:
if href: self.href = href
self.target_frame = '_blank'
@property
def target_frame(self):
return self.get_attr(CN('office:target-frame-name'))
@target_frame.setter
def target_frame(self, framename):
self.set_attr(CN('office:target-frame-name'), framename)
show = 'new' if framename == '_blank' else 'replace'
self.set_attr(CN('xlink:show'), show)
@register_class
class ListHeader(GenericWrapper):
TAG = CN('text:list-header')
def __init__(self, text="", xmlnode=None):
super(ListHeader, self).__init__(xmlnode)
if xmlnode is None:
if text:
self.append(Paragraph(text))
def plaintext(self):
return '\n'.join([e.plaintext() for e in iter(self)])
@register_class
class ListItem(ListHeader, TextNumberingMixin):
TAG = CN('text:list-item')
@register_class
class List(GenericWrapper):
TAG = CN('text:list')
style_name = StringProperty(CN('text:style-name'))
continue_numbering = BooleanProperty(CN('text:continue-numbering'))
def __init__(self, style_name="", xmlnode=None):
super(List, self).__init__(xmlnode)
if xmlnode is None:
if style_name:
self.style_name = style_name
@property
def header(self):
h = self.xmlnode.find(CN('text:list-header'))
return wrap(h) if h is not None else None
@header.setter
def header(self, header):
if header.kind != 'ListHeader':
raise TypeError("param 'header' is not a list header.")
oldheader = self.xmlnode.find(CN('text:list-header'))
if oldheader is not None:
self.xmlnode.remove(oldheader)
self.insert(0, header) # should be first child node
def iteritems(self):
return self.findall(CN('text:list-item'))
@register_class
class Section(GenericWrapper):
TAG = CN('text:section')
style_name = StringProperty(CN('text:style-name'))
name = StringProperty(CN('text:name'))
def __init__(self, name="", style_name="", xmlnode=None):
super(Section, self).__init__(xmlnode)
if xmlnode is None:
if style_name:
self.style_name = style_name
if name:
self.name = name
@property
def protected(self):
return self.get_bool_attr(CN('text:protected'))
@protected.setter
def protected(self, value):
self.set_bool_attr(CN('text:protected'), value)
if self.protected:
self.set_attr(CN('text:protection-key'), random_protection_key())